<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://thebook.zib.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thebook.zib.de/" rel="alternate" type="text/html" /><updated>2026-02-02T07:03:51+01:00</updated><id>https://thebook.zib.de/feed.xml</id><title type="html">Formal Proofs from THE BOOK</title><entry><title type="html">An upper bound on the Ramsey numbers, Part III - the actual bound</title><link href="https://thebook.zib.de/graph%20theory/2025/02/18/ramsey-bound.html" rel="alternate" type="text/html" title="An upper bound on the Ramsey numbers, Part III - the actual bound" /><published>2025-02-18T06:00:00+01:00</published><updated>2025-02-18T06:00:00+01:00</updated><id>https://thebook.zib.de/graph%20theory/2025/02/18/ramsey-bound</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2025/02/18/ramsey-bound.html"><![CDATA[<p>In the <a href="/graph theory/2025/01/16/ramsey-main.html">previous post</a>, we established the following recursive bound on the Ramsey number:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>theorem R_bounded_recursive (m n : ℕ) (posₘ : 0 &lt; m) (posₙ : 0 &lt; n)
    (rₘ : ∃ N, ramseyProp N (m + 1) n)
    (rₙ : ∃ N, ramseyProp N m (n + 1)) :
    ramseyProp (R(m, n + 1) + R(m + 1, n)) (m + 1) (n + 1) := by
</pre></td>
</tr></table>
</div>

<p>This post aims to formalize the proof of the explicit bound \(R(k,k) \leq 2^{2k-3}\) given in the book:</p>
<div class="statement theorem">


    <strong>Explicit Bound for Ramsey Numbers.</strong> 



(...) with the starting values &#92;(R(m, 2) = m&#92;) and &#92;(R(2, n) = n&#92;), we obtain from the familiar recursion for binomial coefficients
&#92;[R(m,n) &#92;leq &#92;binom{m+n-1}{m-1},&#92;]
and, in particular,
&#92;[R(k,k) &#92;leq &#92;binom{2k-2}{k-1} = &#92;binom{2k-3}{k-1} + &#92;binom{2k-3}{k-2} &#92;leq 2^{2k-3}.&#92;]

<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>

<h1 id="an-induction-principle">An induction principle</h1>
<p>The “familiar recursion for binomial coefficients” is proven by induction. However, it is not standard induction, proving a property of one natural number by providing proofs for zero and successor. This kind of proof would be straightforward to do in Lean, e.g. using the <code>induction</code> tactic. Instead, we encounter a proof about <em>two</em> natural numbers, using <em>two</em> base cases, and not even starting from zero! Luckily, Lean allows us to express abstract induction principles for more involved proofs, as we have discussed in a <a href="/fundamentals/2025/01/08/tech-induction.html">previous blog post on this topic</a>. We will now see this technique in action!</p>

<p>First, let us figure out the shape of our <em>motive</em>, namely the statement we would like to prove by induction. There are two variables (\(m\) and \(n\)) of type <code>ℕ</code>, and both are required to be larger or equal 2. Our goal is to prove \(R(m,n) \leq \binom{m+n-1}{m-1}\), which translated to Lean will be of type <code>Prop</code>. Therefore, the type of our motive is</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>P : ∀ (m n : ℕ) , 2 ≤ m → 2 ≤ n → Prop
</pre></td>
</tr></table>
</div>
<p>and the thing we would like to prove with our inductive proof is</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>∀ m n leₘ leₙ, P m n leₘ leₙ
</pre></td>
</tr></table>
</div>

<p>Next, we examine the assumptions under which the proof will work out. We observe two base cases,</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>(baseₙ : ∀ m leₘ, P m 2 leₘ (AtLeastTwo.prop))
(baseₘ : ∀ n leₙ, P 2 n (AtLeastTwo.prop) leₙ)
</pre></td>
</tr></table>
</div>
<p>as well as a recursive statement giving us a proof about \(R(m,n)\) from \(R(m−1, n)\) and \(R(m, n−1)\), which abstracted to <code>P</code> reads as</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>(succ  : ∀ m n leₘ leₙ, P (m + 1) n (le_succ_of_le leₘ) leₙ →
                        P m (n + 1) leₘ (le_succ_of_le leₙ) →
                        P (m + 1) (n + 1) (le_succ_of_le leₘ) (le_succ_of_le leₙ))
</pre></td>
</tr></table>
</div>
<p>With these, we can write down the signature of our induction principle:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
</pre></td>
  <td class="code"><pre>lemma two_le_orth_induction {P : ∀ m n, 2 ≤ m → 2 ≤ n → Prop}
    (baseₙ : ∀ m leₘ,       P m 2 leₘ (AtLeastTwo.prop))
    (baseₘ : ∀ n leₙ,       P 2 n (AtLeastTwo.prop) leₙ)
    (succ  : ∀ m n leₘ leₙ, P (m + 1) n (le_succ_of_le leₘ) leₙ →
                            P m (n + 1) leₘ (le_succ_of_le leₙ) →
                            P (m + 1) (n + 1) (le_succ_of_le leₘ) (le_succ_of_le leₙ)) :
    ∀ m n leₘ leₙ, P m n leₘ leₙ
</pre></td>
</tr></table>
</div>
<p>If we can prove this lemma, for a general <code>P</code>, we will have a tool that allows us to perform inductive proofs of the shape described here, and we will be able to easily formalize our desired result!</p>

<p>We pattern-match on <code>m</code> and <code>n</code>, actually starting at zero and deriving a contradiction from the requirement that they be larger or equal 2:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>| 0, _, le₀, _     =&gt; (two_ne_zero (eq_zero_of_le_zero le₀)).elim
| _, 0, _, le₀     =&gt; (two_ne_zero (eq_zero_of_le_zero le₀)).elim
</pre></td>
</tr></table>
</div>
<p>For the successor pattern we either apply the base assumptions, if <code>m</code> or <code>n</code> are 2, or apply the recursive proof.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
</pre></td>
  <td class="code"><pre>| m + 1, n + 1, le_sucₘ, le_sucₙ =&gt; by
   cases' le_sucₘ with _ leₘ
   · exact (baseₘ _ _)
   · cases' le_sucₙ with _ leₙ
     · exact (baseₙ _ _)
     · have Pₘ := two_le_orth_induction baseₙ baseₘ succ (m + 1) n (le_succ_of_le leₘ) leₙ
       have Pₙ := two_le_orth_induction baseₙ baseₘ succ m (n + 1) leₘ (le_succ_of_le leₙ)
       exact (succ m n _ _ Pₘ Pₙ)
</pre></td>
</tr></table>
</div>

<h1 id="the-existence-proof">The existence proof</h1>
<p>So far, we have only proven a recursive statement about the Ramsey numbers. To prove our goal, we need a universal statement about existence:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>theorem exists_N_ramseyProp {m n : ℕ} (leₘ : 2 ≤ m) (leₙ : 2 ≤ n) :
    (∃ N, ramseyProp N m n) := by
</pre></td>
</tr></table>
</div>
<p>We can use our new induction principle to go from one to the other:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>induction' m, n, leₘ, leₙ using two_le_orth_induction with m _ n _ m n leₘ leₙ ind_assump_rₘ ind_assump_rₙ
</pre></td>
</tr></table>
</div>
<p>The infoview helpfully lists the three cases we have to prove now.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
<a href="#n16" name="n16">16</a>
<a href="#n17" name="n17">17</a>
<a href="#n18" name="n18">18</a>
<a href="#n19" name="n19">19</a>
</pre></td>
  <td class="code"><pre>3 goals

case baseₙ
m✝ n m : ℕ
leₘ✝ : 2 ≤ m
⊢ ∃ N, ramseyProp N m 2

case baseₘ
m n✝ n : ℕ
leₙ✝ : 2 ≤ n
⊢ ∃ N, ramseyProp N 2 n

case succ
m✝ n✝ m n : ℕ
leₘ : 2 ≤ m
leₙ : 2 ≤ n
rₘ : ∃ N, ramseyProp N (m + 1) n
rₙ : ∃ N, ramseyProp N m (n + 1)
⊢ ∃ N, ramseyProp N (m + 1) (n + 1)
</pre></td>
</tr></table>
</div>

<p>The first two cases follow from lemmata we proved in the <a href="/graph theory/2025/01/16/ramsey-prereqs.html">previous post establishing prerequisites</a>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>· exact ⟨m, ramseyProp_two⟩
· exact ⟨n, ramseyProp_symm _ _ _ ramseyProp_two⟩
</pre></td>
</tr></table>
</div>

<p>For the last case, a simple invocation of the recursive lemma suffices!</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>· have := R_bounded_recursive m n (zero_lt_of_lt leₘ) (zero_lt_of_lt leₙ) ind_assump_rₘ ind_assump_rₙ
  exact ⟨R(m, n + 1) + R(m + 1, n), this⟩
</pre></td>
</tr></table>
</div>

<h1 id="the-binomial-bounds">The binomial bounds</h1>
<p>Our next objective is to confirm the upper bound using the binomial coefficient:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>theorem R_le_choose {m n : ℕ} (m1 : 2 ≤ m) (n1 : 2 ≤ n) :
    R(m, n) ≤ (m + n - 2).choose (m - 1) := by
</pre></td>
</tr></table>
</div>

<p>It turns out we can use the same induction principle as before!</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>induction' m, n, m1, n1 using two_le_orth_induction with m rₘ n _ m n leₘ leₙ ind_assump_rₘ ind_assump_rₙ
</pre></td>
</tr></table>
</div>

<p>Again, the base cases were already established in separate lemmata before.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>· have := Nat.le_of_eq (choose_succ_left m m (zero_lt_of_lt rₘ))
  simp_all [ind_start_R_two]
· simp_all [ind_start_two_R]
</pre></td>
</tr></table>
</div>

<p>For the inductive step, we use the existence proof we just made and combine it with the recursive bound.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>have Rm_ex : ∃ N, ramseyProp N m (n + 1) := exists_N_ramseyProp leₘ (le_add_right_of_le leₙ)
have Rn_ex : ∃ N, ramseyProp N (m + 1) n := exists_N_ramseyProp (le_add_right_of_le leₘ) leₙ
have N_has_ramseyProp : ramseyProp (R(m,n + 1) + R(m + 1,n)) (m + 1) (n + 1) :=
  R_bounded_recursive _ _ (zero_lt_of_lt leₘ) (zero_lt_of_lt leₙ) Rn_ex Rm_ex
</pre></td>
</tr></table>
</div>
<p>This allows us to write down an inequality chain using the induction hypotheses to arrive at an expression of binomial coefficients!</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>calc R(m + 1, n + 1)
  _ ≤ R(m, n + 1) + R(m + 1, n)                                     := Nat.sInf_le N_has_ramseyProp
  _ ≤ R(m, n + 1)  + (m + 1 + n - 2).choose (m + 1 - 1)             := by simp_all [R_symm, ind_assump_rₘ]
  _ ≤ (m + (n + 1) - 2).choose (m - 1) + (m + 1 + n - 2).choose m   := by simp [ind_assump_rₙ]
</pre></td>
</tr></table>
</div>
<p>The remainder of the proof is just applications of results about the binomial coefficient.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  _ = (m + (n + 1) - 2).choose (m - 1) + (m + (n + 1) - 2).choose m := by simp [add_assoc, add_comm n 1]
  _ = (m + (n + 1) - 2 + 1).choose m                                := (choose_succ_left (m+(n+1)-2) m (zero_lt_of_lt leₘ)).symm
  _ = (m + 1 + (n + 1) - 2).choose m                                := by rw [add_comm, add_comm m 1, add_assoc, ← Nat.add_sub_assoc (le_add_right_of_le leₘ)]
</pre></td>
</tr></table>
</div>
<p>This concludes the proof of the binomial upper bound! The bound on \(R(k,k)\) now follows by another small inequality chain, with the most important ingredient from mathlib being <code>choose_succ_le_two_pow</code> <span class="pr-ref">[<a href="#20735">20735</a>]</span>
.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>lemma R_le_two_pow {k : ℕ} (h : 2 ≤ k) : R(k) ≤ 2 ^ (2 * k - 3) := by
  calc R(k)
    _ ≤ (2*k - 2).choose (k - 1)               := by simp [R_le_choose, h, Nat.two_mul]
    _ = ((2 * k - 2 - 1) + 1).choose (k - 1)   := congrFun (congrArg Nat.choose ((Nat.sub_eq_iff_eq_add (le_sub_of_add_le (le_of_succ_le (Nat.mul_le_mul_left 2 h)))).mp rfl)) (k - 1)
    _ ≤ 2 ^ (2 * k - 3)                        := choose_succ_le_two_pow
</pre></td>
</tr></table>
</div>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Röhrig&quot;, &quot;first_name&quot;=&gt;&quot;Olivia&quot;, &quot;homepage&quot;=&gt;&quot;https://iol.zib.de/team/olivia-roehrig.html&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[In the previous post, we established the following recursive bound on the Ramsey number:]]></summary></entry><entry><title type="html">An upper bound on the Ramsey numbers, Part II - the main result</title><link href="https://thebook.zib.de/graph%20theory/2025/02/16/ramsey-main.html" rel="alternate" type="text/html" title="An upper bound on the Ramsey numbers, Part II - the main result" /><published>2025-02-16T06:00:00+01:00</published><updated>2025-02-16T06:00:00+01:00</updated><id>https://thebook.zib.de/graph%20theory/2025/02/16/ramsey-main</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2025/02/16/ramsey-main.html"><![CDATA[<p>In the <a href="/graph theory/2025/01/16/ramsey-prereqs.html">previous post</a>, we introduced definitions and established prerequisites for proving an upper bound on the Ramsey numbers. We can now proceed with the central statement that will lead to our final proof. It reads as follows:</p>

<div class="statement theorem">


    <strong>Recursive Bound for Ramsey Numbers.</strong> 



Suppose &#92;(R(m −1, n)&#92;) and &#92;(R(m, n −1)&#92;) exist. We then prove that &#92;(R(m, n)&#92;) exists and that &#92;[R(m, n) &#92;leq R(m−1, n) + R(m, n−1).&#92;]

<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>
<p>Recall that we defined the Ramsey number as the infimum over the naturals that have the Ramsey property:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>noncomputable def R (m n : ℕ) : ℕ := sInf { N | ramseyProp N m n}
</pre></td>
</tr></table>
</div>

<h1 id="the-statement">The statement</h1>
<p>We note a couple of adjustments we need to make to the statement before we can usefully formalize it in Lean. In our case, “existence of the Ramsey number” means the existence of a natural number that has the Ramsey property. If we show that \(R(m−1, n) + R(m, n−1)\) has the \((m,n)\)-Ramsey property, the stated inequality directly follows from the definition of the infimum.
The inequality also does not actually hold for \(m=0\) or \(n=0\), since \(R(0,m) = 0\) and \(R(1,m) = 1\), but 
\[R(1,1) = 1 &gt; 0 = 0 + 0 = R(0,1) + R(1,0),\]
so we require both parameters to be strictly positive.
Finally, dealing with subtraction of natural numbers in Lean is tiresome, so we shift everything by \(1\).
Given these considerations, we can state the theorem we are going to prove as follows:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>theorem R_bounded_recursive (m n : ℕ) (posₘ : 0 &lt; m) (posₙ : 0 &lt; n)
    (rₘ : ∃ N, ramseyProp N (m + 1) n)
    (rₙ : ∃ N, ramseyProp N m (n + 1)) :
    ramseyProp (R(m, n + 1) + R(m + 1, n)) (m + 1) (n + 1) := by
</pre></td>
</tr></table>
</div>

<h1 id="establishing-definitions">Establishing definitions</h1>
<p>The first few sentences of the proof translate nicely into Lean.
<span class="quote proof">“
Suppose \(N = R(m − 1, n) + R(m, n − 1)\)…”</span>
We shift by 1, and use <code>set</code> instead of the usual <code>let</code> to name the definition.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>set N := R(m, n + 1) + R(m + 1, n) with Neq
</pre></td>
</tr></table>
</div>

<p>Next,
<span class="quote proof">“consider an arbitrary red-blue coloring of \(K_N\).”</span>
The definition of the Ramsey property already quantifies over an arbitrary coloring, so we can use the <code>intro</code> tactic to make it explicit from the goal.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>intro V _ _ cardV C _
</pre></td>
</tr></table>
</div>

<p>Now,
<span class="quote proof">“for a vertex \(v\), let \(A\) be the set of vertices joined to \(v\) by a red edge, and \(B\) the vertices joined by a blue edge.”</span>
We need to ensure that we’re not talking about the empty graph so that we can pick a vertex –  we do this by proving the vertex set of our coloring is nonempty, since its cardinality is <code>N</code> and we have already proven the lemma <code>R_pos</code> the prerequisites, stating that <code>0 &lt; R(m,n)</code> for strictly positive <code>m</code> and <code>n</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>have V_nonempty : Nonempty V := by
  rw [← card_pos_iff, cardV]
  exact add_pos (R_pos m (n+1) posₘ (zero_lt_succ n) rₙ) (R_pos (m+1) n (zero_lt_succ m) posₙ rₘ)
</pre></td>
</tr></table>
</div>
<p>Now we can define <code>v</code> and <code>A</code>. Recall that in our encoding of edge colorings, an edge <code>(v,w)</code> is colored red if <code>m</code> an <code>n</code> are nonadjacent in the encoding graph <code>C</code>. Hence, we define <code>A</code> as the neighbor set of <code>v</code> in the complement graph, which is all vertices with an edge to <code>v</code> in <code>Cᶜ</code> and therefore nonadjacent in <code>C</code> (a fact we will have to establish explicitly later).</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>let v : V := V_nonempty.some
let A := Cᶜ.neighborFinset v
</pre></td>
</tr></table>
</div>

<p>Now, 
<span class="quote proof">“we find that either \(|A| ≥ R(m − 1, n)\) or \(|B| ≥ R(m, n − 1)\). Suppose |A| ≥R(m −1, n), the other case being analogous.”</span>
We again shift by 1 and use the <code>wlog</code> tactic to get two cases:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>wlog R_le_cardA : R(m, n + 1) ≤ #A with h
</pre></td>
</tr></table>
</div>
<p>The goal of the first case is to prove that if our theorem statement follows from <code>|A| ≥ R(m − 1, n)</code> and we assume <code>|A| &lt; R(m − 1, n)</code> (that is, <code>|B| ≥ R(m, n − 1)</code>), the theorem statement also follows.
The goal of the second case is to prove that if our theorem statement follows from <code>|A| ≥ R(m − 1, n)</code>.</p>

<h1 id="the-first-case-reducing-to-symmetry">The first case: reducing to symmetry</h1>
<p>Proving the first case, only shrugged off as “analogous” in the text, is a bit technical. We examine the infoview to find our new state to be the following:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
<a href="#n16" name="n16">16</a>
</pre></td>
  <td class="code"><pre>(...)
h : ∀ (m n : ℕ),
  0 &lt; m →
    0 &lt; n →
      (∃ N, ramseyProp N (m + 1) n) →
        (∃ N, ramseyProp N m (n + 1)) →
          let N := R(m,n + 1) + R(m + 1,n);
          N = R(m,n + 1) + R(m + 1,n) →
            ∀ (V : Type) [inst : Fintype V] [inst_1 : DecidableEq V],
              Fintype.card V = N →
                ∀ (C : SimpleGraph V) [inst_2 : DecidableRel C.Adj] (V_nonempty : Nonempty V),
                  let v := V_nonempty.some;
                  let A := Cᶜ.neighborFinset v;
                  R(m,n + 1) ≤ #A → ∃ s, C.IsNIndepSet (m + 1) s ∨ C.IsNClique (n + 1) s
R_le_cardA : ¬R(m,n + 1) ≤ #A
⊢ ∃ s, C.IsNIndepSet (m + 1) s ∨ C.IsNClique (n + 1) s
</pre></td>
</tr></table>
</div>
<p>That’s quite a mouthful, but in essence we need to prove that given the previous context and <code>R(m,n + 1) ≤ #A</code> (this is the assumption called <code>h</code> here), and a proof that <code>¬R(m,n + 1) ≤ #A</code> (this is the assumption <code>R_le_cardA</code>), we can prove the theorem statement, namely that <code>ramseyProp (R(m, n + 1) + R(m + 1, n)) (m + 1) (n + 1)</code>, the definition of is already unfolded in the above infoview.</p>

<p>According to lemma <code>R_symm</code> from the prerequisites, the Ramsey number is symmetric. Also, we know that an N-independent set in a graph is an <code>N</code>-clique in the complement graph, and vice versa. These two facts will be sufficient to provide all the conditions of the hypothesis <code>h</code>!
We start by defining <code>B</code> and establishing that <code>|B| ≥ R(m, n − 1)</code> indeed follows from the <code>wlog</code> assumption that <code>|A| &lt; R(m − 1, n)</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
</pre></td>
  <td class="code"><pre>let B := C.neighborFinset v
have R_le_cardB : R(n, m + 1) ≤ #B := by
      have : Fintype.card V - 1 = Cᶜ.degree v + C.degree v := by simp [degree_compl, degree_lt_card_verts, le_sub_one_of_lt]
      have := calc (R(m, n + 1) + R(n, m + 1)) - 1
          _ = #A + #B          := by rw [@R_symm n, ← Neq, ← cardV, card_neighborFinset_eq_degree]; exact this
          _ &lt; R(m, n + 1) + #B := by simp [lt_of_not_le R_le_cardA]
  exact le_of_add_le_add_left (le_of_pred_lt this)
</pre></td>
</tr></table>
</div>

<p>Next, we rewrite the required things in our context to match the signature of <code>h</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>have ex m n := Exists.imp (ramseyProp_symm m n)
apply ex at rₘ; apply ex at rₙ
rw [Neq, @R_symm m, @R_symm (m + 1), add_comm] at cardV
have : #B = Cᶜᶜ.degree v := by simp [B]; congr!; exact (compl_compl C).symm
rw [this] at R_le_cardB
</pre></td>
</tr></table>
</div>
<p>We can now use <code>h</code> to obtain a monochromatic vertex subset of the complement graph <code>Cᶜ</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>simp only [forall_const, Nonempty.forall] at h
obtain ⟨s, rs⟩ := h n m posₙ posₘ rₙ rₘ V cardV Cᶜ v R_le_cardB
</pre></td>
</tr></table>
</div>
<p>Rewriting with the mentioned facts about cliques and complements show that the obtained subset is monochromatic with the other color in <code>C</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>simp_rw [isNIndepSet_compl, ← isNIndepSet_compl Cᶜ, compl_compl] at rs
exact ⟨s, rs.symm⟩
</pre></td>
</tr></table>
</div>
<p>This concludes the fist case, and lets us continue with the paper proof.</p>

<h1 id="the-second-case-following-the-text">The second case: following the text</h1>
<p>Before we continue with the proof, let us establish that the edges connecting <code>v</code> to the vertices in <code>A</code> are indeed colored red.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>have A_all_red : ∀ {u}, u ∈ A → ¬ C.Adj v u := by
     intro u a
     simp_all only [mem_neighborFinset, compl_adj, not_false_eq_true, A, v]
</pre></td>
</tr></table>
</div>

<p>The paper proof continues by stating that 
<span class="quote proof">“(…) by the definition of \(R(m − 1, n)\), there either exists in \(A\) a subset \(A_R\) of size \(m − 1\) all of whose edges are colored red which together with \(v\) yields a red \(K_m\), or there is a subset \(A_B\) of size \(n\) with all edges colored blue.”</span> Recall that we proved the following monotonicity property in the prerequisites:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>lemma ramseyProp_mono {m n : ℕ} (N s : ℕ) (h : N ≤ s) (ramseyProp_N : ramseyProp N m n) : ramseyProp s m n := by
</pre></td>
</tr></table>
</div>

<p>By assumption, <code>|A| ≥ R m (n + 1)</code>, so the coloring <code>A</code> induces also has the Ramsey property according to this lemma. Hence, there exists a monochromatic subset of <code>A</code>. We call it <code>Aₘ</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>let ⟨Aₘ, monochrom⟩ := ramseyProp_mono R_le_cardA (sInf_mem rₙ) _ (card_coe A) C[A].coe
</pre></td>
</tr></table>
</div>

<p><code>Aₘ</code> is a subtype of the induced graph’s vertices <code>A</code>. We need to embed it into <code>V</code> to talk about corresponding vertices in the big graph <code>C</code>:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>set AₘV : Finset V := (Finset.map ⟨Subtype.val, Subtype.val_injective⟩ Aₘ) with AVe

have AₘV_subset_A : AₘV ⊆ A := by
  intro _ memAV
  simp_all
  exact memAV.1
</pre></td>
</tr></table>
</div>

<p>We are now ready to examine the two cases proposed in the paper proof.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>cases' monochrom with all_red all_blue
</pre></td>
</tr></table>
</div>

<p><strong>Case one:</strong> <code>AₘV</code> has size <code>m</code> with all edges colored red, which together with <code>v</code> yields a red <code>K_(m+1)</code>. We define the candidate set:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>let Aᵥ := insert v AₘV
</pre></td>
</tr></table>
</div>

<p>It indeed describes an all-red subgraph of <code>C</code>, which we show by establishing pairwise redness of some <code>u, w ∈ Aᵥ</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>have C_red : C.IsIndepSet Aᵥ := by
  intro u u_elem_Aᵥ w w_elem_Aᵥ unw
</pre></td>
</tr></table>
</div>
<p>We need to handle the case that <code>u</code> or <code>w</code> happen to be <code>v</code>. It follows from the definition of <code>A</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
</pre></td>
  <td class="code"><pre>by_cases uvw : (u = v) ∨ (w = v)
  · have elem_A {x : V} (xnv : v ≠ x) (x_elem_Aᵥ : x ∈ Aᵥ) : x ∈ A := by
      cases' mem_insert.mp x_elem_Aᵥ with xeqv x_elem_AV
      · simp_all only [ne_eq, not_true_eq_false]
      · exact AₘV_subset_A x_elem_AV
    cases uvw
    all_goals simp_all [A]
    intro a
    exact (elem_A (Ne.symm unw) u_elem_Aᵥ) (C.adj_symm a)
</pre></td>
</tr></table>
</div>

<p>Now for the interesting case: two members of <code>Aᵥ</code> that are not <code>v</code> have a red edge. We project them to <code>A</code>…</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>push_neg at uvw
obtain ⟨wₐ, ⟨wₐelem_Aₘ, cw⟩⟩ := mem_map.mp (mem_of_mem_insert_of_ne w_elem_Aᵥ uvw.right)
obtain ⟨uₐ, ⟨uₐelem_Aₘ, cu⟩⟩ := mem_map.mp (mem_of_mem_insert_of_ne u_elem_Aᵥ uvw.left)
rw [← cw, ← cu]
</pre></td>
</tr></table>
</div>

<p>…and show that the projections are indeed red in the induced coloring.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>have : uₐ ≠ wₐ := by intro a; simp_all only [ne_eq]
have := all_red.1 uₐelem_Aₘ wₐelem_Aₘ this
simp [Subgraph.coe_adj, Subgraph.induce_adj] at this
assumption
</pre></td>
</tr></table>
</div>

<p>It remains to show the size of <code>Aᵥ</code> is <code>m+1</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>refine ⟨Aᵥ, (Or.inl ⟨C_red, ?_⟩)⟩
have : v ∉ AₘV := fun a =&gt; (not_mem_neighborFinset_self Cᶜ v) (AₘV_subset_A a)
simp_all [isNIndepSet_iff, Aᵥ, AₘV]
</pre></td>
</tr></table>
</div>

<p><strong>Case two:</strong> if <code>Aₘ</code> is all blue and of size <code>n + 1</code>, we’re done. We chose its embedding as the monochromatic set…</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>exists AₘV
</pre></td>
</tr></table>
</div>
<p>…and prove it’s an <code>(n + 1)</code>-clique.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
</pre></td>
  <td class="code"><pre>have : C.IsNClique (n + 1) AₘV :=
  have isClique : C.IsClique AₘV := by
    simp [AVe, coe_map]
    exact C.induce_isClique all_blue.1
  
  have card_eq : #AₘV = n + 1 := by
    rw [card_map]
    exact all_blue.2
  {isClique, card_eq}
exact Or.inr this
</pre></td>
</tr></table>
</div>
<p>This concludes the proof of the central statement. It remains to resolve this recurrence and use some basic properties of the binomial coefficient to show that the Ramsey number is bounded above by \(2^{2k-3}\) in <a href="/graph theory/2025/02/18/ramsey-bound.html">the next post</a>.</p>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Röhrig&quot;, &quot;first_name&quot;=&gt;&quot;Olivia&quot;, &quot;homepage&quot;=&gt;&quot;https://iol.zib.de/team/olivia-roehrig.html&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[In the previous post, we introduced definitions and established prerequisites for proving an upper bound on the Ramsey numbers. We can now proceed with the central statement that will lead to our final proof. It reads as follows:]]></summary></entry><entry><title type="html">An upper bound on the Ramsey numbers, Part I - definitions and prerequisites</title><link href="https://thebook.zib.de/graph%20theory/2025/01/16/ramsey-prereqs.html" rel="alternate" type="text/html" title="An upper bound on the Ramsey numbers, Part I - definitions and prerequisites" /><published>2025-01-16T06:00:00+01:00</published><updated>2025-01-16T06:00:00+01:00</updated><id>https://thebook.zib.de/graph%20theory/2025/01/16/ramsey-prereqs</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2025/01/16/ramsey-prereqs.html"><![CDATA[<p>In the following posts, we adress the proof of an upper bound on the Ramsey numbers. Ultimately, we want to arrive at the result that the \(k\)-th Ramsey number is bounded above by \(2^{2k-3}\). In this first post, we will introduce the required definitions and prove the prerequisites required for the main result.</p>

<h1 id="definition-of-ramsey-property">Definition of Ramsey property</h1>
<p>Before defining the Ramsey numbers, the book introduces the so-called “Ramsey property” of a complete graph.</p>

<div class="statement definition">


    <strong>Ramsey property.</strong> 



Consider the complete graph &#92;(K_N&#92;) on &#92;(N&#92;) vertices. We say that &#92;(K_N&#92;) has property &#92;((m, n)&#92;)
if, no matter how we color the edges of &#92;(K_N&#92;) red and blue, there is always a
complete subgraph on &#92;(m&#92;) vertices with all edges colored red or a complete
subgraph on n vertices with all edges colored blue.


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>
<p>We will already diverge from the literal definition in our implementation, because that will make our lives much easier: Since the theorem is only about two-colorings of the complete graph \(K_N\), we don’t need to actually use a data structure assigning colors to graph edges explicitly. Instead, we can represent the set of colorings in questions as the set of graphs \(C\) on \(N\) vertices, and consider an edge \((v, w)\) to be colored blue in \(K_N\) if the vertices \(v\) and \(w\) are adjacent in \(C\), and red if they are nonadjacent. This allows us to use the machinery for cliques and independent sets when talking about the Ramsey property, since our definition translates to the following:</p>

<div class="statement definition">


    <strong>Ramsey property, adjacency version.</strong> 



We say that &#92;(K_N&#92;) has property &#92;((m, n)&#92;) if, no matter which graph &#92;(C&#92;) on &#92;(N&#92;) vertices we choose, there is always an independent set of &#92;(m&#92;) vertices or a clique of n vertices.



</div>

<p>This does not translate to Lean literally, since “a graph on \(N\) vertices” is not a builtin type. Instead, we need to quantify over the vertex types that are finite with <code>N</code> elements. This sorted, we can write down our definition!</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>def ramseyProp (N m n : ℕ) :=
    ∀ (V : Type) [Fintype V] [DecidableEq V] (_ : Fintype.card V = N)
      (C : SimpleGraph V) [DecidableRel C.Adj],
    ∃ (s : Finset V), (C.IsNIndepSet m s) ∨ (C.IsNClique n s)
</pre></td>
</tr></table>
</div>

<p>Before we continue, let us introduce notation for subgraphs induces by vertex subsets:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>notation:max G &quot;[&quot; A &quot;]&quot; =&gt; SimpleGraph.Subgraph.induce (⊤ : Subgraph G) (Finset.toSet A)
</pre></td>
</tr></table>
</div>

<h1 id="definition-of-ramsey-number">Definition of Ramsey number</h1>
<p>A statement about monotonicity of this property follows:</p>

<div class="statement lemma">


    <strong>Lemma.</strong> 



It is clear that if &#92;(K_N&#92;) has property &#92;((m, n)&#92;), then so does every &#92;(K_s&#92;) with &#92;(s ≥ N&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>lemma ramseyProp_mono {m n : ℕ} (N s : ℕ) (h : N ≤ s) (ramseyProp_N : ramseyProp N m n) :
    ramseyProp s m n := by
</pre></td>
</tr></table>
</div>

<p>The proof is omitted in the book, but we spell it out. First, we pick any <code>N</code>-element subset of the vertex set <code>W</code> of our <code>s</code>-graph, and consider the subgraph induced by it.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>  intros W _ _ Wcard C _
  rw [← Wcard, ← Fintype.card_fin N] at h
  obtain ⟨A, A_subset, A_card⟩ := exists_subset_card_eq h
  let C' := C[A]
</pre></td>
</tr></table>
</div>
<p>Since \(K_N\) has the Ramsey property, we can find a monochromatic vertex subset <code>A'</code> in the induced subgraph.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>    obtain ⟨A', red_or_blue⟩ := ramseyProp_N A (by simp [A_card]) C'.coe
</pre></td>
</tr></table>
</div>
<p>We embed <code>A'</code> back into <code>W</code>.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  use map ⟨Subtype.val, Subtype.val_injective⟩ A'
</pre></td>
</tr></table>
</div>
<p>Since cliques and independent sets in the induced subgraph are also cliques and independent sets in the supergraph, the Ramsey property of the supergraph directly follows with the embedding of <code>A'</code> being monochromatic.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  exact Or.imp (induce_isNIndepSet C).mp (induce_isNClique C) red_or_blue
</pre></td>
</tr></table>
</div>

<p>With this settled, we move on to actually defining the Ramsey number:</p>

<div class="statement definition">


    <strong>Ramsey number.</strong> 



(...) we ask for the smallest number &#92;(N&#92;) (if it exists) with this property — and this is the Ramsey number &#92;(R(m, n)&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>

<p>We use the <a href="https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Lattice.html#Nat.instInfSet">sInf instance of Nat</a> to talk about the smallest number of a set. It is noteworthy that this definition is zero if the set is empty, so the Lean definition does not quite match the paper one – existence of a graph with this property is not a prerequisite.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>noncomputable def R (m n : ℕ) : ℕ := sInf { N | ramseyProp N m n}

notation:max &quot;R(&quot; m &quot;,&quot; n &quot;)&quot; =&gt; R m n
notation:max &quot;R(&quot; n &quot;)&quot; =&gt; R n n
</pre></td>
</tr></table>
</div>

<p>Note that we need to mark this definition as <code>noncomputable</code>, since finding the minimum of a set requires the set to be non-empty, and we have not yet established the existence of the Ramsey number for all <code>m</code> and <code>n</code> (in fact, this will be the largest effort of this entire proof). We could condition the definition on the existence of a Ramsey number and explicitly pass a proof. For the sake of brevity and since computability is not of the essence here, we will make this concession and profit from the nice results the mathlib already containsabout <code>sInf</code>.</p>

<h1 id="lemma-for-induction-base-case-rm-2--m">Lemma for induction base case \(R(m, 2) = m\)</h1>

<p>Let us establish the inductive principle that will be used to prove the main result, see also the <a href="/fundamentals/2025/01/08/tech-induction.html">previous post on induction in Lean</a>.</p>

<div class="statement lemma">


    <strong>Lemma.</strong> 



As a start, we certainly have &#92;(R(m, 2) = m&#92;) because either all of the edges
of &#92;(K_m&#92;) are red or there is a blue edge, resulting in a blue &#92;(K_2&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>

<p>To this end, we will have to first prove that \(K_m\) actually has the \((m,2)\)-property, and then go on to prove that \(K_m\) is actually the smallest graph with this property.
The first proof follows the book closely:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>lemma ramseyProp_two {m : ℕ} : ramseyProp m m 2 := by
    intro _ _ _ cardV C _
    by_cases all_red : C.IsNIndepSet m univ
</pre></td>
</tr></table>
</div>

<p>In the first case, all edges are red, so we’re done.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>    · exact ⟨univ, (Or.inr all_red).symm⟩
</pre></td>
</tr></table>
</div>

<p>In the second case, there is a blue edge, resulting in a blue \(K_2\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>    · simp [isNIndepSet_iff, isIndepSet_iff, Set.Pairwise, card_univ, cardV] at all_red
      obtain ⟨v, ⟨w, ⟨_, vwblue⟩⟩⟩ := all_red
      exact ⟨{v, w}, by simp_all [isNClique_iff]⟩
</pre></td>
</tr></table>
</div>

<p>We can now prove the actual statement:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>lemma ind_start_R_two {m : ℕ} : R(m, 2) = m := by
</pre></td>
</tr></table>
</div>
<p>We bound the infimum by \(m\) from above and below. The bound from below follows by considering an all-red \(K_N\), in our representation the graph on \(N\) vertices with no edges ⊥, which has an \(N\)-independent set.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>  have m_le_N (N : ℕ) (ram : ramseyProp N m 2) : m ≤ N := by
    obtain ⟨s, h⟩ := ram (Fin N) (Fintype.card_fin N) ⊥
    simp [isNClique_bot_iff] at h
    rw [← h.2]
    exact (card_finset_fin_le s)
</pre></td>
</tr></table>
</div>

<p>The bound from above directly follows from the previous lemma, and we complete the proof.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>  have r2 : ramseyProp m m 2 := ramseyProp_two
  exact le_antisymm (Nat.sInf_le r2) (le_csInf ⟨m, r2⟩ m_le_N)
</pre></td>
</tr></table>
</div>

<h1 id="lemma-for-symmetry-and-induction-base-case">Lemma for symmetry and induction base case</h1>

<p>The other base case follows with symmetry, so we first need to establish that.</p>
<div class="statement lemma">


    <strong>Lemma.</strong> 



By symmetry, we have &#92;(R(2, m) = m&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 296.
    
    </div>

</div>
<p>We start off showing symmetry for the Ramsey property.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>lemma ramseyProp_symm (m n N : ℕ) (h : ramseyProp N m n) :
    (ramseyProp N n m) := by
</pre></td>
</tr></table>
</div>
<p>To this end, the hypothesis allows us to find a monochromatic subset in the complement graph.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>  intro W _ _ Wcard C _
  obtain ⟨s, red_or_blue⟩ := h W Wcard Cᶜ
</pre></td>
</tr></table>
</div>
<p>The result follows directly from the fact that a clique in a graph is an independent set in its complement and vice versa.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  cases' red_or_blue with c₁ c₂
  · exact ⟨s, Or.inr ((isNIndepSet_compl C).mp c₁)⟩
  · exact ⟨s, (Or.inr ((isNClique_compl C).mp c₂)).symm⟩
</pre></td>
</tr></table>
</div>
<p>This easily transfers to the Ramsey number.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>lemma R_symm {m n : ℕ} : R(m,n) = R(n,m) := by
  have {N : ℕ} := Iff.intro (ramseyProp_symm m n N) (ramseyProp_symm n m N)
  simp [R, this]
</pre></td>
</tr></table>
</div>
<p>With this, we obtain our second base case:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>lemma ind_start_two_R {m : ℕ} : R(2, m) = m := by
  simp[R_symm]; exact ind_start_R_two
</pre></td>
</tr></table>
</div>

<h1 id="strict-positivity">Strict positivity</h1>
<p>The proof for the recursive bound that we are going to attempt silently uses the fact that if both summands exist, \(N = R(m - 1, n) + R(m, n - 1)\) is strictly positive, i.e., non-zero, and so we can pick a vertex from \(K_N\). We show that the Ramsey number, if it exists, is positive if both \(m\) and \(n\) are strictly positive.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>lemma R_pos (m n : ℕ) (_ : 0 &lt; m) (_ : 0 &lt; n) (h : ∃ N, ramseyProp N m n) :
    0 &lt; R(m, n) := by
</pre></td>
</tr></table>
</div>
<p>To this end, we assume \(0 = R(m, n)\):</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  by_contra R0; apply Nat.eq_zero_of_not_pos at R0
</pre></td>
</tr></table>
</div>
<p>Then zero has the ramsey property.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  have : ramseyProp 0 m n := R0 ▸ sInf_mem h
</pre></td>
</tr></table>
</div>
<p>We can hence find \(s\), an \(m\)-independent set or an \(n\)-clique, in the empty graph.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  obtain ⟨s, p⟩ := this (Fin 0) rfl (⊥ : SimpleGraph (Fin 0))
</pre></td>
</tr></table>
</div>
<p>That leads to contradiction, since \(s\) must be empty but \(0 &lt; m,n\).</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  simp_rw [isNIndepSet_iff, isNClique_iff,
    eq_zero_of_le_zero (card_finset_fin_le s)] at p
  cases p &lt;;&gt; simp_all
</pre></td>
</tr></table>
</div>

<p>With these prerequisites in place, we are ready to tackle the recursive bound on Ramsey numbers in the <a href="/graph%20theory/2025/02/16/ramsey-main.html">next post</a>.</p>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Röhrig&quot;, &quot;first_name&quot;=&gt;&quot;Olivia&quot;, &quot;homepage&quot;=&gt;&quot;https://iol.zib.de/team/olivia-roehrig.html&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[In the following posts, we adress the proof of an upper bound on the Ramsey numbers. Ultimately, we want to arrive at the result that the (k)-th Ramsey number is bounded above by (2^{2k-3}). In this first post, we will introduce the required definitions and prove the prerequisites required for the main result.]]></summary></entry><entry><title type="html">Inductive types and proof by induction</title><link href="https://thebook.zib.de/fundamentals/2025/01/08/tech-induction.html" rel="alternate" type="text/html" title="Inductive types and proof by induction" /><published>2025-01-08T07:00:00+01:00</published><updated>2025-01-08T07:00:00+01:00</updated><id>https://thebook.zib.de/fundamentals/2025/01/08/tech-induction</id><content type="html" xml:base="https://thebook.zib.de/fundamentals/2025/01/08/tech-induction.html"><![CDATA[<p>Proofs by induction are ubiquitous throughout mathematics, providing an axiomatic tool for proving universal statements about recursively defined mathematical objects through two steps:
proving base cases and showing preservation under construction from substructures.
Of course, Lean provides ample machinery to write inductive proofs.
Let’s take a look!</p>

<h1 id="a-basic-example-peanos-axiom-of-induction">A basic example: Peano’s axiom of induction</h1>

<p>Let us consider a basic inductive proof of the form most probably encounter first in their mathematics education:
In order to prove that a statement (we call it <em>motive</em> in Lean proofs) <code>motive : (n : Nat) → Prop</code> about a natural number holds for all natural numbers <code>n</code>, it suffices to prove that it holds for <code>n = 0</code>, and that given a proof that it holds for some <code>n : Nat</code> we can derive that it also holds for <code>n + 1</code>, according to <a href="https://en.wikipedia.org/wiki/Mathematical_induction#Axiom_of_induction">Peano’s axiom of induction</a>.
The <code>induction</code> tactic makes it straightforward to write such a proof in Lean, as done in this <a href="https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Choose/Basic.html#Nat.choose_two_right">example taken from mathlib</a> about the binomial coefficient:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>example (n : Nat) : choose n 2 = n * (n - 1) / 2 := by
  induction' n with n ih
  · simp
  · rw [Nat.triangle_succ n]
    rw [Nat.choose, ih]
    simp [Nat.add_comm]
</pre></td>
</tr></table>
</div>

<p>We actually used the <code>induction'</code> variant of the tactic that allowed us to assign the names <code>n</code> and <code>ih</code> to the induction variable and hypothesis.
If we look at the goals the tactic produced here, we see that we are required to provide proofs for the base case <code>0</code> and the induction step:</p>

<div class="language-plaintext highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
</pre></td>
  <td class="code"><pre>2 goals

case zero
⊢ Nat.choose 0 2 = 0 * (0 - 1) / 2

case succ
n : Nat
ih : n.choose 2 = n * (n - 1) / 2
⊢ (n + 1).choose 2 = (n + 1) * (n + 1 - 1) / 2
</pre></td>
</tr></table>
</div>

<h1 id="inductive-types-and-recursors">Inductive Types and Recursors</h1>

<p>That was smooth enough!
However, many proofs by induction we encounter don’t follow the exact structure given by Peano’s axiom, or are not even about natural numbers.
To formalise such proofs in Lean, we take a more abstract, structural view of induction, as a general method for proving statements of the form \(∀ a \in A, P(a)\), where \(A\) is some domain and \(P(a)\) is some proposition about elements of \(A\).</p>

<p>What do we require of \(A\) so we can make inductive proofs about its members?
Each element \(a \in A\) needs to be representable as either an instance of some base cases, or constructed from other elements of \(A\) that are “smaller” than \(a\), meaning that they are closer than \(a\) to some base case according to a (partial) ordering on \(A\).
In type theory, the domains of universal quantifications are types, and we say a type \(A\) is an <a href="https://leanprover.github.io/theorem_proving_in_lean4/inductive_types.html"><em>inductive type</em></a> if each of its members is generated from finite compositions of a finite number of <em>constructors</em>.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>In Lean, they are defined by using the keyword <code>inductive</code>.
Inductive types are so expressive that apart from type universes and dependent function types, all Lean types are inductive types, even though that fact is hiding underneath a thick layer of syntactic sugar.
We’ve already encountered a prominent example of an inductive type with <code>Nat</code>, which is defined through one constant constructor <code>zero</code> and one recursive constructor <code>succ</code>:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>inductive Nat : Type
| zero : Nat
| succ : Nat → Nat
</pre></td>
</tr></table>
</div>

<p>We see that by definition, all natural numbers in Lean are either <code>0</code> or <code>n + 1</code> for some natural number <code>n</code>, which means it suffices to prove a statement for those two cases to prove it for all of <code>Nat</code>.
When we define any <code>inductive</code> type in Lean, this principle (called the <em>recursor</em> or <em>eliminator</em> of the type) is automatically derived from the definition and accessible in the <code>.rec</code> field.
Looking at what it came up with for our definition of <code>Nat</code> through <code>#print Nat.rec</code>, we get something that looks suspiciously like Peano’s axiom:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>&gt;&gt; recursor Nat.rec.{u} : {motive : Nat → Sort u} →
                          motive Nat.zero →
                          ((n : Nat) → motive n → motive n.succ) →
                          (t : Nat) → motive t
</pre></td>
</tr></table>
</div>

<h1 id="different-induction-principles">Different Induction Principles</h1>

<p>The <code>induction</code> tactic is just a shortcut that allows us to choose the current goal as motive, creating goals for the constructor arguments, and handling variable naming.
By default, it uses the recursor of the induction variable’s type as an <em>induction principle</em> specifying the shape of the generated proof though we can influence that choice through the <code>using</code> keyword.
We can easily verify that the previous example remains valid when we explicitly demand <code>Nat.rec</code> to be used through <code>induction' n using Nat.rec with n ih</code>.</p>

<p>Mathlib provides a <a href="https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Defs.html#Recursion-and-induction-principles">collection of alternative induction principles on <code>Nat</code></a> for frequently occuring proof techniques:</p>

<table>
  <thead>
    <tr>
      <th>Induction Principle</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>twoStepInduction</code></td>
      <td>Uses hypotheses for <code>n</code> and <code>n - 1</code> to prove for <code>n + 1</code></td>
    </tr>
    <tr>
      <td><code>strong_induction_on</code></td>
      <td>Proves for <code>n + 1</code> using all <code>m &lt; n + 1</code></td>
    </tr>
    <tr>
      <td><code>le_induction</code></td>
      <td>Proves for numbers above a bound</td>
    </tr>
    <tr>
      <td><code>decreasingInduction</code></td>
      <td>Proves for numbers below a bound</td>
    </tr>
    <tr>
      <td><code>diag_induction</code></td>
      <td>Proves using diagonal arguments with two parameters</td>
    </tr>
  </tbody>
</table>

<p>We can in fact easily reformulate the proof of our previous example using <code>strong_induction_on</code>. Note that it requires us to manually separate out the base case:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
</pre></td>
  <td class="code"><pre>example (n : Nat) : choose n 2 = n * (n - 1) / 2 := by
  induction' n using Nat.strong_induction_on with k ik
  by_cases h : k = 0
  · simp [h]  
  · obtain ⟨n, rfl⟩ := Nat.exists_eq_succ_of_ne_zero h
    rw [Nat.triangle_succ n]
    rw [Nat.choose, ik n (Nat.lt_succ_self n)]
    simp [Nat.add_comm]
</pre></td>
</tr></table>
</div>

<p>Let us also consider a slightly more complex example using <code>le_induction</code>. Looking at the signature of this induction principle through <code>#print le_induction</code>, we see that the form of the motive we can prove with this principle is <code>P : (n : Nat) → m ≤ n → Prop</code>, and that we need to prove the statement for the lower bound <code>m</code> (<code>P m ⋯ </code> in the signature), as well as for the induction step <code>P n hmn → P (n + 1) ⋯)</code> for all <code>n</code> within the bound:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>&gt;&gt; theorem Nat.le_induction : ∀ {m : Nat} {P : (n : Nat) → m ≤ n → Prop},
  P m ⋯ → (∀ (n : Nat) (hmn : m ≤ n), P n hmn → P (n + 1) ⋯) → ∀ (n : Nat) (hmn : m ≤ n), P n hmn
...
</pre></td>
</tr></table>
</div>

<p>The following easy <a href="https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Nat/Choose/Sum.html#Nat.sum_Icc_choose">example from mathlib</a>, establishing that</p>

<p>\[\sum_{m = k}^{n} {m \choose k} = {n + 1 \choose k + 1},\]</p>

<p>demonstrates how to use <code>le_induction</code>, with the <code>induction'</code> tactic now invoked using two induction parameters as required by the motive <code>P</code> in the <code>le_induction</code> principle.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>example (n k : Nat) (h : n ≥ k) : ∑ m ∈ Icc k n, m.choose k = (n + 1).choose (k + 1) := by
  induction' n, h using le_induction with n _ ih
  · simp
  · rw [← Ico_insert_right (by omega), sum_insert (by simp),
      show Ico k (n + 1) = Icc k n by rfl, ih, choose_succ_succ' (n + 1)]
</pre></td>
</tr></table>
</div>

<h1 id="writing-your-own-principles">Writing Your Own Principles</h1>

<p>Now what happens if we want to prove a universal statement about a motive that’s more intricate than the simple unary predicate that we get from the recursor?
We likely could, in theory, reduce it to nested induction using only type recursors on some obtuse inductive type, but there is a much nicer way.</p>

<p>As we have seen, the recursor of an inductive type \(A\) is a function from a proposition over a variable (the motive, itself a function mapping from \(A\) to <code>Prop</code>), and one argument for each constructor, to a proof of the motive under universal quantification over \(A\). The <code>induction</code> tactic can in fact use any function in place of the recursor, as long as it</p>

<ul>
  <li>binds a variable for <code>motive : (t₀ : T₀) → ... → (tₙ : Tₙ) → Sort*</code>,</li>
  <li>binds variables <code>(t₀ : T₀)</code>,  …,  <code>(tₙ : Tₙ)</code>, and</li>
  <li>has <code>motive t₀ ... tₙ</code> as the result type.</li>
</ul>

<p>The function can also bind other variables and the <code>induction</code> tactic will create a separate goal for each of them.
It expects as input one bound variable for each <code>t_i</code>, and will result in a proof universally quantified over all those variables.</p>

<p>While this is such a general tactic I hesitate to call the resulting proofs “by induction” for all input principles, we can use it to generate proofs using nonstandard induction (not even necessarily over an inductively defined type!) by specifying an induction principle where the cases correspond to proofs about base cases and inductive steps.</p>

<p>We can look at an <a href="https://github.com/leanprover-community/mathlib4/blob/9e3f841ce2b5742da14a9cce51b947c77ca55d8e/Mathlib/Data/Nat/Defs.lean#L828-L836">example from mathlib</a> again: the two-step induction principle on the naturals, deriving the next case from the two previous ones.</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>def twoStepInduction {P : ℕ → Sort*} (zero : P 0) (one : P 1)
    (more : ∀ n, P n → P (n + 1) → P (n + 2)) : ∀ a, P a
  | 0 =&gt; zero
  | 1 =&gt; one
  | _ + 2 =&gt; more _ (twoStepInduction zero one more _) (twoStepInduction zero one more _)
</pre></td>
</tr></table>
</div>
<p>We observe the <code>motive</code>, here called <code>P</code>, the bound variable <code>a</code> and the result type <code>P a</code>. The remaining bound variables, <code>zero</code>, <code>one</code>, and <code>more</code>, will each get a seperate case when we use this with the <code>induction</code> tactic, requiring us to prove <code>P 0</code>, <code>P 1</code> and <code>∀ n, P n → P (n + 1) → P (n + 2)</code> respectively. All that was needed to write this induction principle was proving that given those three things, we can infer <code>∀ a, P a</code>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Constructors are functions whose codomain is \(A\). They need to adhere to <a href="https://leanprover.github.io/theorem_proving_in_lean4/inductive_types.html#axiomatic-details">certain restrictions</a> to <a href="https://vilhelms.github.io/posts/why-must-inductive-types-be-strictly-positive/">maintain consistency</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Röhrig&quot;, &quot;first_name&quot;=&gt;&quot;Olivia&quot;, &quot;homepage&quot;=&gt;&quot;https://iol.zib.de/team/olivia-roehrig.html&quot;}</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[Proofs by induction are ubiquitous throughout mathematics, providing an axiomatic tool for proving universal statements about recursively defined mathematical objects through two steps: proving base cases and showing preservation under construction from substructures. Of course, Lean provides ample machinery to write inductive proofs. Let’s take a look!]]></summary></entry><entry><title type="html">Mantel’s Theorem via the Inequality of the Arithmetic and Geometric Mean</title><link href="https://thebook.zib.de/graph%20theory/2024/11/08/mantel-amgm.html" rel="alternate" type="text/html" title="Mantel’s Theorem via the Inequality of the Arithmetic and Geometric Mean" /><published>2024-11-08T06:00:00+01:00</published><updated>2024-11-08T06:00:00+01:00</updated><id>https://thebook.zib.de/graph%20theory/2024/11/08/mantel-amgm</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2024/11/08/mantel-amgm.html"><![CDATA[<p>In this post, we provide another proof for Mantel’s theorem. Let’s recall the statement from the <a href="/graph%20theory/2024/10/15/mantel-cauchy-schwarz.html">previous post</a>:</p>
<div class="statement theorem">


    <strong>Mantel's Theorem.</strong> 


Suppose &#92;(G&#92;) is a graph on &#92;(n&#92;) vertices without triangles. Then &#92;(G&#92;) has at most &#92;(&#92;lfloor n^2/4 &#92;rfloor&#92;) edges, and equality holds if and only if &#92;(G&#92;) is the complete bipartite graph &#92;(K_{&#92;lfloor n/2 &#92;rfloor, &#92;lceil n/2 &#92;rceil}&#92;).


</div>

<p>Again, we focus on proving the upper bound for now. The following proof is supposed to be folklore, and uses the inequality of the arithmetic and geometric mean.</p>

<div class="statement proof">


    <strong>Proof.</strong> 



Let &#92;(&#92;alpha&#92;) be the size of a largest independent set &#92;(A&#92;), and set &#92;(&#92;beta = n - &#92;alpha&#92;). Since &#92;(G&#92;) is triangle-free, the neighbors of a vertex &#92;(i&#92;) form an independent set, and we infer &#92;(d_i &#92;leq &#92;alpha&#92;) for all &#92;(i&#92;).


The set &#92;(B = V &#92;setminus A&#92;) of size &#92;(&#92;beta&#92;) meets every edge of &#92;(G&#92;). Counting the edges of &#92;(G&#92;) according to their endvertices in &#92;(B&#92;), we obtain &#92;(|E| &#92;leq &#92;sum_{i &#92;in B} d_i&#92;). The inequality of the arithmetic and geometric mean now yields &#92;[|E| &#92;leq &#92;sum_{i &#92;in B} d_i &#92;leq &#92;alpha&#92;beta &#92;leq &#92;left(&#92;frac{&#92;alpha+&#92;beta}{2}&#92;right)^2 = &#92;frac{n^2}{4}.&#92;]


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 146.
    
    </div>

</div>

<p>The theorem statement is the same as the one we’ve seen in the previous post:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>theorem mantel (h: G.CliqueFree 3) : #E ≤ (n^2 / 4) := by
</pre></td>
</tr></table>
</div>

<p>The proof begins with the words <span class="quote proof">“Let \(\alpha\) be the size of a largest independent set \(A\), and set \(\beta = n - \alpha\)”</span>. \(\alpha\) is also refered to as the <a href="https://en.wikipedia.org/wiki/Independent_set_(graph_theory)">coclique number</a> and obtain the existence of a maximum independent set \(A\) attaining that number from <code>maximumIndependentSet_exists</code> <span class="pr-ref">[<a href="#18446">18446</a>]</span>
 <span class="pr-ref">[<a href="#18694">18694</a>]</span>
:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>let α := SimpleGraph.cocliqueNum G
let ⟨A, maxA⟩  := G.maximumIndependentSet_exists
have hA : #A = α := maximumIndependentSet_card_eq_cocliqueNum _ _ maxA
</pre></td>
</tr></table>
</div>

<p>We then continue <span class="quote proof">“and set \(\beta = n - \alpha\) ”</span>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>let β := n - α
have hαβ: α + β = n := Nat.add_sub_of_le (le_of_eq_of_le (hA.symm) (card_le_card (subset_univ _)))
</pre></td>
</tr></table>
</div>

<p>Note that we are again using subtraction in <code>Nat</code>, so the proof that <code>α + β = n</code> requires us to show that in fact <code>α ≤ n</code>, which follows by <code>card_le_card</code> and <code>subset_univ</code>. The proof thenmakes some brief statements that are used to justify the final inequality chain, beginning with <span class="quote proof">“Since \(G\) is triangle-free, the neighbors of a vertex \(i\) form an independent set, and we infer \(d_i \leq \alpha\) for all \(i\).”</span> The first half of that is the exact statement of theorem <code>isIndependentSet_neighborSet_if_triangleFree</code> <span class="pr-ref">[<a href="#18694">18694</a>]</span>
, but we’ll have to adjust it to finite sets to use it in our proof.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>have nbhd_ind_of_triangle_free : ∀ (i : γ), G.IsIndependentSet N(i) := by
  simp [Set.coe_toFinset, G.isIndependentSet_neighborSet_if_triangleFree h, neighborFinset]
</pre></td>
</tr></table>
</div>

<p>The second half of the sentence, <span class="quote proof">“we infer \(d_i \leq \alpha\) for all \(i\)”</span>, requires us to use two more theorems about independent sets. The first is <code>card_le_cocliqueNum</code> <span class="pr-ref">[<a href="#18446">18446</a>]</span>
 <span class="pr-ref">[<a href="#18447">18447</a>]</span>
, stating that the cardinality of any independent set is less than or equal to the coclique number. The second is <code>maximumIndependentSet_card_eq_cocliqueNum</code> <span class="pr-ref">[<a href="#18446">18446</a>]</span>
 <span class="pr-ref">[<a href="#18447">18447</a>]</span>
, stating that the cardinality of a maximum independent set is precisely said coclique number. Combining them with our previous lemma using transitivity of inequality and equality, we obtain:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>have degree_le_alpha : ∀ (i : γ) , d(i) ≤ α := fun i =&gt; 
  hA ▸ (le_of_le_of_eq (nbhd_ind_of_triangle_free i).card_le_cocliqueNum
                (G.maximumIndependentSet_card_eq_cocliqueNum A maxA).symm)
</pre></td>
</tr></table>
</div>
<p>The next step is to prove the inequality \(|E| \leq \sum_{i \in B} d_i\). The proof states <span class="quote proof">“The set \(B = V \setminus A\) of size \(\beta\) meets every edge of \(G\)”</span>, which is the statement of the theorem <code>compl_independentSet_meets_every_edge</code> <span class="pr-ref">[<a href="#18608">18608</a>]</span>
. Since we’re about to prove the next step in lean using a chain of inequalities with the <code>calc</code> tactic, we will turn the statement about “meeting every edge” into an inequality:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>let B := V &amp;#92; A
have hB : #B = β := by have := card_sdiff (subset_univ A); simp_all [hαβ]

have one_ge_num_incident_verts : ∀ e ∈ E, 1 ≤ #{ i ∈ B | i ∈ e } := by
  simp only [one_le_card, mem_edgeFinset]
  exact G.compl_independentSet_meets_every_edge maxA.independentSet
</pre></td>
</tr></table>
</div>

<p>Equipped with this, we can prove our lemma using <code>calc</code>:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
</pre></td>
  <td class="code"><pre>have count_edges_by_B := by calc
  #E = ∑ e ∈ E, 1                          := by simp
    _ ≤ ∑ e ∈ E, #{ i ∈ B | i ∈ e }         := sum_le_sum one_ge_num_incident_verts
    _ = ∑ e ∈ E, ∑ i ∈ {i ∈ B | i ∈ e}, 1   := by simp
    _ = ∑ i ∈ B, ∑ e ∈ {e ∈ E | i ∈ e}, 1   := sum_sum_bipartiteAbove_eq_sum_sum_bipartiteBelow _ _
    _ = ∑ i ∈ B, #{e ∈ E | i ∈ e}           := by simp
    _ = ∑ i ∈ B, d(i)                       := sum_congr rfl fun i _ =&gt; by
                                                rw [(G.card_incidenceFinset_eq_degree i).symm,
                                                      G.incidenceFinset_eq_filter]
</pre></td>
</tr></table>
</div>

<p>Note that, like in the previous post, we use the weighted double counting lemma <code>Finset.sum_sum_bipartiteAbove_eq_sum_sum_bipartiteBelow</code><span class="pr-ref">[<a href="#17651">17651</a>]</span>
. We are now equipped to prove the final chain of inequalities: \[|E| \leq \sum_{i \in B} d_i \leq \alpha \beta \leq \left( \frac{ \alpha + \beta}{2} \right)^2 = \frac{n^2}{4}. \]</p>

<p>Since rational numbers are more involved to deal with, we start with proving the first two steps, using the lemmata about meeting all edges and bounded degree:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>have card_E_bound := calc
  #E ≤ ∑ i ∈ B, d(i)      := by exact count_edges_by_B
    _ ≤ ∑ _ ∈ B, α         := sum_le_sum fun _ _ =&gt; degree_le_alpha _
    _ = α * β              := by simp [Nat.mul_comm, hB]
</pre></td>
</tr></table>
</div>

<p>The remaining two steps are first proven multiplied by four, so we can stick to natural numbers and deal with the division in the end. This is where we use the inequality of the arithmetic and geometric mean that gives this post its name, by invoking <code>four_mul_le_pow_two_add</code> from <code>Mathlib.Analysis.MeanInequalities</code> <span class="pr-ref">[<a href="#17877">17877</a>]</span>
.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>4 * #E ≤ 4 * α * β        := by linarith
      _ ≤ (α + β)^2        := four_mul_le_pow_two_add _ _
      _ = n^2              := by simp only [hαβ, Nat.sub_add_cancel]
</pre></td>
</tr></table>
</div>

<p>Finally, all that’s left to do is divide both sides by four <span class="pr-ref">[<a href="#18694">18694</a>]</span>
, and we’re done!</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  exact (Nat.le_div_iff_mul_le_comm Nat.ofNat_pos).mpr four_times_card_E_bd
</pre></td>
</tr></table>
</div>

<p>This concludes the proof. 🎉</p>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Röhrig&quot;, &quot;first_name&quot;=&gt;&quot;Olivia&quot;, &quot;homepage&quot;=&gt;&quot;https://iol.zib.de/team/olivia-roehrig.html&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[In this post, we provide another proof for Mantel’s theorem. Let’s recall the statement from the previous post: Mantel's Theorem. Suppose \(G\) is a graph on \(n\) vertices without triangles. Then \(G\) has at most \(\lfloor n^2/4 \rfloor\) edges, and equality holds if and only if \(G\) is the complete bipartite graph \(K_{\lfloor n/2 \rfloor, \lceil n/2 \rceil}\).]]></summary></entry><entry><title type="html">Mantel’s Theorem via Cauchy’s inequality</title><link href="https://thebook.zib.de/graph%20theory/2024/10/15/mantel-cauchy-schwarz.html" rel="alternate" type="text/html" title="Mantel’s Theorem via Cauchy’s inequality" /><published>2024-10-15T15:30:00+02:00</published><updated>2024-10-15T15:30:00+02:00</updated><id>https://thebook.zib.de/graph%20theory/2024/10/15/mantel-cauchy-schwarz</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2024/10/15/mantel-cauchy-schwarz.html"><![CDATA[<p>Mantel’s theorem goes back to 1907 and is a foundational result in extremal graph theory. It can be viewed as the first non-trivial case of <a href="https://en.wikipedia.org/wiki/Turán%27s_theorem">Turán’s theorem</a> and essentially states that any triangle-free graph can contain at most 50% of all possible edges.</p>

<div class="statement theorem">


    <strong>Mantel's Theorem.</strong> 


Suppose &#92;(G&#92;) is a graph on &#92;(n&#92;) vertices without triangles. Then &#92;(G&#92;) has at most &#92;(&#92;lfloor n^2/4 &#92;rfloor&#92;) edges, and equality holds if and only if &#92;(G&#92;) is the complete bipartite graph &#92;(K_{&#92;lfloor n/2 &#92;rfloor, &#92;lceil n/2 &#92;rceil}&#92;).


</div>

<p>For now, we will limit ourselves to establishing the upper bound on the number of edges and leave the characterization of the extremal example for a future post. Here we will follow the proof using Cauchy’s inequality and the handshaking lemma, which Aigner and Ziegler attribute to Mantel himself.</p>

<div class="statement proof">


    <strong>Proof.</strong> 



Let &#92;(V = &#92;{1, &#92;dots, n&#92;}&#92;) be the vertex set and &#92;(E&#92;) the edge set of &#92;(G&#92;). By &#92;(d_i&#92;) we denote the degree of &#92;(i&#92;), hence &#92;(&#92;sum_{i &#92;in V} d_i = 2|E|&#92;). Suppose ij is an edge. Since &#92;(G&#92;) has no triangles, we find &#92;(d_i + d_j &#92;leq n&#92;) since no vertex is a neighbor of both &#92;(i&#92;) and &#92;(j&#92;).

It follows that &#92;[&#92;sum_{ij &#92;in E} d_i + d_j &#92;leq n |E|.&#92;] Note that &#92;(d_i&#92;) appears exactly &#92;(d_i&#92;) times in the sum, so we get &#92;[n |E| &#92;ge &#92;sum_{ij &#92;in E} (d_i + d_j) = &#92;sum_{i &#92;in V} d_i^2,&#92;] and hence with Cauchy's inequality applied to the vectors &#92;((d_1, &#92;ldots, d_n)&#92;) and &#92;((1, &#92;ldots, 1)&#92;), &#92;[n |E| &#92;ge &#92;sum_{i &#92;in V} d_i^2 &#92;ge &#92;frac{(&#92;sum d_i)^2}{n} = &#92;frac{4 |E|^2}{n},&#92;] and the result follows.


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 145.
    
    </div>

</div>

<p>As for the <a href="/graph%20theory/2024/10/11/handshaking-lemma.html">handshaking lemma</a>, we first set up some notation, i.e., we use <code>#</code> to denote the cardinality of a set, <code>V</code> and <code>E</code> to denote the vertex<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and edge set<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> of a given <code>(G : SimpleGraph α)</code>, <code>I(v)</code> and <code>d(v)</code> to respectively denote the set of edges incident to a vertex <code>v</code> as well as its degree, and finally <code>n</code> to denote the order of <code>G</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>prefix:100 &quot;#&quot; =&gt; Finset.card
local notation &quot;V&quot; =&gt; @Finset.univ α _
local notation &quot;E&quot; =&gt; G.edgeFinset
local notation &quot;I(&quot; v &quot;)&quot; =&gt; G.incidenceFinset v
local notation &quot;d(&quot; v &quot;)&quot; =&gt; G.degree v
local notation &quot;n&quot; =&gt; Fintype.card α
</pre></td>
</tr></table>
</div>

<p>Using this, we can now easily state (our simplified version of) Mantel’s theorem.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>theorem mantel (h: G.CliqueFree 3) : #E ≤ (n^2 / 4) := by
</pre></td>
</tr></table>
</div>

<p>At first it might seem like we cheated: \(n^2 / 4\) is not \( \lfloor n^2 / 4 \rfloor \)! But all involved quantities, that is <code>#E</code>, <code>n^2</code>, and <code>4</code> are integer, so <code>n^2 / 4</code> in fact corresponds to the integer \(\lfloor n^2 / 4 \rfloor\) <a href="https://leanprover-community.github.io/mathlib4_docs/Init/Prelude.html#HDiv">mathlib’s documentation of <code>HDiv</code></a>.</p>

<p>The proof now starts with <span class="quote proof">“Suppose \(ij\) is an edge. Since \(G\) has no triangles, we find \(d_i + d_j \leq n\) since no vertex is a neighbor of both \(i\) and \(j\).”</span> The statement of this fact is easily written down, but for its proof we need to be slightly more detailed.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>  have adj_degree_bnd (i j : α) (hij: G.Adj i j) : d(i) + d(j) ≤ n := by
    by_contra hc; simp at hc
    obtain ⟨k, h⟩ := Finset.inter_nonempty_of_card_lt_card_add_card (by simp) (by simp) hc
    simp at h
    obtain ⟨hik, hjk⟩ := h
    exact h {k, j, i} ⟨by aesop (add safe G.adj_symm), by simp [hij.ne', hik.ne', hjk.ne']⟩
</pre></td>
</tr></table>
</div>

<p>The proof follows by contradiction, i.e., using <code>by_contra</code> we assume that <code>d(i) + d(j) &gt; n</code>.
Of course, since there are only <code>n</code> vertices, this means that by pigeon-hole-principle, or by <code>inter_nonempty_of_card_lt_card_add_card</code> to be precise, the vertices <code>i</code> and <code>j</code> have a common neighbor <code>k</code>.
But now, \(\{i,j,k\}\) would of course form a triangle, contradicting that <code>G</code> is assumed to betriangle-free.</p>

<p>For the next step we will need the sum \(\sum_{ij \in E} d(i) + d(j)\). There is a minor detail in this notation that is easily overlooked but that Lean does not let us get away with: the term <code>d(i) + d(j)</code> is only well-defined if it is independent of the order of <code>i</code> and <code>j</code>. This is of course true by commutativity, so we give Lean that information in a definition of the <code>sum_deg</code> of an edge. By the just established fact that two adjacent vertices <code>i</code> and <code>j</code> satisfy <code>d(i) + d(j) ≤ n</code>, <code>sum_deg</code> is of course also upper bounded by <code>n</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>  let sum_deg (e : Sym2 α) : ℕ := Sym2.lift ⟨λ x y ↦ d(x) + d(y), by simp [Nat.add_comm]⟩ e

  have adj_degree_bnd' (e : Sym2 α) (he: e ∈ E) : sum_deg e ≤ n := by
    induction e with | _ v w =&gt; simp at he; exact adj_degree_bnd v w (by simp [he])
</pre></td>
</tr></table>
</div>

<p>Let us now address the statement that
<span class="quote proof">“\(d_i\) appears exactly \(d_i\) times in the sum, so we get \(\sum_{ij \in E} (d_i + d_j) = \sum_{i \in V} d_i^2\)”</span>, which again requires a bit more detail to formally prove. The decisive step here uses <code>Finset.sum_sum_bipartiteAbove_eq_sum_sum_bipartiteBelow</code>, a weighted double counting lemma <span class="pr-ref">[<a href="#17651">17651</a>]</span>
.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
</pre></td>
  <td class="code"><pre>  have sum_sum_deg_eq_sum_deg_sq : ∑ e ∈ E, sum_deg e = ∑ v ∈ V, d(v)^2 := by
    calc  ∑ e ∈ E, sum_deg e
      _ = ∑ e ∈ E, ∑ v ∈ e, d(v)                  := Finset.sum_congr rfl (λ e he ↦ by induction e with | _ v w =&gt; simp at he; simp [sum_deg, he.ne])
      _ = ∑ e ∈ E, ∑ v ∈ {v' ∈ V | v' ∈ e}, d(v)  := Finset.sum_congr rfl (by intro e _; exact congrFun (congrArg Finset.sum (by ext; simp)) _)
      _ = ∑ v ∈ V, ∑ _ ∈ {e ∈ E | v ∈ e}, d(v)    := Finset.sum_sum_bipartiteAbove_eq_sum_sum_bipartiteBelow _ E V _
      _ = ∑ v ∈ V, ∑ _ ∈ I(v), d(v)               := Finset.sum_congr rfl (λ v ↦ by simp [G.incidenceFinset_eq_filter v])
      _ = ∑ v ∈ V, d(v)^2                         := by simp [Nat.pow_two]
</pre></td>
</tr></table>
</div>

<p>This finally allows us to get to the meat of the argument, namely the following calculation. We have slightly modified it from the template by multplying with <code>n</code> to avoid moving from natural numbers to rationals. The most important step is the application of Cauchy’s inequality, or more specifically <code>sum_mul_sq_le_sq_mul_sq</code><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>, and the handshaking lemma in the form of <code>sum_degrees_eq_twice_card_edges</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
</pre></td>
  <td class="code"><pre>  have := calc #E * n^2
    _ = (n * (∑ e ∈ E, 1)) * n               := by simp [Nat.pow_two, Nat.mul_assoc, Nat.mul_comm]
    _ = (∑ _ ∈ E, n) * n                     := by rw [Finset.mul_sum]; simp
    _ ≥ (∑ e ∈ E, sum_deg e) * n             := Nat.mul_le_mul_right n (Finset.sum_le_sum adj_degree_bnd')
    _ = (∑ v ∈ V, d(v)^2) * (∑ v ∈ V, 1^2)   := by simp [sum_sum_deg_eq_sum_deg_sq]
    _ ≥ (∑ v ∈ V, d(v) * 1)^2                := (Finset.sum_mul_sq_le_sq_mul_sq V (λ v ↦ d(v)) 1)
    _ = (2 * #E)^2                           := by simp [G.sum_degrees_eq_twice_card_edges]
    _ = 4 * #E^2                             := by ring
</pre></td>
</tr></table>
</div>

<p>Aigner and Ziegler conclude with <span class="quote proof">“… and the result follows.”</span> Lean however does not let us get away with so easily. We first massage our ineqality into the right form through a series of <code>rw</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  rw [Nat.pow_two (#E)] at this
  rw [(Nat.mul_assoc 4 (#E) (#E)).symm] at this
  rw [Nat.mul_comm (4 * #E) (#E)] at this
</pre></td>
</tr></table>
</div>

<p>We then conclude by simply dividing by <code>4 * #E</code>… which is only possibly if <code>#E</code> is not zero! So we need to do a case distinction using <code>by_cases</code> and only then are we able to get the desired statement.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
</pre></td>
  <td class="code"><pre>  by_cases hE : #E = 0
  · simp [hE]
  · apply Nat.zero_lt_of_ne_zero at hE
    apply Nat.le_of_mul_le_mul_left this at hE
    rw [Nat.mul_comm] at hE
    exact (Nat.le_div_iff_mul_le (Nat.zero_lt_succ 3)).mpr hE
</pre></td>
</tr></table>
</div>

<p>This concludes the proof. 🎉</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Technically the vertices of <code>(G : SimpleGraph α)</code> are fully characterized by the type <code>α</code>, which should satisfy <code>[Fintype α]</code> in order for <code>G</code> to be a finite graph, and the explicit use of the set of all vertices <code>V</code> is superfluous. In many cases one could in fact simply replace <code>∑ v ∈ V</code> with <code>∑ v : α</code> or even just <code>∑ v</code> and be closer to the ‘correct’ way of formulating the statement. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>The edge set however truly needs to be a set, since the underlying type <code>Sym2 α</code> simply corresponds to all possible edges, including loops, in a complete graph with vertices of type <code>α</code>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>There is a large number of Cauchy’s inequalities in mathlib, but for our purposes this <code>Finset</code> version is the most useful one. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Spiegel&quot;, &quot;first_name&quot;=&gt;&quot;Christoph&quot;, &quot;portrait&quot;=&gt;&quot;/assets/portraits/christophspiegel.jpg&quot;, &quot;homepage&quot;=&gt;&quot;https://christophspiegel.berlin&quot;, &quot;bio&quot;=&gt;&quot;I&apos;m a postdoc at Zuse Institute Berlin and lecturer at Technical University of Berlin, focusing on using computational tools in theoretical mathematics. Recently, I&apos;ve used flag algebras, semidefinite programming, and discrete optimization to enhance bounds in extremal combinatorics. Currently, I&apos;m exploring proof verification and assistance.&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[Mantel’s theorem goes back to 1907 and is a foundational result in extremal graph theory. It can be viewed as the first non-trivial case of Turán’s theorem and essentially states that any triangle-free graph can contain at most 50% of all possible edges.]]></summary></entry><entry><title type="html">The Handshaking Lemma</title><link href="https://thebook.zib.de/graph%20theory/2024/10/11/handshaking-lemma.html" rel="alternate" type="text/html" title="The Handshaking Lemma" /><published>2024-10-11T08:00:00+02:00</published><updated>2024-10-11T08:00:00+02:00</updated><id>https://thebook.zib.de/graph%20theory/2024/10/11/handshaking-lemma</id><content type="html" xml:base="https://thebook.zib.de/graph%20theory/2024/10/11/handshaking-lemma.html"><![CDATA[<p>The <a href="https://en.wikipedia.org/wiki/Handshaking_lemma">handshaking lemma</a> is a simple result in graph theory that establishes a relationship between the degrees of the vertices in a finite graph and the number of edges in the graph. It states that the sum of the degrees of all the vertices in a graph is twice the number of edges in the graph</p>

<div class="statement theorem">


    <strong>The Handshaking Lemma.</strong> 


Any finite simple graph satisfies &#92;(&#92;sum_{v} &#92;deg(v) = 2 |E|&#92;).


</div>

<p>The above formula is also sometimes referred to as the <em>sum degree formula</em> with the term <em>handshaking lemma</em> used to refer to the corollary that the number of vertices that touch an odd number of edges is even. The proof of the sum degree formula will be short and sweet and canonically relies on <a href="https://en.wikipedia.org/wiki/Double_counting_(proof_technique)">double counting</a>. Aigner and Ziegler write it up as follows:</p>

<div class="statement proof">


    <strong>Proof.</strong> 



For the proof consider &#92;(S &#92;subseteq V &#92;times E&#92;), where &#92;(S&#92;) is the set of pairs &#92;((v,e)&#92;) such that &#92;(v &#92;in V&#92;) is an end-vertex of &#92;(e &#92;in E&#92;). Counting &#92;(S&#92;) in two ways gives on the one hand &#92;(&#92;sum_{v &#92;in V} &#92;deg(v)&#92;), since every vertex contributes &#92;(&#92;deg(v)&#92;) to the count, and on the other hand &#92;(2|E|&#92;), since every edge has two ends.


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 185.
    
    </div>

</div>

<p>Graphs are represented by the <code>SimpleGraph</code> structure in mathlib:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>structure SimpleGraph (V : Type u) where
  Adj : V → V → Prop
  symm : Symmetric Adj := by aesop_graph
  loopless : Irreflexive Adj := by aesop_graph
</pre></td>
</tr></table>
</div>

<p>Given some type of vertices <code>V</code>, a graph consists of a symmetric and irreflexive (meaning loopless) relation on <code>V</code>. It is a finite graph if the type <code>V</code> has the typeclass <code>Fintype</code>. This notion is perhaps slightly different from how many extremal combinatorialists might think of it in two way: First, there is no underlying vertex set, but instead an underlying vertex type. We have already discussed this <a href="/fundamentals/2024/02/23/tech-type-theory.html">previously</a>, but for now all this means is that we write  <code>∑ v : V, ...</code> or simply <code>∑ v, ...</code> instead of <code>∑ v ∈ V, ...</code>. The second and more important distinction is that edges are not defined as sets of two vertices but through a relation on the type of vertices. Invoking <code>edgeSet</code> or <code>edgeFinset</code> does yield the set of edges. However, on closer inspection we will see that the underlying type of the elements of these is not <code>Finset V</code> but rather <code>Sym2 V</code>,  the type of unordered pairs of elements of <code>V</code>. Notably this type inherently does not prohibit loops and instead one needs to invoke the irreflexivity of the adjacency relation to exclude them. See <span class="pr-ref">[<a href="#17587">17587</a>]</span>
 for an example of how this can make handling edges somewhat unintuitive.</p>

<p>Let us fix some notation to make the proof nicer to read, i.e., we use <code>#</code> to denote the cardinality of a <code>Finset</code> <span class="pr-ref">[<a href="#17646">17646</a>]</span>
, <code>E</code> to denote the <code>edgeFinset</code> of a given <code>G</code>, and finally <code>d(v)</code> and <code>I(v)</code> to denote the <code>degree</code> and <code>incidenceFinset</code> of a given vertex <code>v</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>prefix:100 &quot;#&quot; =&gt; Finset.card
local notation &quot;E&quot; =&gt; G.edgeFinset
local notation &quot;d(&quot; v &quot;)&quot; =&gt; G.degree v
local notation &quot;I(&quot; v &quot;)&quot; =&gt; G.incidenceFinset v
</pre></td>
</tr></table>
</div>

<p>Using this notation, we can state and prove the sum degree formula in a concise and readable form using the <code>calc</code> tactic.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
</pre></td>
  <td class="code"><pre>lemma handshaking : ∑ v, d(v) = 2 * #E := by
  calc  ∑ v, d(v)
    _ = ∑ v, #I(v)             := by simp [G.card_incidenceFinset_eq_degree]
    _ = ∑ v, #{e ∈ E | v ∈ e}  := by simp [G.incidenceFinset_eq_filter]
    _ = ∑ e ∈ E, #{v | v ∈ e}  := Finset.sum_card_bipartiteAbove_eq_sum_card_bipartiteBelow _
    _ = ∑ e ∈ E, 2             := Finset.sum_congr rfl (λ e he ↦ (G.card_filter_mem_of_mem_edgeFinset e he))
    _ = 2 * ∑ e ∈ E, 1         := (Finset.mul_sum E (λ _ ↦ 1) 2).symm
    _ = 2 * #E                 := by rw [Finset.card_eq_sum_ones E]
</pre></td>
</tr></table>
</div>

<p>The decisive step here is going from <code>∑ v, #{e ∈ E | v ∈ e}</code> to <code>∑ e ∈ E, #{v | v ∈ e}</code>, which is possible precisely due to the double counting argument. There are <a href="https://leanprover.github.io/mathlib_docs/combinatorics/doubleCounting.html">a number of double counting arguments in mathlib</a>, but the one relevant for our purposes is <code>sum_card_bipartiteAbove_eq_sum_card_bipartiteBelow</code>. This lemma states that for any sets <code>(s : Finset α)</code> and <code>(t : Finset β)</code> along with a relation <code>(r : α → β → Prop)</code> the sum <code>∑ a ∈ s, #(t.bipartiteAbove r a)</code> is the same as <code>∑ b ∈ t, #(s.bipartiteBelow r b)</code>; this means that summing the number of elements in <code>t</code> related to a given <code>a  ∈ s</code> is the same as summing the number of elements in <code>s</code> related to a given <code>b ∈ t</code>. Everything before this step simply modifies the summand to make the application of this lemma possible: <code>card_incidenceFinset_eq_degree</code> allows us to rewrite the degree as the cardinality of the incidence finset, and <code>incidenceFinset_eq_filter</code> essentially unfolds the definition of the incidence finset. After this, we simple use the fact that an edge contains two vertices in the form of <code>card_filter_mem_of_mem_edgeFinset</code> <span class="pr-ref">[<a href="#17587">17587</a>]</span>
 and that summing ones over a finite set yields the cardinality of that set.</p>

<p>This concludes the proof. 🎉</p>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Spiegel&quot;, &quot;first_name&quot;=&gt;&quot;Christoph&quot;, &quot;portrait&quot;=&gt;&quot;/assets/portraits/christophspiegel.jpg&quot;, &quot;homepage&quot;=&gt;&quot;https://christophspiegel.berlin&quot;, &quot;bio&quot;=&gt;&quot;I&apos;m a postdoc at Zuse Institute Berlin and lecturer at Technical University of Berlin, focusing on using computational tools in theoretical mathematics. Recently, I&apos;ve used flag algebras, semidefinite programming, and discrete optimization to enhance bounds in extremal combinatorics. Currently, I&apos;m exploring proof verification and assistance.&quot;}</name></author><category term="[&quot;Graph Theory&quot;]" /><summary type="html"><![CDATA[The handshaking lemma is a simple result in graph theory that establishes a relationship between the degrees of the vertices in a finite graph and the number of edges in the graph. It states that the sum of the degrees of all the vertices in a graph is twice the number of edges in the graph]]></summary></entry><entry><title type="html">A closer look at type theory</title><link href="https://thebook.zib.de/fundamentals/2024/05/10/closer-look-at-types.html" rel="alternate" type="text/html" title="A closer look at type theory" /><published>2024-05-10T08:00:00+02:00</published><updated>2024-05-10T08:00:00+02:00</updated><id>https://thebook.zib.de/fundamentals/2024/05/10/closer-look-at-types</id><content type="html" xml:base="https://thebook.zib.de/fundamentals/2024/05/10/closer-look-at-types.html"><![CDATA[<p>When writing Lean code, an <em>interpreter</em> recognises keywords (such as <code>def</code>) and settles ambiguous notation in a step referred to as <em>parsing</em>. The expression <code>a+b-c</code> for example could either refer to <code>(a+b)-c</code> or <code>a+(b-c)</code> and may yield different values in <code>Nat</code>. It then tries to clarify certain omissions or semantic ambiguities, for example filling in placeholders <code>_</code> and trying to settle which subtraction you mean when using the minus sign (<code>Nat.sub</code> or <code>Int.sub</code>), referred to as <em>elaboration</em>. It is during this second stage that most of the errors you’ve made in your code will be discovered and stored as messages to be printed by the VS Code infoview. Finally, Lean will <em>type-check</em> your code with respect to a rather elaborate type theory, the crucial feature of Lean that makes it usable as a formal theorem prover.</p>

<p>We have previously already had a <a href="/fundamentals/2024/02/23/tech-type-theory.html">first look</a> at types in Lean. In this post, we’ll take a closer look at type theory and type checking, and how they allow us to express and check the correctness of mathematics. We’ll first discuss types from a theoretical perspective, sketching type-checking in the process. Then we’ll illustrate how to encode mathematics in this framework using explicit examples.</p>

<h1 id="terms-types-expressions">Terms, types, expressions</h1>

<p>In programming, types are used to prevent errors when performing operations on variables. Numbers and text for example will both be stored as a series of ones and zeros. so you may end up accidentally adding numbers and text. To prevent this from happening, the compiler or interpreter will ask you to label your variables with types (or it will try to infer them from context or during execution), and point you to the part of the code that may cause issues.</p>

<p>At a first glance, types can be thought of as having the same purpose in Lean. The objects of interest in Lean are expressions, such as <code>2+2 = 4</code> or <code>List.append xs [1,2,3]</code>. These expressions have a type: <code>2+2 = 4</code> for example has type <code>Prop</code> and <code>List.append xs [1,2,3]</code> has type <code>List Nat</code>. As we have previously already seen, these types are expressions too. This can for example be clearly seen in <code>List Nat</code>, which we think of as the type constructor <code>List</code> being applied to the type <code>Nat</code>. This illustrates that Lean’s types, the labels of our variables and constants, can be manipulated, and are therefore labeled as well. Indeed, if you try to get the type of natural numbers with <code>#check Nat</code>, Lean will tell you that it has type <code>Type</code>. You can then try <code>#check Type</code> to see that this type has type <code>Type 1</code>. The types <code>Prop</code>, <code>Type</code>, <code>Type 1</code>, <code>Type 2</code>, … are called <em>sorts</em> and they are built-in types of the theory.</p>

<p>We may add constant types via the axiom command <code>axiom myType : Type</code>. Most fundamental types of Lean, including <code>Nat</code> and <code>List</code> are however defined as inductive types, which can be thought of as introducing a multitude of axioms at each inductive declaration. We may also build further expressions with the introduction of functions. In Lean, one can define functions <code>fun x : T =&gt; e(x)</code>, where <code>T</code> is some previously defined type and <code>e(x)</code> stands for some expression that may or may not contain the symbol <code>x</code>. The type of this function is of form <code>T → E</code>, <code>(x : T) → E(x)</code> or <code>∀ x : T, E(x)</code> for some expression <code>E</code> potentially containing symbol <code>x</code>, which is the type of <code>e(x)</code>. For some examples, consider the messages of:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>#check fun x : Nat =&gt; true
#check fun x : Nat =&gt; (⟨x, rfl⟩ : Odd (2*x+1))
#check fun x : Nat =&gt; (0 : ZMod x)
</pre></td>
</tr></table>
</div>
<p><code>T → E</code>, <code>(x : T) → E(x)</code> or <code>∀ x : T, E(x)</code> are all treated as the same expression. The first corresponds to the middle case, where we omit <code>x</code> in the case where the expression <code>E</code> doesn’t contain this symbol. The last is the mathematical interpretation of the middle one, that we’ll explore in a moment. What would be the use of functions if we couldn’t evaluate them? The answer is “representation”, as we’ll see soon, but we introduce function application, denoted <code>f x</code> (instead of the mathematician’s <code>f(x)</code>), as a valid way to form further expressions.</p>

<p>We now have the most essential ways in wich valid expressions may be formed in Lean. To see how any expression is formed, consider the following diagram (representing a grammar in <a href="https://de.wikipedia.org/wiki/Backus-Naur-Form">Backus-Naur-Form</a>), depicting rules and their names:</p>
<div class="language-plaintext highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>E --&gt; V   (variables)
E --&gt; C   (constants)
E --&gt; fun V : E =&gt; E    (functions)
E --&gt; ∀ x : E, E    (function-type)
E --&gt; E E   (function-application)
</pre></td>
</tr></table>
</div>
<p>To form an expression, start with symbol <code>E</code> and iteratively replace upper-case symbols with the right-hand-sides indicated above. <code>V</code> may be replaced by a symbol in an alphabet of variables, and <code>C</code> by a symbol in an alphabet of constants, which we imagine to be lower-case symbols. Once no upper-case symbols remain, the expression is formed. In our above description, some expressions may be ill-formed in a sense, as we allow for the presence of variables without guaranteeing that they are bound by an above <code>fun</code>. You can compare this with Lean’s built-in type <code>Lean.Expr</code>.</p>

<p>For example, let’s derive the expression <code>2+2 = 4</code>, which is notation (or ‘syntactic sugar’) for <code>(Eq (Nat.add 2 2)) 4</code>. Recall that relations and binary operations are treated as functions of two arguments, and that functions of multiple arguments are treated as repeated application of one-variable functions that map to functions, a representation known as <em>currying</em>.</p>
<div class="language-plaintext highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>E --&gt; E E
E E --&gt; (E E) E
(E E) E --&gt; (E (E E)) E
(E (E E)) E --&gt; ... --&gt; (C (C C)) C
(C (C C)) C --&gt; ... --&gt; (Eq (Nat.add 2 2)) 4
</pre></td>
</tr></table>
</div>
<p>As we hinted at in the introduction, the parts of the Lean compiler responsible for turning the concrete syntax of your code into these expressions are <em>parsing</em> and <em>elaboration</em>. Tactics will produce such expressions as well, as you can tell when <code>#print</code>-ing the lemmata and definitions that made use of them (though the expressions can quickly get very big, even when pretty-printed).</p>

<p>Any expression has a type, that can be computed, or <em>inferred</em>, as we say in type theory, with the following recursive algorithm: for variables, we refer to the type declared in the function that introduced them. For constants, we refer to the context of previous definitions. For functions <code>fun v : d =&gt; b</code>, we take note that <code>v</code> has type <code>d</code>, and use this to find the type <code>B</code> of <code>b</code>, in a recursive call to the type inference process, and return type <code>∀ x : d, B</code>. For function types, we’ll keep our exposition simple and just mention that the returned type will be a sort.</p>

<p>Finally, we reach function application, at which a new form of ill-formedness can be discovered for expressions. We want, in <code>f x</code>, for <code>f</code> to have a type of form <code>∀ y : d, B(y)</code> and simultaneously have <code>x</code> of type <code>d</code>, so that we may assign type <code>B(x)</code> (with occurrences of <code>y</code> replaced by <code>x</code>) to <code>f x</code>. If this condition fails, Lean will not consider the expression valid, and throw an error. This is essentially where the type-checking happens. An extremely important aspect we will not discuss is that when checking types at <code>f x</code>, for <code>f</code> of type <code>∀ x : d1, B</code> and <code>x</code> of type <code>d2</code>, we consider <code>d1</code> and <code>d2</code> equal not only for syntactic equality, but also equal up to reductions, such as alpha reduction (renaming of bound variables), beta reduction (replacing of <code>(fun x : T =&gt; e(x)) v</code> with <code>e(v)</code>) and delta reduction (replacement of constants with their value stored in context), among others.</p>

<h1 id="mathematics">Mathematics</h1>

<p>Where does math fit into all this? So far, we have only described how to form expressions in Lean, and which ones Lean considers to be valid. We will now exemplify how to encode common mathematical objects and notions in this language, and how the process of checking if expressions are well-form corresponds to checking if proofs are correct.</p>

<h2 id="a-first-example-the-natural-numbers">A first example: the natural numbers</h2>

<p>We can imagine the natural numbers to be defined via</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>axiom myNat : Type
axiom myZero : myNat
axiom mySuccessor : myNat → myNat
</pre></td>
</tr></table>
</div>

<p>where <code>0</code> is encoded by <code>myZero</code>, <code>1</code> by <code>mySuccessor myZero</code>, <code>2</code> by <code>mySuccessor (mySuccessor myZero)</code>, … . This is an encoding similar to Peano arithmetic. Lean’s actual definition of natural numbers is quote similar to this, bundled in the inductive type <code>Nat</code> and with the role of <code>myZero</code> and <code>mySuccessor</code> being fulfilled by <code>Nat.zero</code> and  <code>Nat.succ</code>.</p>

<p>We can also define a “less or equal than” relation via</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>axiom myLe : myNat → myNat → Type
axiom myLe_symm : (x : myNat) → myLe x x
axiom myLe_step : (x : myNat) → (y : myNat) → myLe x y → myLe x (mySuccessor y)
</pre></td>
</tr></table>
</div>

<p>The first axiom postulates the existence of a binary relation <code>≤</code>. The second and third axiom say that we have <code>∀ n : myNat, n ≤ n</code> and <code>∀ n : myNat, ∀ m : myNat,n ≤ m → n ≤ m + 1</code>. In Lean, these are respectively captured through <code>Nat.le</code>, <code>Nat.le.refl</code>, <code>Nat.le.step</code>.  Note that they are function types, which we interpret as universal quantifiers. Let’s look at what happens if we evaluate them:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>#check myLe_symm myZero
#check myLe_step myZero myZero (myLe_symm myZero)
#check myLe_step myZero (mySuccessor myZero) (myLe_step myZero myZero (myLe_symm myZero))
</pre></td>
</tr></table>
</div>
<p>The types of these expressions are <code>0 ≤ 0</code>, <code>0 ≤ 1</code> and <code>0 ≤ 2</code>, which is why we consider them proofs of these statements.</p>

<p>Let’s follow how the type of the second expression is derived. Due to currying, it is read as <code>(myLe_step myZero myZero) (myLe_symm myZero)</code>, so we’re dealing with a function application, with function <code>(myLe_step myZero myZero)</code> and argument <code>(myLe_symm myZero)</code>. We should therefore derive the type of both, check that the function truly has a function-type, with domain-type definitionally equal to the type of the argument. Let’s first look at the type of the argument <code>myLe_symm myZero</code>, which is itself a function application. Here, the function has type <code>(x : myNat) → myLe x x</code>, and the argument type <code>myNat</code> (which we get be querying the context for the type of constants). The domain-type and the type of the argument match, so the type check passes, and we assign <code>myLe_symm myZero</code> the type <code>myLe myZero myZero</code>, which was the image-type of the function, where we replace the symbol of the variable (<code>x</code>) with the argument. Now, let’s return to the task of finding the type of <code>(myLe_step myZero myZero) (myLe_symm myZero)</code>, and assume that we recursively derived the type of <code>myLe_step myZero myZero</code> to be <code>myLe myZero myZero → myLe myZero (mySuccessor myZero)</code> (try doing this as an exercise). <code>myLe_symm myZero</code> has the domain-type of the function-type <code>myLe_step myZero myZero</code>, so type-checking passes and we obtain the desired <code>0 ≤ 1</code>.</p>

<p>Next, let’s look at more examples of formalism. We can model equality of natural numbers as follows:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>axiom myEq : myNat → myNat → Type
axiom myEq_refl: (x : myNat) → myEq x x
axiom myEq_subst : (x : myNat) → (y : myNat) → (P : myNat → Type) → myEq x y → P x →  P y
</pre></td>
</tr></table>
</div>
<p>This says that there is a binary relation <code>myEq</code>, which is reflexive, in the sense that for any <code>x</code>, we may build a term (a proof) of type (statement) <code>myEq x x</code>, by the axiom <code>myEq_refl</code>. The last axiom says that if <code>x</code> and <code>y</code> are equal, then for any predicate <code>P</code> on natural numbers, if <code>P x</code> then <code>P y</code>. In actual Lean, <code>myEq_refl</code> is <code>Eq.refl</code> and <code>myEq_subst</code> is <code>Eq.rec</code>.</p>

<p>We’ll take a brief moment to illustrate the notion of definitional equality we hinted at earlier. Consider the following:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>noncomputable
example:  myEq myZero myZero := myEq_refl ((fun _ : myNat =&gt; myZero) (mySuccessor myZero))
</pre></td>
</tr></table>
</div>
<p>When we define something with <code>def</code> or <code>example</code> or state a theorem with <code>lemma</code>, Lean will compute the type of what follows <code>:=</code> and check if it matches what follows <code>:</code>. Technically speaking, the term of the example has type <code>(fun _ : myNat =&gt; myZero) (mySuccessor myZero) = (fun _ : myNat =&gt; myZero) (mySuccessor myZero)</code>, and not <code>myZero = myZero</code>. However these types are congruent up to reduction (in this case, beta-reduction), which is why Lean considers them to be the same, and is satisfied.</p>

<p>Next, we’ll define addition of natural numbers. Here, we will deviate slightly from the way addition is implemented for <code>Nat</code> in Lean in order to simplify the exposition.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>axiom myAdd : myNat → myNat → myNat
axiom myAdd_reduce_zero : (x : myNat) → myEq x (myAdd x myZero)
axiom myAdd_reduce_succ : (x : myNat) → (y : myNat) → myEq (mySuccessor (myAdd x y)) (myAdd x (mySuccessor y))
</pre></td>
</tr></table>
</div>
<p>We introduce function <code>myAdd</code> and provide two computation rules which are, morally speaking, <code>∀ x : myNat, x+0=0</code> and <code>∀ x : myNat, ∀ y : myNat, x+(y+1)=(x+y)+1</code>. We can now show that <code>2 = 1+1</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
<a href="#n16" name="n16">16</a>
<a href="#n17" name="n17">17</a>
<a href="#n18" name="n18">18</a>
<a href="#n19" name="n19">19</a>
<strong><a href="#n20" name="n20">20</a></strong>
<a href="#n21" name="n21">21</a>
<a href="#n22" name="n22">22</a>
<a href="#n23" name="n23">23</a>
</pre></td>
  <td class="code"><pre>noncomputable
def myProof :
  let one := mySuccessor myZero
  let two := mySuccessor (mySuccessor myZero)
  myEq two (myAdd one one)
    :=
    let one := mySuccessor myZero
    let two := mySuccessor (mySuccessor myZero)
    myEq_subst
      (mySuccessor (myAdd one myZero))
      (myAdd one one)
      (fun x : myNat =&gt; myEq two x)
      (myAdd_reduce_succ
        one
        myZero
        )
      (myEq_subst
        one
        (myAdd one myZero)
        (fun x : myNat =&gt; myEq two (mySuccessor x))
        (myAdd_reduce_zero one)
        (myEq_refl two)
        )
</pre></td>
</tr></table>
</div>
<p>We highly recommend you explore the code: you may comment out lines with <code>--</code> and write a placeholder <code>_</code> in front of the comment, so as to have the infoview display the expected type we want the term to have. Alternatively, you may copy some parts out and <code>#check</code> them, as follows:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
</pre></td>
  <td class="code"><pre>noncomputable def one := mySuccessor myZero
noncomputable def two := mySuccessor (mySuccessor myZero)

#check (myEq_refl two)
#check (myAdd_reduce_zero one)
#check (fun x : myNat =&gt; myEq two (mySuccessor x))
#check (myEq_subst one (myAdd one myZero))
#check (myEq_subst
        one
        (myAdd one myZero)
        (fun x : myNat =&gt; myEq two (mySuccessor x))
        (myAdd_reduce_zero one)
        (myEq_refl two)
        )
</pre></td>
</tr></table>
</div>

<p>Both the claimed type and proof-term are expressions as we described them previously: they are made of functions, function applications, and constants. The claimed type encodes <code>2 = 1+1</code>. To prove it, we will build a term of this type, which is the content after <code>:=</code>. We’ll get the claimed type from the application of <code>myEq_subst</code>, so that we use predicate <code>fun x : myNat =&gt; myEq two x</code>, to be able to get this from a proof of <code>2 = mySuccessor (1+0)</code>. This, in turn, we’ll also deduce from he application of <code>myEq_subst</code>, so that we use predicate <code>fun x : myNat =&gt; myEq two (mySuccessor x))</code>, to be able to get this from a proof of <code>2 = mySuccessor (mySuccessor 0)</code>. The axiom <code>myEq_refl</code> serves as proof for latter fact.</p>

<p>Type checking and inference thus ensure that we are using correct proofs of the hypotheses needed to be proven in intermediate steps of the proof, and that the proof truly produces evidence for the claimed type, which we interpret as statement. In the previous code excerpt, the <code>#check</code>s reveal that the type checker will be satisfied with the expression, as the arguments have the type that the function we apply them to requests in its type.</p>

<p>In actual Lean, addition is defined using a recursion principle for natural numbers, and the above computation holds by <em>definitional equality</em>, which in this context means that during type checking, a reduction specific to inductive (and quotient) types, called iota reduction, will be applied, so that the addition will be syntactically congruent to <code>2</code>, and the proof therefore just requires reflexivity of equality.</p>

<p>We take a brief moment to look back at equality. Not all basic properties must be added as axioms. We may sometimes deduce certain basic properties from other ones. For example, the following proves that equality is symmetric, based on the previous axioms:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
</pre></td>
  <td class="code"><pre>noncomputable
def myEq_symm :
  (x : myNat) → (y : myNat) →
  myEq x y → myEq y x
    :=
    fun x : myNat =&gt;
      fun y : myNat =&gt;
        fun eq : myEq x y =&gt;
          (myEq_subst
           x
           y
           (fun z =&gt; myEq z x)
           eq
           (myEq_refl x))
</pre></td>
</tr></table>
</div>
<p>We see that proving a universally quantified statement is equivalent to building a function that given a argument return a term of the quantified type. Indeed, this is (almost) the only way to get universal quantification in the type during type inference (we could also get it from querying the type of constants).</p>

<p>If we add a induction principle for natural numbers, which is the equivalent of Lean’s <code>Nat.rec</code>, we can show that for every natural number <code>n</code>, zero is less then or equal to <code>n</code>:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
</pre></td>
  <td class="code"><pre>axiom myNat_rec : (P : myNat → Type) → (P myZero) → ((x : myNat) → (P x) →  P (mySuccessor x)) → (y : myNat) → P y

noncomputable
def myInductionProof :
    (n : myNat) → myLe myZero n
      :=
      fun n : myNat =&gt;
        myNat_rec
          (fun x : myNat =&gt; myLe myZero x)
          (myLe_symm myZero)
          (fun x : myNat =&gt;
            fun ineq : myLe myZero x =&gt;
              (myLe_step myZero x ineq))
          n
</pre></td>
</tr></table>
</div>
<p>When using the recursion principle, the first argument is the so-called <em>motive</em>, then we show the base case, which in the above corresponds to showing that 0 is less then 0, which follows from reflexivity, and finally we show the induction step. In the above, we get the implications as functions, and show that we may deduce the step from axiom <code>myLe_step</code>.</p>

<h2 id="a-second-example-logic">A second example: logic</h2>

<p>So far, we looked mostly at computational proofs. But we can also model logic in this type theory. For example, consider this encoding of the logical “or”:</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>axiom myOr : Type → Type → Type
axiom myOr_inl : (P : Type) → (Q : Type) → (p : P) → myOr P Q
axiom myOr_inr : (P : Type) → (Q : Type) → (p : Q) → myOr P Q
</pre></td>
</tr></table>
</div>
<p>This gives us two ways of proving <code>P ∨ Q</code>: either we build a proof of type <code>P</code>, or we build one of type <code>Q</code>. Can you guess how one could define the logical “and” ? Here is “or” in action, where we show that <code>2=1+1 ∨ 1=0</code>:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
</pre></td>
  <td class="code"><pre>noncomputable
def mySecondProof :
  let one := mySuccessor myZero
  let two := mySuccessor (mySuccessor myZero)
  myOr (myEq two (myAdd one one)) (myEq one myZero)
    :=
    let one := mySuccessor myZero
    let two := mySuccessor (mySuccessor myZero)
    myOr_inl
    (myEq two (myAdd one one))
    (myEq one myZero)
    myProof
</pre></td>
</tr></table>
</div>

<p>Here is how one can encode existential quantification:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>axiom myExists : (P : myNat → Type) → Type
axiom myExists_intro : (P : myNat → Type) → (e : myNat) → (he : P e) →  myExists P
</pre></td>
</tr></table>
</div>
<p>The introduction rule allows us to produce type <code>∃ e, P e</code> if we have an <code>e</code> and a proof of <code>P e</code> (a term of this type). Let’s use it to show that for every natural number, we may find a number greater or equal to it:</p>
<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
</pre></td>
  <td class="code"><pre>noncomputable
def myProofWithExists :
  (n : myNat) → myExists (fun x =&gt; myLe n x)
  :=
    fun n : myNat =&gt;
      myExists_intro
        (fun x =&gt; myLe n x)
        (mySuccessor n)
        (myLe_step
          n
          n
          (myLe_symm n)
          )
</pre></td>
</tr></table>
</div>
<p>The second argument to the introduction rule says that for <code>n</code>, one of the numbers greater then <code>n</code> that we claim to exists is <code>n+1</code>. The last argument is the proof to the fact that <code>n+1</code> is greater than <code>n</code>. This is directly proven with axiom <code>myLe_step</code>. As an exercise, you may try to prove this statement with a different candidate as evidence for the existential quantifier.</p>

<h1 id="conclusion">Conclusion</h1>

<p>This concludes our little trip around the basics of Lean’s type theory. It is quite stunning to see how the basic rules we introduced in the first section allow us to express basic mathematical objects, statements and proofs. In a future post, we’ll perhaps take a closer looks at further mathematical and logical foundational formalisms, as we started doing in the previous section, or at implementation aspects of type theory, as we did in the first section. In the meantime, we wish you happy proving!</p>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Jäckle&quot;, &quot;first_name&quot;=&gt;&quot;Yves&quot;, &quot;portrait&quot;=&gt;&quot;/assets/portraits/yvesetiennejaeckle.jpg&quot;, &quot;homepage&quot;=&gt;&quot;https://happyves.github.io/&quot;, &quot;bio&quot;=&gt;&quot;I&apos;m a PhD Student at the Technical University of Berlin, working on formal proof verification. I formalize content and work on methods to automate this task.&quot;}</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[When writing Lean code, an interpreter recognises keywords (such as def) and settles ambiguous notation in a step referred to as parsing. The expression a+b-c for example could either refer to (a+b)-c or a+(b-c) and may yield different values in Nat. It then tries to clarify certain omissions or semantic ambiguities, for example filling in placeholders _ and trying to settle which subtraction you mean when using the minus sign (Nat.sub or Int.sub), referred to as elaboration. It is during this second stage that most of the errors you’ve made in your code will be discovered and stored as messages to be printed by the VS Code infoview. Finally, Lean will type-check your code with respect to a rather elaborate type theory, the crucial feature of Lean that makes it usable as a formal theorem prover.]]></summary></entry><entry><title type="html">Infinitude of Primes - Fürstenberg’s proof</title><link href="https://thebook.zib.de/number%20theory/2024/03/15/primes-infinitude-furstenberg.html" rel="alternate" type="text/html" title="Infinitude of Primes - Fürstenberg’s proof" /><published>2024-03-15T07:00:00+01:00</published><updated>2024-03-15T07:00:00+01:00</updated><id>https://thebook.zib.de/number%20theory/2024/03/15/primes-infinitude-furstenberg</id><content type="html" xml:base="https://thebook.zib.de/number%20theory/2024/03/15/primes-infinitude-furstenberg.html"><![CDATA[<p>Let us have a look at <a href="https://en.wikipedia.org/wiki/Furstenberg%27s_proof_of_the_infinitude_of_primes">Fürstenberg’s proof of the infinitude of primes</a>. This is a fun one, deriving a contradiction from the assumption that there are only finitely many primes by defining a topology on the integers. The proof as written down by Aigner and Ziegler is slightly longer, so let us split it up into three parts.</p>

<div class="statement proof">


    <strong>Proof.</strong> 



Consider the following curious topology on the set &#92;(&#92;mathbb{Z}&#92;) of integers. For &#92;(a, b &#92;in &#92;mathbb{Z} &#92;), &#92;(b &gt; 0&#92;), we set &#92;[N_{a,b}  = &#92;{a + n &#92;, b &#92;mid n &#92;in &#92;mathbb{Z} &#92;}.&#92;] Each set &#92;(N_{a,b}&#92;) is a two-way infinite arithmetic progression. Now call a set &#92;(O ⊆ &#92;mathbb{Z}&#92;) <i>open</i> if either &#92;(O&#92;) is empty, or if to every &#92;(a &#92;in O&#92;) there exists some &#92;(b &gt; 0&#92;) with &#92;(N_{a,b} &#92;subseteq O&#92;). Clearly, the union of open sets is open again. If &#92;(O_1, O_2&#92;) are open, and &#92;(a &#92;in O_1 &#92;cap O_2&#92;) with &#92;(N_{a,b_1} &#92;subseteq O_1&#92;) and &#92;(N_{a,b_2} &#92;subseteq O_2&#92;), then &#92;(a &#92;in N_{a, b_1 b_2}&#92;). So we conclude that any finite intersection of open sets is again open. So, this family of open sets induces a bona fide topology on &#92;(&#92;mathbb{Z}&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 5.
    
    </div>

</div>

<p>The definition of \(N_{a,b}\) is taken care of easily.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>def N (a b : ℤ) : Set ℤ := { a + b * n | n : ℤ}
</pre></td>
</tr></table>
</div>

<p>Before continuing and defining the topology on \(\mathbb{Z}\), let us establish some trivial but important properties of \(N_{a,b}\) that will implicitly be used later in the proof. The first states that the sets \(N_{a,b}\) are infinite and is easily proven through an injective map \(f : \mathbb{Z} \hookrightarrow N_{a,b}\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>lemma N_infinite (a b : ℤ) (h : b ≠ 0) : (N a b).Infinite := by
  let f (n : ℤ) : N a b := ⟨a + b * n, by simp [N]⟩
  have f_inj : Function.Injective f := λ a₁ a₂ f_eq ↦ by simp [f, h] at f_eq; exact f_eq
  exact Set.infinite_coe_iff.mp (Infinite.of_injective f f_inj)
</pre></td>
</tr></table>
</div>

<p>Next is a small but crucial statement in the form of <code>el_N_of_dvd</code>, that will make many arguments a lot easier: \(n\) is an element of \(N_{a,b}\) if and only if \(b ∣ (n - a)\). Note that for Lean \(0 \mid 0\). One immediate consequence of this (<code>N_sub</code>) states that \(N_{a, b \, m} \subseteq N_{a,b}\) for any integer \(m\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
</pre></td>
  <td class="code"><pre>lemma el_N_of_dvd (a b n : ℤ) : n ∈ N a b ↔ b ∣ (n - a) := by
  constructor &lt;;&gt; {intro ⟨c, _⟩; use c; linarith }

lemma N_sub (a b m: ℤ) : N a (b * m) ⊆ N a b := by
  intro n h
  rw [el_N_of_dvd] at *
  exact Int.dvd_trans (Exists.intro m rfl) h
</pre></td>
</tr></table>
</div>

<p>Let us now tackle the specified topology on \(\mathbb{Z}\). We will talk in more detail about type categories in a future post, but let us already spoil that in order to give  \(\mathbb{Z}\) a topology, we need to establish that <code>ℤ</code> is an <code>instance</code> of <code>TopologicalSpace</code>. That means that to any lemmas that require a topological structure we will simply pass <code>ℤ</code> and the notion of open and closed sets will be automatically inferred from <code>int_topology</code>. Note that this behavior is quite similar to how we inferred the information that some natural number <code>p</code> is primed from a <code>Fact</code>; in a sense a <code>Fact</code> behaves like a type class with a single property.</p>

<p>The first thing any topological space needs is a notion of open sets, which we deliver in the form of <code>IsOpen</code>. The fact that <code>ℤ</code> itself needs to be open (<code>isOpen_univ</code>) was skimmed over by Aigner and Ziegler, but thankfully Lean makes relatively easy work of this.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>instance int_topology : TopologicalSpace ℤ where
  IsOpen O := O = ∅ ∨ (∀ a : O, ∃ b, b ≠ 0 ∧ N a b ⊆ O)
  isOpen_univ := by simp; exact exists_ne 0
</pre></td>
</tr></table>
</div>

<p>The first non-trivial property of a topological space requires that the (not necessarily finite) union of open sets is again open (<code>isOpen_sUnion</code>). Aigner and Ziegler take care of this through <span class="quote proof">“Clearly, the union of open sets is open again”</span>, but we don’t have that luxury. Using <code>by_cases</code>, we need to distinguish whether all sets are empty (which is take care of easily) or if there exists some nonempty <code>U₀</code> from which we can take the set \(N_{a,b}\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
</pre></td>
  <td class="code"><pre>  isOpen_sUnion U := by
    simp; intro sets_open
    by_cases h: ∀ s ∈ U, s = ∅
    · exact Or.inl h
    · rw [or_iff_right h]
      intro a U₀ U₀_in_U a_in_U₀
      specialize sets_open U₀ U₀_in_U
      simp [or_iff_right (Set.Nonempty.ne_empty (Exists.intro a a_in_U₀))] at sets_open
      obtain ⟨b, b_ne_zero, N_contained⟩ := sets_open a a_in_U₀
      exact ⟨b, b_ne_zero, λ _ a ↦ Set.subset_sUnion_of_mem U₀_in_U (N_contained a)⟩
</pre></td>
</tr></table>
</div>

<p>Next, a topological space requires that any intersection of two open sets is again open (<code>isOpen_inte</code>). Aigner and Ziegler state the following: <span class="quote proof">“If \(O_1, O_2\) are open, and \(a \in O_1 \cap O_2\) with \(N_{a,b_1} \subseteq O_1\) and \(N_{a,b_2} \subseteq O_2\), then \(a \in N_{a, b_1 b_2}\).”</span> The case where the intersection is empty was again skipped, but otherwise we proceed very much the same way by using the set \(N_{a, b_1 \, b_2}\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
</pre></td>
  <td class="code"><pre>  isOpen_inter O₁ O₂ h₁ h₂ := by
    simp at *
    by_cases h: O₁ ∩ O₂ = ∅
    · exact Or.inl h
    · simp [h]
      intro a a_in_O₁ a_in_O₂
      simp [Set.Nonempty.ne_empty (Exists.intro a a_in_O₁)] at h₁
      simp [Set.Nonempty.ne_empty (Exists.intro a a_in_O₂)] at h₂
      obtain ⟨b₁, b₁_ne_zero, N₁_contained⟩ := h₁ a a_in_O₁
      obtain ⟨b₂, b₂_ne_zero, N₂_contained⟩ := h₂ a a_in_O₂
      use b₁ * b₂
      have Nab1b2_su_Nab1 := N_sub a b₁ b₂
      have Nab1b2_su_Nab2 := N_sub a b₂ b₁
      rw [mul_comm] at Nab1b2_su_Nab2
      exact ⟨mul_ne_zero b₁_ne_zero b₂_ne_zero, λ _ a =&gt; N₁_contained (Nab1b2_su_Nab1 a), λ _ a =&gt; N₂_contained (Nab1b2_su_Nab2 a)⟩
</pre></td>
</tr></table>
</div>

<p>Before continuing, let es establish another trivial little fact about \(N_{a,b}\) likewise used implicitly later on, namely that it is obviously open (<code>N_open</code>).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>lemma N_open (a b : ℤ) (h : b ≠ 0): IsOpen (N a b) := by 
  simp [IsOpen, TopologicalSpace.IsOpen, N]
  apply Or.inr
  exact λ a' ↦ (by use b; exact ⟨h, λ a'' ↦ by use a' + a''; ring_nf⟩)    
</pre></td>
</tr></table>
</div>

<p>We are now ready to proceed with the second part of the proof.</p>

<div class="statement proof">


    <strong>Proof (Cont'd).</strong> 



Let us note two facts:

<ul>
<li>(A) Any nonempty open set is infinite.</li>
<li>(B) Any set &#92;(N_{a,b}&#92;) is closed as well.</li>
</ul>

Indeed, the first fact follows from the definition. For the second we observe &#92;[N_{a,b} = &#92;mathbb{Z} &#92;setminus &#92;bigcup_{i=1}^{b-1} N_{a+i,b},&#92;] which proves that &#92;(N_{a,b}&#92;) is the complement of an open set and hence closed.


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 5.
    
    </div>

</div>

<p>We will have to be slightly more explicit than <span class="quote proof">“the first fact follows from the definition”</span> when establishing our proof of (A), but we can still keep it reasonably short. Note that <code>Set.Nonempty O</code> is generally prefered over <code>O ≠ ∅</code>, but we’ll keep the later notation to make the code slightly nicer to read.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
</pre></td>
  <td class="code"><pre>lemma A (O : Set ℤ) (h₁ : IsOpen O) (h₂ : O ≠ ∅) : O.Infinite := by
  simp [IsOpen, TopologicalSpace.IsOpen, h₂] at h₁
  obtain ⟨a, a_in_O⟩ := (by push_neg at h₂; exact h₂)
  obtain ⟨b, b_ne_zero, N_su_O⟩ := h₁ a a_in_O
  exact Set.Infinite.mono N_su_O (N_infinite a b b_ne_zero)
</pre></td>
</tr></table>
</div>

<p>Regarding statement (B), we first <span class="quote proof">“observe \(N_{a,b} = \mathbb{Z} \setminus \bigcup_{i=1}^{b-1} N_{a+i,b}\) …”</span> in the form of <code>N_eq_Ubc</code>; this requires a surprising amount of work. Using <code>compl_unique</code> we split the argument into two parts, first establishing that \(N_{a,b}\) does not intersect \(\bigcup_{i=1}^{b-1} N_{a+i,b}\) (<code>N_disjoint</code>) and then that together they make up \(\mathbb{Z}\) (<code>N_covers</code>).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
<a href="#n16" name="n16">16</a>
<a href="#n17" name="n17">17</a>
<a href="#n18" name="n18">18</a>
<a href="#n19" name="n19">19</a>
<strong><a href="#n20" name="n20">20</a></strong>
<a href="#n21" name="n21">21</a>
<a href="#n22" name="n22">22</a>
<a href="#n23" name="n23">23</a>
<a href="#n24" name="n24">24</a>
<strong><a href="#n25" name="n25">25</a></strong>
<a href="#n26" name="n26">26</a>
<a href="#n27" name="n27">27</a>
<a href="#n28" name="n28">28</a>
<a href="#n29" name="n29">29</a>
<strong><a href="#n30" name="n30">30</a></strong>
<a href="#n31" name="n31">31</a>
<a href="#n32" name="n32">32</a>
<a href="#n33" name="n33">33</a>
</pre></td>
  <td class="code"><pre>lemma B (a : ℤ) (b : ℕ) (h : b &gt; 0) : IsClosed (N a b) := by
  let Ub := (⋃ i : {x : ℕ // 1 ≤ x ∧ x &lt; b}, N (a + i) b)

  have N_eq_Ubc : N a b = Ubᶜ := by

    have N_disjoint : Ub ∩ N a b = ∅ := by
      rw [Set.iUnion_inter]; simp
      intro idx idx_lw idx_up
      apply Set.disjoint_iff_inter_eq_empty.mp
      apply Set.disjoint_right.mpr
      intro n b_dvd b_dvd'
      simp [el_N_of_dvd] at b_dvd b_dvd'
      have b_dvd_idx : b ∣ idx := by have := Int.dvd_sub b_dvd' b_dvd; simp [Int.ofNat_dvd] at this; exact this
      exact (Nat.not_dvd_of_pos_of_lt idx_lw idx_up) b_dvd_idx

    have N_covers : Ub ∪ N a b = Set.univ := by
      apply Set.Subset.antisymm_iff.mpr
      constructor
      · exact λ _ _ ↦ trivial
      · intro n _
        simp [el_N_of_dvd, Ub]
        let i := (n - a) % (b : ℤ)
        by_cases i_cases : i = 0
        · exact Or.inr (Int.modEq_zero_iff_dvd.mp (i_cases))
        · apply Or.inl
          use Int.toNat i
          have i_ge_0 : i ≥ 0 := Int.emod_nonneg (n - a) (Int.coe_nat_ne_zero_iff_pos.mpr h)
          have nat_i_ge_0 := Int.lt_toNat.mpr (Ne.lt_of_le' i_cases i_ge_0)
          have nat_i_lt_b := (Int.toNat_lt i_ge_0).mpr (Int.emod_lt_of_pos (n - a) (Int.ofNat_pos.mpr h))
          have minus_i_dvd: (b : ℤ) ∣ (n - (a + i)) := Int.sub_sub n a i ▸ Int.dvd_sub_of_emod_eq rfl
          exact ⟨ ⟨nat_i_ge_0, nat_i_lt_b⟩, Int.toNat_of_nonneg i_ge_0 ▸ minus_i_dvd⟩

    exact (compl_unique N_disjoint N_covers).symm
</pre></td>
</tr></table>
</div>

<p>Using this, the rest of the proof is straightforward and in fact almost shorter than stating that the just established <code>N_eq_Ubc</code> <span class="quote proof">“proves that \(N_{a,b}\) is the complement of an open set and hence closed.”</span></p>

<div class="language-plaintext highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
</pre></td>
  <td class="code"><pre>  rw [N_eq_Ubc]
  apply isClosed_compl_iff.mpr
  apply isOpen_iUnion
  exact λ i ↦ N_open (a + i) b (Int.coe_nat_ne_zero_iff_pos.mpr h)
</pre></td>
</tr></table>
</div>

<p>Finally, let’s look at the actual proof of the infinitude of primes using what we just established.</p>

<div class="statement proof">


    <strong>Proof (Cont'd).</strong> 



So far the primes have not yet entered the picture - but here they come. Since any number &#92;(n  &#92;ne 1, −1&#92;) has a prime divisor &#92;(p&#92;), and hence is contained in &#92;(N_{0,p}&#92;), we conclude &#92;[&#92;mathbb{Z} &#92;setminus &#92;{1, -1&#92;} = &#92;bigcup_{p &#92;in &#92;mathbb{P}} N_{0,p}.&#92;] Now if &#92;(&#92;mathbb{P}&#92;) were finite, then &#92;(&#92;bigcup_{p &#92;in &#92;mathbb{P}} N_{0,p}&#92;) would be a finite union of closed sets (by (B)), and hence closed. Consequently, &#92;(&#92;{1, −1&#92;}&#92;) would be an open set, in violation of (A).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 5.
    
    </div>

</div>

<p>The main goal is to establish that <span class="quote proof">“\(\mathbb{Z} \setminus \{1, -1\} = \bigcup_{p \in \mathbb{P}} N_{0,p}\)”</span>, which we do through <code>Z_as_U</code>. The <code>constructor</code> splits the equality of the two sets up into establishing that each is contained in the other. For the direction \(\subseteq\), <span class="quote proof">“any number \(n  \ne 1, −1\) has a prime divisor \(p\)”</span> is taken care of by <code>Nat.exists_prime_and_dvd</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
<strong><a href="#n10" name="n10">10</a></strong>
<a href="#n11" name="n11">11</a>
<a href="#n12" name="n12">12</a>
<a href="#n13" name="n13">13</a>
<a href="#n14" name="n14">14</a>
<strong><a href="#n15" name="n15">15</a></strong>
<a href="#n16" name="n16">16</a>
</pre></td>
  <td class="code"><pre>theorem infinitude_primes : { p : ℕ | Nat.Prime p }.Infinite := by
  have Z_as_U : {1, -1}ᶜ = (⋃ p ∈ {p : ℕ | Nat.Prime p}, N 0 p) := by
    apply Set.ext; intro n

    constructor &lt;;&gt; intro n_el
    · obtain ⟨p, p_prime, p_dvd_n⟩ := Nat.exists_prime_and_dvd (λ c ↦ n_el (Int.natAbs_eq_iff.mp c))
      have n_in_Np : n ∈ N 0 p := by simp [el_N_of_dvd]; exact Int.ofNat_dvd_left.mpr p_dvd_n
      simp; exact ⟨p, p_prime, n_in_Np⟩

    · simp [el_N_of_dvd] at *
      obtain ⟨p, p_prime, p_dvd_n⟩ := n_el
      have p_ndvd_one := Int.ofNat_dvd.not.mpr (Prime.not_dvd_one (Nat.prime_iff.mp p_prime))
      push_neg; constructor
      all_goals {by_contra c; rw [c, ←Nat.cast_one] at p_dvd_n ; simp [Int.dvd_neg.mpr] at p_dvd_n; exact p_ndvd_one p_dvd_n}

  by_contra h
</pre></td>
</tr></table>
</div>

<p>Having established <code>Z_as_U</code>, the rest of the proof is a rather neat proof by contradiction. <span class="quote proof">“if \(\mathbb{P}\) were finite, then \(\bigcup_{p \in \mathbb{P}} N_{0,p}\) would be a finite union of closed sets (by (B)), and hence closed”</span> is taken care of by <code>U_close</code> and  <span class="quote proof">“\(\{1, −1\}\) would be an open set”</span> by <code>one_open</code>. The violation of (A) comes in the form of <code>one_infinite</code>, which states that \(\{1, -1\}\) would need to be infinite, which is obviously not true.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
<a href="#n9" name="n9">9</a>
</pre></td>
  <td class="code"><pre>  by_contra h
  
  have U_close : IsClosed (⋃ p ∈ {p : ℕ | Nat.Prime p}, N 0 p) := by
    have (p : ℕ) (p_prime : p ∈ {p : ℕ | Nat.Prime p}) : IsClosed (N 0 p) := by simp at p_prime; exact B 0 p (Nat.Prime.pos p_prime)
    exact Set.Finite.isClosed_biUnion (Set.not_infinite.mp h) this
  have one_open : IsOpen {1, -1} := isClosed_compl_iff.mp (Z_as_U ▸ U_close)
  have one_infinite := A {1, -1} one_open (Set.Nonempty.ne_empty (Set.insert_nonempty 1 {-1}))

  exact one_infinite (Set.toFinite {1, -1})
</pre></td>
</tr></table>
</div>

<p>This concludes the proof. 🎉</p>

<!-- [^1]: Note that for Lean &#92;(0 &#92;mid 0&#92;). -->
<!-- [^2]: Note that for the direction &#92;(&#92;supseteq&#92;) another `constructor` occurs where we can rather neatly argue both cases in parallel using `<;>`. -->]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Spiegel&quot;, &quot;first_name&quot;=&gt;&quot;Christoph&quot;, &quot;portrait&quot;=&gt;&quot;/assets/portraits/christophspiegel.jpg&quot;, &quot;homepage&quot;=&gt;&quot;https://christophspiegel.berlin&quot;, &quot;bio&quot;=&gt;&quot;I&apos;m a postdoc at Zuse Institute Berlin and lecturer at Technical University of Berlin, focusing on using computational tools in theoretical mathematics. Recently, I&apos;ve used flag algebras, semidefinite programming, and discrete optimization to enhance bounds in extremal combinatorics. Currently, I&apos;m exploring proof verification and assistance.&quot;}</name></author><category term="[&quot;Number Theory&quot;]" /><summary type="html"><![CDATA[Let us have a look at Fürstenberg’s proof of the infinitude of primes. This is a fun one, deriving a contradiction from the assumption that there are only finitely many primes by defining a topology on the integers. The proof as written down by Aigner and Ziegler is slightly longer, so let us split it up into three parts.]]></summary></entry><entry><title type="html">Infinitude of Primes - Folklore proof</title><link href="https://thebook.zib.de/number%20theory/2024/03/08/primes-infinitude-folklore.html" rel="alternate" type="text/html" title="Infinitude of Primes - Folklore proof" /><published>2024-03-08T07:00:00+01:00</published><updated>2024-03-08T07:00:00+01:00</updated><id>https://thebook.zib.de/number%20theory/2024/03/08/primes-infinitude-folklore</id><content type="html" xml:base="https://thebook.zib.de/number%20theory/2024/03/08/primes-infinitude-folklore.html"><![CDATA[<p>The next proof takes an algebraic turn, deriving a contradiction from the assumption that there exists a largest prime through Lagrange’s theorem.</p>

<div class="statement proof">


    <strong>Proof.</strong> 



Suppose &#92;(&#92;mathbb{P}&#92;) is finite and &#92;(p&#92;) is the largest prime. We consider the so-called <i>Mersenne number</i> &#92;(2^p - 1&#92;) and show that any prime factor &#92;(q&#92;) of &#92;(2^p - 1&#92;) is bigger than &#92;(p&#92;), which will yield the desired conclusion. Let &#92;(q&#92;) be a prime dividing &#92;(2^p - 1&#92;), so we have &#92;(2^p &#92;equiv 1 &#92;: (&#92;operatorname{mod} &#92;, q)&#92;). Since &#92;(p&#92;) is prime, this means that the element &#92;(2&#92;) has order &#92;(p&#92;) in the multiplicative group &#92;(&#92;mathbb{Z}_q &#92;setminus &#92;{0&#92;}&#92;) of the field &#92;(&#92;mathbb{Z}_q&#92;). This group has &#92;(q-1&#92;) elements. By Lagrange's theorem [...] we know that the order of every element divides the size of the group, that is, we have &#92;(p &#92;mid q-1&#92;) and hence &#92;(p &lt; q&#92;).


<div style="margin-top: 7px; font-size: 9pt; font-style: italic; text-align: right;">
    
    
        Aigner, M., &amp; Ziegler, G. M. (5th ed.). Proofs from THE BOOK, p. 4.
    
    </div>

</div>

<p>The sentence <span class="quote proof">“consider the so-called Mersenne number \(2^p - 1\) and show that any prime factor \(q\) of \(2^p - 1\) is bigger than \(p\)”</span> indicates that we first establish the following lemma, from which the actual infinitude of primes will later be derived.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>lemma mersenne_prime_dvd_gt (p q : ℕ) [p_prime: Fact (Nat.Prime p)] [q_prime : Fact (Nat.Prime q)] (h : q ∣ 2^p - 1) : p &lt; q := by
</pre></td>
</tr></table>
</div>

<p>Note that <code>Fact</code> is simply a neat way of letting Lean infer an assumption when applying a statement, so that you can just call <code>some_theorem p</code> instead of <code>some_theorem p p_prime</code>. We will use several statements that will automatically infer the info that <code>p</code> and <code>q</code> are prime, but we will also encounter statements that want to be given this info explicitly, in which case we simply turn the <code>Fact</code> back into its underlying assumption through <code>p_prime.out</code> or, more explicitly, <code>Fact.out p_prime</code>. In order to create a fact from an assumption, one can use <code>Fact.mk</code>.</p>

<p>The assumptions <code>[q_prime: Fact (Nat.Prime q)]</code> and <code>(h : q ∣ 2^p - 1)</code> express <span class="quote proof">“(l)et \(q\) be a prime dividing \(2^p - 1\)”</span> and the consequence <span class="quote proof">“we have \(2^p \equiv 1 \: (\operatorname{mod} \, q)\)”</span> follows from <code>Nat.modEq_iff_dvd'</code>. The <code>Nat</code> indicates this lemma applies to natural numbers (rather than integers) and indeed the expression <code>2^p - 1</code> also referred to subtraction in the natural numbers that saturates at zero. Thankfully this is not a problem, since \(2^p \geq 1\) due to <code>Nat.one_le_two_pow</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  have two_pow_p_eq_one_mod_q : 2^p ≡ 1 [MOD q] := ((λ x ↦ (Nat.modEq_iff_dvd' x).mpr) (Nat.one_le_two_pow) h).symm
</pre></td>
</tr></table>
</div>

<p>Next up, the proof states that <span class="quote proof">“(s)ince \(p\) is prime, this means that the element \(2\) has order \(p\) in the multiplicative group \(\mathbb{Z}_q \setminus \{0\}\) of the field \(\mathbb{Z}_q\).”</span> Fortunately, the <code>orderOf_eq_prime</code> lemma in mathlib directly supports this argument.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  have two_pow_p_eq_one := ((ZMod.eq_iff_modEq_nat q).mpr two_pow_p_eq_one_mod_q)
  rw [Nat.cast_pow, Nat.cast_one] at two_pow_p_eq_one
  have order_two_eq_p := orderOf_eq_prime two_pow_p_eq_one (by apply sub_eq_zero.not.mp; norm_num)
</pre></td>
</tr></table>
</div>

<p>There are some nuances here: the first is that <code>orderOf_eq_prime</code> also requires a proof that  \(2 \ne 1\) in \(\mathbb{Z}_q\), which we take care of through <code>(by apply sub_eq_zero.not.mp; norm_num)</code>. The second is that <code>two_pow_p_eq_one_mod_q</code> was a statement about natural numbers <code>2</code> and <code>1</code>, but we need a statement that \(2^p = 1\) in \(\mathbb{Z}_q\). <code>ZMod.eq_iff_modEq_nat</code> takes us most of the way there, but it takes <code>2^p</code> as an integer and casts it in its entirety to  \(\mathbb{Z}_q\). This is expressed in the Infoview through <code>↑(2 ^ p)</code>. However, what we actually want is the <code>p</code>-th exponent of the element <code>2</code> in \(\mathbb{Z}_q\). Rewriting with <code>Nat.cast_pow</code> gives us exactly that and turns the <code>↑(2 ^ p)</code> into an <code>↑2 ^ p</code>. However, one tiny issue remains: mathlib right now does not understand that <code>↑1</code> is also the multiplicative unit of \(\mathbb{Z}_q\). This important for <code>orderOf_eq_prime</code>, so we rewrite <code>↑1</code> to <code>1</code> using <code>Nat.cast_one</code>.</p>

<p>The proof continues by stating that <span class="quote proof">“(t)his group has \(q-1\) elements.”</span> and <span class="quote proof">“(b)y Lagrange’s theorem […] we know that the order of every element divides the size of the group, that is, we have \(p \mid q-1\)”</span>. Thankfully we do not have to go all the way to Lagrange’s theorem, since there is a lemma in mathlib in the form of <code>ZMod.orderOf_dvd_card_sub_one</code> stating this very fact.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> We just again need to supply it with a small, easily overlooked detail: we must have \(2 \ne 0\) in \(\mathbb{Z}_q\) (<code>two_ne_zero</code>). This is of course the case as long as \(q \ne 2\), which somewhat cumbersomely derive from <code>h : q ∣ 2^p - 1</code>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
</pre></td>
  <td class="code"><pre>  have two_ne_zero : (2 : ZMod q) ≠ 0 := by
    by_contra contra
    have q_lq_two : q ≤ 2 := (Nat.Prime.dvd_factorial q_prime.out).mp ((ZMod.nat_cast_zmod_eq_zero_iff_dvd 2 q).mp contra)
    have q_gt_two : q &gt; 2 := Nat.lt_of_le_of_ne (Nat.Prime.two_le q_prime.out) (Odd.ne_two_of_dvd_nat (Nat.Even.sub_odd (Nat.one_le_two_pow) ( even_iff_two_dvd.mpr (dvd_pow_self 2 (Nat.Prime.ne_zero p_prime.out))) (Exists.intro Nat.zero rfl)) h).symm
    exact (Nat.not_lt.mpr q_lq_two) q_gt_two
    
  have p_dvd_q_minus_1 : p ∣ (q - 1) := order_two_eq_p ▸ (ZMod.orderOf_dvd_card_sub_one two_ne_zero)
</pre></td>
</tr></table>
</div>

<p>Finally, we conclude the proof of our main lemma by deriving that <span class="quote proof">“\(p &lt; q\)”</span>.</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
  <td class="code"><pre>  exact Nat.lt_of_le_pred (Nat.Prime.pos q_prime.out) (Nat.le_of_dvd (Nat.sub_pos_of_lt (Nat.Prime.two_le q_prime.out)) p_dvd_q_minus_1)
</pre></td>
</tr></table>
</div>

<p>This however does not conclude the actual proof of the infinitude of primes. The proof simply suggests we <span class="quote proof">“(s)uppose \(\mathbb{P}\) is finite and \(p\) is the largest prime”</span> and use the previous lemma to derive a contradiction. Let us do just that! We start by defining <code>P</code> as the set of primes and supposing that it is finite using <code>by_contra</code>, which allows us to define <code>p</code> as its maximum</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
<a href="#n4" name="n4">4</a>
<strong><a href="#n5" name="n5">5</a></strong>
<a href="#n6" name="n6">6</a>
<a href="#n7" name="n7">7</a>
<a href="#n8" name="n8">8</a>
</pre></td>
  <td class="code"><pre>theorem infinitude_of_primes : { p : ℕ | Nat.Prime p}.Infinite := by
  by_contra contra
  have : Fintype { p : ℕ | Nat.Prime p} := Set.Finite.fintype (Set.not_infinite.mp contra)
  let P := { p : ℕ | Nat.Prime p}.toFinset
  let P_nonenmpty : P.Nonempty := (Set.nonempty_of_mem (Set.mem_toFinset.mpr Nat.prime_two))
  let p := Finset.max' P P_nonenmpty
  have p_in_P : p ∈ P := Finset.max'_mem P P_nonenmpty
  have p_prime : Nat.Prime p := by simp [P] at p_in_P; exact p_in_P
</pre></td>
</tr></table>
</div>

<p>Using this, we let \(q\) be an arbitrary prime divisor of \(2^p-1\), after establishing that \(2^p - 1 \neq 1\) (<code>two_pow_p_minus_one_ne_one</code>), and apply <code>mersenne_prime_dvd_gt</code> to obtain the fact that \(q &gt; p\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
<a href="#n3" name="n3">3</a>
</pre></td>
  <td class="code"><pre>  have two_pow_p_minus_one_ne_one : 2^p - 1 ≠ 1 := Nat.ne_of_gt (Nat.lt_sub_of_add_lt ((@Nat.size_le 2 p).mp (Nat.Prime.two_le p_prime)))
  let ⟨q, q_prime, q_dvd⟩ := Nat.exists_prime_and_dvd two_pow_p_minus_one_ne_one
  have q_gt_p : q &gt; p := @mersenne_prime_dvd_gt p q (Fact.mk p_prime) (Fact.mk q_prime) q_dvd
</pre></td>
</tr></table>
</div>

<p>But this is of course a contradiction, since \(p\) was the largest prime and \(q\) is prime, so we must also have \(q \leq p\).</p>

<div class="language-lean highlighter-coderay"><table class="CodeRay"><tr>
  <td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
</pre></td>
  <td class="code"><pre>  have q_lt_p : q ≤ p := Finset.le_max' P q (Set.mem_toFinset.mpr q_prime)
  exact Nat.lt_irrefl p (Nat.lt_of_lt_of_le q_gt_p q_lt_p)
</pre></td>
</tr></table>
</div>

<p>This concludes the proof. 🎉</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>If this feels like cheating, check out <a href="https://github.com/mo271/formal_book/blob/main/FormalBook/Ch01_Six_proofs_of_the_infinity_of_primes.lean">Moritz Firsching’s version of this proof</a> which consciously uses mathlib’s version of Lagrange’s theorem. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>{&quot;last_name&quot;=&gt;&quot;Spiegel&quot;, &quot;first_name&quot;=&gt;&quot;Christoph&quot;, &quot;portrait&quot;=&gt;&quot;/assets/portraits/christophspiegel.jpg&quot;, &quot;homepage&quot;=&gt;&quot;https://christophspiegel.berlin&quot;, &quot;bio&quot;=&gt;&quot;I&apos;m a postdoc at Zuse Institute Berlin and lecturer at Technical University of Berlin, focusing on using computational tools in theoretical mathematics. Recently, I&apos;ve used flag algebras, semidefinite programming, and discrete optimization to enhance bounds in extremal combinatorics. Currently, I&apos;m exploring proof verification and assistance.&quot;}</name></author><category term="[&quot;Number Theory&quot;]" /><summary type="html"><![CDATA[The next proof takes an algebraic turn, deriving a contradiction from the assumption that there exists a largest prime through Lagrange’s theorem.]]></summary></entry></feed>