Aula 5 · Verificação Formal de Software

Provas Progressivas

Provas estruturadas, provas calculacionais e o princípio PAT

Baseado no Hitchhiker's Guide to Logical Verification (LoVe), capítulo 4.

§5.1 Progressiva e regressiva, mais uma vez

  • 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.

§5.1 O princípio PAT

  • 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  a
fun h => h : True  True
  • fix e assume, da biblioteca da LoVe, são os analisadores de termos que expandem para fun.

§5.2 Construções estruturadas

  • 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

§5.2 Estruturada versus tática

  • 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 ca c a:Propb:Propc:Prophab:a bhbc:b cha:ac a:Propb:Propc:Prophab:a bhbc:b cha:ab a:Propb:Propc:Prophab:a bhbc:b cha:aa All goals completed! 🐙 end Forward
  • De ha e hab temos b; de b e hbc temos c.

§5.3 Progressivo sobre conjunção e disjunção

  • 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

§5.3 Progressivo sobre existencial e bicondicional

  • 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

§5.3 Regras do ponto único

  • 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

§5.4 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 := m:n:m + m + n = m + n + m All goals completed! 🐙 end Forward
  • Cada passo é o tipo de igualdade que rw consome, e ac_rfl fecha um rearranjo.

§5.4 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 := m:n:2 * m + n = m + n + m m:n:h1:2 * m + n = m + m + n2 * m + n = m + n + m m:n:h1:2 * m + n = m + m + nh2:m + m + n = m + n + m2 * m + n = m + n + m All goals completed! 🐙 end Forward
namespace Forward theorem two_mul_ac (m n : ) : 2 * m + n = m + n + m := m:n:2 * m + n = m + n + m m:n:m + m + n = m + n + m All goals completed! 🐙 end Forward
  • A escolha é sobre o leitor, não sobre o verificador.

§5.5 Passos progressivos no modo de táticas

  • 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 := P: Proph: (n : ), P nP 7 P: Proph:P 7P 7 All goals completed! 🐙 end Forward
namespace Forward example (α : Type) (P : α Prop) (Q : Prop) (hex : x, P x) (h : x, P x Q) : Q := α:TypeP:α PropQ:Prophex: x, P xh: (x : α), P x QQ α:TypeP:α PropQ:Proph: (x : α), P x Qa:αha:P aQ All goals completed! 🐙 end Forward

§5.5 Uma prova mista

  • 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 := a:Propb:Propc:Prophab:a bhbc:b ca c a:Propb:Propc:Prophab:a bhbc:b cha:ac a:Propb:Propc:Prophab:a bhbc:b cha:ahb:bc 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.

§5.6 Provas por recursão

  • 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

§5.6 Recursão versus 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) α:Typeys:List αreverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse []) α:Typeys:List αreverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse []) All goals completed! 🐙 α:Typex:αxs:List αys:List αreverse (appendPretty (x :: xs) ys) = appendPretty (reverse ys) (reverse (x :: xs)) α:Typex:αxs:List αys:List αreverse (appendPretty (x :: xs) ys) = appendPretty (reverse ys) (reverse (x :: xs)) All goals completed! 🐙 end Forward
namespace Forward theorem reverse_append_tac {α : Type} (xs ys : List α) : reverse (appendPretty xs ys) = appendPretty (reverse ys) (reverse xs) := α:Typexs:List αys:List αreverse (appendPretty xs ys) = appendPretty (reverse ys) (reverse xs) induction xs with α:Typeys:List αreverse (appendPretty [] ys) = appendPretty (reverse ys) (reverse []) All goals completed! 🐙 α: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')) All goals completed! 🐙 end Forward
  • As semanas 6 e 7 dão o método geral.

§5.7 Exemplo resolvido: Forall_one_point

  • Uma 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 .

§5.7 Exemplo resolvido: a lei distributiva

  • 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.

Resumo

  • 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.