Casamentos superficiais versus profundos

47

Ao codificar uma lógica em um assistente de prova, como Coq ou Isabelle, é necessário fazer uma escolha entre usar uma incorporação superficial e profunda . Em uma incorporação superficial, as fórmulas lógicas são escritas diretamente na lógica do provador do teorema, enquanto em uma incorporação profunda as fórmulas lógicas são representadas como um tipo de dados.

  • Quais são as vantagens e limitações das várias abordagens?
  • Existem diretrizes disponíveis para determinar qual usar?
  • É possível alternar entre as duas representações de alguma maneira sistemática?

Como motivação, eu gostaria de codificar várias lógicas relacionadas à segurança no Coq e fico imaginando quais são os prós e os contras das diferentes abordagens.

Dave Clarke
fonte

Respostas:

28

Quais são as vantagens e limitações das várias abordagens?

  • Prós de incorporação profunda: você pode provar e definir as coisas por indução na estrutura das fórmulas. Exemplos de interesses são o tamanho de uma fórmula.

  • Contras de incorporações profundas: você lida explicitamente com a ligação de variáveis. Isso geralmente é muito trabalhoso.

Existem diretrizes disponíveis para determinar qual usar?

Incorporações pouco profundas são muito úteis para importar resultados comprovados na lógica do objeto. Por exemplo, se você provar algo em uma lógica pequena (por exemplo, lógica de separação), combinações superficiais podem ser uma ferramenta de escolha para importar seu resultado no Coq.

Por outro lado, a incorporação profunda é quase obrigatória quando você deseja provar meta-teoremas sobre a lógica do objeto (como eliminação de corte, por exemplo).

É possível alternar entre as duas representações de alguma maneira sistemática?

A idéia por trás da incorporação superficial é realmente trabalhar diretamente em um modelo de fórmulas de objetos. Geralmente, as pessoas mapeiam uma fórmula de objeto P diretamente (usando notações ou fazendo a tradução manualmente) para um habitante de Prop. É claro que existem habitantes de Prop que não podem ser obtidos incorporando uma fórmula da lógica do objeto. Portanto, você perde algum tipo de integridade.

Portanto, é possível enviar todos os resultados obtidos em uma configuração de incorporação profunda por meio de uma função de interpretação.

Aqui está um pequeno exemplo de coq:

Fórmula indutiva: Conjunto: =
    Ftrue: formula
  | Ffalse: formula
  | Fand: fórmula -> fórmula -> fórmula
  | Para: formula -> formula -> formula.

Interpretação do ponto de correção (F: fórmula): Prop: = combina F com 
    Ftrue => Verdadeiro
  | Falso => ​​Falso
  | Fand ab => (interpretar a) / \ (interpretar b)
  | Para ab => (interpretar a) \ / (interpretar b)
 fim.

Derivado indutivo: fórmula -> Prop: = 
    deep_axiom: Ftrue derivável
  | deep_and: forall ab, derivável a -> derivável b -> derivável (Fand ab)
  | deep_or1: forall ab, derivável a -> derivável (para ab)
  | deep_or2: forall ab, derivável b -> derivável (para ab).

Subdivisão indutiva: Prop -> Prop: = 
    shallow_axiom: sderivable True 
  | shallow_and: forall ab, sderivable a -> sderivable b -> sderivable (a / \ b)
  | shallow_or1: forall ab, sderivable a -> sderivable (a \ / b)
  | shallow_or2: forall ab, sderivable b -> sderivable (a \ / b).

(* Você pode provar o seguinte lema: *)
Lema superficial: 
   todos F, derivável F -> derivável (interpretar F).

(* Você NÃO pode provar o seguinte lema: *)
Lema t: 
   para todos P, derivável P -> existe F, interpreta F = P.
Marc
fonte
22

Grosso modo, com uma incorporação profunda de uma lógica, você (1) define um tipo de dados que representa a sintaxe para sua lógica e (2) fornece um modelo da sintaxe e (3) prova que axiomas sobre sua sintaxe são sólidos com respeito para o modelo. Com uma incorporação superficial, você pula as etapas (1) e (2) e apenas começa com um modelo e prova as vinculações entre as fórmulas. Isso significa que casamentos superficiais geralmente são menos trabalhosos para decolar, pois representam o trabalho que você normalmente acabaria fazendo de qualquer maneira com uma incorporação profunda.

No entanto, se houver uma incorporação profunda, geralmente é mais fácil escrever procedimentos de decisão reflexivos, já que você está trabalhando com fórmulas que possuem realmente sintaxe que você pode recorrer. Além disso, se o seu modelo é estranho ou complicado, geralmente você não deseja trabalhar diretamente com a semântica. (Por exemplo, se você usar a biorrogonalidade para forçar o fechamento admissível, ou usar os modelos do estilo Kripke para forçar as propriedades do quadro em lógicas de separação ou jogos semelhantes.) No entanto, os embeddings profundos quase certamente o forçarão a pensar muito sobre ligações e substituições variáveis , que encherá seu coração de raiva, já que isso é (a) trivial e (b) uma fonte inesgotável de aborrecimento.

A sequência correta que você deve seguir é: (1) tente sobreviver com uma incorporação superficial. (2) Quando isso acabar, tente usar táticas e cotações para executar os procedimentos de decisão que você deseja executar. (3) Se isso também acabar, desista e use uma sintaxe do tipo dependente para sua incorporação profunda.

  • Planeje levar alguns meses em (3) se esta for sua primeira vez. Você vai precisar para se familiarizar com a fantasia características de seu assistente de prova para manter a sanidade. (Mas este é um investimento que será recompensado em geral.)
  • Se o seu assistente de prova não tiver tipos dependentes, permaneça no nível 2.
  • Se o idioma do seu objeto for digitado de forma independente, mantenha-se no nível 2.

Além disso, não tente subir gradualmente a escada. Ao decidir subir a escada da complexidade, dê um passo completo de cada vez. Se você fizer as coisas pouco a pouco, obterá muitos teoremas estranhos e inutilizáveis ​​(por exemplo, obterá várias sintaxes meia-boca e teoremas que misturam sintaxe e semântica de maneiras estranhas), que você eventualmente tem que jogar fora.

Edição: Aqui está um comentário explicando por que subir a escada gradualmente é tão tentador e por que leva (em geral) ao sofrimento.

Concretamente, suponha que você tenha um banco de areia a incorporação da lógica de separação, com os conectivos e unidade . Então, você provará teoremas como e e assim por diante. Agora, quando você tentar realmente usar a lógica para provar que um programa está correto, você terá algo como e realmente desejará algo como .I A BABI( A B ) CABBA( I A ) ( B C ) A ( B ( C I ) )(AB)CA(BC)(IA)(BC)A(B(CI))

Nesse ponto, você ficará aborrecido com a necessidade de reassociar manualmente as fórmulas e pensará: "Eu sei! Vou interpretar um tipo de dados de listas como uma lista de fórmulas separadas. Dessa forma, posso interpretar como concatenação dessas listas e, em seguida, as fórmulas acima serão definitivamente iguais! "

Isso é verdade e funciona! No entanto, observe que a conjunção também é ACUI, assim como disjunção. Portanto, você passará pelo mesmo processo em outras provas, com tipos de dados de lista diferentes e, em seguida, terá três sintaxes para fragmentos diferentes da lógica de separação, além de ter metateoremas para cada um deles, que inevitavelmente serão diferentes, e você se sentirá querendo um metateorema comprovado para separar a conjunção por disjunção e, em seguida, desejará misturar sintaxes e, em seguida, ficará louco.

É melhor direcionar o maior fragmento possível com um esforço razoável e apenas fazê-lo.

Neel Krishnaswami
fonte
Obrigado por esta ótima resposta, Neel. Eu gostaria de poder aceitar duas respostas (decidi com base nos votos dos outros).
Dave Clarke
Sem problemas. Acabei de me lembrar de algo que preciso adicionar a esta resposta, sobre por que ir de forma incremental é tão tentador.
Neel Krishnaswami
Lidar com as propriedades da ACUI é sempre um incômodo. Por que Coq não pode tirar uma folha do livro de Maude?
Dave Clarke
14

É importante entender que existe um espectro de profundo a raso. Você modela profundamente as partes de sua linguagem que de alguma forma devem participar de algum argumento indutivo sobre a construção dela; o restante fica melhor na visão superficial da semântica direta do substrato da lógica.

Por exemplo, quando você deseja discutir sobre o Hoare Logic, pode modelar a linguagem da expressão de maneira superficial, mas o contorno da linguagem atribuir se enquanto deve ser um tipo de dados concreto. Você não precisa inserir a estrutura de x + y ou a <b, mas precisa trabalhar com whileetc.

Nas outras respostas, houve alusões a tipos dependentes. Isso lembra o antigo problema de representar linguagens com ligantes de maneira sã, de modo que elas sejam tão superficiais quanto possível, mas ainda admitam alguns argumentos indutivos. Minha impressão é que o júri ainda está julgando todas as diferentes abordagens e documentos que surgiram nos últimos 10 a 20 anos sobre esse assunto. O "desafio POPLmark" para as diferentes comunidades de assistentes de provas também foi sobre isso em certa medida.

Estranhamente, no HOL clássico sem tipos dependentes, a abordagem HOL-Nominal de C. Urban funcionou muito bem para encadernação superficial, embora não acompanhasse as mudanças culturais nessas comunidades de formalização da linguagem de programação.

Makarius
fonte