<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>

    <channel>
        <title>Quant Out of Water</title>
        <atom:link href="%7balternate%20%7brss%20application/rss&#43;xml%20%20index%20alternate%20%20false%20false%20true%20false%20false%200%7d%20/index.xml%20https://rjtk.github.io/index.xml%7d" rel="self" type="application/rss+xml" />
        <link>https://rjtk.github.io/</link>
        <managingEditor>qoow</managingEditor>
        <description>Quant Out of Water</description>
        <lastBuildDate>Sun, 24 Sep 2023 00:00:00 -0700</lastBuildDate>
        <language>en-us</language>
        <generator>Hugo -- gohugo.io</generator><item>
            <title>Constraint Programming, Puzzles, ILP, and z3</title>
            <link>https://rjtk.github.io/posts/constraint-programming-puzzles-ilp-and-z3/</link>
            <pubDate>Sun, 24 Sep 2023 00:00:00 -0700</pubDate>
            <guid>https://rjtk.github.io/posts/constraint-programming-puzzles-ilp-and-z3/</guid><description>&lt;p&gt;Solving puzzles with computers.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Earlier in the year I stumbled upon a puzzle posted by Jane Street called the &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.janestreet.com/puzzles/twenty-four-seven-four-in-one-index/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;4-in-1 24-7 puzzle&lt;/a
&gt;
.  The puzzle is in the front matter of this post.  On a first look, and on first reading through the rules for the puzzle, it appears cartoonishly complicated.  But, if you&amp;rsquo;d like to take your own crack at the problem, there is a simpler &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.janestreet.com/puzzles/twenty-four-seven-index/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;24-7 puzzle&lt;/a
&gt;
 which can be solved first (as I did), before tackling the 4-in-1 version.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure if the creators of the puzzle imagined people would print the puzzle onto paper and solve it by hand, but I immediately thought to myself: I am &lt;strong&gt;way&lt;/strong&gt; too lazy for that.  So instead I naturally wanted to get a computer to do all the work for me&amp;hellip;&lt;/p&gt;
&lt;p&gt;Throughout my short academic career, I generally took much more interest in continuous optimization than I did in discrete optimization.  Almost everything I learned about discrete optimization I learned ages ago from &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.coursera.org/learn/discrete-optimization&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this Coursera course&lt;/a
&gt;
, and the application of semidefinite programming to &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.cs.cmu.edu/~anupamg/adv-approx/lecture14.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;approximating MAXCUT&lt;/a
&gt;
.  That being said, I knew that constraint programming was a natural approach to solving puzzles, the 24-7 puzzles being no exception.&lt;/p&gt;
&lt;p&gt;Interestingly, I&amp;rsquo;m no stranger to algorithmic puzzle-solving.  Many years ago, my girlfriend showed me an optical illusion puzzle that involved fitting a 4x4 grid of tiles with matching patterns on adjacent edges &amp;ndash; you had to figure out how to place the tiles so that the patterns on the edges all matched.&lt;/p&gt;


&lt;p&gt;I wrote a simple &lt;kbd&gt;C++&lt;/kbd&gt; program to basically brute force this puzzle &amp;ndash; I remember running it for about a day on my ancient laptop.  Armed with the tools described in this blog post, it could probably be solved in under a second, and the modeling and implementation could be done in a couple hours (rather than the whole weekend, as the &lt;kbd&gt;C++&lt;/kbd&gt; program probably took me).&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;constraint-programming&#34;
    &gt;Constraint Programming&lt;a href=&#34;#constraint-programming&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Constraint programming&lt;/em&gt; is an algorithmic methodology that targets problems that have a discrete solution space, such as arranging puzzle pieces, rather than continuous problems like optimizing the shape of an aluminum can.  Moreover, in contrast to optimization, the focus is upon simply &lt;em&gt;finding&lt;/em&gt; a feasible solution that satisfies a given set of constraints, rather than optimizing an objective function &lt;em&gt;subject to&lt;/em&gt; those constraints.&lt;/p&gt;
&lt;p&gt;The approach involves systematically enumerating constraints that a solution must meet and which, if met, constitute a solution. A search algorithm then explores the solution space and either successfully returns a solution, or proves that one does not exist.&lt;/p&gt;
&lt;p&gt;An example of how constraint programming might be used is in solving Sudoku puzzles.  For instance, a common technique involves initially considering all numbers from 1 to 9 as candidates for each square. You then iteratively narrow down these options based on existing numbers in the grid, filling in a square when only one candidate remains and backtracking when no candidates are left.  The process of checking how a decision affects the constraints is called &lt;em&gt;constraint propagation&lt;/em&gt; and it is one particular algorithmic building block for trying to tackle &lt;em&gt;constraint programming&lt;/em&gt; problems.&lt;/p&gt;
&lt;p&gt;I followed a strategy like this to &lt;a
    class=&#34;link&#34;
    href=&#34;https://github.com/RJTK/sodoku&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;hack together a Sudoku solver&lt;/a
&gt;
, similarly inspired when I saw my girlfriend working on a book of Sudoku puzzles&amp;hellip; &lt;em&gt;Perhaps this is a theme&lt;/em&gt; 🤔 &amp;hellip; At the time I was rather satisfied to have been able to solve Sudokus in a couple hundred milliseconds (so slow largely because of a rather inefficient representation, and writing it in Python), though there are some &lt;a
    class=&#34;link&#34;
    href=&#34;https://t-dillon.github.io/tdoku/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;rather fast&lt;/a
&gt;
 programs that can solve Sudoku on the order of microseconds.&lt;/p&gt;
&lt;p&gt;These examples are ad-hoc, but I am more interested in general approaches.  Similarly to how &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.cvxpy.org/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CVX&lt;/a
&gt;
 makes it incredibly easy to experiment with convex optimization problems, there are general modeling tools that can be used to model and solve whole classes of discrete optimization problems.  Indeed, if the constraints can be appropriately described, general purpose algorithms can be applied to search for feasible solutions.&lt;/p&gt;
&lt;p&gt;For the rest of this post, I will describe two such general tools: &lt;em&gt;Integer Linear Programming&lt;/em&gt;, and &lt;em&gt;Satisfiability Modulo Theories&lt;/em&gt;, and how they can be used to solve constraint programming problems.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;integer-linear-programming&#34;
    &gt;Integer Linear Programming&lt;a href=&#34;#integer-linear-programming&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;A concrete and practical means of describing constraint programs is by means of &lt;em&gt;integer linear programming&lt;/em&gt; (ILP).  Specifically, a &lt;em&gt;feasibility&lt;/em&gt; ILP:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\text{find}&amp;amp;\ x\\
\text{such that}
&amp;amp;\ Ax \le b\\
&amp;amp;\ x_i \in \mathbb{N}_0.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;This looks a lot like an ordinary linear program \(\text{min}\{c^{\mathsf{T}} x\ |\ Ax \le b, x \in \mathbb{R}^n\}\) except there is no objective (practically speaking, just set \(c = 0\)) and there is an &lt;em&gt;integer constraint&lt;/em&gt; \(x_i \in \mathbb{N}_0\).  That is, each element of the solution vector \(x\) must be an integer (including zero): \(0, 1, 2, \ldots\)&lt;/p&gt;
&lt;p&gt;While linear programming problems are convex optimization problems, and thus entirely tractable, ILPs are NP-complete.  Theoretically, there is no known &amp;ldquo;fast&amp;rdquo; algorithm for solving ILPs in general, but practically speaking, we can usually still find optimal solutions (or good suboptimal approximations) fast enough for it to be useful.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Totally Unimodular Matrices): If the matrix&lt;/kbd&gt; \(A\) &lt;kbd&gt;in the ILP constraints is &amp;ldquo;totally unimodular&amp;rdquo;, then the LP solution and the ILP solution coincide.&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://courses.engr.illinois.edu/cs598csc/sp2010/Lectures/Lecture4.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;This is an important and tractable special case&lt;/a
&gt;
.&lt;/p&gt;
&lt;p&gt;Another special case of ILP is what one might call &lt;em&gt;Boolean Linear Programming&lt;/em&gt; (BLP), but is usually actually referred to as \(0-1\) linear programming.  In this case, we have the constraint \(x_i \in \{0, 1\}\) (equivalent to just including the constraint \(x_i \le 1\) in the ILP).  To give a sense of how powerful ILP is, the reader is encouraged to think about why the following constraints encode logical operators as BLP constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logical-and: \(z = x \wedge y\) is encoded as \(z \ge x + y - 1, z \le x, z \le y\).&lt;/li&gt;
&lt;li&gt;logical-not: \(z = \neg x\) is encoded as \(z = 1 - x\)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given that we can encode &lt;kbd&gt;AND&lt;/kbd&gt; and &lt;kbd&gt;NOT&lt;/kbd&gt;, we can encode &lt;em&gt;any&lt;/em&gt; Boolean expression &amp;ndash; these operators together are universal.  Here&amp;rsquo;s a couple more examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logical-enforcement (if x then y): \(x \implies y\) is encoded as \(x \le y\)&lt;/li&gt;
&lt;li&gt;logical-implication: \(z = (x \implies y) = y \vee \neg x\): is encoded as \(z \le (1 - x) + y, z \ge 1 - x, z \ge y\)
&lt;ul&gt;
&lt;li&gt;This looks complicated, but is nothing but the combination of logical-or and logical-not.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reader is encouraged to try to encode some other logical expressions, e.g., exclusive-or, logical-or, &lt;em&gt;etc&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll shortly use this modeling framework to encode and solve the (vanilla) 24-7 puzzle.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;satisfiability-modulo-theories&#34;
    &gt;Satisfiability Modulo Theories&lt;a href=&#34;#satisfiability-modulo-theories&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Similarly to ILP, &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Satisfiability_modulo_theories&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Satisfiability modulo theories&lt;/a
&gt;
 (SMT) provide a means of implementing constraint programming.  SMT is a powerful methodology that generalizes &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Boolean_satisfiability_problem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Boolean Satisfiability&lt;/a
&gt;
 (SAT) to allow the inclusion of arithmetic (among other things) in the constraints.  For example, an instance of a SAT problem is to find a Boolean assignment to the variables \(x,y,z\) such that&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
(x \vee \neg y \vee z) \wedge (y \vee \neg z)
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;evaluates to &lt;kbd&gt;True&lt;/kbd&gt;, or prove that such an assignment does not exist.&lt;/p&gt;
&lt;p&gt;An example of an SMT problem is to solve the (nonlinear!) system of inequalities&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
x^2 + y^2 &amp;amp;&amp;gt; 3\\
x^3 + y &amp;amp;&amp;lt; 5
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;over the integers.  The inclusion of arithmetic makes SMT more general than SAT.&lt;/p&gt;
&lt;p&gt;SAT and SMT both have rich mathematical theory surrounding them.  However, I am primarily interested in SMT as a practical engineering tool, so I admit to knowing almost nothing about the theory.  Indeed, I hardly know anything at all about theoretical computer science, and everything I&amp;rsquo;ve learned about SMT so far comes from &lt;a
    class=&#34;link&#34;
    href=&#34;https://sat-smt.codes/SAT_SMT_by_example.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this very practical book&lt;/a
&gt;
.  Computationally, a library for doing SMT programming is &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Z3_Theorem_Prover&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the z3 theorem prover&lt;/a
&gt;
, which has &lt;a
    class=&#34;link&#34;
    href=&#34;https://ericpony.github.io/z3py-tutorial/guide-examples.htm&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;convenient Python bindings&lt;/a
&gt;
.  That same page has plenty of examples, as does &lt;a
    class=&#34;link&#34;
    href=&#34;https://theory.stanford.edu/~nikolaj/programmingz3.html&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this guide&lt;/a
&gt;
.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick example that solves the above nonlinear system:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; z3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Int(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Int(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;y&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;solve(z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;And(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x&lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; y&lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x&lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; y &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[x = -2, y = 0]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ll shortly use &lt;kbd&gt;z3&lt;/kbd&gt; to solve the complicated 4-in-1 24-7 puzzle.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;solving-the-vanilla-24-7-puzzle&#34;
    &gt;Solving the Vanilla 24-7 Puzzle&lt;a href=&#34;#solving-the-vanilla-24-7-puzzle&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Now the fun begins.  Here I&amp;rsquo;m going to apply integer linear programming to solve the &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.janestreet.com/puzzles/twenty-four-seven-index/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;ldquo;vanilla&amp;rdquo; 24-7 puzzle,&lt;/a
&gt;
 which looks vaguely similar to a Sudoku:&lt;/p&gt;


&lt;p&gt;The rules are described thus:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;The grid is incomplete. Place numbers in some of the empty cells below so that in total the grid contains one 1, two 2’s, etc., up to seven 7’s. Furthermore, each row and column must contain exactly 4 numbers which sum to 20. Finally, the numbered cells must form a connected region, but every 2-by-2 subsquare in the completed grid must contain at least one empty cell.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Read this over a few times &amp;ndash; the first couple of constraints sound straightforward, but the &amp;ldquo;connected region&amp;rdquo; constraint is difficult to even wrap one&amp;rsquo;s head around.  It means that the cells which are filled in need to form a connected set &amp;ndash; there are no &amp;ldquo;islands&amp;rdquo; of numbers.  We&amp;rsquo;ll see these in detail as I show how they can be encoded as constraints in an ILP.&lt;/p&gt;
&lt;p&gt;First, I am using &lt;a
    class=&#34;link&#34;
    href=&#34;https://developers.google.com/optimization&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ortools&lt;/a
&gt;
, so let&amp;rsquo;s do some setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; ortools.sat.python &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; cp_model
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; itertools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; product
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;N &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;)  &lt;span style=&#34;color:#6272a4&#34;&gt;# Grid size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;K &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;)  &lt;span style=&#34;color:#6272a4&#34;&gt;# Values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cp_model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;CpModel()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;solver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cp_model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;CpSolver()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This sets up a Constraint Programming model called &lt;kbd&gt;model&lt;/kbd&gt; and a &lt;kbd&gt;solver&lt;/kbd&gt;.  We now just need to add our constraints!&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (performance): I generally first write code that is readable and easy to debug and understand, only trying to optimize when it is necessary.  I&amp;rsquo;m quite directly describing the constraints of the problem, and not trying to do anything clever.  I&amp;rsquo;ve introduced a number of variables which I believe add clarity, but which may, technically, be redundant.  Fortunately, it is quite likely that the ILP solver is fairly capable of recognizing and eliminating obvious redundancies.  That being said, the way in which a program is encoded (in any modeling language) &lt;em&gt;can&lt;/em&gt; potentially have a significant impact on performance, so it can be worth it to revise and refactor if one is not getting the throughput they need.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (modeling tip): While it may be counter intuitive, it is actually possible that &lt;em&gt;increasing&lt;/em&gt; the number of constraints in the problem can actually &lt;em&gt;improve&lt;/em&gt; performance.  The reason being that the search space is more constrained.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;The first variable I&amp;rsquo;m introducing is what I&amp;rsquo;ve called a &lt;kbd&gt;panel&lt;/kbd&gt;.  This is a \(7 \times 7 \times 7\) grid of \(\{0, 1\}\) indicators where &lt;kbd&gt;panel[k][i][j] = 1&lt;/kbd&gt; if and only if the cell at position &lt;kbd&gt;(i, j)&lt;/kbd&gt; is equal to &lt;kbd&gt;k&lt;/kbd&gt;.   Let&amp;rsquo;s first encode all the static assignments using this variable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Declare variables which encode the values on the board as 7 sets of Boolean indicators&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;panel &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {k: [[model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;NewIntVar(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;z_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;k&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; K}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Ensure that only one of the panel indicators is active&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(panel[k][i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; K) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Manually encode the given values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(panel[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The function &lt;kbd&gt;model.Add&lt;/kbd&gt; adds a constraint to the ILP.&lt;/p&gt;


&lt;p&gt;Notice that the constraint &lt;kbd&gt;sum(panel[k][i][j] for k in K) &amp;lt;= 1&lt;/kbd&gt;, along with the fact that &lt;kbd&gt;panel[k][i][j]&lt;/kbd&gt; is either \(0\) or \(1\), ensures that just one of these indicators can be active at a time.&lt;/p&gt;
&lt;p&gt;Another obvious variable to introduce is the &lt;kbd&gt;grid&lt;/kbd&gt;, which is a \(7 \times 7\) array of variables equal to the value of that cell.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Variables that Store the value of the grid at position (i, j)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grid &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;NewIntVar(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Encode the values of the grid from the zero-one variables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(grid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(k &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; panel[k][i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; K))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Since only one &lt;kbd&gt;panel&lt;/kbd&gt; indicator can be active, &lt;kbd&gt;sum(k * panel[k][i][j] for k in K)&lt;/kbd&gt; must be equal to the integer value that is being encoded.&lt;/p&gt;
&lt;p&gt;This &lt;kbd&gt;grid&lt;/kbd&gt; variable makes it easier to encode the row and column sum constraints.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Each row and column must sum to 20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(grid[row]) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;20&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; col &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(grid[i][col] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;20&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And, the reason I introduced the &lt;kbd&gt;panel&lt;/kbd&gt; variable in the first place is because it makes is easy to encode the counting constraint:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The grid must contain one 1, two 2s, three 3s, etc.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; K:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(panel[k][i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; k)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The constraint that each row and column contain exactly four non-zero numbers, and that each \(2 \times 2\) subsquare contain an empty cell is easy to encode if we introduce another &lt;em&gt;Boolean&lt;/em&gt; &lt;kbd&gt;nz&lt;/kbd&gt; variable where &lt;kbd&gt;nz[i][j] = 1&lt;/kbd&gt; if and only if grid position \((i, j)\) is non-zero.&lt;/p&gt;
&lt;p&gt;This variable feels redundant, as we should be able to just do things like &lt;kbd&gt;if grid[i][j] &amp;gt; 0&lt;/kbd&gt; right?  Technically yes, but encoding if-else statements directly in an ILP is cumbersome and confusing &amp;ndash; the &lt;kbd&gt;nz&lt;/kbd&gt; variable makes reasoning easier.  We&amp;rsquo;ll see later how &lt;kbd&gt;z3&lt;/kbd&gt; addresses this with higher level programming constructs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# An indicator variable for whether position (i, j) is non-zero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nz &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;NewIntVar(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;nz_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Encode the non-zero indicator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(grid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; nz[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(nz[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; grid[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Each row and column must contain exactly four non-zero numbers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(nz[row]) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; col &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(nz[i][col] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Every 2x2 subsquare must contain at least one empty cell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(nz[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; nz[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; nz[i][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; nz[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Finally, the entries need to form a &lt;em&gt;connected region&lt;/em&gt;.  This is the most difficult constraint to encode.  If you are confused about what constitutes a &amp;ldquo;connected region&amp;rdquo;, take a look at &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.janestreet.com/puzzles/twenty-four-seven-solution/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the solution&lt;/a
&gt;
 &amp;ndash; you can trace a path between any two filled in digits: there are no &amp;ldquo;islands&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;One idea is to try encode this is to do some kind of &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Connected-component_labeling&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;connected component labeling&lt;/a
&gt;
, but it seems difficult (to me?) to encode this in an ILP such that it is both necessary &lt;em&gt;and&lt;/em&gt; sufficient.  That is, it is fairly easy to construct a set of ILP constraints which will be satisfied if there is just one connected component, but going in the other direction (that there is necessarily a single connected component when the constraints are satisfied) seems much more difficult.&lt;/p&gt;
&lt;p&gt;An alternative method is to imagine that the cells and their neighbours form a network of pipes, and then model some kind of &lt;em&gt;flow&lt;/em&gt; within that network.  We can let each non-zero part of the grid consume a single unit of flow, and then designate one of the given filled-in cells as a source with exactly enough units of flow to cover the whole grid.  Since there is one 1, two 2s, etc. up to seven 7s, we know there must be exactly 28 non-zero cells.&lt;/p&gt;
&lt;p&gt;It is well known that flow problems can be modeled with linear programs, so this approach seems promising for our ILP.  The following code does exactly this &amp;ndash; the comments should make the code fairly clear.  However, it can be subtle.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;39
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;40
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;41
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;42
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;43
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;44
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;45
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;46
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;47
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;48
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;49
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;50
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;51
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;52
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;53
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;54
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;55
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;56
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;57
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;58
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;59
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;60
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;61
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;62
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;63
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;64
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;65
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;66
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;67
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The maximum amount of flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEEDED_FLOW &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;28&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_neighbours&lt;/span&gt;(i, j):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Outgoing flow from cell (i, j) to neighbouring cell (p, q)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (p, q, model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;NewIntVar(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, M, &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flow_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;p&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;q&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; (p, q) &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; _neighbours(i, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Sum up all the outgoing flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;outgoing_flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; _, _, f &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; flow[i][j]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        outgoing_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# And separately keep track of the incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;incoming_flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; p, q, f &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; flow[i][j]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        incoming_flow[p][q] &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Cells which are zero must have zero flow through them&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(outgoing_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; M &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; nz[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; M &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; nz[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Pick an arbitrary fixed node as the source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;i_source, j_source &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Give it enough flow to fill all non-zero cells (minus itself)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    outgoing_flow[i_source][j_source] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; NEEDED_FLOW &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The source should have no incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;incoming_flow[i_source][j_source] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Conserve flow in the network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; i_source &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; j_source:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# Except the source is unconstrained&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# non-zero cells must have positive incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# which ensures they&amp;#39;re connected to the source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; nz[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# The outgoing flow must be one less than the incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# i.e., each node sinks 1 unit of flow.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Add(outgoing_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; nz[i][j])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This code looks fairly simple, but writing it was quite a challenge.  It was particularly difficult to write down the right constraints that ensured only the one designated node could act as a source.  In many earlier iterations I failed to properly enforce this and wound up with islands of non-zero cells.  I also had a subtle bug wherein I wrote &lt;kbd&gt;model.Add(outgoing_flow[i][j] == incoming_flow[i][j] - 1)&lt;/kbd&gt; instead of &lt;kbd&gt;model.Add(outgoing_flow[i][j] == incoming_flow[i][j] - nz[i][j])&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;:   When I originally solved the 24-7 puzzle (and the 4-in-1 puzzle below) I actually didn&amp;rsquo;t encode the path constraint condition.  Instead, I just encoded a simpler sufficient condition and printed out all of the possible solutions (there were only 3) and selected the correct one manually &amp;ndash; an expedient method.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;In any case, we can finally print out a solution.  I&amp;rsquo;ve hidden the implementation of the &lt;kbd&gt;panel_to_str&lt;/kbd&gt; function as it is uninteresting.  It just relies on getting the value of variables in the program with the method &lt;kbd&gt;solver.Value&lt;/kbd&gt;, for example: &lt;kbd&gt;solver.Value(panel[k][i][j])&lt;/kbd&gt;, &lt;em&gt;etc&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Solve(model) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; cp_model&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;OPTIMAL:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Optimal solution found:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(panel_to_str(panel, solver))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;FAILED TO FIND A SOLUTION&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Optimal solution found:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;7, 4, 3, 0, 6, 0, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 0, 6, 3, 5, 0, 6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 0, 5, 0, 5, 5, 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 3, 6, 4, 0, 0, 7
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4, 7, 0, 0, 0, 7, 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2, 0, 0, 7, 4, 7, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;7, 6, 0, 6, 0, 1, 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And there we have it 😀&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;solving-the-4-in-1-puzzle&#34;
    &gt;Solving the 4-in-1 Puzzle&lt;a href=&#34;#solving-the-4-in-1-puzzle&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The previous example with the 24-7 puzzle shows that ILP can be a rather cumbersome approach to encoding constraint programming problems, particularly if you endeavor to &lt;em&gt;directly&lt;/em&gt; construct all of the linear inequalities needed to encode your program.  In this section, I&amp;rsquo;m going to use the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Z3_Theorem_Prover&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;z3 theorem prover&lt;/a
&gt;
 for SMT, which I think is considerably more expressive.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll also be tackling a more difficult puzzle &amp;ndash; the 4-in-1 24-7 puzzle.  Indeed, the previous section was merely a warm up.  The specific rules for the 4-in-1 puzzle is to:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;Place numbers in some of the empty cells so that in total each of the four 7-by-7 outlined grids is a legal “Twenty Four Seven” grid. Namely: each 7-by-7 grid’s interior should contain one 1, two 2’s, etc., up to seven 7’s. Furthermore, each row and column within the 7-by-7’s must contain exactly 4 numbers which sum to 20. Finally, the numbered cells must form a connected region, but every 2-by-2 subsquare in the completed grid must contain at least one empty cell.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;Some numbers have been placed inside the grid. Additionally, some blue numbers have been placed outside of the grids. A number outside the grid represents either the sum of the row or column it is facing, or the value of the first number it sees in that row or column.&lt;/kbd&gt;&lt;/p&gt;


&lt;p&gt;Let&amp;rsquo;s begin!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; z3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; itertools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; product
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MAX_VAL &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OFFSET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;N &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;)  &lt;span style=&#34;color:#6272a4&#34;&gt;# Subgrid size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;K &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;)  &lt;span style=&#34;color:#6272a4&#34;&gt;# Available values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;full_N &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#bd93f9&#34;&gt;12&lt;/span&gt;)  &lt;span style=&#34;color:#6272a4&#34;&gt;# The whole grid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The full grid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grid &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Int(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; full_N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; full_N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# And each of the subgrids&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grids &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [[[grid[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; di][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; dj] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; dj &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, OFFSET)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; di &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, OFFSET)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# We&amp;#39;ll append all our constraints here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;As before, I&amp;rsquo;m keeping track of the grid values, with a separate convenience array for the subgrids.  As opposed to ILP, &lt;kbd&gt;z3&lt;/kbd&gt; is expressive enough that I don&amp;rsquo;t have to directly introduce multiple separate variables for different conditions like &lt;kbd&gt;nz&lt;/kbd&gt;, &lt;kbd&gt;panel&lt;/kbd&gt;, &lt;em&gt;etc.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Constraints are set similarly, and can also be &lt;em&gt;tagged&lt;/em&gt;.  The constraints list will contain tuples of the form &lt;kbd&gt;(tag, constraint)&lt;/kbd&gt;, as the tag can be useful for debugging.  You can ask &lt;kbd&gt;z3&lt;/kbd&gt; to print out the tags of some set of unsatisfiable constraints (whenever it finds one), so you can track down bugs more easily.  Ironically, I didn&amp;rsquo;t actually make proper use of this feature, since I noticed the bug I was searching for during the process of meticulously tagging each constraint.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Manually encode the grid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[1][3] = 1&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[1][9] = 6&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;9&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[1][10] = 5&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[2][5] = 3&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[2][10] = 6&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[3][1] = 4&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[3][8] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[4][4] = 2&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[4][11] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[5][2] = 6&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[5][8] = 3&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[5][9] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;9&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[8][3] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[8][5] = 5&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[9][1] = 5&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;9&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[9][5] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;9&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[10][1] = 6&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[10][2] = 7&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;g[11][4] = 6&amp;#34;&lt;/span&gt;, grid[&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;][&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I didn&amp;rsquo;t set bounds for the variables when I constructed them, so lets do that here.  Notice that &lt;kbd&gt;z3&lt;/kbd&gt; directly supports convenient constructs like &lt;kbd&gt;z3.And&lt;/kbd&gt;.  There is also &lt;kbd&gt;z3.Or, z3.Implies, z3.If&lt;/kbd&gt;, and many others &amp;ndash; pay attention for these throughout.  These functions make it much easier to encode the constraints than having to reason through encoding them with ILP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Bounds for all the integers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;bounds_[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;, z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;And(grid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, grid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; MAX_VAL))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(full_N, full_N)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The following lengthy code block implements again the 24-7 puzzle logic, but this time in &lt;kbd&gt;z3&lt;/kbd&gt;.  Since I&amp;rsquo;ve already explained it, I don&amp;rsquo;t do so again, except for the comments in the code itself.  Consider comparing this code with the ILP implementation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 39
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 40
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 41
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 42
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 43
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 44
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 45
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 46
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 47
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 48
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 49
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 50
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 51
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 52
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 53
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 54
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 55
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 56
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 57
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 58
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 59
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 60
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 61
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 62
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 63
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 64
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 65
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 66
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 67
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 68
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 69
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 70
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 71
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 72
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 73
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 74
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 75
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 76
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 77
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 78
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 79
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 80
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 81
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 82
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 83
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 84
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 85
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 86
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 87
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 88
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 89
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 90
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 91
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 92
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 93
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 94
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 95
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 96
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 97
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 98
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 99
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;100
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;101
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;102
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;103
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;104
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;105
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;106
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;107
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;108
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;109
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;110
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;111
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;112
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;113
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;114
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;115
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;116
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;117
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;118
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;119
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;120
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;121
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;122
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;123
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;124
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;125
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;126
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;127
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;128
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;129
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;130
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;131
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;132
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;133
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;134
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;135
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;136
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;137
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;138
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;139
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;140
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;141
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;142
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;143
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;144
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;145
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;146
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;147
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;148
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;149
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;150
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;151
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;encode_247_puzzle&lt;/span&gt;(subgrid_ix, flow_source: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    The flow_source is an integer tuple for where flow originates from.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    i_sg, j_sg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; subgrid_ix
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subgrid &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; grids[i_sg][j_sg]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_neighbours&lt;/span&gt;(i, j):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;yield&lt;/span&gt; (i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    constraints &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# For every row and column&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# They must contain exactly four non-zero numbers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;row_count4_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;row&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[row][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# And sum up to exactly 20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append((&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;row_sum20_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;row&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(subgrid[row]) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;20&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; col &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;col_count4_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;col&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][col] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;col_sum20_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;col&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(subgrid[i][col] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;20&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Must contain one 1, two 2s, ..., seven 7s.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; K:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;counts_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;k&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; k, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N)) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; k,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Every 2x2 subsquare must contain at least one zero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;2x2_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Again implementing path connectedness by using the flow model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Outgoing flow from cell (i, j) to neighbouring cell (p, q)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (p, q, z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Int(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flow_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;p&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;q&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; (p, q) &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; _neighbours(i, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; p, q, f &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; flow[i][j]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append((&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;f_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;p&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;q&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;] &amp;gt;= 0&amp;#34;&lt;/span&gt;, f &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;f_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;p&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;q&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;] &amp;lt;= M&amp;#34;&lt;/span&gt;, f &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(N) &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Sum up all the outgoing flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    outgoing_flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; _, _, f &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; flow[i][j]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            outgoing_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# And separately keep track of the incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    incoming_flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; N]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; p, q, f &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; flow[i][j]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            incoming_flow[p][q] &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Cells which are zero must have zero flow through them&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flowbal_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Implies(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;And(outgoing_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# The source has exactly enough outgoing flow to fill the network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# This time, I directly summed up the count of non-zero entries.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    i_source, j_source &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; flow_source
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sourceinc_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;, incoming_flow[i_source][j_source] &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sourceout_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            outgoing_flow[i_source][j_source]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N)) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Conserve flow in the network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(N, N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; i_source &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; j &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; j_source:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#6272a4&#34;&gt;# Except the source is unconstrained&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# non-zero cells must have positive incoming flow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# which ensures they&amp;#39;re connected to the source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;nzflow+_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Implies(subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# The outgoing flow must be one less than the incoming flow,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# i.e., each node sinks 1 unit of flow.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sink+_&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}{&lt;/span&gt;j_sg&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                outgoing_flow[i][j]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; incoming_flow[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(subgrid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; constraints
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This function is now being used as a subroutine &amp;ndash; each subgrid needs to be a valid 24-7 puzzle.  I specify the flow sources for each subgrid manually and arbitrarily from the fixed given data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Pick some arbitrary fixed nodes for the zero groups.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(encode_247_puzzle((&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;), (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(encode_247_puzzle((&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;), (&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(encode_247_puzzle((&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;), (&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;extend(encode_247_puzzle((&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;), (&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now, there are some additional rules described for the 4-in-1 puzzle.  Specifically, recall:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;Some numbers have been placed inside the grid. Additionally, some blue numbers have been placed outside the grid. A number outside the grid represents either the sum of the row or column it is facing, or the value of the first number it sees in that row or column.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;This constraint is a bit confusing, but ultimately we can just encode it with a sequence of nested if/else statements.  I&amp;rsquo;ve constructed this with a recursive function as follows, but one could just as easily write down the constraints &amp;ldquo;manually&amp;rdquo; with a &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.gnu.org/software/emacs/manual/html_node/emacs/Keyboard-Macros.html&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;keyboard macro&lt;/a
&gt;
.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;blue_num_constraint&lt;/span&gt;(i: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, j: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, di: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, dj: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, num):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    Starting at position i, j and moving in the direction di, dj,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    either the sum of the whole line is num OR the first non-zero
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    value encountered is equal to num.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;nest_if_else&lt;/span&gt;(m):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(full_N):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#6272a4&#34;&gt;# Terminal condition: we reached the end of the row or column&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            g &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; grid[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; di][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; dj]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#6272a4&#34;&gt;# If g &amp;gt; 0 then it must equal num.  Otherwise, check the next cell.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;If(g &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, g &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; num, nest_if_else(m &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;blue_[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;j&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;di&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;dj&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;num&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# Either the sum of the row or column is num, or we go into the nested if else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# statements to constraint the value of the first non-zero entry.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Or(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sum&lt;/span&gt;(grid[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; di][j &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; dj] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; m &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; full_N) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; num, nest_if_else(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We now need to manually write down all the constraints from the figure.  I enumerate them in counter-clockwise order, starting from the top-left corner.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Down the left side&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;33&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;29&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;40&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;28&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;36&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# left-to-right along the bottom&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Up the right side&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# right-to-left along the top&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;27&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;40&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;27&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;34&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;30&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;36&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(blue_num_constraint(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Finally, we instantiate and call the solver.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;solve_constraints&lt;/span&gt;(constrs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Solver()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# Add all the tagged constraints&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; tag, constr &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; constrs:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_and_track(constr, tag)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# This will enable the solver to tell you about a subset of unsat constraints.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;set(unsat_core&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;check() &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;sat:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        solution &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;model()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print_full_grid(solution, grid)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; solution
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;INFEASIBLE&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# The unsat core is some subset of unsat constraints.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# It need not be minimal in any sense, but may be useful for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# debugging.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unsat subset: &amp;#34;&lt;/span&gt;, solver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;unsat_core())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;solution &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; solve_constraints(constraints)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 5, 0, 6, 0 | 3, 6 | 0, 0, 0, 7, 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 7, 7, 1, 0 | 0, 5 | 0, 4, 6, 5, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 0, 0, 7, 5 | 3, 5 | 0, 6, 0, 6, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;6, 4, 3, 0, 0 | 7, 0 | 5, 7, 1, 0, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;7, 0, 0, 0, 2 | 7, 4 | 2, 0, 0, 0, 7
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2, 0, 6, 6, 6 | 0, 0 | 6, 3, 7, 0, 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5, 4, 4, 0, 7 | 0, 0 | 7, 0, 6, 2, 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;7, 0, 0, 0, 1 | 5, 7 | 1, 0, 0, 7, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 5, 3, 7, 0 | 5, 0 | 0, 5, 0, 4, 6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;6, 5, 0, 0, 0 | 7, 2 | 0, 6, 0, 0, 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 6, 7, 3, 0 | 0, 4 | 6, 6, 4, 0, 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0, 0, 0, 4, 6 | 3, 7 | 0, 0, 3, 7, 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Which is a correct solution!  We can also check that the solution is unique by adding the negation of the solution back as another constraint and then re-solving.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;soln1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        z3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Or([grid[i][j] &lt;span style=&#34;color:#ff79c6&#34;&gt;!=&lt;/span&gt; solution[grid[i][j]] &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, j &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; product(full_N, full_N)]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;solve_constraints(constraints)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;  9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 39
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 40
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 41
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 42
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 43
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 44
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 45
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 46
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 47
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 48
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 49
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 50
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 51
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 52
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 53
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 54
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 55
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 56
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 57
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 58
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 59
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 60
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 61
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 62
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 63
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 64
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 65
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 66
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 67
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 68
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 69
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 70
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 71
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 72
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 73
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 74
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 75
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 76
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 77
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 78
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 79
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 80
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 81
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 82
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 83
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 84
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 85
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 86
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 87
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 88
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 89
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 90
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 91
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 92
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 93
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 94
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 95
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 96
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 97
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 98
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 99
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;100
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;101
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;102
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;103
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;104
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;105
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;106
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;107
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;108
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;109
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;110
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;111
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;112
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;113
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;114
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;115
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;116
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;117
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;118
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;119
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;120
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;121
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;122
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;123
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;124
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;125
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;126
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;127
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;128
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;129
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;130
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;131
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFEASIBLE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Unsat subset:  [g[1][3] = 1,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[1][9] = 6,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[1][10] = 5,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[2][5] = 3,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[2][10] = 6,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[3][1] = 4,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[3][8] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[4][4] = 2,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[4][11] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[5][2] = 6,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[5][8] = 3,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[5][9] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[8][3] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[8][5] = 5,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[9][1] = 5,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[9][5] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[10][1] = 6,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[10][2] = 7,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; g[11][4] = 6,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[0][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[1][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[2][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[3][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[4][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[5][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[6][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[7][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[8][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][4],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][5],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][6],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][7],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][8],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][9],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][10],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[9][11],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[10][0],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[10][1],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[10][2],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; bounds_[10][3],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ...]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This proves that the solution I found is unique, and also serves to illustrate the &amp;ldquo;unsat subset&amp;rdquo; functionality.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: You can find my name on the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.janestreet.com/puzzles/twenty-four-seven-four-in-one-solution/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;solution page&lt;/a
&gt;
 &lt;kbd&gt;of this puzzle, so you know I&amp;rsquo;m no fraud.  But similarly as the vanilla 24-7 puzzle, I didn&amp;rsquo;t fully encode the entire problem as I&amp;rsquo;ve done now.  Instead, since I only cared about getting to a final result, I manually narrowed down some of the constraints with my own direct reasoning, or through sufficient conditions, and then printed out all the candidate solutions (there were 8) with ILP.  I finally found the only correct one ad-hoc, and computed the product of the areas of white space by hand.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;We&amp;rsquo;ve seen how both Integer Linear Programming (ILP) and Satisfiability Modulo Theories (SMT), a generalization of SAT, can be used to solve constraint programming problems.  These tools are incredibly powerful, and it seems likely that most practical &amp;ldquo;day to day&amp;rdquo; discrete optimization problems I might care to solve can be easily implemented and solved with &lt;kbd&gt;ortools&lt;/kbd&gt; or &lt;kbd&gt;z3&lt;/kbd&gt;.  I find &lt;kbd&gt;z3&lt;/kbd&gt; to be considerably more expressive than ILP, and it is probably a good go-to tool for constraint programming.  That being said, there are plenty of mathematical optimization problems which can be encoded as ILPs, so if optimization is the goal, rather than simple constraint satisfaction, ILP might be the right choice.&lt;/p&gt;</description></item><item>
            <title>All About Quadratic Forms</title>
            <link>https://rjtk.github.io/posts/all-about-quadratic-forms/</link>
            <pubDate>Tue, 18 Jul 2023 00:00:00 -0700</pubDate>
            <guid>https://rjtk.github.io/posts/all-about-quadratic-forms/</guid><description>&lt;p&gt;Quadratic forms are functions defined through symmetric matrices and represent a ubiquitous class of functions for which there is an enormous amount of useful theoretical and computational results.  Indeed, in &amp;ldquo;linear-quadratic&amp;rdquo; models, it is possible to provide analytic solutions to more-or-less any question you would like to ask.  This post is a tour of some foundations and results relating to quadratic forms.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Quadratic forms are functions \(f: \R^n \rightarrow \mathbb{R}\) defined by a constant \(\kappa \in \mathbb{R}\), a vector \(q \in \mathbb{R}^n\) and a matrix \(Q \in \mathbb{S}^n\) (\(\mathbb{S}^n\) denoting the set of symmetric \(n \times n\) matrices), namely, \(f(x) = \frac{1}{2}x^{\mathsf{T}} Q x + q^{\mathsf{T}} x + \frac{1}{2}\kappa\).  That the matrix \(Q\) is assumed symmetric is without loss of generality since if \(Q\) were not symmetric, we could instead consider \(\frac{1}{2}Q + \frac{1}{2}Q^{\mathsf{T}}\) without affecting the value of the function.  The \(\frac{1}{2}\)&amp;rsquo;s in the definition of \(f\) will be seen to be a convenient convention.&lt;/p&gt;
&lt;p&gt;Firstly, we can recognize that it is possible to expand this out explicitly as \[f(x) = \frac{1}{2}\sum_{i = 1}^n \sum_{j = 1}^n Q_{ij}x_i x_j + \sum_{i = 1}^n q_i x_i + \frac{1}{2}\kappa.\]  Thus, the function \(f\) is defined through a linear combination of all &lt;em&gt;pairs&lt;/em&gt; of products in \(x\), but not higher order powers.  Another convenient way to write QFs is through a single matrix, augmenting \(x\) with a \(1\)&lt;/p&gt;
&lt;p&gt;\begin{equation}
f(x) = \frac{1}{2}\begin{bmatrix}x\\ 1\\ \end{bmatrix}^{\mathsf{T}}\begin{bmatrix}Q &amp;amp; q\\ q^{\mathsf{T}} &amp;amp; \kappa \end{bmatrix}\begin{bmatrix}x\\ 1 \end{bmatrix},
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;so for the most part, techniques that work for the simpler case with \(q = 0\) and \(\kappa = 0\), also apply to the more general case.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Assumption&lt;/strong&gt;: For the purposes of this post, I will usually assume that $Q$ is invertible.  This assumption is not essential, but if this does not hold, then the quadratic form is degenerate and breaks down into a lower-dimensional full-rank quadratic form (restricted to the span of the columns of $Q$) and a linear form restricted to the perpendicular complement thereof.  It is analogous to the assumption that $a \ne 0$ in the classical quadratic polynomial $ax^2 + bx + c$.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;from-whence&#34;
    &gt;From Whence?&lt;a href=&#34;#from-whence&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Where do quadratic forms arise from?  At the most superficial level, they are nothing but a natural multivariate generalization of the quadratic polynomial \(ax^2 + bx + c\).  However, they also arise in extremely natural ways in numerous applications.  They are truly ubiquitous.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Least Squares&lt;/strong&gt;&lt;/kbd&gt;: The least-squares cost function \(f(x) = \frac{1}{2}||y - Ax||_2^2\) is a quadratic form \(f(x) = \frac{1}{2}x^{\mathsf{T}} A^{\mathsf{T}} A x - (A^{\mathsf{T}} y)^{\mathsf{T}} x + \frac{1}{2}||y||_2^2\) where \(Q = A^{\mathsf{T}} A, q = A^{\mathsf{T}} y\) and \(\kappa = ||y||_2^2\).  The least-squares cost function arises in statistical regression, estimation, as a design objective, &lt;em&gt;etc.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Variance of a Sum&lt;/strong&gt;&lt;/kbd&gt;: If we have some vector random variable \(Z\) with variance matrix \(\Sigma \in \mathbb{S}^n\), then the variance of the linear combination \(\text{Var}[Z^{\mathsf{T}} x] = x^{\mathsf{T}} \Sigma x\) is a quadratic form when viewed as a function of \(x\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Local Approximation&lt;/strong&gt;&lt;/kbd&gt;: Generally, differentiable functions \(f(x)\) can be expanded into a Taylor series about some point \(x_0\) as in \(f(x) = f(x_0) + \mathsf{D}f(x_0) (x - x_0) + \frac{1}{2} (x - x_0)^{\mathsf{T}} \mathsf{D}^2 f(x_0)(x - x_0) + R(x)\) where the remainder term \(R(x)\) is a \(3^{\text{rd}}\) order polynomial of the components of \((x - x_0)\).  Truncating the \(R(x)\) term results in a quadratic form that gives a local approximation of the function \(f\) near the point \(x_0\).  These approximations are used in (second order) iterative optimization algorithms &amp;ndash; particularly Newton&amp;rsquo;s method, sequential least squares, and interior point algorithms.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Gaussian Distributions&lt;/strong&gt;&lt;/kbd&gt;: The Gaussian distribution is one of the most important distribution functions in probability.  It is characterized by two parameters \(\mu \in \mathbb{R}^n\) (the mean) and \(\Sigma \in \mathbb{S_{+}^n}\) (the positive semidefinite variance matrix).  When \(\Sigma\) is positive definite (hence invertible) Gaussian random variables have the density function \(f(x) \propto \text{exp}\bigl(\frac{1}{2}(x - \mu)^{\mathsf{T}} \Sigma^{-1} (x - \mu) \bigr)\), which involves a quadratic form.  Moreover, the characteristic function \(\psi(t) = \text{exp}\bigl(j\mu^{\mathsf{T}} t - \frac{1}{2}t^{\mathsf{T}} \Sigma t\bigr)\) is also a quadratic form in \(t\).  (&lt;em&gt;Remark&lt;/em&gt;: The definition of the Gaussian should most properly be given in terms of \(\psi\), instead of the density, since the expression for \(\psi\) does not require that \(\Sigma\) be invertible!)&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Use in Optimization&lt;/strong&gt;&lt;/kbd&gt;: A huge number of general optimization problems, each of which have their own space of applications, involve quadratic forms in the objective function.  Examples are given by &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Quadratic_programming&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Quadratic Programming&lt;/a
&gt;
 (including &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Support_vector_machine&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Support Vector Machines&lt;/a
&gt;
), the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Quadratic_assignment_problem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Quadratic Assignment Problem&lt;/a
&gt;
, the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Linear%E2%80%93quadratic_regulator&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Linear Quadratic Regulator&lt;/a
&gt;
, and many more.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;completing-the-square&#34;
    &gt;Completing the Square&lt;a href=&#34;#completing-the-square&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;For the classical quadratic polynomial \(ax^2 + bx + c\), to &amp;ldquo;complete the square&amp;rdquo; is an elementary algebraic operation wherein we eliminate the linear term \(bx\) by writing \(ax^2 + bx + c = a(x + b / (2a))^2 + c - b^2 / (4a^2)\).  This procedure enables one to derive the quadratic formula, identify minimizers and maximizers, etc.  There is an analogous procedure for general quadratic forms:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
f(x)
&amp;amp;= \frac{1}{2}x^{\mathsf{T}} Q x + q^{\mathsf{T}} x + \frac{1}{2}\kappa\\
&amp;amp;= \frac{1}{2}(x + Q^{-1} q)^{\mathsf{T}} Q (x + Q^{-1} q) + \frac{1}{2}\kappa - q^{\mathsf{T}} Q^{-1} q,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where the \(Q^{-1}\) can be replaced by a pseudo-inverse \(Q^\dagger\) if \(Q\) is not invertible but merely \(q \in \mathcal{R}(Q)\).  Completing the square is a useful technique for derivations involving quadratic forms &amp;ndash; particularly, it will help us to recognize minimizers of \(f\), and it is commonly used for manipulating expressions relating to Gaussian distributions.  I solve a few optimization problems in this post, and nowhere do I resort to differentiation.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;optimization&#34;
    &gt;Optimization&lt;a href=&#34;#optimization&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Quadratic forms are extremely important in optimization.  Since they are so tractable, many algorithms for solving general optimization problems proceed by solving quadratic optimization problems in sequence, iteratively refining the approximation as they proceed.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;convexity&#34;
    &gt;Convexity&lt;a href=&#34;#convexity&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;There is a particular class of quadratic forms which are of particular interest in optimization &amp;ndash; these are &lt;em&gt;convex&lt;/em&gt; quadratic forms.  We will see later that, more generally, quadratic forms can be decomposed into convex, concave, and linear subspaces.&lt;/p&gt;
&lt;p&gt;Recall that a function \(f\) is &lt;em&gt;convex&lt;/em&gt; if \[\forall t \in (0, 1), x, y \in \mathsf{dom}\ f: f(tx + (1 - t)y) \le tf(x) + (1 - t)f(y).\]  This means that the line joining two points on the graph of \(f\) is above the graph itself.  Intuitively, convex functions &amp;ldquo;look like bowls&amp;rdquo; and their importance stems, at least in part, from the fact that any &lt;em&gt;local&lt;/em&gt; minimizer is also a &lt;em&gt;global&lt;/em&gt; minimizer.  Thus, for example, gradient descent applied to convex functions is guaranteed to converge to a global minimizer of the function.  A function is &lt;em&gt;concave&lt;/em&gt; if its negative is convex &amp;ndash; these functions look like upside down bowls, and gradient &lt;em&gt;ascent&lt;/em&gt; will converge to a global &lt;em&gt;maximizer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;How do we determine when a quadratic form is convex?  Without loss of generality, we consider simply the function \(f(x) = \frac{1}{2}x^{\mathsf{T}} Q x\) and calculate (notice that we need not resort to computing second order derivatives!):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
&amp;amp;\ f(tx + (1 - t)y) - tf(x) - (1 - t)f(y)\\
&amp;amp;= \frac{1}{2}t^2 x^{\mathsf{T}} Q x + t(1 - t) x^{\mathsf{T}} Q y + \frac{1}{2}(1 - t)^2 y^{\mathsf{T}} Q y - \frac{1}{2}tx^{\mathsf{T}}Q x - \frac{1}{2}(1 - t) y^{\mathsf{T}} Q y\\
&amp;amp;= \frac{1}{2}(t^2 - t) x^{\mathsf{T}} Q x + t(1 - t) x^{\mathsf{T}} Q y + \frac{1}{2}\bigl((1 - t)^2 - (1 - t)\bigr)y^{\mathsf{T}} Q y\\
&amp;amp;= -\frac{1}{2}t(1 - t) x^{\mathsf{T}} Q x + t(1 - t) x^{\mathsf{T}} Q y - \frac{1}{2}t (1 - t) y^{\mathsf{T}} Q y \\
&amp;amp;= -\frac{1}{2}t(1 - t) \bigl[x^{\mathsf{T}} Q x - 2x^{\mathsf{T}} Q y + y^{\mathsf{T}} Q y\bigr] \\
&amp;amp;= -\frac{1}{2}t(1 - t) (x - y)^{\mathsf{T}} Q (x - y)\\
&amp;amp;\overset{(a)}{\le} 0,
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where if the final inequality \((a)\) holds over all \(x, y, t\), then we will have verified the definition of convexity for the function \(f\).  Since \(t \in (0, 1)\) it must be that \(t(1 - t) \in (0, 1)\) as well, and because \(x, y\) appear only as the difference \((x - y)\), we can consider simply the condition where \(\forall z:\ z^{\mathsf{T}} Q z \ge 0\).  This is exactly what it means for the matrix \(Q\) to be &lt;em&gt;positive semi-definite&lt;/em&gt;, denoted \(Q \succeq 0\).&lt;/p&gt;
&lt;p&gt;Thus (also using the fact that a convex function plus the linear function \(x \mapsto q^{\mathsf{T}} x\) is still convex), a quadratic form \(f\) is convex &lt;em&gt;if and only if&lt;/em&gt; \(Q \succeq 0\).  If the matrix \(Q\) is both non-singular and positive semi-definite, then it is &lt;em&gt;positive-definite&lt;/em&gt; written as \(Q \succ 0\), which also means that the inequality in the definition of convexity holds strictly, and we call the function &lt;em&gt;strictly convex&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark:&lt;/strong&gt; It is worth noting here that every quadratic form associated with a Gaussian random variable is a convex quadratic form (since variance matrices are positive semidefinite).  Moreover, for smooth functions, they are locally convex in the region of minimizers, and the local quadratic approximations of convex functions are themselves always convex.  As well, the quadratic form $\frac{1}{2}||Ax||_2^2 = x^{\mathsf{T}} A^{\mathsf{T}} A x$ will always be convex as well since $A^{\mathsf{T}} A \succeq 0$.  Convex quadratic forms arise almost as often as quadratic forms themselves!&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;minimizing-quadratic-forms&#34;
    &gt;Minimizing Quadratic Forms&lt;a href=&#34;#minimizing-quadratic-forms&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;From the representation of the quadratic form \(f\) after completing the square \[f(x) = \frac{1}{2}(x + Q^{-1} q)^{\mathsf{T}} Q (x + Q^{-1} q) + \kappa - q^{\mathsf{T}} Q^{-1} q,\] it appears rather immediate to identify a minimizer: \[x^\star = - Q^{-1} q.\]  However, we must be careful &amp;ndash; in making this claim we have already assumed that a minimizer &lt;em&gt;exists,&lt;/em&gt; and the existence of minimizers should not be taken lightly.  If the matrix \(Q\) is indefinite (&lt;em&gt;i.e.,&lt;/em&gt; \(Q\) nor \(-Q\) are positive semi-definite), then the point \(-Q^{-1} q\) may be only a &lt;em&gt;saddle point&lt;/em&gt; and in fact no minimizer exists: \(\underset{x \in \mathbb{R}^n}{\text{inf}}\ f(x) = -\infty\).  The question of existence in infinite dimensions is &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3FBRoKD&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;incredibly interesting&lt;/a
&gt;
, but it is straightforward in the present finite-dimensional case: we need only eliminate the possibility that the infimum is \(-\infty\) by the strict convexity assumption \(Q \succ 0\) (more generally, we can have \(Q \succeq 0\) and \(q \in \mathcal{R}(Q)\)).  If \(Q \prec 0\), then \(x^\star\) will be a &lt;em&gt;maximizer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The actual value of the function \(f\) at the minimizer can also be easily identified by inspection after completing the square:&lt;/p&gt;
&lt;p&gt;\[f^\star := f(x^\star) = -q^{\mathsf{T}} Q^{-1} q + \kappa.\]&lt;/p&gt;
&lt;p&gt;It is an interesting observation that this is a &lt;em&gt;concave&lt;/em&gt; quadratic form in \(q\).&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;the-shape-of-a-quadratic-form&#34;
    &gt;The Shape of a Quadratic Form&lt;a href=&#34;#the-shape-of-a-quadratic-form&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;We can get some intuition about convex and non-convex quadratic forms from the contour plots below.  In the convex case, the function is a prototypical &amp;ldquo;bowl-shaped&amp;rdquo; function with a clearly defined minimizer, and the contour lines are concentric ellipses.  In the non-convex case, the function is hyperbolic and the only minimizing sequences diverge.  In this hyperbolic case, the point \(-Q^{-1} q\) is a saddle point.&lt;/p&gt;


&lt;p&gt;The prototypical hyperbolic function is \(f(x, y) = xy\).  However, it is clearly possible for functions containing \(xy\) terms to still be convex, &lt;em&gt;e.g.,&lt;/em&gt; \(f(x, y) = \frac{1}{2}(x^2 - xy + y^2)\) (the function on the left in the figure), but not \(f(x, y) = \frac{1}{2}(x^2 - 2xy + y^2)\) (the function on the right).  Whether or not the function contains the pernicious hyperbolic behaviour comes down to the question of whether or not \(Q \succeq 0\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;subspace-constraints&#34;
    &gt;Subspace Constraints&lt;a href=&#34;#subspace-constraints&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;There are many important cases, particularly in optimization, where we want to restrict the domain of the function \(f\) to some subspace (actually, an &lt;em&gt;affine&lt;/em&gt; space, which may include a constant offset from \(0\)) \(\mathbb{V} \subseteq \R^n\), of some dimension strictly less than \(n\), &lt;em&gt;i.e.&lt;/em&gt;, \(f: \mathbb{V} \rightarrow \R\).  In practice, this typically arises through linear constraints \(Ax = b\) on the variable \(x\), where \(A \in \R^{n \times m}\).  The representation \(\mathbb{V} = \{x\ |\ Ax = b\}\) however is not analytically insightful.&lt;/p&gt;
&lt;p&gt;To obtain a more friendly representation of the space \(\mathbb{V}\), take a (compact) &lt;a
    class=&#34;link&#34;
    href=&#34;https://souravsengupta.com/cds2016/lectures/Strang_Paper1.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Singular Value Decomposition&lt;/a
&gt;
 of the matrix: \(A = U\Sigma V^{\mathsf{T}}\) wherein \(U \in \R^{n \times r}\), \(\Sigma \in \R^{r \times r}\) and \(V \in \R^{m \times r}\); I use \(r \le \text{min}(m, n)\) to indicate the rank of the matrix \(A\).  It is essential to assume that \(b \in \mathcal{R}(A)\), &lt;em&gt;i.e.&lt;/em&gt;, that \(b\) is in the range of \(A\) &amp;ndash; if this is not the case then the subspace \(\mathbb{V}\) is ill-defined.  Now, recall that the columns of \(U\) constitute an orthonormal basis of \(\mathcal{R}(A)\) of dimension \(r\) and that the columns of \(V\) constitute an orthonormal basis for \(\mathcal{N}(A)^\perp\), the orthogonal complement of the nullspace of \(A\), which is of dimension \(n - r\).  Operationally, this means that \(U^{\mathsf{T}} U = I_r\) and \(V^{\mathsf{T}} V = I_r\) (but definitely not, at least in general, that \(UU^{\mathsf{T}} = I_n\) or \(VV^{\mathsf{T}} = I_m\)).  Thus, we can simplify the representation of the subspace \(\mathbb{V}\) as in:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbb{V}
&amp;amp;= \{x\ |\ Ax = b\}\\
&amp;amp;= \{x\ |\ U\Sigma V^{\mathsf{T}} x = b\}\\
&amp;amp;\overset{(a)}{=} \{x\ |\ V^{\mathsf{T}} x = \Sigma^{-1} U^{\mathsf{T}} b\}\\
&amp;amp;\overset{(b)}{=} \{V \Sigma^{-1} U^{\mathsf{T}} b + \overline{V}u\ |\ u \in \R^{n - r}\}\\
&amp;amp;\overset{( c)}{=} \{A^{\dagger} b + \overline{V}u\ |\ u \in \R^{n - r}\},
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where in \((a)\) it is always the case that \(\Sigma\) is invertible (and also diagonal) for a compact SVD and in \((b)\) the matrix \(\overline{V} \in \R^{n \times (n - r)}\) is a matrix whose columns form a basis for the nullspace of \(A\) (which can be obtained, for example, by running a full SVD on \(A\)), and in \(( c)\) we use the definition of the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Moore-Penrose inverse&lt;/a
&gt;
 of \(A\), denoted by \(A^{\dagger} = V\Sigma^{-1} U^{\mathsf{T}}\).  The vector \(\bar{x} := V \Sigma^{-1} U^{\mathsf{T}} b\) is usually taken to be the nominal solution of \(Ax = b\) (it is easy to verify that it is a solution) and the remainder of the degrees of freedom are provided by the span of the columns of \(\overline{V}\).  This makes more clear that \(\mathbb{V}\) is an affine space: the subspace \(\{\bar{V}u\ |\ u \in \R^{n - r}\}\) is offset from \(0\) by \(A^\dagger b\).&lt;/p&gt;
&lt;p&gt;From here, a quadratic form \(f\) subject to the constraint \(x \in \mathbb{V}\) can be represented simply by modifying the parameters \(Q, q, \kappa\) and using the variable \(u\) from which the original \(x\) variable can be recovered by \(x(u) = A^{\dagger} b + \overline{V}u\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
f(x)
&amp;amp;= \frac{1}{2}x^{\mathsf{T}} Q x + q^{\mathsf{T}} x + \frac{1}{2}\kappa; \quad Ax = b\\
\rightarrow f(x(u)) &amp;amp;= \frac{1}{2}(A^{\dagger} b + \overline{V}u)^{\mathsf{T}} Q (A^{\dagger} b + \overline{V}u) + q^{\mathsf{T}} (A^{\dagger} b + \overline{V}u) + \frac{1}{2}\kappa\\
\rightarrow \tilde{f}(u) &amp;amp;= \frac{1}{2}u^{\mathsf{T}} \overline{V}^{\mathsf{T}} Q \overline{V} u + \bigl((\overline{V}^{\mathsf{T}} q + \overline{V}^{\mathsf{T}} QA^\dagger b \bigr)^{\mathsf{T}} u + q^{\mathsf{T}} A^\dagger b + \frac{1}{2}\kappa + \frac{1}{2} (A^\dagger b)^{\mathsf{T}} Q (A^{\dagger} b)\\
&amp;amp;= \frac{1}{2}u^{\mathsf{T}} \tilde{Q} u + \tilde{q}^{\mathsf{T}} u + \tilde{\kappa}\\
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(\tilde{Q} = \overline{V}^{\mathsf{T}} Q \overline{V}\), \(\tilde{q} = \overline{V}^{\mathsf{T}} (q + QA^\dagger b)\) and \(\tilde{\kappa} = \kappa + (A^\dagger b)^{\mathsf{T}} Q (A^{\dagger} b)\).  Thus, the quadratic form restricted to a subspace \(\mathbb{V}\) is yet again just another quadratic form.  Moreover, there are computationally tractable means of explicitly representing the subspace \(\mathbb{V}\) when it is described through the linear system \(Ax = b\).&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;convex-and-concave-subspaces&#34;
    &gt;Convex and Concave Subspaces&lt;a href=&#34;#convex-and-concave-subspaces&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;To get some further geometric sense of the function \(f\), one will usually appeal to the eigendecomposition of the matrix \(Q\).  Since it is symmetric, it is guaranteed to be orthogonally diagonalizable, and admits of real eigenvalues.  This is &lt;a
    class=&#34;link&#34;
    href=&#34;https://rjtk.github.io/posts/generalized-eigenvalue-problems-and-trace-optimization/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;The Spectral Theorem&lt;/a
&gt;
.  Specifically, we have \[Q = V\Lambda V^{\mathsf{T}} = \sum_{i = 1}^N \lambda_i v_i v_i^{\mathsf{T}} \] where \(V \in \mathbb{R}^{n \times n}\) is an orthogonal matrix of eigenvectors, and \(\Lambda = \mathsf{Dg}(\lambda_1, \ldots, \lambda_n)\) is a diagonal matrix containing the associated eigenvalues.  The space can be split into three subspaces, call them&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
S_+ &amp;amp;= \mathsf{span}\bigl\{v_i\ |\ \lambda_i &amp;gt; 0\}\\
S_- &amp;amp;= \mathsf{span}\bigl\{v_i\ |\ \lambda_i &amp;lt; 0\}\\
S_0 &amp;amp;= \mathsf{span}\bigl\{v_i\ |\ \lambda_i = 0\}.
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;The subspace \(S_+\) might be called the &amp;ldquo;convex subspace&amp;rdquo; since the function \(x \mapsto \frac{1}{2} x^{\mathsf{T}} Q x\) is convex when restricted to \(x \in S_+\), and \(S_-\) might be called the &amp;ldquo;concave subspace&amp;rdquo; since \(x \mapsto \frac{1}{2} x^{\mathsf{T}} Q x\) is concave when restricted to \(x \in S_-\).  The subspace \(S_0\) is the linear subspace since \(\forall x \in S_0:\ \frac{1}{2} x^{\mathsf{T}} Q x = 0\) &amp;ndash; when a quadratic form is restricted to \(S_0\) it degenerates into a linear function.  The subspace \(S_0 = \{0\}\) if \(Q\) is non-singular, and \(S_- = \{0\}\) if \(Q \succeq 0\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark:&lt;/strong&gt; There are practical reasons why these subspace distinctions are interesting.  In some natural problems, we may encounter a matrix $Q$ which is indefinite, but where certain problem constraints guarantee that we are fully confined to the convex subspace $S_+$.  Therefore, given the constraints of the problem, the quadratic form associated with $Q$ is &amp;ldquo;effectively&amp;rdquo; convex.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Using ideas from the previous section, we can animate 2-d slices of a quadratic form in \(\R^4\) where the 2-d slices are constructed to smoothly vary across convex and concave subspaces.  The transition between the two, where the subspace passes through \(S_+\) and \(S_-\) could be termed a &lt;em&gt;hyperbolic&lt;/em&gt; subspace of the quadratic form.&lt;/p&gt;


&lt;p&gt;I created this animation by constructing the matrix \(Q = 2q_1 q_1^{\mathsf{T}} + q_1 q_1^{\mathsf{T}} - p_1 p_1^{\mathsf{T}} - 2 p_2 p_2^{\mathsf{T}}\) where \(q_1, p_1, q_2, p_2\) form an orthogonal basis of \(\mathbb{R}^4\), and then using a matrix \[H(t) = [\frac{3}{2}(\frac{2}{3} - t)_+ q_1 + 3(t - \frac{2}{3})_+ p_1, \frac{3}{2}(t - \frac{1}{3})_+ p_2 + 3(\frac{1}{3} - t)_+ q_2]\] to project \(x = H(t) u\) onto convex, hyperbolic, and concave subspaces depending on the value of \(t\).  The plot is over \(u \in \mathbb{R}^2\) and animated over \(t \in [0, 1]\).&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;linear-algebra&#34;
    &gt;Linear Algebra&lt;a href=&#34;#linear-algebra&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The linear algebra related to quadratic forms largely comes down to the linear algebra associated with the symmetric matrix \(Q\).  The main aspects are the Cholesky factorization and Schur complements.  The Cholesky decomposition is one of the most important (and fast!) matrix factorizations available, and the Schur complement arises in many different ways.  Moreover, these two aspects are intimately linked.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;schur-complements&#34;
    &gt;Schur Complements&lt;a href=&#34;#schur-complements&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Quadratic forms often arise as &lt;em&gt;bivariate&lt;/em&gt; quadratic forms (or more generally, multivariate quadratic forms with natural distinctions between different &lt;em&gt;blocks&lt;/em&gt; of variables).  For now, consider the function of \(x \in \mathbb{R}^{n_x}\) and \(y \in \mathbb{R}^{n_y}\) given by&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
f(x, y) &amp;amp;= \frac{1}{2}\begin{bmatrix}x\\ y\\ 1\end{bmatrix}^{\mathsf{T}}\begin{bmatrix}P &amp;amp; C &amp;amp; p\\ C^{\mathsf{T}} &amp;amp; Q &amp;amp; q\\ p^{\mathsf{T}} &amp;amp; q^{\mathsf{T}} &amp;amp; \kappa \end{bmatrix}\begin{bmatrix}x\\ y\\ 1\end{bmatrix}\\
&amp;amp;= \frac{1}{2}x^{\mathsf{T}}Px + \frac{1}{2}y^{\mathsf{T}}Qy + x^{\mathsf{T}} C y + p^{\mathsf{T}} x + q^{\mathsf{T}} y + \kappa
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where I&amp;rsquo;ll assume \(P, Q\) are both symmetric, that \(P\) is non-singular, and that \(C\) is such that the overall block matrix is symmetric positive definite.  These assumptions mean that the function \(f(x, y)\) is jointly convex in the variables \((x, y)\).&lt;/p&gt;
&lt;p&gt;There are two other interesting functions associated with \(f\), namely, \[y \mapsto \underset{x}{\text{inf}}\ f(x, y),\] and \[x \mapsto \underset{y}{\text{inf}}\ f(x, y).\]&lt;/p&gt;
&lt;p&gt;The computation of these functions can be done through a mechanical procedure called &lt;em&gt;Schur-complementation&lt;/em&gt;.  The idea of the Schur complement can serve as a natural &amp;ldquo;chunk&amp;rdquo; in one&amp;rsquo;s cognition and enables easy manipulation of complicated expressions.&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;partial-minimization&#34;
    &gt;Partial Minimization&lt;a href=&#34;#partial-minimization&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;The above functions are those formed from &lt;em&gt;partial minimization&lt;/em&gt; of the function \(f\), and for concreteness I&amp;rsquo;ll focus on the function $g(y) := \underset{x}{\text{inf}}\ f(x, y).$  We know from the previous section that we can guarantee that a minimizer exists if \(P \succ 0\), so we will also make this assumption here to avoid degeneracy.&lt;/p&gt;
&lt;p&gt;To identify the minimizer of \(f\) with respect to \(x\), we need only make the identifications \(q \leftarrow Cy + p\) and \(\kappa \leftarrow y^{\mathsf{T}} Q y + q^{\mathsf{T}} y + \kappa\) in the original general definition of a quadratic form to recognize that \(x^\star(y) = -P^{-1} (Cy + p)\).  An explicit formula for the function \(g\) is then obtained through the calculation&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
g(y)
&amp;amp;= \frac{1}{2}y^{\mathsf{T}} Q y - y^{\mathsf{T}} C^{\mathsf{T}} P^{-1} (C y + p) + q^{\mathsf{T}} y + \kappa \\
&amp;amp;= \frac{1}{2}y^{\mathsf{T}} (Q - C^{\mathsf{T}} P^{-1} C) y + (q - C^{\mathsf{T}}P^{-1} p)^{\mathsf{T}} y + \kappa\\
&amp;amp;= \frac{1}{2}\begin{bmatrix}y\\ 1 \end{bmatrix}^{\mathsf{T}} \begin{bmatrix}Q - C^{\mathsf{T}} P^{-1} C &amp;amp; (q - C^{\mathsf{T}}P^{-1} p)\\ (q - C^{\mathsf{T}}P^{-1} p)^{\mathsf{T}} &amp;amp; \kappa \end{bmatrix} \begin{bmatrix}y\\ 1\end{bmatrix}.
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;The beauty of this partial minimization is that \(g\) is &lt;em&gt;still a quadratic form&lt;/em&gt;.  If it is still convex (we&amp;rsquo;ll check this next!) then we could say that the set of positive definite quadratic forms is &lt;em&gt;closed&lt;/em&gt; under the operation of partial minimization.&lt;/p&gt;
&lt;p&gt;The formulas above seem rather complicated at first.  However, they follow a completely mechanical pattern and you can write down analytic expressions for cartoonishly complicated block quadratic forms with simple notation, and understand at a glance exactly what is going on (even if you&amp;rsquo;re not reading every single equation in detail).&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;definiteness-conditions&#34;
    &gt;Definiteness Conditions&lt;a href=&#34;#definiteness-conditions&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;What we have discovered through exploring this is called a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Schur_complement&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;Schur Complement&lt;/em&gt;&lt;/a
&gt;
.  Suppose for simplicity that \(p = q = 0\) and \(\kappa = 0\) and for sake of notation that \[\mathcal{Q} := \begin{bmatrix}P &amp;amp; C\\ C^{\mathsf{T}} &amp;amp; Q\end{bmatrix}.\]  Then, \(g(y) = y^{\mathsf{T}}(Q - C^{\mathsf{T}} P^{-1} C)y\) and the matrix \(Q - C^{\mathsf{T}} P^{-1} C\) is called &lt;em&gt;The Schur Complement of \(P\) in \(\mathcal{Q}\)&lt;/em&gt; and it is usually denoted \(\mathcal{Q} / P\).  There is also a Schur complement of \(Q\) in \(\mathcal{Q}\) given by \(\mathcal{Q} / Q := P - CQ^{-1}C^{\mathsf{T}}\).  The way to remember which is which is to think of the &amp;ldquo;\(/P\)&amp;rdquo; as a form of division, analogous to \(P^{-1}\) (now you remember which matrix is being inverted) and then to just match up the shapes of \(C\) and \(C^{\mathsf{T}}\) with \(P\) or \(Q\) to figure out if you should write \(C^{\mathsf{T}} P^{-1} C\) or \(CP^{-1} C^{\mathsf{T}}\).&lt;/p&gt;
&lt;p&gt;Forming the matrix \(\mathcal{Q} / P\) requires at least that \(P\) be non-singular.  If, in addition, it is positive definite \(P \succ 0\), then \(g(y) = y^{\mathsf{T}} (\mathcal{Q} / P) y\) is the partial minimizer of the whole quadratic form with respect to \(x\).  In order for \(g(y)\) to have a minimizer (&lt;em&gt;i.e.,&lt;/em&gt; the infimum is not \(-\infty\)) then the Schur complement must be positive-definite as well: \(\mathcal{Q} / P \succ 0\).  This, combined with the the fact that \[\underset{x}{\text{inf}}\ \underset{y}{\text{inf}}\ f(x, y) = \underset{y}{\text{inf}}\ \underset{x}{\text{inf}}\ f(x, y) = \underset{x, y}{\text{inf}}\ f(x, y),\] implies the following methods of checking definiteness:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If \(P \succ 0\) and \(\mathcal{Q} / P \succ 0\), then \(\mathcal{Q} \succ 0\).&lt;/li&gt;
&lt;li&gt;If \(\mathcal{Q} \succ 0\) and \(P\) is non-singular, then \(P \succ 0\) and \(\mathcal{Q} / P \succ 0\).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These relations have important theoretical use.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;square-roots-and-cholesky-factorization&#34;
    &gt;Square Roots and Cholesky Factorization&lt;a href=&#34;#square-roots-and-cholesky-factorization&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;There is a natural way of discovering the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Cholesky_decomposition&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Cholesky Decomposition&lt;/a
&gt;
 by continuing the minimization of the block quadratic form \(f(x, y)\).  Indeed, computing the minimizer \((x, y)\) of this function involves solving the linear system&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\begin{bmatrix}P &amp;amp; C\\ C^{\mathsf{T}} &amp;amp; Q\end{bmatrix} \begin{bmatrix}x\\ y \end{bmatrix} = \begin{bmatrix}p\\ q \end{bmatrix},
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;involving the matrix \(\mathcal{Q}\) discussed above.  Following the idea of (block) Gauss-Jordan elimination on this matrix, the Schur complement makes another appearance:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\begin{bmatrix}P &amp;amp; C\\ C^{\mathsf{T}} &amp;amp; Q\end{bmatrix}
&amp;amp;= \begin{bmatrix}I &amp;amp; 0\\ C^{\mathsf{T}}P^{-1} &amp;amp; I\end{bmatrix}\begin{bmatrix}P &amp;amp; C\\ 0 &amp;amp; Q - C^{\mathsf{T}} P^{-1} C\end{bmatrix},
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a block LU decomposition of \(\mathcal{Q}\), having a (dense) Schur-complement in the lower-right entry of the block upper triangular matrix.  Now, rather than a &lt;em&gt;block&lt;/em&gt; triangular decomposition, what we would really like to have is a bona-fide triangular decomposition \(\mathcal{Q} = LU\) where \(L\) is lower-triangular and \(U\) is upper triangular.  Moreover, in analogy with non-negative numbers, which have square-roots, we would like to ask for even more of a positive definite matrix \(\mathcal{Q} \succ 0\) (the positive semi-definite case is possible, but more involved) &amp;ndash; we would like an LU-decomposition \(\mathcal{Q} = LL^{\mathsf{T}}\), &lt;em&gt;i.e.&lt;/em&gt;, where \(U = L^{\mathsf{T}}\), which is a &lt;em&gt;matrix square root&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Operating under the standing assumption that \(\mathcal{Q} \succ 0\), and that \(P\) is non-singular (hence \(P \succ 0\) by the above), we will obtain this factorization by induction.  Make the &lt;em&gt;induction hypothesis&lt;/em&gt; that any positive definite matrix admits of a lower-triangular square root and let \(P = L_{11}L_{11}^{\mathsf{T}}\).  As well, using the induction hypothesis again, along with the definiteness theorem described in the last section, we know that the Schur complement \(\mathcal{Q} / P = Q - C^{\mathsf{T}} P^{-1} C \succ 0\) is also positive definite and thus admits another decomposition \(\mathcal{Q} / P = L_{22}L_{22}^{\mathsf{T}}\).  We can now write&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathcal{Q} &amp;amp;= \begin{bmatrix}I &amp;amp; 0\\ C^{\mathsf{T}}P^{-1} &amp;amp; I\end{bmatrix}\begin{bmatrix}P &amp;amp; C\\ 0 &amp;amp; Q - C^{\mathsf{T}} P^{-1} C\end{bmatrix}\\
&amp;amp;= \begin{bmatrix}I &amp;amp; 0\\ C^{\mathsf{T}} (L_{11} L_{11}^{\mathsf{T}})^{-1} &amp;amp; I\end{bmatrix}\begin{bmatrix}L_{11} L_{11} &amp;amp; C\\ 0 &amp;amp; L_{22}L_{22}^{\mathsf{T}} \end{bmatrix}\\
&amp;amp;= \begin{bmatrix}L_{11} &amp;amp; 0\\ C^{\mathsf{T}} L_{11}^{-\mathsf{T}} &amp;amp; L_{22} \end{bmatrix}\begin{bmatrix}L_{11} &amp;amp; 0\\ C^{\mathsf{T}} L_{11}^{-\mathsf{T}} &amp;amp; L_{22} \end{bmatrix}^{\mathsf{T}},
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a Cholesky decomposition obtained recursively from the decomposition of \(P\) and the Schur complement \(\mathcal{Q} / P\).  Beginning the recursion with \(P = \mathcal{Q}_{11}\) (the upper left entry) and \(L_{11} = \sqrt{P}\) leads to a practical &lt;em&gt;algorithm&lt;/em&gt; for computing the decomposition.  The definiteness condition of \(\mathcal{Q} \succ 0\) is essential to continuing this process since otherwise we would not be able to recurse on the Schur complement \(\mathcal{Q} / P\).  What would happen in practice is that we would try to divide by zero or to take the square root of a negative number.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;39
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; numpy &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; math &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; sqrt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FLOAT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;float64
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;LinAlgError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;cholesky&lt;/span&gt;(A):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;An extremely terrible Cholesky implementation.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    A &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;array(A, dtype&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;FLOAT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;shape[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]  &lt;span style=&#34;color:#6272a4&#34;&gt;# A = [[P; C]; [C&amp;#39;; Q]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;shape) &lt;span style=&#34;color:#ff79c6&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;shape[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;!=&lt;/span&gt; n:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;A must be a square matrix but got &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;shape&lt;span style=&#34;color:#f1fa8c&#34;&gt;=}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;all(A &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;T):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;A must be a symmetric matrix!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    L &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;zeros_like(A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Q &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;copy(A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        P &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;float&lt;/span&gt;(Q[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        C &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Q[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;:][&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, :]  &lt;span style=&#34;color:#6272a4&#34;&gt;# a row vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Q &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Q[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;:, &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;:]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            L[i, i] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; sqrt(P)  &lt;span style=&#34;color:#6272a4&#34;&gt;# This will detect negative eigenvalues&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;except&lt;/span&gt; ValueError:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; LinAlgError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Matrix A is not positive definite&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        L[i &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; :, i] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; L[i, i]) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; C
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Q &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Q &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; C&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;T &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; (C &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; P)  &lt;span style=&#34;color:#6272a4&#34;&gt;# Division by P will fail on a *semi*-definite matrix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; L
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;random&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;randint(&lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;30&lt;/span&gt;, size&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1000&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    A &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;random&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;normal(size&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;(n, n))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    A &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; A &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;T
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    L &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cholesky(A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;assert&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;allclose(L &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; L&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;T, A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Passed :)&amp;#34;&lt;/span&gt;  &lt;span style=&#34;color:#6272a4&#34;&gt;# org-mode recognizes this odd &amp;#39;return&amp;#39; as the output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Passed :)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Testing Definiteness): The Cholesky decomposition is not only guaranteed to succeed if $\mathcal{Q} \succ 0$, it is also &lt;em&gt;guaranteed to fail&lt;/em&gt; if $\mathcal{Q} \succ 0$ does not hold.  Thus, computationally, the Cholesky decomposition serves as a reliable means of testing the definiteness of a matrix: a matrix $A$ is positive definite &lt;em&gt;if and only if&lt;/em&gt; a Cholesky decomposition algorithm successfully terminates in a factorization $A = LL^{\mathsf{T}}$.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Computing Schur Complements): Even though we&amp;rsquo;ve derived the Cholesky decomposition from the Schur complement, we can also go the other way.  That is, the Cholesky decomposition algorithm can be used to compute Schur complements.  Moreover, this results directly in the Cholesky decomposition of the Schur complement itself, and therefore in a representation of $\mathcal{Q} / P$ which provides a numerical guarantee on the positive definiteness of the Schur complement.  To see this, consider the calculations below:&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathcal{Q}
&amp;amp;= \begin{bmatrix}P &amp;amp; C\\ C^{\mathsf{T}} &amp;amp; Q\end{bmatrix}\\
&amp;amp;= \begin{bmatrix}L_{11} &amp;amp; 0\\ L_{12} &amp;amp; L_{22} \end{bmatrix}\begin{bmatrix}L_{11}^{\mathsf{T}} &amp;amp; L_{12}^{\mathsf{T}} \\ 0 &amp;amp; L_{22}^{\mathsf{T}} \end{bmatrix}\\
&amp;amp;= \begin{bmatrix}L_{11}L_{11}^{\mathsf{T}} &amp;amp; L_{11}L_{12}^{\mathsf{T}} \\ L_{12}L_{11}^{\mathsf{T}} &amp;amp; L_{12}L_{12}^{\mathsf{T}} + L_{22}L_{22}^{\mathsf{T}} \end{bmatrix}
\end{aligned}
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;whereby we can calculate the Schur complement $$\mathcal{Q} / P = L_{12}L_{12}^{\mathsf{T}} + L_{22}L_{22}^{\mathsf{T}} - L_{12}L_{11}^{\mathsf{T}} (L_{11}L_{11}^{\mathsf{T}})^{-1} L_{11}L_{12} = L_{22}L_{22}^{\mathsf{T}}.$$  Thus, the Cholesky decomposition of $\mathcal{Q} / P$ is nothing but the bottom-right block of the Cholesky decomposition of the block matrix $\mathcal{Q}$.  The advantage of this method is in numerical stability: the subtraction $Q - C^{\mathsf{T}} P^{-1} C$ not only involves a pesky inversion of the matrix $P$, but it also involves the &lt;em&gt;difference&lt;/em&gt; of two positive definite matrices, which, due to numerical roundoff, can result in a matrix which is in actuality indefinite.  Using this method, if the Cholesky algorithm succeeds, then we are in the clear.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;probability-and-statistics&#34;
    &gt;Probability and Statistics&lt;a href=&#34;#probability-and-statistics&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Quadratic forms appear in many places in statistic, largely as a result of computing second order statistics (the mean and the variance).  Remarkably, though I derived them from examples in optimization, both the Schur complement and the Cholesky factorization play important roles in this theory as well.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;computations-with-the-mean-and-the-variance&#34;
    &gt;Computations with the Mean and the Variance&lt;a href=&#34;#computations-with-the-mean-and-the-variance&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Two of the most important statistics of an $\mathbb{R}$-valued random variable, call it \(X\), are the mean and the variance \(\mu = \mathbb{E}X\) and \(\sigma^2 = \mathbb{E}(X - \mu)^2\).  Taken together, \((\mu, \sigma^2)\) are the &lt;em&gt;second-order statistics&lt;/em&gt; of \(X\).  The mean value measures the central tendency of \(X\), serving as one estimate of its &amp;ldquo;typical&amp;rdquo; value, and the variance \(\sigma^2\) is the average squared distance of \(X\) from its mean value.  When \(\sigma^2\) is small, then the probability that \(X\) is close to \(\mu\) is high, and when \(\sigma^2\) is large, that probability is small.&lt;/p&gt;
&lt;p&gt;For vectors of random variables \(\mathbf{X} = (X_1, \ldots, X_n) \in \mathbb{R}^n\) the mean is simply defined element-wise \[\mathbb{E}\mathbf{X} = \begin{bmatrix}
\mathbb{E}X_1\\ \vdots\\ \mathbb{E}X_n\\
\end{bmatrix} = \begin{bmatrix}\mu_1\\ \vdots \\ \mu_n \end{bmatrix} = \bm{\mu}.\]  The variance of the vector random variable becomes somewhat more complicated, as the best definition is now a &lt;em&gt;matrix&lt;/em&gt;.  This &lt;em&gt;variance matrix&lt;/em&gt;, usually denoted \(\Sigma \in \mathbb{R}^{n \times n}\), is defined through an outer-product of the centered random variables \[\Sigma = \mathbb{E}(\mathbf{X} - \mathbf{\mu})(\mathbf{X} - \mathbf{\mu})^{\mathsf{T}},\] and includes all of the individual (&lt;em&gt;i.e.&lt;/em&gt;, marginal) variance \(\Sigma_{ii} = \mathbb{E}(X_i - \mu_i)^2\), but also all of the &lt;em&gt;co-variances&lt;/em&gt; \(\Sigma_{ij} = \mathbb{E}(X_i - \mu_i)(X_j - \mu_j)\).  These covariance terms provide a measure of how the random variables in the vector tend to move together &amp;ndash; &lt;em&gt;i.e.&lt;/em&gt;, they provide a quantitative measure of dependence.  The reason for this interpretation may become clear when we look at estimating one random variable given another.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;sampling&#34;
    &gt;Sampling&lt;a href=&#34;#sampling&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;It is easy to see that the covariance matrix is symmetric &amp;ndash; indeed, \(\Sigma_{ij} = \mathbb{E}(X_i - \mu_i)(X_j - \mu_j) = \mathbb{E}(X_j - \mu_j)(X_i - \mu_i) = \Sigma_{ji}\).  What is somewhat less obvious is that it is also &lt;em&gt;positive semi-definite&lt;/em&gt; \(\Sigma \succeq 0\).  To see this:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
a^{\mathsf{T}} \Sigma a^{\mathsf{T}}
&amp;amp;= a^{\mathsf{T} }\bigl[\mathbb{E}(\mathbf{X} - \bm{\mu})(\mathbf{X} - \bm{\mu})^{\mathsf{T}} \bigr] a\\
&amp;amp;\overset{(a)}{=} \mathbb{E} a^{\mathsf{T}} (\mathbf{X} - \bm{\mu})(\mathbf{X} - \bm{\mu})^{\mathsf{T}} a\\
&amp;amp;\overset{(b)}{=} \mathbb{E} ||a^{\mathsf{T}} (\mathbf{X} - \bm{\mu})||_2^2 \ge 0,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \((a)\) uses the &lt;em&gt;linearity of expectation&lt;/em&gt; \(a\mathbb{E}X = \mathbb{E}[aX]\) and \((b)\) uses \(a^{\mathsf{T}} a = ||a||_2^2 := \sum_{i = 1}^n a_i^2\).  For simplicity, I will assume that \(\Sigma\) is &lt;em&gt;positive definite&lt;/em&gt; \(\Sigma \succ 0\), which is generally the case &amp;ndash; if \(\Sigma\) is not positive definite, then it can be shown that (at least) two components of the vector \(\mathbf{X}\) must be perfectly linearly related, &lt;em&gt;e.g.&lt;/em&gt;, \(X_1 = X_2 - X_3\), a case we simply exclude.&lt;/p&gt;
&lt;p&gt;Since \(\Sigma \succ 0\), it must admit a Cholesky factorization \(\Sigma = LL^{\mathsf{T}}\).  This factorization can be used computationally to &lt;em&gt;sample&lt;/em&gt; random variables with a prescribed variance matrix.  As long as it is possible to sample independent random variables with variance \(1\), then we can construct a random vector with variance equal to the \(n \times n\) identity matrix \(I_n\) (how?) &amp;ndash; call this vector \(\mathbf{Z}\).  Supposing that \(\mathbf{Z}\) has zero mean \(\mathbb{E}\mathbf{Z} = 0\) (adding back a constant mean vector \(\bm{\mu}\) afterwards is easy) we can now transform \(\mathbf{Z}\) so that it has variance \(\Sigma\): let \(\mathbf{X} = L\mathbf{Z}\) and calculate:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbb{E}\mathbf{XX}^{\mathsf{T}}
&amp;amp;= \mathbb{E}(L\mathbf{Z})(L\mathbf{Z})^{\mathsf{T}}\\
&amp;amp;= \mathbb{E}L\mathbf{Z}\mathbf{Z}^{\mathsf{T}} L^{\mathsf{T}}\\
&amp;amp;= L\bigl[\mathbb{E}\mathbf{Z}\mathbf{Z}^{\mathsf{T}} \bigr]L^{\mathsf{T}}\\
&amp;amp;= LI_n L^{\mathsf{T}}\\
&amp;amp;= \Sigma.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Incidentally, a slight modification to the calculation above can show us that if \(\mathbf{X}\) has variance matrix \(\Sigma\), then for any matrix \(A\), the variance of \(A\mathbf{X}\) is given by \(A\Sigma A^{\mathsf{T}}\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;linear-minimum-mean-squared-error-estimation&#34;
    &gt;Linear Minimum Mean Squared Error Estimation&lt;a href=&#34;#linear-minimum-mean-squared-error-estimation&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;To understand more about the meaning of the variance matrix, let&amp;rsquo;s see how it appears in a particularly important quadratic optimization problem: the linear minimum mean-squared error estimator (LMMSE estimator).  The setup for this problem is that we have a joint random variable \((X, Y)\) where \(X\) has dimension \(m\) and \(Y\) has dimension \(n\), and where \(X, Y\) have a &lt;em&gt;known&lt;/em&gt; joint variance matrix:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\text{Var}\begin{bmatrix}X\\ Y\end{bmatrix} = \begin{bmatrix}\Sigma_X &amp;amp; R\\ R^{\mathsf{T}} &amp;amp; \Sigma_Y \end{bmatrix} := \Sigma
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;\(\Sigma \in \mathbb{S}_{++}^{n + m}\), and mean values \(\mu_X \in \mathbb{R}^m \mu_Y \in \mathbb{R}^n\).  We want to find a &amp;ldquo;gain matrix&amp;rdquo; \(K \in \mathbb{R}^{m \times n}\) and a &amp;ldquo;bias vector&amp;rdquo; \(b \in \mathbb{R}^{n}\) to solve the following problem:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{K, b}{\text{minimize}}\ \frac{1}{2}\mathbb{E}||X - KY - b||_2^2.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;The interpretation of this problem is that we are making an actual observation of \(Y\), and want to use that to make some inference about what the value of \(X\) is, given the joint second order statistics (mean and variance) of \((X, Y)\).  An example application is that \(X\) is some &amp;ldquo;state&amp;rdquo; we are interested in (&lt;em&gt;e.g.&lt;/em&gt;, temperature, position, velocity, &lt;em&gt;etc&lt;/em&gt;.) and \(Y\) is the measurement of some sensor system.&lt;/p&gt;
&lt;p&gt;To solve this problem, first start by minimizing over \(b\).  If we expand the objective we have&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\frac{1}{2}\mathbb{E} ||X - KY - b||_2^2
&amp;amp;= \frac{1}{2}\mathbb{E}||X - KY||_2^2 + \frac{1}{2}||b||_2^2 - \mathbb{E}(X - KY)^{\mathsf{T}} b\\
&amp;amp;= \frac{1}{2}\bigl((\mu_X - K\mu_Y) - b\bigr)^{\mathsf{T}} \bigl((\mu_X - K\mu_Y) - b\bigr) + \frac{1}{2}\mathbb{E}||X - KY||_2^2 - (\mu_X - K\mu_Y)^{\mathsf{T}}(\mu_X - K\mu_Y),
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;from which it is clear the optimal value is \(b = \mu_X - K\mu_Y\).  It is quite convenient that the optimal value of \(b\) makes \(X - KY - b\) have zero mean.  Because of this we can simply assume, without loss of generality, that \(\mathbb{E}X = 0\) and \(\mathbb{E}Y = 0\); indeed, we could make the substitutions \(X_0 = X - \mu_X\) &lt;em&gt;etc.&lt;/em&gt;, but this is unnecessarily cumbersome.  So, making this zero mean assumption, we again expand out the objective function&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbb{E}||X - KY||_2^2
&amp;amp;= \mathbb{E}(X - KY)^{\mathsf{T}}(X - KY)\\
&amp;amp;= \mathbb{E}\bigl( X^{\mathsf{T}} X - X^{\mathsf{T}}KY - Y^{\mathsf{T}}K^{\mathsf{T}}X - Y^{\mathsf{T}}K^{\mathsf{T}}KY \bigr)\\
&amp;amp;= \mathsf{tr}\bigl(\Sigma_X - KR^{\mathsf{T}} - R^{}K^{\mathsf{T}} + K\Sigma_Y K^{\mathsf{T}} \bigr)\\
&amp;amp;= \mathsf{tr}(K - R\Sigma_Y^{-1})\Sigma_Y(K - R\Sigma_Y^{-1})^{\mathsf{T}} + \mathsf{tr}(\Sigma_X - R \Sigma_Y^{-1} R^{\mathsf{T}}),
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where in the last step we are again completing the square.  The second trace term does not depend upon \(K\), and the first term is minimized by the choice \(K = R\Sigma_Y^{-1}\) (why?  Obtain a generalization for quadratic forms and use \(\Sigma_Y \succ 0\)).  Remarkably, the &lt;em&gt;value&lt;/em&gt; of the objective at the optimum is given exactly by&lt;/p&gt;
&lt;p&gt;\begin{equation}
\underset{K}{\text{min}}\ \mathbb{E}||X - KY||_2^2 = \mathsf{tr}(\Sigma_X - R \Sigma_Y^{-1} R^{\mathsf{T}}) = \mathsf{tr}(\Sigma / \Sigma_Y),
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is another Schur complement!  More generally, at \(K^\star = R\Sigma_Y^{-1}\) we have that \(\text{Var}(X - K^\star Y) = \Sigma / \Sigma_Y\) so that the variance matrix of the error vector \(E = X - KY\) is given exactly by a Schur complement in the original joint variance matrix.  This Schur complement is measuring the amount of variance which is removed from \(\Sigma_X\) by conditioning on \(Y\).  Another worthwhile observation is that \(\mathsf{tr}E^{\mathsf{T}} K^\star Y = \mathsf{tr}(X - R\Sigma_Y^{-1} Y)^{\mathsf{T}} R\Sigma_Y^{-1} Y = 0\), which is to say that &lt;em&gt;the estimate is orthogonal to the error&lt;/em&gt;.  This is not a coincidence and is a consequence of the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Hilbert_projection_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Projection Theorem&lt;/a
&gt;
.&lt;/p&gt;
&lt;p&gt;We can construct an illustrative example of these calculations, along with the sampling scheme of the previous section in two dimensions.  For this illustration I&amp;rsquo;m using the variance matrix \(\Sigma = \begin{bmatrix}\sigma_X^2 &amp;amp; \rho\sigma_X \sigma_Y \\ \rho \sigma_X \sigma_Y &amp;amp; \sigma_Y^2 \end{bmatrix}\) and thus the optimal gain is \(K = \rho \sigma_X \sigma_Y \times \frac{1}{\sigma_y^2} = \rho \frac{\sigma_X}{\sigma_Y}\) which leads to \(\hat{X}|Y = \rho \frac{\sigma_X}{\sigma_Y} Y\), and \(\sigma^2_{X|Y} = (1 - \rho)\sigma_X^2\) which are all worthwhile formulas to commit to memory.  The particular values used in the below example are \(\sigma_X^2 = 4, \sigma_Y^2 = 1, \rho = 0.8\).  The way that the estimator changes in response to changes in parameters can be visualized by inspecting the equations provided earlier &amp;ndash; in particular, the gain \(K\) and the conditional variance \(\sigma^2_{X|Y}\) both change linearly in response to changes in \(\rho\) or \(\sigma_X^2\).&lt;/p&gt;


&lt;p&gt;It may at first look a bit odd that the regression line is not aligned with the principle axis of the contour plot.  This is not a mistake.  The regression line which &lt;em&gt;is&lt;/em&gt; aligned with that &amp;ldquo;natural looking&amp;rdquo; direction is obtained from &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Principal_component_regression&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Principle Component Regression&lt;/a
&gt;
, and is sub-optimal in this setting where we know exact statistics.  If the statistics \(\Sigma\) have to be estimated from available data, then it is possible that the regularization effect got from looking only at some top \(k\) principle components gives us overall lower squared error on a test set.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (MMSE Estimator): As opposed to the LMMSE estimator, the MMSE estimator is the optimal estimator which is not constrained to be linear.  The MMSE in general depends upon all of the higher order statistics, and cannot be computed without full knowledge of the joint distribution of $X, Y$.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (The Gaussian Distribution): These formulas also arise in the conditioning formulas for&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Multivariate_normal_distribution#Conditional_distributions&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the Gaussian distribution&lt;/a
&gt;
.  &lt;kbd&gt;This is not exactly a coincidence &amp;ndash; Gaussian&amp;rsquo;s are fully characterized by their second order statistics and, as a family of distributions, are closed under linear combinations.  In the Gaussian case, the MMSE coincides with the LMMSE.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;linear-regression&#34;
    &gt;Linear Regression&lt;a href=&#34;#linear-regression&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Let&amp;rsquo;s look at yet another example: linear regression.  This is a problem which is closely related to &lt;a
    class=&#34;link&#34;
    href=&#34;#linear-minimum-mean-squared-error-estimation&#34;&gt;Linear Minimum Mean Squared Error Estimation&lt;/a
&gt;
, but instead of using the observations \(Y\) of a sensor to estimate some state variable \(X\), we consider a &lt;em&gt;finite&lt;/em&gt; dataset \(\mathcal{D}_N = \{(x_i, y_i)\}_{i = 1}^N\) of \(N\) examples.  The goal of linear regression is to find some model function \(f\) such that the average squared error of the model \[y_i \approx f(x_i)\] is minimized.  Formally, we would write&lt;/p&gt;
&lt;p&gt;\begin{equation}
\underset{f \in \mathcal{F}}{\text{minimize}}\ \frac{1}{2N}\sum_{i = 1}^N \big(y_i - f(x_i)\big)^2
\tag{$R_{\mathcal{F}}$}
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(\mathcal{F}\) is some &amp;ldquo;admissible class&amp;rdquo; of functions.&lt;/p&gt;
&lt;p&gt;The simplest useful class of functions \(\mathcal{F}\) are &lt;em&gt;linear functions&lt;/em&gt; \(f(x) = x^{\mathsf{T}} \beta\), parameterized by some vector \(\beta\).  Specializing the problem to this case is the problem of (ordinary) &lt;em&gt;linear regression&lt;/em&gt;.  By expanding the objective function we can easily pluck out the optimizer:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\frac{1}{2N}\sum_{i = 1}^N \big(y_i - f(x_i)\big)^2
&amp;amp;= \frac{1}{2N}\sum_{i = 1}^N \big(y_i - x_i^{\mathsf{T}} \beta \big)^2\\
&amp;amp;= \frac{1}{2N}\sum_{i = 1}^N y_i^2 - \frac{1}{N}\sum_{i = 1}^N y_i (x_i^{\mathsf{T}} \beta) + \frac{1}{2N}\sum_{i = 1}^N  (x_i^{\mathsf{T}} \beta)^2\\
&amp;amp;= \kappa - \big(\frac{1}{N}\sum_{i = 1}^N y_i x_i \big)^{\mathsf{T}} \beta + \frac{1}{2N}\sum_{i = 1}^N  \mathsf{tr}\ (x_i^{\mathsf{T}} \beta)(x_i^{\mathsf{T}} \beta) \\
&amp;amp;= \kappa -  \big(\frac{1}{N}\sum_{i = 1}^N y_i x_i \big)^{\mathsf{T}} \beta + \frac{1}{2} \beta^{\mathsf{T}} \big(\frac{1}{N}\sum_{i = 1}^N x_i x_i^{\mathsf{T}} \big) \beta,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;and hence \[\beta^\star_{lr} = \big( \frac{1}{N}\sum_{i = 1}^N x_i x_i^{\mathsf{T}} \big)^{-1} \big( \frac{1}{N}\sum_{i = 1}^N y_i x_i \big).\]&lt;/p&gt;
&lt;p&gt;The whole problem can be made considerably simpler by means of matrix notation&lt;/p&gt;
&lt;p&gt;\begin{equation}
\mathbf{y} = \begin{bmatrix}y_1\\ \vdots\\ y_N \end{bmatrix}, \mathbf{X} = \begin{bmatrix}x_1^{\mathsf{T}} \\ \vdots \\ x_N^{\mathsf{T}} \end{bmatrix},\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;so that&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\frac{1}{2N} \sum_{i = 1}^N \bigl( y_i - x_i^{\mathsf{T}} \beta \bigr)^2 &amp;amp;= \frac{1}{2N}||\mathbf{y} - \mathbf{X}\beta||_2^2\\
\implies \beta^\star_{lr} = \bigl(\mathbf{X}^{\mathsf{T}} \mathbf{X}\bigr)^{-1} \mathbf{X}^{\mathsf{T}} \mathbf{y},
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a rather famous expression.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Admissible Functions): Contrary to initial impressions, regression problem $R_\mathcal{F}$ can actually be solved, in closed form, at least up to an $\epsilon$ of approximation error, for an extremely large class of functions $\mathcal{F}$.  Indeed, for just one example when each $x$ is a single real number (to lighten notation) $x_i \in \R$ (as opposed to a vector) and $\mathcal{F}$ is the class of all &lt;em&gt;bounded and continuous&lt;/em&gt; functions, then any $f \in \F$ can be (uniformly) approximated to arbitrary accuracy using a polynomial $f_a(x) \approx \sum_{k = 0}^K a_k x^k$ parameterized by the coefficients $a$&lt;/kbd&gt; (this is the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Stone%E2%80%93Weierstrass_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Stone-Weierstrass Theorem&lt;/a
&gt;
).  &lt;kbd&gt;Each sample $x_i$ can then be transformed into a vector $(1, x_i, x_i^2, \ldots, x_i^K)$, and we can perform &lt;em&gt;linear&lt;/em&gt; regression with the parameters $\beta = (1, a_1, \ldots, a_K)$.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: The way I&amp;rsquo;ve written the optimal solution $\beta_{lr}^\star$ is naive.  Computationally, the problem will usually be solved by means of a QR or Singular Value decomposition of the data matrix $X$ or by an iterative descent algorithm.  Analytically, it is also possible to write the optimal solution using the pseudo-inverse as in $\beta_{lr}^\star = X^\dagger y$, which is much more notationally compact, and doesn&amp;rsquo;t assert the existence of an inverse matrix.  Indeed, writing out&lt;/kbd&gt; \(\bigl(\mathbf{X}^{\mathsf{T}} \mathbf{X}\bigr)^{-1} \mathbf{X}^{\mathsf{T}} \mathbf{y}\) &lt;kbd&gt;is a plain waste of ink.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;This post is a bit of a whirlwind tour of fundamental facts about quadratic forms and positive definite matrices.  All of these facts are truly elementary pieces of the computational linear algebra toolkit, and serve as building blocks for more sophisticated problems.  Indeed, a substantial motivation for writing all of this down is that now I can fearlessly use all these facts in future posts without feeling like I&amp;rsquo;m skipping over important details!&lt;/p&gt;
&lt;p&gt;I hope you, dear reader (I trust there is at least one 🙏), have enjoyed this tour and can now comfortably apply this knowledge to your own problems :)&lt;/p&gt;</description></item><item>
            <title>Generalized Eigenvalue Problems and Trace Optimization</title>
            <link>https://rjtk.github.io/posts/generalized-eigenvalue-problems-and-trace-optimization/</link>
            <pubDate>Tue, 18 Jul 2023 00:00:00 -0700</pubDate>
            <guid>https://rjtk.github.io/posts/generalized-eigenvalue-problems-and-trace-optimization/</guid><description>&lt;p&gt;Most people are familiar with the concept of matrix eigenvalues.  Less well known is that this concept can be fruitfully expanded to the &lt;em&gt;generalized eigenvalues&lt;/em&gt; of &lt;em&gt;pairs&lt;/em&gt; of matrices.  Closely related are matrix trace optimization problems, which extremizes the trace of certain matrix products.  Trace optimization constites a large class of practically solvable &lt;em&gt;non-convex&lt;/em&gt; optimization problems commonly useful for dimensionality reduction and which includes the unsupervised &lt;em&gt;weighted&lt;/em&gt; principle component analysis and the supervised method of Fisher&amp;rsquo;s Linear Discriminant.  The purpose of this post is to explore some of these problems, their intuition, and their applications.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The eigenvalues of a matrix are profoundly important quantities and are usually encountered early on in one&amp;rsquo;s mathematical career.  The concept of the eigenvalues of a single matrix \(A\) can be expanded to the &lt;em&gt;generalized&lt;/em&gt; eigenvalues of a &lt;em&gt;pair&lt;/em&gt; of matrices \((A, B)\).  My purpose here is to summarize and distill some of what I&amp;rsquo;ve learned about the theory and application of generalized eigenvalues.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;spectral-theory&#34;
    &gt;Spectral Theory&lt;a href=&#34;#spectral-theory&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Before discussing eigenvalue problems, we need to set up some basic notation and review critical facts about matrices.&lt;/p&gt;
&lt;p&gt;Consider an \(n \times n\) matrix \(A \in \R^{n \times n}\).  The &lt;em&gt;eigenvalues&lt;/em&gt; \(\lambda_i \in \mathbb{C}\) and &lt;em&gt;eigenvectors&lt;/em&gt; \(v_i \in \mathbb{C}^n\) of the matrix \(A\) are those (complex!) scalars and vectors such that \[Av_i = \lambda_i v_i.\]  That is, the eigenvectors are the &amp;ldquo;directions&amp;rdquo; in which the operation of the matrix \(A\) are &amp;ldquo;simple&amp;rdquo;, &lt;em&gt;i.e.&lt;/em&gt;, nothing but multiplication by a scalar.  It is one of the hallmarks of linear algebra that the eigenvectors of an \(n \times n\) matrix will often (not always) happen to provide us with a basis for \(\mathbb{R}^n\), i.e., we can use the eigenvectors as coordinate axes.  This is incredibly useful when working with the matrix \(A\), since the matrix does nothing but scale the axes in this basis, a rather simple operation.  When this is possible, we say that the matrix is &lt;em&gt;diagonalized&lt;/em&gt; by the change of basis.  Conditions of matrix diagonalizability are captured by the &lt;em&gt;Spectral Theorem&lt;/em&gt;, so called because the collection of eigenvalues of a matrix are often referred to as &amp;ldquo;the spectrum&amp;rdquo; (of the matrix).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;the-spectral-theorem&#34;
    &gt;The Spectral Theorem&lt;a href=&#34;#the-spectral-theorem&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;To &lt;em&gt;diagonalize&lt;/em&gt; the matrix \(A\) means that we can re-write it as \(A = V \Lambda V^{-1}\) where \(\Lambda = \mathsf{Dg}(\mathbf{\lambda})\) is a diagonal matrix of the eigenvalues of \(A\), and \(V = \begin{bmatrix}v_1 &amp;amp; v_2 &amp;amp; \cdots &amp;amp; v_n \end{bmatrix} \in \mathbb{C}^{n \times n}\) are the eigenvectors. The matrix \(V^{-1}\) represents a transformation &lt;em&gt;into&lt;/em&gt; an &lt;em&gt;eigenbasis&lt;/em&gt; and \(V\) is a transformation &lt;em&gt;out&lt;/em&gt; of it.  Unfortunately, diagonalizing a matrix is not always possible, and matrix diagonalizability is quite a slippery issue.  Fortunately though, there is a simple criteria, occuring frequently for matrices of interest in practice, that guarantees that \(A\) is diagonalizable.  The condition is that \(A\) be real-valued and &lt;em&gt;symmetric&lt;/em&gt;: \(A = A^{\mathsf{T}}\) (more generally, \(A\) can be &lt;em&gt;normal&lt;/em&gt;: \(AA^{\mathsf{T}} = A^{\mathsf{T}} A\)).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Spectral Theorem:&lt;/strong&gt; Let $A \in \R^{n \times n}$ be a symmetric matrix.  Then, there exist $n$ &lt;em&gt;real&lt;/em&gt; eigenvalues $\lambda_i$ of $A$ and $n$ eigenvectors $v_i \in \R^n$ such that $A_i v_i = \lambda_i v_i$.  Moreover, the vectors $v_i$ constitute an orthonormal basis of $\R^n$&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;If we express \(x\) in the eigenbasis as \(x = \sum_{i = 1}^n \langle v_i, x \rangle v_i\) then the matrix-vector product is \(Ax = \sum_{i = 1}^n \lambda_i \langle v_i, x \rangle v_i\), and in matrix notation this is simply \(Ax = V\Lambda V^{\mathsf{T}}x\) where \(V = \begin{bmatrix}v_1\ \cdots v_n \end{bmatrix}\).   In fact, this shows that \(A = V\Lambda V^{\mathsf{T}}\), and since \(V\) is an orthonormal basis we have \(V^{\mathsf{T}}V = I_n = VV^{\mathsf{T}}\) meaning that \(V^{-1} = V^{\mathsf{T}}\).  So the eigenvectors of \(A\) diagonalize \(A\) itself.&lt;/p&gt;
&lt;p&gt;The spectral theorem is one of the most important results in mathematics and generalizes in various ways to certain sorts of infinite dimensional operators.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Conventionally, the eigenvalues are &lt;em&gt;sorted&lt;/em&gt; from low to high as in $\lambda_1(A) \le \cdots \le \lambda_n(A)$ (the opposite sorting convention is also common).&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;the-courant-fischer-variational-characterization&#34;
    &gt;The Courant-Fischer Variational Characterization&lt;a href=&#34;#the-courant-fischer-variational-characterization&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Min-max_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Courant-Fischer&lt;/a
&gt;
 theorem provides a &lt;em&gt;variational characterization&lt;/em&gt; of the eigenvalues of a symmetric matrix.  Basically, a variational characterization is a way to calculate some quantity in terms of an optimization problem, the term &amp;ldquo;variational&amp;rdquo; presumably arising from &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Calculus_of_variations&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the Calculus of Variations&lt;/a
&gt;
, and the idea of an infinitestimal &amp;ldquo;variation&amp;rdquo; of a function.  Specifically, the smallest eigenvalue \(\lambda_1(A)\) of an \(n \times n\) symmetric matrix can be characterized by the optimization problem&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{x \in \R^n}{\text{maximize}}&amp;amp;\ x^{\mathsf{T}} A x\\
\text{subject to}&amp;amp;\ ||x||_2 = 1,
\end{aligned}\tag{$\lambda_n(A)$}
\end{equation}&lt;/p&gt;
&lt;p&gt;where the maximum is obtained by an eigenvector corresponding to the smallest eigenvalue \(\lambda_n\), which is also the value of the problem.  Variational characterizations are incredibly useful in practice &amp;ndash; they are often providing one or both of: (a) a concrete procedure for calculating some quantity by means of an optimization algorithm or (b) a way to solve seemingly difficult optimization problems by directly calculating the quantity known to be the solution.  The optimization problem I called \((\lambda_n(A))\) above falls into the second category.  Particularly since it is not a convex problem, directly applying an iterative optimization algorithm to this problem is not likely to pan out well, but there do exist reliable algorithms for calculating matrix eigenvalues.&lt;/p&gt;
&lt;p&gt;To see &lt;em&gt;why&lt;/em&gt; the problem \((\lambda_n(A))\) provides us with the smallest eigenvalue, we recognize that it is exactly equivalent to the following:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{u \in \R^n}{\text{maximize}}&amp;amp;\ u^{\mathsf{T}} \Lambda u\\
\text{subject to}&amp;amp;\ ||u||_2 = 1,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(\Lambda\) is the diagonal matrix of eigenvalues from \(A = V\Lambda V^{\mathsf{T}}\).  This equivalence follows since \(V\) is an orthonormal basis: for any \(x\) with \(||x||_2 = 1\) there exists a \(v\) such that \(x = Vu\) and \(||u||_2 = 1\) since \(||Vu||_2 = \sqrt{\lang Vu, Vu\rang} = \sqrt{\lang u, V^{\mathsf{T}} Vu\rang} = ||u||_2\).  The optimizer of this latter problem is clearly \(u^\star = (0, 0, \ldots, 1)\) (selecting out the largest eigenvalue) with the value \(\lambda_n\) and the corresponding solution of the original problem is \(x = Vu^\star = v_n\), an eigenvector associated with \(\lambda_n\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Incidentally, this also shows that $\lambda_n(A)$ is a convex function of&lt;/kbd&gt; \(A\) &lt;kbd&gt;since&lt;/kbd&gt; \(A \mapsto x^{\mathsf{T}} A x\) &lt;kbd&gt;is a linear function of the matrix&lt;/kbd&gt; $A,$ &lt;kbd&gt;and the maximum over a family of linear functions is a convex function.  Similarly, it can be shown that&lt;/kbd&gt; \(\lambda_1(A)\) &lt;kbd&gt;is a &lt;strong&gt;concave&lt;/strong&gt; function of $A,$ and can be obtained by replacing &amp;ldquo;maximize&amp;rdquo; with &amp;ldquo;minimize&amp;rdquo; in the variational problem.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;The Courant-Fischer theorem takes this one step further by providing a variational characterization of &lt;em&gt;any&lt;/em&gt; of the eigenvalues.  Specifically,&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Theorem (Courant-Fischer)&lt;/strong&gt;: Let&lt;/kbd&gt; \(A \in \R^{n \times n}\) &lt;kbd&gt;be a real symmetric matrix.  Then&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;\begin{equation}
\lambda_k = \underset{U}{\text{min}}\ \Bigl\{\underset{x \in \R^n}{\text{max}}\ x^{\mathsf{T}} A x\ \big|\ ||x||_2 = 1, x \in U, \mathsf{dim}(U) = k \Bigr\},\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;where the minimization is over subspaces of&lt;/kbd&gt; \(\R^n\).&lt;/p&gt;
&lt;p&gt;Intuitively, if \(x\) can live in a \(k\) dimensional space, then the maximization over \(x\) can always attain &lt;em&gt;at least&lt;/em&gt; the value of the \(k^{\text{th}}\) smallest eigenvalue, no matter which \(k\) dimensional space it is confined to.  From the optimization problem described earlier, the maximizing \(x\) is given by the eigenvector corresponding to the largest eigenvalue, subject to the constraint that that eigenvector be in the space \(U\).  The minimizing space \(U\) is then of course given by the space spanned by the \(k\) smallest eigenvectors \(U = \mathsf{span}\{v_1, \ldots, v_k\}\).&lt;/p&gt;
&lt;p&gt;To finally see the connection with trace optimization problems, it follows from the Courant-Fisher theorem that the value of the optimization problem (generalizing the first variational characterization above)&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{U \in \R^{n \times k}}{\text{minimize}}&amp;amp;\ \mathsf{tr}\ U^{\mathsf{T}} A U\\
\text{subject to}&amp;amp;\ U^{\mathsf{T}} U = I
\end{aligned}\tag{${\mathcal{E}_k}$}
\end{equation}&lt;/p&gt;
&lt;p&gt;is given by \(\lambda_1 + \cdots + \lambda_k\), and that an optimizing \(U = \begin{bmatrix}v_1\ \cdots\ v_k \end{bmatrix}\) is given by the \(n \times k\) matrix formed from the first \(k\) eigenvectors.  When reading this problem, be careful to remember that \(U^{\mathsf{T}} U = I_k\) definitely does not imply \(UU^{\mathsf{T}} = I_n\), unless \(n = k\).&lt;/p&gt;
&lt;p&gt;This optimization problem can be obtained from the Courant-Fischer theorem more-or-less by simply taking the summation of the first \(k\) eigenvalues and stacking separate &amp;ldquo;\(x\)&amp;rdquo; optimization variables together into a matrix, along with elementary facts about traces.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Main Point&lt;/strong&gt;: The main point I am making here is that Problem&lt;/kbd&gt; $(\mathcal{E}_k),$ (parameterized by the number \(k\) of vectors in \(U\)) &lt;kbd&gt;which is a &lt;em&gt;non-convex&lt;/em&gt; optimization problem, can be solved by computing an eigendecomposition of the symmetric matrix&lt;/kbd&gt; $A.$  &lt;kbd&gt;This is remarkable, since there exist efficient algorithms for computing this factorization.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;generalized-eigenvalues&#34;
    &gt;Generalized Eigenvalues&lt;a href=&#34;#generalized-eigenvalues&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The eigenvalue problem of finding a pair \((\lambda, v)\) for a matrix \(A\) such that \(Av = \lambda v\) can be generalized in various ways.  One particular direction is to consider a generalized eigenvalue problem (GEVP) associated to a &lt;em&gt;pair&lt;/em&gt; of matrices \((A, B)\) wherein we seek to find \((\lambda, v)\) such that \(Av = \lambda Bv\).  Such pairs are now referred to as &lt;em&gt;generalized&lt;/em&gt; eigenpairs associated with \((A, B)\).&lt;/p&gt;
&lt;p&gt;The case of most interest in applications is where \(A\) is symmetric, and \(B \succ 0\) is &lt;em&gt;positive definite&lt;/em&gt;.  Recall that a positive definite matrix can be characterized by the fact that they admit a &lt;em&gt;Cholesky factorization&lt;/em&gt; \(B = LL^{\mathsf{T}}\) where \(L\) is an invertible lower-triangular matrix &amp;ndash; this is a particular type of matrix square root.  Using this Cholesky decomposition, the GEVP can be reduced to an ordinary EVP:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
Av &amp;amp;= \lambda Bv\\
\iff A L^{-\mathsf{T}} L^{\mathsf{T}} v &amp;amp;= \lambda LL^{\mathsf{T}} v\\
\iff \bigl(L^{-1} A L^{-\mathsf{T}}) w &amp;amp;= \lambda w;\ w = L^{\mathsf{T}} v,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;wherein \(S \overset{\Delta}{=} L^{-1} A L^{-\mathsf{T}}\) is a symmetric matrix with eigenpairs \((\lambda, w)\) from which the generalized eigenvectors \(v = L^{-\mathsf{T}} w\) can be recovered.  (&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: I avoid the computation $B^{-1} A$ since this need not be symmetric.&lt;/kbd&gt;)&lt;/p&gt;
&lt;p&gt;To understand the intuition of this situation, we first apply the fact that the eigenvectors \(W = \begin{bmatrix} w_1\ \cdots\ w_n \end{bmatrix}\) of \(S\) will form an orthogonal basis for \(\R^n\).  Thus, since \(W = L^{\mathsf{T}} V\) we must have&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
W^{\mathsf{T}} W &amp;amp;= I\\
\implies V^{\mathsf{T}} LL^{\mathsf{T}} V &amp;amp;= I\\
\implies V^{\mathsf{T}} B V &amp;amp;= I.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Thus, it becomes natural to construct the associated trace optimization problems for the pair \((A, B)\)&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{U \in \R^{n \times k}}{\text{minimize}}&amp;amp;\ \mathsf{tr}\ U^{\mathsf{T}} A U\\
\text{subject to}&amp;amp;\ U^{\mathsf{T}} B U = I.
\end{aligned}\tag{${\mathcal{G}_k}$}
\end{equation}&lt;/p&gt;
&lt;p&gt;To reiterate, the point here is that this is a class of &lt;em&gt;non-convex&lt;/em&gt; optimization problems which can be efficiently solved by eigenvalue algorithms.  Indeed, the value of this problem is given by \[\lambda_1(A, B) + \cdots + \lambda_k(A, B),\] the sum of the first \(k\) generalized eigenvalues, and the optimizing matrix \(U \in \R^{n \times k}\) constitutes the first \(k\) generalized eigenvectors of \((A, B)\).  Keep in mind that the problem is only well defined when \(A\) is symmetric, and \(B\) is positive-definite (though a reasonable solution for the semidefinite case can probably still be worked out).  If these assumptions do not hold, the situation becomes substantially more complicated.&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;interpretations-and-intuition&#34;
    &gt;Interpretations and Intuition&lt;a href=&#34;#interpretations-and-intuition&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;The generalized eigenvectors \(V = \begin{bmatrix}v_1\ \cdots\ v_n \end{bmatrix}\) are still a basis for \(\R^n\) (since \(L\) is invertible) but they are no longer orthogonal.  Instead, they are orthogonal with respect to an inner product modified by the matrix $B.$  That is, if we write \(x^{\mathsf{T}} y \overset{\Delta}{=} \lang x, y \rang\) then we might generalize this by writing \[\lang x, y \rang_{B^{-1}} = x^{\mathsf{T}} B y = \lang L^{\mathsf{T}} x, L^{\mathsf{T}} y \rang,\] where the inner product \(\lang \cdot, \cdot \rang_{B^{-1}}\) (which, recall, describes the geometry of the space through the relationship to angles \(||x|| ||y|| \mathsf{cos}\ \theta_{xy} = \lang x, y \rang\) and distances \(||x|| = \sqrt{\lang x, x \rang}\)) has modified the geometry of \(\R^n\).  Using a subscript \(B^{-1}\), rather than just \(B\) for this inner product is, I believe, the convention.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve now equipped \(\R^n\) with two different inner products &amp;ndash; the ordinary one \(\langle x, y \rangle = x^{\mathsf{T}}y\), and the new one \(\langle x, y \rangle_{B^{-1}} = x^{\mathsf{T}} B y\).  To gain some intuition for this new inner product, consider the ordinary euclidean ball \(\mathbb{B} = \{x \in \R^n\ |\ ||x||_2 \le 1\}\) and the ball in the norm \(||x||_{B^{-1}} = \sqrt{x^{\mathsf{T}} B^{} x}\), namely \[\mathbb{B}_B = \{x \in \R^n\ |\ ||x||_{B^{-1}} \le 1\},\] which is an ellipsoid.  Since if \(y = L^{-\mathsf{T}} x\) and \(||x||_2 \le 1\) then \(||L^{\mathsf{T}} y||_2 = ||y||_{B^{-1}} \le 1\) the mapping \(x \mapsto L^{-\mathsf{T}}\) transforms the ordinary euclidean ball into \(\mathbb{B}_B\) as in \(L^{-\mathsf{T}} \mathbb{B} = \mathbb{B}_B\).&lt;/p&gt;
&lt;p&gt;To visualize the ball \(L^{-\mathsf{T}} \mathbb{B}\) recognize that if \(u_i\) is an (ordinary) eigenvector of \(B\), then \(u_i^{\mathsf{T}} B^{} u_i = \lambda_i(B)\) and therefore any vector proportional to \(u_i\) and with an euclidean length between \(0\) and \(1 / \sqrt{\lambda_i(B)}\) will be inside of the ball.  Essentially what this means is that the eigenvectors \(u_i\) of \(B\) define the &lt;em&gt;principle axes&lt;/em&gt; of the ellipse, each of which have a length \(1 / \sqrt{\lambda_i(B)}\).  After stretching the space through the mapping \(x \mapsto L^{-\mathsf{T}}x\) the directions associated with large eigenvalues are shortened, and directions associated with small eigenvalues of \(B\) are lengthened.  Another way to think about it is through a topographic map &amp;ndash; the terrain in the direction of small eigenvalues is very steep.&lt;/p&gt;


&lt;p&gt;The figure above depicts the transformation of Euclidean space \(\R^n\) by the mapping \(x \mapsto L^{-\mathsf{T}}x\).  The dotted lines are points in the original space \(\R^n\), and the solid lines are the result of applying the transformation.  The ellipse in particular includes eigenvectors of \(B\) (as calculated by &lt;kbd&gt;scipy.linalg.eigh&lt;/kbd&gt;) scaled by \(1 / \sqrt{\lambda_i(B)}\) in white.  The contours are drawn for the function \(x \mapsto \frac{1}{2}x^{\mathsf{T}} B x\) and I&amp;rsquo;ve used the particular matrix \[B = \begin{bmatrix}5 &amp;amp; 1 \\ 1 &amp;amp; 1 \end{bmatrix},\] which has an eigendecomposition \[B \approx \begin{bmatrix}0.230 &amp;amp; -0.973 \\ -0.973 &amp;amp; -0.230 \end{bmatrix}\begin{bmatrix}0.764 &amp;amp; 0 \\ 0 &amp;amp; 5.236 \end{bmatrix}\begin{bmatrix}0.230 &amp;amp; -0.973 \\ -0.973 &amp;amp; -0.230 \end{bmatrix}^{\mathsf{T}}.\]&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;applications-and-computations&#34;
    &gt;Applications and Computations&lt;a href=&#34;#applications-and-computations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Practically speaking, a reliable means of solving generalized eigenvalue problems is by means of &lt;kbd&gt;scipy.linalg.eig&lt;/kbd&gt; (which will try to solve \(Av = \lambda B v\) for any square \(A, B\)) and &lt;kbd&gt;scipy.linalg.eigh&lt;/kbd&gt; (which assumes \(A\) is symmetric, \(B\) is positive definite and always returns a &lt;em&gt;real&lt;/em&gt; factorization).  These functions also provide the option to (efficiently) compute and return only a subset of eigenvalues (along with corresponding eigenvectors).  Moreover, this subset can be defined either by &lt;em&gt;indices&lt;/em&gt; (&lt;em&gt;e.g.,&lt;/em&gt; return the third, fourth, and fifth) or by &lt;em&gt;values&lt;/em&gt; (&lt;em&gt;e.g.,&lt;/em&gt; return eigenvalues between \(0\) and \(1\)).  These are incredibly useful featuers in practice.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;principle-component-analysis&#34;
    &gt;Principle Component Analysis&lt;a href=&#34;#principle-component-analysis&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The most famous application of trace optimization problems is the feature-extraction or dimensionality-reduction technique known as &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Principal_component_analysis&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Principle Component Analysis&lt;/a
&gt;
 (PCA).  Suppose we have a random variable \(X \in \R^n\) with mean zero \(\mathbb{E}[X] = 0\) and variance \(\mathbb{V}[X] = \Sigma \succ 0\).  The goal of PCA is to find a matrix \(V \in \R^{n \times k}\) having orthogonal columns (so that \(V^{\mathsf{T}} V = I\)) and such that the total variance of \(Y = V^{\mathsf{T}} X\) is &lt;em&gt;maximized&lt;/em&gt;.  The number \(k\) of columns of \(V\) is the key here &amp;ndash; we will usually choose \(k \ll n\) so that the idea of maximizing the variance corresponds to searching for the \(k\) &amp;ldquo;most explanatory&amp;rdquo; directions in \(\R^n\).  Another way to think about this is that we are finding a subspace of dimension \(k\) which &amp;ldquo;explains&amp;rdquo; the greatest amount of variability in \(X\).&lt;/p&gt;
&lt;p&gt;It is easy to set this problem up as a trace optimization problem.  The total variance of \(Y\) (which is necessarily also zero mean) is given by&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbb{E} Y^{\mathsf{T} }Y
&amp;amp;= \mathbb{E} \mathsf{tr}\ YY^{\mathsf{T}}\\
&amp;amp;= \mathbb{E} \mathsf{tr}\ V^{\mathsf{T}} X X^{\mathsf{T}} V\\
&amp;amp;= \mathsf{tr}\ V^{\mathsf{T}} \Sigma V.\\
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Thus, the problem comes down to solving:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{V \in \R^{n \times k}}{\text{maximize}}&amp;amp;\ \mathsf{tr}\ V^{\mathsf{T}} \Sigma V\\
\text{subject to}&amp;amp;\ V^{\mathsf{T}} V = I,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a standard trace optimization problem.  The solution of which is, famously, given by \(k\) eigenvectors corresponding to the \(k\) largest eigenvalues of \(\Sigma\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Given an actual data matrix $X \in \R^{N \times n}$, one might naturally think to just estimate a covariance matrix $\widehat{\Sigma} \approx \frac{1}{N} X^{\mathsf{T}} X$.  However, this is a mistake.  The eigendecomposition of $\widehat{\Sigma}$ can be computed directly from a&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Singular_value_decomposition&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Singular Value Decomposition&lt;/a
&gt;
 &lt;kbd&gt;of the matrix $X$ itself.  Indeed, for most purposes I am aware of, the actual computation of $X^{\mathsf{T}} X$ is unnecessary and should not be performed in practice.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;weighted-principle-component-analysis&#34;
    &gt;Weighted Principle Component Analysis&lt;a href=&#34;#weighted-principle-component-analysis&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The choice in the last section of maximizing the total variance of \(Y = V^{\mathsf{T}} X\) was arbitrary.  There is a natural generalization of this formulation where we will still find a matrix \(V\) to compute some projection \(Y = V^{\mathsf{T}} X\) as usual, but now we will weight the &amp;ldquo;importance&amp;rdquo; of the various components of \(X\) by some matrix \(W\) (which is often likely to be diagonal, though this is not a requirement).  A natural objective is then the quantity \(\mathbb{E}||V^{\mathsf{T}} W^{1/2} X||_2^2 = \mathsf{tr}\ V^{\mathsf{T}} W^{1/2} \Sigma W^{1/2} V\).  This objective can be plugged into another trace optimization problem with constraint \(V^{\mathsf{T}} V = I\).&lt;/p&gt;
&lt;p&gt;Interestingly, if we absorb the factor \(W^{1/2}\) into \(V\) as in \(\tilde{V}_W = W^{1/2} V\) we can re-write the constraint in terms of \(\tilde{V}_W\) as in \(V^{\mathsf{T}} V = \tilde{V}_W W^{-1} \tilde{V}_W = I\).  That is, the matrix \(\tilde{V}_W\) will be orthogonal with respect to the inner product \(\lang\cdot, \cdot\rang_W\).&lt;/p&gt;


&lt;p&gt;The PCA weighting scheme will not necessarily make any significant difference in regards to the estimated or reconstructed vector \(\widehat{X} = \sum_{k = 1}^K \lang v_i, X \rang_W W^{-1} v_i\), particularly if both the covariance matrix of \(X\) and the weight matrix \(W\) are diagonal.  The differences arise when there is significant correlation structure in \(X\), in which case the weighting scheme can substantively impact the correlation structure of the factors \(v_i\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: When reconstructing an estimate of an image (or other data) from a weighted PCA, one must be careful to carry out the linear projections with respect to the weighted inner product, otherwise the results will be nonsensical.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;rayleigh-quotients-and-fisher-s-linear-discriminant&#34;
    &gt;Rayleigh Quotients and Fisher&amp;rsquo;s Linear Discriminant&lt;a href=&#34;#rayleigh-quotients-and-fisher-s-linear-discriminant&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Principle component analysis is a ubiquitous technique for dimensionality reduction.  However, it suffers a major drawback in that it is completely &lt;em&gt;unsupervised&lt;/em&gt;, whereas data often come attached with &lt;em&gt;class labels&lt;/em&gt;.  For example, we may have a random variable \(X\) and, associated with \(X\) is a random &lt;em&gt;class label&lt;/em&gt; \(Y \in [C] = \{1, \ldots, C\}\) indicating that \(X\) belongs to one of \(C\) separate classes.  These classes may represent a data source, a target label, or be simply some discrete feature of the data.&lt;/p&gt;
&lt;p&gt;For some typical concrete examples, ponder these possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data \(X\) are natural language embeddings and the labels \(Y\) are the language (English, French, Persian, &amp;hellip;)&lt;/li&gt;
&lt;li&gt;Data \(X\) represent stock returns, labels \(Y\) indicate the stock&amp;rsquo;s industry&lt;/li&gt;
&lt;li&gt;\(X\) is real-valued features of a house, and the class label \(Y\) is a categorical variable such as the neighbourhood.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In PCA, the projection matrix \(V\) is constructed from analyzing \(X\) alone, disregarding the class label \(Y\).  Ideally, we would use the information available in \(Y\) to construct \(V\), and Fisher&amp;rsquo;s Linear Discriminant (FLD) is one particular method to do so.  The idea is to find the projection \(V\) such that samples \(X_i\) which share a class label \(Y_i\) are clustered close to each other, and at the same time that clusters formed by samples with different class labels are far apart.&lt;/p&gt;
&lt;p&gt;Quantitatively, first suppose that we&amp;rsquo;ve centered our data \(\mu = \mathbb{E}[X] = 0\).  Then, to establish more notation, let \(\mu_c = \mathbb{E}[X\ |\ Y = c]\) and \(\Sigma_c = \mathbb{E}[(X - \mu_c)(X - \mu_c)^{\mathsf{T}}\ |\ Y = c]\) be the &lt;em&gt;class conditional&lt;/em&gt; mean and variance of \(X\), respectively.  Finally, \(\mathbb{P}(Y = c) = \pi_c\) be the probability that each data sample is from class \(c\).&lt;/p&gt;
&lt;p&gt;Now, just as we did for principle component analysis, let&amp;rsquo;s consider the variance of the projection \(V^{\mathsf{T}} X\).  To bring out the class labels, we will condition on the value of \(Y\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbf{Var}[V^\mathsf{T} X]
&amp;amp;= V^{\mathsf{T}} \mathbb{E}(X - \mathbb{E}[X])(X - \mathbb{E}[X])^{\mathsf{T}} V\\
&amp;amp;= V^{\mathsf{T}} \mathbb{E}[XX^{\mathsf{T}}] V\\
&amp;amp;= V^{\mathsf{T}} \mathbb{E}\mathbb{E}[XX^{\mathsf{T}} \ |\ Y]V\\
&amp;amp;= V^{\mathsf{T}} \sum_c \pi_c \mathbb{E}[XX^{\mathsf{T}} \ |\ Y = c]V\\
&amp;amp;\overset{(a)}{=} V^{\mathsf{T}} \sum_c \pi_c \bigl(\mathbf{Var}[X\ |\ Y = c] + \mathbb{E}[X\ |\ Y = c]\mathbb{E}[X\ |\ Y = c]^{\mathsf{T}}\bigr) V\\
&amp;amp;= V^{\mathsf{T}} \sum_c \pi_c \bigl(\Sigma_c + \mu_c \mu_c^{\mathsf{T}} \bigr) V,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \((a)\) is rearranging the variance formula \(\mathbf{Var}(Z) = \mathbb{E}[ZZ^{\mathsf{T}}] - \mathbb{E}[Z]\mathbb{E}[Z]^{\mathsf{T}}\).  The same result could be got by applying the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Law_of_total_variance&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Law of Total Variance&lt;/a
&gt;
, which is what I did first before seeing that the above calculation is easier.&lt;/p&gt;
&lt;p&gt;In the context of FLD, the term \(\Sigma_w = \sum_c \pi_c \Sigma_c\) is a measure of the average &lt;em&gt;within-class&lt;/em&gt; variance (&lt;em&gt;i.e.,&lt;/em&gt; the variance of the data conditioned on a class label) and \(\Sigma_b = \sum_c \pi_c \mu_c \mu_c^{\mathsf{T}}\) is the &lt;em&gt;between-class&lt;/em&gt; variance (&lt;em&gt;i.e.&lt;/em&gt;, a measure of the separation between the classes).&lt;/p&gt;
&lt;p&gt;The key idea of FLD is to &lt;em&gt;maximize&lt;/em&gt; the within class variance (of the projection) \(V^{\mathsf{T}} \Sigma_w V\), and &lt;em&gt;minimize&lt;/em&gt; the between class variance \(V^{\mathsf{T}} \Sigma_b V\).  Projections which achieve this goal are identifying the axes which simultaneously explain the differences and similarities between classes.&lt;/p&gt;
&lt;p&gt;To operationalize what it means to minimize the within-class variance, and maximize the between-class variance we need two things: (1) we need to turn the matrix-valued variances into a scalar and (2) we need to define the tradeoff between maximizing and minimizing.  There are surely many ways of doing this, but the choice of objective function made by FLD is to minimize the following scalar cost:&lt;/p&gt;
&lt;p&gt;\begin{equation}
J(V) = \mathsf{tr} (V^{\mathsf{T}} \Sigma_w V)^{-1} (V^{\mathsf{T}} \Sigma_b V).
\end{equation}&lt;/p&gt;
&lt;p&gt;The trace serves to turn the objective into a scalar, and inverting the within-class variance means that the minimization objective \(J\) will lead us to attempt to &lt;em&gt;maximize&lt;/em&gt; that wiithin-class variance.&lt;/p&gt;
&lt;p&gt;While the function \(J(V)\) obviously looks like it has a lot of interesting structure, it may not be immediately clear what to do with it or how to optimize it (certainly, it is not a convex function of \(V\)).  However, we can recognize an &lt;em&gt;analogy&lt;/em&gt; &amp;ndash; suppose that \(V\) is just a vector, and that \(\Sigma_b = I\).  In this case, \(J(v) = \frac{v^{\mathsf{T}} \Sigma_w v}{v^{\mathsf{T}} v}\), which is a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Rayleigh_quotient&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rayleigh Quotient&lt;/a
&gt;
 for the matrix \(\Sigma_w\).  The reader familiar with how to connect Rayleigh quotients to eigenvalues should be inspired &amp;ndash; the Rayleigh Quotient is invariant to rescaling of the vector, so let&amp;rsquo;s consider a change of basis matrix \(C \in \mathbb{R}^{k \times k}\) (&lt;em&gt;i.e.&lt;/em&gt;, an invertible matrix) and check the objective:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
J(VC)
&amp;amp;= \mathsf{tr}\ (C^{\mathsf{T}} V^{\mathsf{T}} BV C)^{-1} (C^{\mathsf{T}} V^{\mathsf{T}} A V C)\\
&amp;amp;= \mathsf{tr}\ C^{-1} (V^{\mathsf{T}} BV)^{-1} C^{-\mathsf{T}} C^{\mathsf{T}} (V^{\mathsf{T}} A V) C\\
&amp;amp;= \mathsf{tr}\ C^{-1} (V^{\mathsf{T}} BV)^{-1} C^{-\mathsf{T}} C^{\mathsf{T}} (V^{\mathsf{T}} A V) C\\
&amp;amp;= \mathsf{tr}\ (V^{\mathsf{T}} BV)^{-1} (V^{\mathsf{T}} A V) \\
&amp;amp;= J(V).
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Therefore, the objective is invariant to a change of basis (just like ordinary Rayleigh Quotients are invariant to rescalings) so we might as well impose the convenient constraint that \(V^{\mathsf{T}} \Sigma_w V = I_k\) since if \(U\) is some minimizer of \(J\) which does not satisfy this condition, we can rescale \(U\) so that it does.  Thus, we are left with the GEVP:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{V \in \mathbb{R}^{n \times k}}{\text{minimize}}&amp;amp;\ \mathsf{tr}(V^{\mathsf{T}} \Sigma_b V)\\
\text{subject to}&amp;amp;\ V^{\mathsf{T}} \Sigma_w V = I_k,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;the solutions of which provide a matrix \(V \in \R^{n \times k}\) which can be used to reduce the dimensionality of the data \(X\) such that the different classes are well separated in the lower dimensional space.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;Remark: Notice that optimizing the Rayleigh quotient provides another &lt;em&gt;variational characterization&lt;/em&gt; of the generalized eigenvalues!&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;There is however an important caveat.  Notice that \(\Sigma_w = \sum_c \pi_c \mu_c \mu_c^{\mathsf{T}}\) is a sum of \(C\) rank-1 matrices.  This matrix therefore has rank at most \(C\), and then \(V^{\mathsf{T}} \Sigma_w V\) must also have rank at most \(C\), though it is of dimension \(k \times k\) (where \(V\) is \(n \times k\) and \(k \le n\)).  Thus, we need to be careful that \(k \le C\) (smaller for degenerate cases), otherwise the inverse is not well defined &amp;ndash; this places a restriction on the dimensionality of the space onto which we project.&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;example&#34;
    &gt;Example&lt;a href=&#34;#example&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;One of the most natural datasets on which to apply this technique is the classic &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/MNIST_database&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;MNIST handwritten digits data&lt;/a
&gt;
, for which I&amp;rsquo;ve just used the data &lt;a
    class=&#34;link&#34;
    href=&#34;https://scikit-learn.org/stable/datasets/toy_dataset.html#optical-recognition-of-handwritten-digits-dataset&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;shipped with scikit-learn&lt;/a
&gt;
.  This dataset consists of \(1797\) examples of \(8 \times 8\) images of written digits between \(0\) and \(9\).  Pixel intensities are between \(0\) and \(16\).&lt;/p&gt;
&lt;p&gt;Simply flattening out the \(8 \times 8\) images into a vector gives us a data matrix \(X \in \R^{1797 \times 64}\), which I&amp;rsquo;ll project into a lower dimensional subspace using either FLD or PCA &amp;ndash; the labels \(y \in \{0, \ldots, 9\}^{1797}\) are natural class labels for FLD.&lt;/p&gt;
&lt;p&gt;Calculating the projections onto a \(K \ll 64\) dimensional subspace (after centering and standardizing the \(X\) matrix) results in two new datasets \(\widehat{X}_{\text{pca}} \in \R^{1797 \times K}\) and \(\widehat{X}_{\text{fld}} \in \R^{1797 \times K}\), the former being the projection onto the axes which maximizes the variance, and the latter being constructed to try to separate the clumps of classes.&lt;/p&gt;
&lt;p&gt;In order to make a nice visual comparison between these two projections, I&amp;rsquo;d like to try to &amp;ldquo;line up&amp;rdquo; the projected data as well as possible so that if the data were to fall into similar clumps for PCA and FLD, that these clumps would appear in similar locations in a visualization.  To do so, I&amp;rsquo;ve rotated the matrix \(\widehat{X}_{\text{fld}}\) with an orthogonal matrix \(Q\) obtained from solving an &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Orthogonal Procrustes Problem&lt;/a
&gt;
.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;Orthogonal Procrustes Problem&lt;/summary&gt;
  &lt;p&gt;This is a &lt;em&gt;bonus&lt;/em&gt; problem which is similar in spirit to a trace optimization problem, but takes on a different form and has a different solution than the problems I&amp;rsquo;ve considered so far.  Specifically, we solve&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{Q \in \R^{n \times n}}{\text{minimize}} &amp;amp;\quad \frac{1}{2}||Q\widehat{X}_{\text{fld}} - \widehat{X}_{\text{pca}}||_F^2\\
\text{subject to} &amp;amp;\quad Q^\mathsf{T} Q = I,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;so that \(\widehat{X}_{\text{fld}}\) is rotated (and possibly reflected) to better line up with \(\widehat{X}_{\text{pca}}\).  We can make this look like a trace optimization problem by expanding the objective as in&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\frac{1}{2}||Q\widehat{X}_{\text{fld}} - \widehat{X}_{\text{pca}}||_F^2
&amp;amp;= \frac{1}{2}\mathsf{tr}(\widehat{X}_{\text{fld}}^{\mathsf{T}}Q^{\mathsf{T}}Q\widehat{X}_{\text{fld}} + \widehat{X}_{\text{pca}}^{\mathsf{T}}\widehat{X}_{\text{pca}}) - 2\mathsf{tr}(Q^{\mathsf{T}} \widehat{X}_{\text{pca}}\widehat{X}_{\text{fld}}^{\mathsf{T}})\\
&amp;amp;\overset{(a)}{=} \frac{1}{2}\mathsf{tr}(\widehat{X}_{\text{fld}}^{\mathsf{T}}\widehat{X}_{\text{fld}} + \widehat{X}_{\text{pca}}^{\mathsf{T}}\widehat{X}_{\text{pca}}) - \mathsf{tr}(Q^{\mathsf{T}} \widehat{X}_{\text{pca}}\widehat{X}_{\text{fld}}^{\mathsf{T}})\\
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \((a)\) uses the orthogonality constraint.  This leaves us with a sort of &amp;ldquo;one-sided&amp;rdquo; trace optimization problem, distinct from the problems encountered so far:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{Q \in \R^{n \times n}}{\text{maximize}} &amp;amp;\quad \mathsf{tr}(Q^{\mathsf{T}} \widehat{X}_{\text{pca}}\widehat{X}_{\text{fld}}^{\mathsf{T}})\\
\text{subject to} &amp;amp;\quad Q^\mathsf{T} Q = I.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;To actual see the solution of this problem, expand out the related term&lt;/p&gt;
&lt;p&gt;\begin{equation}
\frac{1}{2}||Q - \widehat{X}_{\text{pca} }\widehat{X}_{\text{fld}}^{\mathsf{T}}||_F^2 = \frac{1}{2}\mathsf{tr}(Q^{\mathsf{T}}Q + \widehat{X}_{\text{fld}}\widehat{X}_{\text{pca}}^{\mathsf{T}}\widehat{X}_{\text{pca}} \widehat{X}_{\text{fld}}^{\mathsf{T}}) - \mathsf{tr}(Q^{\mathsf{T}} \widehat{X}_{\text{pca}}\widehat{X}_{\text{fld}}^{\mathsf{T}}),\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which similarly involves constants and the above trace objective.  We can therefore equivalently solve \[\underset{Q: Q^{\mathsf{T}}Q = I}{\text{minimize}}\ \frac{1}{2}||Q - \widehat{X}_{\text{pca} }\widehat{X}_{\text{fld}}^{\mathsf{T}}||_F^2.\] Apply a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Singular_value_decomposition&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Singular Value Decomposition&lt;/a
&gt;
 \(\widehat{X}_{\text{pca} }\widehat{X}_{\text{fld}}^{\mathsf{T}} = U\Sigma V^{\mathsf{T}}\) to the product, and using the fact that \(U, V\) are orthogonal, and therefore we can freely multiply by \(U, V\) inside the norm without change its value, leads to the solution \(Q = UV^{\mathsf{T}}\), with cost \(\sum_{i = 1}^m (1 - \sigma_i)^2\) for singular values \(\sigma_i\) of \(\widehat{X}_{\text{pca} }\widehat{X}_{\text{fld}}^{\mathsf{T}}\).  Precisely,&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{Q: Q^{\mathsf{T}}Q = I}{\text{min}}\ \frac{1}{2}||Q - \widehat{X}_{\text{pca}}\widehat{X}_{\text{fld}}^{\mathsf{T}}||_F^2
&amp;amp;= \underset{Q: Q^{\mathsf{T}}Q = I}{\text{min}}\ \frac{1}{2}||Q - U\Sigma V^{\mathsf{T}}||_F^2\\
&amp;amp;\overset{(a)}{=} \underset{D: D^{\mathsf{T}}D = I}{\text{min}}\ \frac{1}{2}||UDV^{\mathsf{T}} - U\Sigma V^{\mathsf{T}}||_F^2\\
&amp;amp;\overset{(b)}{=} \underset{D: D^{\mathsf{T}}D = I}{\text{min}}\ \frac{1}{2}||D - \Sigma||_F^2\\
&amp;amp;\overset{( c)}{=} \frac{1}{2}\sum_{i = 1}^m (1 - \sigma_i)^2,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \((a)\) expresses \(Q\) in the same basis (\(D\) is not yet necessarily diagonal! &lt;em&gt;e.g.,&lt;/em&gt; consider \(D = U^{\mathsf{T}} Q V\)), and \((b)\) uses the fact that \(U, V\) are orthogonal.  Minimizing the approximation error of the diagonal matrix \(\Sigma\) after equality \((b)\) &lt;em&gt;now&lt;/em&gt; requires that \(D\) be diagonal, and meeting the equality (which is derived from the requirement that \(Q^{\mathsf{T}}Q = I\)) necessitates (equality \(( c)\)) that $D = I.$   \(\ \square\)&lt;/p&gt;

&lt;/details&gt;

&lt;p&gt;I&amp;rsquo;ve used this matrix \(Q\) to better align the data in the animation below.  Notice that each coloured clump (corresponding to different digits) is roughly aligned in space &amp;ndash; there is nothing intrinsic about PCA and FLD that would encourage this alignment.&lt;/p&gt;


&lt;p&gt;In addition to the figure, I also fit a simple &lt;kbd&gt;sklearn.svm.LinearSVC&lt;/kbd&gt; to the projected data in order to verify that the clusters are in fact well informed by the class labels.  The &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Phi_coefficient&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;MCC&lt;/a
&gt;
 value is annotated in the title of the each figure.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;optimizing-signal-to-noise-ratio&#34;
    &gt;Optimizing Signal to Noise Ratio&lt;a href=&#34;#optimizing-signal-to-noise-ratio&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;As a final example, let&amp;rsquo;s examine a problem in signal processing.  Let \(X \in \R^{n}\) be an &lt;em&gt;unobserved&lt;/em&gt; zero mean random vector with variance \(\Sigma\) and \(E \in \R^{n}\) be another zero-mean and unobserved random vector with variance \(\Omega\).  The observed quantity is given by \(Y = X + E\), &lt;em&gt;i.e.&lt;/em&gt;, a noisy measurement of the signal of interest.&lt;/p&gt;
&lt;p&gt;Similarly to PCA, we will find a matrix \(V \in \R^{n \times k}\) to project \(Y\) onto some lower dimensional space, but this time we will optimize the &lt;em&gt;signal-to-noise ratio&lt;/em&gt; (SNR), where the &amp;ldquo;signal&amp;rdquo; is \(V^{\mathsf{T}} X\) (containing information about the vector \(X\) that we care about) and the &amp;ldquo;noise&amp;rdquo; is \(V^{\mathsf{T}} E\) (which is just corrupted by \(E\)).  This leads to the optimization problem:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{V \in \R^{n \times k}}{\text{maximize}}&amp;amp;\ \frac{\mathbb{E}||V^{\mathsf{T}} X||_2^2}{\mathbb{E}||V^{\mathsf{T}} E||_2^2}\\
\text{subject to}&amp;amp;\ V^{\mathsf{T}} V = I.
\end{aligned}\tag{$\mathcal{P}$}
\end{equation}&lt;/p&gt;
&lt;p&gt;This is a generalization of PCA since if \(E\) is isotropic, \(\mathbb{E}||V^{\mathsf{T}} E||_2^2\) is a constant given the constraint, and can be dropped.  Moreover, this problem is different from the GEVPs encountered earlier, because we enforce the constraint that \(V^{\mathsf{T}}V = I\), as opposed to \(V^{\mathsf{T}}\Omega V = I\).&lt;/p&gt;
&lt;p&gt;While this problem doesn&amp;rsquo;t immediately take the form of an eigenvalue problem, we can relate it to one with some clever manipulations (essentially drawn from &lt;a
    class=&#34;link&#34;
    href=&#34;https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=0d185e6de595bd3844909d3606e9218a498a9bd8&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kokiopoulou et. al.&lt;/a
&gt;
).  Supposing that \(s^\star &amp;gt; 0\) is the optimal SNR, it must be the case that \[\frac{\mathbb{E}||V^{\mathsf{T}} X||_2^2}{\mathbb{E}||V^{\mathsf{T}} E||_2^2} \le s^\star\] for every orthogonal matrix $V.$  Therefore, denoting by \(\mathcal{O}_k\) the set of \(n \times k\) matrices with orthogonal columns, we have&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\forall V \in \mathcal{O}_k:\ \mathbb{E}||V^{\mathsf{T}} X||_2^2 - s^\star \mathbb{E}||V^{\mathsf{T}} E||_2^2 &amp;amp;\le 0\\
\implies \forall V \in \mathcal{O}_k:\ \mathbb{E} \mathsf{tr}\ V^{\mathsf{T}} XX^{\mathsf{T}} V - s^\star \mathbb{E} \mathsf{tr}\ V^{\mathsf{T}} EE^{\mathsf{T}} V &amp;amp; \le 0\\
\implies \forall V \in \mathcal{O}_k:\ \mathsf{tr}\ V^{\mathsf{T}} \Sigma V - s^\star \mathsf{tr}\ V^{\mathsf{T}} \Omega V &amp;amp;\le 0\\
\implies \forall V \in \mathcal{O}_k:\ \mathsf{tr}\ V^{\mathsf{T}} \bigl(\Sigma - s^\star \Omega\bigr) V &amp;amp;\le 0\\
\implies \underset{V \in \mathcal{O}_k}{\text{max}}\ \mathsf{tr}\ V^{\mathsf{T}} \bigl(\Sigma - s^\star \Omega\bigr) V &amp;amp;\le 0.\\
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;From what we know of eigenvalue problems, the maximizer \(V\) in \(\mathcal{O}_k\) of \(\mathsf{tr}\ V^{\mathsf{T}} \bigl(\Sigma - s \Omega \bigr) V\) is given by the last \(k\) eigenvectors of the matrix \(\Sigma - s \Omega\).  Lets call these \(V_s\).  We want to maximize the SNR, and thus find the largest \(s\) which satisfies the above inequalities &amp;ndash; i.e., the optimal value \(s^\star\) should be a root of the function \[\mathcal{S}(s) = \mathsf{tr}\ V^{\mathsf{T}}_s \bigl( \Sigma - s \Omega \bigr) V_s,\] a function which can be evaluated through an eigendecomposition of \(\Sigma - s \Omega\).  (&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: expressions like $\Sigma - s\Omega$ involving a matrix minus a scalar times another matrix are often referred to as &lt;em&gt;matrix pencils&lt;/em&gt;.  I am told they are named after the concept&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Pencil_%28geometry%29&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;in geometry&lt;/a
&gt;
.)&lt;/p&gt;
&lt;p&gt;To check that this can actually occur, notice that since \(\Omega \succ 0\), the function \(\mathcal{S}\) must be &lt;em&gt;monotone decreasing&lt;/em&gt; on \(\R_+\).  To establish the existence of an \(s^\star\) such that \(\mathcal{S}(s^\star) = 0\), and to apply bisection search, we need to show that \(\mathcal{S}\) does actually cross \(0\).  To this end, \(\mathcal{S}(0) &amp;gt; 0\) (since \(\Sigma \succ 0\)).  Since \(\mathcal{S}\) is strictly monotone, this is enough, but we&amp;rsquo;d like to find a compact interval where we know \(s^\star\) lies.  I show in the detail below that \(\mathcal{S}(s) &amp;lt; 0\) for any \(s &amp;gt; \lambda_n(\Sigma, \Omega)\), the largest generalized eigenvalue.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;Generalized Eigenvalue Decomposition&lt;/summary&gt;
  &lt;p&gt;Supposing that \(\Lambda = \mathsf{Dg}(\lambda_1, \ldots, \lambda_n)\) is a diagonal matrix of generalized eigenvalues and \(V \in \mathbb{R}^{n \times n}\) contains the generalized eigenvectors in the columns, we can write the GEVP as \[\Sigma V = \Omega V \Lambda.\]  Then, since \(V^{\mathsf{T}} \Omega V = I\) we have \(VV^{\mathsf{T}} \Omega V = V\) which, since \(V\) is invertible (we know it is a basis for \(\R^n\)) implies that \(VV^{\mathsf{T}} \Omega = I\).  Multiplying the GEVP on the right by \(V^{\mathsf{T}} \Omega\) then provides to us a generalized eigenvalue decomposition of \(\Sigma\) with respect to \(\Omega\): \[\Sigma = \Omega V \Lambda V^{\mathsf{T}} \Omega.\]&lt;/p&gt;
&lt;p&gt;We can use these facts to analyze the function \(\mathcal{S}\) as in&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathcal{S}(s)
&amp;amp;= \mathsf{tr}\ (\Sigma - s\Omega)\\
&amp;amp;= \mathsf{tr}\ (\Omega V \Lambda V^{\mathsf{T}} \Omega - s\Omega)\\
&amp;amp;= \mathsf{tr}\ \Omega (V \Lambda V^{\mathsf{T}} \Omega - sI)\\
&amp;amp;= \mathsf{tr}\ \Omega (V \Lambda V^{\mathsf{T}} \Omega - sVV^{\mathsf{T}} \Omega)\\
&amp;amp;= \mathsf{tr}\ \Omega V (\Lambda - sI) V^{\mathsf{T}} \Omega \\
&amp;amp;= \mathsf{tr}\ (\Lambda - sI)V^{\mathsf{T}} \Omega^2 V, \\
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;and since \(V^{\mathsf{T}} \Omega^2 V \succ 0\) we must have \(\mathcal{S}(s) &amp;lt; 0\) for any \(s &amp;gt; \lambda_n\).  \(\ \square\)&lt;/p&gt;

&lt;/details&gt;

&lt;p&gt;Now, it follows that \(s^\star\) is the &lt;em&gt;unique&lt;/em&gt; root of the monotone function \(\mathcal{S}\) and it lies in the interval \([0, \lambda_n(\Sigma, \Omega)]\).  Thus, we can find \(s^\star\) by performing bisection on \(\mathcal{S}\), and then recover the optimal matrix \(V_{s^\star}\) by computing the last \(k\) eigenvectors of \(\Sigma - s^\star \Omega\).  The value \(s^\star\), remember, is also the value of the optimal SNR.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;For the Reader&lt;/strong&gt;: I&amp;rsquo;ve glossed over another property that should be verified for $\mathcal{S}$ before making these conclusions.  What is it and why does it hold?&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;An example of this technique is given for the same faces dataset as the PCA example, where I&amp;rsquo;ve corrupted the images with noise that is locally correlated in space.  Clearly the result is not particularly &lt;strong&gt;good&lt;/strong&gt;, in an absolute sense, but the PCA reconstruction is completely corrupted by the anisotropic noise distribution.&lt;/p&gt;


&lt;p&gt;Some code to implement this technique in &lt;a
    class=&#34;link&#34;
    href=&#34;https://julialang.org/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Julia&lt;/a
&gt;
 is below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-julia&#34; data-lang=&#34;julia&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;using&lt;/span&gt; LinearAlgebra;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;function&lt;/span&gt; snr_pca(Σ, Ω, V)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;The objective function.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; tr(V&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; Σ &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; V) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; tr(V&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; Ω &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; V)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;function&lt;/span&gt; optimal_snr_pca(Σ, Ω, k, eps&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1e-6&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Computes the optimal projection matrix.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; size(Σ)[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    S &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; (sum &lt;span style=&#34;color:#ff79c6&#34;&gt;∘&lt;/span&gt; eigvals)(Σ &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; s &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; Ω, n &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    V &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; eigvecs(Σ &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; s &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; Ω)[&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt;, n &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; k &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; n]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s_max &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; eigvals(Σ, Ω)[n]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s_star &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; bisection(S, s_max, eps)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    V_star &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; V(s_star)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; V_star, s_star
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;function&lt;/span&gt; bisection(S, s_max, eps&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1e-6&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Finds the zero of a monotone decreasing function in [0, s_max].&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s_left &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s_right &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s_max
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;while&lt;/span&gt; abs(s_right &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; s_left) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; eps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s_left &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; (s_right &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; s_left) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; S(s) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            s_left &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            s_right &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; s_left
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;This blog examines the theory and applications of eigenvalue problems and generalized eigenvalue problems.  We&amp;rsquo;ve seen through the Spectral Theorem that the symmetry of the matrix is essential for obtaining real-valued eigenvalue decompositions (and thus an ordering to the eigenvalues).  These eigenvalues can be characterized by variational problems, and useful applied problems (particularly PCA and its weighted version, and FLD) can be formulated in the same optimization problems.&lt;/p&gt;
&lt;p&gt;The key implication of this is that these optimization problems admit of efficient solutions: eigenvalue decompositions.  These optimization problems are by no means convex, yet they still admit of efficient solutions through the use of &lt;kbd&gt;scipy.linalg.eigh&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve also seen some &amp;ldquo;bonus&amp;rdquo; relationships with the singular value decomposition in the form of the Orthogonal Procrustes Problem, and the fact that you can compute the eigenvalues needed for PCA directly from \(X\), rather than having to form \(\widehat{\Sigma} = \frac{1}{N}X^{\mathsf{T}}X\).  As well, the detailed computations for signal to noise ratio optimization show us the generalized eigenvalue decomposition \(\Sigma = \Omega V \Lambda V^{\mathsf{T}}\Omega\) of \(\Sigma\) with respect to \(\Omega\).  This is another useful decomposition which generalizes the &amp;ldquo;vanilla&amp;rdquo; eigenvalue decomposition \(\Sigma = U \Lambda U^{\mathsf{T}}\).&lt;/p&gt;
&lt;p&gt;These problems present plenty of further interesting avenues for exploration, which I invite the reader to meditate upon :)&lt;/p&gt;</description></item><item>
            <title>Nature&#39;s Dartboard: The Axioms of Probability</title>
            <link>https://rjtk.github.io/posts/natures-dartboard-the-axioms-of-probability/</link>
            <pubDate>Sun, 23 Apr 2023 00:00:00 -0700</pubDate>
            <guid>https://rjtk.github.io/posts/natures-dartboard-the-axioms-of-probability/</guid><description>&lt;p&gt;The basic axioms of probability and the idea of a random variable as a function from the sample space to \(\mathbb{R}\) are quite abstract and rather confusing at first pass.  However, the axioms can be well motivated from our intuition, and defining random variables simply as functions turns out to be a brilliant and intuitive abstraction.  My goal in this post is to try to explain the ideas behind the axiomatization of probability theory, and hopefully make the study of measure-theoretic probability seem a bit less intimidating.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;mathematics-and-axiomatization&#34;
    &gt;Mathematics and Axiomatization&lt;a href=&#34;#mathematics-and-axiomatization&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Exactly what is &lt;em&gt;Mathematics&lt;/em&gt; is not necessarily an easy thing to pin down, and to try to give some sort of clear formal definition would be senseless.  However, it is easy to say what it is &lt;em&gt;not&lt;/em&gt;: it is not simply a prescribed set of rules for calculating numbers, or &amp;ldquo;solving for \(x\)&amp;rdquo; &amp;ndash; a prescribed set of calculating rules has another name: an &lt;em&gt;algorithm&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In contrast to the mechanical process described by an algorithm, mathematics is a highly creative endeavour.  Indeed, many mathematicians are motivated largely by the aesthetics and the &amp;ldquo;beauty&amp;rdquo; of their results.  To many mathematicians, it is the abstraction and the concepts themselves that are of interest, and they may not even be entirely interested in whether or not the results and calculations are \(100\%\) correct!  A great book about the process of doing math (which can be got for rather low prices) is &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/41Rgdeb&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;The Mathematical Experience&lt;/a
&gt;
 💁‍♂️&lt;/p&gt;
&lt;p&gt;Still, many mathematicians are at the same time concerned with correctness and &lt;em&gt;rigor&lt;/em&gt;.  That is, they want to be able to say with supreme confidence that their results are true and correct, with no room for doubt.  Essential to this is the idea of a mathematical &lt;em&gt;proof&lt;/em&gt; &amp;ndash; a sequence of logical deductions which begin from some specified premises \(P\) and lead inexorably to some conclusion \(Q\).  If the logical deductions are correct, then the mathematician has rigorously obtained a proof of the implication \(P \implies Q\).  That is, given that the premises \(P\) are true, it must be the case that the conclusions \(Q\) are true.  Mathematicians call this \(P \implies Q\) implication a &lt;em&gt;Theorem&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Once a mathematician (or a group of them, perhaps spanning continents and generations) has amassed a rather sizable number of theorems, and understands something about the various connections amongst them, the mathematician might attempt to lay down a &lt;em&gt;fundamental&lt;/em&gt; set of premises from which all of their conclusions can be derived.  This set of fundamental premises, which cannot be derived (as far as the mathematician can tell) from any other still more fundamental premises, are called &lt;em&gt;axioms&lt;/em&gt;.  The axioms are the basic starting point for a &lt;em&gt;theory&lt;/em&gt;, that is, for all the further conclusions the mathematician has derived.  Axioms are ultimately just premises, but the former term tends to imply that it is a &lt;em&gt;foundational&lt;/em&gt; premise &amp;ndash; &lt;em&gt;i.e.&lt;/em&gt;, something that is self-evident that can serve as a starting point.&lt;/p&gt;
&lt;p&gt;The most famous set of axioms are &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Euclidean_geometry&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Euclid&amp;rsquo;s Axioms&lt;/a
&gt;
 laying the classical foundation for geometry.  Another being those of &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_theory&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ZF Set Theory&lt;/a
&gt;
, which lay the foundations for set theory, and therefore of more-or-less all of modern &amp;ldquo;mainstream&amp;rdquo; mathematics.  The axioms I care about discussing in this post are the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Probability_axioms&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;axioms of Probability Theory&lt;/a
&gt;
.&lt;/p&gt;
&lt;p&gt;As opposed to the rigorous series of logical deductions by which proofs proceed, the way that the &lt;em&gt;idea&lt;/em&gt; of a Theorem comes about (&lt;em&gt;i.e.&lt;/em&gt;, the thought that some implication might possibly be true) is much more creative and messy.  Often, one begins with a conclusion \(Q\) that &amp;ldquo;seems true&amp;rdquo;, and then proceeds to search for the set of premises \(P\) that &lt;em&gt;make&lt;/em&gt; it true, possibly going back and forth many times tweaking the premises and the conclusion &amp;ndash; a process reminiscent of Rawl&amp;rsquo;s &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Reflective_equilibrium&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;reflective equilibrium&lt;/a
&gt;
 in philosphy.  The mathematician is done with this inquiry when they have obtained a Theorem which they think is &amp;ldquo;good&amp;rdquo;.  I have seen it claimed that a Theorem is &lt;em&gt;good&lt;/em&gt; if it is surprising, deep, and can be used to derive other good theorems.  My memory tells me that this should be attributed to Polya, but I cannot find a reference (please email me if you know where this is from!).&lt;/p&gt;
&lt;p&gt;As well, we usually come up with axioms long after having figured out most of the conclusions (possibly using stronger or more numerous premises).  Axiomatization is a process of &lt;em&gt;cleaning up&lt;/em&gt; the theory and placing it upon a nice, beautiful, and simple foundation.  Thus, I think it can be rather confusing, from a didactic standpoint, to write textbooks or teach classes which simply begin with the axioms and proceed to derive all of the conclusions &amp;ndash; the student is left confused and wondering what the axioms mean, where did they come from, &lt;em&gt;etc&lt;/em&gt;.  To be comfortable in following this process straight from the axioms requires some amount of experience and mathematical maturity.&lt;/p&gt;
&lt;p&gt;Indeed, the process of derivation from a set of axioms is not reflective of how the theory was developed, and it is hardly even reflective of how mathematicians think about the theory themselves.  My hope is that the rest of this post can clarify some of the intuition behind the axioms of probability.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;the-axioms-of-probability&#34;
    &gt;The Axioms of Probability&lt;a href=&#34;#the-axioms-of-probability&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The purpose of probability theory is to provide a set of mathematical tools for reasoning about events which are &lt;em&gt;random&lt;/em&gt;.  Exactly what random &lt;em&gt;means&lt;/em&gt; is on one hand left to intuition (&lt;em&gt;e.g.&lt;/em&gt;, we think of a dice roll as random, because we have no practical means of determining what the outcome will be), and on the other hand, is rigorously axiomatized by the theory of probability.  My purpose here is to help to make the axiomatization itself more intuitive.&lt;/p&gt;
&lt;p&gt;The rigorous theory of probability was axiomatized in 1933 by Russian mathematician &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Andrey_Kolmogorov&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Andrey Kolmogorov&lt;/a
&gt;
 (1903-1987).  This axiomatization begins with a set \(\Omega\) called the &lt;em&gt;sample space&lt;/em&gt;.  What this set is exactly need not be fully specified &amp;ndash; it is just some abstract set, and it may be a finite set like \(\{\mathsf{H}, \mathsf{T}\}\) (to model flipping a coin), it may be a countably infinite set like \(\{1, 2, \ldots\}\), it could be an uncountably infinite set like \(\mathbb{R}\), or some even more abstract set.&lt;/p&gt;
&lt;p&gt;To set the stage, the following sections will introduce the probability triple \((\Omega, \mathcal{F}, \mathbb{P})\) consisting of a sample space \(\Omega\), a $σ$-algebra \(\mathcal{F}\) thereof, and the probability measure \(\mathbb{P}\).  On this triple we axiomatize probability with the following three premises:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The domain of \(\mathbb{P}\) is \(\mathcal{F}\) and for any \(S \in \mathcal{F}\) we have \(P(S) \ge 0\) (non-negativity)&lt;/li&gt;
&lt;li&gt;\(\mathbb{P}(\Omega) = 1\) (Normalization)&lt;/li&gt;
&lt;li&gt;If \(S_1, S_2, \ldots \in \mathcal{F}\) are disjoint, then \(\mathbb{P}\bigl(\bigcup_{i = 1}^\infty S_i \bigr) = \sum_{i = 1}^\infty \mathbb{P}(S_i)\) (Countable Additivity)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end of this section, I hope the reader will have some understanding of what all of this means, and why these axioms are so beautiful.  It is remarkable that probability theory flows from these three simple statements!&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;samples-and-events&#34;
    &gt;Samples and Events&lt;a href=&#34;#samples-and-events&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;In probability theory, an element \(\omega \in \Omega\) of the sample space is called a &lt;em&gt;sample&lt;/em&gt;.  We don&amp;rsquo;t know what this sample is, but it encodes the &lt;strong&gt;state of the universe&lt;/strong&gt;.  Subsets \(S \subseteq \Omega\) are called &lt;em&gt;events&lt;/em&gt; (actually, they must be elements \(S \in \mathcal{F}\), which is a set of subsets of \(\Omega\) &amp;ndash; more on this later), and we imagine that an event has &lt;em&gt;occurred&lt;/em&gt; if \(\omega \in S\).&lt;/p&gt;
&lt;p&gt;Intuitively, there are many states of the universe that might lead to the same event; e.g., the outcome of a coin toss ultimately depends upon, at least, the arrangement of molecules in the air through which the coin travels, and there are many more than two such arrangements.  Thus, there are many \(\omega\) (arrangements of molecules) which result in the &amp;ldquo;Heads&amp;rdquo; outcome of the coin toss.&lt;/p&gt;
&lt;p&gt;When we do modelling in practice, we usually don&amp;rsquo;t work directly with \(\Omega\).  Instead, we work with &lt;em&gt;random variables&lt;/em&gt;, which are functions from the sample space into \(\mathbb{R}\).  The sample space is kept as an abstract entity &amp;ldquo;in the background&amp;rdquo;.  But, before we discuss this additional layer of abstraction, we need to understand \((\Omega, \mathcal{F}, \mathbb{P})\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;nature-s-dartboard&#34;
    &gt;Nature&amp;rsquo;s Dartboard&lt;a href=&#34;#nature-s-dartboard&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;On an &lt;em&gt;intuitive&lt;/em&gt; level, I think of the set \(\Omega\) as being &lt;em&gt;Nature&amp;rsquo;s Dartboard&lt;/em&gt; &amp;ndash; it is just a disc, and &amp;ldquo;Mother Nature&amp;rdquo; is throwing a dart at it.  The exact location at which the dart lands selects for us a state of the universe \(\omega \in \Omega\).&lt;/p&gt;


&lt;p&gt;In addition to the set \(\Omega\), we are given a &lt;em&gt;function&lt;/em&gt; \(\mathbb{P}\) called a &lt;em&gt;probability measure&lt;/em&gt;.  The function \(\mathbb{P}\) is a bit more abstract than the sort of function (a curve) that one might usually encounter in calculus.  Instead, \(\mathbb{P}\) is a function which maps from &lt;em&gt;subsets&lt;/em&gt; of \(\Omega\) into some number between \([0, 1]\).  That is, if we have some set \(S \subset \Omega\), we would like \(\mathbb{P}\) to assign some number to \(S\) as in \(\mathbb{P}(S)\).  This number is called the probability of the event (subset), and is directly analogous to the &lt;em&gt;volume&lt;/em&gt; of \(S\).&lt;/p&gt;
&lt;p&gt;Indeed, if we imagine that Mother Nature throws her dart at the dartboard \(\Omega\) completely &amp;ldquo;at random&amp;rdquo; (following our intuition) then our intuitive idea of the probability of the dart landing within some region \(S \subseteq \Omega\) of the dartboard is nothing but \(\text{vol}(S) / \text{vol}(\Omega)\), where \(\text{vol}(S)\) measures the &lt;em&gt;volume&lt;/em&gt; (in this case, just an &lt;em&gt;area&lt;/em&gt;) of the set \(S\).&lt;/p&gt;
&lt;p&gt;Now, if we imagine that \(\mathbb{P}(S) = \text{vol}(S) / \text{vol}(\Omega)\), then the axiom \(\mathbb{P}(S) \ge 0\) is natural since volumes cannot be negative, and \(\mathbb{P}(\Omega) = 1\) is elementary algebra.  &lt;strong&gt;&lt;strong&gt;This is the basic intuition&lt;/strong&gt;&lt;/strong&gt; of the axioms of probability &amp;ndash; we measure the probability of &lt;strong&gt;events&lt;/strong&gt; \(S\), which are nothing but subsets of the sample space \(\Omega\), by appealing to the volume of the dartboard that they occupy.&lt;/p&gt;
&lt;p&gt;Continuing this line of reasoning, imagine we have some set \(S \subseteq \Omega\) which has some probability \(\mathbb{P}(S)\).  If we break this set into two pieces (&lt;em&gt;i.e.,&lt;/em&gt; two disjoint sets \(S_1, S_2\) with \(S_1 \cup S_2 = S\) and \(S_1 \cap S_2 = \emptyset\)) then it stands to reason that we should have \[\mathbb{P}(S) = \mathbb{P}(S_1) + \mathbb{P}(S_2),\] since the volume of one set must be the sum of the volumes of any &lt;strong&gt;partition&lt;/strong&gt; of that set.  This is the idea behind Axiom 3, &lt;strong&gt;additivity&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That Axiom 3 is stated for &lt;em&gt;countably infinite&lt;/em&gt; partitions of events (&lt;em&gt;i.e.&lt;/em&gt;, infinite sums) is a technical condition: &lt;strong&gt;countable additivity&lt;/strong&gt;.  Simple finite additivity turns out to not be strong enough to result in a rich enough theory.  Making the axioms works &lt;em&gt;rigorously&lt;/em&gt; for these infinite sums, and for infinite sample spaces, is the whole point of the formalism surrounding the $σ$-algebra \(\mathcal{F}\).  We turn to this next.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;the-banach-tarski-paradox-volume-and-measure&#34;
    &gt;The Banach-Tarski Paradox: Volume and Measure&lt;a href=&#34;#the-banach-tarski-paradox-volume-and-measure&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The object \(\mathcal{F}\) is called a $σ$-algebra of subsets of \(\Omega\), and it is in general a subset of the set of all subsets of \(\Omega\).  Understanding this $σ$-algebra is part of the difficult technical work that goes into learning rigorous probability theory, but the &lt;strong&gt;purpose&lt;/strong&gt; of \(\mathcal{F}\) is to eliminate certain pathological sets in \(2^\Omega\) which are not &lt;strong&gt;measurable&lt;/strong&gt; (see: &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Measure_%28mathematics%29&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Measure Theory&lt;/a
&gt;
).  That is, there are some sets (&lt;em&gt;e.g.&lt;/em&gt;, &lt;a
    class=&#34;link&#34;
    href=&#34;https://proofwiki.org/wiki/Vitali_Theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Vitali Sets&lt;/a
&gt;
) for which it simply makes no sense to assign a notion of volume, and the issue arises directly as a result of dealing with infinite sets.&lt;/p&gt;
&lt;p&gt;A famous example illustrating this issue is the &lt;strong&gt;Banach-Tarski&lt;/strong&gt; paradox.  This paradox shows that we can take a sphere, partition it into six pieces, and then, after rotating some of those pieces in
a clever way, reassemble &lt;strong&gt;two&lt;/strong&gt; copies of the original sphere.  Here is a &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.youtube.com/watch?v=s86-Z-CbaHA&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Video explanation&lt;/a
&gt;
 of the procedure as well as a &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.quantamagazine.org/how-a-mathematical-paradox-allows-infinite-cloning-20210826/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Quanta Magazine article&lt;/a
&gt;
.  The fact that we can take a set, partition that set, and then reassemble &lt;strong&gt;two&lt;/strong&gt; identical copies of that set is rather problematic when it comes to assigning volumes!  It seems that we can derive \(\mathbb{P}(S) = 2\mathbb{P}(S)\) where \(S\) is a sphere!  This is a pathology that needs to be eliminated.&lt;/p&gt;
&lt;p&gt;Indeed, we would really like for it to be the case that \(\mathbb{P}(A \cup B) = \mathbb{P}(A) + \mathbb{P}(B)\) if \(A \cap B = \emptyset\) and where \(\mathbb{P}(S) = \text{vol}(S) / \text{vol}(\Omega)\) is consistent with our notion of volume.  But, this can only be done if we simply &lt;strong&gt;exclude a-priori&lt;/strong&gt; so-called non-measurable sets.  This is more-or-less what the $σ$-algebra \(\mathcal{F}\) is doing &amp;ndash; it is a special collection of subsets of \(\Omega\) which ensures that any \(S \in \mathcal{F}\) is a measurable set to which we can assign a notion of volume that corresponds with our intuition.  In the language of probability, it means that only certain subsets of \(\Omega\) can be assigned a probability: the measurable subsets.  There are some pathological subsets of Nature&amp;rsquo;s dartboard which we simply cannot measure.&lt;/p&gt;
&lt;p&gt;Specifically, a collection of subsets \(\mathcal{F}\) of \(\Omega\) is a $σ$-algebra of subsets if the following conditions are satisfied:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;\(\Omega \in \mathcal{F}\) (The universe is one of the sets)&lt;/li&gt;
&lt;li&gt;\(A, B \in \mathcal{F} \implies A \setminus B \in \mathcal{F}\) (Closed under set differences)&lt;/li&gt;
&lt;li&gt;If \(A_1, A_2, \ldots \in \mathcal{F}\) and \(A_i \cap A_j = \emptyset\) if \(i \ne j\), then \(\bigcup_{i = 1}^\infty A_i \in \mathcal{F}\) (Closed under countable disjoint union)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that these are basically nothing but the conditions that make the axioms of probability &amp;ldquo;work&amp;rdquo;, particularly closure under countable unions.  That is, the notion of a $σ$-algebra is cooked up exactly for the purpose of making sure that measure corresponds to our intuition of volume.&lt;/p&gt;
&lt;p&gt;To actually construct a volume measure for the dartboard, one begins by constructing a measure on the real line \(\mathbb{R}\) by defining \(\mathbb{P}([a, b]) = b - a\), and then generating a $σ$-algebra out of intervals (by taking complements and countable unions &lt;em&gt;etc&lt;/em&gt;.).  This guarantees that the resulting \(\sigma\) algebra is generated by &amp;ldquo;reasonable&amp;rdquo; operations on the sets, excluding those which are not measurable.  Then, by putting together the rules of the $σ$-algebra above, and using the basic intuition of \(\mathbb{P}([a, b]) = b - a\), we can eventually construct what is called &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Lebesgue_measure&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Lebesgue Measure&lt;/a
&gt;
, a rigorous way of assigning volume to (measurable) subsets of \(\mathbb{R}\).  This is enough to get us to a suitable notion of volume, and therefore to a construction of a probability measure \(\mathbb{P}\)!&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Axiom of Choice): The problem of measure is not entirely inescapable.  If we did not accept the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Axiom_of_choice&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Axiom of Choice&lt;/a
&gt;
, &lt;kbd&gt;which basically just says that it is possible to choose an arbitrary element from an infinite set, then non-measurable sets could not be constructed, and the problem would disappear.  Some mathematicians thus view the existence of non-measurable sets, which are incredibly pathological, as a reason to refuse to accept the axiom of choice.  However, we need to pick our poison; there are at the same time a vast number of perfectly intuitive and incredibly useful theorems which &lt;em&gt;depend&lt;/em&gt; on the axiom of choice.  My favourite example being the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Hahn%E2%80%93Banach_theorem#Hahn.E2.80.93Banach_separation_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hanh-Banach Theorem&lt;/a
&gt;
, &lt;kbd&gt;the cornerstone of convex analysis (named after the same Banach as the Banach-Tarski Paradox).  Instead of doing without more-or-less all of functional analysis, most instead choose to accept non-measurable sets as a fact of (mathematical) life, and carry around all the baggage associated with $\sigma$-algebras and measure theory.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;random-variables&#34;
    &gt;Random Variables&lt;a href=&#34;#random-variables&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;All of the formalism laid out in understanding the probability triple \((\Omega, \mathcal{F}, \mathbb{P})\) is rather complicated.  Fortunately, in most &amp;ldquo;practical&amp;rdquo; scenarios where we want to &lt;em&gt;use&lt;/em&gt; probability for modelling, we just let this formalism sit back as an abstract foundation, more-or-less out of sight.  We instead work more directly with &lt;em&gt;random variables&lt;/em&gt; which are defined as nothing but &lt;em&gt;functions from \(\Omega\) into \(\mathbb{R}\)&lt;/em&gt; as in: \[X: \Omega \rightarrow \mathbb{R},\] sometimes written as \(X(\omega)\).&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;randomness&amp;rdquo; thus passes from Nature&amp;rsquo;s Dartboard \(\Omega\), an abstract set that we don&amp;rsquo;t want to think about too much, to \(\mathbb{R}\), a natural set that we like to work with.  The way this works is as follows &amp;ndash; suppose we want to compute the probability that \(X(\omega)\) is less than some real number \(x\), that is \(F(x) = \mathbb{P}(X \le x)\).  This is an important function called the &lt;em&gt;cumulative distribution function&lt;/em&gt; of \(X\) and it turns out to be enough to fully determine the random behaviour of \(X\).  The notation \(\mathbb{P}(X \le x)\) is really nothing but a shorthand for \[F(x) = \mathbb{P}(\{\omega: X(\omega) \le x\}).\]&lt;/p&gt;
&lt;p&gt;That is, the common notation \(\mathbb{P}(X \le x)\), which is used to mean &amp;ldquo;the probability that the random variable \(X\) is less than or equal to the real number \(x\)&amp;rdquo; is really a shorthand for \(\mathbb{P}(\{\omega:\ X(\omega) \le x\})\) which is &amp;ldquo;the volume of the set of all events \(\omega\) such that the function \(X\) evaluated \(X(\omega)\) is less than or equal to the real number \(x\)&amp;rdquo;.  We can simplify this further:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
F(x)
&amp;amp;= \mathbb{P}(\{\omega: X(\omega) \le x\})\\
&amp;amp;= \mathbb{P}(\{\omega: X(\omega) \in (-\infty, x]\})\\
&amp;amp;= \mathbb{P}(X^{-1} ((-\infty, x]))\\
&amp;amp;= \mathbb{P} \circ X^{-1} ((-\infty, x]).
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;These elementary manipulations show us that that the cumulative distribution function \(F\) is nothing but the composition of the probability measure \(\mathbb{P}\) with the inverse mapping of \(X\) (the domain of which are all the half-open intervals like \((-\infty, x]\))!  What does this mean?  It allows us to define events on \(\mathbb{R}\), like the probability of the event \(X \le x\) by measuring the size of the set of all \(\omega\) which satisfy \(X(\omega) \le x\).  So, what we do &amp;ldquo;in practice&amp;rdquo; is that we start by positing a distribution function \(F\) (like a Gaussian distribution) which we want to work with on \(\mathbb{R}\), and then we just trust that there is some appropriate probability triple \((\Omega, \mathcal{F}, \mathbb{P})\) which is &amp;ldquo;adequately expressive&amp;rdquo; and some function \(X\) such that \(F = \mathbb{P} \circ X^{-1}\).  The function \(X\) is now a random variable having &lt;em&gt;c.d.f.&lt;/em&gt; \(F\).  This gives us a huge variety of possibilities for modelling, and the randomness on \(\mathbb{R}\) is inherited from the simple and intuitive notion of randomness as volume on Nature&amp;rsquo;s dartboard.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Measurable Functions): Technically, the random variable $X$ must be a&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Measurable_function&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;measurable function&lt;/a
&gt;
.  &lt;kbd&gt;This is another important technical condition, but irrelevant for developing an intuition.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Functions of Random Variables): Suppose we have some function $g: \mathbb{R} \rightarrow \mathbb{R}$.  It is worthwhile to point out that a function of a random variable $g(X)$ defines a &lt;em&gt;new&lt;/em&gt; random variable $Y(\omega) = g \circ X(\omega)$, since it is nothing but a another function from $\Omega$ to $\mathbb{R}$.  The cumulative distribution function of this new random variable is given by $F_Y(y) = \mathbb{P} \circ X^{-1} \circ g^{-1}((-\infty, y])$.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;expectation&#34;
    &gt;Expectation&lt;a href=&#34;#expectation&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Another essential concept in probability theory is that of the &lt;strong&gt;expectation&lt;/strong&gt;.  Intuitively, this is like a generalization of an &lt;em&gt;average&lt;/em&gt;.  If the sample space \(\Omega\) is finite, then we &lt;em&gt;define&lt;/em&gt; \[\mathbb{E}X = \sum_{i = 1}^N X(\omega) \mathbb{P}(\omega),\] and if \(\mathbb{P}(\omega) = \frac{1}{N}\) then it is exactly an average of the random variable over the sample space.  Generally, finite sample spaces aren&amp;rsquo;t rich enough to be interesting, so we define the expectation by means of an integral \[\mathbb{E}X = \int_\Omega X(\omega) \mathsf{d}\mathbb{P}(\omega).\]  This is called a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Lebesgue_integration&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Lebesgue Integral&lt;/a
&gt;
, and our desire to define and use this integral is another part of the motivation for studying probability in the context of measure theory.  Regardless, it is usually possible to dispense with this integral &amp;ldquo;in practice&amp;rdquo; and to work directly with the distribution function \(F\).  This works as follows:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\mathbb{E}X
&amp;amp;= \int_\Omega X(\omega) \mathsf{d}\mathbb{P}(\omega)\\
&amp;amp;\overset{(a)}{=} \int_{X(\Omega)} x\ \mathsf{d} \mathbb{P} \circ X^{-1} (x)\\
&amp;amp;\overset{(b)}{=} \int_{\mathbb{R}} x\ \mathsf{d} F(x)\\
&amp;amp;\overset{( c)}{=} \int_{-\infty}^\infty xf(x)\ \mathsf{d}x,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \((a)\) comes from making the substitution \(x = X(\omega)\), \((b)\) just simplifies using \(F = \mathbb{P} \circ X^{-1}\), and \(( c)\) is a final simplification for &lt;strong&gt;continuous&lt;/strong&gt; random variables many readers may be used to: if \(F\) is a differentiable function, then the random variable admits a &lt;strong&gt;density function&lt;/strong&gt; \(f(x) = F^\prime(x)\) and \(\mathsf{d}F(x)\) turns into \(f(x)\mathsf{d}x\), resulting in an &amp;ldquo;ordinary&amp;rdquo; Riemann integral (as opposed to \(\int_\mathbb{R} x \mathsf{d} F(x)\) which is a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Lebesgue%E2%80%93Stieltjes_integration&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Lebesgue-Stieltjes integral&lt;/a
&gt;
).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Expectations of Functions of Random Variables): Given a function $g: \mathbb{R} \rightarrow \mathbb{R}$, we may like to compute the expectation of the new random variable $g(X)$ as in $\mathbb{E}g(X) = \int_\Omega g(X(\omega)) \mathsf{d} \mathbb{P}(\omega).$ We could do so by determining the distribution function $G(x) = \mathbb{P}(g(X) \le x)$ of the new random variable $g(X)$ and then doing the calculation $\int_\mathbb{R} x \mathbb{d} G(x)$.  But alternatively, we can see by repeating the same substitution $x = X(\omega)$ in the integral that we made earlier that $\mathbb{E}g(X) = \int_\mathbb{R} g(x) \mathsf{d} F(x)$ which is still just an integral over the distribution of $X$!  This is an incredibly convenient fact called the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Law_of_the_unconscious_statistician&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Law of the Unconcious Statistician&lt;/a
&gt;
.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;data-and-sampling-the-passage-to-statistics&#34;
    &gt;Data and Sampling &amp;ndash; the Passage to Statistics&lt;a href=&#34;#data-and-sampling-the-passage-to-statistics&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Many people are now drawn to the study of probability for the purposes of doing statistics, &lt;em&gt;i.e.&lt;/em&gt;, analyzing data.  So, I wanted to add some more commentary about random variables and their connection to the modelling of data.  That is, how do statistics and probability connect?&lt;/p&gt;
&lt;p&gt;Statistics is often concerned with &lt;em&gt;experiments&lt;/em&gt;.  Suppose we plan an experiment where we are going to measure someone&amp;rsquo;s height.  The outcome of this experiment is not known a-priori, so it makes sense to model it using probability theory &amp;ndash; the outcome is random.  As well, a person&amp;rsquo;s height is a real number, and we now know that we can model random real numbers using &lt;em&gt;random variables&lt;/em&gt;.  Thus, we might like to construct a model of the measurement of someone&amp;rsquo;s height as a random variable \(H(\omega)\).&lt;/p&gt;
&lt;p&gt;If we measure the heights of many people, we will wind up with a multitude of random variables \(H_1, H_2, \ldots, H_N\), where \(N\) is the number of measurements we make.  These random variables all together define some &lt;em&gt;joint distribution&lt;/em&gt; \(F(h_1, \ldots, h_N) = \mathbb{P}(H_1 \le h_1, \ldots, H_N \le h_N)\), and statistics is often concerned with what can be learned about the distribution of these random variables through the actually observed outcomes of experiments.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;independent-and-identically-distributed-random-variables&#34;
    &gt;Independent and Identically Distributed Random Variables&lt;a href=&#34;#independent-and-identically-distributed-random-variables&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The multivariate distribution described above can be difficult to work with &amp;ndash; some assumptions need to be made.  The most common assumption is that the random variables are &lt;em&gt;independent and identically distributed&lt;/em&gt;, that is, &lt;em&gt;i.i.d.&lt;/em&gt;  What this means is that we &lt;em&gt;assume&lt;/em&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;\(\mathbb{P}(H_i \le h) = \mathbb{P}(H_j \le h)\) (identically distributed)&lt;/li&gt;
&lt;li&gt;\(\mathbb{P}(H_1 \le h_1, \ldots, H_N \le h_N) = \mathbb{P}(H_1 \le h_1) \times \cdots \times \mathbb{P}(H_N \le h_N)\) (independent).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The idea of the random variables having an identical distribution is that we are observing separate outcomes of the same experiment.  Independence, where the joint distribution is nothing but the product of the individual (&lt;em&gt;marginal&lt;/em&gt;) distributions, encodes the idea that the outcome of one experiment does not influence (and is not influenced by) any other experiment.&lt;/p&gt;
&lt;p&gt;Thus, one of the most common settings in statistics is that we are given a sequence \(X_1, X_2, \ldots, X_N\) of &lt;em&gt;i.i.d.&lt;/em&gt; random variables, and our goal is to learn something about the &lt;em&gt;statistics&lt;/em&gt; of their common distribution, call it \(F_X\).  The word &lt;em&gt;statistic&lt;/em&gt; here just means &amp;ldquo;functionals of the distribution&amp;rdquo;, &lt;em&gt;i.e.&lt;/em&gt;, functions which take the distribution function \(F_X\) as an input, and output some real number.  The mean value \(\mathbb{E}X = \int_{\mathbb{R}} x \mathsf{d} F_X(x)\) being an important example, &lt;em&gt;quantiles&lt;/em&gt; being another: \(\mathbb{P}(X \le q) = \int_{\mathbb{R}} \mathbf{1}[x \le q]\mathsf{d} F_X(x)\).&lt;/p&gt;
&lt;p&gt;Given data, and statistical theorems about how collections of &lt;em&gt;i.i.d.&lt;/em&gt; random variables behave, statisticians are able to answer questions like: &amp;ldquo;what is the probability that a randomly selected person will be over six feet tall?&amp;rdquo;, &amp;ldquo;in a room of \(N\) people, what is the probability that there will be someone more than six feet five inches?&amp;rdquo;, &lt;em&gt;etc&lt;/em&gt;.  Going further, the combination of powerful computers with sophisticated statistical models can be used to produce striking results &amp;ndash; the dart throwing woman that serves as the cover photo of this blog post was itself generated from a statistical model.  That is, the image itself is essentially nothing but a particular realization of a random variable!&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (The LLN): The most natural estimator of&lt;/kbd&gt; \(\mathbb{E}X\) &lt;kbd&gt;given the sample is the &lt;em&gt;average&lt;/em&gt;&lt;/kbd&gt; \(\overline{X}_N = \frac{1}{N}\sum_{i = 1}^N X_i\) &lt;kbd&gt;and indeed, many of the most famous theorems in statistics (the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Law_of_large_numbers&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Law of Large Numbers&lt;/a
&gt;
 &lt;kbd&gt;and the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Central_limit_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Central Limit Theorem&lt;/a
&gt;
) &lt;kbd&gt;are concerned exactly with how $\overline{X}_N$ behaves under various different assumptions about the common distribution.  Indeed, it can be shown that in almost all sensible cases $\overline{X}_N \rightarrow \mathbb{E}X \text{ as } N \rightarrow \infty$ (given an appropriate sense of convergence for random variables): the average of the sample converges to the expectation as the size of the sample increases.  This is a highly intuitive outcome that can be derived entirely from the axioms of probability, and it is an incredibly satisfying result!  Some historical formulations of probability &lt;em&gt;began&lt;/em&gt; with the postulate that this is true, so it is a testament to how effectively the axioms &amp;ldquo;clean up&amp;rdquo; the theory.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Axiomatic and measure theoretic probability is a topic more than worth studying.  It is very difficult, if not impossible, to have a full understanding of probability (particularly stochastic processes) without eventually going down this path.  I hope that I have here shown that the elementary ideas are not just esoteric abstractions, but that they are well supported by a very natural intuition.  It is the technical need to deal with annoying pathologies (like the Banach-Tarski paradox) that necessitates some technical machinery.  But, the underlying intuition comes down to nothing but the measure of volumes in Nature&amp;rsquo;s Dartboard.  Indeed, most abstractions in mathematics have some completely natural and intelligible intuition behind them!&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Some book recommendations): The first book about probability I read was&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3mVmQ0l&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;An Intermediate Course in Probability&lt;/a
&gt;
 &lt;kbd&gt;by Allan Gut.  However, this book is not measure theoretic.  Instead,&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3mYdUY1&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this book by the same author&lt;/a
&gt;
 &lt;kbd&gt;can serve as a reasonable introduction, or the&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3LoD6jD&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;well known book by Billingsley&lt;/a
&gt;
 &lt;kbd&gt;which is more-or-less the canonical book to recommend.  Another great book which is somewhat less concerned with the details of measure theory, focusing more on probability, is&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/41Vq2YL&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Probability: Theory and Examples&lt;/a
&gt;
 &lt;kbd&gt;which contains all of the classic results and, as the name suggestions, a lot of very nice examples.  However, I only read this book long after I became well acquainted with measure-theoretic probability.  Since its treatment of measure is rather more limited, it&amp;rsquo;s difficult for me to know whether or not this is a good book to start with.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt; (Other topics):  There are a number of other rather famous books broadly in the area of machine learning and statistics &amp;ndash; examples being the following:&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3mYy0kY&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Theoretical Statistics&lt;/a
&gt;
, &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3N79MQ0&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Pattern Recognition and Machine Learning&lt;/a
&gt;
, &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3Aodk8Y&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Bayesian Data Analysis&lt;/a
&gt;
, &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3oxC101&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Deep Learning&lt;/a
&gt;
, and &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/3V8mLD5&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Mathematics for Machine Learning&lt;/a
&gt;
 &lt;kbd&gt;(this last one I have not myself read).  These books are perfectly good, but they are emphatically &lt;em&gt;not&lt;/em&gt; about probability theory &amp;ndash; they are applied books in machine learning and statistics.  To learn probability theory itself, stick with the above.&lt;/kbd&gt;&lt;/p&gt;</description></item><item>
            <title>Group Theory, Symmetry, and a Brain Teaser</title>
            <link>https://rjtk.github.io/posts/group-theory-symmetry-and-a-brain-teaser/</link>
            <pubDate>Sun, 19 Feb 2023 00:00:00 -0800</pubDate>
            <guid>https://rjtk.github.io/posts/group-theory-symmetry-and-a-brain-teaser/</guid><description>&lt;p&gt;I review some elementary discrete math concepts, use them to describe and solve a neat brain-teaser, and generalize the solution into a form of robust breadth first search.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Recently, a friend introduced me to the following brain teaser:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Problem Statement&lt;/strong&gt;: Suppose you are kidnapped, blindfolded, and placed into a room.  Your kidnappers hand you a plate with four coins on it, arranged in a&lt;/kbd&gt; \(2 \times 2\) &lt;kbd&gt;square.  They will let you leave if you can flip all of the coins so that they are face up, in a &lt;em&gt;finite&lt;/em&gt; amount of time (they ain&amp;rsquo;t got all day!).  Since you can&amp;rsquo;t see, you can ask the interrogator if you&amp;rsquo;ve successfully placed all the coins heads up (if you&amp;rsquo;d like to increase the immersion, you can imagine you are actually blind, otherwise you could just take off the blindfold).  The interrogator won&amp;rsquo;t lie to you, but will try to mess with you: any time the answer is &amp;ldquo;no&amp;rdquo;, they will rotate the plate before giving it back to you.  The amount by which it is rotated is arbitrary &amp;ndash; it could be random, follow a fixed pattern, or be adversarial.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;The first things coming to mind with this puzzle is that you could just iterate through every possible sequence of flips, asking the interrogator whether they&amp;rsquo;re all heads up after each flip.  This possibility is immediately shut down by their adversarial rotations &amp;ndash; the interrogator can easily foil this plan.&lt;/p&gt;
&lt;p&gt;Another possibility is through &lt;em&gt;random&lt;/em&gt; flips.  Randomization is an effective tool because the interrogator cannot determine your next move by finding a pattern in your behaviour (if you randomize effectively, even you don&amp;rsquo;t know your next move!).  If you simply pick up all the coins at random, flip them randomly, and then ask if you&amp;rsquo;ve solved the puzzle, it is guaranteed (by the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Borel%E2%80%93Cantelli_lemma&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Borell-Cantelli Lemma&lt;/a
&gt;
) that you will eventually stumble upon the solution.  However, this is where the caveat about &lt;em&gt;finite time&lt;/em&gt; comes in.  In probability theory, it is sometimes a point of confusion what is actually meant by a random variable being finite, so let&amp;rsquo;s spell it out.  If \(N(\omega)\) is the number of flips taken, given a state of the universe \(\omega\), the Borell-Cantelli Lemma tells us that \(N(\omega) &amp;lt; \infty\) almost surely, so \(N\) is &lt;em&gt;bounded&lt;/em&gt;, but it is not &lt;em&gt;uniformly bounded&lt;/em&gt; (over all possible events \(\omega\)).  What we want is that there is some real number \(N_{\text{max}} &amp;lt; \infty\) such that \(N(\omega) \le N_{\text{max}}\) almost-surely, and this cannot be done by the random flipping strategy.&lt;/p&gt;
&lt;p&gt;That all being said, the solution I arrived upon does not include any randomization.  Before explaining the solution, I&amp;rsquo;m going to take a detour through the ideas that came to my mind, and some of the formalisms that can be used to understand the puzzle, its solution, and potential generalization.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;a-mathematical-preamble&#34;
    &gt;A Mathematical Preamble&lt;a href=&#34;#a-mathematical-preamble&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The purpose of this section is to review some mathematical formalism and terminology used in reasoning about the coins-on-a-plate puzzle.  I have a limited background in discrete math or abstract algebra, so even though these concepts are rather elementary, it has been good for me to write things down.  Indeed, reviewing elementary concepts facilitates &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Chunking_%28psychology%29&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Chunking&lt;/a
&gt;
 (an essential part of &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/41gvuFQ&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;learning&lt;/a
&gt;
).  If all one is interested in is the solution to the puzzle, or if they are already well familiar with these concepts, they can skip to &lt;a
    class=&#34;link&#34;
    href=&#34;#solving-the-puzzle&#34;&gt;Solving the Puzzle&lt;/a
&gt;
.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;equivalence-classes&#34;
    &gt;Equivalence Classes&lt;a href=&#34;#equivalence-classes&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;In mathematics, an &lt;em&gt;equivalence relation&lt;/em&gt; \(\sim\) on a set \(X\) is a binary relation satisfying the following properties:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Reflexivity&lt;/em&gt;: \(\forall x \in X\ x \sim x\)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Symmetry&lt;/em&gt;: \(\forall x, y \in X\ x \sim y \iff y \sim x\)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Transitivity&lt;/em&gt;: $∀ x, y, z ∈ X\ x ∼ y, y ∼ z \implies x ∼ z.$&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most famous equivalence relation is &amp;ldquo;ordinary&amp;rdquo; equality: &amp;lsquo;\(=\)&amp;rsquo;, but there are various more exotic equivalence relations: equivalence up to rotations in geometry, equivalence almost surely in probability, equivalence except on measure-zero sets in the theory of \(L_p\) spaces, &lt;em&gt;etc&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When dealing with a finite number of objects (finiteness is not necessary in some/many of the cases I talk about, but I keep the assumption to avoid accidentally writing something that is technically wrong for infinite sets), equivalence classes easily arise from partitions of the set.  That is, if \(X\) is a finite set, and \(X = \bigcup_{i = 1}^M X_i\) for subsets \(X_i \subseteq X\) which are all disjoint \(i \ne j \implies X_i \cap X_j = \emptyset\) (&lt;em&gt;i.e.&lt;/em&gt;, the \(X_i\) are a partition \(X\)) we can define an equivalence relation on \(X\) by \[x \sim y \iff (\exists i: x \in X_i \wedge y \in X_i),\] where \(\wedge\) indicates &amp;ldquo;and&amp;rdquo;.  Let us quickly check that this is a bona-fide equivalence relation: Let \(x \in X\).  There is an \(X_i\) in the partition such that \(x \in X_i\) and hence &amp;ldquo;also&amp;rdquo; \(x \in X_i\) verifying it is reflexive.  That it is symmetric arises since \((x \in X_i \wedge y \in X_i) \iff (y \in X_i \wedge x \in X_i)\).  Finally, it is transitive since the \(X_i\) are a partition without any overlap: if \(x, y\) are in the same set \(X_i\) and \(y, z\) are both in another set \(X_j\), it must be that \(X_i = X_j\) (and hence \(x, z \in X_i\)) since \(y\) must be in exactly one of the \(X_i\).  Conversely, the reader can check that any set partition also induces an equivalence relation.  Thus, set partitions and equivalence relations are equivalent.&lt;/p&gt;
&lt;p&gt;Given an equivalence relation \(\sim\) on a set \(X\), the corresponding partition (&lt;em&gt;i.e.&lt;/em&gt;, the partition &lt;em&gt;induced&lt;/em&gt; by \(\sim\)) is commonly denoted as \(X / \sim\) as the &lt;em&gt;quotient set&lt;/em&gt;.  We then say that elements of a set in the partition (that is, elements \(x \in X_i\) for some \(X_i \in X / \sim\), which is a subset \(X_i \subseteq X\)) are &lt;em&gt;equivalent up to&lt;/em&gt; \(\sim\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;functions-permutations-and-groups&#34;
    &gt;Functions, Permutations, and Groups&lt;a href=&#34;#functions-permutations-and-groups&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Any &lt;em&gt;finite&lt;/em&gt; set with \(N\) elements can be represented simply as the set of integers from \(1\) to \(N\) as in \(X = \{1, 2, \ldots, N\}\) (a set which is commonly denoted \([N]\)).  It does not matter what the elements of the set actually are, you can simply refer to the first, second, &lt;em&gt;etc&lt;/em&gt;., in an arbitrary order.&lt;/p&gt;
&lt;p&gt;Now, what are the functions on finite sets?  Well, a function \(f: X \rightarrow X\) is just a mapping from some element of \(x \in X\) (without loss, an integer between \(1\) and \(N\)) to another element \(f(x) \in X\).  If the function is a one-to-one function (&lt;em&gt;i.e.&lt;/em&gt;, it is &lt;em&gt;injective&lt;/em&gt;) whose domain is all of \(X\), then it must also (why?) be an onto function (&lt;em&gt;i.e.&lt;/em&gt;, a &lt;em&gt;surjection&lt;/em&gt;) and thus it is a &lt;em&gt;bijection&lt;/em&gt; and in this context such functions are called &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Permutation&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;permutations&lt;/em&gt;&lt;/a
&gt;
.  These are nothing but re-orderings of the integers, usually denoted by \(\sigma\) (or other Greek letter, instead of the more generic \(f\)) as in &amp;ldquo;\(\sigma\) re-orders the sequence \((1, 2, \ldots, N)\) into \((\sigma(1), \sigma(2), \ldots, \sigma(N))\)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Since permutations are bijections, they have an inverse.  Moreover, the composition operation amongst permutations is &lt;em&gt;associative&lt;/em&gt; (why?): for permutations \(\sigma, \pi, \tau\) on \(X\), it is the case that \((\sigma \circ \pi) \circ \tau = \sigma \circ (\pi \circ \tau)\), where, &lt;em&gt;e.g.&lt;/em&gt;, \((\sigma \circ \pi)(x) = \sigma(\pi(x))\).  This means that (with the trivial &amp;ldquo;doing nothing&amp;rdquo; permutation \(Id(x) = x\) serving as the identity) the set of all permutations of \(X\), call it \[G_X = \{\sigma: X \rightarrow X\ | \ \sigma \text{ is a bijection}\},\] along with the composition operation constitutes a (finite) &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Group_%28mathematics%29&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;Group&lt;/em&gt;&lt;/a
&gt;
 \((G_X, \circ)\).  This particular group \(G_X\) of all bijections is particularly special, and is referred to as the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Symmetric_group&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Symmetric Group&lt;/a
&gt;
 \(S_N\) on \(N\) elements &amp;ndash; it is worthwhile noting that the size of this group grows rather quickly: there are \(N!\) bijections in \(S_N\).&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;cycles-and-decomposition&#34;
    &gt;Cycles and Decomposition&lt;a href=&#34;#cycles-and-decomposition&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;Given a permutation \(\sigma \in S_N\), an $r$-&lt;em&gt;cycle&lt;/em&gt; is a sequence of \(r \ge 1\) numbers \(i_1, \ldots, i_r\) such that \(i_{k + 1} = \sigma(i_k)\) and \(i_r = i_1\).  Given an $r$-cycle of \(\sigma\), we can collect each \(i_1, \ldots, i_r\) into a set \(C \subseteq [N]\) called an &lt;em&gt;orbit&lt;/em&gt; and restrict \(\sigma\) to this cycle so that \(\sigma_C\) is equal to \(\sigma\) in \(C\), and the identity outside of \(C\) as in \(\forall x \in C:\ \sigma_C(x) = \sigma(x)\) and \(\forall x \in C^{\mathsf{c}}:\ \sigma_C(x) = x\).  Remarkably, permutations can be &lt;em&gt;decomposed&lt;/em&gt; into a composition of their cycles:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Theorem&lt;/strong&gt; (Cyclic Decomposition): Let $\sigma \in S_N$.  Then, there exists a partition $C_1, \ldots, C_m$ of $[N]$ such that $\sigma = \sigma_{C_1} \circ \cdots \circ \sigma_{C_m}.$  Moreover, each $C_i$ is an orbit of a cycle of $\sigma$, and for any cycle of $\sigma$ there is a corresponding orbit $C_i$.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;proof (sketch): Since \(\sigma\) acts on a finite set, there exists at least one cycle, call its orbit \(C_1\).  We have the partition \([N] = C_1^{\mathsf{c}} \cup C_1\) and the decomposition \(\sigma = \sigma_{C_1^{\mathsf{c}}} \circ \sigma_{C_1}\) since \(\sigma_{C_1}(x) = x\) on \(C_1^{\mathsf{c}}\).  Now, \(C_1\) contains at least one element, and \(\sigma_{C_1^{\mathsf{C}}}\) is another permutation when restricted to \(C_1^{\mathsf{c}}\), so the theorem follows by induction.&lt;/em&gt; \(\square\)&lt;/p&gt;
&lt;p&gt;From the partitioning of \([N]\) by the orbits of a permutation, we see that any permutation also induces a collection of equivalence classes (exactly the orbits of the permutation)!  Within each of these equivalence classes, we can say that the elements are identical &lt;em&gt;up to&lt;/em&gt; the operation of \(\sigma\).  As well, we say that any set \(C\) such that \(x \in C \implies \sigma(x) \in C\) is &lt;em&gt;invariant&lt;/em&gt; to \(\sigma\) &amp;ndash; clearly, the partitions induced by \(\sigma\) are all invariant sets.  These formalisms can be used to understand the structure of the coins-on-a-plate puzzle.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;solving-the-puzzle&#34;
    &gt;Solving the Puzzle&lt;a href=&#34;#solving-the-puzzle&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The main idea for solving the puzzle revolves around identifying particular partitions of the set of all states of the game, thus identifying equivalence relations.  I then define some natural operations that one can apply to the coins on the plate and reason about how those operations map elements of one equivalence class into another.  It turns out that we can apply these operations in a particular order such that we are guaranteed to eventually reach the solution.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;representation-and-rotations&#34;
    &gt;Representation and Rotations&lt;a href=&#34;#representation-and-rotations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Abstractly, we can represent the state of each coin as either being heads \(H\) or tails \(T\).  Since the coins are arranged in a square, we can think of the state of the whole puzzle as being the sequence of states of each individual coin following a left-to-right and top-to-bottom ordering.  Notice that we&amp;rsquo;ve already encountered an equivalence class: we naturally think of aligning the coins to look as though they are on a &amp;ldquo;right-angle&amp;rdquo; when facing us &amp;ndash; following this convention, a whole set of angles of the plate spanning \(90^\circ\) (\(45^\circ\) in either direction) all represent &lt;em&gt;equivalent&lt;/em&gt; configurations.  If the interrogator rotates the plate by, say, \(37^\circ\) before handing it back to us, we should be able to feel around the coins to rotate this back to \(0^\circ\).  Thus, when we speak of an arrangement of coins on the plate, we are already really speaking of a whole equivalence class, all of which are equivalent up to rotation back to the nearest right-angle alignment of the coins.  The upshot here is that listing the coins in this order results in a finite representation of the set of all possible states (of which there are \(2^4 = 16\)) as elements of the set \[S = \Bigl\{{HH \atop HH}, {TH \atop HH}, {HT \atop HH}, \ldots, {TT \atop TH}, {TT \atop TT}\Bigr\},\] which is naturally isomorphic (&lt;em&gt;i.e.&lt;/em&gt;, equivalent) to the set \([16] = \{1, \ldots, 16\}\).&lt;/p&gt;
&lt;p&gt;Any such rotation operation can now naturally be thought of as a permutation \(\texttt{rotate}^n\), where \(n\) indicates the number of \(90^\circ\) angles the plate is clockwise rotated.  For example, \(\texttt{rotate} \Bigl({HT \atop HH}\Bigr) = {HH \atop HT}\) and \(\texttt{rotate}^2 \Bigl({HT \atop HH}\Bigr) = {HH \atop TH}\), &lt;em&gt;etc.&lt;/em&gt;  The power notation with \(n\) is naturally suggestive since \(n\) rotations by \(90^\circ\) is equivalent to applying the single \(\texttt{rotate}\) permutation \(n\) times.  Since we don&amp;rsquo;t know how many times the interrogator will apply \(\texttt{rotate}\) before giving us the plate back, we need to work with equivalence classes of states which are equivalent up to the action of this permutation.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: It is an exercise for the reader to enumerate all of the equivalence classes induced by $\texttt{rotate}$.  I should note at this point though that I stumbled upon the appropriate sets and operations for solving the puzzle through intuition, and only went backwards afterwards to add some formalism.  In my experience, this is typical of mathematical problem solving and research &amp;ndash; you begin by understanding and solving particular problems and special cases ad-hoc through intuition, and then go back to formalize the ideas, which opens up new ideas and generalizations, which you begin by solving ad-hoc, and then later formalize, which leads to more questions&amp;hellip;&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;solved-states&#34;
    &gt;Solved States&lt;a href=&#34;#solved-states&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The next key observation involves another permutation and collection of equivalence classes.  Consider a \(\texttt{flip\_all}\) operation &amp;ndash; you can apply this operator to the coins on the plate by flipping every coin onto its other face.  Since this \(\texttt{flip\_all}\) operation is just a permutation, it decomposes the space \(S\) into equivalence classes (whose elements are all equivalent up to the operation of \(\texttt{flip\_all}\)).  Examples include \(\Bigl\{ {HT \atop HT}, {TH \atop TH} \Bigr\}, \Bigl\{ {HT \atop TH}, {TH \atop HT} \Bigr\}\), &lt;em&gt;etc&lt;/em&gt;, which are all orbits containing just two elements.  Another important example is \[S_\star = \Bigl\{{HH \atop HH}, {TT \atop TT}\Bigr\},\] which is the equivalence class (of \(\texttt{flip\_all}\)) containing the solved state \({HH \atop HH}\).  The importance of this class arises as follows.&lt;/p&gt;
&lt;p&gt;Consider the following query, which I&amp;rsquo;ll refer to as \(\texttt{solved?}(s)\): &lt;kbd&gt;Ask the interrogator if the puzzle in state&lt;/kbd&gt; \(s\) &lt;kbd&gt;is solved.  If it is, you&amp;rsquo;re done.  If not, the plate is returned to you after application of some number of $\texttt{rotate}$ permutations.&lt;/kbd&gt;  Now, consider the composite query (\(\texttt{solved?}(s)\) is not formally a permutation, so I use the word &amp;ldquo;query&amp;rdquo;): \(\texttt{solved?} \circ \texttt{flip\_all}\), which in words describes: &lt;kbd&gt;first flip all the coins onto their other side and then ask the interrogator if the puzzle is solved.&lt;/kbd&gt;  Therefore, since \(\texttt{rotate}\) does not change the number of heads or tails in the configuration (&lt;em&gt;i.e.&lt;/em&gt;, both elements of \(S_\star\) are &lt;em&gt;fixed-points&lt;/em&gt; of \(\texttt{rotate}\)) if you reach a state \(s \in S_\star\) then you can first query \(\texttt{solved?}\) and if the plate is returned to you (which means you must have asked about the configuration \({TT \atop TT}\)) then you know that the state \(s\) has not changed and you can apply the composite query \(\texttt{solved?} \circ \texttt{flip\_all}\) which is now guaranteed to result in successfully solving the puzzle.&lt;/p&gt;
&lt;p&gt;Thus, if you do this every time you want to know if the puzzle is solved, then you have &lt;em&gt;effectively&lt;/em&gt; solved the puzzle anytime all of the coins are all face up, or all face down &amp;ndash; if they are face down, then they will be face up the second time you ask.  Thus, the two states in \(S_\star\) &amp;ldquo;&lt;kbd&gt;coins all face up&lt;/kbd&gt;&amp;rdquo; and &amp;ldquo;&lt;kbd&gt;coins all face down&lt;/kbd&gt;&amp;rdquo; form an equivalence class of &amp;ldquo;solved states&amp;rdquo; according to the composite query described above &amp;ndash; I&amp;rsquo;ll call this composite query \(\texttt{solved?}^\star\).  We can now frame our problem as reaching any state in \(S_\star\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;three-more-equivalence-classes&#34;
    &gt;Three More Equivalence Classes&lt;a href=&#34;#three-more-equivalence-classes&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Consider the following three sets: \[S_1 = \Bigl\{{HH \atop HT}, {HH \atop TH}, {HT \atop HH}, {TH \atop HH}, {TT \atop TH}, {TT \atop HT}, {TH \atop TT}, {HT \atop TT}\Bigr\},\] which has either a single head or a single tail; \[S_2^{(d)} = \Bigl\{{HT \atop TH}, {TH \atop HT}\Bigr\},\] with two heads and two tails arranged diagonally from one and other; and \[S_2^{(s)} = \Bigl\{{HH \atop TT}, {HT \atop HT}, {TT \atop HH}, {TH \atop TH}\Bigr\},\] consisting again of two heads and two tails, but now sitting side-by-side.  Critically, the sets we have so far defined form a &lt;em&gt;partition&lt;/em&gt; of the space: \[S = S_\star \cup S_1 \cup S_2^{(s)} \cup S_2^{(d)},\] and thus more equivalence relations given by membership in these sets.  Moreover, each of these sets is invariant to both permutations \(\texttt{rotate}\) and \(\texttt{flip\_all}\).  Indeed, \(S_2^{(d)}\) is exactly one of the equivalence classes induced by both, \(S_2^{(s)}\) is an equivalence class induced by \(\texttt{rotate}\) and is the union of two equivalence classes of \(\texttt{flip\_all}\), and \(S_1\) is the union of two equivalence classes of \(\texttt{rotate}\) along with the corresponding ones of \(\texttt{flip\_all}\).  Since the elements of the sets are all equivalent to one and other (in the sense of being members of a partition, and in the sense of rotational symmetry) we can treat all members of the class simultaneously.  These sets, along with three more operations, will allow us to formalize a solution to the puzzle.&lt;/p&gt;
&lt;p&gt;Due to the invariance of these sets to the \(\texttt{rotate}\) permutation, the action of the rotation introduced by the interrogator when you query \(\texttt{solved?}\) does not have any affect on the membership of \(s\) in one of these sets.  This is, of course, &lt;em&gt;not&lt;/em&gt; true of any arbitrary subset of \(S\), and hence the choice of sets above is in some sense &amp;ldquo;cunning&amp;rdquo; (and now we see, well motivated by the consideration of equivalence classes!).  Indeed, rotations applied to \(\tilde{S} = \{{HH \atop TT}, {TT \atop HH}\}\) could produce something in \(\tilde{S}\), but it could also produce something in \(\{{HT \atop HT}, {TH \atop TH}\}\)!&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;more-permutations-on-s&#34;
    &gt;More Permutations on \(S\)&lt;a href=&#34;#more-permutations-on-s&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The additional permutations we need to solve the puzzle are: \(\texttt{flip\_one}\), which flips over an arbitrary single coin; \(\texttt{flip\_side}\), which flips over two side-by-side coins; and \(\texttt{flip\_diag}\), which flips over arbitrary cross-diagonal coins.  These functions are not uniquely defined (&lt;em&gt;e.g.&lt;/em&gt;, which coin do you flip over for \(\texttt{flip\_one}\)) but the choice of which particular permutation you use does not matter, so I&amp;rsquo;m glossing over this.&lt;/p&gt;
&lt;p&gt;We can reason through the effects of how these map between our equivalence classes as follows (I skip the consideration of how \(S_\star\) is affected since we will always query \(\texttt{solved?}^\star\) between each operation, so if we hit \(S_\star\) we are done):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\texttt{flip\_one}: S_1 \rightarrow S_2^{(d)} \cup S_2^{(s)} \cup S_\star\\
\texttt{flip\_one}: S_2^{(d)} \cup S_2^{(s)} \rightarrow S_1,
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;since if \(s \in S_1\) and we flip over a single coin, then we must now have either two heads and two tails, or we happened to flip over the &amp;ldquo;right&amp;rdquo; coin and reach a solved state.  Alternatively, if we begin with two $H$s and two $T$s, then flipping one coin over necessarily puts us back in \(S_1\) with either three $H$s and one \(T\), or three $T$s and one \(H\).  For the side-by-side flip operator:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\texttt{flip\_side}: S_1 \rightarrow S_1\\
\texttt{flip\_side}: S_2^{(d)} \rightarrow S_2^{(s)}\\
\texttt{flip\_side}: S_2^{(s)} \rightarrow S_\star \cup S_2^{(d)},
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;since if there is one head (or one tail), application of \(\texttt{flip\_side}\) must necessarily result in three heads or tails; if there are heads diagonal from one and other (&lt;em&gt;i.e.&lt;/em&gt;, \(s \in S_2^{(d)}\)) then \(\texttt{flip\_side}\) must necessarily flip one head and one tail, and thus result in side-by-side heads and tails; and if \(s \in S_2^{(s)}\) then we either get lucky and flip the &amp;ldquo;right&amp;rdquo; coins into a solved state, or we flip the &amp;ldquo;wrong&amp;rdquo; coins into a diagonal state \(S_2^{(d)}\).  Finally, for the diagonal flipping operator:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\texttt{flip\_diag}: S_1 \rightarrow S_1\\
\texttt{flip\_diag}: S_2^{(d)} \rightarrow S_\star\\
\texttt{flip\_diag}: S_2^{(s)} \rightarrow S_2^{(s)},
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;by similar reasoning.  This is the &lt;strong&gt;&lt;strong&gt;key to the puzzle&lt;/strong&gt;&lt;/strong&gt;.  If we are faced with some \(s \in S_2^{(d)}\), then application of \(\texttt{flip\_diag}\) is guaranteed to place us into the solved state \(S_\star\).  The only difficulty now is that we cannot &lt;em&gt;know&lt;/em&gt; whether or not \(s \in S_2^{(d)}\)!  Regardless, this feels like progress.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;transition-diagramming&#34;
    &gt;Transition Diagramming&lt;a href=&#34;#transition-diagramming&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The simple enumeration of the action of the operators \(\texttt{flip\_one}, \texttt{flip\_side}, \texttt{flip\_diag}\) on the sets \(S_1, S_2^{(s)}, S_2^{(d)}\) described in the previous section can be difficult to visualize.  Our intuition is that we should try to find some appropriate order in which to apply these operations which will guarantee that we eventually reach the state \(S_\star\), but how?  We can make progress towards this task by drawing a diagram of all the possible transitions:&lt;/p&gt;


&lt;p&gt;One thing we can notice from this diagram is that some operations map from a set and back into itself, &lt;em&gt;i.e.&lt;/em&gt;, some of the sets described above are also invariant to \(\texttt{flip\_diag}\) or \(\texttt{flip\_side}\).  For example, \(\texttt{flip\_diag}\) maps from \(S_2^{(s)}\) back into itself, as well as from \(S_1\) and back into itself.  Recall that there is also an &lt;em&gt;implicit&lt;/em&gt; self-loop corresponding to the \(\texttt{rotate}\) permutation for each set, &lt;em&gt;i.e.&lt;/em&gt;, we can safely ask if the puzzle is solved (following the logic described in &lt;a
    class=&#34;link&#34;
    href=&#34;#solved-states&#34;&gt;Solved States&lt;/a
&gt;
) without the interrogator being able to effect the state we are in.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;a-solution&#34;
    &gt;A Solution&lt;a href=&#34;#a-solution&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Using the diagram above, we can deduce a solution to the puzzle.  Start in any arbitrary state \(s \in S\): first, make the \(\texttt{solved?}^\star\) query, if \(s \in S_\star\) then we are done (that was easy!), otherwise, we are in a state \[s \in S \setminus S_\star = S_1 \cup S_2^{(d)} \cup S_2^{(s)}.\]  Apply the operator \(\texttt{flip\_diag}\) and then query \(\texttt{solved?}^\star\) again.  If we happened to be in \(S_2^{(d)}\), then the puzzle is solved, otherwise (notice the self-loops) we must be in \(s \in S_2^{(s)} \cup S_1\).  Now, apply \(\texttt{flip\_side}\), followed by \(\texttt{solved?}^\star\), \(\texttt{flip\_diag}\), and \(\texttt{solved?}^\star\) again.  If we were in \(S_2^{(s)}\), then \(\texttt{flip\_side}\) transitioned us into \(S_2^{(d)}\), and \(\texttt{flip\_diag}\) brought us into \(S_\star\).  If we were in \(S_1\), then we are still in \(S_1\), since this set is invariant to both \(\texttt{flip\_diag}\) and \(\texttt{flip\_side}\).  To solve the puzzle from here, we apply \(\texttt{flip\_one}\) and query \(\texttt{solved?}^\star\).  If that query failed, then we must be in \(S_2^{(d)} \cup S_2^{(s)}\) in which case we can apply \(\texttt{flip\_diag}\) and query \(\texttt{solved}?^\star\), which will finish the puzzle if we were in \(S_2^{(d)}\).  Otherwise, we are in \(S_2^{(s)}\) and applying \(\texttt{flip\_side}\) followed by \(\texttt{solved?}^\star\) and then again \(\texttt{flip\_diag}\) which is finally guaranteed to place us into \(S_\star\), wherein a final \(\texttt{solved?}^\star\) query is the end.&lt;/p&gt;
&lt;p&gt;We have solved the puzzle &amp;ndash; regardless of our starting position, and regardless of how random or adversarial are the rotations to the plate of coins done by the interrogator (they can even know our exact plan!), the above policy will eventually move us into the solved state \(S_\star\).  To summarize the policy, we apply the following order of operations, querying \(\texttt{solved?}^\star\) between each operator: \(\texttt{flip\_diag}, \texttt{flip\_side}, \texttt{flip\_diag}, \texttt{flip\_one}, \texttt{flip\_diag}, \texttt{flip\_side}, \texttt{flip\_diag}\).&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;computational-generalizations&#34;
    &gt;Computational Generalizations&lt;a href=&#34;#computational-generalizations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Solving this particular brain-teaser is satisfying, but a solution is not a &lt;em&gt;good&lt;/em&gt; solution to me unless it is illustrative or indicative of a more general &lt;em&gt;pattern&lt;/em&gt;.  Indeed, I wrote out the formalism above exactly for the purpose of trying to reason through a more general idea.  To introduce the idea, suppose we have \(N\) states \(V = [N] \overset{\Delta}{=} \{1, 2, \ldots, N\}\) and \(K\) functions \(\mathcal{F} = \{f_k: V \rightarrow V\ |\ k \in [K]\}\) on \(V\).  Given this data, we can define a graph \(\mathcal{G} = (V, \mathcal{E})\) where the set of vertices is naturally \(V\) and the edges are tuples \(\mathcal{E} \subseteq \{(i, j, f)\ |\ i, j \in V, f \in \mathcal{F}\}\) where the edges \((i, j, f) \in \mathcal{E}\) is present in the graph if there is a function \(f \in \mathcal{F}\) which maps from node \(i\) to node \(j\).  Each node will have \(K\) edges emanating from it &amp;ndash; one for each function.  Let&amp;rsquo;s suppose as well that there is a fixed &lt;em&gt;target&lt;/em&gt; state \(s^\star \in S\) and that this state is invariant to every function application \(f_k(s^\star) = s^\star\), &lt;em&gt;i.e.&lt;/em&gt;, when we reach the target state, we know it.  This graph is analogous to the graph I drew for the coin problem.&lt;/p&gt;
&lt;p&gt;Now, the interesting part of this comes in when we suppose we do not know what state we are in.  The new question is: &lt;strong&gt;&lt;strong&gt;can we find a search algorithm which will find a sequence of functions leading to a goal state, but which does not require knowledge of what vertex it is currently visiting&lt;/strong&gt;&lt;/strong&gt;?  Abstractly, given a target state \(s^\star\), can we find a &lt;em&gt;finite&lt;/em&gt; (ideally minimal) sequence of functions \(f_1, f_2, \ldots, f_M \in \mathcal{F}\) which when applied from an arbitrary starting point, are guaranteed to eventually reach the target state \(s^\star\)?&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;uncertain-breadth-first-search&#34;
    &gt;Uncertain Breadth First Search&lt;a href=&#34;#uncertain-breadth-first-search&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;There may exist a better algorithm, but this problem can be solved by means of breadth first search.  However, we cannot just apply BFS directly upon the graph \(\mathcal{G}\), since we don&amp;rsquo;t know our starting point.  Instead, we&amp;rsquo;ll apply BFS on a graph over the &lt;em&gt;power set&lt;/em&gt; \(2^V\) of \(V\) (&lt;em&gt;i.e.&lt;/em&gt;, the set of all subsets of \(V\)).  We generalize the application of \(f \in \mathcal{F}\) to act on \(2^V\) by the definition \[ W\subseteq V:\ f(W) = \{f(w)\ |\ w \in W\},\] &lt;em&gt;i.e.&lt;/em&gt;, we apply \(f\) to every element of the subset.  This gives us an edge relation for a graph \(\mathcal{G}^\star = (2^V, \mathcal{E}^\star)\): we have \[(U, W) \in \mathcal{E}^\star \iff \bigl(\exists f \in \mathcal{F}:\ f(U) = W\bigr).\] The goal is to find a path through this &amp;ldquo;power-graph&amp;rdquo; leading to the state \(S_\star = \{s^\star\}\), the singleton set containing only the goal state.&lt;/p&gt;
&lt;p&gt;A completely hacked-together implementation of this idea for the coins-on-a-plate problem is given here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;39
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;40
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;41
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;42
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;43
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;44
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;45
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;46
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;47
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;48
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;49
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;50
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;51
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;52
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;53
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;54
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;55
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;56
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;57
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;58
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;59
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; ty
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; functools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; reduce
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; collections &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; deque
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Some nicer names for the nodes in the graph&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Node &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;S1: Node &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;S1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;S2d: Node &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;S2d&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;S2s: Node &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;S2s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;S_star: Node &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;S_star&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TransitionFunction &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Callable[[Node], ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Set[Node]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# To easily keep track of what has been visited&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_hash&lt;/span&gt;(s: ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Set[Node]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;hash&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;hash&lt;/span&gt;(n)) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; s))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Computes the set of next nodes obtained by applying `op` from some uncertainty set&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_next_nodes&lt;/span&gt;(nodes: ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Set[Node], op: TransitionFunction):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; reduce(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; acc, new_nodes: acc &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; new_nodes, (op(w) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; w &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; nodes), &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;set&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The robust implementation of BFS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;uncertain_bfs&lt;/span&gt;(nodes: ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Set[Node], ops: ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Dict[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, TransitionFunction]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    visited &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {_hash(nodes)}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queue &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; deque([((&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;START&amp;#34;&lt;/span&gt;,), nodes)])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;while&lt;/span&gt; queue:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        path, nodes &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; queue&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; name, op &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; ops&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;items():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            new_nodes &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _next_nodes(nodes, op)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; (hsh &lt;span style=&#34;color:#ff79c6&#34;&gt;:=&lt;/span&gt; _hash(new_nodes)) &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; visited:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                visited&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add(hsh)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                queue&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(((&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;path, name), new_nodes))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; new_nodes &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; {S_star}:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;SUCCESS: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;path, name)&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;FAILURE!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Helper to make transition functions with S_star being an absorbing state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_make_op&lt;/span&gt;(mapping: ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Dict[Node, Node]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TransitionFunction:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;op&lt;/span&gt;(v: Node) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ty&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Set[Node]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; v &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; S_star:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; {S_star}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; mapping[v]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The operations on the coins&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flip_diag &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _make_op({S1: {S1}, S2d: {S_star}, S2s: {S2s}})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flip_side &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _make_op({S1: {S1}, S2d: {S2s}, S2s: {S2d, S_star}})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flip_one &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _make_op({S1: {S2d, S_star, S2s}, S2d: {S1}, S2s: {S1}})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# The possible starting positions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nodes &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {S1, S2d, S2s, S_star}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# And names for the transition functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;transition_functions &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flip_diag&amp;#34;&lt;/span&gt;: flip_diag, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flip_one&amp;#34;&lt;/span&gt;: flip_one, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;flip_side&amp;#34;&lt;/span&gt;: flip_side}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Which we finally run the algorithm on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uncertain_bfs(nodes, transition_functions)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;\[\texttt{SUCCESS: (&amp;lsquo;START&amp;rsquo;, &amp;lsquo;flip\_diag&amp;rsquo;, &amp;lsquo;flip\_side&amp;rsquo;, &amp;lsquo;flip\_diag&amp;rsquo;, &amp;lsquo;flip\_one&amp;rsquo;, &amp;lsquo;flip\_diag&amp;rsquo;, &amp;lsquo;flip\_side&amp;rsquo;, &amp;lsquo;flip\_diag&amp;rsquo;)}\]&lt;/p&gt;
&lt;p&gt;And indeed, this code outputs the same policy I came up with ad-hoc in the previous section.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;I thought that the puzzle described at the beginning of this article was very neat and wanted to write down a complete solution.  We saw how the idea of symmetries were crucial for solving the puzzle, and that these symmetries can be described through the language of &lt;strong&gt;equivalence classes&lt;/strong&gt;.  Moreover, the remarkable cyclic decomposition theorem for &lt;strong&gt;permutations&lt;/strong&gt; (nothing but bijections on finite sets) tell us that we can &lt;strong&gt;partition&lt;/strong&gt; all the possible states of the coins on the plate into a collection of cycles and equivalence classes.  Some of these sets are &lt;strong&gt;invariant&lt;/strong&gt; to certain operations on the coins, and therefore create self-loops in a graph describing how the state of the puzzle evolves.  Diagramming this graph allowed us to write down an ad-hoc solution to the coins-on-a-plate puzzle.&lt;/p&gt;
&lt;p&gt;The idea of searching in a graph when you don&amp;rsquo;t know your starting point generalized to a form of &lt;strong&gt;robust breadth-first search&lt;/strong&gt;.  In this algorithm, we keep track of an entire &lt;strong&gt;uncertainty set&lt;/strong&gt; of nodes that we can plausibly be, and stop once that set contains nothing but the goal state.  I have few ideas for what such an algorithm might be practically useful for, but there it is!&lt;/p&gt;</description></item><item>
            <title>Lyapunov Stability, Linear Systems, and Semidefinite Programming</title>
            <link>https://rjtk.github.io/posts/lyapunov-stability-linear-systems-and-semidefinite-programming/</link>
            <pubDate>Sat, 17 Dec 2022 00:00:00 -0800</pubDate>
            <guid>https://rjtk.github.io/posts/lyapunov-stability-linear-systems-and-semidefinite-programming/</guid><description>&lt;p&gt;Dynamical systems are ubiquitous models occuring in science, engineering, and mathematics.  Not only are they used to model real-world dynamic phenomena like the dynamics of chemical plants, population growth, and physical engineered systems, they can also be applied to model algorithms themselves.  This post focuses on &lt;em&gt;linear&lt;/em&gt; dynamical systems, their analysis by means of semidefinite programming, and connections with control theory through the computation of quadratic functionals of their paths.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;This post introduces the Lyapunov approach to stability and convergence analysis.  While this approach and its connection to semidefinite programming may seem like &amp;ldquo;overkill&amp;rdquo; for linear systems (where you can just look at the eigenvalues of the system matrix), I believe it is an insightful starting point.  Analysis based on Lyapunov functions generalizes naturally to both nonlinear and stochastic systems, and the connection with semidefinite programming can be exploited to derive an analysis which may be, in some sense, &lt;em&gt;optimal&lt;/em&gt;.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;references&#34;
    &gt;References&lt;a href=&#34;#references&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Much of the material in this post is derived or inspired from: &lt;kbd&gt;Boyd, Stephen, Laurent El Ghaoui, Eric Feron, and Venkataramanan Balakrishnan. Linear matrix inequalities in system and control theory. Society for industrial and applied mathematics, 1994.&lt;/kbd&gt;  This is an &lt;a
    class=&#34;link&#34;
    href=&#34;https://web.stanford.edu/~boyd/lmibook/lmibook.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;easily accessible&lt;/a
&gt;
 book on &lt;em&gt;linear matrix inequalities&lt;/em&gt;.  I have also drawn some ideas and inspiration from the &lt;a
    class=&#34;link&#34;
    href=&#34;https://amzn.to/41kpMCU&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Convex Optimization book&lt;/a
&gt;
 (also &lt;a
    class=&#34;link&#34;
    href=&#34;https://web.stanford.edu/~boyd/cvxbook/bv_cvxbook.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;freely available&lt;/a
&gt;
) &lt;kbd&gt;Boyd, Stephen, Stephen P. Boyd, and Lieven Vandenberghe. Convex optimization. Cambridge university press, 2004.&lt;/kbd&gt; and have made use of &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.cvxpy.org/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;cvxpy&lt;/a
&gt;
 for many some of the examples: &lt;kbd&gt;Diamond, Steven, and Stephen Boyd. &amp;ldquo;CVXPY: A Python-embedded modeling language for convex optimization.&amp;rdquo; The Journal of Machine Learning Research 17, no. 1 (2016): 2909-2913.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;linear-dynamical-systems&#34;
    &gt;Linear Dynamical Systems&lt;a href=&#34;#linear-dynamical-systems&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Linear dynamical systems are generally characterized by a matrix \(A \in \R^{n \times n}\) which describes how the &lt;em&gt;state vector&lt;/em&gt; \(x \in \R^n\) changes through time.  For &lt;em&gt;continuous time&lt;/em&gt; dynamical systems, we work with ordinary differential equations&lt;/p&gt;
&lt;p&gt;\begin{equation}
\frac{\mathsf{d}}{\mathsf{d} t} x(t) = Ax(t),
\end{equation}&lt;/p&gt;
&lt;p&gt;usually written simply as \(\dot{x} = Ax\) where it is to be understood that \(x\) is a continuous function of time \(t\) and \(\dot{x}\) denotes differentiation with respect to \(t\).  In this case, \(A\) describes a &lt;em&gt;velocity field&lt;/em&gt; in state space &amp;ndash; each point \(x \in \R^n\) is associated to some velocity \(Ax\).  Analogously, for &lt;em&gt;discrete time&lt;/em&gt; dynamical systems, we have an iterative relationship&lt;/p&gt;
&lt;p&gt;\begin{equation}
x(t + 1) = Ax(t),
\end{equation}&lt;/p&gt;
&lt;p&gt;where in this case the matrix \(A\) specifies the next state of the system.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: I like to use $t$ for both discrete and continuous time.  The reader is trusted to recognize which is which (or if it matters at all) from the context.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Somewhat more generally, we could work with systems having some &lt;em&gt;constant offset&lt;/em&gt; \(b \in \R^n\) as in \(\dot{x} = Ax + b\).  However, if \(A\) is a full-rank matrix (meaning that it is invertible), then this additional offset term is not interesting.  Indeed, we can just as-well shift the coordinates of the system and instead work with \(\dot{z} = Az\) where \(z = x - A^{-1} b\).  Anything interesting that can be established for \(x\) can be done so by first doing it for \(z\).  If the matrix \(A\) is &lt;em&gt;not&lt;/em&gt; invertible, then there is a subspace which the matrix \(A\) has no effect upon, and this subspace can be analyzed separately.  It will become more-or-less obvious how to do this as we proceed, so I will make the standing assumption that \(A\) is full-rank and \(b = 0\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: The case of linear systems with a general input $\dot{x}(t) = Ax(t) + Bu(t)$ on the other hand are quite interesting, and is likely to be the topic of some future post.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;examples&#34;
    &gt;Examples&lt;a href=&#34;#examples&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Firstly, it should be understood that much of the analysis of &lt;em&gt;nonlinear&lt;/em&gt; dynamical systems, &lt;em&gt;i.e.,&lt;/em&gt; ordinary differential equations of the form \(\dot{x} = f(x)\), comes down to the linear case.  The reason lies in the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Hartman%E2%80%93Grobman_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hartman-Grobman Theorem&lt;/a
&gt;
 (which we have also &lt;a
    class=&#34;link&#34;
    href=&#34;https://rjtk.github.io/posts//solving-equations-with-jacobi-iteration/&#34;&gt;already seen&lt;/a
&gt;
): the nonlinear system \(\dot{x} = f(x)\) can be approximated by the &lt;em&gt;linear&lt;/em&gt; dynamical system \(\dot{x} = Ax\) for in a neighbourhood of an &lt;em&gt;equilibrium point&lt;/em&gt; (&lt;em&gt;i.e.,&lt;/em&gt; point \(x_e\) such that \(f(x_e) = 0\)) with the matrix \(A = \mathsf{D} f(x_e)\), the derivative of \(f\).  More examples follow.&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;gradient-descent&#34;
    &gt;Gradient Descent&lt;a href=&#34;#gradient-descent&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;One of the modern drivers of interest in systems theory is for applications in machine learning, or more specifically, optimization algorithms.  One of the most famous classical algorithms for optimization is &lt;em&gt;gradient descent&lt;/em&gt;: given a function \(f: \mathbb{R}^n \rightarrow \mathbb{R}\) that we want to minimize, the gradient descent algorithm proceeds through the iterates \[x(t + 1) = x(t) - \alpha \nabla f(x(t)),\] where \(\nabla f(x) = (\mathsf{D} f(x))^{\mathsf{T}}\) is the gradient and \(\alpha &amp;gt; 0\) is a step-size parameter.  In general, this is a nonlinear system.  However, there is a particularly important special case when \(\nabla f(x)\) is a linear function of \(x\): when \(f(x) = \frac{1}{2}x^{\mathsf{T}} Q x\) is a quadratic function.  In this case, \(\nabla f(x) = Qx\) and gradient descent \[x(t + 1) = (I - \alpha Q)x(t)\] is a linear system with $A = I - α Q.$&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;electronic-circuits&#34;
    &gt;Electronic Circuits&lt;a href=&#34;#electronic-circuits&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;Another interesting example comes from electrical engineering.  In the design of electronic circuits, there are devices called &lt;em&gt;capacitors&lt;/em&gt; (basically two metal plates sandwiched next to each other) which can store a small electric charge and then subsequently release it.  Similarly, &lt;em&gt;inductors&lt;/em&gt; (coils of wire) store &lt;em&gt;magnetic&lt;/em&gt; energy, and later release it.&lt;/p&gt;
&lt;p&gt;Using these devices, along with simple resistors, we can construct an &lt;em&gt;RLC circuit&lt;/em&gt; &amp;ndash; a circuit consisting of resitors, inductors, and capacitors (the &amp;lsquo;L&amp;rsquo; for inductors coming from the scientist Heinrich Lenz).  One of the simplest such circuits is to just place these three elements in a loop with each other.&lt;/p&gt;


&lt;p&gt;Using &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws#Kirchhoff%27s_voltage_law&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kirchoff&amp;rsquo;s voltage law&lt;/a
&gt;
, which says that the total voltage differences around a loop must sum to zero, we can obtain the equation \(V_R(t) + V_L(t) + V_C(t)\) where \(V_R, V_L, V_C\) are the voltages across the resistor, inductor, and capacitor at time \(t\), respectively.&lt;/p&gt;
&lt;p&gt;The needed facts for this analysis are the  the &lt;em&gt;current-voltage relationships:&lt;/em&gt; $V_R(t) = RI_R(t),$ \(V_L(t) = L\dot{I}_L(t)\), and \(V_C(t) = \frac{1}{C}\int_0^t I_C(\tau)\mathsf{d}\tau\) with \(I_R, I_L, I_C\) being the currents passing through the devices, and \(R, L, C\) denoting the &amp;ldquo;size&amp;rdquo; of the devices in units of Ohms (resistance), Henries (inductance), and Farads (capacitance).  These are nothing but functions which describe how the devices operate.  The other key observation is that, since these devices are all placed in series one after the other along the same wire: \(I_R = I_L = I_C\)!  Thus, we have obtained the integro-differential equation:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
RI(t) + L\dot{I}(t) + \frac{1}{C}\int_0^t I(\tau) \mathsf{d} \tau &amp;amp;= 0\\
\implies R\dot{I}(t) + L\ddot{I}(t) + \frac{1}{C}I(t) &amp;amp;= 0,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which by differentiating through the whole thing results in a second order ODE.  We now intend to play a clever trick with this equation &amp;ndash; a common source of linear systems.  We think of the state variables as being \(x(t) = \bigl(\dot{I}(t), I(t)\bigr)\), and by using the relationship between these derivatives described above we obtain:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{bmatrix}
\ddot{I}\\
\dot{I}
\end{bmatrix}=
\begin{bmatrix}
-\frac{R}{L} &amp;amp; -\frac{1}{CL}\\
1 &amp;amp; 0\\
\end{bmatrix}
\begin{bmatrix}
\dot{I}\\
I
\end{bmatrix},
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a linear dynamical system.  Simulation of this system is straightforward.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; scipy.integrate &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; solve_ivp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;simulate&lt;/span&gt;(t, R, C, L, I0&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1.0&lt;/span&gt;, I_dot0&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;0.0&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    A &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;array([[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;1.0&lt;/span&gt;], [&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1.0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; (C &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; L), &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;R &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; L]])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ode&lt;/span&gt;(I):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; A &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; I
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    res &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; solve_ivp(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fun&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; t, y: ode(y),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        t_span&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;(t[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;], t[&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        y0&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;array([I0, I_dot0]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        t_eval&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;t
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; (res&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;y[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, :], res&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;y[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, :])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The reader unfamiliar with this trick is encouraged to study this example, as the technique is very commonly used.  The behaviour of the system can now be understood by analyzing the dynamics of this ODE given some set of initial conditions \(I(0), \dot{I}(0)\).&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;semidefinite-programming&#34;
    &gt;Semidefinite Programming&lt;a href=&#34;#semidefinite-programming&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;In convex optimization, a &lt;a
    class=&#34;link&#34;
    href=&#34;https://web.stanford.edu/~boyd/papers/pdf/semidef_prog.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;semidefinite program&lt;/a
&gt;
 is an optimization problem involving a matrix variable \(X\), a linear cost function \(J(x) = \mathsf{tr}\ C^{\mathsf{T}} X = \sum_{i, j} C_{ij} X_{ij}\), a linear equality constraint \(\mathcal{A}(X) = 0\), and a constraint \(X \succeq 0\), which means that \(X\) must be a &lt;em&gt;positive semidefinite matrix&lt;/em&gt;.  If you are not familiar with what this is, it is a matrix which is (1) &lt;em&gt;symmetric&lt;/em&gt; and (2) has &lt;em&gt;non-negative eigenvalues&lt;/em&gt;.  A generic SDP is as follows:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\underset{X \in \R^{n \times n}}{\text{minimize}}&amp;amp;\quad \mathsf{tr}\ C^{\mathsf{T}} X\\
\text{subject to}
&amp;amp;\quad \mathcal{A}(X) = 0\\
&amp;amp;\quad X \succeq 0.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;The linear function \(\mathcal{A}(X) = 0\) can be any linear function of \(X\) &amp;ndash; examples include \(AX = 0\), \(AXA^{\mathsf{T}} - X + Q = 0\), &lt;em&gt;etc.&lt;/em&gt;  Of course, much more general SDPs are possible, but this will be adequate for our purposes.&lt;/p&gt;
&lt;p&gt;Practically speaking, solving (relatively small: \(n &amp;lt; 100\)) SDPs is fairly straightforward.  Here is a basic &lt;a
    class=&#34;link&#34;
    href=&#34;https://www.cvxpy.org/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CVX Program&lt;/a
&gt;
 to solve an SDP we will encounter shortly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; math &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; inf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; scipy.linalg &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; la
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; cvxpy &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; cvx
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;solve_lyapunov_sdp&lt;/span&gt;(A, g, verbose&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Solves the feasibility problem P &amp;gt; 0, (1 - g) * P - A&amp;#39; P A &amp;gt; 0.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n, _ &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;shape
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    P &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cvx&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Variable(shape&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;(n, n), symmetric&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    I &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;eye(n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    objective &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cvx&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Minimize(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    constraints &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        P &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; I,  &lt;span style=&#34;color:#6272a4&#34;&gt;# P is positive semi-definite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; g) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; P &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; A&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;T &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; P &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; A &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; I
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    problem &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cvx&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;Problem(objective, constraints&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;constraints)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    problem&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;solve(solver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;CVXOPT&amp;#34;&lt;/span&gt;, verbose&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;verbose)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; problem&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;value, P&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;value
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Given a matrix \(A\), and a constant \(\gamma\), this program will solve a semidefinite &lt;em&gt;feasibility program&lt;/em&gt; to find a matrix \(P \succeq I\) such that \((1 - \gamma) P - A^{\mathsf{T}} P A \succeq I\).  This is actually just a computational means of solving a special type of feasability problem called a &lt;em&gt;linear matrix inequality&lt;/em&gt; (LMI).  Usually, LMIs are &lt;em&gt;strict&lt;/em&gt; inequalities where it is required to find \(P \succ 0\) and \((1 - \gamma) P - A^{\mathsf{T}} P A \succ 0\), and any such \(P\) will do.  However, since optimization problems are not usually well defined when strict inequalities are involved (since the feasible region is then not closed, &lt;em&gt;c.f.,&lt;/em&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Extreme_value_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Weierstrass&amp;rsquo; Theorem&lt;/a
&gt;
) it is necessary to modify the LMI to involve non-strict inequalities.  Doing this in a naive way, simply replacing \(\succ\) by \(\succeq\), may result in a trivial output \(P = 0\).  However, since the LMI is linear in \(P\), we can just scale the inequality and ask that it be \(\succeq I\) &amp;ndash; indeed, if any \(P \succ 0\) exists which satisfies the inequality, there must also exist one satisfying \(P \succeq I\).&lt;/p&gt;
&lt;p&gt;The meaning of this LMI will be explained next.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;lyapunov-stability-theory&#34;
    &gt;Lyapunov Stability Theory&lt;a href=&#34;#lyapunov-stability-theory&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The most important question to answer when it comes to linear systems is &lt;em&gt;stability&lt;/em&gt;.  The term stability is used to invoke the idea of a system which, when slightly perturbed, returns back to its nominal position.  However, it might be somewhat of a misnomer, as what is often really meant is &lt;em&gt;convergence&lt;/em&gt;: given some starting point \(x(0)\), does the linear system satisfy \(x(t) \rightarrow 0\ \text{as}\ t \rightarrow \infty\).  There are many methods of determining this, the most natural being an analysis of the eigenvalues of the system matrix \(A\).  Analyzing the eigenvalues however, is a method which is quite particular to the case of simple linear systems.  Another method, which has a much greater deal of potential for generalization, is the method of analysis by means of &lt;em&gt;Lyapunov Stability Theory&lt;/em&gt;.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;lyapunov-stability&#34;
    &gt;Lyapunov Stability&lt;a href=&#34;#lyapunov-stability&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;One of the most important techniques for establishing system stability is &lt;em&gt;Lyapunov&amp;rsquo;s direct method&lt;/em&gt; &amp;ndash; I&amp;rsquo;ll explain the idea for discrete time systems, although much of the literature focuses on the continuous time case.&lt;/p&gt;
&lt;p&gt;The idea is to find a so-called &lt;em&gt;Lyapunov function&lt;/em&gt;  \(V: \R^n \rightarrow \R\) on the state space which captures some generalized notion of &amp;ldquo;kinetic energy&amp;rdquo; of the system.  A Lyapunov function must satisfy a few properties.  The first of which is that $V(0) = 0,$ but \(V(x) &amp;gt; 0\) everwhere else (&lt;em&gt;i.e.,&lt;/em&gt; everywhere that \(x \ne 0\)).  I will call this property &lt;em&gt;positivity&lt;/em&gt;, and it is this property that makes a Lyapunov function analogous to a generalized notion of kinetic energy: it is positive except at an equilibrium point when the system stops moving.  The next key property is that \(V\) must be constructed such that its value decreases along the path traced by the system.  That is, if it can be shown that \(V(x(t + 1)) &amp;lt; V(x(t))\) for the system \(x(t + 1) = Ax(t)\), with \(x(0) = x_0 \in \R^n\) being an arbitrary initial condition, then the system can be expected to converge towards \(0\).&lt;/p&gt;
&lt;p&gt;These two properties (positivity and decreasing along trajectories) are not the whole story though.  The additional technical property of &lt;em&gt;coerciveness&lt;/em&gt; is required: \(||x|| \rightarrow \infty \implies V(x) \rightarrow \infty\).  This appears like an esoteric technical requirement, but it is essential.  If \(V\) is not coercive, then the &lt;em&gt;level sets&lt;/em&gt; of \(V\), the sets \(L_\alpha = \{x \in \R^n\ |\ V(x) \le \alpha\}\) may be unbounded, and the trajectory \(x(t)\) can drift off \(x(t) \rightarrow \infty\) while \(V(x(t))\) is stil decreasing.&lt;/p&gt;
&lt;p&gt;In the linear case, excellent &lt;em&gt;Lyapunov function candidates&lt;/em&gt; (i.e., functions \(V\) which you hope will serve as Lyapunov functions) are quadratic forms \(V(x) = x^{\mathsf{T}} P x\), for some matrix \(P \in \R^{n \times n}\).  It is quite easy to determine when these functions are positive &amp;ndash; this is the case exactly when \(P \succ 0\) (the matrix \(P\) is positive-definite), and moreover, \(V(x)\) will be coercive when \(P \succ 0\) (the matrix \(P\) is positive definite).  Thus, it makes sense to refer to positive coercive functions \(V\) as being positive definite; we can refer to a Lyapunov function for a system as being a positive definite function which is decreasing along trajectories of the system.&lt;/p&gt;
&lt;p&gt;So, in the linear case we have the candidate function \(V(x) = x^{\mathsf{T}} P x\) and the requirement \(P &amp;gt; 0\).  All that remains is to figure out if \(V(x(t + 1)) &amp;lt; V(x(t))\) along trajectories of the system.  This can be established by verifying \(V(Ax) &amp;lt; V(x)\) (why?), that is:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
\forall x \in \R^n:\ V(Ax) - V(x) &amp;lt; 0
&amp;amp;\iff \forall x \in \R^n:\ x^{\mathsf{T}}A^{\mathsf{T}}PAx - x^{\mathsf{T}}Px &amp;lt; 0\\
&amp;amp;\iff \forall x \in \R^n:\ x^{\mathsf{T}}\bigl(A^{\mathsf{T}} P A - P\bigr)x &amp;lt; 0\\
&amp;amp;\iff A^{\mathsf{T}} P A - P \prec 0.
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;If we are able to simply find some matrix \(P \succ 0\) such that \(V(x)\) is a Lyapunov function, then we have established the convergence of our system.  The astute reader will notice that this is a linear matrix inequality and can be solved by means of the above semidefinite feasibility program (with \(\gamma = 0\)).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Lyapunov stability is a fundamental tool for establishing convergence in nonlinear systems.  One of the most elegant examples is that of the gradient flow $\dot{x} = -\nabla f(x)$ for a strongly convex function $f$ (making this assumption for simplicity).  In this case, the function $f$ itself can serve as a Lyapunov function:  That is, $\frac{\mathsf{d}}{\mathsf{d}t} f(x) = \nabla f(x)^{\mathsf{T}} \dot{x} = -||\nabla f(x)||_2^2 &amp;lt; 0$ except at $x = x^\star$, the minimizer where $\nabla f(x^\star) = 0$.  Incidentally, this also tells us that the gradient flow monotonically decreases the function $f$.  Convergence proofs of &lt;em&gt;many&lt;/em&gt; algorithms come down to constructing an appropriate Lyapunov function.&lt;/kbd&gt;&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;example&#34;
    &gt;Example&lt;a href=&#34;#example&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;Consider a simple example with&lt;/p&gt;
&lt;p&gt;\begin{equation}
A = \begin{bmatrix}
0.750 &amp;amp; 1.00\\
-0.667 &amp;amp; 0.111
\end{bmatrix}.
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Notice that \(A\) does not have any particularly simple properties (triangularity, diagonal dominance&amp;hellip;) that would allow us to determine at a glance (unless we are rather speedy when it comes to calculating eigenvalues of \(2 \times 2\) matrices!) that the system \(x(t + 1) = Ax(t)\) is stable.  However, we can plug this matrix into our trusty semidefinite program and obtain a matrix&lt;/p&gt;
&lt;p&gt;\begin{equation}
P = \begin{bmatrix}
8.419 &amp;amp; 3.636\\
3.636 &amp;amp; 12.109
\end{bmatrix},
\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which proves stability by Lyapunov&amp;rsquo;s method.&lt;/p&gt;


&lt;p&gt;I&amp;rsquo;ve plotted above a figure showing the value of various functions of the state \(x(t)\) for the example.  The main observation is that while they all are roughly &amp;ldquo;decreasing&amp;rdquo;, it is only the Lyapunov function which is decreasing &lt;em&gt;monotonically&lt;/em&gt;.  That is not to say that this Lyapunov function is the &lt;em&gt;only&lt;/em&gt; function with this property &amp;ndash; there are many Lyapunov functions.  More subtly, it may also be possible to find starting points such that some other arbitrary function decreases monotonically towards zero from that particular system initialization, but a Lyapunov function will do so from &lt;em&gt;any&lt;/em&gt; starting point.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;fast-convergence&#34;
    &gt;Fast Convergence&lt;a href=&#34;#fast-convergence&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Can we modify Lyapunov&amp;rsquo;s method to somehow establish &lt;em&gt;fast&lt;/em&gt; convergence?  The answer is yes.  Consider the stronger condition:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
V(Ax) - V(x) &amp;amp;&amp;lt; -\gamma V(x)\\
\implies V(Ax) &amp;amp;&amp;lt; (1 - \gamma)V(x)\\
\implies V(A^2 x) &amp;amp;&amp;lt; (1 - \gamma)V(Ax) &amp;lt; (1 - \gamma)^2 V(x)\\
&amp;amp;\vdots\\
\implies V(A^T x) &amp;amp;&amp;lt; (1 - \gamma)^T V(x).
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;which is establishing a &lt;em&gt;rate of convergence&lt;/em&gt; on the Lyapunov function &amp;ndash; after \(T\) iterations of the system, the value must have decreased by a factor of \((1 - \gamma)^T\).  It is quite convenient to recognize that the existence of a quadratic Lyapunov function \(V_\gamma\) which verifies this fast convergence is equivalent to finding an ordinary quadratic Lyapunov function for the modified system with \(A_\gamma = \frac{1}{\sqrt{1 - \gamma}} A\).&lt;/p&gt;
&lt;p&gt;How do we incorporate the search for some such \(\gamma\) into our SDP?  All we need is that \(A^{\mathsf{T}}PA - (1 - \gamma)P \prec 0\), and indeed, we would like to find the largest satisfactory \(\gamma\).  Unfortunately, I do not believe it is possible to directly encode optimization over \(\gamma\) into a convex SDP, since the product $γ P $, where both \(\gamma\) and \(P\) are variables, is non-convex.  Instead, since \(\gamma \in \R\) is just a single parameter, we can apply &lt;em&gt;bisection&lt;/em&gt; to find an optimal \(\gamma\):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; math &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; inf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_feasible&lt;/span&gt;(A, g, verbose&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Checks if P &amp;gt; 0, (1 - g) * P - A&amp;#39; P A &amp;gt; 0 is feasible.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    val, _ &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; solve_lyapunov_sdp(A, g, verbose&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;verbose)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; val &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; inf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;find_optimal_convergence_rate&lt;/span&gt;(A, num_iter&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;20&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    right &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    left &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; it &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(num_iter):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        g &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; left &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; (right &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; left) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; is_feasible(A, g, verbose&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            left &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; g
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            right &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; g
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; g
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;By solving the feasability SDP multiple times, we obtain a value of \(\gamma\) such that \(V(A^T x) &amp;lt; (1 - \gamma)^T V(x)\), establishing a fast convergence rate for the system.  It should be noted that specialized algorithms to solve the Lyapunov inequality \(A^{\mathsf{T}} P A - P \prec 0\), but doing so by semidefinite programming opens up a more general array of possibilities, which I turn to next.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;em&gt;Remark&lt;/em&gt;: It is possible to represent this optimization problem as a joint optimization problem over $P, \gamma$, but not as a &lt;em&gt;convex&lt;/em&gt; optimization problem.  However, the resulting problem is &lt;em&gt;quasiconvex&lt;/em&gt;.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;stability-of-uncertain-linear-systems&#34;
    &gt;Stability of Uncertain Linear Systems&lt;a href=&#34;#stability-of-uncertain-linear-systems&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;So far, I&amp;rsquo;ve talked about linear systems with a single and perfectly well known matrix \(A\).  However, in many applications it may be the case that the matrix \(A\) is not known exactly, or that it is only an approximation of a real system.  For this reason, we would like to establish some degree of &lt;em&gt;robust&lt;/em&gt; stability guarantee.  To this end, let us suppose that we have some &lt;strong&gt;uncertainty set&lt;/strong&gt; \(\mathbf{A} \subset \R^{n \times n}\) and that our system matrix \(A \in \mathbf{A} \subset \R^{n \times n}\).  If this is the case, then we can expect our linear system to satisfy a linear &lt;strong&gt;recursive inclusion&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;\begin{equation}
x(t + 1) \in \mathbf{A} x(t) \overset{\Delta}{=} \{A x(t)\ |\ A \in \mathbf{A}\}. \notag
\end{equation}&lt;/p&gt;
&lt;p&gt;That is, the next point \(x(t + 1)\) is obtained from \(x(t)\) by simple multiplication by a matrix \(A\), we just don&amp;rsquo;t know &lt;strong&gt;which&lt;/strong&gt; matrix \(A\); all we know is that \(A \in \mathbf{A}\).  Amazingly, establishing the stability of this system can still be done by looking for a Lyapunov function \(V(x) = x^{\mathsf{T}} P x\) with \(P \succ 0\) and such that \(\forall x \in \R^n, A \in \mathbf{A}:\ V(A x) - V(x) &amp;lt; 0\) and this is equivalent to the collection of LMIs: \[P \succ 0, \forall A \in \mathbf{A}:\ A^{\mathsf{T}} P A - P \prec 0.\]&lt;/p&gt;
&lt;p&gt;Of course, not all such LMIs can be solved &amp;ndash; it depends on the model of \(\mathbf{A}\).  A quite flexible model is furnished by &lt;strong&gt;polytopic&lt;/strong&gt; sets: \(\mathbf{A} = \mathsf{conv}\{ A_k\ |\ k \in [K] \}\), that is, the convex hull of a &lt;strong&gt;finite&lt;/strong&gt; number of matrices \(A_k\), &lt;em&gt;i.e.,&lt;/em&gt; for any \(A \in \mathbf{A}\) there exists some \(\mu \in [0, 1]\) and matrices \(A_i, A_j\) such that \(A = \mu A_i + (1 - \mu) A_j\).  These sets can be used to construct arbitrarily accurate approximations of any convex set \(\mathbf{A}\).&lt;/p&gt;
&lt;p&gt;Then, using the fact that \(V(x)\) is convex if \(P \succ 0\), we have \(V(\mu A_i x + (1 - \mu) A_j x) \le \mu V(A_i x) + (1 - \mu) V(A_j x)\) and therefore a sufficient condition for the stability of the recursive inclusion is to find some \(P \succ 0\) which satisfies the system of \(K\) LMIs: \[P \succ 0, \forall k \in [K]:\ A_k^{\mathsf{T}} P A_k - P \prec 0.\]&lt;/p&gt;
&lt;p&gt;Speaking intuitively, if such a \(P\) exists for some uncertain linear system, we can make a conclusion about the &lt;strong&gt;robust&lt;/strong&gt; stability of the system.&lt;/p&gt;


&lt;h4 class=&#34;group &#34; id=&#34;example-gain-margins-and-momentum-gradient-descent&#34;
    &gt;Example &amp;ndash; Gain Margins and Momentum Gradient Descent&lt;a href=&#34;#example-gain-margins-and-momentum-gradient-descent&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h4&gt;

&lt;p&gt;One of the main motivations for considering uncertain linear systems arises from control theory.  A very common situation is that we have some system matrix \(A\), as well as a &lt;em&gt;gain matrix&lt;/em&gt; \(K\) which we have designed.  The gain matrix \(K\) is intended to be used to steer the system \(x(t + 1) = A x(t)\) towards zero by means of a &lt;em&gt;feedback control&lt;/em&gt; \(x(t + 1) = Ax(t) - Kx(t)\).  Importantly, the system \(A\) may very well be unstable, in which case the matrix \(K\) is absolutely essential.&lt;/p&gt;
&lt;p&gt;Suppose that we have some \(K\) in hand which makes the controlled system \(x(t + 1) = (A - K)x(t)\) stable.  The idea of a &lt;em&gt;gain margin&lt;/em&gt; is to determine if for some interval \([\alpha_1, \alpha_2]\) that the modified system \(x(t + 1) = (A - \alpha K) x(t)\) remains stable, for any \(\alpha \in [\alpha_1, \alpha_2]\).  If this interval is wide, then it may give us some assurance that the matrix \(K\) is robust, in some sense.  The collection of system matrices \[\mathbf{A} = \{A - \alpha K\ |\ \alpha_1 \le \alpha \le \alpha_2\}\] forms a natural polytopic set.&lt;/p&gt;
&lt;p&gt;Gradient descent constitutes a perfectly good example of this situation.  For a quadratic function \(f(x) = \frac{1}{2}x^{\mathsf{T}} Q x\) we have non-stable natural dynamics \(x(t + 1) = x(t)\), a controller \(Q\), and the stepsize \(\alpha\) fills in for the gain margin, giving us \(x(t + 1) = (I - \alpha Q)x(t)\).  To make this a bit more interesting, we can augment this system with a &lt;em&gt;momentum&lt;/em&gt; term by introducing a &lt;em&gt;velocity&lt;/em&gt; \(v(t)\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
v(t + 1) &amp;amp;= \gamma v(t) + \alpha Q x(t)\\
x(t + 1) &amp;amp;= x(t) - v(t + 1).
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;These equations are known as &lt;a
    class=&#34;link&#34;
    href=&#34;https://arxiv.org/pdf/1609.04747.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;gradient descent with momentum&lt;/a
&gt;
.  The idea is that if we are heading in some &amp;ldquo;nominal&amp;rdquo; direction \(v(t)\), we might as well keep going in that direction, and just add the gradient step \(\alpha Qx(t)\) to our velocity.  A slight modification of this momentum scheme, Nesterov&amp;rsquo;s momentum, is known to be optimal in a certain sense.&lt;/p&gt;
&lt;p&gt;In any case, the ordinary Momentum gradient descent algorithm corresponds to the (block) system matrix&lt;/p&gt;
&lt;p&gt;\begin{equation}
A_{\gamma, \alpha} =
\begin{bmatrix}
\gamma I &amp;amp; \alpha Q\\
-\gamma I &amp;amp; I - \alpha Q
\end{bmatrix}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;for the joint system \(\bigl(v(t), x(t)\bigr)\).&lt;/p&gt;
&lt;p&gt;Now, if you can construct a Lyapunov function (by solving a semidefinite program) for \(\gamma, \alpha \in \{\gamma_1, \gamma_2\} \times \{\alpha_1, \alpha_2\}\) (&lt;em&gt;i.e.&lt;/em&gt;, all four corners of a square) then you will have proof that the algorithm will converge for every pair \(\gamma, \alpha \in [\gamma_1, \gamma_2] \times [\alpha_1, \alpha_2]\).&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: This example is quite simplistic, but it is illustrative of&lt;/kbd&gt; &lt;a
    class=&#34;link&#34;
    href=&#34;https://arxiv.org/pdf/1502.02009.pdf&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;general and powerful techniques&lt;/a
&gt;
. &lt;kbd&gt;Specifically, if you can construct and solve an appropriate SDP &lt;em&gt;analytically&lt;/em&gt;, then it can serve as a constructive proof of convergence.  As well, SDPs can potentially be used to find &lt;em&gt;optimal&lt;/em&gt; parameters for algorithms by similar means as were used earlier to calculate convergence rates by means of bisection search.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;quadratic-integrals&#34;
    &gt;Quadratic Integrals&lt;a href=&#34;#quadratic-integrals&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;As a final illustration of the power of the Lyapunov approach, let&amp;rsquo;s consider how to evaluate infinite quadratic integrals (or summations, in the discrete case).  Say we have a linear system \(x(t + 1) = Ax(t)\), and are interested in computing the value of a of the path of this system according to the quadratic functional&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
J_Q(x_0) = \sum_{t = 0}^\infty x(t)^{\mathsf{T}} Q x(t); x(0) = x_0,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(Q \succ 0\) is some arbitrary positive definite matrix.  Such functions arise often in control theory.&lt;/p&gt;
&lt;p&gt;To see the connection with Lyapunov theory, recognize that we can fully expand out the summation in terms of \(x_0\) by using the fact that \(x(t) = A^t x_0\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
J_Q(x_0)
&amp;amp;= \sum_{t = 0}^\infty \bigl(A^t x_0 \bigr)^{\mathsf{T}} Q \bigl(A^t x_0)\\
&amp;amp;= x_0^{\mathsf{T}} \Bigl( \sum_{t = 0}^\infty (A^t)^{\mathsf{T}} Q A^t \Bigr)x_0\\
&amp;amp;= x_0^{\mathsf{T}} Q x_0 + \Bigl( \sum_{t = 1}^\infty (A^t)^{\mathsf{T}} Q A^t \Bigr)x_0\\
&amp;amp;= x_0^{\mathsf{T}} \Bigl(Q + \sum_{t = 1}^\infty (A^t)^{\mathsf{T}} Q A^t \Bigr)x_0\\
&amp;amp;= x_0^{\mathsf{T}} \Bigl(Q + A^{\mathsf{T}} \bigl[\sum_{t = 0}^\infty (A^t)^{\mathsf{T}} Q A^t \bigr]A \Bigr)x_0\\
&amp;amp;= x_0^{\mathsf{T}} Q x_0 + J(Ax_0).
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;Thus, since \(J\) is a quadratic function of \(x_0\), there must be some \(P\) such that \(J(x_0) = x_0^{\mathsf{T}} P x_0\) and therefore \(J(x)\) satisfies, for any starting point \(x \in \R^n\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
J(x)
&amp;amp;= x^{\mathsf{T}} P x\\
&amp;amp;= x^{\mathsf{T}}[Q + A^{\mathsf{T}} P A] x,
\end{aligned}\notag
\end{equation}&lt;/p&gt;
&lt;p&gt;or in other words, \(P - A^{\mathsf{T}} P A = Q\).  What this tells us is that if we have a matrix \(P \succ 0\) verifying the Lyapunov inequality \(P - A^{\mathsf{T}} P A \succ 0\), then not only do we have a Lyapunov function, but there is some positive definite matrix \(Q = P - A^{\mathsf{T}} P A\) such that \(x^\mathsf{T} P x\) is the value of the quadratic functional \(J_Q(x)\).  Conversely, if we can solve the Lyapunov equation \(P - A^{\mathsf{T}} P A = Q\) for some \(P \succ 0\), then we have a Lyapunov function, as well as a function for easily evaluating the value of \(J_Q(x)\), namely, \(J_Q(x) = x^{\mathsf{T}} P x\).&lt;/p&gt;
&lt;p&gt;I intend to explore the ideas surrounding quadratic functionals in more detail in a future post.  These functions can be used to tie together semidefinite programming duality, &lt;em&gt;stochastic&lt;/em&gt; linear systems (with random disturbances), as well as the classical methods of stochastic optimal control: the Kalman filter and the linear quadratic regulator.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Lyapunov&amp;rsquo;s equation&lt;/kbd&gt; \(P - A^{\mathsf{T}} P A = Q\) &lt;kbd&gt;is a &lt;em&gt;linear&lt;/em&gt; equation in&lt;/kbd&gt; \(P\) &lt;kbd&gt;and can be solved efficiently.  In Python, the function&lt;/kbd&gt; &lt;code&gt;scipy.linalg.solve_discrete_lyapunov&lt;/code&gt; &lt;kbd&gt;can be used for this purpose.  Lyapunov equations arise in a number of different ways, so it is a useful pattern to have in mind.&lt;/kbd&gt;&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;My purpose in writing this has been to explore the Lyapunov approach to analyzing the stability of linear dynamical systems.  This is only one of many possible approaches to this analysis, but it also generalizes (in various directions) much more easily than does a direct analysis of the eigenvalues of the system matrix.  Indeed, the Lyapunov approach can also lead to &lt;em&gt;global&lt;/em&gt; convergence theorems for nonlinear systems, whereas the Hartman-Grobman theorem combined with an eigenvalue analysis can only lead to a &lt;em&gt;local&lt;/em&gt; convergence result.  Moreover, the semidefinite programming perspective on this problem can also generalize greatly, and opens the door to constructing &lt;em&gt;optimal&lt;/em&gt; algorithms.  The tradeoff with the Lyapunov approach is that constructing an appropriate Lyapunov function can be &lt;em&gt;extremely&lt;/em&gt; difficult, and there is no ready-made recipe for doing so &amp;ndash; it often comes down to your creativity!&lt;/p&gt;</description></item><item>
            <title>Solving Equations with Jacobi Iteration</title>
            <link>https://rjtk.github.io/posts/solving-equations-with-jacobi-iteration/</link>
            <pubDate>Sun, 11 Dec 2022 00:00:00 -0800</pubDate>
            <guid>https://rjtk.github.io/posts/solving-equations-with-jacobi-iteration/</guid><description>&lt;p&gt;Jacobi iteration is a natural idea for solving certain types of nonlinear equations, and reduces to a famous algorithm for linear systems.  This post discusses the algorithm, its convergence, benefits and drawbacks, along with a discussion of examples and pretty pictures 🖼️.&lt;/p&gt;
&lt;h2 class=&#34;group &#34; id=&#34;introduction&#34;
    &gt;Introduction&lt;a href=&#34;#introduction&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The method of &lt;em&gt;Jacobi Iteration,&lt;/em&gt; named after the 19th century mathematician &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Carl_Gustav_Jacob_Jacobi&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Carl Gustav Jacobi&lt;/a
&gt;
 (the same Jacobi for whom the &lt;em&gt;Jacobian&lt;/em&gt; in calculus is named after), is a numerical method for solving systems of equations.  It is particularly famous as a classical iterative algorithm for solving &lt;em&gt;linear&lt;/em&gt; systems.  One of the main reasons one might want to use Jacobi iteration in practice is that it admits of a naturally &lt;em&gt;parallel&lt;/em&gt; implementation, and can thus scale to very large and complex systems, and even to problems without any closed form representation of the system we want to solve (like a simulator, or a black-box machine learning algorithm).&lt;/p&gt;
&lt;p&gt;In this post, I&amp;rsquo;ll introduce the basic mathematical intuition of Jacobi iteration, along with some examples of how and where it might arise.  I&amp;rsquo;ll also give a brief analysis of convergence for &lt;em&gt;linear&lt;/em&gt; systems of equations, where the concept of a &lt;em&gt;diagonally dominant&lt;/em&gt; matrix arises.  I also attempt to extend the intuition of the linear case to nonlinear systems by using techniques from the theory of ordinary differential equations.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;systems-of-equations&#34;
    &gt;Systems of Equations&lt;a href=&#34;#systems-of-equations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;We are all familiar with the grade-school notion of a &lt;em&gt;mathematical equation&lt;/em&gt;.  Particularly famous are the quadratic polynomial equations, &lt;em&gt;e.g.,&lt;/em&gt; \(x^2 - x - 2 = 0\), which has exactly the solutions \(x = 2\) and \(x = -1\).  For another example, the trigonometric equation \(\mathsf{sin}(\frac{(2x + 1) \pi}{2}) - 1 = 0\) has any \(x\) being an integer multiple of \(2\), &lt;em&gt;i.e.,&lt;/em&gt; \(x \in 2\Z\) as a solution.&lt;/p&gt;
&lt;p&gt;What is meant by a &lt;em&gt;system&lt;/em&gt; of equations is simply a multitude of ordinary equations that need to be satisfied &lt;em&gt;simultaneously&lt;/em&gt;.  For instance, if we combined the above two examples into the system of &lt;em&gt;two equations&lt;/em&gt; and &lt;em&gt;one variable&lt;/em&gt; we would be left with the &lt;em&gt;system&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
x^2 + x - 2 &amp;amp;= 0\\
\mathsf{sin}\bigl(\frac{(2x + 1) \pi}{2}\bigr) - 1 &amp;amp;= 0,
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;which now has a single unique &lt;em&gt;simultaneous&lt;/em&gt; solution \(x = 2\).&lt;/p&gt;
&lt;p&gt;In general, we can write systems of equations using a single multivariate function \(F: \R^m \rightarrow \R^n\) which takes \(m\) variables \(x = (x_1, x_2, \ldots, x_m)\) as in \(F(x)\), and outputs \(n\) values \(F(x) = \bigl(F_1(x), F_2(x), \ldots, F_n(x)\bigr)\).  It is a &lt;em&gt;rule of thumb&lt;/em&gt; (certainly not an actual &lt;em&gt;rule&lt;/em&gt;) that if there are \(n\) equations, you can expect to be able to solve for \(n\) variables, &lt;em&gt;i.e.&lt;/em&gt;, the function \(F\) is &amp;ldquo;square&amp;rdquo; with \(m = n\).  Assuming this square case is by no-means essential, but it simplifies many of our examples, so we will run with this case.&lt;/p&gt;
&lt;p&gt;The ultimate goal of &lt;em&gt;solving&lt;/em&gt; systems of equations is to find some \(x \in \R^n\) such that \(F(x) = 0\).  We can motivate such problems with a few illustrative examples.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;minimizing-functions&#34;
    &gt;Minimizing Functions&lt;a href=&#34;#minimizing-functions&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Consider a function \(f: \R^n \rightarrow \R\) which we want to &lt;em&gt;minimize&lt;/em&gt;, &lt;em&gt;i.e.&lt;/em&gt;, to find an \(x^\star \in \R^n\) such that \(f(x^\star) \le f(x)\) for every other \(x\) in some neighbourhood of \(x^\star\).  One might want to think of \(f(x)\) perhaps as a design objective (find the best design according to the cost function \(f\)), or as a machine learning loss function, &lt;em&gt;etc.&lt;/em&gt;  It is a theorem (the &lt;em&gt;first order necessary conditions&lt;/em&gt;) that for differentiable functions \(f\), any minimizer \(x^\star\) must necessarily satisfy the derivative condition \(\mathsf{D} f(x^\star) = 0\), where \(\mathsf{D} f: \R^n \rightarrow \R^n\) is the derivative of \(f\).&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;economic-equilibria&#34;
    &gt;Economic Equilibria&lt;a href=&#34;#economic-equilibria&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;In economics, many believe that prices of goods in an economy are determined by the matching of supply and demand.  That is, if \(\mathcal{D}(p)\) is a &lt;em&gt;demand curve&lt;/em&gt; and \(\mathcal{S}(p)\) is a &lt;em&gt;supply curve&lt;/em&gt;, then we expect the price \(p\) to be found from solving the nonlinear equation \(\mathcal{D}(p) = \mathcal{S}(p)\).  I have much more to say about this example in Section &lt;a
    class=&#34;link&#34;
    href=&#34;#example-supply-and-demand-with-substitution&#34;&gt;Example: Supply and Demand with Substitution&lt;/a
&gt;
, where I apply Jacobi iteration to a simple coffee and tea economy where the two goods serve as partial substitutes for one and other.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;robotic-manipulators&#34;
    &gt;Robotic Manipulators&lt;a href=&#34;#robotic-manipulators&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Robotic arms with stiff linkages can be modeled by the angles \(\theta\) of their joints.  For example, the position of your hand (your &lt;em&gt;end affector&lt;/em&gt;) in space can be determined as a function of the length of your upper arm and your forearm, along with the angles formed at your elbow and shoulder (including the multiple dimensions of rotation your shoulder is capable of).  Specifically, we might wish to describe the position of your hand in space by a function \(F(\theta)\) of these joints.  The problem of determining the appropriate angular settings of your joints, in order to place your hand at a point \(p \in \R^3\) in space, is a problem of solving the system of equations \(F(\theta) - p = 0\), and one which your brain apparently solves with remarkable ease.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;jacobi-iteration&#34;
    &gt;Jacobi Iteration&lt;a href=&#34;#jacobi-iteration&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;The ideal of &lt;em&gt;Jacobi iteration&lt;/em&gt; is to split up the system \(F(x) = 0\) of \(n\) equations in \(n\) unknowns, into a sequence of simpler equations of one variable and one unknown.  For equation \(i\), we imagine that every variable &lt;em&gt;except&lt;/em&gt; \(x_i\), let&amp;rsquo;s call them \(x_{-i}\) is held fixed, and that we don&amp;rsquo;t care about the value of any function except \(F_i\).  We then solve the &lt;em&gt;univariate&lt;/em&gt; equation \(F_i(x_i; x_{-i}) = 0\) (This notation is common in game theory, I hope that it is understood) to find the single value of \(x_i\) that results in function \(F_i\) being satisfied.&lt;/p&gt;
&lt;p&gt;This process is carried out, &lt;em&gt;possibly in parallel&lt;/em&gt;, simultaneously for each equation to obtain a new set of points which we hope is closer to satisfying the full system of equations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remark&lt;/strong&gt;: Proceeding sequentially, rather than simultaneously, by updating each \(x_i\) value immediately after finding a univariate solution, and before finding the next one, is an algorithm called &lt;em&gt;Gauss-Seidel Iteration&lt;/em&gt;.  The reader is encouraged to meditate upon the difference.&lt;/p&gt;
&lt;p&gt;A pseudo-code algorithm implementing Jacobi iteration is given as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Initialize&lt;/kbd&gt; \(x(0) \in \R^n\)&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;for&lt;/kbd&gt; \(t = 1, 2, \ldots\)
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;parallel for each&lt;/kbd&gt; \(i \in [n]\)
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;find&lt;/kbd&gt; \(\tilde{x}_i\) &lt;kbd&gt;such that&lt;/kbd&gt; \(F_i(\tilde{x}_i, x_{-i}(t)) = 0\)  &lt;kbd&gt;// i.e., solve the $i^{th}$ equation&lt;/kbd&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;set&lt;/kbd&gt; \(x(t + 1) = \tilde{x}\)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order that this algorithm be an appropriate choice for your problem, it is &lt;em&gt;at least&lt;/em&gt; necessary that the step requiring that we &amp;ldquo;solve the \(i^{th}\) equation&amp;rdquo; can be reliably carried out.  However, even if solving each of the sub-problems is challenging, we may benefit from the straight-forward parallelism offered by finding each \(\tilde{x}_i\) simultaneously.&lt;/p&gt;
&lt;p&gt;The other major issue is &lt;em&gt;convergence&lt;/em&gt;, &lt;em&gt;i.e.,&lt;/em&gt; does the algorithm actually find a solution?  We can get fairly clear answers to this question in the linear case by applying dynamical systems theory.  As is so often the case, the theory for linear functions serves as a stepping stone to building intuition more generally.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;the-case-of-linear-equations&#34;
    &gt;The Case of Linear Equations&lt;a href=&#34;#the-case-of-linear-equations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Whether or not the algorithm is actually able to find some \(x\) such that \(F(x) = 0\) (assuming at the very least that such an \(x\) &lt;em&gt;exists&lt;/em&gt;!) is highly problem dependent and in practice may require various &amp;ldquo;clever tweaks&amp;rdquo;, or benefit from restarting the algorithm from a wide range of initial points \(x(0)\).  However, for the case of &lt;em&gt;linear&lt;/em&gt; systems of equations \(Ax = b\) (with \(A \in \R^{n \times n}\) a square matrix with real entries), the algorithm is both elegantly simple, and admits of an easily verifiable &lt;em&gt;sufficient condition&lt;/em&gt; for convergence known as &lt;em&gt;diagonal dominance&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To understand how this works, lets solve for \(x_i\) such that the \(i^{th}\) equation is satisfied.  That is,&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
\sum_{j = 1}^n A_{ij} x_j &amp;amp;= b_i\\
\iff A_{ii} x_i &amp;amp;= b_i - \sum_{j: j \ne i} A_{ij} x_j\\
\iff x_i &amp;amp;= \frac{1}{A_{ii}}\bigl(b_i - \sum_{j: j \ne i}A_{ij} x_j \bigr),
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;where we should notice that we already require the diagonal elements of \(A\) to be non-zero (otherwise we couldn&amp;rsquo;t divide).&lt;/p&gt;
&lt;p&gt;Focusing for a moment on the term \(\sum_{j \ne i} A_{ij} x_j\), this is nothing but the \(i^{th}\) element in the matrix multiplication \(Ax\) minus the component \(A_{ii} x_i\), that is, \(\sum_{j \ne i} A_{ij} x_j = (Ax)_i - A_{ii} x_i\).  Using this, we can write the parallel updates to the entire vector \(x\), and the entire Jacobi iteration algorithm as:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
x(t + 1) = D^{-1} (b - Mx(t)),
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(D = \mathsf{dg}(A)\) (the diagonal elements of \(A\)), \(M = A - D\) is all of the &lt;em&gt;off-diagonal&lt;/em&gt; elements of \(A\), and \(x(0) \in \R^n\) is an arbitrary starting point for the sequence of candidate solutions \(x(t)\).  When people refer to &amp;ldquo;Jacobi iteration&amp;rdquo;, it is usually this algorithm in particular that they are referring to.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;convergence&#34;
    &gt;Convergence&lt;a href=&#34;#convergence&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;The hope is that the iterations \(x(t)\) converge to a solution \(x^\star\), &lt;em&gt;i.e.,&lt;/em&gt; \(x(t) \rightarrow x^\star\), where \(Ax^\star = b\).&lt;/p&gt;
&lt;p&gt;To see why we might expect this to happen, imagine that \(x(t)\) converges to a &lt;em&gt;fixed point&lt;/em&gt; of the algorithm, &lt;em&gt;i.e.,&lt;/em&gt; some \(x(\infty)\) satisfying \(x(\infty) = D^{-1} (b - M x(\infty))\).  For such a point it holds that:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
x(\infty) &amp;amp;= D^{-1} (b - M x(\infty))\\
Dx(\infty) &amp;amp;= b - M x(\infty)\\
(D + M)x(\infty) &amp;amp;= b\\
Ax(\infty) &amp;amp;= b,
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;which is a solution to the equation \(Ax = b\)!&lt;/p&gt;
&lt;p&gt;To establish the convergence of the iterations, it is well known (this is likey to be a topic of a future post! 😜) that for linear systems \(x(t + 1) = a + K x(t)\), \(x(t)\) will converge whenever \(\rho(K) &amp;lt; 1\), where \(\rho(K) = \text{max}_i\ |\lambda_i(K)|\) is the largest eigenvalue magnitude, a quantity called the &lt;em&gt;spectral radius&lt;/em&gt;.  For the case of Jacobi iteration, we require that \(\rho(D^{-1} M) &amp;lt; 1\).&lt;/p&gt;
&lt;p&gt;Using the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Gershgorin_circle_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Gershgorin circle theorem&lt;/a
&gt;
, and the fact that the diagonal values of \(M\) are all \(0\) (by construction), it holds that every eigenvalue of \(D^{-1} M\) lies within a distance \(|D_{ii}^{-1}|\sum_{j \ne i} |M_{ij}|\) of \(0\).  A condition on \(A\) which guarantees this is  &lt;em&gt;diagonal dominance&lt;/em&gt;: \(\sum_{j \ne i} |A_{ij}| &amp;lt; |A_{ii}|\) for each \(i\).  If this holds, then the eigenvalues \(\lambda_i\) of \(A\) are &amp;ldquo;close&amp;rdquo; to the diagonals of \(A_{ii}\), so such matrices are in some sense &amp;ldquo;almost diagonal&amp;rdquo;.  Since linear systems \(Dx = b\), for diagonal \(D\), are easy to solve, it is not surprising that there is a simple iterative algorithm for solving linear systems which are diagonally dominant.  Moreover, if the diagonals of \(A\) are also non-zero, \(A\) will necessarily have non-zero eigenvalues, and therefore it will be full-rank, invertible, and admit of unique solutions.  This is all summarized with the following theorem.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Theorem&lt;/strong&gt;: Let $A \in \R^{n \times n}$ be a square matrix with real entries.  Suppose that $A$ has a non-zero diagonal and is &lt;em&gt;diagonally dominant&lt;/em&gt;.  Then, for any vector $b \in \R^n$, there exists a unique solution $x^\star$ to the linear equation $Ax = b$ and Jacobi iteration converges, from any initial condition, to the solution $x^\star$.&lt;/kbd&gt;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;linear-2-times-2-example&#34;
    &gt;Linear \(2 \times 2\) Example&lt;a href=&#34;#linear-2-times-2-example&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Some straightforward Python code implementing linear Jacobi iteration is provided in the listing below.  A realistic implementation should have a method of detecting divergence.  As well, checking the norm of the distance to the solution on every iteration is relatively expensive &amp;ndash; it essentially doubles the computational effort.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;solve_linear_system&lt;/span&gt;(A, b, x0&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, eps&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1e-6&lt;/span&gt;, maxiter&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;inf):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Solves the linear system Ax = b by Jacobi iteration.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    The algorithm&amp;#39;s starting point is x0.  The algorithm is only guaranteed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    to converge if A is diagonally dominant.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Dinv_M, Dinv_b &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (A &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;diagflat(D)) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;diag(A)[:, &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;], b &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;diag(A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;array(x0) &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; x0 &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;zeros_like(b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    it &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;while&lt;/span&gt; it &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; maxiter &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;linalg&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;norm(A &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; x &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; b) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; eps:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Dinv_b &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; Dinv_M &lt;span style=&#34;color:#ff79c6&#34;&gt;@&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        it &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For problems in \(\R^2\) (&lt;em&gt;i.e.,&lt;/em&gt; with just two variables) it is quite straight-forward to produce nice-looking figures.  The following figure plots the &lt;em&gt;flow&lt;/em&gt; of an associated ODE, as well as an &amp;ldquo;SOR&amp;rdquo; modification (both to be explained shortly 💁), along with the discrete iterates of the Jacobi iteration algorithm for the matrix&lt;/p&gt;
&lt;p&gt;\[
A = \begin{bmatrix} 4/5 &amp;amp; 3 / 5 \\ -6 / 5 &amp;amp; 7/5\end{bmatrix},
\]&lt;/p&gt;
&lt;p&gt;and \(b = 0\).  The case \(b = 0\) is without loss of generality for the purpose of plotting.  Even though finding a solution to the equation \(Ax = 0\) is trivial in this case, it is equivalent to re-orienting the center of our coordinate system upon the solution \(A^{-1} b\).  It is also worth noting that this matrix is row-wise diagonally dominant (corresponding to our definition) but &lt;em&gt;not&lt;/em&gt; column wise.&lt;/p&gt;


&lt;p&gt;If you squint closely at this figure, you might even believe that the lines joining the discrete iterates are &lt;em&gt;tangent&lt;/em&gt; to the flow lines in the background&amp;hellip;&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;relaxation-and-associated-flows&#34;
    &gt;Relaxation and Associated Flows&lt;a href=&#34;#relaxation-and-associated-flows&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Our figure above is constructed by plotting the &lt;em&gt;vector field&lt;/em&gt; from the &lt;em&gt;flow&lt;/em&gt; of a closely related ordinary differential equation.  Let&amp;rsquo;s use \(t\) to denote time, and as in the previous example, assume without loss that \(b = 0\).&lt;/p&gt;
&lt;p&gt;Merely as a device to construct a differential equation, imagine that each step of the algorithm takes \(\Delta\) &amp;ldquo;algorithm time&amp;rdquo;.  Precisely, let us write&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
x(t + \Delta) &amp;amp;= -D^{-1} Mx(t)\\
\iff x(t + \Delta) - x(t) &amp;amp;= -(I + D^{-1} M)x(t))\\
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;Now, we have an analogy with continually &lt;em&gt;taking steps&lt;/em&gt; \(-(I + D^{-1} M)x(t)\) to update the value of \(x(t)\).  If we reduce the size of these steps by \(\Delta\) and then take a limit as \(\Delta \rightarrow 0\)&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
x(t + \Delta) - x(t) &amp;amp;= -\Delta(I + D^{-1} M)x(t))\\
\iff  \frac{1}{\Delta} [x(t + \Delta) - x(t)] &amp;amp;= -(I + D^{-1} M)x(t))\\
\overset{\Delta \rightarrow 0}{\implies} \dot{x}(t)  &amp;amp;= -(I + D^{-1} M)x(t)),
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;we obtain a &lt;em&gt;linear&lt;/em&gt; ordinary differential equation.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Remark&lt;/strong&gt;:  The stablity of linear ODEs can be established by checking that the eigenvalues of the corresponding system matrix have negative real part.  In our case, due to the negative sign, we require that $\mathfrak{R}[\lambda_i(I + D^{-1} M)] &amp;gt; 0$ hold for every $i$, where $\lambda_i(A)$ is the $i^{th}$ eigenvalue of $A$.  Since $D^{-1}M$ has $0$ on the main diagonal (by construction) the Gershgorin circle theorem tells us that the eigenvalues lie within Gershgorin discs centered at $1$.  Clearly, the condition $\rho(D^{-1} M) &amp;lt; 1$ is &lt;em&gt;sufficient&lt;/em&gt; for the stability of this ODE.  But, thinking intuitively, should one expect that this ODE be &amp;ldquo;more likely&amp;rdquo; to converge than the discrete algorithm?  The reader is encouraged to visualize this situation, and to think about this stability condition.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s play with this a bit.  What if we didn&amp;rsquo;t take a full limit towards \(\Delta \rightarrow 0\)?  Rearranging the above equations results in another discrete algorithm&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
x(t + \Delta) = (1 - \Delta) x(t) -\Delta D^{-1} Mx(t)),
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(\Delta &amp;gt; 0\) is a &lt;em&gt;parameter&lt;/em&gt; of the algorithm, with \(\Delta = 1\) corresponding to ordinary Jacobi iteration.&lt;/p&gt;
&lt;p&gt;This algorithm is now picking the next point to lie somewhere along the line connecting \(x\) and the nominal next step \(s(x)\), that is: as \(x \leftarrow (1 - \Delta) x + \Delta s(x)\).  This is a general pattern called &lt;em&gt;Successive Over-relaxation&lt;/em&gt; (SOR), and can be applied to any iterative algorithm which takes as input a point \(x\), and outputs the next point as \(x \leftarrow s(x)\).  When \(\Delta &amp;lt; 1\), the next point will lie somewhere in the interval \([x, s(x)]\) between the current point and the nominal next step (the &amp;ldquo;relaxation&amp;rdquo; part of SOR); and \(\Delta &amp;gt; 1\) means that the step goes &lt;em&gt;beyond&lt;/em&gt; \(s(x)\) to move a further distance (the &amp;ldquo;over&amp;rdquo; part).  The value \(\Delta = 0\) corresponds to the &lt;em&gt;vanilla&lt;/em&gt; version of the algorithm.  I don&amp;rsquo;t think that the case \(\Delta &amp;lt; 0\) has any sensible use (at least not directly in this context) as you would be going &lt;em&gt;backwards&lt;/em&gt; in some sense, but maybe it&amp;rsquo;s an interesting possibility to think about.&lt;/p&gt;
&lt;p&gt;The reason that the SOR algorithm can be expected to converge for a broader collection of \(A\) matrices than ordinary Jacobi iteration, is that the space of matrices satisfying \(\mathfrak{R}[\lambda_i(I + D^{-1} M)] &amp;gt; 0\) is larger than those which satisfy \(\rho(D^{-1} M) &amp;lt; 1\) (can you see why?).  However, for a matrix \(A\) such that \(D^{-1}M\) has an eigenvalue with real part less than \(-1\), both algorithms will diverge.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;the-case-of-nonlinear-equations&#34;
    &gt;The case of Nonlinear Equations&lt;a href=&#34;#the-case-of-nonlinear-equations&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;For the case of nonlinear equations, we can use tools from ordinary differential equations to say something about the convergence of the associated Jacobi flow, and then extend that intuition through successive over-relaxation to try to understand something about how the discrete algorithm will behave.&lt;/p&gt;
&lt;p&gt;To this end, let us go back to the &lt;em&gt;nonlinear&lt;/em&gt; equation \(F(x) = 0\) that we want to solve.  Supposing that the Jacobi iteration algorithm can be implemented for \(F\) (&lt;em&gt;i.e.,&lt;/em&gt; each equation in the system can be solved in the &amp;ldquo;diagonal&amp;rdquo; variable), we can write the algorithm with the notation&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
x(t + 1) = \mathcal{M}(x(t)),
\end{equation}&lt;/p&gt;
&lt;p&gt;where for any \(x\), \(y = \mathcal{M}(x)\) is a vector such that \(F_i(x_{-i}, y_i) = 0\), that is, solves the \(i^{th}\) equation in the \(i^{th}\) variable.  Following the same trick as we had in the linear case, we can construct an ODE:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
\frac{x(t + \Delta) - x(t)}{\Delta} &amp;amp;= \mathcal{M}(x(t)) - x(t)\\
\overset{\Delta \rightarrow 0}{\implies} \dot{x}(t) &amp;amp;= \mathcal{M}(x(t)) - x(t).
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;Thus, we can hope to understand the convergence of nonlinear Jacobi iteration by analyzing the convergence of the nonlinear system of ODEs associated to the function \(\mathcal{M}\).  Firstly, a fixed point of this ODE is, similarly to the linear case, a solution to the nonlinear equation.  So, perhaps we can understand the convergence of \(x(t)\) for points where \(\mathcal{M}(x(t)) \approx x(t)\)?  The method for doing this type of &lt;em&gt;local&lt;/em&gt; analysis of ODE convergence is the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Hartman%E2%80%93Grobman_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hartman-Grobman Theorem&lt;/a
&gt;
 (HGT from hereon).  Intuitively, our hope is that if we initialize the algorithm somewhere &amp;ldquo;close&amp;rdquo; to a solution, that it will actually converge to that solution.  That is, we hope that the ODE is &lt;em&gt;locally asymptotically stable&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The HGT essentially tells us that if the &lt;em&gt;Jacobian&lt;/em&gt; \(A = \mathsf{D}\mathcal{M}(x^\star) - I\) of the system at an equilibrium point \(x^\star\) is stable, &lt;em&gt;i.e.,&lt;/em&gt; \(\dot{x} = Ax\) converges to \(x^\star\), then there is a local neighbourhood around \(x^\star\) such that the original &lt;em&gt;nonlinear&lt;/em&gt; ODE \(\dot{x} = \mathcal{M}(x) - x\) converges to \(x^\star\).&lt;/p&gt;
&lt;p&gt;This statement is still pretty abstract, since we don&amp;rsquo;t really have a handle on what \(\mathcal{M}\) is explicitly.  Another abstract tool that we could apply here is the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Implicit_function_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Implicit Function Theorem&lt;/a
&gt;
, which would allow us to calculate \(\mathsf{D}\mathcal{M}\) in terms of derivatives of the original function \(F\).  While this approach can certainly get us somewhere, I&amp;rsquo;d like to make a closer analogy with the linear case.  To this end, let&amp;rsquo;s make a simplifying assumption about the structure of \(F\):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
F_i(x) = f_i(x_i) + g_i(x_{-i})\ \forall i,
\end{equation}&lt;/p&gt;
&lt;p&gt;that is, the \(i^{th}\) equation consists of an individual function \(f_i: \mathbb{R} \rightarrow \mathbb{R}\) of \(x_i\), along with additional &lt;em&gt;coupling&lt;/em&gt; function \(g_{i}: \mathbb{R}^{n - 1} \rightarrow \mathbb{R}^{}\) involving the remainder of the variables.  We can now write down what \(\mathcal{M}_i\) is &lt;em&gt;explicitly&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\mathcal{M}_i(x) = f_i^{-1} (-g_i(x_{-i})).
\end{equation}&lt;/p&gt;
&lt;p&gt;In the linear case, \(f_i^{-1}(z) = z / A_{ii}\) and \(g_i(x_{-i}) = \sum_{j: j \ne i} A_{ij} x_j - b_i\).  Considering now the derivatives of \(\mathcal{M}_i,\)  we just need to combine the &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Inverse_function_theorem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Inverse Function Theorem&lt;/a
&gt;
 along with the chain rule to obtain&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\mathsf{D}\mathcal{M}_i(x) = -\frac{1}{f_i^\prime \circ \mathcal{M}_i(x)} \begin{bmatrix}\frac{\partial g_i(x_{-i})}{\partial x_1} &amp;amp; \cdots &amp;amp; \frac{\partial g_i(x_{-i})}{\partial x_{i - 1}} &amp;amp; 0 &amp;amp; \frac{\partial g_i(x_{-i})}{\partial x_{i + 1}} &amp;amp; \cdots &amp;amp;\frac{\partial g_i(x_{-i})}{\partial x_{n}}\end{bmatrix}
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(f^\prime \circ \mathcal{M}_i(x) = f^\prime (f_i^{-1} (-g_{i}(x_{-i})))\).  Thus, in exact analogy with the linear case, we can write&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\mathsf{D}\mathcal{M}(x) = -D(x)^{-1} M(x),
\end{equation}&lt;/p&gt;
&lt;p&gt;where \(D(x)\) is a diagonal matrix consisting of the derivatives \(f_i^\prime\) evaluated at the point \(\mathcal{M}_i(x)\) and \(M(x)\) is the Jacobian \(\mathsf{D}F\) of \(F\) itself, but with the diagonal elements zeroed out.&lt;/p&gt;
&lt;p&gt;Returning to the HGT, we need to consider the stability of the linear ODE defined by the matrix \(A = \mathsf{D}\mathcal{M}(x^\star) - I\).  Since \(\mathcal{M}(x^\star) = x^\star\), we have that \(D_i(x^\star) = f_i^\prime(x^\star_i)\).  Using this fact, we are inspired to make a definition of a &lt;em&gt;locally diagonally dominant&lt;/em&gt; function.  We will say that \(F\) is a locally diagonally dominant (around an equilibrium point \(x^\star\)) if it has the form \(F_i(x) = f_i(x_i) + g_i(x_{-i})\) and \[|f_i^\prime(x_i^\star)| &amp;gt; \sum_{j: j \ne i} |\frac{\partial g_i (x^\star_{-i})}{\partial x_j}|\] for every \(i\).&lt;/p&gt;
&lt;p&gt;Using what we know from the linear case, combined with the HGT, and the fact that the SOR scheme is an &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Euler_method&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Euler Method&lt;/a
&gt;
 for the ODE (which converges towards the ODE itself as \(\Delta \rightarrow 0\)), we have a theorem about local convergence of nonlinear Jacobi iteration:&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;&lt;strong&gt;Theorem&lt;/strong&gt;: Let $F: \mathbb{R}^n \rightarrow \mathbb{R}^n$ be a smooth function.  Suppose there exists some $x^\star$ such that $F(x^\star) = 0$ and that $F(x^\star)$ is locally diagonally dominant in a neighbourhood of $x^\star$.  Then, there exists a neighbourhood $\mathcal{N}$ of $x^\star$ and a $\Delta &amp;gt; 0$ such that SOR nonlinear Jacobi iteration with step-size $\Delta$ converges to $x^\star$ from any initial point $x \in \mathcal{N}$.&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Of course, the linear case corresponds to the functions \(f_i(x_i) = A_{ii} x_i\) and \(g_i(x_{-i}) = \sum_{j: j \ne i} A_{ij} x_j - b_i\), wherein we recover from above the convergence theorem of linear Jacobi iteration.&lt;/p&gt;


&lt;h3 class=&#34;group &#34; id=&#34;example-supply-and-demand-with-substitution&#34;
    &gt;Example: Supply and Demand with Substitution&lt;a href=&#34;#example-supply-and-demand-with-substitution&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h3&gt;

&lt;p&gt;Let&amp;rsquo;s cook up an example from economics where we&amp;rsquo;ll try to work out the equilibrium prices of goods in an economy.  A typical economic model (at least so far as I know&amp;hellip; I&amp;rsquo;m no economist!), is to find the prices at which the supply of a good is matched to the demand for that good.  The story is that when prices are high, manufacturers will scramble to produce that good in order to sell it for a large profit, which will drive down the price for the good through competition.  Similarly, when prices are low, demand will be very high, since you can get a lot of utility out of consuming the good in comparison to keeping the small amount of money.  This can be expected to drive up prices.  Through these competing effects pushing prices up and down, it is hoped that prices will &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/General_equilibrium_theory&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;equilibriate&lt;/em&gt;&lt;/a
&gt;
 at some fixed value through a process of &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Walrasian_auction&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;tâtonnement&lt;/em&gt;&lt;/a
&gt;
.&lt;/p&gt;
&lt;p&gt;The critical aspect that will make this model an interesting example for solving nonlinear equations is a &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Substitute_good&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;substitution effect&lt;/em&gt;&lt;/a
&gt;
.  To get more specific, suppose that when the prices for all \(n\) goods are given by \(p \in \R^n\), the supply for good \(i\) is described by a function \(\mathcal{S}_i(p) = S_i(p_i)\) and the demand for this good is \[\mathcal{D}_i(p) = D_i(p_i) + T_i(p_{-i}).\]  What this latter equation says is that there is some &lt;em&gt;nominal&lt;/em&gt; demand \(D_i(p_i)\) given by a scalar function of the price of that good, as well as the functions \(T_i(p_{-i})\) which serves to model the &lt;em&gt;substitution effect&lt;/em&gt;: the demand for good \(i\) also depends upon the prices of other goods in the economy.  A set of equilibrium prices \(p^\star \in \R^n\) (which may not be unique!) in this economy are prices such that \[\mathcal{S_i}(p^\star) - \mathcal{D}_i(p^\star) = 0\ \forall i \in [n],\] a nonlinear equation.&lt;/p&gt;
&lt;p&gt;To make this more concrete, suppose we have an economy with two goods: coffee and tea.  As the price of coffee increases, demand for coffee also falls.  But, the demand for tea &lt;em&gt;rises&lt;/em&gt;, since some coffee drinkers that are tightening their belt and drinking less coffee, start drinking more tea.  Let&amp;rsquo;s write this all down mathematically.  For the &lt;em&gt;demand&lt;/em&gt; I&amp;rsquo;ll use functions \(D_i(p_i) = \frac{k_i}{1 + p_i}\), modelling the fact that demand should be decreasing toward zero as price increases, and that there is some maximum demand \(k_i\) when the good is free (I don&amp;rsquo;t think it&amp;rsquo;s possible to consume an infinite amount of tea or coffee! ☕).  Picking another perfectly good function, I&amp;rsquo;ll model the substitution effect with a hyperbolic tangent: \(T_i(p_{-i}) = c_i \mathsf{tanh}(p_{-i})\).  This function has a plateau at \(c_i\), as \(p_{-i} \rightarrow \infty\), so the ratio \(c_c / k_\tau\) is measuring the proportion of tea drinkers that would switch to coffee as the price of tea increases.  On the supply side, I&amp;rsquo;ll use a nice S-shaped function \(S_i(p_i) = \frac{m_i p_i}{1 + p_i}\) where the maximum producible amount of the good reaches another plateau at \(m_i\).  This results in the nonlinear system of equations (using \(c\) for coffee and \(\tau\) for tea):&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
\text{coffee:}\quad&amp;amp; \frac{m_c p_c}{1 + p_c} - \frac{k_c}{1 + p_c} - c_c \mathsf{tanh}(p_\tau) = 0,\\
\text{tea:}\quad&amp;amp; \frac{m_\tau p_\tau}{1 + p_\tau} - \frac{k_\tau}{1 + p_\tau} - c_\tau \mathsf{tanh}(p_c) = 0.
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;Solving these equations individually for \(p_c, p_\tau\) we obtain a nonlinear Jacobi algorithm:&lt;/p&gt;
&lt;p&gt;\begin{equation}
\notag
\begin{aligned}
p_c(t + 1) &amp;amp;= \frac{k_c + c_c \mathsf{tanh}(p_\tau(t))}{m_c - c_c \mathsf{tanh}(p_\tau(t))}\\
p_\tau(t + 1) &amp;amp;= \frac{k_\tau + c_\tau \mathsf{tanh}(p_c(t))}{m_\tau - c_\tau \mathsf{tanh}(p_c(t))}.
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;In order for this to constitute a locally diagonally dominant system, we need to check the derivatives.  It is a fairly straightforward calculation which, after cancelling some terms, results in the requirements \[\frac{m_i + k_i}{(1 + p_i^\star)^2} &amp;gt; c_i(1 - \mathsf{tanh}^2 (p_{-i}^\star)).\]  The idea of &amp;ldquo;diagonal dominance&amp;rdquo; is clear in this equation: \(m_i, k_i\) are coefficients controlling the importance of the &amp;ldquo;diagonal&amp;rdquo; part of the function, with \(c_i\) controlling the coupling.  If the coupling is weak: \(m_i + k_i \gg c_i\), then it can be expected that the system is diagonally dominant, and an equilibrium should be close to the &amp;ldquo;nominal&amp;rdquo; equilibrium \(p_i^\star \approx k_i / m_i\).  Incidentally, this should give us a decent starting point for initializing Jacobi iteration.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a generic implementation of nonlinear Jacobi iteration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;36
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;37
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;38
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;39
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; List, Callable
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;jacobi_iteration&lt;/span&gt;(x, f_inv: List[Callable], g: List[Callable]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x_next &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;zeros_like(x)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_i &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;arange(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(x))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; i, (f_inv_i, g_i) &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;enumerate&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;zip&lt;/span&gt;(f_inv, g)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x_next[i] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; f_inv_i(&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;g_i(x[all_i &lt;span style=&#34;color:#ff79c6&#34;&gt;!=&lt;/span&gt; i]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; x_next
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;jacobi_flow&lt;/span&gt;(x, f_inv: List[Callable], g: List[Callable]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; jacobi_iteration(x, f_inv, g) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;solve_nonlinear_system&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    F: Callable,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f_inv: List[Callable],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    g: List[Callable],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x0&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    eps&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1e-6&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    maxiter&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;inf,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    D&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Solves the nonlinear system F_i(x) = f_i(x_i) + g_i(x_{-i}) = 0.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    The algorithm&amp;#39;s starting point is x0.  The algorithm is only guaranteed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    to converge if F is locally diagonally dominant around an equilibrium, and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    x0 is chosen nearby that equilibrium point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;array(x0) &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; x0 &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;zeros(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(g))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _iter &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; partial(jacobi_iteration, f_inv&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;f_inv, g&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;g)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_i &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;arange(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(x))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    it &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;while&lt;/span&gt; it &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; maxiter &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; np&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;linalg&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;norm(F(x)) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; eps:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; D &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; x &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; D) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; _iter(x)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        it &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The code follows a functional programming style that mimics the mathematical description as closely as possible.  A particular implementation of our coffee-tea economy, including useful functions for plotting the progress of the algorithm, might look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; math &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; tanh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; functools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; partial
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;coffee_tea_solver&lt;/span&gt;(k, m, c, D&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;0.0&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&amp;#34;A particular example with coffee and tea...&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;F&lt;/span&gt;(p):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x0 &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (m[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; p[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; k[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; p[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; c[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; tanh(p[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x1 &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (m[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; p[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; k[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; (&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; p[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; c[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; tanh(p[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; (x0, x1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;f_inv&lt;/span&gt;(z, _k, _m):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; (_k &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; z) &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; (_m &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; z)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;g&lt;/span&gt;(p, _c):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;_c &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; tanh(p)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f_invs &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        partial(f_inv, _k&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;k[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;], _m&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;m[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        partial(f_inv, _k&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;k[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;], _m&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;m[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    gs &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [partial(g, _c&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;c[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]), partial(g, _c&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;c[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;])]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _jacobi_iterator &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; partial(jacobi_iteration, f_inv&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;f_invs, g&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;gs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _jacobi_flow &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; partial(jacobi_flow, f_inv&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;f_invs, g&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;gs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _solver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; partial(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        solve_nonlinear_system,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        F&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;F,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        f_inv&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;f_invs,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        g&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;gs,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        eps&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1e-6&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        maxiter&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        D&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;D
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; _jacobi_iterator, _jacobi_flow, _solver, F
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s a plot of the flow of this system for the parmeter settings&lt;/p&gt;
&lt;p&gt;\begin{equation}
\begin{aligned}
&amp;amp;k_c = 3/2, k_\tau = 2\\
&amp;amp;m_c = 5/3, m_\tau = 1/2\\
&amp;amp;c_c = 4/3, c_\tau = 1/3,
\end{aligned}
\end{equation}&lt;/p&gt;
&lt;p&gt;which, at least intuitively, results in a system corresponding to our intuition of diagonal dominance.  Increasing the values of \(c_c, c_\tau\) results in a breakdown of the algorithm and a failure to converge, either as a result of domain errors, or running to infinity.  The SOR technique described earlier can potentially be used to increase the domain of convergence, but that is a matter for experimentation.&lt;/p&gt;


&lt;p&gt;For this particular coffee-tea system, there is surely a lot more analysis that one could do in order to determine clearer convergence criteria, the ranges of parameters that will result in convergence, &lt;em&gt;etc.&lt;/em&gt;  Such analysis is worthwhile in actual practice, but we&amp;rsquo;re here for fun 🙃.&lt;/p&gt;


&lt;h2 class=&#34;group &#34; id=&#34;conclusion&#34;
    &gt;Conclusion&lt;a href=&#34;#conclusion&#34;
        &gt;&lt;i class=&#34;eva eva-link ml-3 align-middle text-theme opacity-0 transition ease-in-out group-hover:opacity-100&#34;&gt;&lt;/i&gt;&lt;/a
&gt;&lt;/h2&gt;

&lt;p&gt;Our main point is in understanding the Jacobi iteration algorithm for solving systems of equations.  The key assumption for this algorithm is that the system be &lt;em&gt;diagonally dominant&lt;/em&gt;.  Intuitively, this means that the equation associated to the \(i^{th}\) variable be only weakly coupled with the remaining variables \(x_{-i}\).  We&amp;rsquo;ve seen how to formalize this in the case of linear equations, and how similar &amp;ldquo;rough guide&amp;rdquo; criteria can be obtained from the Hartman-Grobman Theorem in the nonlinear case.&lt;/p&gt;
&lt;p&gt;In actual practice, Jacobi iteration is not likely to be the best nonlinear equation solver to use, though it depends upon the problem domain.  The fact that it breaks down a system of equations into the &lt;em&gt;parallel&lt;/em&gt; solution of &lt;em&gt;univariate&lt;/em&gt; equations (for which completely general black-box algorithms like &lt;a
    class=&#34;link&#34;
    href=&#34;https://en.wikipedia.org/wiki/Bisection_method&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the Bisection Method&lt;/a
&gt;
 are available) gives it a rather large potential domain of applicability.  If you find yourself confronted with the need to solve a large system of nonlinear equations, available only through slow and black-box evaluation, and where the equations can be expected to be weakly coupled, Jacobi iteration might be for you 🤞.&lt;/p&gt;</description></item><item>
            <title>Introduction</title>
            <link>https://rjtk.github.io/posts/introduction/</link>
            <pubDate>Fri, 09 Dec 2022 00:00:00 -0800</pubDate>
            <guid>https://rjtk.github.io/posts/introduction/</guid><description>&lt;p&gt;The title of this blog, &lt;strong&gt;Quant out of Water&lt;/strong&gt;, is more-or-less the first thing that came to mind.  However, as I (as of 2022) work as a quant at an hedge fund, and I wanted to write a blog that was &lt;strong&gt;&lt;strong&gt;not explicitly about finance&lt;/strong&gt;&lt;/strong&gt;, this title reflects that motivation.  I produced the fish with money using a stable diffusion model.  I hope you enjoy some of my writings.&lt;/p&gt;
&lt;p&gt;You can learn more on the &lt;a
    class=&#34;link&#34;
    href=&#34;https://rjtk.github.io/about/&#34;&gt;&amp;ldquo;about me&amp;rdquo;&lt;/a
&gt;
 page.&lt;/p&gt;
</description></item></channel>
</rss>