Cosa sappiamo dei programmi dimostrabilmente corretti?


37

La sempre crescente complessità dei programmi per computer e la posizione sempre più cruciale che i computer hanno nella nostra società mi lascia chiedermi perché non utilizziamo ancora collettivamente linguaggi di programmazione in cui è necessario fornire una prova formale del corretto funzionamento del codice.

Credo che il termine sia un "compilatore di certificazione" (l'ho trovato qui ): un compilatore che compila un linguaggio di programmazione in cui non solo è necessario scrivere il codice, ma anche specificare le specifiche del codice e dimostrare che il codice aderisce al specifica (o utilizzare un prover automatizzato per farlo).

Durante la ricerca in Internet, ho trovato solo progetti che utilizzano un linguaggio di programmazione molto semplice o progetti falliti che cercano di adattare i linguaggi di programmazione moderni. Questo mi porta alla mia domanda:

Esistono compilatori di certificazione che implementano un linguaggio di programmazione completo o è molto difficile / teoricamente impossibile?

Inoltre, devo ancora vedere qualsiasi classe di complessità che coinvolga programmi dimostrabili, come "la classe di tutte le lingue decidibile da una macchina di Turing per la quale esiste una prova che questa macchina di Turing si ferma", che chiamerò , come analogo a , l'insieme delle lingue ricorsive.ProvableRR

Vedo i vantaggi di studiare una tale classe di complessità: per esempio, per il problema di Halting è decidibile (ho anche ipotizzato che definito in modo ovvio sarebbe la più grande classe di lingue per cui è decidibile). Inoltre, dubito che escluderemmo qualsiasi programma praticamente utile: chi userebbe un programma quando non puoi provare che termina?ProvableRProvableRE

Quindi la mia seconda domanda è:

Cosa sappiamo delle classi di complessità che richiedono che le loro lingue di contenimento abbiano determinate proprietà?


1
Un compilatore potrebbe enumerare tutte le possibili prove di lunghezza i, lasciando che io passi da 1 a infinito, fino a quando non trova una prova che il programma si interrompe. Se richiediamo che l'input per il compilatore si arresti in modo dimostrabile, allora il compilatore troverà sempre quella prova. Dato che il problema dell'arresto è indecidibile, dobbiamo concludere che esistono programmi che fermano, ma non esistono prove a riguardo. Il punto cruciale è che i programmi non sono in grado di scoprire se esiste una prova, non che non sono in grado di trovare una prova se esiste.
Alex ten Brink

3
Penso che dovresti dividerli. Sono domande diverse con risposte diverse.
Mark Reitblatt,

4
Sulla prima domanda, un documento influente è "Processi sociali e prove di teoremi e programmi", portal.acm.org/citation.cfm?id=359106
Colin McQuillan,

1
La verifica del programma è indecidibile. Quindi un problema è quello di dire ciò che costituisce una buona soluzione. Vedi cstheory.stackexchange.com/questions/4016/…
Radu GRIGore

2
@Colin: vale la pena leggere quel documento per l'analisi delle prove, ma le sue previsioni sono state falsificate. Oggi disponiamo di compilatori, kernel del sistema operativo, garbage collector e database corretti, tutti ritenuti impossibili. Il trucco per sfuggire alla loro critica era evitare la verifica umana dei dettagli di basso livello delle prove formali, ma usare la verifica automatica delle prove e usare gli umani per verificare il correttore di prove. Il riferimento di Noam alla teoria dei tipi è dove si trova lo stato dell'arte, che lascia i programmi imperativi in ​​una sorta di vincolo poiché la teoria dei tipi è funzionale.
Neel Krishnaswami,

Risposte:


28

"Certificatore di compilazione" di solito significa qualcosa di leggermente diverso: significa che hai un compilatore che può dimostrare che il codice macchina che emette implementa correttamente la semantica di alto livello. Cioè, questa è una prova che non ci sono bug del compilatore. I programmi che le persone danno al compilatore possono ancora essere sbagliati, ma il compilatore genererà una versione corretta del codice macchina del programma sbagliato. La più grande storia di successo in questo senso è il compilatore verificato CompCert , che è un compilatore per un ampio sottoinsieme di C.

Il compilatore Compcert stesso è un programma con una prova di correttezza (eseguita in Coq), che garantisce che se genera codice per un programma, sarà corretto (rispetto alla semantica operativa di assembly & C utilizzata dai progettisti di CompCert). Lo sforzo di controllare automaticamente queste cose è piuttosto grande; in genere la prova di correttezza sarà compresa tra 1 e 100 volte la dimensione del programma che si sta verificando. Scrivere programmi e prove controllati automaticamente è una nuova abilità che devi imparare - non è matematica o programmazione come al solito, anche se dipende dalla capacità di fare entrambe le cose bene. Sembra che tu stia ricominciando da capo, come essere di nuovo un programmatore alle prime armi.

Non ci sono barriere teoriche speciali a questo, però. L'unica cosa su questa linea è il teorema di Blum Size che per qualsiasi lingua in cui tutti i programmi sono totali, puoi trovare un programma in una lingua ricorsiva generale che sarà almeno esponenzialmente più grande quando programmato nella lingua totale. Il modo per comprendere questo risultato è che una lingua totale codifica non solo un programma, ma anche una prova di terminazione. Quindi puoi avere programmi brevi con prove di terminazione lunghe. Tuttavia, questo non ha molta importanza in pratica, dato che scriveremo solo programmi con prove di terminazione gestibili.

EDIT: Dai Le ha chiesto qualche elaborazione dell'ultimo punto.

Questa è principalmente un'affermazione pragmatica, basata sul fatto che se riesci a capire perché un programma funziona, è improbabile che la ragione sia lunga milioni di pagine invarianti. (Gli invarianti più lunghi che ho usato sono lunghi poche pagine, e ragazzi fanno brontolare i recensori! Comprensibilmente, anche perché l'invariante è la ragione per cui il programma funziona spogliato di tutta la narrazione che aiuta le persone a capirlo.)

Ma ci sono anche alcune ragioni teoriche. Fondamentalmente, non conosciamo molti modi per inventare sistematicamente programmi le cui prove di correttezza sono molto lunghe. Il metodo principale è (1) prendere la logica in cui si dimostra la correttezza, (2) trovare una proprietà che non può essere direttamente espressa in quella logica (le prove di coerenza sono la fonte tipica) e (3) trovare un programma il cui la prova di correttezza si basa su una famiglia di conseguenze espressibili della proprietà indicibile. Poiché (2) è inesprimibile, ciò significa che la prova di ogni conseguenza esprimibile deve essere eseguita in modo indipendente, il che consente di far esplodere la dimensione della prova di correttezza. Come semplice esempio, si noti che nella logica del primo ordine con una relazione principale, non è possibile esprimere la relazione antenata.kk) è espressibile, per ogni fisso . Quindi, dando un programma che utilizza alcune proprietà degli antenati fino a una certa profondità (diciamo, 100), puoi forzare una prova di correttezza in FOL per contenere prove di tali proprietà centinaia di volte.k

La sofisticata interpretazione di questa materia si chiama "matematica inversa" ed è lo studio di quali assiomi sono necessari per dimostrare dati teoremi. Non ne so molto, ma se pubblichi una domanda sulla sua applicazione a CS, e sono sicuro che almeno Timothy Chow, e probabilmente molte altre persone, saranno in grado di dirti cose interessanti.


1
Potresti per favore elaborare questo punto "scriveremo solo programmi con prove di terminazione gestibili" ancora un po '?
Dai Le

Grazie per la tua risposta aggiornata! La tua risposta apre davvero la mia prospettiva. In realtà io stesso lavoro un po 'anche sulla "matematica inversa", ma non ho realizzato la connessione che hai citato. Grazie ancora!
Dai Le

1
Il punto nella tua modifica è legato al fatto che non abbiamo quasi candidati per tautologie che richiedono lunghe prove nei sistemi di prove naturali (come, diciamo, Frege). Parte della ragione di ciò è che l'unico modo in cui conosciamo una tautologia è tautologicamente in primo luogo è perché avevamo in mente una prova, che necessariamente non era così lunga!
Joshua Grochow,

22

Penso che la risposta alla prima domanda sia che in genere è troppo lavoro con gli strumenti attuali. Per avere la sensazione, suggerisco di provare a dimostrare la correttezza di Bubble Sort in Coq (o se preferisci un po 'più di sfida, usa Quick Sort). Non penso che sia ragionevole aspettarsi che i programmatori scrivano programmi verificati purché dimostrare la correttezza di tali algoritmi di base sia così difficile e dispendioso in termini di tempo.

Questa domanda è simile alla domanda sul perché i matematici non scrivono prove formali verificabili da correttori di prove? Scrivere un programma con una prova formale di correttezza significa dimostrare un teorema matematico sul codice scritto e la risposta a quella domanda si applica anche alla tua domanda.

Ciò non significa che non si siano verificati casi di successo di programmi verificati. So che ci sono gruppi che stanno dimostrando la correttezza di sistemi come l'hypervisor di Microsoft . Un caso correlato è il compilatore C verificato di Microsoft . Ma in generale gli strumenti attuali richiedono molto sviluppo (compresi i loro aspetti SE e HCI) prima di diventare utili per programmatori generali (e matematici).

Per quanto riguarda l'ultimo paragrafo della risposta di Neel sulla crescita delle dimensioni del programma per le lingue con solo funzioni totali, in realtà è facile dimostrarlo ancora di più (se l'ho capito correttamente). È ragionevole aspettarsi che la sintassi di qualsiasi linguaggio di programmazione sia ce e l'insieme delle funzioni calcolabili totali non sia ce, quindi per qualsiasi linguaggio di programmazione in cui tutti i programmi sono totali esiste una funzione calcolabile totale che non può essere calcolata da nessun programma ( di qualsiasi dimensione) in quella lingua.


Per la seconda domanda, ho risposto a una domanda simile sul blog di Scott qualche tempo fa. Fondamentalmente se la classe di complessità ha una bella caratterizzazione ed è computabilmente rappresentabile (cioè è ce), allora possiamo dimostrare che alcune rappresentazioni dei problemi nella classe di complessità sono dimostrabili totalmente in teorie molto deboli corrispondenti alla classe di complessità. L'idea di base è che le funzioni dimostrabilmente totali della teoria contengano tutte le funzioni e un problema che sia A C 0AC0AC0-completo per la classe di complessità, quindi contiene tutti i problemi nella classe di complessità e può dimostrare la totalità di tali programmi. La relazione tra prove e teoria della complessità è studiata nella complessità delle prove, vedi il recente libro di SA Cook e P. Nguyen " Fondamenti logici della complessità della prova " se sei interessato. (È disponibile una bozza del 2008). Quindi la risposta di base è quella per molte classi "Provvidibilmente C = C".

Questo non è vero in generale poiché ci sono classi di complessità semantica che non hanno caratterizzazione sintattica, ad esempio funzioni calcolabili totali. Se per ricorsivo intendi le funzioni ricorsive totali, allora le due non sono uguali e l'insieme di funzioni calcolabili che sono dimostrabili totalmente in una teoria è ben studiato nella letteratura della teoria delle prove e sono chiamate funzioni dimostrabili totali della teoria. Ad esempio: le funzioni dimostrabilmente totali di sono funzioni ricorsive ϵ 0 (o equivalentemente funzioni nel sistema T di Godel ), le funzioni provabili totali di P A 2 sono funzioni nel sistema F di Girard , le funzioni provabili totali diPAϵ0TPA2F sono funzioni ricorsive primitive, ....IΣ1

Ma non mi sembra che ciò significhi molto nel contesto della verifica dei programmi, dal momento che ci sono anche programmi che calcolano estensivamente la stessa funzione, ma non possiamo dimostrare che i due programmi stiano calcolando la stessa funzione, vale a dire che i programmi sono estensivamente uguali ma non intenzionalmente. (Questo è simile alla Morning Star e alla Evening Star.) Inoltre è facile modificare un dato programma totalmente dimostrabile per ottenerne uno che la teoria non è in grado di dimostrare la sua totalità.


Penso che le due domande siano correlate. L'obiettivo è ottenere un programma verificato. Un programma verificato significa che il programma soddisfa una descrizione, che è una dichiarazione matematica. Un modo è scrivere un programma in un linguaggio di programmazione e quindi dimostrare le sue proprietà come se soddisfacesse la descrizione, che è la pratica più comune. Un'altra opzione è quella di provare a dimostrare l'affermazione matematica che descrive il problema usando mezzi limitati e quindi estrarre un programma verificato da esso. Ad esempio, se dimostriamo nella teoria corrispondente a che per ogni dato numero n c'è una sequenza di numeri primi che il loro prodotto è uguale a n , allora possiamo estrarre una PPnnPalgoritmo per la fattorizzazione dalla prova. (Ci sono anche ricercatori che cercano di automatizzare il primo approccio il più possibile, ma controllare interessanti proprietà non banali dei programmi è computazionalmente difficile e non può essere completamente verificato senza falsi positivi e negativi.)


3
Bella risposta! Dici che un modo è quello di estrarre programmi dalle prove, cosa che si può fare automaticamente in Coq, per esempio. Un'area correlata è il mining di prove , in cui le persone (che lavorano di solito in logica matematica) cercano di estrarre informazioni da una determinata prova. Ad esempio, in alcuni casi è possibile trovare (automaticamente) una dimostrazione intuitiva data una classica.
Radu GRIGore,

1
@Radu: grazie per aver sottolineato che può essere fatto automaticamente in Coq. Come hai detto, si possono anche estrarre alcune informazioni costruttive e quindi programmi dalle prove classiche del teorema usando l'estrazione di prove per alcune classi di formule (le persone interessate possono controllare i documenti di Ulrich Kuhlenbach per i dettagli). Un altro risultato probabilmente correlato è che se un teorema è dimostrato in P A , è anche dimostrabile in modo costruttivo in H A dalla traduzione di Harvey Friedman. _Π20PAHA
Kaveh,

1
Andrej Bauer ha un nuovo interessante post sul suo blog in cui dimostra l'interpretazione dialettica di Godel in Coq .
Kaveh,

18

Quello che ti stai chiedendo nella prima domanda è talvolta chiamato "compilatore di verifica", e qualche anno fa Tony Hoare lo ha offerto come una grande sfida per la ricerca informatica . In una certa misura questo esiste già ed è in uso attivo in strumenti come il prover del teorema di Coq , che organizza il problema attraverso la teoria dei tipi e il principio delle proposizioni come tipi (" Curry-Howard ").

EDIT: volevo solo aggiungere enfasi su "in una certa misura". Questo è ben lungi dall'essere un problema risolto, ma il successo di Coq fa sperare che non sia un sogno irrealizzabile.


8
Direi che la costruzione di software verificato è il luogo in cui si trovava il vecchio software semplice nel 1956. Era già ovvio che il software sarebbe stato incredibilmente importante, e c'erano già importanti storie di successo. Tuttavia, alla gente mancavano ancora molti concetti fondamentali mancanti (una chiara comprensione di quali procedure e variabili fossero, per esempio), e la distanza dalla teoria alla pratica potrebbe essere breve quanto implementare Lisp programmando il codice in un teorema. Questo è un momento incredibilmente emozionante per lavorare su lingue e verifica.
Neel Krishnaswami,

12

Uno strumento che controlla se un programma è corretto a volte viene chiamato un programma di verifica. In questo contesto, "corretto" di solito significa due cose: che il programma non produce mai determinati output (pensa a errore di segmentazione, NullPointerException, ecc.) E che il programma concorda con una specifica.

Il codice e le specifiche possono essere d'accordo e comunque essere percepiti come errati. In un certo senso, chiedere agli sviluppatori di scrivere specifiche è come chiedere a due sviluppatori di risolvere il problema. Se le due implementazioni concordano, allora hai maggiore sicurezza che siano OK. In un altro senso, tuttavia, le specifiche sono migliori di una seconda implementazione. Poiché la specifica non deve essere efficiente o nemmeno eseguibile, può essere molto più concisa e quindi più difficile sbagliare.

Con questi avvertimenti in mente, ti consiglio di guardare il verificatore del programma Spec # .


Per quanto ho capito Spec # (e la sua estensione Sing #), offre al programmatore la possibilità di verificare staticamente affermazioni, ma non richiede che il programmatore lo faccia, né fornisce la possibilità di dimostrare proprietà arbitrarie del codice.
Alex ten Brink,

Le proprietà arbitrarie possono essere codificate come affermazioni fol, in linea di principio. Non sono sicuro di cosa desideri richiedere lo strumento. Vuoi che le specifiche dicano quale dovrebbe essere l'output per tutti i possibili input?
Radu GRIGore,

4

Nel caso generale, è impossibile creare un algoritmo che confermi se un algoritmo è equivalente a una specifica. Questa è una prova informale:

Quasi tutti i linguaggi di programmazione sono completi di Turing. Pertanto, qualsiasi lingua decisa da una TM può essere decisa anche da un programma scritto in questa lingua.

Equivalence/TM

Equivalence/TMNonemptiness/TMEmptiness/TMEmptiness/TMEquivalence/TMEquivalence/TMè anche inaccettabile. Pertanto è possibile utilizzare un algoritmo indipendentemente dal fatto che due macchine non siano equivalenti, ma non si può essere sicuri che siano equivalenti o se non si è concesso abbastanza tempo al proprio algoritmo.

Tuttavia, questo è solo per il caso generale. È possibile decidere se le specifiche sono equivalenti o meno al programma, risolvendo una versione più rilassata del problema. Ad esempio, potresti esaminare solo un numero di input o dire che i due programmi sono equivalenti con qualche incertezza. Questo è il test del software.

Per il resto delle tue domande:

Nota: questa parte è stata modificata per chiarimenti. Si scopre che ho fatto l'errore che stavo cercando di evitare, scusa.

TrueRTrueRR

ProvableR=TrueRProvableRTrueRTrueRProvableRAϵTrueRAAAϵProvableR

Informalmente, questo può essere sintetizzato come: Non sai che una lingua è decidibile fino a quando non ne avrai provato. Quindi se in un sistema formale hai la consapevolezza che una lingua è decidibile, questa conoscenza può anche servire da prova per questo. Pertanto, non è possibile avere contemporaneamente la consapevolezza che una lingua è sia decidibile che non può essere dimostrata in tal modo, queste due affermazioni si escludono a vicenda.

RProvableRProvableRRR

@Kaveh lo riassume al meglio: Provable significa sempre dimostrabile in qualche sistema / teoria e non coincide con la verità in generale.

Lo stesso vale per qualsiasi altra classe di complessità: per determinare l'appartenenza è necessario prima una prova. Questo è il motivo per cui credo che la tua seconda domanda sia troppo generica, poiché contiene non solo la teoria della complessità, ma anche la teoria del calcolo, a seconda della proprietà che desideri che il linguaggio abbia.


1
RProvableRΣ30Σ10

1
Provable significa sempre dimostrabile in qualche sistema / teoria e non coincide con la verità in generale.
Kaveh,

1
Vedo ora che, per rendere interessante la mia domanda, si dovrebbe parlare dell'insieme di macchine di Turing che si fermano, non dell'insieme delle lingue decidibili.
Alex ten Brink,

1
@Alex Beh, hai bisogno di un modo di parlare delle lingue, ma ce ne sono innumerevoli. Quindi, se vuoi parlare di lingue collegate ad un oggetto finito (come una prova), devi limitarti a lingue identificabili da un oggetto finito, come una TM.
Mark Reitblatt,

2
R

3

La seguente monografia classica studia quasi esattamente la tua seconda domanda:

Hartmanis, J. Calcoli fattibili e comprovabili proprietà di complessità , Serie di conferenze regionali CBMS-NSF in Matematica applicata, 30. Società per la matematica industriale e applicata (SIAM), Filadelfia, Pennsylvania, 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)]}.

For space, however, the situation is better controlled:

Theorem 6.9: (1) If s(n)n is space-constructible, then SPACE[s(n)]=F-SPACE[s(n)].

(2) If SPACE[S(n)]=F-SPACE[S(n)] (S(n)n) then there is a space-constructible s(n) such that SPACE[S(n)]=SPACE[s(n)].


1

The question has to be posed correctly. For example, nobody ever wants to know whether an actual program would complete given infinite memory and some means of accessing it (maybe an operation to move the base address by some number). Turing's theorem is irrelevant to program correctness in any concrete sense and people who cite it as a barrier to program verification are confusing two quite different things. When engineers/programmers talk of program correctness they want to know about finite properties. This is also pretty much true for mathematicians who are interested in whether something is provable. Godel's letter http://vyodaiken.com/2009/08/28/godels-lost-letter/ explains this in some detail.

Namely, it would obviously mean that in spite of the undecidability of the Entscheidungsproblem, the mental work of a mathematician concerning Yes-or-No questions could be completely replaced by a machine. After all, one would simply have to choose the natural number n so large that when the machine does not deliver a result, it makes no sense to think more about the problem.

It may well be infeasible to examine the immense state set of a program executing on an actual computer and detect bad states, there is no theoretical reason why it can't be done. In fact, there has been a lot of progress in this field - for example see http://www.cs.cornell.edu/gomes/papers/SATSolvers-KR-book-draft-07.pdf (thanks to Neil Immerman for telling me about this)

A different and more difficult problem is specifying exactly what properties one wants a program to have in order to be correct.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.