Provas estruturadas, provas calculacionais e o princípio PAT
Christiano Braga · Mestrado em Sistemas e Computação · IME
Baseado no Hitchhiker's Guide to Logical Verification (LoVe), capítulo 4.
Uma prova progressiva parte das hipóteses e deriva novos fatos até alcançar o objetivo. A sua frase é "de … temos …", o espelho do "basta provar" da Aula 4.
Regressiva, a partir do objetivo
para provar c, por hbc basta provar b; para provar b, por hab basta provar a; e ha prova a.
Progressiva, a partir das hipóteses
de ha e hab, temos b; de b e hbc, temos c.
Uma derivação em dedução natural admite as duas leituras; esta aula escreve a progressiva, como um termo estruturado.
Proposições como tipos, provas como termos. Uma proposição é um tipo, e uma prova dela é um termo desse tipo. Uma implicação a → b é o tipo das funções de provas de a em provas de b, então a prova de uma implicação é uma função, e ∀ x, P x é um tipo de função dependente.
fun a h => h : ∀ (a : Prop), a → a#check (fun (a : Prop) (h : a) => h)
fun h => h : True → True#check (assume h : True; h)
fun a h => h : ∀ (a : Prop), a → afun h => h : True → True
fix e assume, da biblioteca da LoVe, são os analisadores de termos que expandem para fun.
fix x : α descarrega um ∀; assume h : P descarrega uma →; have h : P := pf nomeia um fato progressivo; show P from pf reafirma o objetivo.
namespace Forward
theorem fst_of_two_props :
∀ a b : Prop, a → b → a :=
fix a b : Prop;
assume ha : a;
assume hb : b;
show a from ha
end Forward
O mesmo termo, de três modos
namespace Forward
example : ∀ a b : Prop, a → b → a :=
fix a b : Prop;
assume ha : a; assume hb : b; ha
example : ∀ a b : Prop, a → b → a :=
fun a b ha hb => ha
end Forward
A composição de implicações, progressiva como dois passos have e regressiva como o roteiro da Aula 4.
namespace Forward
theorem prop_comp (a b c : Prop)
(hab : a → b) (hbc : b → c) :
a → c :=
assume ha : a;
have hb : b := hab ha;
show c from hbc hb
end Forward
namespace Forward
example (a b c : Prop)
(hab : a → b) (hbc : b → c) :
a → c := a:Propb:Propc:Prophab:a → bhbc:b → c⊢ a → c
a:Propb:Propc:Prophab:a → bhbc:b → cha:a⊢ c
a:Propb:Propc:Prophab:a → bhbc:b → cha:a⊢ b
a:Propb:Propc:Prophab:a → bhbc:b → cha:a⊢ a
All goals completed! 🐙
end Forward
De ha e hab temos b; de b e hbc temos c.
Regras de eliminação desmontam uma hipótese; regras de introdução e o construtor anônimo constroem o objetivo.
namespace Forward
theorem And_swap (a b : Prop) :
a ∧ b → b ∧ a :=
assume h : a ∧ b;
have ha : a := And.left h;
have hb : b := And.right h;
show b ∧ a from
And.intro hb ha
end Forward
namespace Forward
theorem Or_swap (a b : Prop) :
a ∨ b → b ∨ a :=
assume h : a ∨ b;
Or.elim h
(fun ha => Or.inr ha)
(fun hb => Or.inl hb)
end Forward
Exists.intro t pf fornece uma testemunha; Exists.elim h f nomeia uma; Iff.mp e Iff.mpr aplicam uma equivalência em cada direção.
namespace Forward
example (P : ℕ → Prop) (h : P 3) :
∃ n, P n :=
Exists.intro 3 h
example (α : Type) (P : α → Prop)
(Q : Prop) (h : ∃ x, P x)
(f : ∀ x, P x → Q) : Q :=
Exists.elim h f
end Forward
namespace Forward
example (a b : Prop)
(h : a ↔ b) (ha : a) : b :=
Iff.mp h ha
example (a b : Prop)
(h : a ↔ b) (hb : b) : a :=
Iff.mpr h hb
end Forward
O ápice. Um quantificador cuja variável ligada é presa por uma igualdade colapsa a uma única instância.
namespace Forward
theorem Forall_one_point (α : Type)
(t : α) (P : α → Prop) :
(∀ x, x = t → P x) ↔ P t :=
Iff.intro
(assume h : ∀ x, x = t → P x;
h t rfl)
(assume hpt : P t;
fix x : α;
assume hxt : x = t;
hxt ▸ hpt)
end Forward
namespace Forward
theorem Exists_one_point (α : Type)
(t : α) (P : α → Prop) :
(∃ x, x = t ∧ P x) ↔ P t :=
Iff.intro
(assume h : ∃ x, x = t ∧ P x;
Exists.elim h
(fun x hx => hx.1 ▸ hx.2))
(assume hpt : P t;
Exists.intro t
(And.intro rfl hpt))
end Forward
calc
Uma prova calculacional dispõe uma cadeia de igualdades para o leitor, cada passo justificado por uma reescrita ou por um lema, com calc compondo Eq.trans.
namespace Forward
theorem two_mul_example (m n : ℕ) :
2 * m + n = m + n + m := m:ℕn:ℕ⊢ 2 * m + n = m + n + m
calc 2 * m + n = (m + m) + n := m:ℕn:ℕ⊢ 2 * m + n = m + m + n All goals completed! 🐙
_ = m + n + m := by m:ℕn:ℕ⊢ m + m + n = m + n + m ac_rfl All goals completed! 🐙
end Forward
Cada passo é o tipo de igualdade que rw consome, e ac_rfl fecha um rearranjo.
calc versus simp e Eq.trans
Uma identidade, três provas. calc documenta a cadeia, Eq.trans mostra a transitividade, ac_rfl esconde tudo.
namespace Forward
theorem two_mul_trans (m n : ℕ) :
2 * m + n = m + n + m := by m:ℕn:ℕ⊢ 2 * m + n = m + n + m
have h1 :
2 * m + n = (m + m) + n := by
rw [Nat.two_mul m:ℕn:ℕ⊢ m + m + n = m + m + n] m:ℕn:ℕh1:2 * m + n = m + m + n⊢ 2 * m + n = m + n + m
have h2 :
(m + m) + n = m + n + m := by
ac_rfl m:ℕn:ℕh1:2 * m + n = m + m + nh2:m + m + n = m + n + m⊢ 2 * m + n = m + n + m
exact Eq.trans h1 h2 All goals completed! 🐙
end Forward
namespace Forward
theorem two_mul_ac (m n : ℕ) :
2 * m + n = m + n + m := by m:ℕn:ℕ⊢ 2 * m + n = m + n + m
rw [Nat.two_mul m:ℕn:ℕ⊢ m + m + n = m + n + m] m:ℕn:ℕ⊢ m + m + n = m + n + m
ac_rfl All goals completed! 🐙
end Forward
A escolha é sobre o leitor, não sobre o verificador.
have h : P := pf acrescenta um fato provado; let x := t acrescenta uma abreviação; specialize e obtain também são passos progressivos. As provas reais misturam as duas direções.
namespace Forward
example (P : ℕ → Prop)
(h : ∀ n, P n) : P 7 := by P:ℕ → Proph:∀ (n : ℕ), P n⊢ P 7
specialize h 7 P:ℕ → Proph:P 7⊢ P 7
exact h All goals completed! 🐙
end Forward
namespace Forward
example (α : Type) (P : α → Prop)
(Q : Prop) (hex : ∃ x, P x)
(h : ∀ x, P x → Q) : Q := by α:TypeP:α → PropQ:Prophex:∃ x, P xh:∀ (x : α), P x → Q⊢ Q
obtain ⟨a, ha⟩ := hex α:TypeP:α → PropQ:Proph:∀ (x : α), P x → Qa:αha:P a⊢ Q
exact h a ha All goals completed! 🐙
end Forward
Um have progressivo dentro de uma prova regressiva, cada direção marcada.
namespace Forward
theorem prop_comp_tactical
(a b c : Prop)
(hab : a → b) (hbc : b → c) :
a → c := by a:Propb:Propc:Prophab:a → bhbc:b → c⊢ a → c
intro ha a:Propb:Propc:Prophab:a → bhbc:b → cha:a⊢ c
have hb : b := hab ha a:Propb:Propc:Prophab:a → bhbc:b → cha:ahb:b⊢ c
exact hbc hb All goals completed! 🐙
end Forward
intro ha e exact trabalham de forma regressiva a partir do objetivo.
have hb : b := hab ha trabalha de forma progressiva a partir das hipóteses.
A prova mista costuma ser a mais curta, tomando cada fato de onde é mais fácil alcançá-lo.
Sob PAT, uma função recursiva que devolve uma prova é uma prova por indução, e a chamada recursiva é a hipótese de indução.
namespace Forward
theorem append_nil {α : Type} :
∀ (xs : List α), appendPretty xs [] = xs
| [] => rfl
| x :: xs => congrArg (List.cons x) (append_nil xs)
theorem append_assoc {α : Type} :
∀ (xs ys zs : List α),
appendPretty (appendPretty xs ys) zs
= appendPretty xs (appendPretty ys zs)
| [], _, _ => rfl
| x :: xs, ys, zs =>
congrArg (List.cons x) (append_assoc xs ys zs)
end Forward
induction
A prova recursiva e a tática induction são a mesma prova; a chamada recursiva é o ih do ramo.
namespace Forward
theorem reverse_append {α : Type} :
∀ (xs ys : List α),
reverse (appendPretty xs ys)
= appendPretty (reverse ys)
(reverse xs)
| [], ys => α:Typeys:List α⊢ reverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse []) by α:Typeys:List α⊢ reverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse [])
simp [reverse, appendPretty,
append_nil] All goals completed! 🐙
| x :: xs, ys => α:Typex:αxs:List αys:List α⊢ reverse (appendPretty (x :: xs) ys) = appendPretty (reverse ys) (reverse (x :: xs)) by α:Typex:αxs:List αys:List α⊢ reverse (appendPretty (x :: xs) ys) = appendPretty (reverse ys) (reverse (x :: xs))
simp [reverse, appendPretty,
reverse_append xs ys,
append_assoc] All goals completed! 🐙
end Forward
namespace Forward
theorem reverse_append_tac
{α : Type} (xs ys : List α) :
reverse (appendPretty xs ys)
= appendPretty (reverse ys)
(reverse xs) := by α:Typexs:List αys:List α⊢ reverse (appendPretty xs ys) = appendPretty (reverse ys) (reverse xs)
induction xs with
| nil => nil α:Typeys:List α⊢ reverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse [])
simp [reverse, appendPretty,
append_nil] All goals completed! 🐙
| cons x xs' ih => cons α:Typeys:List αx:αxs':List αih:reverse (appendPretty xs' ys) = appendPretty (reverse ys) (reverse xs')⊢ reverse (appendPretty (x :: xs') ys) = appendPretty (reverse ys) (reverse (x :: xs'))
simp [reverse, appendPretty,
ih, append_assoc] All goals completed! 🐙
end Forward
As semanas 6 e 7 dão o método geral.
Forall_one_pointUma prova de quantificador natural de forma progressiva e desajeitada de forma regressiva.
namespace Forward
theorem Forall_one_point_worked (α : Type)
(t : α) (P : α → Prop) :
(∀ x, x = t → P x) ↔ P t :=
Iff.intro
(assume h : ∀ x, x = t → P x; h t rfl)
(assume hpt : P t;
fix x : α; assume hxt : x = t; hxt ▸ hpt)
end Forward
De forma progressiva, instancie h em t e feche t = t por rfl. De forma regressiva, fixe x, suponha x = t, e reescreva com ▸.
a ∧ (b ∨ c) → (a ∧ b) ∨ (a ∧ c), o primeiro exemplo resolvido da Aula 4, agora como um termo estruturado.
namespace Forward
theorem and_or_distrib
(a b c : Prop) :
a ∧ (b ∨ c) →
(a ∧ b) ∨ (a ∧ c) :=
assume habc : a ∧ (b ∨ c);
have ha : a := And.left habc;
Or.elim (And.right habc)
(fun hb =>
Or.inl (And.intro ha hb))
(fun hc =>
Or.inr (And.intro ha hc))
end Forward
De habc temos a, nomeado ha.
A sua conjunção direita b ∨ c dá dois casos.
Em cada caso construímos a disjunção correspondente com o construtor anônimo.
O roteiro regressivo planejou a partir do objetivo; o termo progressivo constrói a partir das hipóteses.
Uma prova progressiva se lê "de … temos …", o espelho do "basta provar" regressivo.
Uma prova estruturada é um termo com a forma da sua proposição, com fix, assume, have, show.
Sob PAT uma proposição é um tipo e uma prova é um termo, então assume é fun.
O raciocínio progressivo usa os nomes das regras por justaposição e o construtor anônimo.
calc dispõe uma cadeia de igualdades para o leitor; ac_rfl e simp a escondem.
As provas reais misturam direções, um have progressivo dentro de um apply regressivo.
A recursão estrutural é uma prova por indução, formalizada nas semanas 6 e 7.
Exercícios: veja as notas de aula.