Lean série 02: Ukázka

Syntetická euklidovská geometrie

Dle slibu v úvodu si teď ukážeme konkrétní příklady. Tedy aspoň tak konkrétní, jak to jen v syntetické euklidovské geometrii jde – zapomeňte na souřadnice bodů a vaše oblíbené analytické výpočty. Následují zjednodušené úryvky z dizertace mého kamaráda Andrého, která formalizuje Euklidovy Základy (mimochodem, takhle jsem se poprvé dostal k Leanu a zpětně mi to připadá jako docela přístupná cesta).

(Poznámka: budeme pracovat v Leanu 4, takže neváhejte si vše zkusit online nebo lokálně. A nezapomeňte, že po umístění kurzoru do kódu můžete vidět aktuální stav v infoview!)

import Mathlib.Data.Real.Basic

universe u1 u2 u3
class incidence_geometry :=
(point : Type u1)
(line : Type u2)
(circle : Type u3)

(length : point → point → ℝ)
(B : point → point → point → Prop) -- Betweenness

(length_nonneg : ∀ (a b : point), 0 ≤ length a b)
(length_symm : ∀ (a b : point), length a b = length b a)
(length_eq_zero_iff : ∀ {a b : point}, length a b = 0 ↔ a = b)
(length_sum_of_B : ∀ {a b c : point},
   B a b c → length a b + length b c = length a c)
(ne_12_of_B : ∀ {a b c : point}, B a b c → a ≠ b )
(ne_13_of_B : ∀ {a b c : point}, B a b c → a ≠ c )
(ne_23_of_B : ∀ {a b c : point}, B a b c → b ≠ c )

variable [i : incidence_geometry]
open incidence_geometry

Toto je minimální příprava, s detaily si teď moc nelamme hlavu. V podstatě deklarujeme, že budeme pracovat s body, přímkami a kružnicemi, přičemž budeme mít funkci, která měří délku dvou bodů a predikát, který říká, zda tři body leží na jedné přímce v daném pořadí. (Všimli jste si, že nikde neříkáme, co tyto objekty jsou? To je právě ten syntetický přístup.) Máme také pár axiomů o těchto funkcích – ne úplně ty, které navrhl Euclid, ale zatím budou stačit.

Pojďme se nyní podívat na první lemma!

lemma len_pos_of_neq (ab : a ≠ b) : 0 < length a b := by sorry

Závorky za názvem lemmatu obsahují pojmenované vstupní předpoklady, zatímco tvrzení za dvojtečkou je přesně to, co se snažíme dokázat (říkejme mu cíl; můžeme ho také vidět v infoview za symbolem ). (Pokud chcete, můžete si lemmata a věty představit jako funkce se vstupy a výstupy oddělenými dvojtečkou.) Po := by měl následovat důkaz; klíčové slovo by nás přepne do taktického režimu (o něm více později) a sorry znamená, že se nám důkaz zatím nepodařilo dokončit důkaz a prosíme kompilátor o trpělivost. (sorry ve skutečnosti zavádí další axiom, takže bychom ho měli používat jen v nedokončených projektech; jinak riskujeme, že v naší teorii dojdeme ke sporu.) Pojďme to napravit:

lemma len_pos_of_neq (ab : a ≠ b) : 0 < length a b :=
  Ne.lt_of_le' (length_eq_zero_iff.not.mpr ab) (length_nonneg a b)

Vyrazilo vám toto tvrzení dech? No…asi ne, je to jen prostý důsledek axiomů o délce. Důkaz možná také nevypadá příliš poučně, ale tím se nenechme nezpomalit.

Modulární design

Teď jste možná trochu zmatení a/nebo demotivovaní. Neměli jsme náhodou dělat opravdovou matematiku? Jde o to, že většina lemmat jsou jen malé stavební kameny, jejichž hodnota se ukáže až ve větším kontextu (trochu to připomíná modulární filozofii Linuxu – každá funkce by měla dělat jen jednu věc, zato pořádně). Jinak řečeno:

Lemma 1 (Euclid-Hernández-Espiet) Každý důkaz se stane zvládnutelným, pokud na něj zaútočíte pomocí dlouhé řady lemmat fungujících jako API – bez ohledu na to, jak malá nebo triviální se zdají.

Lemma 2 ↑Kluci mají pravdu.

Lemma 3 V tom případě…Lemma 1 implikuje Lemma 2.

Lemma 4 Ve skutečnosti jsou ekvivalentní.

Asi netřeba pokračovat. Důkazy takových tvrzení však nemusí být vůbec jednoduché a jejich výstižné zachycení je umění samo o sobě. Zvažme následující příklad:

theorem length_sum_perm_of_B' (Babc : B a b c) :
      0 < length a b ∧ 0 < length b a := by
   have ab : a ≠ b := ne_12_of_B Babc
   have ab_pos : 0 < length a b := len_pos_of_neq ab
   constructor
   exact ab_pos
   rw [length_symm] at ab_pos
   exact ab_pos

Snad aspoň zhruba vidíte, co se tu děje (mrkněte na infoview). Stručně:

  • klíčové slovo have zavádí do našeho kontextu další hypotézu na základě těch předchozích,
  • constructor rozděluje konjunkci v cíli na dva samostatné cíle,
  • exact uzavírá aktuální cíl pomocí předpokladu, který už máme k dispozici,
  • rw přepisuje rovnost vycházející z dřívějšího lemmatu uvnitř tvrzení v našem kontextu.

Zde je ekvivalentní jednořádkový důkaz (za pomocí |> pro řetězení, pro konverzi typu a ⟨⟩ pro tvorbu složených výrazů):

theorem length_sum_perm_of_B'' (Babc : B a b c) :
      0 < length a b ∧ 0 < length b a :=
   ⟨ ne_12_of_B Babc |> len_pos_of_neq,
     ne_12_of_B Babc |> length_symm a b ▸ len_pos_of_neq⟩

A ještě kratší verze, pomocí mocného zjednodušovače simp:

theorem length_sum_perm_of_B''' (Babc : B a b c) :
      0 < length a b ∧ 0 < length b a := by
   simp [length_symm b, Babc.length_pos]

První důkaz nabízí detaily čitelné pro člověka a druhý sestrojuje důkazové členy přímo, ale ten poslední je v mnoha kontextech preferovaný pro svou jednoduchost, protože můžeme nízkoúrovňové detaily ignorovat. Ale ani to nemusí stačit – co když je tvrzení věty mnohem delší?

theorem length_sum_perm_of_B (Babc : B a b c) :
      0 < length a b ∧ 0 < length b a ∧ 0 < length b c ∧
      0 < length c b ∧ 0 < length a c ∧ 0 < length c a ∧
      length a b + length b c = length a c ∧
      length b a + length b c = length a c ∧
      length b a + length c b = length a c ∧
      length b a + length b c = length c a ∧
      length b a + length c b = length c a ∧
      length a b + length c b = length a c ∧
      length a b + length b c = length c a ∧
      length a b + length c b = length c a
   := by sorry

(Pokud se vám to nezdá – nejedná se o vymyšlený příklad, ale o skutečný problém, který bylo třeba v projektu vyřešit.) I když tu není žádný skutečný matematický obsah, napsat důkaz pouze pomocí dosud zmíněných metod by bylo velmi zdlouhavé. (Oprava: pomocí simp to vlastně jde celkem snadno, zkuste si to jako cvičení.). Raději použijeme mocné taktiky – přístup Leanu k automatizaci. Více o tom, jak je vytvořit, v příštím díle!

Pokud máte nějaké návrhy na vylepšení tohoto blogového příspěvku, dejte mi prosím vědět.