¿Qué sabemos sobre los programas probablemente correctos?


37

La complejidad cada vez mayor de los programas de computadora y la posición cada vez más crucial que tienen las computadoras en nuestra sociedad me hacen preguntarme por qué todavía no usamos colectivamente lenguajes de programación en los que tenga que dar una prueba formal de que su código funciona correctamente.

Creo que el término es un 'compilador certificador' (lo encontré aquí ): un compilador que compila un lenguaje de programación en el que uno no solo tiene que escribir el código, sino también indicar la especificación del código y demostrar que el código se adhiere al especificación (o use un probador automatizado para hacerlo).

Mientras buscaba en Internet, solo encontré proyectos que utilizan un lenguaje de programación muy simple o proyectos fallidos que intentan adaptar los lenguajes de programación modernos. Esto me lleva a mi pregunta:

¿Hay algún compilador certificador que implemente un lenguaje de programación completo, o es esto muy difícil / teóricamente imposible?

Además, todavía no he visto ninguna clase de complejidad que involucre programas comprobables, como 'la clase de todos los idiomas que puede decidir una máquina de Turing para la cual existe una prueba de que esta máquina de Turing se detiene', que llamaré , como análogo a , el conjunto de lenguajes recursivos.ProvableRR

Puedo ver las ventajas de estudiar una clase de complejidad de este tipo: por ejemplo, para el problema de detención es decidible (incluso conjeturo que definido de manera obvia, sería la clase de idiomas más grande para la que es decidible). Además, dudo que descartemos cualquier programa prácticamente útil: ¿quién usaría un programa cuando no puede probar que termina?ProvableRProvableRE

Entonces mi segunda pregunta es:

¿Qué sabemos sobre las clases de complejidad que requieren que sus lenguajes contengan ciertas propiedades?


1
Un compilador podría enumerar todas las pruebas posibles de longitud i, permitiéndome ir de 1 a infinito, hasta que encuentre una prueba de que el programa se detiene. Si requerimos que la entrada para el compilador se detenga, entonces el compilador siempre encontrará esa prueba. Dado que el problema de detención es indecidible, debemos concluir que hay programas que se detienen, pero no existen pruebas de ello. El punto crucial es que los programas no pueden averiguar si existe una prueba, no que no puedan encontrar una prueba si existe.
Alex ten Brink

3
Creo que deberías dividirlos. Son preguntas diferentes con respuestas diferentes.
Mark Reitblatt

44
Sobre la primera pregunta, un artículo influyente es "Procesos sociales y pruebas de teoremas y programas", portal.acm.org/citation.cfm?id=359106
Colin McQuillan

1
La verificación del programa es indecidible. Entonces, un problema es decir qué constituye una buena solución. Ver cstheory.stackexchange.com/questions/4016/…
Radu GRIGore

2
@Colin: vale la pena leer ese documento por su análisis de la prueba, pero sus predicciones han sido falsificadas. Hoy tenemos compiladores, núcleos de sistemas operativos, recolectores de basura y bases de datos correctamente correctos, todos los cuales predijeron que serían imposibles. El truco para evadir su crítica fue evitar la verificación humana de los detalles de bajo nivel de las pruebas formales, pero usar la verificación de pruebas por máquina y usar humanos para verificar el verificador de pruebas. La referencia de Noam a la teoría de tipos es donde se encuentra el estado del arte, lo que deja a los programas imperativos en una especie de vínculo ya que la teoría de tipos es funcional.
Neel Krishnaswami

Respuestas:


28

"Compilador certificador" generalmente significa algo ligeramente diferente: significa que tiene un compilador que puede probar que el código de máquina que emite implementa correctamente la semántica de alto nivel. Es decir, esta es una prueba de que no hay errores de compilación. Los programas que las personas dan al compilador aún pueden estar equivocados, pero el compilador generará una versión correcta del código de máquina del programa incorrecto. La mayor historia de éxito en este sentido es el compilador verificado CompCert , que es un compilador para un gran subconjunto de C.

El compilador Compcert en sí es un programa con una prueba de corrección (realizada en Coq), que garantiza que si genera código para un programa, será correcto (con respecto a la semántica operativa de ensamblaje y C que los diseñadores de CompCert usaron). El esfuerzo para verificar a máquina estas cosas es bastante grande; normalmente, la prueba de corrección será de 1x a 100x del tamaño del programa que está verificando. Escribir programas y pruebas verificados por máquina es una nueva habilidad que debes aprender: no es matemática o programación como de costumbre, aunque depende de poder hacer ambas cosas bien. Se siente como si estuvieras comenzando desde cero, como ser un programador novato nuevamente.

Sin embargo, no hay barreras teóricas especiales para esto. Lo único en este sentido es el teorema de Blum Size de que para cualquier lenguaje en el que todos los programas sean totales, puede encontrar un programa en un lenguaje recursivo general que será al menos exponencialmente más grande cuando se programe en el lenguaje total. La forma de entender este resultado es que un lenguaje total codifica no solo un programa, sino también una prueba de terminación. Por lo tanto, puede tener programas cortos con pruebas de terminación largas. Sin embargo, esto realmente no importa en la práctica, ya que solo vamos a escribir programas con pruebas de terminación manejables.

EDITAR: Dai Le pidió alguna explicación sobre el último punto.

Esto es principalmente un reclamo pragmático, basado en el hecho de que si puedes entender por qué funciona un programa, entonces es poco probable que la razón sea una vasta invariante de millones de páginas. (¡Los invariantes más largos que he usado son de unas pocas páginas, y vaya que hacen que los críticos se quejen! También es comprensible, ya que el invariante es la razón por la que el programa funciona despojado de toda la narrativa que ayuda a las personas a entenderlo).

Pero también hay algunas razones teóricas, también. Básicamente, no conocemos muchas maneras de inventar sistemáticamente programas cuyas pruebas de corrección sean muy largas. El método principal es (1) tomar la lógica en la que demuestra la corrección, (2) encontrar una propiedad que no se puede expresar directamente en esa lógica (las pruebas de consistencia son la fuente típica) y (3) encontrar un programa cuya La prueba de corrección se basa en una familia de consecuencias expresables de la propiedad inexpresable. Debido a que (2) es inexpresable, esto significa que la prueba de cada consecuencia expresable debe hacerse de forma independiente, lo que le permite hacer explotar el tamaño de la prueba de corrección. Como un ejemplo simple, tenga en cuenta que en la lógica de primer orden con una relación principal, no puede expresar la relación ancestral.kk) es expresable, para cada fijo . Entonces, al dar un programa que use alguna propiedad de ascendencia hasta cierta profundidad (por ejemplo, 100), puede forzar una prueba de corrección en FOL para que contenga pruebas de esas propiedades cien veces más.k

La versión sofisticada de este tema se llama "matemática inversa", y es el estudio de qué axiomas se requieren para probar teoremas dados. No sé mucho al respecto, pero si publica una pregunta sobre su aplicación en CS, y estoy seguro de que al menos Timothy Chow, y probablemente varias otras personas, podrán contarle cosas interesantes.


1
¿Podría explicar un poco más este punto "solo vamos a escribir programas con pruebas de terminación manejables"?
Dai Le

Gracias por tu respuesta actualizada! Tu respuesta realmente abre mi perspectiva. En realidad, yo también trabajo un poco en "matemática inversa", pero no me di cuenta de la conexión que mencionaste. ¡Gracias de nuevo!
Dai Le

1
El punto en su edición está relacionado con el hecho de que casi no tenemos candidatos para tautologías que requieren pruebas largas en sistemas de pruebas naturales (como, por ejemplo, Frege). Parte de la razón de esto es que la única forma en que conocemos una tautología es tautóloga en primer lugar es porque teníamos una prueba en mente, ¡que necesariamente no fue tan larga!
Joshua Grochow

22

Creo que la respuesta a la primera pregunta es que, en general, es demasiado trabajo con las herramientas actuales. Para tener la sensación, sugiero intentar probar la corrección de Bubble Sort en Coq (o si prefieres un poco más de desafío, usa Quick Sort). No creo que sea razonable esperar que los programadores escriban programas verificados siempre y cuando demostrar que la corrección de estos algoritmos básicos es tan difícil y requiere mucho tiempo.

Esta pregunta es similar a preguntar por qué los matemáticos no escriben pruebas formales verificables por verificadores de pruebas. Escribir un programa con una prueba formal de corrección significa probar un teorema matemático sobre el código escrito, y la respuesta a esa pregunta también se aplica a su pregunta.

Esto no significa que no haya habido casos exitosos de programas verificados. Sé que hay grupos que están demostrando la corrección de sistemas como el hipervisor de Microsoft . Un caso relacionado es el compilador C verificado de Microsoft . Pero, en general, las herramientas actuales necesitan mucho desarrollo (incluidos sus aspectos SE y HCI) antes de ser útiles para programadores generales (y matemáticos).

Con respecto al párrafo final de la respuesta de Neel sobre el crecimiento del tamaño del programa para idiomas con funciones totales, en realidad es fácil probar aún más (si lo entendí correctamente). Es razonable esperar que la sintaxis de cualquier lenguaje de programación sea ce y el conjunto de funciones computables totales no sea ce, por lo que para cualquier lenguaje de programación donde todos los programas sean totales, existe una función computable total que ningún programa puede calcular ( de cualquier tamaño) en ese idioma.


Para la segunda pregunta, respondí una pregunta similar en el blog de Scott hace algún tiempo. Básicamente, si la clase de complejidad tiene una buena caracterización y es computablemente representable (es decir, es ce), entonces podemos demostrar que alguna representación de los problemas en la clase de complejidad es demostrablemente total en teorías muy débiles correspondientes a la clase de complejidad. La idea básica es que las funciones probables totales de la teoría contienen todas las funciones y un problema que es A C 0AC0AC0-completo para la clase de complejidad, por lo tanto, contiene todos los problemas en la clase de complejidad y puede probar la totalidad de esos programas. La relación entre las pruebas y la teoría de la complejidad se estudia en la prueba de la complejidad, vea el reciente libro de SA Cook y P. Nguyen " Fundamentos lógicos de la complejidad de la prueba " si está interesado. (Un borrador de 2008 está disponible.) Entonces, la respuesta básica es que para muchas clases "Probablemente C = C".

Esto no es cierto en general, ya que hay clases de complejidad semántica que no tienen caracterización sintáctica, por ejemplo, funciones computables totales. Si por recursivo te refieres a funciones recursivas totales, entonces las dos no son iguales, y el conjunto de funciones computables que son demostrablemente totales en una teoría está bien estudiado en la literatura de teoría de pruebas y se llaman funciones demostrablemente totales de la teoría. Por ejemplo: las funciones probables totales de son funciones recursivas ϵ 0 (o funciones equivalentes en el sistema T de Godel ), las funciones probables totales de P A 2 son funciones en el sistema F de Girard , las funciones probables totales dePAϵ0TPA2F son funciones recursivas primitivas, ....IΣ1

Pero no me parece que esto signifique mucho en el contexto de verificación de programas, ya que también hay programas que calculan extensivamente la misma función pero no podemos probar que los dos programas calculan la misma función, es decir, los programas son extensionalmente iguales pero no intencionalmente. (Esto es similar al Morning Star y al Evening Star). Además, es fácil modificar un programa comprobablemente total dado para obtener uno que la teoría no puede probar su totalidad.


Creo que las dos preguntas están relacionadas. El objetivo es obtener un programa verificado. Un programa verificado significa que el programa satisface una descripción, que es una declaración matemática. Una forma es escribir un programa en un lenguaje de programación y luego probar sus propiedades como si satisface la descripción, que es la práctica más común. Otra opción es intentar probar el enunciado matemático que describe el problema utilizando medios restringidos y luego extraer un programa verificado de él. Por ejemplo, si demostramos en la teoría correspondiente a que para cualquier número dado n hay una secuencia de números primos cuyo producto es igual a n , entonces podemos extraer un PPnnPalgoritmo de factorización a partir de la prueba. (También hay investigadores que intentan automatizar el primer enfoque tanto como sea posible, pero verificar las propiedades interesantes no triviales de los programas es computacionalmente difícil y no puede verificarse completamente sin falsos positivos y negativos).


3
¡Buena respuesta! Usted menciona que una forma es extraer programas de las pruebas, que es algo que uno puede hacer automáticamente en Coq, por ejemplo. Un área relacionada es la minería de pruebas , donde las personas (que trabajan generalmente en lógica matemática) intentan extraer información de una prueba dada. Por ejemplo, en algunos casos es posible (automáticamente) encontrar una prueba intuitiva dada una clásica.
Radu GRIGore

1
Π20PAHA

1
Andrej Bauer tiene una nueva publicación interesante en su blog en la que demuestra la interpretación dialéctica de Godel en Coq .
Kaveh

18

Lo que usted pregunta en la primera pregunta a veces se llama "compilador de verificación", y hace unos años Tony Hoare lo ofreció como un gran desafío para la investigación informática . Hasta cierto punto, esto ya existe y está en uso activo en herramientas como el demostrador del teorema de Coq , que organiza el problema mediante la teoría de tipos y el principio de proposiciones como tipos (" Curry-Howard ").

EDITAR: solo quería agregar énfasis en "hasta cierto punto". Esto está lejos de ser un problema resuelto, pero el éxito de Coq da la esperanza de que no sea un sueño imposible.


8
Yo diría que construir software verificado es donde estaba la construcción de software antiguo en 1956. Ya era obvio que el software iba a ser increíblemente importante, y ya había grandes historias de éxito. Sin embargo, a la gente todavía le faltaban muchos conceptos fundamentales (una comprensión clara de qué procedimientos y variables eran, por ejemplo), y la distancia de la teoría a la práctica podría ser tan corta como implementar Lisp al programar el código en un teorema. Este es un momento INCREÍBLEMENTE emocionante para trabajar en idiomas y verificación.
Neel Krishnaswami

12

Una herramienta que verifica si un programa es correcto a veces se llama verificador de programa. En este contexto, "correcto" generalmente significa dos cosas: que el programa nunca produce ciertas salidas (piense en la falla de segmentación, NullPointerException, etc.) y que el programa está de acuerdo con una especificación.

El código y la especificación pueden estar de acuerdo y aún ser percibidos como incorrectos. En cierto sentido, pedirles a los desarrolladores que escriban especificaciones es como pedirles a dos desarrolladores que resuelvan el problema. Si las dos implementaciones están de acuerdo, entonces tiene mayor confianza en que están bien. En otro sentido, sin embargo, las especificaciones son mejores que una segunda implementación. Debido a que la especificación no necesita ser eficiente o incluso ejecutable, puede ser mucho más sucinta y, por lo tanto, más difícil de equivocar.

Con estas advertencias en mente, le recomiendo que mire el verificador del programa Spec # .


Hasta donde entiendo la especificación # (y su extensión Sing #), le da al programador la capacidad de verificar estáticamente afirmaciones, pero no requiere que el programador haga esto, ni proporciona la capacidad de probar propiedades arbitrarias del código.
Alex ten Brink

Las propiedades arbitrarias pueden codificarse como afirmaciones siguientes, en principio. No estoy seguro de qué quiere que requiera la herramienta. ¿Desea que las especificaciones digan cuál debería ser la salida para todas las entradas posibles?
Radu GRIGore

4

En el caso general, es imposible crear un algoritmo que confirme si un algoritmo es equivalente a una especificación. Esta es una prueba informal:

Casi todos los lenguajes de programación son completos de Turing. Por lo tanto, cualquier idioma decidido por un TM también puede ser decidido por un programa escrito en este idioma.

Equivalence/TM

Equivalence/TMNonemptiness/TMEmptiness/TMEmptiness/TMEquivalence/TMEquivalence/TMTambién es inaceptable. Por lo tanto, puede usar un algoritmo si dos máquinas no son equivalentes o no, pero no puede estar seguro de si son equivalentes o si no le ha dado suficiente tiempo a su algoritmo.

Sin embargo, esto es solo para el caso general. Es posible que pueda decidir si las especificaciones son o no equivalentes al programa, resolviendo una versión más relajada del problema. Por ejemplo, puede examinar solo una cantidad de entradas o decir que los dos programas son equivalentes con cierta incertidumbre. De esto se trata la prueba de software.

En cuanto al resto de sus preguntas:

Nota: Esta parte ha sido editada para aclaración. Resulta que cometí el error que estaba tratando de evitar, lo siento.

TrueRTrueRR

ProvableR=TrueRProvableRTrueRTrueRProvableRAϵTrueRAAAϵProvableR

Informalmente, esto se puede resumir como: No sabes que un idioma es decidible hasta que lo hayas demostrado. Entonces, si en un sistema formal tiene el conocimiento de que un idioma es decidible, este conocimiento también puede servir como prueba de ello. Por lo tanto, no puede tener el conocimiento simultáneo de que un idioma es a la vez decidible y no se puede probar, por lo que estas dos declaraciones son mutuamente excluyentes.

RProvableRProvableRRR

@Kaveh lo resume mejor: demostrable siempre significa demostrable en algún sistema / teoría y no coincide con la verdad en general.

Lo mismo vale para cualquier otra clase de complejidad: para determinar la membresía, primero necesita una prueba. Es por eso que creo que su segunda pregunta es demasiado general, ya que contiene no solo la teoría de la complejidad, sino también la teoría de la computación, dependiendo de la propiedad que desee que tenga el lenguaje.


1
RProvableRΣ30Σ10

1
Probable siempre significa demostrable en algún sistema / teoría y no coincide con la verdad en general.
Kaveh

1
Ahora veo que para que mi pregunta sea interesante, uno debería hablar sobre el conjunto de máquinas de Turing que se detienen, no el conjunto de lenguajes decidibles.
Alex ten Brink

1
@ Alex Bueno, necesitas hablar de idiomas, pero hay muchos. Por lo tanto, si desea hablar sobre idiomas conectados a algún objeto finito (como una prueba), debe restringirse a los idiomas identificables por un objeto finito, como un TM.
Mark Reitblatt

2
R

3

La siguiente monografía clásica estudia casi exactamente su segunda pregunta:

Hartmanis, J. Cálculos factibles y propiedades de complejidad comprobables , CBMS-NSF Regional Conference Series in Applied Mathematics, 30. Society for Industrial and Applied Mathematics (SIAM), Philadelphia, Pa., 1978.

{L(Mi)|Ti(n)T(n) is provable in F}MiTi(n)Min

T(n)nlog(n)g(n)1FTIME[T(n)]TIME[T(n)g(n)]

T(n)FTIME[T(n)]TIME[T(n)]

T(n)nlog(n)TIME[T(n)]={L(Mi)|F proves(j)[L(Mj)=L(Mi)Tj(n)T(n)]}

Sin embargo, para el espacio, la situación está mejor controlada:

s(n)nSPACE[s(n)]=FSPACE[s(n)]

SPACE[S(n)]=FSPACE[S(n)]S(n)ns(n)SPACE[S(n)]=SPACE[s(n)]


1

La pregunta tiene que ser planteada correctamente. Por ejemplo, nadie quiere saber si un programa real se completaría con una memoria infinita dada y algunos medios para acceder a él (tal vez una operación para mover la dirección base en algún número). El teorema de Turing es irrelevante para la corrección del programa en cualquier sentido concreto y las personas que lo citan como una barrera para la verificación del programa confunden dos cosas muy diferentes. Cuando los ingenieros / programadores hablan de la corrección del programa, quieren saber acerca de las propiedades finitas. Esto también es bastante cierto para los matemáticos que están interesados ​​en saber si algo es demostrable. La carta de Godel http://vyodaiken.com/2009/08/28/godels-lost-letter/ explica esto con cierto detalle.

Es decir, obviamente significaría que, a pesar de la indecidibilidad del problema Entscheidungs, el trabajo mental de un matemático con respecto a las preguntas de Sí o No podría ser completamente reemplazado por una máquina. Después de todo, uno simplemente tendría que elegir el número natural n tan grande que cuando la máquina no entrega un resultado, no tiene sentido pensar más sobre el problema.

Bien puede ser inviable examinar el inmenso conjunto de estados de un programa que se ejecuta en una computadora real y detectar estados incorrectos, no hay una razón teórica por la que no se pueda hacer. De hecho, ha habido un gran progreso en este campo; por ejemplo, consulte http://www.cs.cornell.edu/gomes/papers/SATSolvers-KR-book-draft-07.pdf (gracias a Neil Immerman por contándome sobre esto)

Un problema diferente y más difícil es especificar exactamente qué propiedades desea que tenga un programa para que sea correcto.

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.