Riportare * perché * una query non è riuscita in Prolog in modo sistematico

Aug 24 2020

Sto cercando un approccio, un modello o una funzionalità incorporata in Prolog che posso utilizzare per restituire il motivo per cui un set di predicati non è riuscito, almeno per quanto riguarda i predicati nel database. Sto cercando di essere in grado di dire più di "Questo è falso" quando un utente pone una query in un sistema.

Ad esempio, diciamo che ho due predicati. blue/1è vero se qualcosa è blu, ed dog/1è vero se qualcosa è un cane:

blue(X) :- ...
dog(X) :- ...

Se pongo la seguente domanda a Prolog ed fooè un cane, ma non blu, Prolog normalmente restituisce semplicemente "false":

? blue(foo), dog(foo)
false.

Quello che voglio è scoprire perché la congiunzione dei predicati non era vera, anche se si tratta di una chiamata fuori banda come:

? getReasonForFailure(X)
X = not(blue(foo))

Sto bene se i predicati devono essere scritti in un certo modo, cerco solo gli approcci utilizzati dalle persone.

Il modo in cui l'ho fatto fino ad oggi, con un certo successo, è stato scrivere i predicati in modo stilizzato e utilizzare alcuni predicati di supporto per scoprire il motivo dopo il fatto. Per esempio:

blue(X) :-
    recordFailureReason(not(blue(X))),
    isBlue(X).

E poi implementare recordFailureReason / 1 in modo tale che ricordi sempre il "motivo" che si è verificato più in profondità nello stack. Se una query fallisce, qualunque errore si sia verificato più in profondità viene registrato come il motivo "migliore" del fallimento. Questa euristica funziona sorprendentemente bene per molti casi, ma richiede un'attenta costruzione dei predicati per funzionare bene.

Qualche idea? Sono disposto a guardare fuori da Prolog se ci sono sistemi di logica dei predicati progettati per questo tipo di analisi.

Risposte

1 DavidTonhofer Aug 24 2020 at 17:56

Alcuni pensieri:

Perché il programma logico ha fallito: La risposta al " perché" è ovviamente "perché non esiste un'assegnazione di variabili che soddisfi i vincoli dati dal programma Prolog" .

Questo evidentemente è piuttosto inutile, ma è proprio il caso del "cane blu": non esistono cose del genere (almeno nel problema che modellate).

Infatti l'unica risposta accettabile al problema del cane blu si ottiene quando il sistema entra in modalità completa di dimostrazione dei teoremi e fornisce:

blue(X) <=> ~dog(X)

o forse solo

dog(X) => ~blue(X)

o forse solo

blue(X) => ~dog(X)

a seconda delle ipotesi. "Non ci sono prove di cani blu". Il che è vero, poiché è ciò che afferma il programma. Quindi un "perché" in questa domanda è una richiesta di riscrivere il programma ...

Potrebbe non esserci una buona risposta: "Perché non esiste x tale che x² <0" è mal posto e potrebbe avere come risposta "solo perché" o "perché ti stai limitando ai reali" o "perché quello 0 in l'equazione è semplicemente sbagliata " ... quindi dipende molto.

Per rendere un " perché " più utile, dovrai qualificare questo "perché" in qualche modo. che può essere fatto strutturando il programma ed estendendo la query in modo che le informazioni aggiuntive raccolte durante la costruzione dell'albero di prova stiano ribollendo, ma dovrai decidere in anticipo quali informazioni sono:

query(Sought, [Info1, Info2, Info3])

E questa query avrà sempre successo (poiché query/2"successo" non significa più "successo nella ricerca di una soluzione al problema modellato" ma "successo nel completare il calcolo"),

Variabile Soughtsarà la risposta reificata della query reale che si desidera risposto, vale a dire uno degli atomi trueo false(e magari unknownse avete avuto abbastanza con logica a due valori) e Info1, Info2, Info3sarà in ulteriori dettagli per aiutarvi a rispondere a una perché qualcosa qualcosa nel caso in cui Soughtè false.

Si noti che la maggior parte delle volte, il desiderio di chiedere "perché" è dovuto alla confusione tra i due distinti fallimenti: "fallimento nel trovare una soluzione al problema modellato" e "fallimento nel finire il calcolo". Ad esempio, vuoi applicare maplist/3a due elenchi e aspettarti che funzioni, ma erroneamente i due elenchi sono di lunghezza diversa: otterrai false- ma sarà un falsecalcolo from (in questo caso, a causa di un bug), non un falsefrom modellazione. Essere pesante assertion/1può aiutare qui, ma questo è brutto a modo suo.

In effetti, confrontalo con linguaggi imperativi o funzionali senza parti di programmazione logica: in caso di fallimento (forse un'eccezione?), Quale sarebbe un "perché" corrispondente? Non è chiaro.

Addendum

Questa è una grande domanda, ma più ci rifletto, più penso che si possa rispondere solo in un modo specifico per l'attività: devi strutturare il tuo programma logico in modo che sia whyutilizzabile e devi decidere che tipo di informazioni whydovrebbero effettivamente ritorno. E sarà qualcosa compito specifico: qualcosa su informazioni mancanti "se solo questo o quello fosse vero" indicazioni, dove "questo o quello" sono scelti da un insieme di predicati dedica. Questo è ovviamente previsto, poiché non esiste un modo generale per fare in modo che programmi imperativi o funzionali spieghino i loro risultati (o la loro mancanza).

Ho cercato un po 'di documenti su questo (inclusi IEEE Xplore e ACM Library) e ho appena trovato:

  • Ragionare sulle spiegazioni per le risposte negative alle query in DL-Lite che in realtà è per la logica di descrizione e utilizza il ragionamento abduttivo .
  • WhyNot: debug di query non riuscite in grandi basi di conoscenza che trattano uno strumento per Cyc .
  • Ho anche esaminato a caso la documentazione per Flora-2 ma in pratica sembra che dicano "usa il debugger". Ma il debug è solo il debug, non la spiegazione.

Dev'esserci di più.

1 false Aug 25 2020 at 03:14

Finché rimani all'interno del sottoinsieme monotono puro di Prolog, puoi considerare le generalizzazioni come spiegazioni. Per fare il tuo esempio, le seguenti generalizzazioni potrebbero essere pensabili a seconda della tua precisa definizione di blue/1e dog/1.

? - blue (foo), * dog (foo) .
   falso.

In questa generalizzazione, l'intero obiettivo è dog(foo)stato rimosso. Il prefisso *è in realtà un predicato definito come :- op(950, fy, *). *(_).Informale, sopra può essere letto come: Non solo questa query fallisce, ma anche questa query generalizzata fallisce. Non c'è affatto il foo blu (a condizione che non ce ne sia nessuno). Ma forse c'è un foo blu, ma nessun cane blu a tutti ...

? - blu (_X / * foo * /), cane (_X / * foo * /).
   falso.

Ora abbiamo generalizzato il programma sostituendolo foocon la nuova variabile _X. In questo modo viene mantenuta la condivisione tra i due obiettivi.

Ci sono più generalizzazioni di questo tipo possibili come l'introduzione dif/2.

Questa tecnica può essere applicata sia manualmente che automaticamente. Per di più, c'è una raccolta di sessioni di esempio . Vedi anche Sviluppo di programmi dichiarativi in ​​Prolog con GUPU