Ragionamento del programma sul proprio codice sorgente


15

L'ispirazione per questa domanda è la seguente (vaga) domanda: quali sono il linguaggio di programmazione / le basi logiche per avere un'intelligenza artificiale che potrebbe ragionare sul proprio codice sorgente e modificarlo?

Questo non è affatto rigoroso, quindi ecco il mio tentativo di estrarre una domanda concreta da esso. Ci sono due cose che mi interessano:

(A) Un linguaggio di programmazione P che può rappresentare e manipolare i propri programmi come un tipo di dati Programma (ad es. Come AST). (Se lo si desidera, un oggetto di tipo Programma può essere convertito in una stringa, che è il testo di un programma valido in quella lingua. Questo sarebbe l'opposto di quello che fa un compilatore.)

(B) Un metodo per ragionare su ciò che fa un programma in lingua P. Ecco due livelli a cui sto pensando:

  1. Un altro linguaggio Q (con capacità di dimostrazione del teorema) che modella ciò che fa un programma P. Dovrebbe essere in grado di esprimere e provare affermazioni come "il risultato dell'esecuzione del Programma p è foo".
  2. Un modo di ragionare su ciò che un programma p: Programma fa nella stessa lingua P. (Quindi stiamo prendendo P = Q sopra.)

In che misura è stato implementato qualcosa di simile o quali sono i progressi in questa direzione? Quali sono gli ostacoli pratici? Alla luce dell'intenzione originale della domanda, qual è il modo migliore per formalizzare il problema?

*

Come mostrano le risposte (grazie!), Sia (A) che (B1) possono essere fatti separatamente, anche se sembra che farli insieme sia più una domanda di ricerca.

Ecco alcuni dei miei primi pensieri sulla domanda (avvertimento: piuttosto vago). Vedi anche i miei commenti sulla risposta di Martin Berger.

Sono interessato al linguaggio di programmazione che modella lo stesso linguaggio di programmazione, piuttosto che un linguaggio più semplice (quindi P = Q sopra). Questa sarebbe una "prova di concetto" di un programma in grado di "ragionare sul proprio codice sorgente". I linguaggi di programmazione tipicamente dipendenti possono fornire garanzie sugli output delle sue funzioni, ma questo non conta come "ragionamento sul proprio codice sorgente" non più di un "Hello world!" conta come un quine in una lingua che stampa automaticamente una stringa nuda --- ci deve essere una sorta di citazione / autoreferenziazione. L'analogo qui sta avendo un tipo di dati che rappresenta il Programma.

Sembra un progetto piuttosto grande: più semplice è la lingua, più difficile è esprimere tutto al suo interno; più complicata è la lingua, maggiore è il lavoro da fare per modellare la lingua.

Nello spirito del Teorema della ricorsione, un programma può quindi "ottenere" il proprio codice sorgente e modificarlo (ovvero generare una versione modificata di se stesso). (B2) ci dice quindi che il programma dovrebbe essere in grado di esprimere una garanzia sul programma modificato (questo dovrebbe essere in grado di ricorrere, vale a dire, dovrebbe essere in grado di esprimere qualcosa su tutte le future modifiche-?).


1
Perché hai bisogno che la lingua agisca come teorema proverante per stabilire che "il risultato dell'esecuzione del Programma p è"? La lingua potrebbe semplicemente eseguire p! Anzi, è quello che sta succedendo.
Martin Berger,


3
Si noti che, in linea di principio, tutto il linguaggio che può implementare un interprete per se stesso può fare ciò che si richiede. In un modo più matematico, il teorema della ricorsione vale per qualsiasi modello di calcolo abbastanza forte. Alcuni linguaggi di programmazione rendono più semplice il fatto di averlo incorporato. Lo stesso vale per il ragionamento: è possibile implementare qualsiasi sistema di ragionamento all'interno di questi linguaggi. Ovviamente non ci si può aspettare di ragionare su tutto, ad esempio il problema di arresto dei programmi.
Kaveh,

2
Penso che la domanda non sia molto chiara. Dovresti dare un'occhiata ai linguaggi di programmazione come Python, Java e quelli menzionati da Martin nella sua risposta e chiarire la domanda in modo che sia chiaro che soddisfino ciò che stai cercando o, se no, perché no.
Kaveh,

1
@HoldenLee Quanto a "P = Q", la terminologia stabilita è "meta-programmazione omogenea", che si contrappone a "meta-programmazione eterogenea" in cui P Q.
Martin Berger,

Risposte:


14

Penso che tu stia chiedendo due cose diverse.

  • La capacità di un linguaggio di programmazione di rappresentare tutti i suoi programmi come dati.
  • Ragionamento sui programmi come dati.

A fini analitici è utile tenerli separati. Mi concentrerò sul primo.

La capacità di un linguaggio di programmazione di rappresentare, manipolare (ed eseguire) i suoi programmi come dati va sotto termini come meta-programmazione o omoiconicità .

In un modo (imbarazzante), tutti i linguaggi di programmazione ben noti possono fare meta-programmazione, vale a dire usando il tipo di dati stringa insieme alla capacità di invocare programmi esterni (compilatore, linker ecc.) Su stringhe (ad es. Scrivendoli nel file sistema prima). Tuttavia, probabilmente non è quello che intendi. Probabilmente hai in mente una bella sintassi. Le stringhe non sono una buona sintassi per la rappresentazione del programma perché quasi tutte le stringhe non rappresentano i programmi, ovvero il tipo di dati delle stringhe contiene molta "spazzatura" se visto come un meccanismo di rappresentazione del programma. A peggiorare le cose, l'algebra delle operazioni sulle stringhe non ha sostanzialmente alcun legame con l'algebra della costruzione del programma.

Quello che probabilmente hai in mente è qualcosa di molto più bello. Ad esempio, se è un programma, allora è , ma come dati, a portata di mano per la manipolazione e l'analisi. Questo è spesso chiamato preventivo . In pratica, la quotazione non è flessibile, quindi usiamo invece la quasi-quotazione , che è una generalizzazione della quotazione in cui la quotazione può avere "buchi" in cui possono essere eseguiti programmi che forniscono dati per "riempire" i buchi. Ad esempio è una quasi virgoletta che rappresenta un condizionale dove invece di una condizione abbiamo un bucoPP i fPP[ ] M x > 0 i f

iof[]then7elSe8+9
[] . Se il programma restituisce i dati , la quasi-quotazione restituisce i datiMX>0i f
iof[M]then7elSe8+9
iofX>0then7elSe8+9.

(Si noti che è un programma normale (non un programma come dati) che restituisce un programma quotato, ovvero un programma come dati.) Perché questo funzioni, è necessario un tipo di dati per rappresentare i programmi. Tipicamente quel tipo di dati è chiamato AST (albero di sintassi astratto), e puoi vedere (quasi) virgolette come meccanismi di abbreviazione per AST.M

Diversi linguaggi di programmazione offrono quasi virgolette e altre funzionalità per la meta-programmazione. Fu Lisp con la sua funzionalità di macro che ha aperto la strada a questa capacità di trattare i programmi come dati. Forse sfortunatamente, il potere delle macro basate su Lisp è stato a lungo visto basarsi in gran parte sulla sintassi minimalista di Lisp; non è stato fino a MetaML (1) che un linguaggio moderno e sintatticamente ricco si è dimostrato capace di meta-programmare. Da allora, MetaOCaml (2) (un discendente di MetaML, importante per la sua svolta nella ricerca ancora in corso per risolvere il problema di come digitare i programmi come dati), Template Haskell (3) e Converge (4) (la prima lingua a ottenere tutte le principali funzionalità di meta-programmazione proprio secondo me) hanno dimostrato che una varietà di moderni linguaggi di programmazione può ospitare meta-programmazione. È importante rendersi conto che possiamo prenderequalsiasi linguaggio di programmazione e trasformarlo in un linguaggio di meta-programmazione L m p che è L insieme alla capacità di rappresentare (e valutare) i propri programmi come dati.LLmpL

evun'l()PPPPevun'l(P)PP

Quanto alla seconda dimensione, ragionamento sui programmi forniti come dati. Non appena è possibile convertire i programmi in dati, questi sono dati "normali" e possono essere considerati come dati. Puoi usare qualsiasi tipo di tecnologia prover, ad esempio tipi o contratti dipendenti o dimostratori di teoremi interattivi o strumenti automatizzati, come ha sottolineato Joshua. Tuttavia dovrai rappresentare la semantica della tua lingua nel processo di ragionamento. Se quel linguaggio, come richiesto, ha abilità di meta-programmazione, le cose possono diventare un po 'complicate e non è stato fatto molto lavoro in questa direzione, con (5) l'unica logica di programma per questo scopo. Esiste anche un lavoro basato su Curry-Howard sul ragionamento sulla meta-programmazione (6, 7, 8). Si noti che questi approcci basati sulla logica, e l'approccio basato sul tipo (2) può effettivamente esprimere proprietà che valgono per tutte le future fasi di meta-programmazione. A parte (2) nessuno di questi documenti è stato implementato.

In sintesi: ciò che hai chiesto è stato implementato, ma è abbastanza sottile e ci sono ancora domande aperte, in particolare a che fare con i tipi e il ragionamento semplificato.


  1. W. Taha. Programmazione in più fasi: teoria e applicazioni .

  2. W. Taha e MF Nielsen. Classificatori di ambiente .

  3. T. Sheard e S. Peyton Jones. Meta-programmazione di template per Haskell .

  4. L. Tratt. Meta-programmazione in fase di compilazione in un linguaggio OO tipizzato in modo dinamico .

  5. M. Berger, L. Tratt, Logiche di programma per meta-programmazione omogenea .

  6. R. Davies, F. Pfenning, un'analisi modale del calcolo graduale .

  7. R. Davies, un approccio logico-temporale all'analisi del tempo di legame .

  8. T. Tsukada, A. Igarashi. Una base logica per i classificatori di ambiente .


Hai ragione: il linguaggio di programmazione P può essere diverso dal linguaggio Q che esprime teoremi / prove sui programmi in quel linguaggio (che può essere in Coq per esempio). Il tipo di teorema a cui sto pensando va in questo modo: supponiamo di avere un programma accuratamente progettato A_1. Thm: per ogni n vale: il programma A_n genererà (m_n, A_ {n + 1}), dove m_n è un numero intero, A_ {n + 1} è un altro programma (ad esempio, ottenuto modificando A_n in qualche modo) , e per tutti n, abbiamo che m_n> 0.
Holden Lee,

(La versione di fantascienza di questo è che abbiamo una "prova" che un programma che continua a modificarsi, non premerà mai il pulsante che lancia un missile nucleare, diciamo, o che il programma ottimizzerà sempre una certa quantità.)
Holden Lee,

Questo è il motivo per cui volevo fare una distinzione tra l'esecuzione del programma e il ragionamento su ciò che il programma produrrà: vogliamo conoscere le proprietà di ciò che farà prima che venga eseguito, senza eseguirlo. Nota che se vogliamo che A_n sia in grado di "modificare il suo codice sorgente" per produrre A_ {n + 1}, allora P avrà necessariamente capacità di metaprogrammazione (che ci mette nella posizione di (5)).
Holden Lee,

Mi sembra ancora che sarebbe interessante per P = Q. Ipoteticamente, A è un programma di intelligenza artificiale e il modo in cui si modificherà è ragionando sul proprio codice - per esempio, annota teoremi su bit di codice, dimostrandoli e solo successivamente modificando il suo codice. Quindi sembra che P dovrebbe avere le capacità di Q.
Holden Lee,

@HoldenLee È possibile scrivere programmi come A_n. Se usi stringhe come rappresentante di programmi, questo può essere fatto in modo banale in qualsiasi lingua, se vuoi quasi virgolette o simili, questo è possibile ad es. Converge. Non capisco il ruolo di m_n nella costruzione.
Martin Berger,

6

No, non esiste un sistema attuale che esegua tutti e quattro i passaggi del sistema. Se si desidera progettare un sistema, uno dei primi requisiti è il linguaggio omoiconico. Come minimo vorresti che il tuo linguaggio di programmazione principale fosse il più piccolo possibile in modo che quando entri nel sistema e inizi a farlo interpretare da solo, funzionerà. Quindi, quindi, vuoi un interprete metacircolare, che è stato il pioniere in lisp. Anche altre lingue l'hanno fatto, ma esiste un'enorme quantità di ricerche esistenti sul lisp.

Il primo passo se vuoi fare questo è avere un linguaggio omoiconico come Lisp o qualche framework in cui puoi ragionare su un programma in esecuzione. Lisp è usato per questo per il solo motivo che potresti definire un interprete metacircolare nella lingua o puoi semplicemente trattare il tuo codice come dati. Trattare il codice come dati è la cosa più importante. C'è una lunga discussione su cosa significhi omoiconico su wiki c2.

Ad esempio, in Lisp il tipo di dati "Programma" è un programma lisp valido. Passi i programmi lisp a un interprete e calcola qualcosa. Viene rifiutato dall'interprete se non si programma un "Programma" valido.

Pertanto un linguaggio omoiconico soddisfa tre delle tue esigenze. Puoi anche definire l'idea di un programma formale.

Puoi modellare lisp dentro lisp? Sì, questo viene spesso fatto principalmente come un esercizio alla fine di un libro di programmazione lisp per testare le tue abilità. SICP

Al momento il numero quattro è una domanda di ricerca e di seguito è quello che ho scoperto che tenta di rispondere a questa domanda.

Direi che ci sono molti tipi di programmi che tentano di farlo. Di seguito sono riportati tutti i programmi che conosco.

  • JSLint è un esempio di analizzatori statici che prendono il codice macchina o qualche altra lingua e cercano esplicitamente i bug. Quindi chiede a un programmatore di correggerlo.

  • Coq è un ambiente che ti consente di specificare prove usando un linguaggio di programmazione. Ha anche tattiche in cui ti suggerisce modi per risolvere il problema. Tuttavia, ciò si aspetta che un umano faccia il lavoro. Coq utilizza tipi dipendenti per funzionare ed è quindi molto teorico. È molto popolare tra gli informatici e le persone che lavorano sulla teoria dei tipi di omotopia.

  • ACL2 d'altra parte è un proveratore di teoremi automatizzato. Questo sistema dimostrerà le dichiarazioni basate su qualcosa che programmate.

  • ACL2 e Coq hanno un plug-in software che interfaccia il loro sistema con un sistema di apprendimento automatico . Ciò che viene utilizzato per addestrare questi sistemi sono programmi precedentemente scritti. Dalla mia comprensione, questi sistemi aggiungono un'altra caratteristica in cui non hai solo le tue tattiche ma ti hanno suggerito teoremi che aiutano nello sviluppo di prove.

Di seguito è riportata una base di ciò che significa la tua domanda.

  • gcc è un esempio di un compilatore optimizng che può prendere se stesso come input e quindi produrre una versione ottimizzata di se stesso. L'idea di un compilatore che traduce i programmi da una rappresentazione all'altra e migliora la velocità grazie a qualche flag di ottimizzazione è molto ben compresa. Una volta avviato il compilatore e generato un codice macchina valido, è possibile aggiungere un'ottimizzazione e ricompilare il compilatore e rendere una versione più efficiente di se stesso.

1
Non è necessario ridurre al minimo la lingua. È possibile aggiungere le funzionalità di meta-programmazione pertinenti a qualsiasi lingua. Il lavoro MetaML di Taha ha dimostrato questo. In effetti l'aggiunta di funzionalità di meta-programmazione è meccanica.
Martin Berger,

1
Sono anche in disaccordo sul fatto che "nessun sistema attuale che fa tutti e quattro i passaggi". La domanda 4 parla solo di programmi in esecuzione indicati come codice. Questo è perfettamente possibile, anzi anche lo fa JavaScript.
Martin Berger,

@MartinBerger voglio dire che rendi il kernel core il più minimale possibile. Inoltre, anche se inizi a sperare che il tuo sistema farà il n. 4, vorrai un sistema che possa addestrare non solo gli umani ma anche i computer da usare, quindi ti sarebbe utile avere un sistema minimo e hanno librerie codificate quel sistema come un modello di meta-programmazione
Joshua Herman,

Dipende da cosa (4) stiamo parlando. La domanda originale contiene due elaborazioni di questi. Il primo è banale, basta eseguire il programma. Il secondo può essere gestito dalla logica che ho citato come (5) nella mia risposta al sistema di battitura (2). Il successivo è implementato in MetaOCaml. Ma c'è spazio per ulteriori ricerche: né (2) né (5) sono in grado di gestire forme arbitrarie di meta-programmazione, e le proprietà garantite da (2) sono un po 'deboli (dopo tutto, è un sistema di battitura con inferenza di tipo) .
Martin Berger,

1
Quanto a "rendi il kernel core il più minimo possibile": non è necessario. Puoi aggiungere la meta-programmazione completa a qualsiasi lingua.
Martin Berger,

5

Come menziona la risposta di @ user217281728, esiste un tipo di macchine più legate all'inferenza e all'intelligenza artificiale, chiamate macchine Gödel

Una macchina Gödel è un programma per auto-miglioramento inventato da Jürgen Schmidhuber che risolve i problemi in modo ottimale. Utilizza un protocollo di auto-miglioramento ricorsivo in cui riscrive il proprio codice quando può dimostrare che il nuovo codice fornisce una strategia più ottimale. La macchina è stata inventata da Jürgen Schmidhuber, ma prende il nome da Kurt Gödel che ha ispirato le teorie matematiche.

La pubblicazione citata di Jürgen Schmidhuber su "Macchine Goedel: solutori di problemi universali autoreferenziali che apportano auto-miglioramenti ottimali", (2006) arXiv: cs / 0309048v5

Il modo in cui la macchina lavora per implementare il meta-apprendimento ha due fasi:

  1. Imparare dai dati (livello 1, apprendere)
  2. Utilizzo dei dati appresi per modificare / ottimizzare il suo codice sorgente / algoritmo (livello 2, imparare a imparare)

Poiché la macchina modifica la propria fonte, è autoreferenziale, cioè ha la proprietà di auto-modifica (vedere anche qui ).

In questo senso può modificare l'algoritmo di apprendimento stesso in un senso rigoroso (dimostrando ottimizzazioni di sé). Esistono problemi di autoreferenzialità e indecidibilità che in questo caso assumono la forma:

..una macchina Gödel con risorse computazionali illimitate deve ignorare quegli auto-miglioramenti di cui non può dimostrare l'efficacia

Altre lingue (e le relative macchine interprete associate) che hanno la proprietà di auto-modifica sono ad esempio LISP .

Nel codice LISP i dati sono intercambiabili oppure il codice sorgente AST è disponibile come dati nel programma LISP e può essere modificato come dati. D'altra parte, i dati possono essere visti come AST, per alcuni codici sorgente.

aggiornare

Esistono anche altre macchine , come le macchine di autoprogrammazione (tra le altre) che combinano l' auto-riferimento , l' auto-riproduzione e l' auto-programmazione .

Un aspetto interessante di quanto sopra è che l'autoreferenzialità non è affatto problematica , piuttosto è un elemento necessario negli automi di auto-riproduzione / auto-programmazione .

Per ulteriori dettagli (e altre pubblicazioni) consultare JP Moulin, CR Biologies 329 (2006)

astratto

I sistemi viventi sono in grado di avere risposte adeguate all'ambiente imprevedibile. Questo tipo di auto-organizzazione sembra funzionare come una macchina di autoprogrammazione, cioè una organizzazione in grado di modificarsi. Fino ad ora i modelli di auto-organizzazione degli esseri viventi proposti sono funzioni, soluzioni di sistemi differenziali o funzioni di transizione di automi. Queste funzioni sono fisse e questi modelli non sono quindi in grado di modificare la propria organizzazione. D'altra parte, l'informatica propone molti modelli che hanno le proprietà dei sistemi adattativi degli esseri viventi, ma tutti questi modelli dipendono dal confronto tra un obiettivo e i risultati e le ingegnose scelte di parametri da parte dei programmatori, mentre non ci sono intenzioni del programmatore né scelta nei sistemi viventi.mSpMSp


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.