Salta al contenuto
Note per Studenti Logica del primo ordine - unificazione, forward e backward chaining

Logica del primo ordine - unificazione, forward e backward chaining

In questa pagina 8

Argomento del corso da 9 CFU. Fonte: slide del corso di Intelligenza Artificiale, Ing. dell'Informazione UniPD. Presuppone Agenti logici e logica proposizionale - inferenza e risoluzioneAgenti basati sulla conoscenza (KB, Tell e Ask), mondo del Wumpus come banco di prova, sintassi e semantica della logica proposizionale, conseguenza logica, equivalenze, inferenza per model checking (7 simboli, 128 modelli, 3 modelli della KB) e per risoluzione (CNF, regola di risoluzione, dimostrazione per assurdo). Limite: poca espressività, da cui la logica del primo ordine.Agenti logici e logica proposizionale - inferenza e risoluzione → (CNF, risoluzione, conseguenza logica).

Dalla logica proposizionale al primo ordine

La logica proposizionale assume che il mondo contenga solo fatti. La logica del primo ordine (FOL, first-order logic) assume, come il linguaggio naturale, che contenga:

  • oggetti: persone, case, numeri, teorie, colori, guerre, secoli;
  • relazioni o proprietà: fratello di, più grande di, dentro, possiede, sta tra; rosso, rotondo, primo;
  • funzioni (restituiscono un valore preciso): padre di, migliore amico di, uno in più di.

Elementi della sintassi:

Elemento Esempi
costanti (oggetti) KingJohnKingJohn, 22
funzioni SqrtSqrt, LeftLegOfLeftLegOf
variabili x,y,a,bx, y, a, b
predicati (relazioni) BrotherBrother, >>
connettivi ∧ ∨ ¬ ⇒ ⇔\land\ \lor\ \neg\ \Rightarrow\ \Leftrightarrow
uguaglianza ==
quantificatori ∀ ∃\forall\ \exists
  • Termine: una costante, una variabile oppure Funzione(Termine1,…,Terminen)Funzione(Termine_1, \dots, Termine_n). Indica un oggetto.
  • Enunciato atomico: Predicato(Termine1,…,Terminen)Predicato(Termine_1, \dots, Termine_n). Esprime un fatto che può essere vero o falso: Brother(KingJohn,RichardTheLionheart)Brother(KingJohn, RichardTheLionheart).
  • Enunciato complesso: enunciati atomici combinati con i connettivi, per esempio Sibling(KingJohn,Richard)⇔Sibling(Richard,KingJohn)Sibling(KingJohn, Richard) \Leftrightarrow Sibling(Richard, KingJohn).

Quantificatori

∀x P\forall x\ P è vero se PP è vero per ogni oggetto xx: è circa la congiunzione di tutte le istanze. ∃x P\exists x\ P è vero se PP è vero per almeno un oggetto: è circa la disgiunzione delle istanze.

Errore tipico con ∀\forall. Il connettivo principale con ∀\forall è di norma ⇒\Rightarrow. "Tutti a Oxford sono intelligenti" è ∀x At(x,Oxford)⇒Smart(x)\forall x\ At(x, Oxford) \Rightarrow Smart(x). Con ∧\land, ∀x At(x,Oxford)∧Smart(x)\forall x\ At(x, Oxford) \land Smart(x) significa "tutti sono a Oxford e tutti sono intelligenti".

Errore tipico con ∃\exists. Il connettivo principale con ∃\exists è di norma ∧\land. "Qualcuno non a Oxford è intelligente" è ∃x ¬At(x,Oxford)∧Smart(x)\exists x\ \neg At(x, Oxford) \land Smart(x). Con ⇒\Rightarrow, ∃x ¬At(x,Oxford)⇒Smart(x)\exists x\ \neg At(x, Oxford) \Rightarrow Smart(x) è vero anche se esiste una persona a Oxford (antecedente falso, implicazione vera a vuoto) o intelligente: dice molto meno.

Proprietà.

  • ∀x∀y\forall x \forall y equivale a ∀y∀x\forall y \forall x (si scrive ∀x,y\forall x,y); lo stesso per ∃\exists.
  • ∃x∀y\exists x \forall y non equivale a ∀y∃x\forall y \exists x: ∃x∀y Loves(x,y)\exists x \forall y\ Loves(x,y) è "c'è una persona che ama tutti", ∀y∃x Loves(x,y)\forall y \exists x\ Loves(x,y) è "ognuno è amato da qualcuno".
  • Dualità: ∀x Likes(x,IceCream)≡¬∃x ¬Likes(x,IceCream)\forall x\ Likes(x, IceCream) \equiv \neg \exists x\ \neg Likes(x, IceCream); ∃x Likes(x,Broccoli)≡¬∀x ¬Likes(x,Broccoli)\exists x\ Likes(x, Broccoli) \equiv \neg \forall x\ \neg Likes(x, Broccoli).

Esempi di frasi per una KB:

  • fratelli e sorelle sono fratelli: ∀x,y Brother(x,y)⇒Sibling(x,y)\forall x,y\ Brother(x,y) \Rightarrow Sibling(x,y);
  • "Sibling" è simmetrica: ∀x,y Sibling(x,y)⇔Sibling(y,x)\forall x,y\ Sibling(x,y) \Leftrightarrow Sibling(y,x);
  • la madre è il genitore femmina: ∀x,y Mother(x,y)⇔Parent(x,y)∧Female(x)\forall x,y\ Mother(x,y) \Leftrightarrow Parent(x,y) \land Female(x);
  • un cugino di primo grado è figlio di un fratello di un genitore: ∀x,y FirstCousin(x,y)⇔∃p,s Parent(p,x)∧Parent(s,y)∧Sibling(s,p)\forall x,y\ FirstCousin(x,y) \Leftrightarrow \exists p,s\ Parent(p,x) \land Parent(s,y) \land Sibling(s,p).

Sostituzioni

Un agente del Wumpus con una KB in FOL percepisce puzza, brezza e luccichio al tempo t=5t=5: Tell(KB, Percept([Stench, Breeze, Glitter], 5)). Poi chiede Ask(KB, ∃a Action(a, 5)): la KB implica qualche azione? Sì, per esempio con la sostituzione {a/Grab}\{a/Grab\}.

Definizione (sostituzione). Una sostituzione θ={v1/t1,…,vn/tn}\theta = \{v_1/t_1, \dots, v_n/t_n\} assegna un termine a ciascuna variabile. Dato un enunciato SS, SθS\theta è il risultato di applicare θ\theta a SS.

Esempio. S=Smarter(x,y)S = Smarter(x,y) e θ={x/Hillary,y/Bill}\theta = \{x/Hillary, y/Bill\} danno Sθ=Smarter(Hillary,Bill)S\theta = Smarter(Hillary, Bill). Ask(KB, S) restituisce alcune o tutte le θ\theta tali che KB⊨SθKB \models S\theta.

Proposizionalizzazione

Con le sostituzioni si può ricondurre l'inferenza FOL a quella proposizionale.

Istanziazione universale (UI). Da ∀x α\forall x\ \alpha si ricava α\alpha con xx sostituita da un termine chiuso gg (senza variabili): ∀x King(x)∧Greedy(x)⇒Evil(x)\forall x\ King(x) \land Greedy(x) \Rightarrow Evil(x) dà King(John)∧Greedy(John)⇒Evil(John)King(John) \land Greedy(John) \Rightarrow Evil(John), ma anche King(Father(John))∧…King(Father(John)) \land \dots e così via. Si può applicare più volte: la nuova KB è equivalente alla vecchia.

Istanziazione esistenziale (EI). Da ∃x α\exists x\ \alpha si ricava α\alpha con xx sostituita da una costante di Skolem kk che non compare altrove nella KB: ∃x Crown(x)∧OnHead(x,John)\exists x\ Crown(x) \land OnHead(x, John) dà Crown(C1)∧OnHead(C1,John)Crown(C_1) \land OnHead(C_1, John), con C1C_1 nome inventato. Si applica una volta per eliminare l'enunciato esistenziale; la nuova KB non è equivalente alla vecchia, ma è soddisfacibile se e solo se lo era la vecchia.

Se l'esistenziale sta dentro un ∀\forall, la costante diventa una funzione di Skolem delle variabili universali: ∀x ∃y Soulmate(y,x)\forall x\ \exists y\ Soulmate(y,x) dà ∀x Soulmate(F(x),x)\forall x\ Soulmate(F(x), x), che è diverso da ∀x Soulmate(C2,x)\forall x\ Soulmate(C_2, x) (la seconda dice che c'è un'unica anima gemella per tutti).

Esempio. KB: ∀x King(x)∧Greedy(x)⇒Evil(x)\forall x\ King(x) \land Greedy(x) \Rightarrow Evil(x), King(John)King(John), Greedy(John)Greedy(John), con soli tre oggetti John,Richard,HenryJohn, Richard, Henry. Istanziando la prima regola in tutti i modi si ottengono 33 enunciati (King(John)∧Greedy(John)⇒Evil(John)King(John) \land Greedy(John) \Rightarrow Evil(John) e gli analoghi per Richard e Henry) più i 22 fatti: i simboli proposizionali sono King(John)King(John), Greedy(John)Greedy(John), Evil(John)Evil(John), King(Richard)King(Richard), ecc. Si risolve come nella logica proposizionale.

Problemi.

  • Genera moltissimi enunciati irrilevanti: con ∀y Greedy(y)\forall y\ Greedy(y) si producono Greedy(Richard)Greedy(Richard), Greedy(Henry)Greedy(Henry) anche solo per provare Evil(John)Evil(John).
  • Con i simboli di funzione gli oggetti diventano infiniti: Father(John)Father(John), Father(Father(John))Father(Father(John)), Father(Father(Father(John)))Father(Father(Father(John))), ...

La soluzione è cercare direttamente la sostituzione che serve: l'unificazione.

Unificazione

Nell'esempio, per ricavare Evil(John)Evil(John) basta trovare θ\theta che faccia combaciare King(x)King(x) e Greedy(x)Greedy(x) con King(John)King(John) e Greedy(y)Greedy(y): θ={x/John,y/John}\theta = \{x/John, y/John\}.

Definizione (unificazione). Unify(α,β)=θUnify(\alpha, \beta) = \theta con Subst(θ,α)=Subst(θ,β)\mathrm{Subst}(\theta, \alpha) = \mathrm{Subst}(\theta, \beta). Se non esiste θ\theta l'unificazione fallisce.

α\alpha β\beta θ\theta
Knows(John,x)Knows(John, x) Knows(John,Jane)Knows(John, Jane) {x/Jane}\{x/Jane\}
Knows(John,x)Knows(John, x) Knows(y,Mary)Knows(y, Mary) {x/Mary, y/John}\{x/Mary,\ y/John\}
Knows(John,x)Knows(John, x) Knows(y,Mother(y))Knows(y, Mother(y)) {y/John, x/Mother(John)}\{y/John,\ x/Mother(John)\}
Knows(John,x)Knows(John, x) Knows(x,Mary)Knows(x, Mary) fallisce (variabile xx in comune)

L'ultimo fallimento è fittizio: le due xx appartengono a enunciati diversi, ma hanno lo stesso nome. Si risolve standardizzando (standardize apart) cioè rinominando le variabili di uno dei due: con Knows(z,Mary)Knows(z, Mary) si ha {x/Mary, z/John}\{x/Mary,\ z/John\}. Nei calcoli si rinominano sempre le variabili di una clausola prima di unificarla con un'altra.

Come si procede, regola per regola, confrontando i due termini da sinistra a destra:

  1. due termini identici: si prosegue senza legare nulla;
  2. una variabile e un termine tt: si lega v/tv/t, solo se vv non compare in tt (occur check);
  3. due termini composti: servono lo stesso simbolo di funzione o predicato e lo stesso numero di argomenti, e poi si unificano gli argomenti a coppie, applicando le sostituzioni già trovate;
  4. in ogni altro caso (due costanti diverse, costante contro composto, simboli diversi) si fallisce.

Occur check. xx e Father(x)Father(x) non si unificano: x=Father(x)x = Father(x) richiederebbe un termine infinito Father(Father(Father(… )))Father(Father(Father(\dots))). Senza il controllo l'algoritmo non terminerebbe o sarebbe scorretto. Prolog di solito non lo fa (vedi Prolog - fatti, regole, ricorsione, liste e cutProlog come logica del primo ordine ristretta alle clausole di Horn: fatti, regole, query, motore di inferenza (backward chaining in profondità con backtracking, ordine delle clausole), termini, unificazione e occur check, aritmetica con is, ricorsione, liste e operatore barra, length e append, cut verde e rosso, cut e fail, negazione come fallimento.Prolog - fatti, regole, ricorsione, liste e cut →).

Il θ\theta restituito è il più generale (most general unifier): ogni altro unificatore se ne ottiene con un'ulteriore sostituzione. Per esempio Knows(John,x)Knows(John, x) e Knows(y,z)Knows(y, z) danno {y/John,x/z}\{y/John, x/z\}, non {y/John,x/Jane,z/Jane}\{y/John, x/Jane, z/Jane\}.

python
# variabili: stringhe minuscole; costanti: stringhe maiuscole; composti: tuple ("F", arg1, ...)
def is_var(t): return isinstance(t, str) and t[0].islower()

def walk(t, s):                       # segue i legami già fatti
    while is_var(t) and t in s: t = s[t]
    return t

def occurs(v, t, s):                  # v compare dentro t?
    t = walk(t, s)
    return t == v or (isinstance(t, tuple) and any(occurs(v, a, s) for a in t[1:]))

def unify(a, b, s=None):              # restituisce la sostituzione, o None se fallisce
    s = {} if s is None else s
    a, b = walk(a, s), walk(b, s)
    if a == b: return s
    if is_var(a) or is_var(b):
        v, t = (a, b) if is_var(a) else (b, a)
        return None if occurs(v, t, s) else {**s, v: t}   # occur check
    if isinstance(a, tuple) and isinstance(b, tuple) and a[0] == b[0] and len(a) == len(b):
        for x, y in zip(a[1:], b[1:]):
            s = unify(x, y, s)
            if s is None: return None
        return s
    return None

def subst(t, s):                      # applica la sostituzione fino in fondo
    t = walk(t, s)
    return (t[0],) + tuple(subst(a, s) for a in t[1:]) if isinstance(t, tuple) else t

a, b = ("Knows", "John", "x"), ("Knows", "y", ("Mother", "y"))
s = unify(a, b)
print({v: subst(t, s) for v, t in s.items()})                  # {'y': 'John', 'x': ('Mother', 'John')}
print(unify(("Knows", "John", "x"), ("Knows", "x", "Mary")))   # None
print(unify("x", ("Father", "x")))                             # None (occur check)

Gli esempi di unificazione svolti sono in Esercizio - Unificazione e conversione in CNF nel primo ordine (slide del corso).

Risoluzione nel primo ordine

Come nel caso proposizionale: si converte KB∧¬αKB \land \neg\alpha in CNF e si applica la risoluzione; se si ottiene la clausola vuota, KB⊨αKB \models \alpha (dimostrazione per assurdo).

Regola di risoluzione in FOL. Date due clausole standardizzate (con variabili diverse), con θ=Unify(li,¬mj)\theta = Unify(l_i, \neg m_j): l1∨⋯∨li∨⋯∨lkm1∨⋯∨mj∨⋯∨mnSubst(θ, l1∨⋯∨li−1∨li+1∨⋯∨lk∨m1∨⋯∨mj−1∨mj+1∨⋯∨mn)\frac{l_1 \lor \dots \lor l_i \lor \dots \lor l_k \qquad m_1 \lor \dots \lor m_j \lor \dots \lor m_n}{\mathrm{Subst}\big(\theta,\ l_1 \lor \dots \lor l_{i-1} \lor l_{i+1} \lor \dots \lor l_k \lor m_1 \lor \dots \lor m_{j-1} \lor m_{j+1} \lor \dots \lor m_n\big)}

Esempio. Da ¬Rich(x)∨Unhappy(x)\neg Rich(x) \lor Unhappy(x) e Rich(Ken)Rich(Ken), con Unify(¬Rich(x),¬Rich(Ken))={x/Ken}Unify(\neg Rich(x), \neg Rich(Ken)) = \{x/Ken\} si ottiene Unhappy(Ken)Unhappy(Ken).

Il sistema è completo per refutazione: se la KB implica α\alpha, la clausola vuota si deriva. Ma il problema è solo semidecidibile: se α\alpha non è implicata, l'algoritmo può non terminare mai.

Conversione in CNF

I passi sono quelli proposizionali, più la gestione di variabili e quantificatori. Esempio: "chi ama tutti gli animali è amato da qualcuno", ∀x [∀y Animal(y)⇒Loves(x,y)]⇒[∃y Loves(y,x)].\forall x\ [\forall y\ Animal(y) \Rightarrow Loves(x,y)] \Rightarrow [\exists y\ Loves(y,x)].

  1. Eliminare ⇔\Leftrightarrow e ⇒\Rightarrow: ∀x ¬[∀y (¬Animal(y)∨Loves(x,y))]∨[∃y Loves(y,x)]\forall x\ \neg[\forall y\ (\neg Animal(y) \lor Loves(x,y))] \lor [\exists y\ Loves(y,x)].
  2. Portare ¬\neg all'interno, ricordando ¬∀x p≡∃x ¬p\neg\forall x\ p \equiv \exists x\ \neg p e ¬∃x p≡∀x ¬p\neg\exists x\ p \equiv \forall x\ \neg p: ∀x [∃y ¬(¬Animal(y)∨Loves(x,y))]∨[∃y Loves(y,x)]\forall x\ [\exists y\ \neg(\neg Animal(y) \lor Loves(x,y))] \lor [\exists y\ Loves(y,x)], poi con De Morgan e doppia negazione ∀x [∃y Animal(y)∧¬Loves(x,y)]∨[∃y Loves(y,x)]\forall x\ [\exists y\ Animal(y) \land \neg Loves(x,y)] \lor [\exists y\ Loves(y,x)]. Le due yy sono variabili diverse, che per caso hanno lo stesso nome.
  3. Standardizzare le variabili (un nome diverso per ogni quantificatore): ∀x [∃y Animal(y)∧¬Loves(x,y)]∨[∃z Loves(z,x)]\forall x\ [\exists y\ Animal(y) \land \neg Loves(x,y)] \lor [\exists z\ Loves(z,x)].
  4. Skolemizzare: ogni ∃\exists è dentro ∀x\forall x, quindi diventa una funzione di xx (y→F(x)y \to F(x), z→G(x)z \to G(x)): ∀x [Animal(F(x))∧¬Loves(x,F(x))]∨Loves(G(x),x)\forall x\ [Animal(F(x)) \land \neg Loves(x, F(x))] \lor Loves(G(x), x).
  5. Togliere i ∀\forall (restano impliciti): [Animal(F(x))∧¬Loves(x,F(x))]∨Loves(G(x),x)[Animal(F(x)) \land \neg Loves(x,F(x))] \lor Loves(G(x),x).
  6. Distribuire ∨\lor su ∧\land: [Animal(F(x))∨Loves(G(x),x)]∧[¬Loves(x,F(x))∨Loves(G(x),x)].[Animal(F(x)) \lor Loves(G(x),x)] \land [\neg Loves(x,F(x)) \lor Loves(G(x),x)].

Due clausole. (Verifica: su tutte le 40964096 interpretazioni di AnimalAnimal e LovesLoves su un dominio di 33 oggetti, l'enunciato originale è vero se e solo se esistono due funzioni F,GF, G che rendono vera la CNF: è la proprietà che la Skolemizzazione garantisce.)

Esempio: Colonnello West

La legge dice che è un reato per un americano vendere armi a nazioni ostili. Il paese Nono, nemico dell'America, ha dei missili, e tutti i suoi missili gli sono stati venduti dal colonnello West, che è americano. Dimostrare che West è un criminale.

Enunciati in logica, con le convenzioni che i nemici dell'America sono ostili e i missili sono armi:

  1. American(x)∧Weapon(y)∧Sells(x,y,z)∧Hostile(z)⇒Criminal(x)American(x) \land Weapon(y) \land Sells(x,y,z) \land Hostile(z) \Rightarrow Criminal(x)
  2. Enemy(Nono,America)Enemy(Nono, America)
  3. Enemy(x,America)⇒Hostile(x)Enemy(x, America) \Rightarrow Hostile(x)
  4. ∃x Owns(Nono,x)∧Missile(x)\exists x\ Owns(Nono, x) \land Missile(x): con EI, Owns(Nono,M1)∧Missile(M1)Owns(Nono, M_1) \land Missile(M_1) (M1M_1 costante di Skolem)
  5. Missile(x)⇒Weapon(x)Missile(x) \Rightarrow Weapon(x)
  6. Missile(x)∧Owns(Nono,x)⇒Sells(West,x,Nono)Missile(x) \land Owns(Nono, x) \Rightarrow Sells(West, x, Nono)
  7. American(West)American(West)

Dimostrazione per risoluzione di KB⊨Criminal(West)KB \models Criminal(West). Clausole della KB (variabili già distinte per ogni clausola):

  • K1K_1: ¬American(x)∨¬Weapon(y)∨¬Sells(x,y,z)∨¬Hostile(z)∨Criminal(x)\neg American(x) \lor \neg Weapon(y) \lor \neg Sells(x,y,z) \lor \neg Hostile(z) \lor Criminal(x)
  • K2K_2: American(West)American(West)
  • K3K_3: ¬Missile(x)∨Weapon(x)\neg Missile(x) \lor Weapon(x)
  • K4K_4: Missile(M1)Missile(M_1)
  • K5K_5: ¬Missile(x)∨¬Owns(Nono,x)∨Sells(West,x,Nono)\neg Missile(x) \lor \neg Owns(Nono,x) \lor Sells(West,x,Nono)
  • K6K_6: Owns(Nono,M1)Owns(Nono, M_1)
  • K7K_7: ¬Enemy(x,America)∨Hostile(x)\neg Enemy(x, America) \lor Hostile(x)
  • K8K_8: Enemy(Nono,America)Enemy(Nono, America)
  • K9K_9: ¬Criminal(West)\neg Criminal(West) (negazione della tesi)
Passo Clausole θ\theta Nuova clausola
1 K9K_9, K1K_1 {x/West}\{x/West\} ¬American(West)∨¬Weapon(y)∨¬Sells(West,y,z)∨¬Hostile(z)\neg American(West) \lor \neg Weapon(y) \lor \neg Sells(West,y,z) \lor \neg Hostile(z)
2 passo 1, K2K_2 {}\{\} ¬Weapon(y)∨¬Sells(West,y,z)∨¬Hostile(z)\neg Weapon(y) \lor \neg Sells(West,y,z) \lor \neg Hostile(z)
3 passo 2, K3K_3 {x/y}\{x/y\} ¬Missile(y)∨¬Sells(West,y,z)∨¬Hostile(z)\neg Missile(y) \lor \neg Sells(West,y,z) \lor \neg Hostile(z)
4 passo 3, K4K_4 {y/M1}\{y/M_1\} ¬Sells(West,M1,z)∨¬Hostile(z)\neg Sells(West,M_1,z) \lor \neg Hostile(z)
5 passo 4, K5K_5 {x/M1, z/Nono}\{x/M_1,\ z/Nono\} ¬Missile(M1)∨¬Owns(Nono,M1)∨¬Hostile(Nono)\neg Missile(M_1) \lor \neg Owns(Nono,M_1) \lor \neg Hostile(Nono)
6 passo 5, K4K_4 {}\{\} ¬Owns(Nono,M1)∨¬Hostile(Nono)\neg Owns(Nono,M_1) \lor \neg Hostile(Nono)
7 passo 6, K6K_6 {}\{\} ¬Hostile(Nono)\neg Hostile(Nono)
8 passo 7, K7K_7 {x/Nono}\{x/Nono\} ¬Enemy(Nono,America)\neg Enemy(Nono, America)
9 passo 8, K8K_8 {}\{\} □\square

Clausola vuota: KB∧¬Criminal(West)KB \land \neg Criminal(West) è insoddisfacibile, quindi Criminal(West)Criminal(West) sì. (I passi sono stati rieseguiti con un piccolo programma Python che applica l'unificazione a ogni passo.) La versione svolta come esercizio è Esercizio - Risoluzione, forward e backward chaining sul caso Criminal West (slide del corso).

Clausole definite, forward e backward chaining

La risoluzione completa può essere lenta. Se la KB ha solo clausole definite (definite clauses: disgiunzione con esattamente un letterale positivo, cioè implicazioni p1∧⋯∧pn⇒qp_1 \land \dots \land p_n \Rightarrow q e fatti) si usano due algoritmi più semplici. Il caso dell'esempio vale: tutti gli enunciati 1-7 sono definiti. I fatti (OwnsOwns, MissileMissile, EnemyEnemy, AmericanAmerican) sono clausole definite con corpo vuoto.

Backward chaining (concatenazione all'indietro)

Parte dalla domanda e la verifica ricorsivamente all'indietro: per dimostrare un obiettivo si cerca una clausola la cui conseguenza si unifica con esso, e si dimostrano le premesse con la sostituzione propagata. È una ricerca in profondità (vedi Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informataUn agente risolutore di problemi è un agente basato su obiettivi con rappresentazione atomica: formula un problema (stato iniziale, azioni, modello di transizione, test dell'obiettivo, costo dei passi) e cerca una sequenza di azioni che porta a uno stato obiettivo costruendo un albero di ricerca. Le strategie non informate usano solo la definizione del problema e si distinguono per l'ordine di espansione della frontiera: ampiezza (coda FIFO), costo uniforme (coda con priorità sul costo g), profondità (pila LIFO), profondità limitata, approfondimento iterativo e ricerca bidirezionale. Si confrontano per completezza, ottimalità, tempo e spazio in funzione di b, d, m; tutte sono casi di best-first search con una diversa funzione di valutazione f.Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informata →): non ottima ma molto efficiente. Si applica solo a KB di fatti e implicazioni. È ciò che fa Prolog.

Traccia su Criminal(West)Criminal(West) (ogni riga: obiettivo, clausola usata, sostituzioni nuove; le variabili delle clausole sono rinominate a ogni uso):

Criminal(West)            <- regola 1, {x/West}
  American(West)          <- fatto, {}
  Weapon(y)               <- regola 5, {y/x'}
    Missile(x')           <- fatto Missile(M1), {x'/M1}      quindi y = M1
  Sells(West, M1, z)      <- regola 6, {z/Nono, x''/M1}
    Missile(M1)           <- fatto, {}
    Owns(Nono, M1)        <- fatto, {}
  Hostile(Nono)           <- regola 3, {x'''/Nono}
    Enemy(Nono, America)  <- fatto, {}

Tutte le foglie sono fatti: Criminal(West)Criminal(West) è dimostrato con {x/West,y/M1,z/Nono}\{x/West, y/M_1, z/Nono\}. Un solo ramo di ricerca, nessun tentativo inutile: nella risoluzione servono tutti i passi sopra e in più si potrebbero generare clausole irrilevanti.

Forward chaining (concatenazione in avanti)

Parte dai fatti e applica ripetutamente le regole le cui premesse sono già tutte note, aggiungendo le conseguenze, finché non si ricava la domanda o non c'è più nulla di nuovo (punto fisso). Su questa KB:

  • Giro 1 (fatti: American(West)American(West), Missile(M1)Missile(M_1), Owns(Nono,M1)Owns(Nono,M_1), Enemy(Nono,America)Enemy(Nono,America)): Missile(x)⇒Weapon(x)Missile(x) \Rightarrow Weapon(x) con {x/M1}\{x/M_1\} dà Weapon(M1)Weapon(M_1); Missile(x)∧Owns(Nono,x)⇒Sells(West,x,Nono)Missile(x) \land Owns(Nono,x) \Rightarrow Sells(West,x,Nono) con {x/M1}\{x/M_1\} dà Sells(West,M1,Nono)Sells(West,M_1,Nono); Enemy(x,America)⇒Hostile(x)Enemy(x,America) \Rightarrow Hostile(x) con {x/Nono}\{x/Nono\} dà Hostile(Nono)Hostile(Nono).
  • Giro 2: la regola 1 ha ora tutte le premesse, con {x/West,y/M1,z/Nono}\{x/West, y/M_1, z/Nono\}: nasce Criminal(West)Criminal(West).
  • Giro 3: nessuna conseguenza nuova, punto fisso.

(Giri e fatti controllati con un programma Python.) Il forward chaining è corretto e, per clausole definite senza simboli di funzione, termina sempre; ricava però tutte le conseguenze, anche le irrilevanti per la domanda. Il backward chaining è invece guidato dall'obiettivo.

Forward Backward
Parte da fatti noti domanda
Direzione dalle premesse alle conseguenze dalla conseguenza alle premesse
Adatto a produrre nuovi fatti quando arrivano dati rispondere a una domanda precisa
Rischio deduce molto di irrilevante può non terminare (cicli, ricorsione a sinistra)

Ingegneria della conoscenza

L'ingegneria della conoscenza (knowledge engineering) è il processo di costruzione di una KB. I passi:

  1. identificare le domande (analogo al PEAS dell'agente);
  2. raccogliere la conoscenza rilevante;
  3. scegliere un vocabolario di predicati, funzioni e costanti (ontologia);
  4. codificare la conoscenza generale del dominio;
  5. codificare la descrizione dell'istanza di problema;
  6. porre domande al motore di inferenza e leggere le risposte;
  7. correggere e valutare la KB.

Esempio: circuito sommatore. Domande: il circuito somma correttamente? Se tutti gli ingressi sono alti, qual è l'uscita della porta A2A_2? Quali porte sono collegate al primo terminale di ingresso? Ci sono anelli di retroazione? Conoscenza: i circuiti sono fatti di fili e porte; i segnali vanno lungo i fili ai terminali di ingresso delle porte; ogni porta produce un segnale sul terminale di uscita; i tipi di porta sono AND, OR, XOR (due ingressi) e NOT (uno).

Vocabolario: ogni porta è un oggetto con nome costante, per esempio Gate(X1)Gate(X_1), Circuit(C1)Circuit(C_1), Terminal(T1)Terminal(T_1); il tipo con una funzione, Type(X1)=XORType(X_1) = XOR. Regola generale: ∀t1,t2 Terminal(t1)∧Terminal(t2)∧Connected(t1,t2)⇒Signal(t1)=Signal(t2)\forall t_1,t_2\ Terminal(t_1) \land Terminal(t_2) \land Connected(t_1,t_2) \Rightarrow Signal(t_1) = Signal(t_2). Istanza: Connected(Out(1,X1),In(1,X2))Connected(Out(1,X_1), In(1,X_2)) ("l'uscita 11 di X1X_1 è collegata all'ingresso 11 di X2X_2"). Domanda: per quali ingressi di C1C_1 l'uscita di A1A_1 vale 00?, cioè ∃i1,i2,i3 Signal(In(1,C1))=i1∧Signal(In(2,C1))=i2∧Signal(In(3,C1))=i3∧Signal(Out(1,A1))=0\exists i_1,i_2,i_3\ Signal(In(1,C_1)) = i_1 \land Signal(In(2,C_1)) = i_2 \land Signal(In(3,C_1)) = i_3 \land Signal(Out(1,A_1)) = 0. Per il debug si perturba la KB togliendo o modificando regole e fatti per vedere quali comportamenti errati compaiono.

Domande d'esame

  1. Unificare Knows(John,x)Knows(John, x) e Knows(x,Mary)Knows(x, Mary). Fallisce se si lasciano le due xx con lo stesso nome (si dovrebbe avere x=Johnx = John e x=Maryx = Mary). Dopo aver standardizzato la seconda come Knows(y,Mary)Knows(y, Mary) si ottiene {y/John,x/Mary}\{y/John, x/Mary\}.
  2. Tradurre "chi possiede un cane ama gli animali" e dare la CNF. ∀x (∃y Dog(y)∧Owns(x,y))⇒AnimalLover(x)\forall x\ (\exists y\ Dog(y) \land Owns(x,y)) \Rightarrow AnimalLover(x). Eliminando l'implicazione e portando ¬\neg dentro, l'∃\exists dell'antecedente diventa ∀\forall: ∀x,y ¬Dog(y)∨¬Owns(x,y)∨AnimalLover(x)\forall x,y\ \neg Dog(y) \lor \neg Owns(x,y) \lor AnimalLover(x). Una sola clausola, senza Skolem.
  3. Skolemizzare ∀y ∃x Loves(x,y)\forall y\ \exists x\ Loves(x,y) e ∃x ∀y Loves(x,y)\exists x\ \forall y\ Loves(x,y). Il primo dà Loves(F(y),y)Loves(F(y), y) (funzione di yy, perché l'amante dipende da chi è amato); il secondo ∀y Loves(C,y)\forall y\ Loves(C, y) con costante CC.
  4. Perché l'occur check? Senza, xx si unificherebbe con Father(x)Father(x) in un termine infinito, e l'algoritmo non terminerebbe o dedurrebbe cose sbagliate. Con il controllo l'unificazione di xx con Father(x)Father(x) fallisce.

Versione ripasso

  • FOL: oggetti, relazioni, funzioni. Sintassi: costanti, funzioni, variabili, predicati, connettivi, ==, ∀\forall, ∃\exists. Termine = oggetto; enunciato atomico P(t1,…,tn)P(t_1,\dots,t_n) = fatto. Base: Agenti logici e logica proposizionale - inferenza e risoluzioneAgenti basati sulla conoscenza (KB, Tell e Ask), mondo del Wumpus come banco di prova, sintassi e semantica della logica proposizionale, conseguenza logica, equivalenze, inferenza per model checking (7 simboli, 128 modelli, 3 modelli della KB) e per risoluzione (CNF, regola di risoluzione, dimostrazione per assurdo). Limite: poca espressività, da cui la logica del primo ordine.Agenti logici e logica proposizionale - inferenza e risoluzione →.
  • Quantificatori: con ∀\forall il connettivo principale è ⇒\Rightarrow (∀x At(x,Oxford)⇒Smart(x)\forall x\ At(x,Oxford) \Rightarrow Smart(x)); con ∃\exists è ∧\land. Errori: ∀\forall con ∧\land ("tutti sono a Oxford e tutti intelligenti"); ∃\exists con ⇒\Rightarrow (vero a vuoto). ∃x∀y\exists x \forall y diverso da ∀y∃x\forall y \exists x. Dualità: ∀x P≡¬∃x ¬P\forall x\ P \equiv \neg\exists x\ \neg P.
  • Sostituzione θ={x/t,… }\theta = \{x/t, \dots\}; SθS\theta = enunciato dopo la sostituzione; Ask restituisce le θ\theta con KB⊨SθKB \models S\theta.
  • UI: da ∀x α\forall x\ \alpha si ottiene α{x/g}\alpha\{x/g\} con gg termine chiuso (KB equivalente). EI: ∃x α\exists x\ \alpha dà α{x/k}\alpha\{x/k\} con kk costante di Skolem nuova (soddisfacibile sse lo era la KB); dentro un ∀x\forall x serve una funzione di Skolem F(x)F(x).
  • Proposizionalizzazione: istanzia tutto e usa la logica proposizionale; genera molti enunciati irrilevanti e con funzioni infiniti termini (Father(Father(John))Father(Father(John)), ...).
  • Unificazione: Unify(α,β)=θUnify(\alpha,\beta)=\theta con αθ=βθ\alpha\theta = \beta\theta. Si confronta da sinistra: termini uguali, variabile con termine (v/tv/t solo se vv non compare in tt), composti con stesso funtore e arità e argomenti unificabili in cascata. Knows(John,x)Knows(John,x) con Knows(y,Mother(y))Knows(y,Mother(y)) dà {y/John,x/Mother(John)}\{y/John, x/Mother(John)\}. Con Knows(x,Mary)Knows(x,Mary) fallisce finché non si standardizza (rinominare le variabili): {x/Mary,z/John}\{x/Mary, z/John\}. Si cerca l'unificatore più generale.
  • Occur check: xx e Father(x)Father(x) non si unificano (termine infinito). Prolog di solito non lo fa (Prolog - fatti, regole, ricorsione, liste e cutProlog come logica del primo ordine ristretta alle clausole di Horn: fatti, regole, query, motore di inferenza (backward chaining in profondità con backtracking, ordine delle clausole), termini, unificazione e occur check, aritmetica con is, ricorsione, liste e operatore barra, length e append, cut verde e rosso, cut e fail, negazione come fallimento.Prolog - fatti, regole, ricorsione, liste e cut →).
  • Risoluzione FOL: clausole standardizzate, θ=Unify(li,¬mj)\theta = Unify(l_i, \neg m_j), risolvente con θ\theta applicata. Esempio: ¬Rich(x)∨Unhappy(x)\neg Rich(x) \lor Unhappy(x) con Rich(Ken)Rich(Ken), {x/Ken}\{x/Ken\} dà Unhappy(Ken)Unhappy(Ken). Completa per refutazione, ma semidecidibile.
  • CNF in FOL: 1 eliminare ⇔\Leftrightarrow, ⇒\Rightarrow; 2 portare ¬\neg dentro (¬∀≡∃¬\neg\forall \equiv \exists\neg, ¬∃≡∀¬\neg\exists \equiv \forall\neg); 3 standardizzare; 4 Skolemizzare; 5 togliere i ∀\forall; 6 distribuire ∨\lor su ∧\land. Esempio animali: [Animal(F(x))∨Loves(G(x),x)]∧[¬Loves(x,F(x))∨Loves(G(x),x)][Animal(F(x)) \lor Loves(G(x),x)] \land [\neg Loves(x,F(x)) \lor Loves(G(x),x)].
  • Criminal(West): K9=¬Criminal(West)K_9 = \neg Criminal(West); K1K_1 con {x/West}\{x/West\}, poi K2K_2, K3K_3 {x/y}\{x/y\}, K4K_4 {y/M1}\{y/M_1\}, K5K_5 {x/M1,z/Nono}\{x/M_1, z/Nono\}, K4K_4, K6K_6, K7K_7 {x/Nono}\{x/Nono\}, K8K_8: clausola vuota in 99 passi. Esercizio: Esercizio - Risoluzione, forward e backward chaining sul caso Criminal West (slide del corso).
  • Clausole definite = un solo letterale positivo (implicazioni e fatti). Backward chaining: dall'obiettivo alle premesse, in profondità, sostituzioni propagate, usato da Prolog; trova {x/West,y/M1,z/Nono}\{x/West, y/M_1, z/Nono\}. Forward chaining: dai fatti a un punto fisso; qui giro 1 dà Weapon(M1)Weapon(M_1), Sells(West,M1,Nono)Sells(West,M_1,Nono), Hostile(Nono)Hostile(Nono), giro 2 dà Criminal(West)Criminal(West), giro 3 niente.
  • Esempio di proposizionalizzazione: ∀x King(x)∧Greedy(x)⇒Evil(x)\forall x\ King(x) \land Greedy(x) \Rightarrow Evil(x), King(John)King(John), Greedy(John)Greedy(John) con oggetti John,Richard,HenryJohn, Richard, Henry: 33 istanze della regola più 22 fatti; i simboli sono King(John)King(John), Greedy(John)Greedy(John), Evil(John)Evil(John), ecc. Unificando invece basta θ={x/John,y/John}\theta = \{x/John, y/John\} (con ∀y Greedy(y)\forall y\ Greedy(y)).
  • Clausole della KB Criminal: K1:¬American(x)∨¬Weapon(y)∨¬Sells(x,y,z)∨¬Hostile(z)∨Criminal(x)K_1: \neg American(x) \lor \neg Weapon(y) \lor \neg Sells(x,y,z) \lor \neg Hostile(z) \lor Criminal(x); K2:American(West)K_2: American(West); K3:¬Missile(x)∨Weapon(x)K_3: \neg Missile(x) \lor Weapon(x); K4:Missile(M1)K_4: Missile(M_1); K5:¬Missile(x)∨¬Owns(Nono,x)∨Sells(West,x,Nono)K_5: \neg Missile(x) \lor \neg Owns(Nono,x) \lor Sells(West,x,Nono); K6:Owns(Nono,M1)K_6: Owns(Nono,M_1); K7:¬Enemy(x,America)∨Hostile(x)K_7: \neg Enemy(x,America) \lor Hostile(x); K8:Enemy(Nono,America)K_8: Enemy(Nono,America).
  • Passi dell'unificatore: si legano x/Westx/West (K9K_9 con K1K_1), x/yx/y (K3K_3), y/M1y/M_1 (K4K_4), x/M1x/M_1 e z/Nonoz/Nono (K5K_5), x/Nonox/Nono (K7K_7); le altre risoluzioni hanno θ={}\theta = \{\}.
  • Forward vs backward: forward parte dai fatti e arriva a un punto fisso, produce anche fatti irrilevanti; backward parte dalla domanda, in profondità, può non terminare (cicli, ricorsione a sinistra); entrambi su clausole definite, il secondo è quello di Prolog.
  • Domande tipiche: Knows(John,x)Knows(John,x) con Knows(x,Mary)Knows(x,Mary) fallisce finché non si standardizza; "chi possiede un cane ama gli animali" dà ¬Dog(y)∨¬Owns(x,y)∨AnimalLover(x)\neg Dog(y) \lor \neg Owns(x,y) \lor AnimalLover(x) (l'∃\exists nell'antecedente diventa ∀\forall); ∀y ∃x Loves(x,y)\forall y\ \exists x\ Loves(x,y) dà Loves(F(y),y)Loves(F(y),y), mentre ∃x ∀y Loves(x,y)\exists x\ \forall y\ Loves(x,y) dà Loves(C,y)Loves(C,y).
  • Altre unificazioni verificate: P(x,f(y))P(x,f(y)) e P(g(z),z)P(g(z),z) danno {z/f(y),x/g(f(y))}\{z/f(y), x/g(f(y))\}; Q(x,y)Q(x,y) e Q(y,A)Q(y,A) danno {y/A,x/A}\{y/A, x/A\}; Q(x,x)Q(x,x) e Q(A,B)Q(A,B) falliscono; R(x,f(x))R(x,f(x)) e R(y,y)R(y,y) falliscono per occur check.
  • Ingegneria della conoscenza: domande, conoscenza, vocabolario (ontologia), conoscenza generale, istanza, query, debug. Esempio: circuito sommatore con Type(X1)=XORType(X_1)=XOR e Connected(Out(1,X1),In(1,X2))Connected(Out(1,X_1), In(1,X_2)).
  • Errori tipici: non standardizzare; legare v/tv/t senza occur check; dimenticare di applicare le sostituzioni già trovate; Skolem con costante invece di funzione dentro un ∀\forall.

Esercizi su questo argomento

Teoria collegata