Intelligenza Artificiale - Appunti
Transcript
Intelligenza Artificiale - Appunti
Intelligenza artificiale∗ Appunti NON UFFICIALI del corso del prof. Francesco Amigoni† Politecnico di Milano Marcello Pogliani marcello.pogliani - at - gmail - dot - com A.A. 2012 – 2013 ∗ Questo documento raccoglie gli appunti da me raccolti durante le lezioni del corso. Non sono stati rivisti attentamente: possono contenere errori oppure imprecisioni dovute sia alla fretta con la quale sono stati redatti che alla mia scarsa conoscenza della materia - del resto, sono appunti scritti durante lo studio. Per quanto mi riguarda, è possibile distribuire, modificare, etc, etc questo documento, purché venga citata la fonte. Spero di non aver violato il copyright di nessuno :) Nota: Alcuni esempi e figure citati durante le lezioni e qui riportati sono tratti da [3]. In particolare, alcune figure sono tratte dai file resi disponibili su [2]. † Il programma completo del corso è disponibile sul sito ufficiale [1] Indice 1 Introduzione 1.1 Concetti fondamentali . . . . 1.2 Agenti intelligenti . . . . . . . 1.2.1 Tipologie di ambiente 1.2.2 Tipologie di agente . . 1.2.3 Rappresentazione dello . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 6 7 7 8 2 Risoluzione dei problemi tramite ricerca 2.1 Formulazione del problema . . . . . . 2.2 Ricerca delle soluzioni di un problema 2.3 Strategie di ricerca . . . . . . . . . . . 2.3.1 Ricerca non informata . . . . . 2.3.2 Strategie di ricerca informate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 11 12 12 14 3 Ricerca con avversari 3.1 Giochi come un problema di ricerca . . 3.2 Algoritmo Minimax . . . . . . . . . . . 3.2.1 Potatura alfa-beta . . . . . . . 3.3 Giochi con casualità: expectiminimax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 18 19 21 4 Problemi di soddisfacimento di vincoli 4.1 CSP come problemi di ricerca . . . 4.2 Euristiche . . . . . . . . . . . . . . 4.2.1 Scelta della variabile . . . . 4.2.2 Scelta del valore . . . . . . 4.3 Propagazione dei vincoli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 25 25 26 27 27 5 Agenti basati sulla conoscenza 5.1 Procedure di inferenza per la logica proposizionale . 5.1.1 Concatenazione in avanti (FC) . . . . . . . . 5.1.2 Concatenazione all’indietro (BC) . . . . . . . 5.1.3 Risoluzione (R) . . . . . . . . . . . . . . . . . 5.2 Procedure di inferenza per la logica del primo ordine 5.2.1 Concetti preliminari . . . . . . . . . . . . . . 5.2.2 Concatenazione in avanti e all’indietro . . . . 5.2.3 Risoluzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 32 32 33 33 35 35 36 37 6 Pianificazione 6.1 STRIPS . . . . . . . . . . . 6.2 Pianificazione in avanti . . . 6.3 Pianificazione all’indietro . 6.4 Pianificatori nello spazio dei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 41 43 43 44 . . . . . . . . . . . . . . . . stato . . . . . . . . . . . . piani . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Introduzione 1.1 Concetti fondamentali Il termine “Intelligenza Artificiale” è stato coniato nel 1956 da John McCarthy, all’interno di una proposta per organizzare un gruppo di lavoro al Dartmouth College1 . All’idea di intelligenza artificiale si è comunque arrivati da tante direzioni diverse, che prendono in considerazione punti di vista differenti gli uni dagli altri, e che hanno portato a una ricchezza di background su cui è nata la disciplina. I principali approcci alla definizione di intelligenza artificiale sono: Agire umanamente Questo è l’approccio del test di Turing (1950, Computer Machinery and Intelligence): la domanda “possono le macchine pensare?” è troppo difficile, perché implica una definizione del termine ‘pensare’. Questo approccio cerca di dare come risposta una definizione operativa sotto forma dell’imitation game: ci sono tre stanze, A, B e una stanza centrale, separate le une dalle altre e collegate da telescriventi, con cui gli occupanti di una stanza possono comunicare con quelli delle altre. Nella stanza A viene messo un uomo, nella stanza B una donna, nella stanza centrale un interrogante (di qualsiasi sesso). Lo scopo dell’uomo è di farsi riconoscere come donna, lo scopo della donna è di farsi riconoscere come donna, lo scopo dell’interrogante è quello di riconoscere chi è l’uomo e chi è la donna. In seguito, al posto dell’uomo nella stanza A viene messa una macchina, e si ripete lo stesso gioco. Secondo questo approccio, se in questa seconda configurazione la macchina riesce a ingannare l’interrogante con la stessa frequenza con cui l’interrogante era stato ingannato dall’uomo, allora possiamo concludere che la macchina è intelligente, ossia che la macchina pensa. Astraendo molto, il test di Turing valuta se una macchina si comporta come un essere umano, ossia se agisce umanamente. Pensare umanamente Un secondo approccio è quello di cercare non una macchina che agisce come un essere umano, ma una macchina che pensa come un essere umano. Dal punto di vista ingegneristico, il problema di questo approccio è che, allo stato attuale, non è disponibile un buon modello del funzionamento della mente di un essere umano. Dal punto di vista scientifico, una strada di questo tipo ha dato vita a una serie di branche importanti della scienza, quali le scienze cognitive, che cercano di descrivere con una serie di modelli computazionali il comportamento umano. Pensare razionalmente Un terzo approccio è astrarre dal pensiero dell’essere umano, e cercare di costruire macchine che pensano razionalmente, dove la razionalità è una descrizione idealizzata del pensiero dell’essere umano, di come l’essere umano dovrebbe pensare: ad esempio, non tutti i comportamenti dell’essere umano sono facilmente modellabili, ma il pensiero deduttivo lo è. Agire razionalmente Nell’approccio del pensare razionalmente, in qualche modo, viene fornita alla macchina una specifica del metodo di ragionamento, ossia dell’algoritmo che deve seguire (ad es. il modus ponens). Invece, nell’approccio dell’agire razionalmente si guarda solo all’output: vengono considerate intelligenti quelle macchine che fanno qualcosa (indipendentemente dalla strada che seguono), il cui output è corretto 1 J. McCarthy, M. L. Minsky, N. Rochester, C.E. Shannon, A Proposal for the Dartmouth Summer Research Project on Artificial Intelligence, 31 agosto 1955 6 INTRODUZIONE azione a Agente Ambiente p percezione Figura 1.1: L’agente e l’ambiente ed è razionale, dove per razionale si intende fare la cosa giusta con le informazioni a disposizione. Nel seguito faremo riferimento a quest’ultima definizione: macchine “intelligenti” sono quelle capaci di agire razionalmente secondo qualche definizione di razionalità. Il corso è centrato attorno alla visione di una entità (agente razionale). Un agente è una macchina che interagisce con un ambiente: è capace di percepire parte dello stato dell’ambiente con i sensori e di intervenire sull’ambiente tramite i suoi attuatori. In questo corso, cercheremo di realizzare un agente che sia intelligente, ossia che reagisca razionalmente nel proprio ambiente. Discipline che hanno forti relazioni con l’IA sono la filosofia (le macchine possono pensare? cosa vuol dire il pensiero nelle macchine? ...), oltre alla matematica (logica matematica), psicologia, economia, linguistica (comprensione del linguaggio naturale, ...), scienza del controllo, informatica. 1.2 Agenti intelligenti In figura 1.1 è rappresentato il ciclo base di un sistema intelligente. In maniera astratta, le decisioni dell’agente fanno riferimento a una funzione agente f : P∗ → A che ha come dominio l’insieme di tutte le possibili sequenze di percezioni P ∗ , dove P è l’insieme di tutte le possibili percezioni dell’agente (che sono in numero finito per qualsiasi agente artificiale che si considera), e ha come codominio l’insieme A di tutte le possibili azioni. La funzione agente è una specifica di altissimo livello del funzionamento dell’agente. In questo approccio, il tempo viene considerato come discreto. L’implementazione, in un programma software, della funzione agente, è detta programma agente. Esempio 1.1 (Il mondo dell’aspirapolvere): Siano A e B due stanze in cui si può muovere un agente (aspirapolvere), e sia presente nell’ambiente dello sporco. L’agente è dotato di due sensori, uno che A B indica in che stanza si trova l’agente stesso, e l’altro che indica se la stanza è pulita oppure sporca. Le azioni che può effettuare sono: muoversi a destra (r), muoversi a sinistra (l), aspirare (s), non fare niente (n). Questo può essere formalizzato come A = {l, r, s, n} P = {[A, pulita], [A, sporca], [B, pulita], [B, sporca]} Una possibile specifica tabellare della funzione agente è la seguente: AGENTI INTELLIGENTI P∗ [A, pulita] [A, sporca] [B, pulita] [B, sporca] [A, pulita][A, pulita] [A, pulita][A, sporca] ... 7 A r s l s r s ... Gli agenti a cui verrà fatto riferimento devono essere razionali. Una possibile definizione di razionalità che può essere applicata in questo contesto è: Definizione (Razionalità). Per ogni possibile sequenza di percezioni, un agente è detto razionale se sceglie l’azione che massimizza il valore atteso della misura di performance date le percezioni fino a quel momento e la conoscenza dell’agente La misura di prestazioni è definita dal progettista e rappresenta lo scopo dell’agente. In generale, questa misura va sempre definita sugli stati del mondo, e non sulle azioni dell’agente. Secondo la definizione appena data, dunque, essere razionali non vuol dire né essere onniscenti (“data la conoscenza dell’agente”) né avere conoscenza del futuro (l’obiettivo è massimizzare il valore atteso della misura di prestazioni). Per costruire la funzione agente è necessario conoscere, oltre all’insieme delle percezioni P (i sensori), e l’insieme delle azioni A (gli attuatori), la misura di performance da adottare e il tipo di ambiente in cui l’agente si trova ad operare. In alcune applicazioni, l’imsieme delle percezioni e delle azioni sono dati (ad esempio nel caso di un agente giocatore di scacchi), mentre altre volte sono da progettare (ad esempio nel caso di un robot utilizzato per l’esplorazione di Marte). 1.2.1 Tipologie di ambiente Un ambiente può essere classificato secondo vari aspetti: • completamente osservabile (i sensori dell’agente hanno accesso allo stato completo dell’ambiente) oppure parzialmente osservabile • a singolo agente oppure multi-agente • deterministico (lo stato prossimo dell’ambiente è completamente determinato dallo stato corrente e dall’azione che l’agente esegue nell’ambiente) oppure stocastico (è definita una distribuzione di probabilità sullo stato prossimo) • episodico (nell’ambiente si susseguono episodi indipendenti gli uni dagli altri, ad esempio nel caso di un controllo qualità di pezzi meccanici che si susseguono su un nastro) oppure sequenziale • statico (l’ambiente non cambia mentre l’agente pensa) oppure dinamico • discreto oppure continuo • noto (l’agente conosce le leggi che governano il funzionamento dell’ambiente) oppure ignoto 1.2.2 Tipologie di agente Per implementare un programma agente è possibile utilizzare una delle seguenti architetture di base. schematizzate in figura 1.2: • agenti reattivi semplici, che sono utilizzabili, ad esempio, in ambienti completamente osservabili 8 INTRODUZIONE • agenti reattivi basati su modello, utilizzabili anche in ambienti parzialmente osservabili • agenti basati su obiettivi, detti anche agenti risolutori di problemi • agenti basati sull’utilità. A differenza degli agenti basati su obiettivi, che sono binari (uno stato del mondo è un obiettivo oppure non lo è), questo tipo di agente attribuisce ad ogni stato del mondo un valore di utilità Tutte queste tipologie di agente possono essere estese, aggiungendo un processo di apprendimento. p(t) Agente stato aspetto corrente del mondo azione da eseguire come evolve il mondo aspetto corrente del mondo Ambiente Ambiente regole condizione/azione p(t) Agente cosa fanno le azioni regole condizione/azione azione da eseguire s(t) s(t) (a) agente reattivo semplice (b) agente reattivo basato su modello p(t) Agente stato obiettivi come cambia il mondo se eseguo a azione da eseguire come evolve il mondo aspetto corrente del mondo cosa fanno le azioni come cambia il mondo se eseguo a utilità soddisfazione di essere in s Ambiente cosa fanno le azioni stato aspetto corrente del mondo Ambiente come evolve il mondo p(t) Agente azione s(t) (c) agente basato su obiettivi s(t) (d) agente basato sull’utilità Figura 1.2: Architetture per l’implementazione di programmi agente 1.2.3 Rappresentazione dello stato La rappresentazione dello stato del mondo all’interno di un agente può avere varie caratteristiche, e in particolare può essere classificata come una • rappresentazione atomica: la sola cosa che si può dire riguardo a due stati è stabilire se sono uguali oppure diversi • rappresentazione fattorizzata: lo stato è caratterizzato da un insieme di coppie hvariabile, valorei • rappresentazione strutturata: vengono considerati esplicitamente gli oggetti che rappresentano lo stato e anche le loro relazioni 2 Risoluzione dei problemi tramite ricerca In questo capitolo si considereranno gli agenti risolutori di problemi, un caso particolare degli agenti basati su obiettivo. La risoluzione di un problema si articola in tre passi: 1. Formulazione del problema 2. Ricerca di una soluzione 3. Esecuzione della soluzione Dato un problema formulato opportunamente, un agente risolutore di problemi è in grado di trovarne autonomamente la soluzione, e quindi di eseguirla. Esempio 2.1 (Un agente che gioca a scacchi (o a dama)): La formulazione del problema è una specifica all’agente delle regole del gioco. Il secondo e il terzo passo sono eseguiti dalla macchina stessa: la macchina, autonomamente, cerca una soluzione del problema formulato (ad es. una sequenza di mosse che porta allo scacco matto) ed esegue la soluzione (nel caso della dama e degli scacchi questi passi sono poi reiterati a causa delle mosse dell’avversario). Secondo questo schema, l’algoritmo che viene utilizzato per risolvere il problema non è scritto dal progettista, ma viene trovato automaticamente dall’agente. In questo senso, si può affermare che l’intelligenza artificiale si occupa di meta-algoritmi (algoritmi che generano altri algoritmi). Ha senso applicare questo approccio quando l’algoritmo per risolvere il problema non è ovvio. Se non diversamente specificato, si considereranno ambienti statici, discreti, deterministici, completamente osservabili e noti. 2.1 Formulazione del problema Il primo passo per la risoluzione di un problema consiste nella formulazione, da parte del progettista, delle indicazioni necessarie alla macchina per trovare una soluzione al problema in modo autonomo. Per illustrare i diversi elementi della formulazione del problema, si considererà l’esempio del gioco del 15, nella sua versione semplificata detta gioco dell’otto. Si tratta di un rompicapo costituito da una griglia 4 × 4 con 15 tesserine numerate che si possono muovere. Lo scopo del gioco è metterle in fila, lasciando la casella bianca in un estremo della griglia. La formulazione di un problema si compone di cinque elementi: lo stato iniziale È la configurazione iniziale del problema. Ad esempio, nel gioco dell’otto, è la configurazione iniziale delle tessere nella griglia, come la seguente: 1 8 3 5 2 7 4 6 la funzione azioni È una funzione che prende come argomento uno stato e restituisce un insieme di azioni possibili applicabili a quello stato: Azioni(s) = { a1 , a2 , . . . } 10 RISOLUZIONE DEI PROBLEMI TRAMITE RICERCA Nell’esempio, le azioni possibili a partire dallo stato iniziale s0 descritto al punto precedente sono (6, ↓) e (7, →) che corrispondono rispettivamente a muovere la casella vuota in alto e a sinistra. In termini della funzione azioni, Azioni(s0 ) = { ↑, ← }. il modello di transizioni Prende la forma della funzione Risultato(s, a) = s0 che, preso in ingresso uno stato s e un’azione a applicabile a s, restituisce un nuovo stato s0 (detto successore di s) che risulta dall’avere applicato l’azione a allo stato s. Nell’esempio, Risultato(s0 , ↑) = s1 dove lo stato s1 è 1 8 3 5 2 7 4 6 Si può osservare che, in questa definizione della funzione risultato, si è considerato l’ambiente come deterministico. Spazio degli stati L’insieme di stato iniziale, funzione azioni e funzione risultato definisce lo spazio degli stati, ossia un grafo i cui nodi corrispondono agli stati e i cui archi corrispondono alle azioni. É importante osservare che alla macchina non viene fornita una rappresentazione esplicita dell’intero grafo, ma soltanto le regole per costruirlo, ossia lo spazio degli stati viene fornito implicitamente. La macchina opererà esplorando il grafo: a un certo punto, verrà generato uno stato che contiene la soluzione cercata. La soluzione al problema è il cammino che nello spazio degli stati porta dallo stato iniziale a questo stato, ovvero le mosse che devono essere compiute per raggiungere l’obiettivo a partire dalla configurazione iniziale. Il problema sta nel riuscire a trovare questo cammino senza espandere completamente lo spazio degli stati, che può essere molto ampio1 : è quindi importante avere una rappresentazione implicita dello spazio degli stati. il test obiettivo Distingue gli stati terminali dagli stati non terminali. Questo test può essere implementato elencando esplicitamente gli stati finali (questo tipo di implementazione risulta semplice, ad esempio, nel gioco dell’otto in cui vi è un solo stato finale), oppure specificando una proprietà che gli stati finali devono soddisfare. il costo di cammino Si tratta del costo per ogni cammino presente nello spazio degli stati. In pratica, viene definito come la somma del costo di ogni passo che lo compone. Definizione (Soluzione). Una soluzione di un problema è un cammino nello spazio degli stati che porta dallo stato iniziale a uno degli stati che soddisfano il test obiettivo. Una soluzione ottima è una soluzione a costo di cammino minimo. Esempio 2.2 (Problema delle otto regine): • stato iniziale: scacchiera vuota • azioni possibili: mettere una regina in una casella vuota2 • funzione risultato: lo stato di prima più la regina nella casella in cui è stata inserita • test obiettivo: ci devono essere otto regine e non ce ne devono essere due sulla stessa riga, due sulla stessa colonna, o due sulla stessa diagonale 1 nel problema del gioco degli scacchi è circa 10120 , pari al numero di atomi stimato presente nell’universo visibile 2 non ci sono azioni applicabili se ci sono già 8 regine RICERCA DELLE SOLUZIONI DI UN PROBLEMA 11 • costo di passo: unitario Lo spazio degli stati è composto da 1 + 64 × 63 × 62 × · · · × 57 = 1.8 · 1014 stati. Il problema di questa formulazione è che tutta la conoscenza è descritta dal test obiettivo. Una formulazione alternativa più furba è la seguente • stato iniziale: scacchiera vuota • funzione azioni: dato uno stato, metti una regina nella colonna più a sinistra libera in modo tale che non sia attaccata da alcuna altra regina presente sulla scacchiera • ... Formulando il problema in questo modo, si è passati a soli 2057 stati. In generale, per ridurre lo spazio degli stati, la conoscenza del dominio va messa il più possibile nella funzione azione. In questo problema, per entrambe le formulazioni, la lunghezza della soluzione ottima è 8. 2.2 Ricerca delle soluzioni di un problema Nel modello presentato in questo capitolo, la rappresentazione dello stato è atomica (è noto solo che uno stato è diverso da un altro, ma non sono note altre informazioni sugli stati). Per esplorare una porzione dello spazio degli stati si fa uso di un albero di ricerca, in cui vi è un unico cammino che porta da un nodo alla radice. Per costruire l’albero di ricerca, si procede secondo questo algoritmo: 1. Viene generata la radice dell’albero (ossia un nodo che corrisponde allo stato iniziale) 2. Finché l’algoritmo non termina, si ripetono i passi seguenti: • Si seleziona il prossimo nodo da considerare (secondo qualche strategia) • Si applica il test obiettivo al nodo considerato. L’algoritmo termina se il test viene soddisfatto, altrimenti prosegue • Se il test obiettivo è fallito, si espande il nodo considerato: si costruiscono tutti i suoi successori, applicando dapprima la funzione Azioni al nodo selezionato per trovare gli archi, e poi la funzione Risultato per trovare i nodi successivi. Si dice frontiera, o lista aperta, l’insieme dei nodi che sono nell’albero di ricerca ma devono essere ancora espansi. Per selezionare un nodo da espandere dalla frontiera, si può ipotizzare di ordinare secondo qualche criterio i nodi e poi scegliere il primo della lista. Nei paragrafi successivi verranno descritte le diverse strategie per selezionare il nodo da espandere. Un nodo nell’albero di ricerca corrisponde a un cammino nello spazio degli stati: infatti, nell’albero di ricerca è possibile avere più nodi relativi allo stesso stato, che corrispondono a diversi cammini che portano dallo stato iniziale allo stesso stato. Ogni nodo dell’albero viene rappresentato da una struttura dati che contiene: • lo stato a cui il nodo si riferisce • il nodo padre (serve per risalire l’albero nel caso venga trovata una soluzione) • l’azione che ha portato al nodo (serve per dare in output la serie di azioni che ha portato alla soluzione) • il costo di cammino g(n), somma dei costi delle azioni che portano a nodo n. Se il costo di ogni passo è unitario, questo corrisponde alla profondità. Una variante all’algoritmo presentato in questo paragrafo (Ricerca-Albero) prevede di mantenere, oltre alla frontiera (lista aperta), la cosiddetta lista chiusa, che contiene gli stati per cui si è già espanso un nodo. Questa struttura consente di evitare di espandere nuovamente uno stato già considerato (eliminare gli stati ripetuti): se viene selezionato un nodo già presente nella lista chiusa, questo non viene espanso. L’algoritmo che prevede l’utilizzo della lista chiusa è detto Ricerca-Grafo. 12 RISOLUZIONE DEI PROBLEMI TRAMITE RICERCA 2.3 Strategie di ricerca Lo strumento che viene utilizzato per esplorare una parte dei possibili cammini dello spazio degli stati è l’albero di ricerca. Una strategia di ricerca è un criterio per scegliere i nodi dalla frontiera ad ogni iterazione. Esistono diverse strategie di ricerca, che si differenziano per completezza (se esiste una soluzione, la strategia la trova), ottimalità (la strategia trova sempre la soluzione ottima, se esiste) e complessità, che può essere temporale (calcolata in genere come il numero di nodi generati nell’albero di ricerca) oppure spaziale (misurata in base al numero di nodi che devono essere mantenuti in memoria). Nel seguito, verranno utilizzati alcuni parametri per descrivere le diverse strategie di ricerca: • b (fattore di ramificazione oppure branching factor): il massimo numero dei successori di un nodo • d: profondità della soluzione più vicina alla radice • m: massima lunghezza di un cammino nello spazio degli stati • c∗ : costo della soluzione ottima. Se tutti i passi hanno costo unitario, d = c∗ . 2.3.1 Ricerca non informata Le strategie di ricerca non informate non fanno uso di alcuna altra informazione oltre a quelle contenute nella definizione del problema. In tutti gli esempi che seguono verrà utilizzato l’algoritmo di ricerca Ricerca-Albero (senza eliminazione degli stati ripetuti), tranne quando diversamente indicato. Esempio 2.3: La figura 2.1 presenta un esempio di spazio degli stati con cinque stati (A, B, C, D, E), avente A come stato iniziale ed E come unico stato che soddisfa il test obiettivo. B 5 5 A E 1 1 C 1 stato iniziale: A stato finale: E D Figura 2.1: Esempio di spazio degli stati Ricerca in ampiezza La strategia di ricerca in ampiezza estrae sempre, tra tutti i nodi presenti nella frontiera, quello meno profondo. Per la regola con cui sono scelti i nodi, prima di espandere i nodi di livello n + 1 vengono espansi tutti i nodi di livello n, utilizzando una coda FIFO per la frontiera. La strategia di ricerca in ampiezza è completa (se il numero di successori di un nodo è finito, ossia se il branching factor b è un numero finito) e non è ottima3 . La complessità temporale, calcolata come numero di nodi generati nel caso di Ricerca-albero è data da 1 + b + b2 + ... + bd + b(bd − 1) = O(bd+1 ) 3 La ricerca in ampiezza trova la soluzione più vicina alla radice, fornisce la soluzione con il minore numero di passi. Trova la soluzione ottima quando il costo di cammino è una funzione monotona non decrescente della profondità del nodo STRATEGIE DI RICERCA 13 mentre la complessità spaziale (nodi da tenere in memoria nel caso pessimo) è pari alla complessità temporale (finché non si trova la soluzione, vanno tenuti in memoria tutti i nodi). Se al posto dell’algoritmo generale si applica il test obiettivo appena viene generato un nodo (prima di inserirlo nella frontiera), la complessità si riduce a O(bd ) Esempio 2.4: Per lo spazio di stati della figura 2.1, la strategia di ricerca in ampiezza produce il seguente albero: Andamento della frontiera (nodo, profondità): A • (A, 0) • (B, 1) (C, 1) C B • (C, 1) (A, 2) (E, 2) A E • (A, 2) (E, 2) (D, 2) (A, 2) D A • (E, 2) (D, 2) (A, 2) (B, 3) (C, 3) B C • (E, 2) (D, 2) (B, 3) (C, 3) (B, 3) (C, 3) B C C E • (E, 2) (B, 3) (C, 3) (B, 3) (C, 3) (C, 3) (E, 3) All’ultima iterazione viene considerato il nodo E di profondità 2, il test obiettivo ha successo e l’algoritmo termina. Nell’esempio, per risolvere l’ambiguità che si presenta nel caso più nodi abbiano la stessa profondità, i nodi sono stati estratti in ordine lessicografico. Utilizzando l’algoritmo Ricerca-Grafo invece di Ricerca-Albero, il nodo A non si sarebbe espanso due volte oltre alla radice. Costo uniforme La strategia a costo uniforme sceglie dalla frontiera il nodo che ha costo di cammino più basso. Questa strategia è completa solo se tutti i costi sono strettamente ∗ positivi e ottima. Sia la complessità temporale che quella spaziale è data da O(bdc /εe ), dove ε è un lower bound positivo sul costo di passo4 . Ricerca in profondità La ricerca in profondità sceglie dalla frontiera il nodo più lontano dalla radice (a profondità maggiore). Per la frontiera, questa strategia utilizza una coda LIFO. A C B E A E A B (continua all’infinito) C ... Figura 2.2: Albero prodotto dalla ricerca in profondità per lo spazio degli stati di figura 2.1 La ricerca in profondità non è completa (diventa completa se i nodi non sono infiniti utilizzando l’eliminazione degli stati ripetuti, quindi Ricerca-grafo invece che Ricercaalbero) e non è ottima. In generale, la complessità temporale è O(bm ) (nell’esempio, m = ∞) e la complessità spaziale è O(b · m) (in generale è necessario mantenere attivo soltanto il nodo corrente dell’albero, quindi m livelli con b nodi per livello nel caso peggiore). 4 c∗ /ε è il costo della soluzione nel caso peggiore dunque rappresenta la profondità della soluzione 14 RISOLUZIONE DEI PROBLEMI TRAMITE RICERCA Questa strategia ammette una variante, denominata backtracking, in cui invece di generare tutti i b successori di un nodo ne viene generato uno alla volta. Il vantaggio è che la sua complessità spaziale è O(m), non considerando la struttura dati supplementare per tener traccia del prossimo successore da generare per ogni nodo. Profondità limitata Questa strategia di ricerca è uguale alla ricerca in profondità, con la differenza che è definito un limite l per cui, prima di espandere un nodo dopo avere applicato il test obiettivo, se questo nodo si trova a profondità l non viene espanso. Per applicare questa strategia è necessario modificare la struttura dati del nodo aggiungendo l’informazione sulla profondità. La ricerca a profondità limitata è completa se l ≥ d, non è ottima, ha complessità temporale di O(bl ) e complessità spaziale di O(b · l). Ricerca ad approfondimento iterativo Il maggiore problema della ricerca a profondità limitata è che non è noto il minimo valore di l necessario a trovare una soluzione se non si conosce il valore di d. Questa strategia ripete iterativamente una ricerca a profondità limitata aumentando di 1 ogni volta il valore di l. Questo permette, rispetto alla ricerca in ampiezza, di ridurre notevolmente la complessità spaziale: è completa (nel caso in cui b non sia ∞), non è ottima5 , ha complessità temporale pari a 1(d + 1) + bd + b2 (d − 1) + · · · + bd · 1 = O(bd ) che è la stessa della ricerca in ampiezza quando viene applicato il test obiettivo al momento della generazione del nodo, ma ha complessità spaziale di O(b · d). 2.3.2 Strategie di ricerca informate Le strategie di ricerca informate fanno uso di informazione supplementare rispetto a quella contenuta nella definizione del problema, per scegliere il prossimo nodo da espandere della frontiera. L’idea di base è quella di definire una funzione f (n), detta funzione di valutazione, che valuta in qualche modo i nodi che sono presenti nella frontiera. Tutte le volte che un algoritmo di ricerca (grafo o albero) deve scegliere un nodo dalla frontiera, sceglie quello con valore di f (n) più alto. Ricerca greedy Un primo modo per definire la funzione di valutazione è definirla come la funzione euristica f (n) = h(n), h(n) ≥ 0, che rappresenta una stima del costo per andare dal nodo n al nodo di goal più vicino. In questo caso la strategia di ricerca è detta greedy best first. Si noti che, formalmente, la funzione euristica è definita sui nodi. Tuttavia, nei problemi pratici è più semplice definire la funzione euristica sugli stati: questo significa che tutti i nodi che corrispondono allo stesso stato hanno lo stesso valore della funzione euristica. Viene costruito come al solito l’albero di ricerca. Nella frontiera si utilizza un ordinamento basato sulla funzione f . Esempio 2.5: Albero generato dallo spazio degli stati della figura 2.3 utilizzando la ricerca greedy best first. Andamento della frontiera (nodo, valore di f (n)): A • (A, 3) B C • (B, 2) (C, 3) • (C, 3) (A, 3) (E, 0) A E L’ultimo nodo ad essere espanso è E in quanto ha il valore minore della funzione di valutazione (0). Il test obiettivo fornisce esito positivo, e l’algoritmo termina restituendo la soluzione A − B − E. 5 come nel caso della ricerca in ampiezza, trova sempre la soluzione più vicina alla radice STRATEGIE DI RICERCA 15 h(B) = 2 B 2 h(A) = 3 6 A E h(E) = 0 stato iniziale: A stato finale: E 2 2 C h(C) = 3 D 2 h(D) = 2 Figura 2.3: Uno spazio degli stati in cui sono indicati i valori della funzione euristica. Questa strategia non è completa (si può ciclare all’infinito tra gli stessi stati se le euristiche sono definite in modo opportuno) e quindi non è nemmeno ottima. È invece completa ma non ottima utilizzando Ricerca-grafo. La sua complessità è O(bn ) (sia temporale che spaziale). Ricerca A* Si può definire un’altra funzione di valutazione come f (n) = g(n) + h(n), somma del costo del cammino per arrivare al nodo n e della stima del costo per arrivare da n al nodo di goal (funzione euristica). Questa funzione rappresenta la stima del costo di una soluzione che passa per n. La strategia di ricerca che utilizza questa funzione è detta A∗ . Esempio 2.6: Albero generato dallo spazio degli stati della figura 2.3 utilizzando la ricerca A*. Andamento della frontiera (nodo, valore di f (n)): A • (A, 3) B • (B, 4) (C, 5) C • (C, 5) (A, 7) (E, 8) A E A D • (A, 7) (E, 8) (A, 7) (D, 6) • (A, 7) (E, 8) (A, 7) (C, 9) (E, 6) C E L’ultimo nodo ad essere espanso è E in quanto ha il valore minore della funzione di valutazione (6). Il test obiettivo fornisce esito positivo, e l’algoritmo termina restituendo la soluzione A − C − D − E di costo pari a 6. Con la ricerca A∗ viene scelto quello che globalmente è il nodo più promettente, secondo la combinazione del costo sostenuto per arrivare a quel nodo più il costo stimato per arrivare al goal. Definizione (Euristica ammissibile). Una funzione euristica è ammissibile quando, per ogni nodo n h(n) ≤ h∗ (n) dove h∗ (n) è il costo reale per arrivare da n allo stato obiettivo Una funzione euristica è ammissibile, dunque, quando non sovrastima mai il costo per arrivare al goal. Ci sono diverse tecniche per calcolare funzioni euristiche ammissibili, senza conoscere il valore di h∗ (n) (ad esempio, per problemi geografici, un’euristica ammissibile per problemi in cui il costo è il calcolo della distanza tra luoghi - con ostacoli - può essere la distanza in linea d’aria tra due luoghi). Per qualsiasi tipo di problema, un’euristica 16 RISOLUZIONE DEI PROBLEMI TRAMITE RICERCA sicuramente ammissibile è 0 per tutti gli stati: in questo caso, la ricerca A∗ si riduce alla ricerca a costo uniforme. In un nodo di goal, h(n) = 0. Si può dimostrare la proprietà seguente: A∗ è ottimo se viene utilizzato l’algoritmo Ricerca-albero e h è una euristica ammissibile. Idea della dimostrazione: Siano n e G2 due nodi della frontiera, tali che G2 sia un nodo di goal subottimo e n un nodo che sta sul percorso ottimo per arrivare al goal (nota: questo nodo esiste sempre se nella frontiera è presente un nodo di goal subottimo). f (G2 ) = g(G2 ) > c∗ essendo G2 un nodo di goal subottimo, ed essendo c∗ è il costo del cammino ottimo. f (n) = g(n) + h(n) ≤ c∗ essendo g(n) + h(n) una funzione che non sovrastima il costo ottimo per arrivare al goal. Allora f (n) ≤ c∗ < f (G2 ) Questo significa che non verrà mai scelto dall’algoritmo di ricerca A∗ un nodo di goal subottimo. Definizione (Euristica consistente). Una funzione euristica è consistente quando per ogni coppia di nodi n e n0 tali che n0 sia successore di n, h(n) ≤ c(n, n0 ) + h(n0 ) dove c(n, n0 ) è il costo di passo per andare da n a n0 . La condizione di consistenza è più stringente dell’ammissibilità. Vale infatti l’implicazione seguente: se una euristica h è consistente, allora è anche ammissibile. Il viceversa non vale in generale, anche se in realtà è molto difficile trovare un’euristica ammissibile ma non consistente. Teorema. A∗ è ottimo se viene utilizzata Ricerca-grafo e h è consistente. Idea della dimostrazione: per poter garantire l’ottimaltà su ricerca grafo, devo dimostrare che tutte le volte che estraggo un nodo dalla frontiera, ho già trovato un cammino ottimo che passa da quel nodo. Se l’euristica è consistente, f (n0 ) = g(n0 ) + h(n0 ) = g(n) + c(n, n0 ) + h(n0 ) ≥ g(n) + h(n) = f (n) pertanto f (n0 ) ≥ f (n) per qualsiasi coppia n, n0 t.c. n0 successore di n. Se l’euristica è consistente, la funzione di valutazione del successore non è mai più piccola della funzione di valutazione del padre: la funzione di valutazione in un path dell’albero di ricerca è sempre non decrescente. Inoltre, si può dimostrare che se l’euristica è consistente, A∗ espande i nodi in ordine non decrescente di f (n). Questo implica che tutte le volte che viene espanso un nodo, si è trovato un cammino ottimo fino allo stato corrispondente a quel nodo. In particolare, la prima volta che viene espanso un nodo di goal, si è trovato un cammino ottimo per quel nodo. Sul funzionamento di A∗ si può dimostrare che l’algoritmo espande • tutti i nodi con f (n) < c∗ • qualche nodo con f (n) = c∗ • nessun nodo con f (n) > c∗ Si dice che A∗ è ottimamente efficiente: a parità di euristica, non esiste alcun altro algoritmo che espanda meno nodi di A∗ e che garantisca di trovare la soluzione ottima. Sia la ∗ complessità temporale che quella spaziale di A∗ ha la forma O((h −h)×l ). Esistono delle versioni che utilizzano un quantitativo di memoria limitato (fissato) a spese della complessità temporale. 3 Ricerca con avversari Il problema della ricerca con avversari si riscontra tipicamente in giochi come la dama o gli scacchi. È l’unica tipologia di ambiente multiagente (in particolare, si tratta di un ambiente in cui operano due soli agenti) che verrà considerato. In questo contesto non è più definito il concetto di soluzione del problema di ricerca, che viene sostituito dal concetto di strategia: una strategia indica che cosa il giocatore deve fare rispetto a tutte le possibili azioni che può compiere l’avversario. È possibile classificare i giochi, caso tipico della ricerca con avversari, secondo vari aspetti: • una prima classificazione distingue in giochi deterministici (che quindi non coinvolgono elementi come il lancio di dadi, carte da estrarre, . . . ) e giochi con casualità. • un secondo aspetto distingue i giochi a informazione perfetta da quelli a informazione imperfetta. Un gioco è a informazione perfetta se i due giocatori conoscono ad ogni istante lo stato completo del gioco, ed è a informazione imperfetta se vi sono elementi dello stato del gioco che non sono conosciuti da uno dei giocatori (un esempio è il poker, in cui il giocatore non conosce le carte in mano agli avversari) Inizialmente l’attenzione sarà rivolta ai giochi deterministici a informazione perfetta. In seguito, si vedrà come queste tecniche possono essere estese a giochi a informazione perfetta ma con casualità. 3.1 Giochi come un problema di ricerca Assumendo che il gioco si svolga a turni e che coinvolga due soli giocatori (detti max e min, dove max è il primo giocatore che deve effettuare una mossa), si può formalizzare un gioco come un problema di ricerca costituito dai seguenti elementi: • lo stato iniziale del gioco • una funzione Giocatore(s) che, dato uno stato s, restituisce un valore tra min e max, corrispondente al giocatore che deve effettuare una mossa in s. • una funzione Azioni(s) che restituisce l’insieme di mosse legali nello stato s • una funzione Risultato(s, a) = s0 , che fornisce lo stato s0 risultante dall’esecuzione dell’azione a nello stato s • il test di terminazione che, dato uno stato, indica se è uno stato finale del gioco. Il test di terminazione sostituisce quello che nella formulazione del problema di ricerca era il test obiettivo. A differenza di come era stato definito il test obiettivo (che non impedisce di effettuare ulteriori azioni), se viene soddisfatto il test di terminazione non è possibile effettuare ulteriori azioni • una funzione Utilità(s, p) che ha come parametri uno stato terminale s e uno dei due giocatori p, e indica qual è l’utilità del giocatore p per lo stato s. Un esempio di funzione utilità potrebbe ritornare +1 per il giocatore che vince, −1 per il giocatore che perde, 0 in caso di pareggi). 18 RICERCA CON AVVERSARI Figura 3.1: Porzione dell’albero di gioco per il tris Questa formalizzazione definisce implicitamente lo spazio degli stati, che è necessario esplorare con l’artificio dell’albero di ricerca, detto in questo contesto albero di gioco. Ad esempio, nella figura 3.1 è rappresentata una porzione dell’albero di gioco per il tris. Si può notare, come prima differenza con i problemi di ricerca classici definiti nelle sezioni precedenti, che per poter definire una formalizzazione di un gioco è necessario entrare più profondamente nella struttura dello stato: non è quindi in generale sufficiente avere degli stati identificati semplicemente da una lettera. Definizione (Gioco a somma zero). Un gioco si definisce a somma zero se la somma delle utilità dei giocatori è pari a 0 per ogni stato terminale. Considerando soltanto giochi a somma zero, si può definire soltanto un valore della funzione utilità (che quindi non dipende dal giocatore), corrispondente all’utilità di max, e assumere che il giocatore max vuole massimizzare l’utilità, mentre min vuole minimizzarla. Per questo motivo, nell’esempio di figura 3.1 è rappresentata soltanto l’utilità di max. 3.2 Algoritmo Minimax Per determinare la mossa migliore per il giocatore max in un gioco, si può sfruttare il calcolo del valore minimax, associato ad ogni nodo dell’albero di gioco. Il valore minimax è definito come l’utilità per max di stare in quel nodo, se da quel punto in poi entrambi i giocatori giocano la partita perfetta. L’idea di base dell’algoritmo minimax risiede nel massimizzare il minimo valore di utilità che max può ottenere se min gioca la partita perfetta, ovvero di massimizzare il peggior risultato. Se è stato generato tutto l’albero di gioco, i valori minimax dei nodi possono essere calcolati propagandoli verso la radice a partire dai nodi terminali: ALGORITMO MINIMAX 19 • il valore minimax di un nodo terminale è pari alla sua utilità per max • il valore minimax di un nodo min è il più piccolo tra i valori minimax dei suoi figli • il valore minimax di un nodo max è il più grande tra i valori minimax dei suoi figli Se non è stato ancora generato l’albero di gioco, invece, è possibile calcolare il valore minimax direttamente durante la visita (o la costruzione) dell’albero in profondità. La figura 3.2 rappresenta un albero per un gioco fittizio, in cui si è indicato per ogni nodo il valore minimax. Per convenzione, in figura sono rappresentati i nodi corrispondenti a stati in cui deve giocare max (nodi max) con un triangolo la cui punta è rivolta verso l’alto, mentre i nodi min sono rappresentati da triangoli con la punta rivolta verso il basso. I nodi terminali sono rappresentati da un quadrato. nodo max 3 valore minimax 3 3 12 8 2 4 2 2 6 11 5 2 Figura 3.2: Albero di gioco con valori minimax In giochi di dimensioni reali risulta computazionalmente impossibile costruire tutto l’albero di gioco. In questi casi, gli algoritmi che vengono utilizzati generano l’albero a partire dalla radice, senza arrivare agli stati terminali. In altri termini, questi algoritmi tagliano l’albero in corrispondenza di determinati nodi. È definito un test di taglio che, applicato a un nodo, decide in base ad alcune caratteristiche (ad esempio la profondità del nodo), se espandere o meno il sottoalbero a partire da quel nodo. Altre tecniche che vengono utilizzate consistono nell’espandere, in base a qualche caratteristica, solo un successore di alcuni nodi e non tutti (estensione singola), oppure scegliere le due o tre mosse che potrebbero essere migliori (potatura in avanti). Il risultato finale dell’applicazione di queste ottimizzazioni è un albero le cui foglie non sono terminali. Per calcolare il valore di questi nodi, non essendo definita la funzione di utilità, viene introdotta la funzione di valutazione che, dato uno stato non terminale, restituisce un valore associato a quello stato per il giocatore max: questa funzione è concettualmente simile all’euristica h definita per gli algoritmi di ricerca informata (A∗ ). Ad esempio, una funzione di valutazione per il gioco degli scacchi può valutare elementi come il numero di pezzi rimasti, il tipo di pezzi rimasti, la disposizione dei pezzi sulla scacchiera e altri ancora. Una volta costruito l’albero, viene infine utilizzato l’algoritmo minimax per propagare fino alla radice i valori ottenuti dalla funzione di valutazione. 3.2.1 Potatura alfa-beta Per migliorare l’efficienza della tecnica minimax, si può affiancare la tecnica della potatura alfa-beta (α − β pruning). Per ricavare l’albero a partire dalla definizione del gioco come problema di ricerca, si può procedere costruendolo in profondità. Appena viene raggiunto un nodo terminale, si può osservare che è possibile ricavare informazioni sui valori minimax di altri nodi, pur non conoscendo esattamente quali siano questi valori. Ad esempio, visitando un nodo terminale il cui valore di utilità è pari a 3, si può ricavare che il padre di questo nodo, se è un nodo min, avrà un valore minimax ≤ 3. A partire da considerazioni di questo tipo, si può evitare di generare e visitare intere parti dell’albero, pur mantenendo un risultato finale esattamente identico a quello generato da minimax (viene trovata la soluzione esatta, non una approssimata), in quanto si potano rami che vengono sicuramente scartati. 20 RICERCA CON AVVERSARI 3 ≤2 3 3 12 8 2 11 5 2 2 Figura 3.3: Albero ottenuto con potatura alfa-beta relativo al gioco di figura 3.2 L’entità della potatura (il numero di nodi che non vengono generati) dipende dall’ordine di valutazione dei nodi. Si può dimostrare che, se il numero di nodi generato da minimax quando esplora tutto l’albero di gioco è O(bm ) dove b è il branching factor (il massimo numero di successori di un nodo) e m è la profondità dell’albero, nel caso migliore, ossia con l’ordinamento dei nodi migliore possibile, la potatura alfa-beta permette di avere una complessità di O(bm/2 ). Algorithm 1 Potatura alfa-beta function Vmax(stato, α, β) if TestTerminale(stato) then return Utilita(stato) end if v ← −∞ for all Successori(stato) as s do v ← max(v, Vmin(s, α, β)) if v ≥ β then return v end if α ← max(α, v) end for return v end function . Per lo stato iniziale s0 : Vmax(s0 , −∞, +∞) function Vmin(stato, α, β) if TestTerminale(stato) then return Utilita(stato) end if v ← +∞ for all Successori(stato) as s do v ← min(v, Vmax(s, α, β)) if v ≤ α then return v end if β ← min(β, v) end for return v end function L’implementazione classica per calcolare il valore minimax di un nodo max e di un nodo min (algoritmo 1) fa uso di due parametri, detti α e β, dove α è il valore della scelta migliore per max trovata fino a questo momento, mentre β è il valore della scelta migliore per min trovata fino a questo momento. In generale, si ha una situazione - presentata in figura 3.4 - GIOCHI CON CASUALITÀ: EXPECTIMINIMAX 21 in cui nell’albero di gioco è presenta un nodo max in cui c’è una scelta che porta ad avere un certo valore α di utilità. A un certo punto, un’altra scelta per max porta a un altro nodo max. Se il valore di un nodo min figlio di quest’ultimo nodo è minore o uguale a un certo valore v, con v ≤ α, si possono evitare di generare i nodi sotto questo nodo min (si può effettuare un taglio alfa). Lo stesso ragionamento può essere fatto dal punto di vista di min (taglio beta): si può tagliare quando c’è un’opzione che è sicuramente più grande del suo valore β. ... α ... ... valore ≤ v ≤ α posso tagliare ... Figura 3.4: Taglio alfa 3.3 Giochi con casualità: expectiminimax Le tecniche presentate finora si applicano alla creazione di agenti per giochi deterministici, privi quindi di elementi di casualità quali il lancio di dadi. Queste tecniche possono essere generalizzate a giochi con casualità. Ovviamente, le funzioni definite per la formulazione di un problema, vanno modificate in maniera opportuna per tenere conto della presenza degli elementi di casualità: il modo più semplice per farlo è considerare un terzo giocatore (“natura”) che sceglie una mossa secondo una certa distribuzione di probabilità. Nella rappresentazione dell’albero di gioco quest’estensione equivale all’aggiunta di nodi di casualità, i cui rami uscenti sono etichettati con la probabilità con cui la scelta si verifica. L’algoritmo expectiminimax è un’estensione delle tecniche viste sinora a questo caso. Ad ogni nodo dell’albero viene assegnato un valore valore expectiminimax in questo modo: • per i nodi terminali, è il valore di utilità corrispondente • per un nodo min, è pari al minimo dei valori expectiminimax dei suoi figli • per un un nodo di casualità è il valore atteso dei valori expectiminimax dei suoi figli • per un nodo max è il massimo dei valori dei suoi figli. La figura 3.5 presenta un esempio di albero di gioco e di calcolo dei valori expectiminimax. Nell’esempio sono presenti dei nodi di casualità, rappresentati con dei cerchi, che corrispondono ai momenti del gioco in cui interviene l’elemento della casualità (in questo caso, il lancio di una moneta). In questo gioco, max sceglie una mossa, poi viene lanciata una moneta, min effettua una mossa e il gioco finisce. È possibile applicare l’algoritmo di potatura alfa-beta visto nel paragrafo precedente anche nel caso di giochi con casualità. L’algoritmo di potatura alfa-beta, nel modo in cui è stato presentato, assume che i valori che può assumere la funzione di utilità non siano noti: si aspetta che i prossimi valori della funzione di utilità possano essere qualsiasi. In realtà, è 22 RICERCA CON AVVERSARI 5.5 5.5 4 0.5 3 5 0.5 0.5 3 12 8 0.5 8 2 2 4 6 6 11 Figura 3.5: Gioco con casualità con valori expectiminimax spesso possibile fornire dei limiti superiori e inferiori sui possibili valori della funzione di utilità, ad esempio è possibile supporre che i valori della funzione di utilità per il gioco di figura 3.5 siano compresi nell’intervallo [−2, +2]. A fronte di queste considerazioni, si possono migliorare le prestazioni dell’algoritmo. Nota Essendo la funzione di valutazione una stima della funzione di utilità, possono sorgere problemi quando viene utilizzata la tecnica del taglio e dell’utilizzo della funzione di valutazione con l’algoritmo expectiminimax. Perché l’approccio minimax dia una soluzione ottima, è necessario che i valori alle foglie dell’albero siano i più precisi possibili: non è sufficiente che mantengano l’ordinamento delle preferenze nelle situazioni per max, ma devono mantenerne anche la scala, altrimenti la scelta della mossa migliore viene modificata. 4 Problemi di soddisfacimento di vincoli I problemi di soddisfacimento di vincoli (CSP, constraint satisfaction problem) sono una specializzazione del problema di ricerca in cui la rappresentazione dello stato è di tipo fattorizzato (non è atomica come negli algoritmi di ricerca informata e non informata visti precedentemente). In particolare, uno stato è un insieme di variabili che possono assumere dei valori che spaziano in determinati domini. Formalmente, un problema di soddisfacimento di vincoli è definito da: • un insieme di variabili x1 , x2 , . . . , xn • un insieme di domini D1 , D2 , . . . , Dn dove Di è il dominio dei valori della variabile xi • un insieme C1 , C2 , ..., Cm di vincoli. I vincoli specificano le combinazioni di valori per le variabili ammissibili per il problema (ad esempio, un possibile vincolo è x1 6= x2 ). Dati i valori delle variabili coinvolte, un vincolo può essere vero o falso: un vincolo può essere rappresentato come una relazione, ossia da un sottoinsieme del prodotto cartesiano dei domini delle variabili coinvolte nel vincolo Lo scopo di un problema di soddisfacimento di vincoli è trovare un assegnamento di valori alle variabili che sia completo e consistente. Un assegnamento è consistente quando non viene violato alcun vincolo; è completo quando assegna un valore ad ognuna delle variabili. I problemi CSP possono essere classificati in problemi a domini continui (problemi tipici della ricerca operativa) e problemi a domini discreti. A loro volta, i domini discreti possono essere finiti o infiniti. Nel seguito ci si concentrerà su CSP a domini discreti e finiti. Esempio 4.1 (colorazione di una mappa geografica): Data una mappa geografica dell’Australia, rappresentata in figura 4.1, si vogliono colorare i territori in modo tale che non ci siano due territori adiacenti che abbiano lo stesso colore, avendo a disposizione tre colori (rosso, verde, blu). Il problema può essere Northern Territory Queensland Western Australia South Australia New South Wales Victoria Tasmania Figura 4.1: Mappa relativa all’esempio della colorazione degli Stati formalizzato come segue: • variabili: WA, NT, SA, Q, NSW, V, T (i territori) • domini: Di = r, g, b (i tre colori: red, green, blue) 24 PROBLEMI DI SODDISFACIMENTO DI VINCOLI • vincoli: ad esempio WA 6= NT, WA 6= SA, NT 6= SA, WA 6= SA, . . . Se tutti i vincoli di un CSP sono binari, se ne può tracciare il grafo dei vincoli, ossia un grafo i cui nodi rappresentano le variabili e i cui archi rappresentano i vincoli tra le variabili: due nodi sono collegati da un arco se le variabili che rappresentano sono legate tra loro da un vincolo. Qualsiasi CSP che coinvolga vincoli che non sono binari può essere ricondotto a un CSP equivalente che ha solo vincoli binari. Q NT NSW WA V SA T Figura 4.2: Grafo dei vincoli per il problema dell’esempio 4.1 Esempio 4.2 (problema delle otto regine): Il problema delle otto regine può essere formalizzato come un problema di soddisfacimento di vincoli in vari modi. Una prima formulazione è la seguente: • si definiscono 64 variabili xij , 1 ≤ i, j ≤ 8, una per ogni casella • Dij = 0, 1 (ogni variabile vale 1 se la casella contiene una regina, 0 altrimenti) • vincoli: 1 ≤ k ≤ 8, k 6= j – xij = 1 =⇒ xik = 0 1 ≤ k ≤ 8, k 6= i – xij = 1 =⇒ xkj = 0 – P8−k – P8−k – P8−k i=1 i=1 i=1 xi,i+k ≤ 1 0≤k<8 xi+k,i ≤ 1 1≤k<8 xi,8+1−(i+k) ≤ 1 P8−k – xi+k,8+1−i ≤ 1 Pi=1 – i≤i,j≤8 0≤k<8 1≤k<8 xi,j = 8 Poiché su ogni colonna ci deve essere una e una sola regina, è possibile derivare un’altra formulazione, più compatta, del problema come CSP: • variabili: xj 1 ≤ j ≤ 8 (colonne) • domini: Dj = 1, 2, . . . , 8 (riga su cui sta la regina per la colonna considerata) • vincoli: – xj = k =⇒ xi 6= k 1 ≤ i ≤ 8, i 6= j – xj = k =⇒ |k − xj | 6= |j − i| 1 ≤ i ≤ 8, i 6= j In questa seconda formulazione non è necessario formalizzare il vincolo per cui devono esserci otto regine, in quanto viene garantito dal fatto che si cerca un assegnamento completo. Altri esempi di problemi formalizzabili come CSP sono i problemi di assegnamento (e.g. assegnamento di task a esecutori), di scheduling (programmare le partenze di attività lungo l’asse temporale). Nel caso di un problema di scheduling conviene avere come variabili gli istanti di inizio delle attività, e come vincoli i vincoli di precedenza. Ad esempio, il vincolo x2 > x1 formalizza che l’istante di inizio dell’attività 2 deve iniziare dopo l’inizio dell’attività 1, mentre, se T è la durata dell’attività 1, x2 > x1 +T formalizza che l’attività 2 deve iniziare dopo la fine dell’attività 1. CSP COME PROBLEMI DI RICERCA 25 4.1 CSP come problemi di ricerca Un problema di soddisfacimento di vincoli può essere formulato come un problema di ricerca definendone i cinque elementi caratterizzanti in questo modo: • stato iniziale: l’assegnamento vuoto {} • funzione Azioni(s): dato uno stato s, questa funzione restituisce tutti gli assegnamenti di valori consistenti a una delle variabili non assegnate in s • funzione Risultato(s, a) = s0 : lo stato s0 è costituito dallo stato s unito al nuovo assegnamento. Per come è stata definita la funzione Azioni, tutti gli assegnamenti presenti in s0 sono consistenti. • test obiettivo: verifica se lo stato s è completo1 • costo di passo: viene considerato pari a 1 Per i problemi di soddisfacimento di vincoli, la ricerca in profondità è efficiente: si dirige subito verso il livello - noto a priori - in cui si può trovare una soluzione. {} WA = r WA = g ... T =G W A = r, N T = g W A = r, N T = b . . . ... Figura 4.3: Porzione di albero generato dalla ricerca in profondità per il problema dell’esempio 4.1 Poiché lo stato viene rappresentato in modo fattorizzato, e quindi ne è nota la struttura, è possibile migliorare notevolmente lo schema di base della ricerca in profondità. Una prima ottimizzazione risiede nella considerazione che, per i problemi di soddisfacimento di vincoli, l’ordine con cui si effettuano gli assegnamenti non è importante ai fini della ricerca di una soluzione (vale la proprietà commutativa). Questo permette di fissare l’ordine con il quale assegnare le variabili, e definire la funzione Azioni in modo che, stabilito un ordine delle variabili, al primo livello generi tutti gli assegnamenti consistenti per la prima variabile, al secondo livello tutti gli assegnamenti consistenti per la seconda variabile, e cosı̀ via. Utilizzando una ricerca in profondità standard, per un problema in cui sono presenti n variabili su un dominio di d valori, vengono generati nel caso pessimo n!dn nodi. Utilizzando la funzione Azioni modificata tenendo in considerazione che l’ordine con cui si assegnano variabili non è importante ai fini del trovare una soluzione, nel caso pessimo vengono generati dn nodi. 4.2 Euristiche Pur non influenzando la possibilità di trovare o meno una soluzione, cambiando l’ordine con cui le variabili vengono assegnate, può variare l’efficienza della ricerca. Ad esempio, nel problema della colorazione della carta geografica dell’Australia, per aumentare l’efficienza della soluzione lo stato SA, coinvolto nel maggior numero di vincoli, dovrebbe essere assegnato all’inizio. Un altro aspetto critico, oltre all’ordinamento delle variabili, risiede nella scelta di un ordinamento dei valori del dominio da assegnare alle variabili. 1 la consistenza è assicurata da come è definita la funzione Azioni 26 PROBLEMI DI SODDISFACIMENTO DI VINCOLI {} WA = g WA = b WA = r W A = r, N T = g W A = r, N T = g, SA = b . . . W A = r, N T = b ... ... ... ... Figura 4.4: Porzione di albero generato per l’esempio 4.1 sfruttando la commutatività È possibile definire tutte le euristiche che verranno descritte in seguito esclusivamente perché gli stati del problema di ricerca non sono rappresentate in modo atomico, ma come insiemi di variabili con associati dei valori. Facendo uso delle euristiche, si riescono a risolvere problemi di grandi dimensioni (ad esempio il problema delle 1000 regine). 4.2.1 Scelta della variabile Per scegliere le variabili da assegnare, si può utilizzare l’euristica MRV (minimum remaining value). In base a questa euristica, viene scelta sempre la variabile con il minimo numero di valori legali rimasti nel proprio dominio. Considerando il problema dell’esempio 4.1, utilizzando l’euristica MRV l’inizio della ricerca in profondità prosegue secondo quanto rappresentato in figura 4.5(a). {} WA = b W A = b, N T = g W A = b, N T = g, SA = r (a) MRV {} SA = b SA = b, N T = g SA = b, N T = g, N SW = r (b) euristica di grado Figura 4.5: Porzione di albero di ricerca ricavato utilizzando MRV e l’euristica di grado L’euristica MRV aumenta l’efficienza della ricerca poiché permette di individuare i fallimenti il prima possibile, scegliendo - se esiste - per prima una variabile che ha dominio vuoto. Tuttavia, MRV non permette di dire alcunché sulla prima variabile a cui assegnare il valore: inizialmente, se i domini hanno tutti la stessa cardinalità, tutte le variabili hanno lo stesso numero di valori legali rimasti nel proprio dominio. Per rimediare a questo problema, si può usare l’euristica di grado, che sceglie la variabile che è coinvolta nel maggior numero di vincoli con altre variabili non ancora assegnate. Applicando l’euristica di grado all’esempio 4.1, si ottiene quanto rappresentato in figura 4.5(b), dove sono riportati solo i primi passi. A differenza di MRV, l’euristica di grado cerca di assegnare per prime le variabili più critiche, dove per variabili più critiche si intendono quelle coinvolte nel maggior numero di vincoli. PROPAGAZIONE DEI VINCOLI 27 Nell’esempio riportato, infatti, viene assegnato per primo un valore alla variabile SA. Se tutte le variabili non sono assegnate, il numero di vincoli coinvolti in una variabile è il grado del nodo nel grafo dei vincoli, da cui il nome di euristica di grado. È stato dimostrato che si può ottenere una soluzione migliore combinando sia MRV che l’euristica di grado: di base si utilizza MRV, ricorrendo all’euristica di grado quando MRV non è in grado di determinare una variabile (fornisce una parità tra più variabili). 4.2.2 Scelta del valore Una volta scelta una variabile a cui assegnare un valore a un dato livello dell’albero, rimane il problema di scegliere quale valore assegnare a quella variabile. A questo scopo, si può utilizzare l’euristica del valore meno vincolante: si sceglie il valore che lascia più libertà alle altre variabili ancora da assegnare che sono adiacenti sul grafo dei vincoli alla variabile che si sta assegnando. Esempio 4.3 (rif. esempio 4.1): Generando una prima porzione dell’albero senza utilizzare una particolare euristica, si può arrivare a questo punto: {} WA = r W A = r, N T = g ... W A = r, N T = g, Q =??? . . . ... Al terzo passo ci sono due valori possibili da assegnare alla variabile Q, b e r. Scegliendo b la vaiabile SA avrebbe 0 valori legali rimasti, mentre scegliendo r la variabile SA avrebbe 1 valore legale rimasto. L’euristica del valore meno vincolante sceglie questa seconda possibilità, ponendo Q=r. L’idea di questa euristica è favorire il più possibile il trovare una soluzione, poiché bisogna selezionare il valore da assegnare a una variabile il cui nodo è già stato generato. Questa considerazione è opposta a quella che viene fatta nella scelta della variabile: quando bisogna scegliere una variabile da assegnare, poiché si deve generare un nodo, se ci sono elementi per determinare che un ramo è già destinato al fallimento, conviene scoprirlo subito (euristica MRV). 4.3 Propagazione dei vincoli Sfruttando la rappresentazione dello stato fattorizzata di un problema di soddisfacimento di vincoli, si possono definire delle tecniche di per propagare i vincoli parallelamente alla costruzione dell’albero di ricerca. L’utilizzo di queste tecniche permette di semplificare il problema e agevolare la ricerca della soluzione: mentre nella formulazione come problema di ricerca il controllo del rispetto dei vincoli è contenuto nella funzione Azioni, le tecniche di propagazione dei vincoli controllano i vincoli anche in altri momenti della ricerca. Verifica in avanti La tecnica della verifica in avanti, o forward checking, permette di controllare i vincoli tutte le volte che si assegna un valore a una variabile. A seguito dell’assegnamento di un valore a una variabile x, vengono controllate tutte le variabili non assegnate legate a x da un vincolo, e si eliminano dai domini di queste variabili i valori 28 PROBLEMI DI SODDISFACIMENTO DI VINCOLI che sono inconsistenti con il valore di x. L’informazione che è contenuta nei vincoli viene propagata nei domini delle variabili, in un processo parallelo alla costruzione dell’albero di ricerca. La tecnica della verifica in avanti si applica molto spesso come supporto all’euristica MRV, per aggiornare ad ogni iterazione i domini delle variabili. Esempio 4.4 (rif. esempio 4.1): Nella tabella sottostante sono rappresentati, per ogni assegnamento che viene effettuato nell’esplorazione di parte di un ramo dell’albero di ricerca, i domini delle variabili per come vengono modificati dalla verifica in avanti. I valori in grassetto sono le variabili già assegnate. WA {r, g, b} r r r NT {r, g, b} {g, b} {b} {b} Q {r, g, b} {r, g, b} g g NSW {r, g, b} {r, g, b} {r, b} {r} SA {r, g, b} {g, b} {b} {} V {r, g, b} {r, g, b} {r, g, b} b T {r, g, b} {r, g, b} {r, g, b} {r, g, b} (a) domini {} WA = r W A = r, Q = g W A = r, Q = g, V = b ... (b) albero A questo punto della costruzione dell’albero, il dominio della variabile SA è vuoto: questo significa che questo ramo è destinato al fallimento. Utilizzando l’euristica MRV, SA sarebbe la prossima variabile da generare. Applicando questa tecnica, si è scoperto che il dominio di SA è vuoto appena dopo avere assegnato il valore b allo stato V, e non quando si è cercato di generare il successore (come nell’euristica MRV). In realtà, ci si poteva accorgere che il ramo è destinato al fallimento già dopo l’assegnamento Q=g: NT e SA non possono contemporaneamente essere blu, unico valore consentito, ma questo tipo di inconsistenza non viene rilevato dalla verifica in avanti. Consistenza d’arco Un’euristica migliore per la propagazione dei vincoli è data dalla consistenza d’arco. In particolare, in questo paragrafo viene descritto l’algoritmo AC-3. Questo algoritmo controlla il grafo dei vincoli e considera ogni lato come bidirezionale2 . Definizione (Arco consistente). Si dice che un arco x → y è consistente quando, per ogni valore del dominio di x esiste qualche valore consistente nel dominio di y. Applicare la consistenza d’arco significa rendere consistenti gli archi x → y cancellando valori dal dominio di x. L’idea dell’algoritmo AC-3 è la seguente: • Si crea una coda contenente tutti gli archi • Per ogni arco x → y presente in coda, si verifica la consistenza con gli altri archi • Quando viene aggiornato il dominio di una variabile x per rendere consistente un arco, è necessario rimettere in coda tutti gli archi della forma z → x, perché potrebbero non essere più consistenti a seguito della variazione del dominio di x 2 ad esempio, il lato tra SA e NSW viene scomposto in due archi SA → NSW e NSW → SA PROPAGAZIONE DEI VINCOLI 29 La consistenza d’arco può essere applicata sia come pre-processing prima di iniziare la ricerca, per semplificare i domini (e quindi avere un minore branching factor per l’albero), che applicandola tutte le volte che viene assegnato un valore a una variabile. Ci sono tecniche più potenti della consistenza d’arco per semplificare il problema di ricerca: la consistenza d’arco (2-consistenza) verifica solo la consistenza tra coppie di variabili; altre tecniche come la 3-consistenza verificano la consistenza tra triple di variabili, e cosı̀ via generalizzando, al costo di aumentarne la complessità: verificare la n-consistenza di un problema con n variabili equivale a risolvere il problema di ricerca. Esistono problemi, anche non banali, che possono essere completamente risolti senza costruire l’albero ed effettuare la ricerca, ma semplicemente applicando tecniche come la consistenza d’arco e le sue generalizzazioni. 5 Agenti basati sulla conoscenza Come per tutte le tipologie di agente analizzate finora, anche gli agenti basati sulla conoscenza risolvono problemi costruendo un albero di ricerca, terminando quando viene generato un nodo che corrisponde a uno stato finale nello spazio degli stati. Quello che li differenzia dalle altre tipologie di agente è il contenuto dei singoli nodi. A differenza delle rappresentazioni atomiche tipiche degli algoritmi di ricerca informata e non informata, e delle rappresentazioni di tipo fattorizzato tipiche dei problemi di soddisfacimento di vincoli, gli agenti basati sulla conoscenza utilizzano infatti una rappresentazione dello stato del mondo strutturata. In particolare, lo stato è rappresentato attraverso una KB (knowledge base), che contiene della conoscenza (informazione espressa formalmente). Nel caso che verrà considerato, le informazioni presenti nella knowledge base sono espresse attraverso il linguaggio formale della logica (logica proposizionale oppure logica del primo ordine). Una KB contiene un insieme di formule in and logico tra di loro. Dal punto di vista dell’implementazione, servono due meccanismi per poter operare su una KB di questo tipo: • un meccanismo per asserire fatti, ossia aggiungere formule alla base di conoscenza • una procedura di interrogazione, con la quale si può chiedere se una formula α è conseguenza logica delle formule presenti (KB |= α), ossia se α è vero in tutti i modelli che rendono vere le formule della KB. Procedure di inferenza Si dice che KB `i α quando a partire dalla KB si riesce a derivare α (meccanicamente, ovvero guardando solo la sintassi e non la semantica) applicando la procedura di inferenza i. Rispetto alla relazione tra l’inferenza e il concetto di conseguenza logica, si possono definire le proprietà di completezza e correttezza. Definizione (Completezza). Una procedura di inferenza i è completa se tutte le formule che sono conseguenza logica della KB possono essere derivate dalla procedura i: KB |= α =⇒ KB `i α Definizione (Correttezza). Una procedura di inferenza i si dice corretta (sound) se, quando deriva una formula α, allora α è conseguenza logica della KB: KB `i α =⇒ KB |= α Naturalmente, una procedura di inferenza “ideale” deve essere corretta e completa. In ogni caso, è importante garantire la correttezza rispetto alla completezza. Il vantaggio di utilizzare formule logiche per rappresentare lo stato sta nel poter utilizzare un insieme piccole di formule per la rappresentazione di uno stato del mondo, senza avere la necessità di esplicitarne tutte le conseguenze. Queste conseguenze possono essere derivate all’occorrenza, direttamente dall’agente, attraverso una procedura di inferenza. Per questo motivo gli agenti basati sulla conoscenza possono lavorare anche in ambienti parzialmente osservabili: dagli assiomi e dalla parte del mondo osservabile l’agente può dedurre la struttura delle parti dell’ambiente non direttamente osservabili. 32 AGENTI BASATI SULLA CONOSCENZA Nota Quando l’agente applica una procedura di inferenza per derivare nuova conoscenza, lo sta facendo in un singolo stato del mondo (che è rappresentato da una singola KB), per derivare proprietà di quello stato implicitamente contenute nella KB. 5.1 Procedure di inferenza per la logica proposizionale 5.1.1 Concatenazione in avanti (FC) La concatenazione in avanti è una procedura di inferenza che si applica a basi di conoscenza che contengono esclusivamente clausole definite. Definizione (Clausola definita). Una clausola definita è una formula costituita da un solo simbolo proposizionale oppure da una congiunzione di simboli proposizionali che implica un singolo simbolo proposizionale. Esempi di clausole definite sono A, A ∧ B =⇒ C, A =⇒ C. Esempi di formule che non sono clausole definite sono A∧B =⇒ C ∨D (non implica un singolo simbolo proposizionale) e A ∨ B =⇒ C (l’antecedente non è una congiunzione). Nelle applicazioni pratiche, la restrizione a KB costituite esclusivamente da clausole definite non è troppo limitante, in quanto moltissimi domini possono essere adeguatamente descritti da formule di questo tipo (ad esempio il funzionamento degli artefatti). La concatenazione in avanti applica una singola regola di inferenza: il modus ponens (MP), che nel caso della logica proposizionale assume la seguente forma1 : α1 ∧ α2 ∧ · · · ∧ αn =⇒ β MP β dove α e β sono generici simboli proposizionali. La concatenazione in avanti, data una base di conoscenza, estrae ripetutamente da essa n + 1 formule che soddisfano la condizione espressa dal modus ponens, e aggiunge la formula risultante dall’applicazione della regola di inferenza. α1 α2 ... αn Esempio 5.1: Dalla seguente KB 1. P =⇒ Q 2. L ∧ M =⇒ P 3. B ∧ L =⇒ M 4. A ∧ P =⇒ L 5. A ∧ B =⇒ L 6. A 7. B le formule che vengono aggiunte alla KB dall’algoritmo sono: 8 L 9 M MP(5,6,7) MP(7,8,3) 10 P MP(8,9,2) 11 Q MP(10,1) A questo punto l’algoritmo ha raggiunto un punto fisso, e non può derivare più nulla. La concatenazione in avanti può derivare solo simboli proposizionali (fatti), ma non deriva mai nuove implicazioni (regole). Per questo motivo, un limite superiore al numero di applicazioni del modus ponens effettuate dalla concatenazione in avanti è dato dal numero di regole. Per questo motivo - che si può sfruttare per ricavarne una implementazione efficiente - la complessità temporale dell’algoritmo è lineare nel numero di formule presenti nella KB. 1 nelle rappresentazioni di una regola di inferenza, vengono indicate sopra la linea orizzontale tutte le formule che devono essere presenti nella base di conoscenza per potere applicare la regola, mentre sotto la linea sono indicate le formule che possono essere aggiunte alla KB applicando la regola di inferenza PROCEDURE DI INFERENZA PER LA LOGICA PROPOSIZIONALE 33 Proprietà Per le sole KB contenenti solo formule definite, la procedura di inferenza della concatenazione in avanti è corretta e completa. 5.1.2 Concatenazione all’indietro (BC) La concatenazione all’indietro è una procedura di inferenza che parte dalla dalla formula Q di cui si vuole dimostrare se è o meno conseguenza della base di conoscenza: KB `?BC Q Questa formula, a differenza del caso della concatenazione in avanti, va indicata esplicitamente, in quanto la procedura di inferenza parte dall’obiettivo (la query) e cerca di risalire alle premesse, di di dimostrare se è conseguenza delle formule presenti nella KB. La struttura dati che viene utilizzata è detta stack degli obiettivi. Inizialmente, lo stack contiene l’obiettivo finale Q che si vuole dimostrare. Ogni volta che l’algoritmo estrae da questa struttura dati un obiettivo, controlla se è presente come fatto nella base di conoscenza. Se Q non è presente, cerca di applicare “al contrario” una regola presente nella KB, ossia cerca una regola di cui Q è implicazione. Ad esempio, riprendendo la base di conoscenza dell’esempio 5.1 e considerando come obiettivo Q, l’algoritmo trova la regola P =⇒ Q (1) quindi si riduce a dimostrare il sotto-obiettivo P , che è una premessa dell’obiettivo. L’algoritmo quindi inserisce nello stack il nuovo obiettivo P . Anche P non è presente come fatto nella KB, ma si trova la regola L ∧ M =⇒ P e l’algoritmo procede considerando come obiettivi L e M . A questo punto, l’algoritmo considera il sotto-obiettivo L. Nella KB esistono queste due formule: A ∧ P =⇒ P A ∧ B =⇒ P Questo significa che, per dimostrare vero L bisogna dimostrare vero A ∧ P oppure A ∧ B. In questo caso, vengono inseriti tutti nello stack degli obiettivi. Si procede quindi considerando M . Si trova la regola B∧L =⇒ M , che vengono aggiunti alla struttura dati. Si estrae quindi A che è presente nella KB come fatto, e quindi viene aggiunto nella struttura dati dimostrati. Si estrae P : questo obiettivo non è dimostrabile, perché è proprio quello che si sta cercando di dimostrare, quindi viene eliminato dallo stack degli obiettivi, e la strada di dimostrare A e P per dimostrare L fallisce. A questo punto si considera A, che è già dimostrato. Si estrae B, che risulta presente nella KB come fatto, e quindi anch’esso dimostrato. Avendo dimostrato A e B, si è dimostrato anche L, che quindi viene aggiunto ai dimostrati. Si estraggono B e L dallo stack degli obiettivi, entrambi già dimostrati: a questo punto si può dimostrare M , P , Q e l’algoritmo termina. Il funzionamento dell’algoritmo nel complesso può essere rappresentato dall’albero and-or di figura 5.1. A differenza della concatenazione in avanti, che è guidata dai dati in quanto procede dai fatti presenti nella KB, la concatenazione all’indietro è guidata dagli obiettivi. Si può dimostrare che anche la concatenazione all’indietro è corretta e completa per KB composte da clausole definite. 5.1.3 Risoluzione (R) La risoluzione si applica a basi di conoscenza rappresentate in forma normale congiuntiva (CNF). Una KB di questo tipo è un insieme di clausole, dove una clausola è una disgiunzione 34 AGENTI BASATI SULLA CONOSCENZA Q P L A P M A B B L Figura 5.1: Albero and-or relativo alla concatenazione all’indietro di letterali2 . Un letterale è un simbolo proposizionale oppure un simbolo proposizionale negato. Esempi di clausole sono A ∨ ¬B B ∨ ¬C ∨ ¬D Una base di conoscenza che contiene qualsiasi formula della logica proposizionale è esprimibile in forma normale congiuntiva: il fatto che la risoluzione sia applicabile soltanto a KB in questa forma, pertanto, non è limitante. Rispetto alla definizione data in precedenza, si può notare che una clausola definita è una clausola che contiene esattamente un singolo letterale positivo. Si definisce invece clausola di Horn una clausola che contiene al più un letterale positivo. La procedura di inferenza detta “risoluzione” applica ripetutamente la regola di inferenza della risoluzione: `1 ∨ · · · ∨ `i ∨ · · · ∨ `k , m1 ∨ · · · ∨ mj ∨ · · · ∨ mn `1 ∨ · · · ∨ `i−1 ∨ `i+1 ∨ · · · ∨ `k ∨ m1 ∨ · · · ∨ mj−1 ∨ mj+1 ∨ · · · ∨ mn R dove li e mi sono letterali, e li e mj sono letterali complementari, ossia sono lo stesso simbolo proposizionale, l’uno negato e l’altro no. La risoluzione parte da due clausole e deriva una nuova clausola che contiene tutti i letterali di partenza tranne i due complementari. Esempio 5.2: A ∨ ¬B B ∨ ¬C ∨ ¬D A ∨ ¬C ∨ ¬D R Anche per la risoluzione è necessario fornire l’obiettivo, ossia la formula α che si vuole dimostrare3 : KB `?R α La risoluzione funziona per refutazione: si chiede se, aggiungendo alla base di conoscenza la negazione della formula che si vuole dimostrare, si giunge a un assurdo (la clausola vuota ⊥): KB ∪ {¬α} `?R ⊥ Si può dimostrare che la procedura di inferenza della risoluzione è corretta e completa per la logica proposizionale. 2 all’interno della KB le singole formule, quindi le clausole, sono in congiunzione tra loro differenza delle procedure di inferenza citate precedentemente, con la risoluzione l’obiettivo non è un singolo simbolo proposizionale ma è una generica formula 3a PROCEDURE DI INFERENZA PER LA LOGICA DEL PRIMO ORDINE 35 Nota Per poter applicare la risoluzione, è necessario che ¬α (la negazione della formula da dimostrare) sia in forma normale congiuntiva. Esempio 5.3: Data la seguente KB 1. ¬B ∨ A 2. B si vuole trovare se KB `?R A. Questo equivale a chiedere se KB ∪ {¬A} `?R ⊥. A quest’ultima formulazione si può applicare la risoluzione. Ad esempio, da ¬B ∨ A e B si genera per risoluzione la formula A. Applicando la risoluzione alle due clausole unitarie ¬A e A, che sono complementari, si ottiene la clausola vuota ⊥. Alcune strategie per applicare la risoluzione in modo efficiente sono: • Risoluzione unitaria (o risoluzione con preferenze per le clausole unitarie). Questa strategia cerca di applicare la regola della risoluzione a due clausole di cui una è costituita da un singolo letterale • Risoluzione con insieme di supporto. Questa strategia impone che ogni passo della risoluzione coinvolga almeno una clausola da quello che si è definito come insieme di supporto. Le clausole risolventi vengono a loro volta aggiunte all’insieme di supporto. La criticità di questa strategia risiede nella scelta dell’insieme: una scelta errata può rendere l’algoritmo incompleto • Risoluzione di input. in ogni passaggio della risoluzione combina una formula di input con un’altra formula. Tranne che in casi specifici, questa strategia non è in generale completa. 5.2 Procedure di inferenza per la logica del primo ordine 5.2.1 Concetti preliminari Si definisce unificatore di due formule del primo ordine α, β la sostituzione Unify(α, β) = θ tale che αθ = βθ dove una sostituzione {variabile/termineground} sostituisce (sintatticamente) le occorrenze della variabile con il termine ground specificato. α β θ Knows(John, α) Knows(John, x) Knows(John, x) Knows(John, x) Knows(John, Jane) Knows(y, M other(y)) Knows(x, OJ) Knows(y, x) {x/Jane} {y/John, x/M other(John)} le formule non possono essere unificate {y/John} Tabella 5.1: Alcuni esempi di unificatori La tabella 5.1 riporta alcuni esempi di unificatori. Nell’ultima riga, l’unificatore riportato non è l’unico, ma è l’unificatore più generale (MGU, most general unifier). Altri unificatori - che non sono i più generali - si possono trovare legando x a una costante. In generale, è possibile determinare algoritmicamente l’unificatore più generale per due formule della logica del primo ordine. 36 AGENTI BASATI SULLA CONOSCENZA 5.2.2 Concatenazione in avanti e all’indietro In questo paragrafo siamo interessati a determinare se una formula del prim’ordine α è o meno conseguenza logica di una KB mediante una procedura di inferenza. Se la procedura di inferenza i è corretta, chiedere se KB |=? α equivale a chiedere se KB `?i α Le procedure di inferenza che si utilizzano in logica del primo ordine sono le stesse viste nel caso della logica proposizionale. A differenza del caso proposizionale, la procedura di inferenza fornisce, se determina che la formula α è conseguenza della KB - anche una sostituzione θ tale per cui la formula ottenuta applicando ad α la sostituzione θ è una conseguenza logica della KB (sempre se la procedura di inferenza è corretta). Nelle basi di conoscenza che verranno utilizzate per fare inferenza non si utilizzano i quantificatori, e si assume che, in ogni formula della KB, tutte le variabili sono quantificate universalmente. La concatenazione in avanti si applica a KB che contengono solo clausole definite (predicati singoli, o fatti, e congiunzioni di predicati singoli che implicano un solo predicato, o regole). La concatenazione in avanti applica ripetutamente il modus ponens in questa forma: p1 0 , p2 0 , . . . , pn 0 , (p1 ∧ p2 ∧ . . . ∧ pn ⇒ q) qθ MP dove p01 , p02 , . . . , p0n , p1 , . . . , pn , q sono formule atomiche della logica del primo ordine (predicati singoli) e rispetto alla sostituzione θ: ∀i pi 0 θ = p i θ Esempio 5.4: Data la seguente base di conoscenza 1. King(John) 2. Greedy(y) 3. King(x) ∧ Greedy(x) =⇒ Evil(x) la sostituzione θ che unifica la (1) a King(x) e la (2) a Greedy(x) è θ = {x/John, y/John} per cui si può applicare il Modus Ponens, ottenendo - applicando alla formula risultante la sostituzione θ: 4. Evil(John) Esempio 5.5 (dal linguaggio naturale alla logica): Dato il seguente testo La legge dice che è un crimine per un americano vendere armi alle nazioni ostili. La nazione di Nono, un nemico dell’America, possiede dei missili, e tutti i suoi missili sono stati venduti dal colonnello West, che è un americano. Il colonnello West è un criminale? una KB che rappresenta la situazione è: 1. American(x) ∧ Weapon(y) ∧ Hostile(z) ∧ Sells(x, y, z) =⇒ Criminal(x) 2. Missile(M) 3. Owns(Nono, M) 4. Missile(x) ∧ Owns(Nono, x) =⇒ Sells(West, x, Nono) 5. Missile(x) =⇒ Weapon(x) 6. Enemy(x, America) =⇒ Hostile(x) 7. Enemy(Nono, America) PROCEDURE DI INFERENZA PER LA LOGICA DEL PRIMO ORDINE 37 8. American(West) dove la (2) e la (3) rappresentano la formula (∃x)(M issile(x) ∧ Owns(N ono, x)) che non può essere inserita direttamente nella KB perché x è quantificata esistenzialmente. Per inserirla nella KB, la variabile viene sostituita da un nuovo simbolo di costante, M (questo è un caso particolare del procedimento di Skolemizzazione spiegato in seguito). La domanda “Il colonnello West è un criminale?” si traduce - volendo utilizzare la concatenazione in avanti (forward checking, FC), nel chiedersi se ? KB `F C α dove α = Criminal(West). Applicando la concatenazione in avanti, si ottiene la situazione rappresentata dall’albero di figura 5.2, che viene costruito a partire dalle foglie, ossia dai fatti della base di conoscenza. Criminal(West) Weapon(M1) American(West) Missile(M1) Sells(West,M1,Nono) Owns(Nono,M1) Hostile(Nono) Enemy(Nono,America) Figura 5.2: Albero risultante dall’applicazione della FC all’esempio 5.5 Si può dimostrare che la concatenazione in avanti è corretta e completa per KB composte da sole clausole definite. Il problema sta nella semidecidibilità della logica del primo ordine: se la formula α non è conseguenza logica della KB, la procedura potrebbe non terminare, e questo vale anche per il sottoinsieme della logica del primo ordine costituito dalle sole clausole definite. È invece garantita la terminazione per l’algoritmo di concatenazione in avanti per il sottoinsieme della logica del primo ordine che non utilizza le funzioni. Come nel caso della concatenazione in avanti, anche la concatenazione all’indietro si applica a KB composte da sole clausole definite. La procedura della concatenazione all’indietro parte dall’obiettivo ed è simile al caso proposizionale, effettuando una ricerca in profondità. Un esempio è dato dall’albero di figura 5.3, riferito sempre all’esempio 5.5. La concatenazione all’indietro è una procedura di inferenza corretta, ma non completa (se si considera la presenza di funzioni, la ricerca in profondità potrebbe non terminare). Sulla concatenazione all’indietro è basato il funzionamento del linguaggio di programmazione prolog, mentre la concatenazione in avanti trova applicazione in database deduttivi (Datalog). 5.2.3 Risoluzione Come nel caso proposizionale, la risoluzione si applica a una KB che contiene solamente delle formule in CNF (tutte le formule della logica del primo ordine sono esprimibili in CNF). I passi per portare una formula generica in CNF sono una generalizzazione della procedura utilizzata per la logca proposizionale. Esempio 5.6: La frase “chiunque ami tutti gli animali è amato da qualcuno” può essere espressa in logica del primo ordine come ∀x[(∀y Animal(y) =⇒ Loves(x, y)) =⇒ (∃y Loves(y, x))] 38 AGENTI BASATI SULLA CONOSCENZA {x/West, y/M1, z/Nono} Criminal(West) Weapon(y) American(West) Sells(West,M1,z) Hostile(Nono) { z/Nono } {} Missile(y) Missile(M1) Owns(Nono,M1) Enemy(Nono,America) { y/M1 } {} {} {} Figura 5.3: Applicazione della BC all’esempio 5.5 per portarla in forma normale congiuntiva, si seguono i seguenti passi: • Eliminazione delle implicazioni: α =⇒ β ≡ ¬α ∨ β. Questo si applica anche alle doppie implicazioni, essendo α ⇔ β ≡ (α =⇒ β) ∧ (β =⇒ α). Applicando questo passo all’esempio: ∀x[¬[∀yAnimal(y) =⇒ Loves(x, y)] ∨ [∃yLoves(y, x)]] ∀x[¬[∀y(¬Animal(y) ∨ Loves(x, y))] ∨ [∃yLoves(y, x)]] • Spostamento delle negazioni all’interno. Alla fine di questo passo, la formula deve contenere simboli di negazione solamente davanti ai simboli di predicato, utilizzando regole come ¬∀xα ≡ ∃x¬α oppure ¬∃xα ≡ ∀x¬α Nell’esempio: ∀x[(∃y¬(¬Animal(y) ∨ Loves(x, y))) ∨ (∃yLoves(y, x))] da cui applicando la legge di De Morgan si ottiene ∀x[(∃y(Animal(y) ∧ ¬Loves(x, y))) ∨ (∃yLoves(y, x))] • Standardizzazione delle variabili: ogni quantificatore deve quantificare su una variabile diversa. Nell’esempio, la formula contiene due quantificatori che operano su due variabili che hanno lo stesso nome (y), quindi si cambia nome a una delle due variabili: ∀x[(∃y(Animal(y) ∧ ¬Loves(x, y))) ∨ (∃zLoves(z, x))] • Skolemizzazione: si eliminano le variabili quantificate esistenzialmente, sostituendole con una funzione (detta funzione di Skolem) delle variabili quantificate universalmente nel cui scope ricade la variabile che si sta sostituendo4 . Applicando il procedimento all’esempio: ∀x[(Animal(F (x)) ∧ ¬Loves(x, F (x))) ∨ (Loves(G(x), x))] • Eliminazione dei ∀ [Animal(F (x)) ∧ ¬Loves(x, F (x))] ∨ Loves(G(x), x) • Distribuzione degli ∧ sugli ∨ [Animal(F (x)) ∨ Loves(G(x), z)] ∧ [¬Loves(x, F (x)) ∨ Loves(G(x), x)] che è formula di partenza in forma CNF, ed è una congiunzione di disgiunzioni composta da due clausole. 4 non è sufficiente sostituirle con costanti, altrimenti si stravolgerebbe il significato della formula di partenza PROCEDURE DI INFERENZA PER LA LOGICA DEL PRIMO ORDINE 39 Il procedimento (automatizzabile) è in grado di trasformare in CNF qualsiasi formula della logica del primo ordine. La formula che si ottiene è inferenzialmente equivalente alla formula di partenza. La procedura di inferenza applica la regola della risoluzione, che per la logica del primo ordine assume la forma `1 ∨ `2 ∨ · · · ∨ `k , m1 ∨ m2 ∨ · · · ∨ mn (`1 ∨ · · · ∨ `i−1 ∨ `i+1 ∨ · · · ∨ `k ∨ m1 ∨ · · · ∨ mj−1 ∨ mj+1 ∨ · · · ∨ mn )θ dove Unify(`i , ¬mj ) = θ Esempio 5.7: Date le formule • ¬Rich(x) ∨ U nhappy(x) • Rich(Ken) si può applicare la risoluzione utilizzando la sostituzione θ = {x/Ken} ottenendo la clausola U nhappy(Ken), che può essere aggiunta alla KB. La procedura di inferenza della risoluzione applica ripetutamente la regola di inferenza a una KB composta dalle formule di partenza e dalla negazione della formula a cui si vuole arrivare, cercando di inferire la clausola vuota, ovvero per dimostrare se ? KB |= α applica l’inferenza ? KB ∪ {¬α} `R ⊥ La procedura è corretta ed è completa per refutazione, ossia se α è effettivamente conseguenza logica della KB prima o poi la risoluzione troverà la clausola vuota. Se α non è conseguenza logica della KB la risoluzione potrebbe fermarsi fornendo la risposta corretta oppure andare avanti all’infinito. Esempio 5.8: Data la KB relativa all’esempio 5.5, si vuole dimostrare se ? KB |= Criminal(West) utilizzando la risoluzione, ossia ? KB ∪ {¬Criminal(West)} `R ⊥ Per prima cosa si aggiunge alla KB la formula 9. ¬Criminal(West) Applicando la risoluzione, si trovano le seguenti clausole: 10. ¬American(West) ∨ ¬Weapon(y) ∨ ¬Sells(West, y, z) ∨ ¬Hostile(z) 11. ¬Weapon(y) ∨ ¬Sells(West, y, z) ∨ ¬Hostile(z) 12. ¬Missile(y) ∨ ¬Sells(West, y, z) ∨ ¬Hostile(z) 13. ¬Sells(West, M, z) ∨ ¬Hostile(z) R(10, 2) R(3, 11) R(4, 12), = {y/M} 14. ¬Missile(M) ∨ ¬Owns(Nono, M) ∨ ¬Hostile(Nono) 15. ¬Owns(Nono, M) ∨ ¬Hostile(Nono) 16. ¬Hostile(Nono) R(5, 13), {x/M, z/Nono} R(4, 14) R(6, 15) 17. ¬Enemy(Nono, America) 18. ⊥ R(1, 9), {x/West} R(7, 16), {x/Nono} R(8, 17) La strategia di risoluzione eseguita è una risoluzione di input (tutte le volte si è risolta una formula generica con una formula appartenente alla KB iniziale o a ¬α), e si è inoltre cercato di eliminare ad ogni passo il letterale più a sinistra della clausola risultante. 6 Pianificazione Le tecniche di pianificazione mettono insieme la costruzione di un albero per trovare la soluzione a un problema di ricerca e l’utilizzo della logica per rappresentare gli stati come insiemi di formule, il che ha un forte impatto sull’efficienza e sull’espressività della rappresentazione. Nella pianificazione, uno stato è un insieme di formule logiche. La rappresentazione degli stati è basata sullo standard PDDL (planning domain definition language). Nel seguito verrà utilizzato un sottoinsieme di questo linguaggio, il linguaggio STRIPS (Stanford Research Institute Problem Solver), che storicamente è antecedente a PDDL ed è meno espressivo. 6.1 STRIPS Uno stato è formato da una congiunzione di formule della logica del primo ordine. Le formule che costituiscono uno stato devono essere letterali positivi, senza funzioni, e ground (senza variabili). Le formule di uno stato sono implicitamente in and tra loro. Esempio 6.1 (Mondo dei blocchi): In questo esempio, c’è un tavolo su cui sono posati dei blocchi (A, B e C). Un robot li può afferrare e spostare. Un possibile stato di questo mondo è il seguente: C A B che in STRIPS può essere rappresentato come: BLOCK(A), BLOCK(B), BLOCK(C), ON(A, TABLE), ON(B, TABLE), ON(C,A), CLEAR(B), CLEAR(C), HANDEMPTY Nella rappresentazione in STRIPS dell’esempio, ci sono ridondanze e informazioni rappresentate: ad esempio, i letterali CLEAR(B) e CLEAR(C), che indicano che B e C sono liberi, potrebbero essere dedotti dal fatto che non c’è alcun blocco sopra B e C. Per poter effettuare questo tipo di deduzioni, non valide in logica, spesso in questo ambito si introduce l’ipotesi del mondo chiuso: si ipotizza che tutte le formule scritte sono vere, e tutte le formule che non vengono menzionate sono false. Facendo questa ipotesi, i letterali CLEAR(B) e CLEAR(C) sono effettivamente ridondanti. In STRIPS, le azioni si indicano attraverso gli schemi di azione, che rappresentano un insieme di azioni possibili a seconda di come vengono istanziate le variabili. Un possibile schema di azioni per il mondo dei blocchi è il seguente: UnStack(x, y) P: HANDEMPTY, BLOCK(x), BLOCK(y), CLEAR(x), ON(x, y) E: ¬HANDEMPTY, ¬CLEAR(x), HOLDING(x), ¬ON(x, y), CLEAR(y) 42 PIANIFICAZIONE Uno schema d’azioni è costituito da due liste di formule. La lista P, detta lista delle precondizioni, che contiene un insieme di formule della logica del primo ordine analoghe a quelle utilizzate per la rappresentazione dello stato (letterali positivi ground privi di funzioni). La lista delle precondizioni indica ciò che deve essere vero in uno stato per poter eseguire l’azione. La lista E, detta lista degli effetti, contiene letterali positivi oppure negativi. È a sua volta costituita dalla lista delle aggiunte (add list), che comprende i letterali positivi e indica quali fatti devono essere veri successivamente all’applicazione dell’azione, e dalla delete list, composta dai letterali negativi, che indica i fatti che erano veri prima di applicare l’azione e non sono più veri in seguito. Come per la rappresentazione dello stato, precondizioni ed effetti si intendono in and logico tra loro. Un’azione si ottiene istanziando uno schema di azione, ossia assegnando dei valori alle variabili. A partire dallo stato dell’esempio 6.1, un’azione che può essere eseguita è UnStack(C, A) In generale, un’azione A è applicabile in uno stato S quando tutte le precondizioni di A compaiono nella descrizione di S. Il risultato dell’applicazione di un’azione a uno stato è un nuovo stato ottenuto • copiando la descrizione del vecchio stato nel nuovo • aggiungendo alla descrizione gli elementi della lista delle aggiunte dell’azione applicata • cancellando gli elementi presenti nella lista delle cancellazioni Osservazione Un classico problema della pianificazione è il cosiddetto frame problem: da un frame all’altro, moltissimi elementi rimangono identici e vengono modificati solo alcuni elementi. Non è banale a questo proposito inserire in logica il concetto di tempo, il che rende complicato risolvere questo problema in logica. STRIPS risolve questo problema copiando le formule e modificando solo ciò che le azioni modificano. Un altro problema è il qualification problem, che riguarda le precondizioni. Si assume che le precondizioni elencate in P siano tutte e sole le precondizioni necessarie perché l’azione abbia successo. Questo, nel mondo reale, porta rapidamente ad elencare un numero infinito di precondizioni. Per completare la definizione di un problema di ricerca, manca la definizione di un obiettivo (goal), che in STRIPS è una congiunzione di formule della logica del primo ordine che siano letterali positivi ground e privi di funzioni. Si noti che un obiettivo non è uno stato, ma è un insieme di letterali che devono essere veri in uno stato affinché quello stato sia considerato terminale. Ad esempio, il goal ON(B,A) è verificato in tanti stati del mondo dei blocchi: dipende da dove sta il blocco C. La soluzione che viene restituita (piano) è la sequenza di azioni che porta da uno stato iniziale a uno stato che soddisfa l’obiettivo. In PDDL, a differenza di STRIPS, precondizioni e obiettivi possono contenere letterali negativi. Un letterale negativo in una precondizione oppure in un obiettivo è soddisfatto se non compare nella descrizione dello stato. Esempio 6.2: Completiamo la definizione del mondo dei blocchi aggiungendo gli schemi di azioni, oltre ad UnStack(x, y), che ne completano la definizione. Lo schema di azione Stack(x, y) posa il blocco x, contenuto nel braccio robotico, sopra il blocco y. Stack(x, y) P: HOLDING(x), BLOCK(x), BLOCK(y), CLEAR(y) E: ON(x, y), ¬CLEAR(y), ¬HOLDING(x), HANDEMPTY, CLEAR(x) I due schemi di azione analizzati finora non permettono di mettere oppure togliere un blocco dal tavolo. È quindi necessario definire un’altra coppia di schemi di azione per posare e togliere un blocco dal tavolo. PIANIFICAZIONE IN AVANTI 43 PickUp(x) P: HANDEMPTY, BLOCK(x), CLEAR(x), ON(x, TABLE) E: ¬HANDEMPTY, ¬CLEAR(x), HOLDING(x), ¬ON(x, TABLE) PullDown(x) P: HOLDING(x) E: ON(x, TABLE), ¬HOLDING(x), CLEAR(x), HANDEMPTY 6.2 Pianificazione in avanti Esistono vari approcci algoritmici per risolvere i problemi di pianificazione. Un primo approccio è quello della pianificazione in avanti, che considera il problema come un problema di ricerca. I cinque elementi che lo caratterizzano sono: stato iniziale la configurazione iniziale del mondo funzione azioni le azioni applicabili in uno stato s sono tutte le istanze degli schemi di azione applicabili a partire dallo stato s. Un’azione è applicabile a uno stato quando tutte le precondizioni sono presenti nella descrizione dello stato funzione risultato il risultato dell’applicazione di un’azione a a uno stato s è uno stato descritto dallo stesso insieme di formule di s, a cui si tolgono gli effetti negativi di a e si aggiungono gli effetti positivi test obiettivo verifica se nell’insieme di formule che descrivono uno stato sono presenti le formule che descrivono il goal costo di passo può essere definito in vari modi, con un costo unitario oppure tenendo conto di vari aspetti rilevanti per il dominio applicativo (ad esempio, sollevare un blocco costa di più che abbassarlo) Definito il problema in questo modo, si può impiegare una qualsiasi delle strategie di ricerca, informata o non informata, per risolvere il problema di pianificazione. Il problema di questo approccio è che i problemi reali sono di grandi dimensioni, e risultano intrattabili con questo approccio: in un dominio di dimensione realistica il branching factor è troppo elevato per essere gestito. 6.3 Pianificazione all’indietro L’approccio della pianificazione all’indietro sfrutta l’idea di partire dall’obiettivo, cercando di applicare gli schemi d’azione al contrario fino a risalire allo stato iniziale. Sono necessarie alcune definizioni preliminari: Definizione (Azione rilevante). Si dice che un’azione A è rilevante per un goal G se tra gli effetti positivi dell’azione A compare un letterale di G. Definizione (Azione consistente). Un’azione A si dice consistente per un goal G se tra i suoi effetti negativi non compare alcun letterale di G. Ad esempio, nel mondo dei blocchi, Stack(B,A) è rilevante e consistente per l’obiettivo G: ON(B, A), ON(C, B) mentre è rilevante, ma non consistente, per l’obiettivo 44 PIANIFICAZIONE G’: CLEAR(B), CLEAR(A) Definizione (Regressione). Si dice regressione di un goal g attraverso un’azione a (rilevante e consistente per g) un nuovo goal R[g, a] che si ottiene in questo modo: • si cancellano da g gli effetti positivi di a • si aggiungono a g le precondizioni di a, se non sono già presenti Dato un obiettivo g e un certo numero di stati che lo soddisfano, effettuando la regressione di g si ottiene un nuovo goal R[g, a] che ha le seguenti proprietà: • dato uno stato s0 che soddisfa R[g, a], le precondizioni di a sono soddisfatte in s0 • applicando l’azione a a s0 , si ottiene un nuovo stato s che soddisfa g Il tutto può essere rappresentato da un diagramma commutativo: g soddisfa si applica a regressione R[g, a] s soddisfa s0 Anche nell’approccio della pianificazione all’indietro viene formulato un problema di ricerca, in questo modo: stato iniziale il goal iniziale funzione azioni le azioni applicabili allo stato s sono tutte le azioni rilevanti e consistenti per il goal contenuto in s funzione risultato il risultato dell’applicazione di un’azione a a uno stato s è la regressione del goal corrispondente a s attraverso l’azione a test obiettivo è verificato se il goal generato è soddisfatto dallo stato iniziale costo di passo anche in questo caso, può essere unitario oppure dipendente dall’applicazione Il vantaggio di questo approccio rispetto alla pianificazione in avanti è un ridotto branching factor. Inoltre, mentre la pianificazione in avanti ragiona passando da uno stato a un altro stato (spazio degli stati), la pianificazione all’indietro passa da un obiettivo a un altro (spazio dei goal): i due metodi esplorano spazi concettuali diversi 6.4 Pianificatori nello spazio dei piani Nella realtà si utilizza l’approccio della pianificazione nello spazio dei piani. Questo tipo di pianificatore cerca un piano (sequenza di azioni), tra tutti i possibili piani generabili. A partire da un piano vuoto, aggiunge un’azione per ogni livello dell’albero. Inoltre, solitamente si fa uso di tecniche gerarchiche: si definiscono delle macro-azioni e si cerca un piano basato su queste, poi si trova un piano per le azioni che corrispondono a una singola macro-azione. Nell’approccio che è stato considerato (agenti risolutori di problemi), si cerca di costruire offline un albero di ricerca, e quindi eseguire il piano trovato. Perché l’agente sia sicuro di raggiungere l’obiettivo, l’ambiente dev’essere statico e deterministico. Per tener conto di PIANIFICATORI NELLO SPAZIO DEI PIANI 45 un ambiente reale, che non è né statico né deterministico, una delle tecniche utilizzate è la pianificazione (replanning): dopo avere eseguito ogni azione del piano, l’agente percepisce l’ambiente per controllare se l’azione è andata a buon fine. Se non è andata a buon fine, viene eseguita una ripianificazione. Bibliografia [1] Francesco Amigoni. Sito del corso di intelligenza artificiale. http://home.deib.polimi. it/amigoni/IntelligenzaArtificiale.html. [2] S. Russell and P. Norvig. Artificial intelligence: A modern approach (sito di riferimento). http://aima.cs.berkeley.edu. [3] S. Russell and P. Norvig. Artificial Intelligence: A Modern Approach. Pearson – Prentice Hall, third edition, 2010. [4] S. Russell and P. Norvig. Intelligenza Artificiale: Un approccio moderno, volume 1. Pearson Italia, third edition, 2010.