Como lidar com matrizes durante provas de correção no estilo Hoare

11

Na discussão em torno dessa questão , Gilles menciona corretamente que qualquer prova de correção de um algoritmo que utiliza matrizes deve provar que não há acesso a matrizes fora dos limites; dependendo do modelo de tempo de execução, isso causaria um erro de tempo de execução ou acesso a elementos que não são da matriz.

Uma técnica comum para executar essas provas de correção (pelo menos nos estudos de graduação e provavelmente na verificação automatizada) é usar a lógica Hoare . Não sei que o conjunto padrão de regras contém qualquer coisa relacionada a matrizes; eles parecem estar restritos a variáveis ​​monádicas.

Eu posso imaginar adicionando axiomas do formulário

{0i<A.lengthP[A[i]/E]} A[i]:=E; {P}

No entanto, não está claro para mim como você lidaria com um acesso à matriz no lado direito, ou seja, se ele é parte de uma expressão complexa em alguma declaração .x : = EEx:=E

Como os acessos de matrizes podem ser modelados na lógica do Hoare para que a ausência de acessos inválidos possa e tenha que ser comprovada quanto à correção do programa?

As respostas podem assumir que não permitimos que elementos de matriz sejam usados ​​em declarações diferentes de ou como parte de alguns em pois isso não restringe a expressividade; sempre podemos atribuir a uma variável temporária o valor desejado, ou seja, escreva vez de .E x : = E t : = A [ i ] ; i f ( t > 0 ) i f ( A [ i ] > 0 ) A[i]:=EEx:=Et:=A[i]; if(t>0)if(A[i]>0)

Rafael
fonte

Respostas:

8

Seu axioma não é realmente um axioma, faltam hipóteses. Apresentações simples da lógica Hoare manipulam fórmulas do formato que e são fórmulas lógicas e é um comando. Você precisa garantir que seja bem formado . Em linguagens simples, como as usadas frequentemente para uma primeira introdução à lógica de Hoare, a boa formação é sintática: normalmente é uma questão de verificar seP P C C C{P}C{P}PPCCCestá em conformidade com uma gramática livre de contexto e, possivelmente, que as variáveis ​​livres estejam dentro de um conjunto permitido. Se a linguagem incluir construções com correção semântica, como acessos a elementos de matriz, você precisará adicionar hipóteses para expressar essa correção semântica.

Formalmente, você pode adicionar julgamentos para expressar a correção de expressões e comandos. Se as expressões não tiverem efeitos colaterais, elas não precisarão de pós-condições, apenas pré-condições. Por exemplo, você pode escrever regras de boa formação, como e permite apenas expressões bem formadas nos comandos: {P[xE]}

{P}E wf{P0E<length(A)}A[E] wf{P}E1 wf{P}E2 wf{P}E1+E2 wf
{P[xE]}E wf{P[xE]}x:=E{P}

Uma abordagem diferente é tratar todas as expressões como bem formadas, mas fazer com que qualquer expressão que envolva um cálculo mal formado tenha um valor especial . Em idiomas com verificação de limites de tempo de execução, significa "este programa gerou uma exceção fatal". Você acompanharia se um programa cometeu algum através de um predicado lógico ; um programa é válido apenas se você puder provar que sua pós-condição implica . e r r o r E r r o r ¬ E r r o rerrorerrorError¬Error

{P[xE]}x:=E{PError}P[xE]Eerror{P[xE]}x:=E{P}

Ainda outra abordagem é considerar um Hoare triplo para manter apenas se o programa terminar corretamente. Essa é a abordagem comum para programas não-determinantes: a pós-condição é mantida quando o comando termina, o que nem sempre pode acontecer. Se você tratar os erros de tempo de execução como sem término, varre todos os problemas de correção por baixo do capô. Você ainda precisará provar a correção do programa de alguma forma, mas ele não precisa estar na lógica de Hoare se você preferir algum outro formalismo para essa tarefa.

A propósito, observe que expressar o que acontece quando uma variável composta, como uma matriz, é modificada, está mais envolvido do que o que você escreveu. Suponha era, digamos, : a substituição não mudaria , ainda a atribuição pode invalidar . Mesmo se você restringir a sintaxe dos predicados para falar apenas sobre átomos, considere a atribuição na pré-condição : você não pode fazer uma substituição simples para obter a pós-condição correta , é necessário avaliarI s S o r t e d ( A ) Uma [ i ] E P Uma [ i ] P P A [ A [ 0 ] - 1 ]PIsSorted(A)A[i]EPA[i]PPA [ 0 ] = 2 Um [ 1 ] = 3 A [ 0 ] = 1 A[A[0]1]:=A[0]A[0]=2A[1]=3A [ 0 ] A [ 0 ] A A [ i E ]A[0]=1A[1]=1A[0](o que pode ser difícil em geral, pois a pré-condição pode não especificar um único valor possível para ). Você precisa executar a substituição no próprio array: . As notas de aula de Mike Gordon apresentam uma boa lógica de Hoare de apresentação com matrizes (mas sem verificação de erro).A[0]AA[iE]

Gilles 'SO- parar de ser mau'
fonte
0

Como mencionado por Gilles, existe um axioma de atribuição de matriz (consulte as notas de Gordon, ): Em palavras, se você tiver uma atribuição de matriz, substitua a matriz original pela matriz que possui na posição o valor . Observe que, se você já possui a publicação e atribui , deve obter o pré (sim, nesta ordem - a atualização recente é executada primeiro!).

{Q[AA.store(i,expr)]}A[i]=expr{Q}
A.store(i,expr)iexprA.store(i,vi)A[j]=vjA.store(j,vj).store(i,vi)

Além disso, precisamos do axioma acesso à matriz: A.store(i,v)[i]pode ser substituído por v( "se você acessar th elemento que você acaba de atualizar, em seguida, retornar o valor atribuído").i

Penso que, para provar que um programa com matrizes está correto ("sem acesso fora de limite"), os axiomas acima são suficientes. Vamos considerar o programa:

...
A[i] = 12
...

Anotaríamos este programa:

...
@ {0<i<A_length}
A[i] = 12
...

onde A_lengthé uma variável que especifica o comprimento da matriz. Agora tente provar a anotação - ou seja, trabalhe de trás para frente (de baixo para cima, "como sempre" nas provas de Hoare). Se você chegar ao topo {false}, o acesso fora de limite pode acontecer; caso contrário, a expressão obtida é a pré-condição sob a qual não é possível acessar fora de limite. (além disso, precisamos garantir que, quando a matriz for criada como int A=int[10];na pós-condição que temos {A_length==10}).

Ayrat
fonte
Seus axiomas não modelam o acesso fora dos limites: eles nem mencionam o comprimento! No seu exemplo de programa, como você se relaciona com lengtha A?
Gilles 'SO- stop be evil'
Certo, os axiomas não modelam acessos fora do limite. Primeiro, para provar que um programa está correto, adiciono anotações que exigem que o acesso esteja dentro dos limites. ( lengthfoi renomeado A_length.) Segundo, precisamos de axiomas de "criação" de array como int[] a = int[length] {a_length==length}. Eu acho que isso deve ser suficiente.
Ayrat