Corrección del programa, la especificación


17

De Wikipedia: en informática teórica, la corrección de un algoritmo se afirma cuando se dice que el algoritmo es correcto con respecto a una especificación.

Pero el problema es que obtener la especificación "apropiada" no es una tarea trivial, y no existe un método 100% correcto (hasta donde yo sé) para obtener la correcta, es solo una estimación, así que si vamos a tomar un predicado como especificación solo porque "se ve" como "uno", ¿por qué no tomar el programa como correcto solo porque "se ve" correcto?


2
Porque, con suerte, la especificación es menos complicada que el programa, por lo que tendrá menos errores que el programa.
user253751

1
Tenga en cuenta que un sistema de tipos es una forma de especificación; podemos usarlo para probar algunas propiedades no triviales de los programas. Cuanto más rico es el sistema de tipos, más fuertes son las propiedades que podemos probar.
cabeza de jardín

@immibis pero si tiene un solo error, todo está mal
Maykel Jakson

@MaykelJakson Cierto ... Una vez puse una contradicción en un axioma en Rodin por error (lo que estaba tratando de hacer era correcto pero la sintaxis estaba mal). Me tomó un tiempo "hmm, el autoverificador parece estar funcionando inusualmente bien en este momento" antes de darme cuenta.
user253751

Respuestas:


30

En primer lugar, tienes toda la razón: tienes una verdadera preocupación. La verificación formal transfiere el problema de la confianza en la corrección del programa al problema de la confianza en la corrección de las especificaciones, por lo que no es una bala de plata.

Sin embargo, hay varias razones por las que este proceso puede ser útil.

  1. Las especificaciones son a menudo más simples que el código en sí. Por ejemplo, considere el problema de ordenar una matriz de enteros. Hay algoritmos de clasificación bastante sofisticados que hacen cosas inteligentes para mejorar el rendimiento. Pero la especificación es bastante simple de establecer: la salida debe estar en orden creciente y debe ser una permutación de la entrada. Por lo tanto, podría decirse que es más fácil ganar confianza en la corrección de la especificación que en la corrección del código mismo.

  2. No hay un solo punto de falla. Suponga que una persona escribe una especificación y otra persona escribe el código fuente y luego verifica formalmente que el código cumple con la especificación. Entonces cualquier defecto no detectado tendría que estar presente tanto en la especificación como en el código. En algunos casos, para algunos tipos de fallas, esto se siente menos probable: es menos probable que pase por alto la falla al inspeccionar la especificación y que pase por alto la falla al inspeccionar el código fuente. No todos, pero algunos.

  3. Las especificaciones parciales pueden ser mucho más simples que el código. Por ejemplo, considere el requisito de que el programa esté libre de vulnerabilidades de desbordamiento de búfer. O bien, el requisito de que no hay errores de índice de matriz fuera de límites. Esta es una especificación simple que obviamente es algo bueno y útil para poder probar. Ahora puede intentar utilizar métodos formales para demostrar que todo el programa cumple con esta especificación. Esa podría ser una tarea bastante complicada, pero si tiene éxito, gana una mayor confianza en el programa.

  4. Las especificaciones pueden cambiar con menos frecuencia que el código. Sin métodos formales, cada vez que actualizamos el código fuente, tenemos que verificar manualmente que la actualización no presente errores ni fallas. Los métodos formales pueden potencialmente reducir esta carga: suponga que la especificación no cambia, por lo que las actualizaciones de software implican solo cambios en el código y no cambios en la especificación. Luego, para cada actualización, se libera de la carga de verificar si la especificación sigue siendo correcta (no ha cambiado, por lo que no hay riesgo de que se hayan introducido nuevos errores en la especificación) y de la carga de verificar si el código todavía está correcto (el verificador del programa lo verifica por usted). Todavía debe verificar que la especificación original sea correcta, pero no necesita seguir verificando cada vez que un desarrollador confirma un nuevo parche / actualización / cambio.

Finalmente, recuerde que las especificaciones generalmente son declarativas y no necesariamente se pueden ejecutar ni compilar directamente en el código. Por ejemplo, considere ordenar de nuevo: la especificación dice que la salida está aumentando y es una permutación de la entrada, pero no hay una forma obvia de "ejecutar" esta especificación directamente y no hay una forma obvia de que un compilador la compile automáticamente en el código. Entonces, simplemente tomar la especificación como correcta y ejecutarla a menudo no es una opción.

No obstante, el resultado final sigue siendo el mismo: los métodos formales no son una panacea. Simplemente transfieren el problema (muy difícil) de confianza en la corrección del código al problema (simplemente difícil) de confianza en la corrección de las especificaciones. Los errores en la especificación son un riesgo real, son comunes y no pueden pasarse por alto. De hecho, la comunidad de métodos formales a veces separa el problema en dos partes: la verificación consiste en garantizar que el código cumpla con las especificaciones; la validación se trata de garantizar que la especificación sea correcta (satisfaga nuestras necesidades)

También puede disfrutar de la verificación formal del programa en la práctica y ¿por qué no estamos investigando más para garantizar el tiempo de compilación? para más perspectivas con alguna relación con esto.


Por otro lado, a medida que una especificación se vuelve más detallada, aumenta la probabilidad de que se pueda escribir como pseudocódigo. Usando su ejemplo de ordenación, una versión más detallada de "la salida debe estar en orden creciente" sería "cada número entero en la salida, después del primero, debe ser mayor que el número anterior". Esto, a su vez, se puede escribir fácilmente como algo así como for each integer I<sub> N</sub> in set S (where N > 1) { assert I<sub> N</sub> > I<sub> N - 1</sub> }. No estoy 100% seguro de la notación.
Justin Time - Restablece a Monica

Por lo tanto, una buena especificación también puede ayudar a crear el código, no solo verificarlo.
Justin Time - Restablece a Monica

1
La forma obvia de ejecutar la especificación de clasificación es enumerar todas las permutaciones de la entrada y elegir la ordenada. Sin embargo, el problema con esto debería estar claro ...
Derek Elkins se fue del SE

19

La respuesta de DW es excelente , pero me gustaría ampliar un punto. Una especificación no es solo una referencia contra la cual se verifica el código. Una de las razones para tener una especificación formal es validarla probando algunas propiedades fundamentales. Por supuesto, la especificación no se puede validar por completo: la validación sería tan compleja como la especificación en sí misma, por lo que sería un proceso interminable. Pero la validación nos permite obtener una garantía más sólida en algunas propiedades críticas.

Por ejemplo, supongamos que está diseñando un piloto automático para automóvil. Esta es una cosa bastante compleja que involucra muchos parámetros. Las propiedades de corrección del piloto automático incluyen cosas como "el automóvil no chocará contra una pared" y "el automóvil conducirá donde se le indique que vaya". Una propiedad como "el automóvil no chocará contra una pared" es realmente muy importante, por lo que nos gustaría demostrarlo. Como el sistema funciona en el mundo físico, deberá agregar algunas restricciones físicas; La propiedad real del sistema computacional será algo así como "bajo estos supuestos con respecto a la ciencia de los materiales, y estos supuestos con respecto a la percepción de obstáculos por los sensores del automóvil, el automóvil no chocará contra una pared". Pero aun así, el resultado es una propiedad relativamente simple que es claramente deseable.

¿Podría probar esta propiedad del código? En última instancia, eso es lo que está sucediendo, si está siguiendo un enfoque totalmente formal¹. Pero el código tiene muchas partes diferentes; Los frenos, las cámaras, el motor, etc. se controlan de forma autónoma. Una propiedad de corrección de los frenos sería algo así como "si la señal de" aplicar frenos "está activada, entonces se aplican los frenos". Una propiedad de corrección del motor sería "si la señal del embrague está apagada, entonces el motor no conduce las ruedas". Se necesita una vista de muy alto nivel para ponerlos todos juntos. Una especificación crea capas intermedias donde los diferentes componentes del sistema se pueden articular juntos.

De hecho, un sistema tan complejo como el piloto automático de un automóvil tendría varios niveles de especificaciones con diferentes cantidades de mejoras. A menudo se usa un enfoque de refinamiento en el diseño: comience con algunas propiedades de alto nivel como "el automóvil no chocará contra una pared", luego descubra que esto requiere sensores y frenos y calcule algunos requisitos básicos para los sensores, los frenos y el software piloto, luego refina nuevamente esos requisitos básicos en un diseño del componente (para el sensor, voy a necesitar un radar, un DSP, una biblioteca de procesamiento de imágenes, ...), etc. En un proceso de desarrollo formal, cada nivel de especificación cumple con los requisitos establecidos por el nivel superior, desde las propiedades de más alto nivel hasta el código.

Es imposible estar seguro de que la especificación es correcta. Por ejemplo, si te equivocaste en la física, los frenos podrían no ser efectivos a pesar de que la matemática que relaciona el código de freno con los requisitos formales es correcta. No es bueno demostrar que los descansos son efectivos con 500 kg de carga si realmente tienes 5000 kg. Pero es más fácil ver que 500 kg están mal que ver dentro del código de frenos que no serán lo suficientemente buenos para los parámetros físicos del automóvil.

¹ Lo opuesto a un enfoque totalmente formal es "Supongo que esto funciona, pero no puedo estar seguro". Cuando estás apostando tu vida, eso no parece tan bueno.


¿Es posible probar solo una propiedad de mi código y asegurarme de que siempre sea correcta? Por ejemplo, solo quiero demostrar que el índice de una matriz nunca está fuera del límite de la matriz, y no me importa las otras cosas?
Maykel Jakson

55
@MaykelJakson ¡Seguro! Solo usa eso como su especificación. Probablemente sea una especificación débil, pero nada lo detiene al usar eso y usar métodos formales para probarlo.
chi
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.