Aula 4 · Verificação Formal de Software

Provas Regressivas

Modo de táticas, táticas básicas, reescrita e indução em Lean

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

§4.1 Regressivo e progressivo

  • Uma tática opera sobre um objetivo e o prova ou cria subobjetivos. Um objetivo é o sequente C ⊢ Q, com antecedente C, o contexto local, e consequente Q, a conclusão.

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.
  • A frase característica de uma prova regressiva é "basta provar". Em uma derivação de dedução natural, as premissas de cada regra ficam acima do traço de inferência e a conclusão abaixo dele. A leitura progressiva vai das suposições, no topo, para a conclusão, e a regressiva vai da conclusão para as suposições.

§4.1 Modo de táticas

  • A palavra-chave by entra no modo de táticas, e cada linha depois dela é uma tática. trace_state imprime o objetivo entre os passos.

namespace Backward theorem fst_of_two_props : a b : Prop, a b a := (a b : Prop), a b a a:Propb:Propa b a a b:Propa b aa:Propb:Propa b a a:Propb:Propha:ahb:ba a b:Propha:ahb:baa:Propb:Propha:ahb:ba All goals completed! 🐙 end Backward

Depois do primeiro e do segundo intro

a b:Propa  b  a
a b:Propha:ahb:ba

§4.2 As quatro táticas básicas

  • Táticas básicas realizam uma transformação elementar do estado da prova cada uma, e nenhuma depende de um conectivo ou de uma teoria em particular.

  • intro move variáveis e suposições para o contexto; apply casa a conclusão do objetivo com a de um teorema e deixa os seus argumentos e premissas não resolvidos como objetivos; exact fecha o objetivo com um termo; assumption procura no contexto.

namespace Backward theorem fst_of_two_props_params (a b : Prop) (ha : a) (hb : b) : a := a:Propb:Propha:ahb:ba All goals completed! 🐙 theorem fst_of_two_props_exact (a b : Prop) (ha : a) (hb : b) : a := a:Propb:Propha:ahb:ba All goals completed! 🐙 end Backward
namespace Backward theorem fst_of_two_props_assumption (a b : Prop) (ha : a) (hb : b) : a := a:Propb:Propha:ahb:ba All goals completed! 🐙 theorem prop_comp (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 Backward
  • Lean insere os parâmetros à esquerda dos dois-pontos no contexto local do objetivo inicial, então estas provas dispensam intro.

§4.2 Perder a demonstrabilidade

apply pode perder um objetivo demonstrável

declaration uses `sorry`example (a b : Prop) (hb : b) : a b := a:Propb:Prophb:ba b a:Propb:Prophb:ba a b:Prophb:baa:Propb:Prophb:ba All goals completed! 🐙
a b:Prophb:ba
example (a b : Prop) (hb : b) : a b := a:Propb:Prophb:ba b a:Propb:Prophb:bb All goals completed! 🐙

sorry, clear, rename

namespace Backward theorem cleanup_example (a b c : Prop) (ha : a) (hb : b) (hab : a b) (hbc : b c) : c := a:Propb:Propc:Propha:ahb:bhab:a bhbc:b cc b:Propc:Prophb:bhbc:b cc b:Propc:Prophb:bhbc:b cb b:Prophb:bb b:Proph:bb All goals completed! 🐙 end Backward
  • Um objetivo demonstrável continua demonstrável após intro. apply e clear podem transformar um objetivo demonstrável em um indemonstrável. sorry fecha qualquer coisa e #print axioms o relata como sorryAx.

§4.3 Regras como teoremas

  • Cada figura de inferência da Aula 1 é um teorema ordinário, aplicado regressivamente por apply.

Introdução e eliminação

And.intro : ?a → ?b → ?a ∧ ?b
And.left  : ?a ∧ ?b → ?a
And.right : ?a ∧ ?b → ?b
Or.inl    : ?a → ?a ∨ ?b
Or.inr    : ?b → ?a ∨ ?b
Or.elim   : ?a ∨ ?b →
  (?a → ?c) → (?b → ?c) → ?c
Iff.intro : (?a → ?b) →
  (?b → ?a) → (?a ↔ ?b)
Iff.mp    : (?a ↔ ?b) → ?a → ?b
Iff.mpr   : (?a ↔ ?b) → ?b → ?a
namespace Backward theorem And_swap (a b : Prop) : a b b a := a:Propb:Propa b b a a:Propb:Prophab:a bb a a:Propb:Prophab:a bba:Propb:Prophab:a ba a:Propb:Prophab:a b?left.a ba:Propb:Prophab:a bPropa:Propb:Prophab:a ba a:Propb:Prophab:a ba a:Propb:Prophab:a ba ?right.ba:Propb:Prophab:a bProp All goals completed! 🐙 end Backward

§4.3 Metavariáveis e marcadores

  • Uma metavariável ?a representa um termo por determinar, e a unificação o determina. O marcador · foca um subobjetivo; a justaposição instancia uma regra progressivamente.

namespace Backward theorem And_swap_braces : a b : Prop, a b b a := (a b : Prop), a b b a a:Propb:Prophab:a bb a a:Propb:Prophab:a bba:Propb:Prophab:a ba a:Propb:Prophab:a bb All goals completed! 🐙 a:Propb:Prophab:a ba All goals completed! 🐙 end Backward
namespace Backward opaque f : theorem f5_if (h : n : , f n = n) : f 5 = 5 := h: (n : ), f n = nf 5 = 5 All goals completed! 🐙 end Backward
  • Passar hab diretamente a And.right é um pequeno passo progressivo dentro de uma prova regressiva, e evita as metavariáveis que apply And.right deixaria.

§4.3 Quantificadores, verdade, falsidade, negação

Exists.intro : ∀ (w : ?α), ?p w → ∃ x, ?p x
Exists.elim  : (∃ x, ?p x) → (∀ (w : ?α), ?p w → ?b) → ?b
True.intro   : True
False.elim   : False → ?c
Classical.em : ∀ (p : Prop), p ∨ ¬p
Classical.byContradiction : (¬?a → False) → ?a
  • A negação dispensa regras: ¬a é definida como a → False, então intro se aplica a uma conclusão negada.

  • True.intro é a única regra da verdade; a falsidade não tem regra de introdução, e False.elim fecha qualquer objetivo a partir de uma prova de False.

namespace Backward theorem Not_Not_intro (a : Prop) : a ¬¬ a := a:Propa ¬¬a a:Propha:ahna:¬aFalse a:Propha:ahna:¬aa All goals completed! 🐙 end Backward

§4.3 Estratégias

As estratégias do guia para provas proposicionais.

  • Olhe a conclusão. Uma implicação ou uma negação pede intro.

  • Olhe as hipóteses. Uma conjunção oferece And.left e And.right, uma disjunção oferece Or.elim, uma equivalência oferece Iff.mp e Iff.mpr.

  • Case a conclusão do objetivo com uma regra de introdução e a aplique com apply.

  • Prefira táticas que preservam a demonstrabilidade enquanto fizerem progresso, e registre os pontos de escolha em que uma tática se compromete com um lado.

  • Quando um subobjetivo repete uma hipótese, exact ou assumption o fecha.

  • Quando nada construtivo se aplica, considere uma análise de casos sobre Classical.em.

  • Se a prova não progride, retroceda até o último ponto de escolha e tente a outra opção.

§4.4 rfl e as conversões

  • rfl prova l = r quando os lados coincidem a menos de computação, e cada passo de computação é uma conversão nomeada.

α  renomeia uma variável ligada
β  aplica uma função anônima
δ  desdobra uma definição
ζ  substitui um let
η  fun x => f x é igual a f
ι  projeta um construtor
namespace Backward def double (n : ) : := n + n end Backward
namespace Backward theorem β_example {α β : Type} (f : α β) (a : α) : (fun x => f x) a = f a := α:Typeβ:Typef:α βa:α(fun x => f x) a = f a All goals completed! 🐙 theorem δ_example : double 5 = 5 + 5 := double 5 = 5 + 5 All goals completed! 🐙 theorem ι_example {α β : Type} (a : α) (b : β) : Prod.fst (a, b) = a := α:Typeβ:Typea:αb:β(a, b).fst = a All goals completed! 🐙 end Backward
  • ac_rfl adiciona associatividade e comutatividade dos operadores registrados, como em a + b + c = c + b + a.

§4.4 A igualdade como regras

Eq.refl  : ∀ (a : ?α), a = a
Eq.symm  : ?a = ?b → ?b = ?a
Eq.trans : ?a = ?b → ?b = ?c →
           ?a = ?c
Eq.subst : ?a = ?b → ?P ?a → ?P ?b
  • = liga mais forte que os conectivos, então a = b ∧ c = d se lê (a = b) ∧ (c = d).

namespace Backward theorem Eq_trans_symm {α : Type} (a b c : α) (hab : a = b) (hcb : c = b) : a = c := α:Typea:αb:αc:αhab:a = bhcb:c = ba = c α:Typea:αb:αc:αhab:a = bhcb:c = ba = ?bα:Typea:αb:αc:αhab:a = bhcb:c = b?b = cα:Typea:αb:αc:αhab:a = bhcb:c = bα α:Typea:αb:αc:αhab:a = bhcb:c = ba = ?b All goals completed! 🐙 α:Typea:αb:αc:αhab:a = bhcb:c = bb = c α:Typea:αb:αc:αhab:a = bhcb:c = bc = b All goals completed! 🐙 end Backward

§4.5 rw

  • rw aplica uma equação da esquerda para a direita, uma vez: encontra o primeiro subtermo que casa, substitui toda ocorrência dele e então tenta rfl. inverte a equação, at h mira uma hipótese, e o nome de uma constante usa as equações que a definem.

namespace Backward theorem Eq_trans_symm_rw {α : Type} (a b c : α) (hab : a = b) (hcb : c = b) : a = c := α:Typea:αb:αc:αhab:a = bhcb:c = ba = c α:Typea:αb:αc:αhab:a = bhcb:c = bb = c All goals completed! 🐙 end Backward
namespace Backward theorem a_proof_of_negation (a : Prop) : a ¬¬ a := a:Propa ¬¬a a:Propa ¬a False a:Propa (a False) False a:Propha:ahna:a FalseFalse a:Propha:ahna:a Falsea All goals completed! 🐙 end Backward

§4.5 simp

  • simp reescreve com o conjunto simp exaustivamente. simp [t] adiciona um teorema ou constante por chamada, simp [-t] remove um, simp [*] at * usa tudo em toda parte, e @[simp] registra um teorema permanentemente.

namespace Backward theorem cong_two_args_1p1 {α : Type} (a b c d : α) (g : α α α) (hab : a = b) (hcd : c = d) : g a c (1 + 1) = g b d 2 := α:Typea:αb:αc:αd:αg:α α αhab:a = bhcd:c = dg a c (1 + 1) = g b d 2 All goals completed! 🐙 end Backward
  • A reescrita é onde as provas deixam de ser previsíveis. Tente uma tática, estude os subobjetivos que surgem e ajuste. Nas palavras do guia, NÃO ENTRE EM PÂNICO.

§4.6 Indução

  • induction produz um subobjetivo nomeado por construtor, e os nomes do ramo ligam os argumentos do construtor e a hipótese de indução.

induction n with
| zero       => (caso base)
| succ n' ih => (caso do passo)
  • Induza sobre o argumento que a recursão consome.

  • Um caso base difícil sinaliza a variável errada ou um teorema auxiliar que falta.

namespace Backward theorem add_zero (n : ) : add 0 n = n := n:add 0 n = n induction n with add 0 0 = 0 All goals completed! 🐙 n':ih:add 0 n' = n'add 0 (n' + 1) = n' + 1 All goals completed! 🐙 theorem add_succ (m n : ) : add (Nat.succ m) n = Nat.succ (add m n) := m:n:add m.succ n = (add m n).succ induction n with m:add m.succ 0 = (add m 0).succ All goals completed! 🐙 m:n':ih:add m.succ n' = (add m n').succadd m.succ (n' + 1) = (add m (n' + 1)).succ All goals completed! 🐙 end Backward

§4.6 Comutatividade e ac_rfl

  • Estas reprovam leis que a Aula 3 enunciou com sorry, agora como teoremas próprios, e as instâncias deixam ac_rfl tratar add como +.

namespace Backward theorem add_comm (m n : ) : add m n = add n m := m:n:add m n = add n m induction n with m:add m 0 = add 0 m All goals completed! 🐙 m:n':ih:add m n' = add n' madd m (n' + 1) = add (n' + 1) m All goals completed! 🐙 theorem add_assoc (l m n : ) : add (add l m) n = add l (add m n) := l:m:n:add (add l m) n = add l (add m n) induction n with l:m:add (add l m) 0 = add l (add m 0) All goals completed! 🐙 l:m:n':ih:add (add l m) n' = add l (add m n')add (add l m) (n' + 1) = add l (add m (n' + 1)) All goals completed! 🐙 end Backward
namespace Backward instance Associative_add : Std.Associative add := { assoc := add_assoc } instance Commutative_add : Std.Commutative add := { comm := add_comm } theorem mul_add (l m n : ) : mul l (add m n) = add (mul l m) (mul l n) := l:m:n:mul l (add m n) = add (mul l m) (mul l n) induction n with l:m:mul l (add m 0) = add (mul l m) (mul l 0) All goals completed! 🐙 l:m:n':ih:mul l (add m n') = add (mul l m) (mul l n')mul l (add m (n' + 1)) = add (mul l m) (mul l (n' + 1)) l:m:n':ih:mul l (add m n') = add (mul l m) (mul l n')add l (add (mul l m) (mul l n')) = add (mul l m) (add l (mul l n')) All goals completed! 🐙 end Backward

§4.7 Exemplo trabalhado: descarregar reverse_cons

  • A Aula 3 enunciou reverse (x :: xs) = snoc (reverse xs) x com sorry. O enunciado mistura appendPretty e snoc, então a peça que falta é o teorema que os relaciona.

O teorema auxiliar

namespace Backward theorem append_snoc {α : Type} (ys : List α) (x : α) : appendPretty ys [x] = snoc ys x := α:Typeys:List αx:αappendPretty ys [x] = snoc ys x induction ys with α:Typex:αappendPretty [] [x] = snoc [] x All goals completed! 🐙 α:Typex:αy:αys':List αih:appendPretty ys' [x] = snoc ys' xappendPretty (y :: ys') [x] = snoc (y :: ys') x All goals completed! 🐙 end Backward

O teorema, a um simp de distância

namespace Backward theorem reverse_cons {α : Type} (x : α) (xs : List α) : reverse (x :: xs) = snoc (reverse xs) x := α:Typex:αxs:List αreverse (x :: xs) = snoc (reverse xs) x All goals completed! 🐙 end Backward
  • Um caso difícil costuma sinalizar um teorema auxiliar que falta.

Resumo

  • Uma tática transforma o objetivo, e uma prova regressiva se lê como uma cadeia de "basta provar".

  • intro, apply, exact e assumption provam os teoremas proposicionais desta aula, e entre elas só intro nunca perde um objetivo demonstrável.

  • Cada regra da Aula 1 é um teorema que apply consome regressivamente e a justaposição instancia progressivamente.

  • rfl decide a igualdade a menos de computação, uma conversão nomeada por vez, e ac_rfl adiciona associatividade e comutatividade.

  • rw reescreve uma vez no primeiro casamento e então tenta rfl; simp reescreve exaustivamente com o conjunto simp.

  • induction … with prova as leis gerais que a computação não alcança, e reprova vários dos enunciados da Aula 3 como teoremas próprios.

  • A Aula 5 vira as mesmas provas do avesso, em provas progressivas e estruturadas.

Exercícios: veja as notas de aula.