Notas

Anuncio
Alejandro Guerra-Hernández
Metodologı́as de Programación I
Programación Lógica
5 de noviembre de 2009
Departamento de Inteligencia Artificial
Sebastián Camacho No. 5, Xalapa, Ver.,
México 91000
Índice general
1.
Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1. Breve historia de la programación lógica. . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Una breve introducción a Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1. Hechos y relaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2. Reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.3. Definición de reglas recursivas . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3. ¿Cómo computa Prolog una solución? . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4. Organización del curso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Parte I Formalismos
2.
Lógica de Primer Orden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2. Sistemas formales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3. El lenguaje de la lógica de primer orden . . . . . . . . . . . . . . . . . . . . . . . .
2.3.1. Sintaxis de la lógica de primer orden . . . . . . . . . . . . . . . . . . . .
2.4. La semántica de la lógica de primer orden . . . . . . . . . . . . . . . . . . . . . .
2.4.1. Teorı́a de modelo de la lógica de primer orden . . . . . . . . . . . .
2.5. Inferencia en la lógica de primer orden . . . . . . . . . . . . . . . . . . . . . . . . .
2.6. Substituciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
21
23
23
25
26
27
29
31
3.
Cláusulas y Programas Definitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1. Cláusulas definitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Programas definitivos y Metas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. El modelo mı́nimo de Herbrand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1. Resultados concernientes a los modelos de Herbrand . . . . . .
3.3.2. Construcción del modelo mı́nimo de Herbrand . . . . . . . . . . . .
33
33
35
37
39
41
4.
Principio de Resolución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2. ¿Qué es un procedimiento de prueba? . . . . . . . . . . . . . . . . . . . . . . . . . . 44
V
Índice general
VI
4.3.
4.4.
4.5.
4.6.
Pruebas y programas lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Substitución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Unificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Resolución-SLD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.6.1. Propiedades de la resolución-SLD . . . . . . . . . . . . . . . . . . . . . .
45
48
50
52
55
5.
Negación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2. La compleción de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3. Resolución SLDNF para programas definitivos . . . . . . . . . . . . . . . . . .
5.4. Programas Lógicos Generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.5. Resolución SLDNF para programas generales . . . . . . . . . . . . . . . . . . .
57
57
59
62
65
67
6.
Corte y Aritmética . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.1. Corte: podando el árbol-SLD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.2. Aritmética . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Parte II Prolog
7.
Introducción a Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.
Estrategias básicas de resolución de problemas . . . . . . . . . . . . . . . . . . . .
8.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2. Búsqueda primero en profundidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3. Búsqueda primero en amplitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4. Búsqueda primero el mejor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.
Sistemas Expertos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
9.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
9.2. Caracterı́sticas de los SE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
9.2.1. Razonamiento basado en metas . . . . . . . . . . . . . . . . . . . . . . . . 101
9.2.2. Incertidumbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
9.2.3. Razonamiento guiado por los datos . . . . . . . . . . . . . . . . . . . . . 102
9.3. Usando la máquina de inferencia de Prolog . . . . . . . . . . . . . . . . . . . . . 103
9.3.1. Reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
9.3.2. Reglas para relaciones jerárquicas . . . . . . . . . . . . . . . . . . . . . . 104
9.3.3. Reglas para otras relaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
9.4. Interfaz del usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
9.5. Un Shell simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
9.5.1. REPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
9.6. Encadenamiento hacı́a atrás con incertidumbre . . . . . . . . . . . . . . . . . . 111
9.6.1. Factores de certidumbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
9.6.2. Factores de certidumbre à la MYCIN . . . . . . . . . . . . . . . . . . . 114
9.6.3. Formato de las reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
9.6.4. La máquina de inferencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
9.6.5. Interfaz con el usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
85
85
87
89
92
Índice general
VII
10. Arboles de Decisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.1. Representación de los árboles de decisión . . . . . . . . . . . . . . . . . . . . . . 121
10.2. Problemas apropiados para la aplicación de árboles de decisión . . . . 123
10.3. El algoritmo básico de aprendizaje de árboles de decisión . . . . . . . . . 124
10.3.1. ¿Qué atributo es el mejor clasificador? . . . . . . . . . . . . . . . . . . 124
10.3.2. Entropı́a y ganancia de información . . . . . . . . . . . . . . . . . . . . . 126
10.4. Espacio de hipótesis en el aprendizaje inductivo de árboles de
decisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
10.5. Sesgo inductivo en el aprendizaje de árboles de decisión . . . . . . . . . . 128
10.5.1. Sesgo por restricción y sesgo por preferencia . . . . . . . . . . . . . 129
10.5.2. ¿Porqué preferir hipótesis más compactas? . . . . . . . . . . . . . . . 129
10.6. Consideraciones sobre el aprendizaje inductivo de árboles de
decisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
10.6.1. Evitando un sobreajuste con los datos de entrenamiento . . . . 130
10.6.2. Incorporando valores contı́nuos . . . . . . . . . . . . . . . . . . . . . . . . 132
10.6.3. Medidas alternativas para la selección de atributos . . . . . . . . 133
10.7. Implementación el Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
10.7.1. Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
10.7.2. Distribución de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
10.7.3. El mejor atributo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
10.7.4. El árbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
10.7.5. Imprimiendo el árbol construido. . . . . . . . . . . . . . . . . . . . . . . . 139
10.7.6. Ejecutando todo el experimento . . . . . . . . . . . . . . . . . . . . . . . . 140
10.7.7. Predicados auxiliares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
11. Planeación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
11.1. Acciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
11.2. Análisis medios-fines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
11.3. Metas protegidas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
11.4. Aspectos procedimentales de la búsqueda en amplitud . . . . . . . . . . . . 149
11.5. Regresión de metas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
11.6. Combinando planeación medios fines con primero el mejor . . . . . . . 154
11.7. Variables y planes no lineales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
11.7.1. Acciones y metas no instanciadas . . . . . . . . . . . . . . . . . . . . . . . 159
11.7.2. Planes no lineales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Acrónimos
⇒
CWA
fbf
FOL
FOPC
IA
LFOL
MGU
NAF
R
ssi
WAM
U
Implicación material
Suposición del mundo cerrado (Closed World Assumption).
Fórmula bien formada (wff - well formed formula).
Lógica de primer orden (First-Order Logic).
Cálculo de predicados en primer orden (First-Order Predicate Calculus).
Inteligencia Artificial.
El lenguaje de la lógica de primer orden.
Unificador más general (Most General Unifier).
Negación por fallo finito (Negation as Finite Failure).
Función de selección en la resolución-SLD.
Si y sólo si.
Máquina abstracta de Warren (Warren Abstract Machine).
Universo de discurso. En ciertas ocasiones se presentará como D (dominio).
IX
Capı́tulo 1
Introducción
Resumen El tema de este curso de metodologı́as de programación es la programación lógica. En este capı́tulo se presenta un panorama general de este paradigma de
programación, con el objetivo de que ustedes puedan responder a ¿Porqué y para
qué es necesario estudiar a la programación lógica en un curso de su maestrı́a en
Inteligencia Artificial (IA)? Para ello, se revisarán algunas notas históricas sobre los
origenes de la programación lógica y Prolog, su lenguaje de programación más conocido; se experimentará con el uso de Prolog; y se revisará brevemente cuales son
los fundamentos teóricos del paradigma. Finalmente, el capı́tulo ofrece la organización del material que revisaremos en el resto del curso.
1.1.
Breve historia de la programación lógica.
La historia reciente, y à la française, de la programación lógica comienza en
julio de 1970 en Montreal, Canadá, donde Alain Colmerauer, Philippe Roussel y
Robert Pasero [4] trabajaban en un proyecto sobre traducción automática y procesamiento del lenguaje natural. El sistema en cuestión incluı́a analizadores sintácticos
y generadores de frases para el francés. Un estudiante de Colmerauer, decidió trabajar sobre la demostración automática de teoremas, con base en el trabajo sobre el
principio de resolución de Alan Robinson [15]. La conjunción de estos trabajos dio
como resultado una interfase entre el francés y las fórmulas lógicas del demostrador
de teoremas que permitı́a interacciones como que se muestra en el cuadro 1.1.
Este sistema hacı́a uso de constantes para designar elementos (Tom, Jerry, Max,
Queso); para designar conjuntos (Gatos, Ratones, Ratones que comen queso, etc.);
y relaciones binarias (Matar, Comer, Gustar, No gustar). Las constantes, junto con
los sı́mbolos funcionales T he, Subset, y True, especificaban un lenguaje de fórmulas lógicas. Mientras se seguı́a trabajando en la demostración de teoremas en este
lenguaje, apareció la referencia obligada al trabajo de Robert Kowalski [7] sobre
el método conocido como resolución-SL, que como veremos, es fundamental en el
lenguaje Prolog.
1
2
1 Introducción
Usuario > Los gatos matan ratones.
Usuario > Tom es un gato al que no le gustan los ratones
que comen queso.
Usuario > Jerry es un ratón que come queso.
Usuario > Max no es un gato.
Usuario > ¿Qué hace Tom?
Computadora > A Tom no le gustan los ratones que comen
queso.
Computadora > Tom mata ratones.
Usuario > ¿Quién es un gato?
Computadora > Tom.
Usuario > ¿Qué come Jerry?
Computadora > Queso.
Usuario > ¿Qué come Tom?
Computadora > Lo que comen los gatos a los que no les gustan
los ratones que comen queso.
Cuadro 1.1 Sistema de lenguaje natural de Alain Colmerauer et al. [4].
En realidad, la creación de este paradigma de programación tiene una larga historia más universal, cuya mayor parte transcurre en los dominios de la lógica matemática y recientemente en el de las ciencias de la computación. La programación
lógica se basa en la sintaxis de la lógica de primer orden, originalmente propuesta por Gottlob Frege en la segunda mitad del siglo XIX y modificada a su forma
actual por Giuseppe Peano y Bertrand Russell. En la década de los años treinta,
Kurt Göedel y Jacques Herbrand estudiaron la noción de computabilidad basada
en derivaciones. Su trabajo puede verse como el origen de la “computación como
deducción”. Además, Herbrand discutió en su tesis doctoral un conjunto de reglas
para manipular ecuaciones algebraicas en términos que pueden verse ahora como un
bosquejo de la unificación. Treinta años más tarde, Alan Robinson [15] publicó su
artı́culo fundacional sobre la demostración automática. En este trabajo se introduce
el principio de resolución, la noción de unificación y un algoritmo de unificación.
Y es que, si bien el paradigma de la programación lógica tiene sus raı́ces en la
demostración automática de teoremas, de donde tomó la noción de deducción, presenta una novedad importante: en el proceso de demostración algunos valores serán
computados. Pero otro paso era necesario para vislumbrar como es posible computar
en este marco.
En 1974, Robert Kowalski [6] introduce la noción de programas lógicos con una
forma restringida de resolución. La sintaxis propuesta por Kowalski era más restringida que la de Robinson, pero tenı́a un efecto colateral sobre la forma de una
substitución satisfactoria. Esta substitución puede verse como el resultado de una
computación, y en consecuencia, ciertas fórmulas lógicas (cláusulas de Horn) pueden interpretarse como programas. El trabajo de Kowalski termino un debate del
todo relevante para nosotros: dadas las metas de la inteligencia artificial ¿El conocimiento debe representarse de forma declarativa o procedimental? Si la forma
declarativa era la adecuada, tal como defendı́a John McCarthy [8], la realización
de la inteligencia artificial pasaba por representar el conocimiento en cálculo de
predicados e implementar procedimientos de prueba eficientes sobre este lenguaje;
1.2 Una breve introducción a Prolog
3
Si la forma procedimental era la adecuada, entonces tal realización pasaba por la
implementación de procedimientos organizados como una sociedad de agentes que
compiten y cooperan, tal como lo resume Marvin Minsky [9]. Los programas lógicos de Kowalski tienen evidentemente una interpretación declarativa, pero también
procedimental.
Entre 1971 y 1973 Kowalski y Colmeraruer colaboraron intensamente, concluyendo con la creación de Prolog en 1973. Prolog puede verse como la realización
práctica del concepto de programa lógico. Aunque sus inicios estuvieron enfocados
al procesamiento del lenguaje natural, pronto se encontró que Prolog podı́a ser usado
como un lenguaje de programación de propósito general. Originalmente, Prolog fue
implementado por Philippe Roussel como un intérprete escrito en Algol-W. Un paso
adelante fue logrado por David H. Warren [20] quién propuso en 1983 una máquina
abstracta, ahora conocida como WAM (Warren Abstract Machine). La WAM cuenta con un conjunto de instrucciones para compiladores de Prolog independientes de
la máquina y se convirtió en el estándar para la implementación de Prolog y otros
lenguajes lógicos de programación.
De esta breve historia (para una versión más detallada ver J.A. Robinson [16])
podemos extraer algunas consideraciones sobre este curso:
La programación lógica es una herramienta y un sujeto de estudio de la inteligencia artificial.
La lógica de primer orden es fundamental para entender este paradigma de programación.
La programación lógica es un paradigma de programación, que difiere de otros
paradigmas, como la programación imperativa (Algol, C, Pascal, etc.), la orientada a objetos (Simula, Smalltalk, Eiffel, C++, Java, etc.), o la funcional (ML,
Haskell, Lisp, etc.).
Prolog 6= programación lógica, pero es su realización práctica más usada en la
actualidad.
1.2.
Una breve introducción a Prolog
Prolog es la realización más utilizada del paradigma de programación lógica.
Escribir un programa en Prolog tiene menos que ver con la tarea de especificar un
algoritmo, como es el caso de la programación imperativa; y más con la especificación de los objetos y las relaciones entre ellos, que ocurren en el contexto de un
problema. En particular, tiene que ver con la especificación de las relaciones que
conforman la solución deseada del problema. Veamos un ejemplo basado en la genealogı́a de una familia [1].
4
1.2.1.
1 Introducción
Hechos y relaciones
La figura 1.1 muestra una relación familiar, donde las flechas X → Y indican
que X es progenitor Y . El hecho de que Tom sea progenitor de Bob 1 se escribe en
Prolog: progenitor(tom,bob).
ann
pam
tom
bob
liz
pat
jim
Figura 1.1 Una relación familiar.
Hemos escogido progenitor como el nombre de una relación que tiene a tom
y bob como argumentos. Por razones que explicaremos más adelante, escribimos
los nombres como tom con minúscula inicial. Para indicar que esta relación tiene
dos argumentos escribimos progenitor/2 y decimos que progenitor tiene aridad 2.
El árbol familiar completo puede definirse como un programa en Prolog:
1
2
3
4
5
6
progenitor(pam,bob).
progenitor(tom,bob).
progenitor(tom,liz).
progenitor(bob,ann).
progenitor(bob,pat).
progenitor(pat,jim).
Este programa consta de seis cláusulas. Cada una de estas cláusulas declara un
hecho sobre la relación progenitor. Por ejemplo, progenitor(tom,bob) es un
caso particular de la relación progenitor. Una relación está definida por el conjunto
de todos sus casos.
Podemos editar un archivo con este programa Prolog y llamarlo clase01.pl.
Para utilizar este programa es necesario invocar a Prolog, por ejemplo, si usamos
1
Decidı́ usar una familia gringa, porque nuestros bellos nombres como Marı́a del Pilar, no caben
en un grafo fácil de leer. Si usted quiere llamar a Tom, Pancho; eso, como veremos, no cambia en
nada la historia que voy a contar (a condición de que Pancho sea siempre Pancho).
1.2 Una breve introducción a Prolog
5
SWI Prolog, en una terminal invocarı́amos swipl (ó pl en algunos sistemas operativos):
> swipl
Welcome to SWI-Prolog (Multi-threaded, 32 bits, Version 5.6.64)
Copyright (c) 1990-2008 University of Amsterdam.
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free
software, and you are welcome to redistribute it under certain
conditions.
Please visit http://www.swi-prolog.org for details.
For help, use ?- help(Topic). or ?- apropos(Word).
?-
El sı́mbolo ?- es el indicador de que Prolog espera una instrucción. Si tenemos
un archivo llamado clase01.pl con el conjunto de casos que define la relación
progenitor, podemos consultarla desde SWI Prolog:
?- [clase01].
% clase01 compiled 0.00 sec, 168 bytes
Yes
?-
Prolog responde que el programa clase01 ha sido compilado (¿Sabı́an ustedes
que el código de Prolog es compilado?) y espera una nueva instrucción. La instrucción puede ser la pregunta ¿Es progenitor Bob de Pat?
?- progenitor(bob,pat).
Yes
a lo que Prolog responderá Yes, al encontrar que ese hecho se encuentra en nuestro
programa. Si preguntamos ¿Es Liz progenitora de Pat? obtendremos como respuesta
No, porque nuestro programa no menciona nada (¿Habı́an escuchado el termino
“supuesto del mundo cerrado”?) acerca de que Liz sea progenitora de Pat:
?- progenitor(liz,pat).
No
Lo mismo sucede con la siguiente consulta, pues Ben no es siquiera un objeto
conocido por nuestro programa, esto es, Ben no aparece en ninguna parte de nuestro
código:
?- progenitor(tom,ben).
No
Una pregunta más interesante sobre la relación progenitor es ¿Quién es el progenitor de Liz? Lo cual puede preguntarse como:
?- progenitor(X,liz).
X = tom
Yes
6
1 Introducción
Prolog computa un valor para X tal que la relación progenitor se cumple. Si
preguntamos por los hijos de Bob, tendremos varı́as respuestas posibles. Para obtenerlas todas, es necesario teclear ; y ←- o Enter después de cada respuesta de
Prolog:
?- progenitor(bob,X).
X = ann ;
X = pat ;
No
Prolog nos da las respuestas ann, pat y al no haber más respuestas posibles,
responde No.
Es posible plantear preguntas más complicadas a nuestro programa, por ejemplo
¿Quién es abuelo/a de Jim? Como nuestro programa no conoce directamente la relación abuelo/2, esta pregunta debe descomponerse en dos preguntas como lo muestra
la figura 1.2:
1. ¿Quién es el progenitor de Jim? Asumamos que es alguién Y .
2. ¿Quién es el progenitor de Y? Asumamos que es alguién X.
X
progenitor
Y
abuelo
progenitor
jim
Figura 1.2 La relación abuelo expresada como una composición de dos relaciones progenitor.
La secuencia de preguntas en Prolog es como sigue:
?- progenitor(Y,jim), progenitor(X,Y).
Y = pat
X = bob
Yes
Si invertimos el orden de las dos preguntas, el resultado sigue siendo el mismo:
?- progenitor(X,Y), progenitor(Y,jim).
X = bob
Y = pat
Yes
1.2 Una breve introducción a Prolog
7
Podemos preguntar también ¿Quien es nieto de Tom?:
?- progenitor(tom,X), progenitor(X,Y).
X = bob
Y = ann ;
X = bob
Y = pat ;
No
Otra pregunta interesante serı́a ¿Tienen Ann y Pat progenitores en común? Esto
puede descomponerse nuevamente en dos preguntas:
1. ¿Quién es el progenitor X de Ann?
2. ¿Es X (el mismo) progenitor de Pat?
?- progenitor(X,ann), progenitor(X,pat).
X = bob ;
No
?-
Resumiendo:
Es sencillo definir en Prolog una relación, como progenitor/2, especificando las
n-tuplas de objetos que satisfacen la relación (n, conocido como aridad, es el
número de argumentos de la relación, para progenitor n = 2).
El usuario puede plantear fácilmente preguntas a Prolog sobre las relaciones definidas en un programa.
Un programa Prolog consiste de cláusulas. Cada cláusula termina con un punto.
Los argumentos de una relación pueden ser: objetos concretos o constantes como
tom y ann; objetos generales o variables como X e Y.
Las preguntas planteadas a Prolog consisten en una o más metas. Una secuencia
de metas como progenitor(X,ann), progenitor(X,pat) significa
la conjunción de las metas: X es progenitor de ann y X es progenitor de pat.
La respuesta a una pregunta puede ser positiva o negativa, dependiendo de si la
meta se puede satisfacer o no. En el caso de una respuesta positiva, se dice que
la meta fue satisfecha y tuvo éxito. En cualquier otro caso se dice que la meta no
fue satisfecha y falló.
Si varias respuestas satisfacen una pregunta, Prolog encontrará tantas como el
usuario quiera.
1.2.2.
Reglas
Nuestro ejemplo puede extenderse en muchas formas interesantes. Definamos
las relaciones mu jer/1 y hombre/1, para poder expresarnos sobre el genero de los
miembros de nuestra familia ejemplar:
8
1
2
3
4
5
6
7
1 Introducción
mujer(pam).
mujer(liz).
mujer(pat).
mujer(ann).
hombre(tom).
hombre(bob).
hombre(jim).
Las relaciones unarias (n = 1) se usan normalmente para expresar propiedades de
los objetos. Las relaciones binarias (n = 2) definen relaciones entre pares de objetos.
La cláusula mujer(pam) establece que Pam es una mujer. La misma información
podrı́a definirse como una relación genero/2 como genero(pam,mujer).
Nuestra siguiente extensión al programa será definir la relación vastago/2 como
la inversa de la relación progenitor/2. Para ello podemos definir explı́citamente
las tuplas que satisfacen esta relación, por ejemplo: vastago(liz,tom, etc. Sin
embargo, se puede obtener una definición más elegante si tomamos en cuenta que la
relación vastago/2 es la inversa de progenitor/2 y que progenitor/2 ya fue definida.
La alternativa se basa en el siguiente enunciado lógico: Para todo X y para todo Y ,
Y es un vástago de X si existe un X que es progenitor de un Y . Esta formulación
es muy parecida al formalismo usado en Prolog. La cláusula correspondiente es la
siguiente:
1
vastago(Y,X) :- progenitor(X,Y).
La cláusula puede leerse también como: Si X es un progenitor de Y entonces Y es un vástago de X. A este tipo de cláusulas se les conoce como reglas.
Existe una diferencia fundamental entre los hechos y las reglas. Un hecho como
progenitor(tom,liz) es algo que es siempre, incondicionalmente, verdadero. Las reglas especifican cosas que son ciertas si alguna condición se satisface. Por
ello decimos que las reglas tienen:
Una parte condicional (el lado derecho de la regla o cuerpo de la regla).
Una conclusión (el lado izquierdo de la regla o cabeza de la regla).
¿Qué hace Prolog cuando se le plantea una meta como la siguiente?
?- vastago(liz,tom).
No existe ningún hecho sobre vástagos en nuestro programa, por lo tanto, la
única alternativa es considerar la aplicación de la regla sobre los vástagos. La regla
es general, en el sentido que es aplicable a cualquier objeto X e Y, por lo que puede
ser aplicada a constantes como liz y tom. Para aplicar la regla a liz y a tom es
necesario substituir Y por liz y X por tom. Con tal substitución, obtenemos un
caso especial de nuestra regla:
vastago(liz,tom) :- progenitor(tom,liz).
1.2 Una breve introducción a Prolog
9
La parte condicional de la regla es ahora:
progenitor(tom,liz).
Ahora Prolog tratará de encontrar si esta condición es verdadera, de forma que la
meta inicial:
vastago(liz,tom).
ha sido substituida por una sub-meta progenitor(tom,liz). Esta nueva
sub-meta puede satisfacerse fácilmente a partir de los hechos conocidos por el programa, lo cual significa que la conclusión de la regla también es verdadera, y Prolog
responde con éxito:
?- vastago(liz,tom).
Yes
Especifiquemos ahora la relación madre/2 a partir del siguiente enunciado lógico: Para toda X e Y , X es madre de Y si X es progenitor de Y y X es mujer. Esto se
traduce a Prolog como:
1
madre(X,Y) :- progenitor(X,Y), mujer(X).
La coma en el cuerpo de la regla, indica una conjunción: ambas condiciones
deben ser verdaderas para que la conclusión lo sea.
Las relaciones abuela/2 y hermana/2 pueden definirse como:
1
2
abuela(X,Y) :- progenitor(X,Z), progenitor(Z,Y), mujer(X).
hermana(X,Y) :- progenitor(Z,X), progenitor(Z,Y), mujer(X).
Observen, en el caso de hermana/2, la manera de especificar que X e Y tienen un
mismo progenitor. La condición de esta regla se lee: existe un Z que es progenitor de
X y el mismo Z es progenitor de Y y X es mujer. Gráficamente la relación hermana/2
se muestra en la figura 1.3. Ahora podemos preguntar:
?- hermana(ann,pat).
Yes
Tras nuestra primer pregunta sobre esta relación, podemos concluir que su definición es correcta, pero tiene un sutil error que se revela al preguntar:
?- hermana(X,pat).
X = ann ;
X = pat ;
No
10
1 Introducción
Z
progenitor
mujer
X
progenitor
hermana
Y
Figura 1.3 La relación hermana.
¿Es correcto que Pat sea su propia hermana? Ese es el comportamiento que esperábamos de la definición de hermana/2, y se debe a que no hay nada que diga que
X e Y deben ser ¡diferentes! Esto se puede corregir definiendo hermana como:
1
2
3
4
5
hermana(X,Y) :progenitor(Z,X),
progenitor(Z,Y),
mujer(X),
dif(X,Y).
De forma que:
?- hermana(X,pat).
X = ann ;
No
Resumiendo:
Los programas Prolog pueden extenderse fácilmente agregando nuevas cláusulas.
Las cláusulas en Prolog son de tres tipos: hechos, reglas y metas.
Los hechos declaran cosas que son verdaderas siempre, incondicionalmente.
Las reglas declaran cosas que son verdaderas dependiendo de ciertas condiciones.
Por medio de las preguntas el usuario puede computar qué cosas son verdaderas.
Las cláusulas de Prolog tienen cabeza y cuerpo. El cuerpo es una lista de metas
separadas por comas. Las comas implican conjunción.
Los hechos son cláusulas con el cuerpo vacı́o; las preguntas tienen la cabeza
vacı́a; y las reglas tienen cabeza y cuerpo.
En el curso de una computación, las variables pueden ser substituidas por otros
objetos.
Las variables se asumen cuantificadas universalmente. La cuantificación existencial sólo es posible en las variables que aparecen en el cuerpo de una cláusula. Por
1.2 Una breve introducción a Prolog
11
ejemplo la cláusula tiene hijo(X) :- progenitor(X,Y) puede leerse
como: Para todo X, X tiene un hijo si existe un Y y X es progenitor de Y .
1.2.3.
Definición de reglas recursivas
Agreguemos una relación nueva a nuestro programa: la relación ancestro/2. Esta
relación será definida en términos de la relación progenitor/2. La definición completa puede expresarse por medio de dos reglas. La primera definiendo al ancestro
inmediato (progenitor) y la segunda a los ancestros no inmediatos. Decimos que alguien X es ancestro indirecto de alguien Z, si hay una cadena de progenitores desde
X hasta Z, como lo ilustra la figura 1.4. En nuestro ejemplo de la figura 1.1, Tom es
ancestro directo de Liz e indirecto de Pat.
X
progenitor
progenitor
ancestro
X
ancestro
progenitor
Y
progenitor
Y
Figura 1.4 La relación ancestro en términos de progenitor directo e indirecto.
La primera regla es muy sencilla y se expresa en Prolog como:
1
ancestro(X,Z) :- progenitor(X,Z).
La segunda regla es más complicada porque las cadenas de progenitores presentan un problema: ¡no sabemos cuantas veces hay que aplicar la relación progenitor!
Un primer intento podrı́a ser algo como:
1
2
3
4
ancestro(X,Z) :progenitor(X,Z).
ancestro(X,Z) :progenitor(X,Y),
12
5
6
7
8
9
10
1 Introducción
progenitor(Y,Z).
ancestro(X,Z) :progenitor(X,Y0),
progenitor(Y0,Y1),
progenitor(Y1,Z).
...
Lo cual resulta en un programa largo y, peor aún, que sólo funciona para un
número limitado de ancestros, en el ejemplo: padres, abuelos y bisabuelos. Es decir,
esta definición de ancestro/2 es correcta pero incompleta.
Existe una formulación elegante y completa de la relación ancestro/2, completa en el sentido que puede computar cualquier ancestro, independientemente de la
longitud de la cadena de progenitores que deba aplicarse. La idea central es definir
ancestro en términos de si misma:
1
2
ancestro(X,Z) :progenitor(X,Z).
3
4
5
6
ancestro(X,Z) :progenitor(X,Y),
ancestro(Y,Z).
Ahora podemos preguntar ¿De quien es ancestro Pam?
?- ancestro(pam,X).
X = bob ;
X = ann ;
X = pat ;
X = jim ;
No
O ¿Quienes son los ancestros de Jim?
?- ancestro(X,jim).
X = pat ;
X = pam ;
X = tom ;
X = bob ;
No
Resumiendo:
Las reglas recursivas definen conceptos en términos de ellos mismos.
Están definidas por al menos dos casos: uno terminal (no recursivo) y la llamada
recursiva.
Una relación recursiva define intenSionalmente un concepto.
intenSional 6= intenCional.
1.3 ¿Cómo computa Prolog una solución?
1.3.
13
¿Cómo computa Prolog una solución?
Una pregunta a Prolog es siempre una secuencia de una o más metas. Para responder, Prolog trata de satisfacer estas metas. ¿Qué significa satisfacer una meta?
Satisfacer una meta implica demostrar que la meta es verdadera, asumiendo que
las relaciones en el programa lógico son verdaderas. Satisfacer una meta significa
entonces demostrar que la meta es una consecuencia lógica de los hechos y reglas
definidas en un programa. Si la pregunta contiene variables, Prolog necesita también
encontrar cuales son los objetos particulares (que remplazaran a las variables) para
los cuales la meta se satisface. La asignación de valores a variables es mostrada al
usuario. Si Prolog no puede demostrar para alguna asignación de valores a variables,
que las metas siguen lógicamente del programa, la respuesta a la pregunta será No.
En términos matemáticos, la interpretación de un programa en Prolog es como
sigue: Prolog acepta hechos y reglas como un conjunto de axiomas, y el usuario
plantea preguntas como un teorema; entonces Prolog trata de probar este teorema,
es decir, demostrar que el teorema se sigue lógicamente de los axiomas.
Veamos un ejemplo clásico. Sean los axiomas:
Todos los hombres son falibles.
Socrates es un hombre.
Un teorema que lógicamente sigue de estos dos axiomas es:
Socrates es falible.
El primer axioma puede reescribirse como: Para toda X, si X es un hombre,
entonces X es falible. El ejemplo puede entonces traducirse a Prolog como sigue:
1
2
falible(X) :- hombre(X).
hombre(socrates).
y
?- falible(socrates)
Yes
Un ejemplo más complicado, tomado de la familia de la figura 1.1, es la meta: ?ancestro(tom,pat). Sabemos que progenitor(bob,pat) es un hecho.
Podemos derivar entonces que ancestro(bob,pat). Observen que este hecho
derivado no puede ser encontrado explı́citamente en nuestro programa sobre la familia, pero puede derivarse a partir de los hechos y reglas en el programa. Un paso
en la inferencia de este tipo, puede ser escrito como: progenitor(bob,pat) ⇒
ancestro(bob,pat).
El proceso completo de inferencia en dos pasos puede escribirse como:
14
1 Introducción
progenitor(bob, pat) ⇒ ancestro(bob, pat)
progenitor(tom, bob) ∧ ancestro(bob, pat) ⇒ ancestro(tom, pat)
A este tipo de secuencias se les conoce como secuencias de prueba ¿Cómo encuentra Prolog una secuencia de prueba?
Prolog encuentra la secuencia de prueba en orden inverso al que acabamos de
presentar. En lugar de comenzar con los hechos simples especificados en el programa, Prolog comienza con las metas y, usando reglas, substituye la meta actual por
sub-metas, hasta que estas llegan a resolverse por hechos simples. Dada la pregunta:
?- ancestro(tom,pat).
Prolog tratará de satisfacer esta meta. Para ello, tratará de encontrar una cláusula
en el programa, a partir de la cual la meta dada pueda seguirse lógicamente. Obviamente, las únicas reglas acerca de la relación ancestro/2 son:
1
2
ancestro(X,Z) :progenitor(X,Z).
3
4
5
6
ancestro(X,Z) :progenitor(X,Y),
ancestro(Y,Z).
Decimos que la cabeza de estas reglas coincide o corresponde (match) con la
meta planteada. Las reglas representan formas alternativas en las que Prolog puede resolver la meta. Prolog intentará resolver la pregunta con la primer cláusula que
aparece en el programa (lı́neas 1 y 2). Puesto que la meta es ancestro(tom,pat),
las variables de la regla pueden ser substituidas conforme a X/tom y Z/pat. La
meta original ancestro(tom,pat), es entonces remplazada por la sub-meta
progenitor(tom,pat). El paso consistente en usar una regla para transformar
una meta en una sub-meta, se muestra gráficamente en la figura 1.5.
ancestro(tom, pat)
ancestro(X,Z) :- progenitor(X,Z)
progenitor(tom, pat)
Figura 1.5 El primer paso de la ejecución. La meta de arriba es verdadera si la meta de abajo es
verdadera.
1.3 ¿Cómo computa Prolog una solución?
15
Como no hay una cláusula en el programa que coincida con la nueva sub-meta
progenitor(tom,pat), la sub-meta falla. Ahora Prolog vuelve atrás (backtrack) para evaluar de forma alternativa su meta original. Ahora intentará la segunda cláusula del programa (lı́neas 4–6). Como antes, las variables de la meta toman
los valores: X/tom y Z/pat. Pero Y no toma valor alguno aún. La meta es remplazada por las sub-metas: progenitor(tom,Y), ancestro(Y,pat). La
ejecución de este nuevo paso se muestra en la figura 1.6.
ancestro(tom,pat)
ancestro(X,Z) :progenitor(X,Y), ancestro(Y,Z)
ancestro(X,Z) :- progenitor(Z,X)
progenitor(tom,pat)
progenitor(tom,Y)
ancestro(Y,pat)
No
Figura 1.6 El segundo paso de la ejecución. Dos sub-metas son generadas.
Enfrentado ahora el problema de resolver dos sub-metas, Prolog intentará satisfacer la primer sub-meta definida en el programa (¿Porqué?). La primer sub-meta se
resuelve fácilmente pues coincide con uno de los hechos del programa. Esto obliga a que Y tome el valor de bob, de forma que la segunda sub-meta se vuelve
ancestro(bob,pat).
Para satisfacer está sub-meta, Prolog usará nuevamente la primer cláusula del
programa (lı́neas 1 y 2). Como en este paso se hace una nueva llamada a esta regla, en realidad Prolog utiliza variables diferentes a la llamada del paso anterior,
renombrando las variables como sigue:
1
ancestro(X’,Z’) :- progenitor(X’,Z’).
Lo cual conduce a la substitución de variables: X’/bob y Z’/pat. La meta es
remplazada por progenitor(bob,pat). Esta meta es satisfecha porque coincide con uno de los hechos del programa. Gráficamente este proceso se muestra en
la figura 1.7.
Con esta explicación, estudien la siguiente sesión en Prolog:
?- trace.
Yes
[trace] ?- ancestro(tom,pat).
Call: (7) ancestro(tom, pat) ? creep
Call: (8) progenitor(tom, pat) ? creep
Fail: (8) progenitor(tom, pat) ? creep
Redo: (7) ancestro(tom, pat) ? creep
Call: (8) progenitor(tom, _L345) ? creep
Exit: (8) progenitor(tom, bob) ? creep
Call: (8) ancestro(bob, pat) ? creep
16
1 Introducción
ancestro(tom,pat)
ancestro(X,Z) :progenitor(X,Y), ancestro(Y,Z)
ancestro(X,Z) :- progenitor(Z,X)
progenitor(tom,pat)
progenitor(tom,Y)
ancestro(Y,pat)
No
Y = bob
progenitor(tom,bob)
ancestro(bob,pat)
ancestro(X,Z) :progenitor(Z,X)
progenitor(bob,pat)
Yes
Figura 1.7 El segundo paso de la ejecución. Dos sub-metas son generadas.
Call:
Exit:
Exit:
Exit:
(9)
(9)
(8)
(7)
progenitor(bob, pat)
progenitor(bob, pat)
ancestro(bob, pat) ?
ancestro(tom, pat) ?
? creep
? creep
creep
creep
Yes
1.4.
Organización del curso
Durante el curso revisaremos tanto el fundamento teórico de la programación
lógica, como el uso de Prolog para resolver problemas propios de la inteligencia
artificial. La razón de esto debe ser evidente ya: estamos ante una herramienta que es
a su vez sujeto de estudio de la IA. Este texto de apoyo esta dividido en dos partes:
Fundamentos teóricos y Prolog; sin que esto implique que ambos aspectos serán
revisados estrictamente en este orden. Hay un tercer componente que se cubrirá con
lecturas complementarias y el desarrollo de un proyecto final: las aplicaciones de la
programación lógica.
Con respecto a los fundamentos teóricos, iniciaremos con un recordatorio de la
lógica de primer orden (capı́tulo 2). Posteriormente revisaremos los conceptos de
cláusula y programa definitivos (capı́tulo 3) y el principio de resolución (capı́tulo
4). Continuaremos con el concepto de negación (capı́tulo 5) y cerraremos la primera
1.4 Organización del curso
17
parte del curso con algunas consideraciones sobre el corte y la aritmética (capı́tulo
6).
La segunda parte inicia con una introducción menos breve sobre el lenguaje
(capı́tulo 7) y continua con una serie de aplicaciones de Prolog a problemas propios de la IA: búsquedas en espacios de soluciones (capı́tulo 8), sistemas expertos
(capı́tulo 9), inducción de árboles de decisión (capı́tulo 10), y planeación (capı́tulo
11).
De ser posible, concluiremos el curso con algunos aspectos de meta-programación,
programación por restricciones y programación de agentes.
Parte I
Formalismos
Capı́tulo 2
Lógica de Primer Orden
Resumen En términos generales, la Programación Lógica concierne al uso de la
lógica para representar y resolver problemas. Más adelante precisaremos que, en
realidad, usaremos una lógica restringida a cláusulas de Horn y la resolución como
regla de inferencia [11]. Por ahora, este capı́tulo introduce los conceptos de la lógica
de primer orden necesarios para abordar los aspectos formales de la Programación
Lógica. Para ello, se adopta un enfoque basado en sistemas formales, que nos permita describir el lenguaje, la teorı́a del modelo y la teorı́a de prueba de la lógica
de primer orden. Con este aparato, se introducen los conceptos de unificación y
resolución como regla de inferencia.
2.1.
Introducción
Cuando describimos situaciones de nuestro interés, solemos hacer uso de enunciados declarativos. Decimos que estos enunciados son declarativos en el sentido
lingüı́stico del término, esto es, se trata de expresiones del lenguaje natural que son
o bien verdaderas, o bien falsas; en contraposición a los enunciados imperativos
e interrogativos. La lógica proposicional es declarativa en este sentido, las proposiciones representan hechos que se dan o no en la realidad. La lógica de primer
orden tienen un compromiso ontólogico más fuerte [17], donde la realidad implica además, objetos y relaciones entre ellos. Consideren los siguientes ejemplos de
enunciado declarativo:
1. Julia es madre y Luis es hijo de Julia.
2. Toda madre ama a sus hijos.
donde el enunciado (1) se refiere a los objetos de discurso Julia y Luis, usando propiedades de estos objetos, como ser madre; ası́ como relaciones entre éstos, como
hi jo. El enunciado (2) se refiere a relaciones que aplican a todas las madres, en tanto
que objetos de discurso. A esto nos referimos cuando hablamos de representación
21
22
2 Lógica de Primer Orden
de un problema en el contexto de la Programación Lógica, a describir una situación
en términos de objetos y relaciones entre ellos.
Si se aplican ciertas reglas de razonamiento a tales representaciones, es posible
obtener nuevas conclusiones. Esto concierne a la resolución de problemas en Programación Lógica. Por ejemplo, conociendo (1) y (2) es posible inferir (vı́a Modus
Ponens) que:
3. Julia ama a Luis.
La idea central de la programación lógica es describir los objetos que conforman
un universo de discurso, personas en el ejemplo; ası́ como las relaciones entre
ellos, siguiendo con el ejemplo hi jo y madre; y computar tales descripciones para
obtener conclusiones como (3). Al describir el problema que queremos resolver,
también podemos hacer uso de funciones, relaciones en las cuales sólo hay un valor
dada una entrada. Por ejemplo, “madre de” puede representarse como una función
(todo hijo tiene una sola madre), pero “hijo de” no. Esto se ilustra en la gráfica 2.1.
luis
madre de
madre de
pedro
maria
juana
maria
juana
hijo de
hijo de
luis
pedro
Figura 2.1 La relación madre de es una función; mientras que hijo de no lo es.
Como en todo sistema formal, es necesario especificar cuidadosamente la sintaxis de tales enunciados declarativos, es decir, que expresiones pertenecen al lenguaje de la lógica de primer orden, y cuales no; la semántica de estas expresiones,
es decir qué hace que una expresión sea verdadera o falsa; ası́ como las reglas de
razonamiento que permiten concluir (3) a partir de (1) y (2). Tales cuestiones son el
tema de estudio de la lógica matemática.
Esta sesión del curso introduce los elementos de la lógica de primer orden, necesarios para abordar la resolución como regla de inferencia en lógica de primer orden
y su uso en el lenguaje de programación Prolog. El material aquı́ presentado está basado principalmente en los textos de Michael R. Genesereth y Nils J. Nilsson [5],
capı́tulo 2; y el de Ulf Nilsson y Jan Maluszyński [12], capı́tulo 1. Una lectura complementaria a estos textos son los capı́tulos 8 y 9 del texto de Stuart Russell y Peter
Norvig [17].
2.3 El lenguaje de la lógica de primer orden
2.2.
23
Sistemas formales
La especificación cuidadosa de la sintaxis y semántica de la lógica de primer
orden, se consigue definiendo a ésta última como un sistema formal. Para ello, es
necesario considerar tres aspectos:
Languaje. Este elemento está asociado a la sintaxis de la lógica de primer orden
y de los programas lógicos. El lenguaje de un sistema formal está dado por un
conjunto de sı́mbolos conocido como alfabeto y una serie de reglas de construcción o sintácticas. Una expresión es cualquier secuencia de sı́mbolos pertenecientes al alfabeto (primarios). Cualquier expresión es, o no es, una fórmula
bien formada (fbf). Las fórmulas bien formadas son las expresiones que pueden
formarse con los sı́mbolos del alfabeto a partir de las reglas de construcción y
por tanto, pertenecen al languaje de la lógica de primer orden.
Teorı́a de modelo. Este elemento está asociado a la semántica de la lógica de
primer orden. La teorı́a del modelo establece la interpretación de las fbfs en un
sistema formal. Su función es relacionar las fbfs con alguna representación simplificada de la realidad que nos interesa, para establecer cuando una fbf es falsa y
cuando verdadera. Esta versión de realidad corresponde a lo que informalmente
llamamos “modelo”. Sin embargo, en lógica, el significado de “modelo” está ı́ntimamente relacionado con el lenguaje del sistema formal: si la interpretación M
hace que la fbf α 1 sea verdadera, se dice que M es un modelo de α o que M
satisface α, y se escribe M |= α. Una fbf es válida si toda interpretación es un
modelo para ella.
Teorı́a de prueba. Este elemento está asociado con el razonamiento deductivo.
La teorı́a de la prueba tiene como objetivo hacer de cada enunciado matemático una fórmula demostrable y rigurosamente deducible. Para ello, la actividad
matemática deberı́a quedar reducida a la manipulación de sı́mbolos y sucesiones de sı́mbolos regulada por un conjunto de instrucciones dadas al respecto. La
construcción de tal teorı́a implica, además del lenguaje del sistema formal, un
subconjunto de fbf que tendrán el papel axiomas en el sistema, y un conjunto
de reglas de inferencia que regulen diversas operaciones sobre los axiomas. Las
fbf obtenidas mediante la aplicación sucesiva de las reglas de inferencia a partir
de los axiomas se conocen como teoremas del sistema.
2.3.
El lenguaje de la lógica de primer orden
Básicamente, la lógica de primer orden, también conocida como cálculo de predicados, introduce un conjunto de sı́mbolos que nos permiten expresarnos acerca
1
El sı́mbolo α se usa aquı́ como una variable meta-lógica, es decir, una variable que tiene como
referente el lenguaje del sistema formal mismo, y por lo tanto, no forma parte del lenguaje del
sistema en si. Se usaran letras griegas como variables meta-lógicas.
24
2 Lógica de Primer Orden
de los objetos en un dominio de discurso dado. El conjunto de todos estos objetos se conoce como universo de discurso (U ). Los miembros del universo de
discurso pueden ser objetos concretos, ej., un libro, un robot, etc; abstractos, ej.,
números; e incluso, ficticios, ej., unicornios, etc. Un objeto es algo sobre lo cual
queremos expresarnos. Como ejemplo, consideren el multi citado mundo de los
bloques [5] que se muestra en la figura 2.2. El universo de discurso para tal escenario es el conjunto que incluye los cinco bloques, la el brazo robótico y la mesa:
{a, b, c, d, e, brazo, mesa}.
Brazo robótico
E
A
D
B
C
Mesa
Figura 2.2 El mundo de los bloques, usado para ejemplificar el cálculo de predicados.
Una función es un tipo especial de relación entre los objetos del dominio de
discurso. Este tipo de relaciones mapea un conjunto de objetos de entrada a un
objeto único de salida. Por ejemplo, es posible definir la función parcial sombrero
que mapea un bloque al bloque que se encuentra encima de él, si tal bloque existe.
Las parejas correspondientes a esta función parcial, dado el escenario mostrado en la
figura 2.2 son: {(b, a), (c, d), (d, e)}. El conjunto de todas las funciones consideradas
en la conceptualización del mundo se conoce como base funcional.
Un segundo tipo de relación sobre los objetos del dominio de discurso son los
predicados. Diferentes predicados pueden definirse en el mundo de los bloques, ej.,
el predicado sobre que se cumple para dos bloques, si y sólo si el primero está inmediatamente encima del segundo. Para la escena mostrada en la figura 2.2, sobre/2 se
define por los pares {(a, b), (d, c), (e, d)}. Otro predicado puede ser libre/1, que se
cumple para un bloque si y sólo si éste no tiene ningún bloque encima. Este predicado tiene los siguientes elementos {a, e}. El conjunto de todos los predicados usados
en la conceptuación se conoce como base relacional.
Para universos de discurso finitos, existe un lı́mite superior en el número posible
de predicados n-arios que pueden ser definidos. Para un universo de discurso de
cardinalidad b (cardinalidad es el número de elementos de un conjunto), existen bn
distintas n-tuplas. Cualquier predicado n-ario es un subconjunto de estas bn tuplas.
n
Por lo tanto, un predicado n-ario debe corresponder a uno de máximo 2(b ) conjuntos
posibles.
Además de las funciones y predicados, la flexibilidad de la lógica de primer
orden resulta del uso de variables y cuantificadores. Las variables, cuyos valores
2.3 El lenguaje de la lógica de primer orden
25
son objetos del universo de discurso, se suelen representar por cualquier secuencia
de caracteres que inicie con una mayúscula. El cuantificador “para todo” (∀) nos
permite expresar hechos acerca de todos los objetos en el universo del discurso, sin
necesidad de enumerarlos. Por ejemplo, toda madre . . . El cuantificador “existe”
(∃) nos permite expresar la existencia de un objeto en el universo de discurso con
cierta propiedad en partı́cular, por ejemplo, ∃X libre(X) ∧ enLaMesa(X) expresa
que hay al menos un objeto que no tiene bloques sobre él y aue se encuentra sobre
la mesa.
2.3.1.
Sintaxis de la lógica de primer orden
Los sı́mbolos primarios de la lógica de primer orden se obtienen al considerar un
conjunto numerable de variables, sı́mbolos de predicado y sı́mbolos de funciones.
Se asume que los miembros del conjunto Var toman valores en el universo de discurso. Asociado a cada predicado y función, hay un número natural conocido como
su aridad, que expresa su número de argumentos. Los predicados de aridad 0 se
asumen como variables proposicionales. Las funciones de aridad 0 se asumen como
constantes. Considerando los operadores lógicos y los cuantificadores, tenemos que
los sı́mbolos primarios o alfabeto del lenguaje de la lógica de primer orden son los
que se muestran en la tabla 2.1
Conjunto de constantes:
Conjunto de variables:
Conjunto de predicados:
Conjunto de funciones:
Operadores monarios:
Operadores binarios:
Cuantificadores:
Paréntesis:
Const
Var
Pred
Func
¬ (negación)
∨ (disyunción)
∀ (cuantificador universal)
(, )
Cuadro 2.1 Alfabeto del lenguaje de la lógica de primer orden.
El lenguaje del cálculo de predicados LFOL se especifica recursivamente como
sigue: Primero definimos un conjunto de términos del lenguaje Term, como la unión
de constantes y variables Const ∪ Var; ası́ como la aplicación de las funciones en
Func a una secuencia de términos, cuyo tamaño queda determinado por la aridad de
la función. Recuerden que las funciones de aridad cero representan constantes. Las
siguientes reglas sintácticas expresan que los términos son fbf en el lenguaje:
Sintaxis 1 Si α ∈ Const, entonces α ∈ Term
Sintaxis 2 Si α ∈ Var, entonces α ∈ Term
Sintaxis 3 Si α/n ∈ Func, entonces α(φ1 , . . . , φn ) ∈ Term ssi φi=1,...,n ∈ Term.
26
2 Lógica de Primer Orden
Al igual que en el caso de las funciones, la sintaxis de los predicados involucra la
aridad del predicado y que sus argumentos sean a su vez términos. Recuerden que
los predicados de aridad cero se interpretan como variables proposicionales:
Sintaxis 4 Si α/n ∈ Pred, entonces α(φ1 , . . . , φn ) ∈ LFOL ssi φi=1,...,n ∈ Term.
La sintaxis de la negación y la disyunción se definen como:
Sintaxis 5 Si α ∈ LFOL , entonces ¬α ∈ LFOL
Sintaxis 6 Si α ∈ LFOL y β ∈ LFOL , entonces (α ∨ β ) ∈ LFOL
La sintaxis del cuantificador universal es como sigue:
Sintaxis 7 Si α ∈ LFOL y X ∈ Vars es una variable que ocurre en α, entonces
∀X α ∈ LFOL
Las definiciones de la conjunción, la implicación material, la equivalencia material, verdadero y falso, son como en la lógica proposicional:
Definición 1 (conjunción) (α ∧ β ) =de f ¬(¬α ∨ ¬β );
Definición 2 (implicación material) (α ⇒ β ) =de f (¬α ∨ β );
Definición 3 (equivalencia material) (α ≡ β ) =de f ((α ⇒ β ) ∧ (β ⇒ α));
Definición 4 (falso) f =de f ¬α ∧ α;
Definición 5 (verdadero) t =de f ¬f
La definición del cuantificador existencial es la siguiente:
Definición 6 (cuantificador existencial) ∃X α =de f ¬(∀X ¬α)
Siendo estrictos, el cuantificador propiamente dicho, es el sı́mbolo de cuantificador seguido de una variable, puesto que ∀X y ∀Y tienen significados diferentes. En
una fbf de la forma ∀X α, se dice que la fbf α está en el alcance del cuantificador
∀X. En tal caso, se dice que la ocurrencia de X en α está acotada, en caso contrario
se dice que la ocurrencia de la variable es libre. Por ejemplo, en ∀X sobre(X,Y )
la variable X está acotada, mientras que Y está libre. Un término sin variables se
conoce como término de base.
2.4.
La semántica de la lógica de primer orden
Antes de introducir las definiciones formales de la semántica de la lógica de primer orden, consideremos algunas expresiones posibles en está lógica, usando como
ejemplo el mundo de los bloques (Figura 2.2). Si queremos expresar que al menos
algún bloque no tiene nada encima, podemos usar los predicados bloque/1 y libre/1
en la siguiente expresión: ∃X bloque(X) ∧ libre(X). Esta fbf expresa que existe un
2.4 La semántica de la lógica de primer orden
27
X tal que X es un bloque y X está libre (no tiene otro bloque encima). Observen que
cuando usamos cuantificadores, siempre tenemos en mente el universo de discurso
en cuestión o dominio. El dominio puede especificarse en término de conjuntos.
Luego, si el dominio D es el conjunto de constantes {a, b, c, d, e, brazo, mesa}, podemos decir que B ⊂ D = {a, b, c, d, e} es el conjunto de bloques en D. Entonces,
es posible plantear una expresión equivalente a ∃X bloque(X) ∧ libre(X), usando la
fbf ∀X libre(x), si especificamos que libre/1 tiene como dominio B.
Una interpretación del predicado libre/1 es un subconjunto de B tal que si un
bloque está libre, pertenece a este subconjunto. Para un predicado de aridad dos,
como sobre/2 cuyo dominio son los bloques B × B, podemos decir que su interpretación es un subconjunto de B × B. En general, para un predicado de aridad n, su
interpretación es un subconjunto en Dn .
2.4.1.
Teorı́a de modelo de la lógica de primer orden
Para obtener un modelo para el lenguaje LFOL formamos el par M = hD,V i,
donde D es el universo de discurso, ej. cualquier colección de objetos sobre la que
queremos expresarnos, y la interpretación V es una función, tal que:
Para cualquier predicado α de aridad n, V (α) regresa las n-tuplas que corresponden a la interpretación del predicado. En el ejemplo, siguiendo nuevamente la
figura 2.2, consideren el predicado sobre/2. Su interpretación es un subconjunto
de D2 = D × D. Para la escena mostrada, V (sobre) = {(a, b), (e, d), (d, c)}.
Para una constante, la función V regresa la misma constante, ej. V (a) = a.
Algunas veces la expresión V (α) se abrevia α V . Una posible interpretación V
para la escena del mundo de los bloques mostrada en al figura 2.2, es:
aV = a
bV = b
cV = c
dV = d
eV = e
sobreV = {(a, b), (e, d), (d, c)}
enLaMesaV = {b, c}
libreV = {a, e}
porEncimaV = {(a, b), (e, d), (e, c), (d, c)}
Todo esto puede especificarse formalmente con la siguiente definición:
28
2 Lógica de Primer Orden
Definición 7 (Interpretación) Una interpretación V , con respecto a un dominio de
discurso D, es una función que satisface las siguientes propiedades: i) Si α ∈ Const,
Entonces V (α) = α; ii) Si α/n ∈ Pred, Entonces V (α) ⊆ Dn .
Observen que las variables no están incluidas en la interpretación. Interpretar las
variables de manera independiente a otros sı́mbolos en el lenguaje, es una práctica
aceptada. Decimos que U es una asignación de variables basada en el modelo
M = hD,V i si para todo α ∈ Var, U(α) ∈ Term. Por ejemplo, en el mundo de los
bloques X U = a, es una asignación de variables. Esta abreviatura a veces se expande
como U = {X\a} y se conoce como substitución.
Una interpretación V y una asignación de variables U pueden combinarse en una
asignación conjunta TVU que aplica a los términos de primer orden en general. La
asignación de términos T dadas la interpretación V y la asignación de variables
U, es un mapeo de términos a objetos del universo de discurso que se define como
sigue:
Semántica 1 Si α ∈ Const, entonces TVU (α) = V (α).
Semántica 2 Si α ∈ Var, entonces TVU (α) = U(α).
Semántica 3 Si α ∈ Term y es de la forma α(φ1 , . . . , φn ); y V (α) = g; y TVU (φi ) =
xi , entonces TVU (α(φ1 , . . . , φn )) = g(x1 , . . . , xn ).
El concepto de satisfacción guarda una relación importante con las interpretaciones y las asignaciones. Por convención, el hecho de que el enunciado α sea
satisfecho bajo una interpretación V y una asignación U, se escribe:
|=V α[U]
Entonces podemos escribir M |= VU (α) para expresar que α es verdadera en
el modelo M = hD,V i cuando las variables en α toman valores de acuerdo a la
asignación U. Por ejemplo, M |= VU (sobre(X, b)) si X\a ∈ U.
En realidad, la noción de satisfacción varı́a dependiendo de la clase del enunciado
α. Ası́ tenemos que una interpretación V y una asignación de variables U satisfacen
una ecuación, si y sólo si la correspondiente asignación de términos TVU mapea los
términos igualados a un mismo objeto. Cuando este es el caso, los términos se dicen
correferenciados:
Semántica 4 M |=V (α = β )[U] ssi TVU (α) = TVU (β ).
Para el caso de un enunciado atómico que no sea una ecuación, la satisfacción se
cumple si y sólo si la tupla formada por los objetos designados por los términos en
el enunciado, es un elemento de la relación designada por la relación constante:
Semántica 5 M |=V α(τ1 , . . . , τn )[U] ssi (TVU (τ1 ), . . . , TVU (τn )) ∈ V (α) .
Consideren como ejemplo la interpretación V definida para el mundo de los boques. Puesto que la constante a designa al bloque a y la constante b al bloque b, y
2.5 Inferencia en la lógica de primer orden
29
el par ordenado (a, b) es miembro del conjunto que interpreta la relación sobre, entonces es el caso que |=V sobre(a, b)[U], por lo cual podemos decir que sobre(a, b)
es verdadera en esa intepretación.
Evidentemente:
Semántica 6 M |=V ¬(α)[U] ssi M 6|=V α[U].
y:
Semántica 7 M |=V (α ∨ β )[U] ssi M |=V α[U] ó M |= β [U].
Un enunciado cuantificado universalmente se satisface, si y sólo si el enunciado
bajo el alcance del cuantificador, se satisface para todas las asignaciones posibles de
la variable cuantificada. Un enunciado cuantificado existencialmente se satisface,
si y sólo si el enunciado bajo el alcance del cuantificador es satisfecho por una
asignación de variables.
Semántica 8 M |=V ∀X α[U], ssi para toda β en el universo de discurso, es el caso
que M |=V α[U 0 ], donde U 0 (X) = β y U 0 (γ) = U(γ) para toda γ 6= X.
Debido a la última condición en esta regla, se dice que U 0 es una asignación Xalternativa a U. La regla semántica también puede leerse como: M |=V ∀X α[U] si
para toda asignación de variables X-alternativa U 0 , M |=V α[U 0 ].
Si una interpretación V safisface a un enunciado α para toda asignación de variables, se dice que V es un modelo de α. Un enunciado se dice satisfacible si existe
alguna interpretación y asignación de variables que lo satisfaga. De otra forma, se
dice que el enunciado es insatisfacible. Una fbf α es válida si y sólo si se satisface
en toda intepretación y asignación de variables. Las fbf válidas lo son en virtud de
su estructura lógica, por lo que no proveen información acerca del dominio descrito.
Por ejemplo p(X) ∨ ¬p(X) es una fbf válida.
2.5.
Inferencia en la lógica de primer orden
Volvamos al ejemplo de la introducción:
1. Toda madre ama a sus hijos.
2. Julia es madre y Luis es hijo de Julia.
Conociendo (1) y (2) es posible concluir que:
3. Julia ama a Luis.
Podemos formalizar este ejemplo en Lógica de Primer Orden como sigue:
1. ∀X ∀Y madre(X) ∧ hi jo de(Y, X) ⇒ ama(X,Y )
2. madre( julia) ∧ hi jo de(luis, julia)
30
2 Lógica de Primer Orden
3. ama( julia, luis)
Una vez que hemos formalizado nuestros enunciados, el proceso de inferencia
puede verse como un proceso de manipulación de fbf, donde a partir de formulas
como (1) y (2), llamadas premisas, se produce la nueva fbf (3) llamada conclusión.
Estas manipulaciones se pueden formalizar mediante reglas de inferencia. Entre
las reglas de inferencia de la lógica de primer orden encontramos:
Modus Ponens. O regla de eliminación de la implicación. Esta regla dice que
siempre que las fbfs de la forma α y α ⇒ β pertenezcan a las premisas o sean
concluidas a partir de ellas, podemos inferir β :
α α ⇒β
(⇒ E)
β
Eliminación de cuantificador universal. Esta regla expresa que siempre que
una fbf de la forma ∀Xα pertenezca a las premisas o sea concluida a partir de
ellas, una nueva fbf puede ser concluida al remplazar todas las ocurrencias libres
de X en α por algún término t que es libre con respecto a X (todas las variables
en t quedan libres al substituir X por t. La regla se presenta como sigue:
∀Xα(X)
(∀E)
α(t)
Introducción de conjunción. Cuando las fbf α y β pertenezcan a las premisas
o sean concluidas a partir de ellas, podemos inferir α ∧ β :
α β
(∧I)
α ∧β
La correctez de estas reglas puede ser demostrada directamente a partir de la
definición de la semántica de las fbf en LFOL . El uso de las reglas de inferencia
puede ilustrarse con el ejemplo formalizado. Las premisas son:
1. ∀X∀Y madre(X) ∧ hi jo de(Y, X) ⇒ ama(X,Y )
2. madre( julia) ∧ hi jo de(luis, julia)
Al aplicar la eliminación de cuantificador universal (∀E) a (1) obtenemos:
3. ∀Y (madre( julia) ∧ hi jo de(Y, julia) ⇒ ama( julia,Y )
Al aplicar nuevamente (∀E) a (3) obtenemos:
4. madre( julia) ∧ hi jo de(luis, julia) ⇒ ama( julia, luis)
Finalmente, al aplicar Modus Ponens a (2) y (4):
5. ama( julia, luis)
2.6 Substituciones
31
La conclusión (5) ha sido obtenida rigurosamente, aplicando las reglas de inferencia. Esto ilustra el concepto de derivación. El hecho de que una formula α sea
derivable a partir de un conjunto de fórmulas ∆ se escribe ∆ ` α. Si las reglas de
inferencia son consistentes (sound), siempre que ∆ ` α entonces ∆ |= α. Esto es,
si nuestra lógica es consistente, cualquier fbf que puede ser derivada de otra fbf, es
tambien una consecuencia lógica de ésta última.
Definición 8 (Consistencia y completitud) Un conjunto de reglas de inferencia se
dice consistente si, para todo conjunto de fbf cerradas (sin ocurrencia de variables
libres) ∆ y cada fbf cerrada α, siempre que ∆ ` α se tiene que ∆ |= α. Las reglas
de inferencia se dicen completas si ∆ ` α siempre que ∆ |= α.
2.6.
Substituciones
Formalmente, como ya se mencionó, una substitución es un mapeo de las variables del lenguaje a los términos del mismo:
Definición 9 (Substitución) Una substitución es un conjunto finito de pares de la
forma {X1 /t1 , . . . , Xn /tn } donde cada tn es un término y cada Xn es una variable, tal
que Xi 6= ti y Xi 6= X j si i 6= j. La substitución vacı́a se denota por ε.
Asumamos que Dom({X1 /t1 , . . . , Xn /tn }) denota al conjunto {X1 , . . . , Xn }, también conocido como dominio; y Range({X1 /t1 , . . . , Xn /tn }) denota al conjunto
{t1 , . . . ,tn }, también conocido como rango. Entonces la regla anterior expresa que
las variables en el dominio de una substitución son únicas y no incluyen la substitución de la variable por si misma.
La aplicación Xθ de la substitución θ a la variable X se define como:
t Si X/t ∈ θ
Xθ =
X En otro caso
observen que para las variables no incluidas en Dom(θ ), θ aparece como la función
identidad. Es importante extener el concepto de substitución a las fbf:
Definición 10 (Aplicación) Sea θ una substitución {X1 /t1 , . . . , Xn /tn } y α una fbf.
La aplicación αθ es la fbf obtenida al remplazar simultáneamente ti por toda ocurrencia de Xi en α (1 ≤ i ≤ n). αθ se conoce como un caso (instance) de α.
Ejemplos:
ama(X,Y ) ∧ madre(X){X/ julia,Y /luis} = ama( julia, luis) ∧ madre( julia)
p( f (X, Z), f (Y, a)) {X/a,Y /Z,W /b} = p( f (a, Z), f (Z, a))
p(X,Y ) {X/ f (Y ),Y /b} = p( f (Y ), b)
32
2 Lógica de Primer Orden
Definición 11 (Composición) Sean θ y σ dos substituciones de la forma:
θ = {X1 /s1 , . . . Xm /sm }σ = {Y1 /t1 , . . .Yn /tn }
La composición θ σ se obtiene a partir del conjunto:
{X1 /s1 σ , . . . Xm /sm σ ,Y1 /t1 , . . .Yn /tn }
de la manera siguiente: eliminar todas las Xi /si σ para las que Xi = si σ (1 ≤ i ≤ m)
y eliminar también aquellas Y j /t j para las cuales Y j ∈ Dom(θ ) (1 ≤ j ≤ n).
Por ejemplo:
{X/ f (Z),Y /W }{X/a, Z/a,W /Y } = {X/ f (a), Z/a,W /Y }
Definición 12 (Substitución idempotente) Una substitución θ se dice idempotente si θ = θ θ .
Se puede probar que una substitución θ es idempotente si y sólo si Dom(θ ) ∩
Range(θ ) = 0,
/ es decir si el dominio y el rango de la substitución son disjuntos.
Otras propiedades de las substituciones son:
Definición 13 (Propiedades de las substituciones) Sean θ , α y β substituciones y
sea F una fbf. Entonces:
E(θ α) = (Eθ )α
(θ α)β = θ (αβ )
εθ = θ ε = θ
Observen que, aunque las substituciones son asociativas, éstas no son conmutativas.
Las substituciones son importantes para definir una regla de inferencia de especial relevancia para nosotros, conocida como la regla de resolución. Con las definiciones introducidas en este capı́tulo podemos abordar el tema de los programas
lógicos definitivos.
Capı́tulo 3
Cláusulas y Programas Definitivos
Resumen La idea central de la programación lógica es usar la computadora para
obtener conclusiones a partir de descripciones declarativas, como las introducidas
en el capı́tulo anterior. Estas descripciones, llamadas programas lógicos, consisten
en un conjunto finito de fórmulas bien formadas (fbfs) de la lógica de primer orden. La idea central tiene sus raı́ces en la demostración automática de teoremas,
sin embargo, pasar de la demostración automática de teoremas experimental a la
programación lógica aplicada, requiere mejoras con respecto a la eficiencia del sistema propuesto. Tales mejoras se logran imponiendo restricciones sobre las fbfs del
lenguaje utilizado, de forma que podamos usar una poderosa regla de inferencia
conocida como principio de resolución-SLD. Este capı́tulo introduce el concepto
de cláusula y programa lógico definitivos. Más adelante se introducirá el concepto
menos restrictivo de programas generales, pero el paso por los programas definitivos es necesario para comprender las bases teóricas de Prolog. El aparato técnico
aquı́ presentado se basa principalmente en el texto de Nilsson et al. [12].
3.1.
Cláusulas definitivas
Consideremos una clase especial de enunciados declarativos del lenguaje natural,
que utilizamos para describir hechos y reglas positivos. Un enunciado de este tipo
puede especificar:
Que una relación se mantiene entre elementos del universo de discurso (hechos).
Que una relación se mantiene entre elementos del universo de discurso, si otras
relaciones se mantienen (reglas).
Consideren los siguientes enunciados en lenguaje natural:
1. Antonio es hijo de Juan.
2. Ana es hija de Antonio.
3. Juan es hijo de Marcos.
33
34
3 Cláusulas y Programas Definitivos
4. Alicia es hija de Juan.
5. El nieto de una persona es el hijo del hijo de esa persona.
Estos enunciados pueden formalizarse en dos pasos. Primero, procedemos con
las fbf atómicas que describen hechos:
1.
2.
3.
4.
hijo
hijo
hijo
hijo
de(antonio, juan)
de(ana,antonio)
de(juan,marcos)
de(alicia,juan)
El último enunciado puede aproximarse como: Para toda X e Y , X es nieto de
Y si existe alguna Z tal que Z es hijo de Y y X es hijo de Z. En lógica de primer
orden, esto se escribirı́a (observen que la implicación está invertida (←) a la usanza
de Prolog):
∀X∀Y (nieto de(X,Y ) ← ∃Z(hi jo de(Z,Y ) ∧ hi jo de(X, Z)))
Usando las equivalencias de la lógica de primer orden (en particular α ⇒ β ≡
¬α ∨ β ; y la equivalencia entre cuantificadores ∀Xα ≡ ¬∃X¬α), esta fbf puede
escribirse de diversas maneras:
∀X∀Y (nieto de(X,Y ) ∨ ¬∃Z(hi jo de(Z,Y ) ∧ hi jo de(X, Z)))
∀X∀Y (nieto de(X,Y ) ∨ ∀Z¬(hi jo de(Z,Y ) ∧ hi jo de(X, Z)))
∀X∀Y ∀Z(nieto de(X,Y ) ∨ ¬(hi jo de(Z,Y ) ∧ hi jo de(X, Z)))
∀X∀Y ∀Z(nieto de(X,Y ) ← (hi jo de(Z,Y ) ∧ hi jo de(X, Z)))
Observen que estas fbf están cerradas (no contienen variables fuera del alcance
de los cuantificadores) bajo el cuantificador universal. Además, la regla tiene la
siguiente estructura:
α0 ← α1 ∧ · · · ∧ αn (n ≥ 0)
Los bloques de construcción αi de estas fbf, se conocen como literales.
Definición 14 (Literal) Una literal es un átomo o la negación de un átomo. Una
literal positiva es un átomo. Una literal negativa es la negación de un átomo.
Un ejemplo de literal positiva es hi jo de( juan, marcos). Un ejemplo de literal
negativa es ¬hi jo de( juan, alicia). Si p y q son predicados y f es un functor, entonces p(X, alicia) y q(Y ) son literales positivas. ¬q(alicia, f (Y )) es una literal
negativa.
Definición 15 (Cláusula) Una cláusula es una disyunción finita de cero o más literales.
3.2 Programas definitivos y Metas
35
Definición 16 (Cláusula definitiva) Una cláusula se dice definitiva, si tiene exactamente una literal positiva.
α0 ∨ ¬α1 ∨ · · · ∨ ¬αn (n ≥ 0)
lo cual es equivalente a la forma general de fbf que nos interesaba:
α0 ← α1 ∧ · · · ∧ αn (n ≥ 0)
Si n = 0 tenemos por definición que la literal α0 será una literal positiva, por
lo que la cláusula definitiva toma la forma de un hecho. El cuerpo vacı́o puede
representarse por el conectivo nulo , que es verdadero en toda interpretación (por
simetrı́a también se asume un conectivo nulo 2, que es falso en toda interpretación).
Si n > 0 la cláusula definitiva toma la forma de una regla, donde α0 se conoce como
cabeza de la regla; y la conjunción α1 ∧ · · · ∧ αn se conoce como cuerpo de la regla.
El ejemplo de la relación nieto de/2 y la regla que lo define, muestra que las
cláusulas definitivas usan una forma restringida de cuantificación existencial, las
variables que ocurren sólo en el cuerpo de la cláusula están cuantificadas existencialmente en el cuerpo de la cláusula (el mismo ejemplo muestra que esto equivale
a que tales variables estén cuantificadas universalmente sobre toda la fbf).
3.2.
Programas definitivos y Metas
La definición de programa definitivo es ahora directa:
Definición 17 (Programa definitivo) Un programa definitivo es un conjunto finito
de cláusulas definitivas.
Si una cláusula tiene sólo literales negativas, estamos hablando de una meta definitiva:
Definición 18 (Meta definitiva) Una cláusula sin literales positivas es una meta
definitiva.
← α1 ∧ · · · ∧ αn (n ≥ 1)
Definición 19 (Cláusula de Horn) Una cláusula de Horn es una cláusula definitva
ó una meta definitiva.
Observen que a partir de estas definiciones, la cláusula vacı́a 2 1 es una meta
definitiva y, por lo tanto, una cláusula de Horn.
Adoptar a las cláusulas de Horn para abordar los programas y metas definitivos,
constituye una restricción. Por ejemplo, no podemos expresar p(a) ∨ p(b). Esta perdida en expresividad se ve compensada por la ganancia en tratabilidad. Debido a
su estructura restringida, las cláusulas de Horn son más fáciles de manipular que
1
En realidad, la cláusula vacı́a tiene la forma 2 ← que equivale a 2.
36
3 Cláusulas y Programas Definitivos
las cláusulas generales. En particular, esto es cierto para la deducción basada en
resolución-SLD, que resulta completa para las cláusulas de Horn.
El significado lógico de las metas puede explicarse haciéndo referencia a la fbf
equivalente cuantificada universalmente:
∀X1 . . . Xm ¬(α1 ∧ · · · ∧ αn )
donde las Xi son todas variables que ocurren en la meta. Esto es equivalente a:
¬∃X1 . . . Xm (α1 ∧ · · · ∧ αn )
Esto puede verse como una pregunta existencial que el sistema tratará de negar,
mediante la construcción de un contra ejemplo. Esto es, el sistema tratará de encontrar términos ti . . .tm tales que las fbf obtenidas a partir de α1 ∧ · · · ∧ αm al remplazar
la variable Xi por ti (1 ≤ i ≤ m) son verdaderas en todo modelo del programa. Es
decir, el sistema construirá una consecuencia lógica del programa que es un caso de
la conjunción de todas las submetas de la meta.
Al dar una meta definitiva, el usuario selecciona un conjunto de conclusiones
a ser construı́das. Este conjunto puede ser finito o infinito. El problema de como
construir tal conjunto lo veremos al tratar la resolución SLD.
Ejemplo 1 Tomando en cuenta los hechos y reglas sobre una familia presentados
al principio de esta sesión, el usuario podrı́a estar interesado en las siguientes
consultas (se muestra también la meta definitiva correspondiente):
Consulta
Meta definitiva
¿Es Ana hija de Antonio? ← hi jo(ana, antonio)
¿Quién es nieto de Ana?
← nieto(X, ana)
¿De quién es nieto Antonio? ← nieto(antonio, X)
¿Quién es nieto de quién? ← nieto(X,Y )
Las respuestas obtenidas serı́an:
Puesto que la primer meta no contiene variables, la respuesta serı́a Si (Yes).
Puesto que el programa no contiene información sobre los nietos de Ana, la
respueta a la segunda consulta es No (o ninguno).
Puesto que Antonio es nieto de Marcos, la respuesta obtenida serı́a X = marcos.
La consulta final obtiene tres respuestas: X = antonio Y = alicia, X = alicia Y =
marcos, X = ana Y = juan.
Es posible hacer consultas más elaboradas como ¿Hay alguna persona cuyos
nietos son Antonio y Alicia?
← nieto(antonio, X) ∧ nieto(alicia, X)
cuya respuesta esperada es X = marcos.
3.3 El modelo mı́nimo de Herbrand
3.3.
37
El modelo mı́nimo de Herbrand
Los programas definitivos solo pueden expresar conocimiento positivo, tanto los
hechos, como las reglas, nos dicen que elementos de una estructura están en una
relación, pero no nos dicen cuales no. Por lo tanto, al usar el lenguaje de los programas definitivos, no es posible construir descripciones contradictorias, es decir,
conjuntos de fbf no satisfacibles. En otras palabras, todo programa definitivo tiene
un modelo. Recordemos que una interpretación que hace verdadera una fbf es su
modelo:
Definición 20 (Modelo) Sea α una fbf y V una interpretación. V es un modelo de
α si |=V α.
Definición 21 Sea ∆ un conjunto finito de fbf y V una interpretación. V es un modelo de ∆ si |=V α para toda α ∈ ∆ .
Existe una clase interesante de interpretaciones, llamadas de Herbrand en honor
del francés Jacques Herbrand. En esta sección estudiaremos algunas propiedades
de los modelos de Herbrand que explican porque son útiles y necesarios en el contexto de la programación lógica. Además, los modelos de Herbrand proveen una
semántica natural para los programas definitivos.
Comenzaremos definiendo el Universo y la Base de Herbrand:
Definición 22 (Universo y Base de Herbrand) Sea L un alfabeto de primer orden
que contiene al menos un sı́mbolo de constante (|Const| ≥ 1). El Universo de Herbrand UL es el conjunto de todos los términos formados con las constantes y functores de L. La Base de Herbrand BL es el conjunto de todos los átomos que pueden
formarse con los predicados y los términos en el Universo de Herbrand UL .
El universo y la base de Herbrand se definen normalmente para un programa
dado. En ese caso, se asume que el alfabeto L consiste exactamente de aquellos
sı́mbolos que aparecen en el programa. Se asume también que el programa tiene al
menos una constante (de otra forma el dominio estarı́a vacı́o).
Ejemplo 2 Consideren el siguiente programa definitivo
∆ = {impar(s(0)), impar(s(s(X))) ← impar(X)}
Si restringimos el lenguaje L a los sı́mbolos que aparecen en este programa definitivo, tenemos que el universo de Herbrand es:
UL = {0, s(0), s(s(0)), s(s(s(0))), . . . }
Puesto que el programa sólo incluye al predicado impar, la base de Herbrand
se define como:
BL = {impar(0), impar(s(0)), impar(s(s(0))), . . . }
38
3 Cláusulas y Programas Definitivos
Ejemplo 3 Consideren este otro programa ∆ = {p(a), q(a, f (b), q(X, X) ← p(X)}.
Sea L es lenguaje de primer orden dado por los sı́mbolos en ∆ . El Universo de
Herbrand UL es el conjunto infinito:
UL = {a, b, f (a), f (b), f ( f (a)), f ( f (b)), . . . }
Y la base de Herbrand es:
BL = {p(a), p(b), q(a, b), p( f (a)), p( f (b)), q(a, f (a)), q(a, f (b)), . . . }
Lo que hace especial a una intrepretación de Herbrand es que se toma el conjunto
de todos los términos sin variables (UL ) como el dominio de la interpretación. El
mapeo de los términos a los elementos del dominio es tal que, cada término sin
variables es mapeado al elementos correspondiente en el dominio. De forma que
cada término sin variables en el lenguaje, se refiere a si mismo en el dominio.
Definición 23 (Interpretación de Herbrand) Sea L un lenguaje de primer orden.
V es una interpretación de Herbrand de L si y sólo si:
El dominio de V es UL .
Para cada constance c ∈ L, V (c) = c.
Para cada functor f /n ∈ L, se tiene un mapeo V ( f ) de ULn a UL definido por
V ( f )(t1 , . . . ,tn ) = f (t1 , . . . ,tn ).
Para cada predicado p/n ∈ L, V (p) ⊆ ULn .
La función J f mapea t1 , . . . ,tn al término f (t1 , . . . ,tn ) en el Universo de Herbrand
UL .
Definición 24 (modelo de Herbrand) Sea L un lenguaje de primer orden, ∆ un
conjunto de fbf en L, y V una interpretación de Herbrand de L. Si V es un modelo
de ∆ , se dice que es un modelo de Herbrand de ∆ .
Observen que una interpretación de Herbrand V está completamente especificada
por el conjunto de todas las α ∈ BL que son verdaderas bajo V . Podemos por lo
tanto representar cualquier interpretación de Herbrand economicamente como un
subconjunto (denotado por también V ) de BL . En otras palabras, una interpretación
de Herbrand, es un subconjunto de la Base de Herbrand.
Ejemplo 4 Consideren el programa ∆ en el ejemplo 2. Una posible interpretación
de este programa es imparV = {hs(0)i, hs(s(s(0)))i}. Una intepretación de Herbrand se puede especificar mediante una familia de tales relaciones (una por cada
sı́mbolo de predicado).
Ejemplo 5 Consideren ahora algunas interpretaciones de Herbrand de ∆ tal y como se definio en el ejemplo 3:
3.3 El modelo mı́nimo de Herbrand
39
V1 = {p(a), p(b), q(a, b, ), q(b, b)}
V2 = {p(a), q(a, a), q(a, f (b))}
V3 = {p( f ( f (a))), p(b), q(a, a), q(a, f (b))}
V4 = {p(a), p(b), q(a, a), q(b, b), q(a, f (b))}
V2 y V4 son modelos de Herbrand de ∆ = {p(a), q(a, f (b), q(X, X) ← p(X)}. V1
y V3 no lo son.
3.3.1.
Resultados concernientes a los modelos de Herbrand
Las interpretaciones y los modelos de Herbrand tienen dos propiedades atractivas. La primera es pragmática: para poder determinar si una interpretación de Herbrand V es un modelo de una fbf cuantificada universalmente ∀α, es suficiente verificar si α es verdadera en V , para todas las asignaciones posibles de las variables
de α.
La segunda razón para considerar las interpretaciones de Herbrand es más teórica. Para el lenguaje restringido de cláusulas definitivas, si queremos verificar que
una fbf atómica α es consecuencia de un programa definitivo ∆ basta con verificar
que todo modelo de Herbrand de ∆ es también un modelo de Herbrand de α.
Para entrar en detalles, es necesaria la siguiente proposición:
Proposición 1 Sea ∆ un conjunto de cláusulas en un lenguaje de primer orden L.
Entonces ∆ tiene un modelo, si y sólo si ∆ tiene un modelo de Herbrand.
La prueba de esta proposición es como sigue: Supongamos que ∆ tiene un modelo M. Si definimos una interpretación V de Herbrand tal que V (α(t1 , . . . ,tn )) define
sólo aquellas extensiones de α que son válidas en M), tenemos que V es un modelo
de Herbrand de ∆ . Dicho de otra forma, un modelo de Herbrand, es un modelo.
Observen que esta proposición es verdadera sólo para conjuntos de cláusulas.
Por ejemplo, consideren a L como un lenguaje de primer orden formado por los
sı́mbolos en ∆ = {∃X p(X), ¬p(a)}. Claramente ∆ tiene un modelo, pero este no
es un modelo de Herbrand. La razón es el dominio de la interpretación. En una
interpretación de Herbrand, el dominio es UL = {a}, y necesitamos dos constantes
al menos en UL para construir un modelo de ∆ .
Hemos mencionado la importancia del concepto de implicación lógica (o consecuencia lógica). Es común que a partir de un conjunto ∆ y una fbf γ, queremos
encontrar si ∆ |= γ. Esto es cierto si cada modelo de ∆ es también un modelo de γ.
Lo interesante viene ahora:
Proposición 2 Sea ∆ un conjunto de fbf y α una fbf. Sea S = ∆ ∪ {¬α}. Entonces
∆ |= α si y sólo si S no tiene modelo de Herbrand.
40
3 Cláusulas y Programas Definitivos
La prueba de esta proposición es como sigue: ∆ |= α si y sólo si ∆ ∪ {¬α} no
es satisfacible. Esto es, si S es no satisfacible, lo cual es cierto sólo si S no tiene
modelos y por lo tanto, no tiene modelo de Herbrand.
Lo que esta proposición nos dice es que si queremos probar que ∆ |= α, sólo
debemos considerar modelos de Herbrand de la forma S. Aunque el número de interpretaciones de Herbrand es normalmente infinito, la tarea de investigar interpretaciones de Herbrand es más tratable que la de investigar cualquier interpretación
arbitraria, puesto que nos restringimos a un dominio único definitivo por el Universo de Herbrand UL .
Observen que la base de Herbrand de un programa definitivo ∆ es siempre un
modelo de Herbrand del programa. Sin embargo, es un modelo nada interesante,
esto es, cada predicado n-ario en el programa es interpretado como la relación naria completa sobre el dominio de los terminos cerrados. ¿Qué es lo que hace a un
modelo de programa interesante? En lo que sigue demostraremos la existencia de
un modelo mı́nimo único, llamado el modelo mı́nimo de Herbrand de un programa definitivo. Luego mostraremos que este modelo contiene toda la información
positiva presente en el programa.
Los modelos de Herbrand de un programa definitivo son subconjuntos de su base
de Herbrand. Por lo tanto, la inclusión en conjuntos establece un orden natural en
tales modelos. Para poder demostrar la existencia de modelos mı́nimos con respecto
a la inclusión es suficiente demostrar que la intersección de todos los modelos de
Herbrand es también un modelo de Herbrand.
Teorema 1 (Intersección de modelos) Sea M una familia no vacı́a de modelos
de
T
Herbrand de un programa definitivo ∆ . Entonces la intersección V = M es un
modelo de Herbrand de ∆ .
La demostración es como sigue: Supongamos que V no es un modelo de ∆ . Por
lo tanto existe una una cláusula sin variables en ∆ , de la forma:
α0 ← α1 , . . . , αn (n ≥ 0)
que no es verdera en V . Esto ı́mplica que V contiene a α1 , . . . αn , pero no a α0 .
Luego, α1 , . . . , αn son miembros de toda interpretación en la familia M. Más importante aún, debe existir un modelo Vi ∈ M tal que α0 6∈ Vi , de forma que la cláusula
α0 ← α1 , . . . , αn (n ≥ 0) no es verdadera en ese Vi . Por lo tanto Vi no es un modelo
del programa ∆ , lo que contradice nuestro supuesto.
Al tomar la intersección de todos los modelos de Herbrand (se sabe que todo
programa definitivo tiene un modelo de Herbrand BL ) de un programa definitivo,
obtenemos el modelo mı́nimo de Herbrand el programa.
Ejemplo 6 Sea ∆ el programa definitivo {masculino(adan), f emenino(eva)} con
su interpretación obvia. ∆ tiene los siguientes modelos de Herbrand:
{masculino(adan), f emenino(eva)}
{masculino(adan), masculino(eva), f emenino(eva)}
3.3 El modelo mı́nimo de Herbrand
41
{masculino(adan), masculino(eva), f emenino(adan)}
{masculino(adan), masculino(eva), f emenino(eva), f emenino(adan)}
No es complicado confirmar que la intersección de estos modelos produce un
modelo de Herbrand. Sin embargo, todos los modelos salvo el primero, contienen
átomos incompatibles con el significado esperado del programa. Observen también
que la intersección de todos los modelos nos lleva a un modelo que corresponde con
el significado esperado.
Este ejemplo nos muestra la conexión entre los modelos mı́nimos de Herbrand y
el modelo intentado de un programa definitivo. Este modelo es una abstracción del
mundo a ser descrita por el programa. El mundo puede ser más rico que el modelo
mı́nimo de Herbrand. Por ejemplo hay más f ememinos que eva. Sin embargo, aquella información que no se provea explı́citamente (hechos) o implı́citamente (reglas)
no puede ser obtenida como respuesta a una meta. Las respuestas corresponden a
las consecuencias lógicas del programa.
Teorema 2 El modelo mı́nimo de Herbrand M∆ de un programa definitivo ∆ es el
conjunto de todas las consecuencias lógicas atómicas de base del programa. Esto
es: M∆ = {α ∈ B∆ |∆ |= α}.
La prueba de este teorema pasa por demostrar que M∆ ⊇ {α ∈ BL |∆ |= α} y que
M∆ ⊆ {α ∈ B∆ |∆ |= α}.
3.3.2.
Construcción del modelo mı́nimo de Herbrand
La pregunta que emerge es ¿Cómo podemos construir el modelo mı́nimo de Herbrand? o ¿Cómo puede aproximarse sucesivamente por medio de la enumeración
de sus elementos? La respuesta a estas preguntas se da mediante una aproximación
de punto fijo (el punto fijo de una función f : D → D es un elemento x ∈ D tal que
f (x) = x) a la semántica de los programas definitivos.
Un programa definitivo está compuesto de hechos y reglas. Es evidente que todos
los hechos deben incluirse en cualquier modelo de Herbrand. Si la interpretación V
no incluye el hecho α del programa ∆ , entonces V no es un modelo de Herbrand de
∆.
Ahora consideremos una regla de la forma α0 ← α1 , . . . , αn (n > 0). La regla
especifica que siempre que α1 , . . . , αn son verdaderas, también lo es α0 . Esto es,
tomando cualquier asignacion de valores θ que haga que la regla no tenga variables sin valor (α0 ← α1 , . . . , αn )θ : Si la interpretación V incluye a α1 θ , . . . αn θ ,
deberá incluir también a α0 θ para ser un modelo.
Consideren ahora el conjunto V1 de todos los hechos sin variables de el programa.
Es posible utilizar cada regla para aumentar V1 con nuevos elementos que necesariamente pertenencen a todo modelo. De modo que se obtiene un nuevo conjunto
V2 que puede usarse para generar más elementos que pertenecen a todo modelo.
42
3 Cláusulas y Programas Definitivos
El proceso se repite mientras puedan generarse nuevos elementos. Los elementos
agregados a Vi+1 son aquellos que se siguen inmediatamente de Vi .
La construcción ası́ obtenida puede formalizarse como la iteración de una transformación T∆ sobre las interpretaciones de Herbrand de un programa ∆ . La operación se llama operador de consecuencia inmediata y se define como sigue:
Definición 25 (Operador de consecuencia inmediata) Sea ground(∆ ) el conjunto de todas las cláusulas con valores asignados a todas sus variables en ∆ . T∆ es
una función sobre las interpretaciones de Herbrand de ∆ definida como sigue:
T∆ (V ) = {α0 | α0 ← α1 , . . . , αn ∈ ground(∆ ) ∧ {α1 , . . . , αn } ⊆ V }
Para los programas definitivos, se puede mostrar que existe una interpretación
mı́nima V tal que T∆ (V ) = V y que V es identica al modelo mı́nimo de Herbrand
de ∆ . Más aún, el modelo mı́nimo de Herbrand es el limite de la creciente, posiblemente infinita, secuencia de iteraciones:
0,
/ T∆ (0),
/ T∆ (T∆ (0)),
/ ...
Existe una notación estándar para denotar a los miembros de esta secuencia de
interpretaciones construı́das a partir de ∆ :
T∆ ↑ 0 = 0/
T∆ ↑ (i + 1) = T∆ (T∆ ↑ i)
T∆ ↑ n =
∞
[
T∆ ↑ i
i=0
Ejemplo 7 Tomando ∆ como el programa de impar (ej. 2, tenemos:
T∆ ↑ 0 = 0/
T∆ ↑ 1 = {impar(s(0))}
T∆ ↑ 2 = {impar(s(0)), impar(s(s(s(0))))}
..
.
T∆ ↑ m = {impar(sn (0)) | n ∈ {1, 3, 5, . . . }}
Como mencionamos, el conjunto construı́do de esta manera es identico al modelo
mı́nimo de Herbrand de ∆ .
Teorema 3 Sea ∆ un programa definitivo y V∆ su modelo mı́nimo de Herbrand.
Entonces:
V∆ es la interpretación mı́nima de Herbrand tal que T∆ (V∆ ) = V∆ .
V∆ = T∆ ↑ n.
Capı́tulo 4
Principio de Resolución
Resumen Este capı́tulo introduce el mecanismo de inferencia utilizado por la mayorı́a de los sistemas de programación lógica. Si seguimos considerando Prolog desde la perspectiva de los sistemas formales, hemos descrito ya su lenguaje y su teorı́a
de modelo; ahora describiremos su teorı́a de prueba. El mecanismo en cuestión es
un caso particular de la regla de inferencia llamada principio de resolución [15].
La idea es acotar el uso de este principio a programas definitivos, dando lugar a la
resolución-SLD [7]. Este principio constituye el fundamento de la semántica operacional de los programas definitivos. La resolución-SLD se demostrará correcta con
respecto a la teorı́a del modelo descrita en la clase anterior.
4.1.
Introducción
La programación lógica concierne el uso de la lógica (restringida a cláusulas)
para representar y resolver problemas. Este uso es ampliamente aceptado en Inteligencia Artificial (IA), donde la idea se resume como sigue: Un problema o sujeto
de investigación puede describirse mediante un conjunto de fórmulas bien formadas
(fbf), de preferencia en forma de cláusulas. Si tal descripción es lo suficientemente
precisa, la solución al problema o la respuesta a la pregunta planteada en la investigación, es una consecuencia lógica del conjunto de fbf que describen el problema.
Por lo tanto, encontrar que fbf φ son consecuencia lógica de un conjunto de fbf ∆ ,
es crucial para muchas áreas de la IA, incluyendo la programación lógica. De forma
que nos gustarı́a tener un procedimiento, algorı́tmico, que nos permita establecer si
∆ |= φ es el caso, o no. Este es el tema del presente capı́tulo: un método decidible
conocido como principio de resolución [15].
En el caso de la lógica proposicional, la implicación lógica es decidible, es decir,
existe un algoritmo que puede resolver el problema (contestar si ó no para cada caso
particular ∆ |= φ ). Si n es el número de átomos distintos que ocurren en estas fbf,
el número de interpretaciones posibles es finito, de hecho es 2n . Un algoritmo para
43
44
4 Principio de Resolución
computar ∆ |= φ simplemente busca si φ es verdadero en todos los modelos de ∆ .
¿Qué sucede en el contexto de la lógica de primer orden?
La intuición nos dice que el procedimiento de decisión de la lógica proposicional
no es adecuado en primer orden, pues en este caso podemos tener una cantidad
infinita de dominios e interpretaciones diferentes. Lo que es peor, el teorema de
Church [2, 19], muestra que la lógica de primer orden es indecidible:
Teorema 4 (Church) El problema de si ∆ |= φ , cuando ∆ es un conjunto finito
arbitrario de fbf, y φ es una fbf arbitraria, es indecidible.
Observen que el problema es indecidible para conjuntos arbitrarios de fbf y para
una fbf φ arbitraria. No existe un algoritmo que en un número finito de pasos, de la
respuesta correcta a la pregunta ¿Es φ una consecuencia lógica de ∆ ?
Existen, sin embargo, procedimientos conocidos como procedimientos de prueba que pueden ser de gran ayuda para computar este problema. La idea es que
cuando es el caso que ∆ |= φ , existen procedimientos que pueden verificarlo en
un número finito de pasos. Por ello suele decirse que la lógica de primer orden es
semi-decidible. Aunque parecerı́a trivial, siendo que ∆ |= φ , preguntar ¿∆ |= φ ?, en
realidad tal trivialidad es aparente. Podemos hacer la pregunta al procedimiento sin
que nosotros sepamos que ese es el caso, y obtendremos una respuesta en un número
finito de pasos. Pero si es el caso que ∆ 6|= φ obtendremos la respuesta “no” (en el
mejor de los casos) o el procedimiento no terminará nunca. Esto es infortunado y,
peor aún, inevitable.
Esta sesión introduce el procedimiento de prueba utilizado ampliamente en la
programación lógica: el principio de resolución propuesto por J.A. Robinson [15]. Si
bien este procedimiento está orientado a un lenguaje más expresivo que los programas lógicos definitivos, nosotros nos concentraremos en una versión del principio
que aplica a programas definidos y se conoce como resolución-SLD [7] (resolución
lineal con función de selección para cláusulas definitivas).
4.2.
¿Qué es un procedimiento de prueba?
Hasta este momento, hemos abordado informalmente el concepto de procedimiento de prueba como la manera de generar la prueba de que una fbf φ es consecuencia lógica de un conjunto de fbf ∆ . Las fbf en ∆ se conocen como premisas y
φ es la conclusión de la prueba.
La prueba suele consistir de un pequeño número de transformaciones en los cuales nuevas fbf son derivadas de las premisas y de fbf previamente derivadas. Derivar
una fbf implica construirla a partir de las premisas y otras fbf derivadas, siguiendo alguna regla de inferencia. Toda regla de inferencia formaliza alguna forma
natural de razonamiento. Por ejemplo, el modus ponens es usado comúnmente en
matemáticas, su expresión es:
4.3 Pruebas y programas lógicos
45
φ, φ → ψ
ψ
donde la lı́nea superior expresa las premisas y la lı́nea inferior la conclusión.
Es posible ligar varias aplicaciones del modus ponens para construir una prueba.
Por ejemplo, si tenemos el programa lógico ∆ = {p(a), q(b) ← p(a), r(b) ← q(b)}
es posible derivar la fbf r(b) como sigue:
1. Derivar q(b) a partir de p(a) y q(b) ← p(a).
2. Derivar r(b) a partir de q(b) y r(b) ← q(b).
La secuencia anterior es una prueba de que r(b) puede ser derivada de ∆ .
Es evidente que si usamos modus ponens, la conclusión ψ es una consecuencia
lógica de las premisas: {φ , φ → ψ} |= ψ. A esta propiedad del modus ponens se le
conoce como consistencia (soundness). En general un procedimiento de prueba es
consistente si todas las fbf ψ que pueden ser derivadas de algún conjunto de fbfs ∆
usando el procedimiento, son consecuencias lógicas de ∆ . En otras palabras, un procedimiento de prueba es consistente si y sólo si sólo permite derivar consecuencias
lógicas de las premisas.
Una segunda propiedad deseable de los procedimientos de prueba es su completez. Un procedimiento de prueba es completo si toda fbf que es una consecuencia
lógica de las premisas ∆ , puede ser derivada usando el procedimiento en cuestión. El
modus ponens por si mismo, no es completo. Por ejemplo, no existe secuencia alguna de aplicaciones del modus ponens que deriven la fbf p(a) de ∆ = {p(a) ∧ p(b)},
cuando es evidente que ∆ |= p(a).
La regla ψφ es completa, pero no válida. !Nos permite extraer cualquier conclusión, a partir de cualquier premisa! Esto ejemplifica que obtener completitud es
sencillo, pero obtener completitud y correctez, no lo es.
4.3.
Pruebas y programas lógicos
Recordemos que los enunciados en los programas lógicos tienen la estructura
general de la implicación lógica:
α0 ← α1 , . . . , αn (n ≥ 0)
donde α0 , . . . , αn son fbfs atómicas y α0 puede estar ausente (para representar
cláusulas meta). Consideren el siguiente programa definitivo ∆ que describe un
mundo donde los padres de un recién nacido están orgullosos, Juan es el padre de
Marta y Marta es una recién nacida:
46
4 Principio de Resolución
orgulloso(X) ← padre(X,Y ), recien nacido(Y ).
padre(X,Y ) ← papa(X,Y ).
padre(X,Y ) ← mama(X,Y ).
papa( juan, marta).
recien nacido(marta).
Observen que el programa describe únicamente conocimiento positivo, es decir,
no especifica quién no está orgulloso. Tampoco que significa para alguien no ser
padre.
Supongamos que deseamos contestar la pregunta ¿Quién está orgulloso? Esta
pregunta concierne al mundo descrito por nuestro programa, esto es, concierne al
modelo previsto para ∆ . La respuesta que esperamos es, por supuesto, juan. Ahora,
recuerden que la lógica de primer orden no nos permite expresar enunciados interrogativos, por lo que nuestra pregunta debe formalizarse como una cláusula meta
(enunciado declarativo):
← orgulloso(Z).
que es una abreviatura de ∀Z¬orgulloso(Z) (una cláusula definitiva sin cabeza), que
a su vez es equivalente de:
¬∃Z orgulloso(Z).
cuya lectura es “Nadie está orgulloso”, esto es, la respuesta negativa a la consulta
original – ¿Quién está orgulloso? La meta ahora es probar que este enunciado es
falso en todo modelo del programa ∆ y en particular, es falso en el modelo previsto
para ∆ , puesto que esto es una forma de probar que ∆ |= ∃Z orgulloso(Z). En general para todo conjunto de fbf cerradas ∆ y una fbf cerrada γ, tenemos que ∆ |= γ
si ∆ ∪ {¬γ} es no satisfacerle (no tiene modelo).
Por lo tanto, nuestro objetivo es encontrar una substitución θ tal que el conjunto ∆ ∪ {¬orgulloso(Z)θ } sea no satisfacerle, o de manera equivalente, ∆ |=
∃Z orgulloso(Z)θ .
El punto inicial de nuestro razonamiento es asumir la meta G0 – Para cualquier
Z, Z no está orgulloso. La inspección del programa ∆ revela que una regla describe
una condición para que alguien esté orgulloso:
orgulloso(X) ← padre(X,Y ), recien nacido(Y ).
lo cual es lógicamente equivalente a:
∀(¬orgulloso(X) ⇒ ¬(padre(X,Y ) ∧ recien nacido(Y )))
Al renombrar X por Z, eliminar el cuantificador universal y usar modus ponens
con respecto a G0 , obtenemos:
4.3 Pruebas y programas lógicos
47
¬(padre(Z,Y ) ∧ recien nacido(Y ))
o su equivalente:
← padre(Z,Y ), recien nacido(Y ).
al que identificaremos como G1 . Un paso en nuestro razonamiento resulta en remplazar la meta G0 por la meta G1 que es verdadera en todo modelo ∆ ∪ {G0 }. Ahora
solo queda probar que ∆ ∪ {G1 } es no satisfacible. Observen que G1 es equivalente
a la fbf:
∀Z∀Y (¬padre(Z,Y ) ∨ ¬recien nacido(Y ))
Por lo tanto, puede probarse que la meta G1 es no satisfacible para ∆ , si en
todo modelo de ∆ hay una persona que es padre de un recién nacido. Entonces,
verificamos primero si hay padres con estas condiciones. El programa contiene la
cláusula:
padre(X,Y ) ← papa(X,Y ).
que es equivalente a:
∀(¬padre(X,Y ) ⇒ ¬papa(X,Y ))
por lo que G1 se reduce a:
← papa(Z,Y ), recien nacido(Y ).
que identificaremos como G2 . Se puede mostrar que no es posible satisfacer la nueva
meta G2 con el programa ∆ , si en todo modelo de ∆ hay una persona que es papá de
un recién nacido. El programa declara que juan es padre de marta:
papa( juan, marta).
ası́ que sólo resta probar que “marta no es una recién nacida” no se puede satisfacer
junto con ∆ :
← recien nacido(marta).
pero el programa contiene el hecho:
recien nacido(marta).
equivalente a ¬recien nacido(marta) ⇒ f lo que conduce a una refutación.
Este razonamiento puede resumirse de la siguiente manera: para probar la existencia de algo, suponer lo contrario y usar modus ponens y la regla de eliminación
del cuantificador universal, para encontrar un contra ejemplo al supuesto.
Observen que la meta definitiva fue convertida en un conjunto de átomos a ser
probados. Para ello, se seleccionó una fbf atómica de la meta p(s1 , . . . , sn ) y una
48
4 Principio de Resolución
cláusula de la forma p(t1 , . . . ,tn ) ← A1 , . . . An para encontrar una instancia común de
p(s1 , . . . , sn ) y p(t1 , . . . ,tn ), es decir, una substitución θ que hace que p(s1 , . . . , sn )θ
y p(t1 , . . . ,tn )θ sean idénticos. Tal substitución se conoce como unificador. La nueva meta se construye remplazando el átomo seleccionado en la meta original, por
los átomos de la cláusula seleccionada, aplicando θ a todos los átomos obtenidos de
esta manera.
El paso de computación básico de nuestro ejemplo, puede verse como una regla
de inferencia puesto que transforma fórmulas lógicas. Lo llamaremos principio de
resolución SLD para programas definitivos. Como mencionamos, el procedimiento
combina modus ponens, eliminación del cuantificador universal y en el paso final
un reductio ad absurdum.
Cada paso de razonamiento produce una substitución, si se prueba en k pasos que
la meta definida en cuestión no puede satisfacerse, probamos que:
← (A1 , . . . Am )θ1 . . . θk
es una instancia que no puede satisfacerse. De manera equivalente, que:
∆ |= (A1 ∧ · · · ∧ Am )θ1 . . . θk
Observen que generalmente, la computación de estos pasos de razonamiento no
es determinista: cualquier átomo de la meta puede ser seleccionado y pueden haber
varias cláusulas del programa que unifiquen con el átomo seleccionado. Otra fuente
de indeterminismo es la existencia de unificadores alternativos para dos átomos. Esto sugiere que es posible construir muchas soluciones (algunas veces, una cantidad
infinita de ellas).
Por otra parte, es posible también que el atomo seleccionado no unifique con
ninguna cláusula en el programa. Esto indica que no es posible construir un contra
ejemplo para la meta definida inicial. Finalmente, la computación puede caer en un
ciclo y de esta manera no producir solución alguna.
4.4.
Substitución
Una substitución remplaza variables por términos, por ejemplo, podemos remplazar la variable X por el término f (a) en la cláusula p(X) ∨ q(X), y ası́ obtener la
nueva cláusula p( f (a))∨q( f (a)). Si asumimos que las cláusulas están cuantificadas
universalmente, decimos que está substitución hace a la cláusula original, “menos
general”. Mientras que la cláusula original dice que V (p(X)) = t y que V (q(X)) = t
para cualquier X en el dominio, la segunda cláusula dice que esto sólo es cierto
cuando cuando V (X) = f (a). Observen que la segunda cláusula es consecuencia
lógia de la primera: p(X) ∨ q(X) |= p( f (a)) ∨ q( f (a))
Definición 26 (Substitución) Una substitución θ es un conjunto finito de la forma:
{X1 /t1 , . . . , Xn /tn }, (n ≥ 0)
4.4 Substitución
49
donde las Xi son variables, distintas entre si, y los ti son términos. Decimos que
ti substituye a Xi . La forma Xi /ti se conoce como ligadura de Xi . La substitución θ
se dice se dice de base (grounded) si cada término ti es un término base (no incluye
variables)..
La substitución dada por el conjunto vacı́o, se conoce como substitución de
identidad o substitución vacı́a y se denota por ε. La restricción de θ sobre un
conjunto de variables Var es la substitucion {X/t ∈ θ | X ∈ Var}.
Ejemplo 8 {Y /X, X/g(X,Y )} y {X/a,Y / f (Z), Z/( f (a), X1 /b} son substituciones.
La restricción de la segunda substitución sobre {X, Z} es {X/a, Z/ f (a)}.
Definición 27 (Expresión) Una expresión es un término, una literal, o una conjunción o disyunción de literales. Una expresión simple es un término o una literal.
Observen que una cláusula es una expresión. Las substituciones pueden aplicarse
a las expresiones, lo que significa que las variables en las expresiones serán remplazadas de acuerdo a la substitución.
Definición 28 Sea θ = {X1 /t1 , . . . , Xn /tn } una substitución y α una expresión. Entonces αθ , la ocurrencia (instance) de α por θ , es la expresión obtenida al substituir simultáneamente Xi por ti para 1 ≤ i ≤ n. Si αθ es una expresión de base, se
dice que es una ocurrencia base y se dice que θ es una substitución de base para
α. Si Σ = {α1 , . . . , αn } es un conjunto finito de expresiones, entonces Σ θ denota
{α1 θ , . . . , αn θ }.
Ejemplo 9 Sea α la expresión p(Y, f (X)) y sea θ la substitución {X/a,Y /g(g(X))}.
La ocurrencia de α por θ es αθ = p(g(g(X)), f (a). Observen que X e Y son simultáneamente remplazados por sus respectivos términos, lo que implica que X en
g(g(X)) no es afectada por X/a.
Si α es una expresión cerrada que no es un término, por ejemplo, una literal, o
una conjunción o disyunción de literales, y θ es una substitución, lo siguiente se
cumple:
α |= αθ
por ejemplo: p(X) ∨ ¬q(Y ) |= p(a) ∨ ¬q(Y ) donde hemos usado la substitución
{X/a}.
Podemos aplicar una substitución θ y luego aplicar una substitución σ , a lo cual
se llama composición de las substituciones θ y σ . Si ese es el caso, primero se
aplica θ y luego σ . Las composiciones pueden verse como mapeos del conjunto de
variables en el lenguaje, al conjunto de términos.
Definición 29 (Composición) Sean θ = {X1 /s1 , . . . , Xm /sm } y σ = {Y1 /t1 , . . .Yn /tn }
dos substituciones. Consideren la secuencia:
X1 /(s1 σ ), . . . , Xm /(sm σ ),Y1 /t1 , . . . ,Yn /tn
50
4 Principio de Resolución
Si se borran de esta sencuencia las ligaduras Xi /si σ cuando Xi = si σ y cualquier
ligadura Y j /t j donde Y j ∈ {X1 , . . . , Xm }. La substitución consistente en las ligaduras
de la secuencia resultante es llamada composición de θ y σ , se denota por θ σ .
Ejemplo 10 Sea θ = {X/ f (Y ), Z/U} y σ = {Y /b,U/Z}. Construimos la secuencia de ligaduras X/( f (Y )σ ), Z/(u)σ ,Y /b,U/Z lo cual es X/ f (b), Z/Z,Y /b,U/Z.
Al borrar la ligadura Z/Z obtenemos la secuencia X/ f (b),Y /b,U/Z = θ σ .
Definición 30 (Ocurrencia) Sean θ y σ dos substituciones. Se dice que θ es una
ocurrencia de σ , si existe una substitución γ, tal que σ γ = θ .
Ejemplo 11 La substitución θ = {X/ f (b),Y /a} es una ocurrencia de la substitución σ = {X/ f (X),Y /a}, puesto que σ {X/b} = θ .
Algunas propiedades sobre las substituciones incluyen:
Proposición 3 Sea α una expresión, y sea θ , σ y γ substituciones. Las siguientes
relaciones se cumplen:
1. θ = θ ε = εθ
2. (αθ )σ = α(θ σ )
3. θ σ )γ = θ (σ γ)
4.5.
Unificación
Uno de los pasos principales en el ejemplo de la sección 4.3, consistió en hacer que dos fbf atómicas se vuelvan sintácticamente equivalentes. Este proceso se
conoce como unificación y posee una solución algorı́tmica.
Definición 31 (Unificador) Sean α y β términos. Una substitución θ tal que α y
β sean idénticos (αθ = β θ ) es llamada unificador de α y β .
Ejemplo 12
uni f ica(conoce( juan, X), conoce( juan, maria)) = {X/maria}
uni f ica(conoce( juan, X), conoce(Y, Z)) = {Y / juan, X/Z}
= {Y / juan, X/Z,W /pedro}
= {Y / juan, X/ juan, Z/ juan}
Definición 32 (Generalidad entre substituciones) Una substitución θ se dice más
general que una substitución σ , si y sólo si existe una substitución γ tal que σ = θ γ.
Definición 33 (MGU) Un unificador θ se dice el unificador más general (MGU)
de dos términos, si y sólo si θ es más general que cualquier otro unificador entre
esos términos.
4.5 Unificación
51
Definición 34 (Forma resuelta) Un conjunto de ecuaciones {X1 = t1 , . . . , Xn = tn }
está en forma resuelta, si y sólo si X1 , . . . , Xn son variables distintas que no ocurren
en t1 , . . . ,tn .
Existe una relación cercana entre un conjunto de ecuaciones en forma resuelta y
el unificador más general de ese conjunto: Sea {X1 = t1 , . . . , Xn = tn } un conjunto de
ecuaciones en forma resuelta. Entonces {X1 /t1 , . . . , Xn /tn } es un MGU idempotente
de la forma resuelta.
Definición 35 (Equivalencia en conjuntos de ecuaciones) Dos conjuntos de ecuaciones E1 y E2 se dicen equivalentes, si tienen el mismo conjunto de unificadores.
La definición puede usarse como sigue: para computar el MGU de dos términos
α y β , primero intente transformar la ecuación {α = β } en una forma resuelta
equivalente. Si esto falla, entonces mgu(α, β ) = f allo. Sin embargo, si una forma
resuelta {X1 = t1 , . . . , Xn = tn } existe, entonces mgu(α, β ) = {X1 /t1 , . . . , Xn /tn }. Un
algoritmo para encontrar la forma resuelta de un conjunto de ecuaciones es como
sigue:
Algoritmo 1 Unifica(E)
1: function U NIFICA(E)
. E es un conjunto de ecuaciones
2:
repeat
3:
(s = t) ← seleccionar(E)
4:
if f (s1 , . . . , sn ) = f (t1 , . . . ,tn ) (n ≥ 0) then
5:
remplazar (s = t) por s1 = t1 , . . . , sn = tn
6:
else if f (s1 , . . . , sm ) = g(t1 , . . . ,tn ) ( f /m 6= g/n) then
7:
return(fallo)
8:
else if X = X then
9:
remover la X = X
10:
else if t = X then
11:
remplazar t = X por X = t
12:
else if X = t then
13:
if subtermino(X,t) then
14:
return(fallo)
15:
else remplazar todo X por t
16:
end if
17:
end if
18:
until No hay accion posible para E
19: end function
Ejemplo 13 El conjunto { f (X, g(Y )) = f (g(Z), Z)} tiene una forma resuelta, puesto que:
⇒ {X = g(Z), g(Y ) = Z}
⇒ {X = g(Z), Z = g(Y )}
⇒ {X = g(g(Y )), Z = g(Y )}
52
4 Principio de Resolución
Ejemplo 14 El conjunto { f (X, g(X), b) = f (a, g(Z), Z)} no tiene forma resuelta,
puesto que:
⇒ {X = a, g(X) = g(Z), b = Z}
⇒ {X = a, g(a) = g(Z), b = Z}
⇒ {X = a, a = Z, b = Z}
⇒ {X = a, Z = a, b = Z}
⇒ {X = a, Z = a, b = a}
⇒ f allo
Ejemplo 15 El conjunto { f (X, g(X)) = f (Z, Z)} no tiene forma resuelta, puesto
que:
⇒ {X = Z, g(X) = Z}
⇒ {X = Z, g(Z) = Z}
⇒ {X = Z, Z = g(Z)}
⇒ f allo
Este algoritmo termina y regresa una forma resuelta equivalente al conjunto
de ecuaciones de su entrada; o bien regresa fallo si la forma resuelta no existe.
Sin embargo, el computar subtermino(X,t) (verificación de ocurrencia) hace que
el algoritmo sea altamente ineficiente. Los sistemas Prolog resuelven este problema haciéndo caso omiso de la verificación de ocurrencia. El standard ISO Prolog
(1995) declara que el resultado de la unificación es no decidible. Al eliminar la verificación de ocurrencia es posible que al intentar resolver X = f (X) obtengamos
X = f ( f (X)) · · · = f ( f ( f . . . )). En la practica los sistemas Prolog no caen en este
ciclo, pero realizan la siguiente substitución {X/ f (∞)}. Si bien esto parece resolver
el problema de eficiencia, generaliza el concepto de término, substitución y unificación al caso del infinito, no considerado en la lógica de primer orden, introduciéndo
a su vez inconsistencia.
4.6.
Resolución-SLD
El método de razonamiento descrito informalmente al inicio de esta sesión, puede
resumirse con la siguiente regla de inferencia:
∀¬(α1 ∧ · · · ∧ αi−1 ∧ αi ∧ αi+1 ∧ · · · ∧ αm ) ∀(β0 ← β1 ∧ · · · ∧ βn )
∀¬(α1 ∧ · · · ∧ αi−1 ∧ β1 ∧ · · · ∧ βn ∧ αi+1 ∧ · · · ∧ αm )θ
o, de manera equivalente, usando la notación de los programas definitivos:
4.6 Resolución-SLD
53
← α1 , . . . , αi−1 , αi , αi+1 , . . . , αm β0 ← β1 , . . . , βn
← (α1 , . . . , αi−1 , β1 , . . . , βn , . . . , αm )θ
donde:
1. α1 , . . . , αm son fbf atómicas.
2. β0 ← β1 , . . . , βn es una cláusula definitiva en el programa ∆ (n ≥ 0).
3. MGU(αi , β0 ) = θ .
La regla tiene dos premisas: una meta y una cláusula definitivas. Observen que
cada una de ellas está cuantificada universalmente, por lo que el alcance de los
cuantificadores es disjunto. Por otra parte, solo hay un cuantificador universal para
la conclusión, por lo que se requiere que el conjunto de variables en las premisas
sea disjunto. Puesto que todas las variables en las premisas están cuantificadas, es
siempre posible renombrar las variables de la cláusula definitiva para cumplir con
esta condición.
La meta definida puede incluir muchas fbf atómicas que unifican con la cabeza de
alguna cláusula en el programa. En este caso, es deseable contar con un mecanismo
determinista para seleccionar un átomo αi a unificar. Se asume una función que
selecciona una submeta de la meta definida (función de selección).
La regla de inferencia presentada es la única necesaria para procesar programas
definitivos. Esta regla es una versión de la regla de inferencia conocida como principio de resolución, introducido por J.A. Robinson en 1965. El principio de resolución aplica a cláusulas. Puesto que las cláusulas definitivas son más restringidas que
las cláusulas, la forma de resolución presentada se conoce como resolución-SLD
(resolución lineal para cláusulas definitivas con función de selección).
El punto de partida de la aplicación de esta regla de inferencia es una meta definida G0 :
← α1 , . . . , αm (m ≥ 0)
De esta meta, una submeta αi será seleccionada, de preferencia por una función
de selección. Una nueva meta G1 se construye al seleccionar una cláusula del programa β0 ← β1 , . . . , βn (n ≥ 0) cuya cabeza β0 unifica con αi , resultando en θ1 . G1
tiene la forma:
← (α1 , . . . , αi−1 , β1 , . . . , βn , . . . , αm )θ1
Ahora es posible aplicar el principio de resolución a G1 para obtener G2 , y ası́ sucesivamente. El proceso puede terminar o no. Hay dos situaciones donde no es posible obtener Gi+1 a partir de Gi :
1. cuando la submeta seleccionada no puede ser resuelta (no es unificable con la
cabeza de una cláusula del programa).
2. cuando Gi = 2 (meta vacı́a = f).
Definición 36 (Derivación-SLD) Sea G0 una meta definitiva, ∆ un programa definitivo y R una función de selección. Una derivación SLD de G0 (usando ∆ y R) es
una secuencia finita o infinita de metas:
54
4 Principio de Resolución
G0
α0
G1 . . . Gn−1
αn−1
Gn
Para manejar de manera consistente el renombrado de variables, las variables en
una cláusula αi serán renombradas poniéndoles subı́ndice i.
Cada derivación SLD nos lleva a una secuencias de MGUs θ1 , . . . , θn . La composición
(
θ1 θ2 . . . θn si n > 0
θ=
ε
si n = 0
de MGUs se conoce como la substitución computada de la derivación.
Ejemplo 16 Consideren la meta definida ← orgulloso(Z) y el programa discutido
en la clase anterior.
G0 =← orgulloso(Z).
α0 = orgulloso(X0 ) ← padre(X0 ,Y0 ), recien nacido(Y0 ).
La unificación de orgulloso(Z) y orgulloso(X0 ) nos da el MGU θ1 = {X0 /Z}.
Asumamos que nuestra función de selección es tomar la submeta más a la izquierda.
El primer paso de la derivación nos conduce a:
G1 =← padre(Z,Y0 ), recien nacido(Y0 ).
α1 = padre(X1 ,Y1 ) ← papa(X1 ,Y1 ).
En el segundo paso de la resolución el MGU θ2 = {X1 /Z,Y1 /Y0 } es obtenido. La
derivación continua como sigue:
G2 =← papa(Z,Y0 ), recien nacido(Y0 ).
α2 = papa( juan, marta).
G3 =← recien nacido(marta).
α3 = recien nacido(marta).
G4 = 2
la substitución computada para esta derivación es:
θ1 θ2 θ3 θ4 = {X0 /Z}{X1 /Z,Y1 /Y0 }{Z/ juan,Y0 /marta}ε
= {X0 / juan, X1 / juan,Y1 /marta, Z/ juan,Y0 /marta}
Las derivaciones SLD que terminan en la meta vacı́a (2) son de especial importancia pues corresponden a refutaciones a la meta inicial (y proveen las respuestas a
la meta).
Definición 37 (Refutación SLD) Una derivación SLD finita:
4.6 Resolución-SLD
55
G0
α0
G1 . . . Gn−1
αn−1
Gn
donde Gn = 2, se llama refutación SLD de G0 .
Definición 38 (Derivación fallida) Una derivación de la meta definitiva G0 cuyo
último elemento no es la meta vacı́a y no puede resolverse con ninguna cláusula del
programa, es llamada derivación fallida.
Definición 39 (Arbol-SLD) Sea ∆ un programa definitivo, G0 una meta definitiva,
y R una función de selección. El árbol-SLD de G0 (usando ∆ y R) es un árbol
etiquetado, posiblemente infinito, que cumple las siguientes condiciones:
La raı́z del árbol está etiquetada por G0 .
Si el árbol contiene un nodo etiquetado como Gi y existe una cláusula renombrada αi ∈ ∆ tal que Gi+1 es dervidada de Gi y αi via R, entonces el nodo
etiquetado como Gi tiene un hijo etiquetado Gi+1 El arco que conecta ambos
nodos está etiquetado como αi .
Por ejemplo:
← orgulloso(Z)
← padre(Z,Y0 ), recien nacido(Y0 )
← papa(Z,Y0 ), recien nacido(Y0 )
← mama(Z,Y0 ), recien nacido(Y0 )
← recien nacido(marta)
2
4.6.1.
Propiedades de la resolución-SLD
Definición 40 (Consistencia) Sea ∆ un programa definitivo, R una función de selección, y θ una substitución de respuesta computada a partir de ∆ y R para una
meta ← α1 , . . . , αm . Entonces ∀((α1 ∧ · · · ∧ αm )θ ) es una consecuencia lógica del
programa ∆ .
Definición 41 (Compleción) Sea ∆ un programa definitivo, R una función de selección y ← α1 , . . . , αm una meta definitiva. Si ∆ |= ∀((α1 ∧ · · · ∧ αm )σ ), entonces
existe una refutación de ← α1 , . . . , αm vı́a R con una substitución de respuesta
computada θ , tal que (α1 ∧ · · · ∧ αm )σ es un caso de (α1 ∧ · · · ∧ αm )θ .
Capı́tulo 5
Negación
Resumen Si los programas definitivos representan únicamente conocimiento positivo sobre un problema ¿Cómo es que se pueden computar consecuencias lógicas
negativas? Este capı́tulo introduce los conceptos de supuesto del mundo cerrado
(CWA) y su forma más relajada, conocida como negación por fallo finito (NAF),
para introducir el manejo de conocimiento negativo en los programas definitivos. Se
introducen también los conceptos de compleción de programa y resolución-SLDNF.
Finalmente abordaremos el concepto de programas generales y la resolución para
este tipo de programas.
5.1.
Introducción
Los programas definitivos expresan conocimiento positivo, en el sentido que los
hechos y las reglas describen que ciertos objetos están en cierta relación con otros.
Las relaciones se hacen explı́citas en el modelo mı́nimo de Herbrand – el conjunto
de todas las consecuencias atómicas de base de un programa. Por ejemplo, consideren el siguiente programa ∆ :
sobre(X,Y ) ← en(X,Y ).
sobre(X,Y ) ← en(X, Z), sobre(Z,Y ).
en(c, b).
en(b, a).
El modelo mı́nimo de Herbrand para este programa es el siguiente:
{en(b, a), en(c, b), sobre(b, a), sobre(c, b), sobre(c, a)}
Observen que ni el programa, ni el modelo mı́nimo de Herbrand, incluyen información negativa del tipo: a no está sobre b, o b no está sobre c. Sin embargo,
57
58
5 Negación
nosotros usamos información negativa implı́cita en algunos casos. Por ejemplo, si
cuando buscamos un boleto de autobús, nos encontramos con que no aparecen salidas a México a las 10:12 am, asumimos que tal salida no existe. La ausencia de
información se asume como evidencia de lo contrario. Esto es posible porque, como
en el caso de autobús, asumimos que toda la información disponible está a nuestro
alcance.
La idea anterior se puede formular con la suposición del mundo cerrado
(Closed-World Assumption) ó CWA, una pseudo-regla de inferencia que expresa:
∆ 6` α
(CWA)
¬α
Si una fbf atómica de base (sin variables) α, no puede derivarse del programa ∆
siguiendo las reglas de inferencia del sistema, entonces puede derivarse ¬α.
En el caso de los sistemas correctos y completos, la condición ∆ 6` α es equivalente a ∆ 6|= α. Como este es el caso para la resolución-SLD, la condición puede ser
remplazada por α 6∈ M∆ . Por ejemplo, la fbf sobre(b, c) no puede ser derivada por resolución-SLD a partir del programa ∆ (vean el árbol de derivación en la figura 5.1).
En realidad sobre(b, c) no puede ser derivada por ningún sistema correcto, puesto
que no es una consecuencia lógica de ∆ . Dada la completitud de la resolución-SLD,
se sigue que ∆ 6|= sobre(b, c) y usando la CWA inferimos que ¬sobre(b, c).
← sobre(b, c)
← en(b, c)
← en(b, Z0 ), sobre(Z0 , c)
← sobre(a, c)
← en(a, c)
← en(a, Z2 ), sobre(Z2 , c)
Figura 5.1 Árbol de derivación-SLD fallido
En contra de lo que podrı́a ser nuestra primera intuición, existen problemas asociados a la CWA. El principal tiene que ver con que la no-derivabilidad para los
programas definitivos es no decidible en el caso general. Esto es, no es posible determinar si la pseudo-regla asociada al CWA aplica o no. Una versión más débil de
la suposición de mundo cerrado, se logra si asumimos que ¬α es derivable a partir
del programa ∆ si la meta ← α tiene un árbol-SLD finito que falla. A esta regla se
le conoce como negación como falla (finita) (NAF).
Es necesario contrastar la NAF con la CWA, que también puede verse como
una negación por falla, pero infinita. Para ilustrar la diferencia entre los dos enfo-
5.2 La compleción de un programa
59
ques extendamos el programa ∆ con la siguiente cláusula evidentemente verdadera
sobre(X,Y ) ← sobre(X,Y ).
El árbol-SLD de la meta ← sobre(b, c) sigue sin contener refutaciones, pero ahora es infinito. Por lo tanto no podemos concluir que ¬sobre(b, c) usando NAF, pero
si usando CWA.
Pero el problema más serio con estos enfoques es que son incorrectos, ¬sobre(b, c)
no es una consecuencia lógica del programa ∆ . En lo general, cualquier sistema que
permita inferir literales negativas a partir de un programa definitivo, es incorrecto. La razón es que la base de Herbrand del programa B∆ , en el cual todas las fbf
atómicas cerradas son verdaderas, es siempre un modelo de ∆ .
Existen dos aproximaciones a la solución de estos problemas: ver los programas
como resúmenes de programas más extensos que validan las literales negativas; o
redefinir la noción de consecuencia lógica de forma que sólo algunos modelos del
programa (el mı́nimo de Herbrand, por ejemplo) sean tomados en cuenta. En ambos
casos, el efecto es descartar algunos modelos del programa que no son interesantes.
Primero justificaremos la regla NAF en términos de la compleción de los programas
definitivos y posteriormente, extenderemos el lenguaje de los programas definitivos
para incluir en ellos literales negativas en la cabeza y cuerpo de las cláusulas.
5.2.
La compleción de un programa
La idea que presentaremos a continuación se debe a K. Clark [3] y se basa en que
cuando uno escribe un programa definitivo ∆ , en realidad quiere expresar algo más
que su conjunto de cláusulas definitivas. El programa deseado puede formalizarse
como la compleción de ∆ . Consideren la siguiente definición:
sobre(X,Y ) ← en(X,Y ).
sobre(X,Y ) ← en(X, Z), sobre(Z,Y ).
Estas reglas especifican que un objeto está sobre un segundo objeto, si el primer
objeto está encima del segundo (1) ó si el objeto está sobre otro objeto que a su vez
está encima del segundo (2). Esto también puede escribirse como:
sobre(X,Y ) ← en(X,Y ) ∨ (en(X, Z), sobre(Z,Y ))
Ahora, ¿Qué sucede si remplazamos la implicación por la equivalencia lógica?
sobre(X,Y ) ↔ en(X,Y ) ∨ (en(X, Z), sobre(Z,Y ))
Está fbf expresa que X está sobre Y si y sólo si una de las condiciones es verdadera. Esto es, si ninguna de las condiciones se cumple, ¡se sigue que X no está sobre
Y ! Esta es la intuición seguida para explicar la negación como falla.
60
5 Negación
Desafortunadamente, combinar cláusulas definitivas como en el ejemplo anterior,
sólo es posible para cláusulas con cabezas idénticas. Por ejemplo:
en(c, b).
en(b, a).
Por una simple transformación, el programa puede ser escrito como:
en(X1 , X2 ) ← X1 = c, X2 = b
en(X1 , X2 ) ← X1 = b, X2 = a
Las cláusulas pueden combinarse en una sola fórmula, donde la implicación es
remplazada por la equivalencia lógica.
en(X1 , X2 ) ↔ (X1 = c, X2 = b) ∨ (X1 = b, X2 = a)
La lectura lógica de esta fbf es que X1 está en X2 si y sólo si X1 = c y X2 = b o
si X1 = b y X2 = a. Esta transformación se puede realizar sobre un programa lógico
definitivo ∆ y el resultado se conoce como compleción de ∆ .
Definición 42 (Compleción) Sea ∆ un programa lógico definitivo. La compleción
comp(∆ ) de ∆ es el conjunto de fórmulas obtenido a partir de las siguientes tres
transformaciones:
1. Para cada sı́mbolo de predicado φ remplazar la cláusula α de la forma:
φ (t1 , . . . ,tm ) ← α1 , . . . , αn (n ≥ 0)
por la fórmula:
φ (X1 , . . . , Xm ) ← ∃Y1 , . . . ,Yi (X1 = t1 , . . . , Xm = tm , α1 , . . . , αn )
donde las Yi son todas variables en α y las Xi son variables únicas que no aparecen en α.
2. Para cada sı́mbolo de predicado φ remplazar todas las fbf:
φ (X1 , . . . , Xm ) ← β1
..
.
φ (X1 , . . . , Xm ) ← β j
por la fórmula:
5.2 La compleción de un programa
61
∀X1 , . . . , Xm (φ (X1 , . . . , Xm ) ↔ β1 ∨, . . . , ∨β j si j > 0
∀X1 , . . . , Xm (¬φ (X1 , . . . , Xm )) si j = 0
3. Finalmente el programa se extiende con los siguientes axiomas de igualdad libre,
que definen las igualdades introducidas en el paso 1:
∀(X = X)
∀(X = Y ⇒ Y = X)
∀(X = Y ∧Y = Z ⇒ X = Z)
∀(X1 = Y1 ∧ · · · ∧ Xn = Yn ⇒ f (X1 , . . . , Xn ) = f (Y1 , . . . ,Yn ))
∀(X1 = Y1 ∧ · · · ∧ Xn = Yn ⇒ (φ (X1 , . . . , Xn ) ⇒ φ (Y1 , . . . ,Yn ))
∀( f (X1 , . . . , Xn ) = f (Y1 , . . . ,Yn ) ⇒ X1 = Y1 ∧ · · · ∧ Xn = Yn )
∀(¬ f (X1 , . . . , Xm ) = g(Y1 , . . . ,Yn ))(Si f /m 6= g/n)
∀(¬X = t)(Si X es un subtermino propio de t)
Estas definiciones garantizan que la igualdad (=) sea una relación de equivalencia; que sea una relación congruente; y que formalice la noción de unificación. Las
primeros cinco definiciones se pueden abandonar si se especifica que = representa
la relación de identidad .
Ejemplo 17 Consideremos la construcción de comp(∆ ) tal y como se definió anteriormente. El primer paso produce:
sobre(X1 , X2 ) ← ∃X,Y (X1 = X, X2 = Y, en(X,Y ))
sobre(X1 , X2 ) ← ∃X,Y, Z (X1 = X, X2 = Y, en(Z,Y ), sobre(Z,Y ))
en(X1 , X2 ) ← (X1 = c, X2 = b)
en(X1 , X2 ) ← (X1 = b, X2 = a)
dos pasos más adelante obtenemos:
∀X1 , X2 (sobre(X1 , X2 ↔ ∃X,Y (. . . ) ∧ ∃X,Y, Z(. . . ))
∀X1 , X2 (en(X1 , X2 ) ↔ (X1 = c, X2 = b) ∧ (X1 = b, X1 = a))
y el programa se termina con las definiciones de igualdad como identidad y unificación.
La compleción comp(∆ ) de un programa definitivo ∆ preserva todas las literales
positivas modeladas por ∆ . Esto es, si ∆ |= α entonces comp(∆ ) |= α. Tampoco se
agrega información positiva al completar el programa: Si comp(∆ ) |= α entonces
∆ |= α. Por lo tanto, al completar el programa no agregamos información positiva
al mismo, solo información negativa.
62
5 Negación
Como sabemos, no es posible que una literal negativa pueda ser consecuencia
lógica de un programa definitivo. Pero al substituir las implicaciones en ∆ por equivalencias en comp(∆ ) es posible inferir información negativa a partir del programa
completado. Esta es la justificación de la regla NAF, cuyas propiedades de consistencia se deben a K. Clark [3]:
Teorema 5 (Consistencia de la NAF) Sea ∆ un programa definitivo y ← α una
meta definitiva. Si ← α tiene un árbol-SLD finito fallido, entonces comp(∆ ) |=
∀(¬α).
La consistencia se preserva aún si α no es de base. Por ejemplo, ← en(a, X)
falla de manera finita y por lo tanto, se sigue que comp(∆ ) |= ∀(¬en(a, X)). La
completitud de la NAF también ha sido demostrada:
Teorema 6 (Completitud de la NAF) Sea ∆ un programa definitivo. Si comp(∆ ) |=
∀(¬α) entonces existe un árbol finito fallido para la meta definitiva ← α.
Observen que solo enuncia la existencia de un árbol-SLD finito fallido. Como se
ha mencionado, un árbol-SLD puede ser finito bajo ciertas reglas de computación
e infinito bajo otras. En particular, el teorema de completitud no es válido para
las reglas de computación de Prolog. La completitud funciona para una subclase
de derivaciones-SLD conocidas como justas (fair), las cuales o bien son finitas o
garantizan que cada átomo en la derivación (u ocurrencia de éste), es seleccionado
eventualmente por las reglas de computación. Un árbol-SLD es justo si todas sus
derivaciones son justas. La NAF es completa para árboles-SLD justos. Este tipo
de derivaciones se pueden implementar fácilmente: selecciona la sub-meta más a
la izquierda y agrega nuevas submetas al final de esta (búsqueda en amplitud). Sin
embargo, pocos sistemas implementan tal estrategia por razones de eficiencia.
5.3.
Resolución SLDNF para programas definitivos
En el capı́tulo 4 presentamos el método de resolución-SLD, utilizado para probar
si una literal positiva cerrada es consencuencia lógica de un programa. En la sección
anterior afirmamos que también las literales negadas pueden derivarse a partir de la
terminación de programas lógicos definitivos. Combinando la resolución SLD y la
negación como fallo finito (NAF), es posible generalizar la noción de meta definitiva
para incluir literales positivas y negadas. Tales metas se conocen como generales.
Definición 43 (Meta general) Una meta general tiene la forma:
← α1 , . . . αn (n ≥ 0)
donde cada αi es una literal positiva o negada.
La combinación de la resolución SLD y la NAF se llama resolución SLDNF.
5.3 Resolución SLDNF para programas definitivos
63
Definición 44 (Resolución SLDNF para programas definitivos) Sea ∆ un programa definitivo, G0 una meta general y R una función de selección (también conocida como regla de computación). Una derivación SLDNF de G0 usando R, es
una secuencia finita o infinita de metas generales:
G0
donde Gi
αi
α0
G1 . . . Gn−1
αn−1
Gn
Gi+1 puede ocurrir si:
1. la literal R-seleccionada en Gi es positiva y Gi+1 se deriva de Gi y αi por un
paso de resolución SLD;
2. la literal R-seleccionada en Gi es negativa (¬α) y la meta ← α tiene un árbol
SLD fallido y finito y Gi+1 se obtiene a partir de Gi eliminando ¬α (en cuyo caso
αi , corresponde al marcador especial FF).
Cada paso en una derivación SLDNF produce una substitución, en el caso 1 un
MGU y en el caso 2, la substitución vacı́a ε.
Entonces, una literal negativa ¬α es demostrada si ← α tiene un árbol SLD finito
que falla. Por dualidad, ¬α falla de manera finita si α es demostrada. Además de
la refutación y de la derivación infinita, existen dos clases de derivaciones SLDNF
completas dada una función de selección:
1. Una derivación se dice (finitamente) fallida si (i) la literal seleccionada es positiva y no unifica con ninguna cabeza de las cláusulas del programa, o (2) la literal
seleccionada es negativa y tiene un fallo finito.
2. Una derivación se dice plantada (stuck) si la sub-meta seleccionada es de la
forma ¬α y ← α tiene un fallo infinito.
Ejemplo 18 Considere el siguiente programa:
en(c, b)
en(b, a)
La meta ← en(X,Y ), ¬en(Z, X) tiene una refutación-SLDNF con la substitución
computada {X/c,Y/b}:
64
5 Negación
G = ← en(X,Y ), ¬en(Z, X).
G0 = ← en(X,Y ).
α0 = en(c, b).
θ0 = {X/c,Y /b}
G1 = ¬en(Z, X)θ0 = ← en(Z, c)
α1 = FF
θ1 = ε
G2 = 2
θ = θ0 θ1 = {X/c,Y /b}
En cambio, si la función de selección hubiera computado las cláusulas de abajo
hacı́a arriba α0 = en(b, a) la derivación hubiera sido fallida (a ustedes probarlo).
Como es de esperarse, la resolución-SLDNF es consistente, después de todo, la
resolución-SLD y la NAF son consistentes.
Teorema 7 (Consistencia de la resolución-SLDNF) Sea ∆ un programa definitivo y ← α1 , . . . , αn una meta general. Si ← α1 , . . . , αn tiene una refutación SLDNF
con una substitución computada θ , comp(∆ ) |= ∀(α1 θ , . . . , αn θ ).
Sin embargo, la resolución-SLDNF no es completa aunque pudiéramos haber
esperado lo contrario. La resolución SLDNF no es completa a pesar de que la resolución-SLD y la NAF si lo son. Un simple contra ejemplo es ← ¬en(X,Y ) que
corresponde a la consulta “¿Hay algunos bloques X e Y, tal que X no está en Y?”
Uno esperarı́a varias respuestas a esta consulta, por ejemplo, el bloque a no está encima de ningún bloque, etc.
Pero la derivación SLDNF de ← ¬en(X,Y ) falla porque la meta ← en(X,Y ) tiene
éxito (puede ser demostrada). El problema es que nuestra definición de derivación
fallida es demasiado conservadora. El éxito de ← en(X,Y ) no significa necesariamente que no halla un bloque que no esté en otro bloque, sólo que existe al menos
un bloque que no está en otro.
El problema tiene su origen en que la NAF, en contraste con la resolución SLD,
es sólo una prueba (test). Recuerden que dada la definición de la resolución SLDNF
y la consistencia y completitud de la NAF, tenemos que ¬en(X,Y ) tiene éxito si
y sólo si (≡) en(X,Y ) tiene asociado un árbol SLD fallido y finito; o si y sólo si
comp(∆ ) |= ∀(¬en(X,Y )). Por lo tanto, la meta general ← en(X,Y ) no debe leerse
como una consulta cuantificada existencialmente, sino como una prueba universal:
“Para todo bloque X e Y, ¿No está X en Y?”.
Esta última consulta tiene una respuesta negativa en el modelo deseado del programa, puesto que el bloque b está en el bloque a. El problema anterior se debe a
la cuantificación de las variables en la literal negativa. Si replanteamos la consulta
5.4 Programas Lógicos Generales
65
anterior como ← ¬en(a, b) entonces la resolución SLDNF alcanza una refutación
puesto que ← en(a, b) falla con una derivación finita.
Algunas veces se asume que la función de selección R permite seleccionar una
literal negativa ¬α si la literal α no tiene variables libres o si α tiene asociada
una substitución computada vacı́a. Estas funciones de selección se conocen como
seguras (safe).
5.4.
Programas Lógicos Generales
Con los desarrollos anteriores estamos en posición de extender el lenguaje de los
programas definitivos para incluir cláusulas que contienen literales tanto positivas
como negativas en su cuerpo. Estas fbf se llaman cláusulas generales y a los programas generales que ellas forman, se les conoce a veces como programas lógicos
normales.
Definición 45 (Cláusula General) Una cláusula general es una fbf de la forma
A0 ← α1 , . . . , αn donde A0 es una fbf atómica y α1 , . . . , αn son literales (n ≥ 0).
Definición 46 (Programa General) Un programa lógico general es un conjunto
finito de cláusulas generales.
Ahora podemos extender nuestro programa del mundo de los bloques con las
siguientes relaciones:
base(X) ← en(Y, X), en la mesa(X).
en la mesa(X) ← ¬no en la mesa(X).
no en la mesa(X) ← en(X,Y ).
en(c, b).
en(b, a).
La primer cláusula especifica que un bloque es base si está sobre la mesa y tiene
otro bloque encima. La segunda cláusula indica que cuando no es cierto que un bloque no está sobre la mesa, entonces está sobre la mesa. La tercera cláusula especifica
que un bloque que está sobre otro, no está sobre la mesa.
Parece claro, pero la pregunta que deberı́amos hacernos es qué tipo de sistema
de prueba queremos para los programas lógicos generales y cuales serán las aproximaciones lógicas a las sutilezas, algunas ya discutidas, introducidas por este tipo de
lenguajes.
Observen que aunque el lenguaje fue enriquecido, no es posible de cualquier forma que una literal negativa sea consecuencia lógica de un programa dado. La razón
es la misma que para los programas definidos, la base de Herbrand de un programa
∆ , B∆ es un modelo de ∆ en el que todas las literales negativas son falsas. Al igual
que con los programas definidos, la pregunta es entonces como lograr inferencias
66
5 Negación
negativas consistentes. Afortunadamente el concepto de compleción de programa
puede aplicarse también a los programas lógicos generales.
Ejemplo 19 La compleción de gana(X) ← mueve(X,Y ), ¬gana(Y ) contiene la fbf:
∀X1 (gana(X1 ) ≡ ∃X,Y (X1 = X, mueve(X,Y ), ¬gana(Y )))
Desafortunadamente, la compleción de los programas normales puede ocasionar
paradojas. Consideren la cláusula general p ← ¬p, su compleción incluye p ≡ ¬p.
La inconsistencia del programa terminado se debe a que p/0 está definida en términos de su propio complemento.
Una estrategia de programación para evitar este problema consiste en componer
los programas por capas o estratos, forzando al programador a referirse a las negaciones de una relación hasta que ésta ha sido totalmente definida. Se entiende que
tal definición se da en un estrato inferior a donde se presenta la negación. En la definición del programa estratificado usaremos ∆ p para referirnos al subconjunto de
cláusulas en ∆ que tienen a p como cabeza.
Definición 47 (Programa Estratificado) Un programa general ∆ se dice estratificado si y sólo si existe al menos una partición ∆1 ∪ · · · ∪ ∆n de ∆ tal que :
1. Si p(. . . ) ← q(. . . ), · · · ∈ ∆i entonces ∆ q ⊆ ∆1 ∪ · · · ∪ ∆i ;
2. Si p(. . . ) ← ¬q(. . . ), · · · ∈ ∆i entonces ∆ q ⊆ ∆1 ∪ · · · ∪ ∆i−1 .
Por ejemplo, el siguiente programa está estratificado:
∆2 :
base(X) ← en(Y, X), en la mesa(X).
en la mesa(X) ← ¬no en la mesa(X).
∆1 :
no en la mesa(X) ← en(X,Y ).
en(c, b).
en(b, a).
La compleción de un programa estratificado es siempre correcta (Apt, Blair y
Walker, 1988). Sin embargo, observen que determinar si un programa es estratificado o no, es decidible; pero determinar si la compleción de un programa es o
no decidible, es incorrecto. Por lo tanto, hay programas generales no estratificados,
cuya terminación es consistente.
5.5 Resolución SLDNF para programas generales
5.5.
67
Resolución SLDNF para programas generales
Hemos revisado el caso de la resolución-SLDNF entre programas definitivos y
metas generales. Informalmente podemos decir que la resolución-SLDNF combina
la resolución SLD con los siguientes principios:
1. ¬α tiene éxito si ← α tiene un árbol-SLD finito que falla.
2. ¬α falla finitamente si y sólo si ← α tiene una refutación-SLD.
El paso de programas definitivos a programas generales, es complicado. Para probar ¬α, debe de existir un árbol finito fallido para ← α. Tal árbol puede contener
nuevas literales negativas, las cuales a su vez deben tener éxito o fallar finitamente.
Esto complica considerablemente la definición de la resolución-SLDNF para programas generales. Por ejemplo, es posible llegar a situaciones paradójicas cuando
los predicados están definidos en términos de sus propios complementos. Consideren el programa no estratificado:
α ← ¬α
Dada la meta inicial ← α, se puede construir una derivación ← α
← ¬α. La
derivación puede extenderse hasta una refutación si ← α falla finitamente. De manera alternativa, si ← α tiene una refutación, entonces la derivación falla. Helas! esto
es imposible pues la meta ← α no puede tener una refutación y fallar finitamente al
mismo tiempo.
En lo que sigue, definiremos las nociones de derivación-SLDNF y árbol-SLDNF,
de manera similar a la derivación-SLD y a los arboles-SLD. La idea se concreta
en el concepto de bosque-SLDNF: un conjunto de árboles-SLDNF cuyos nodos
está etiquetados con metas generales.
Definición 48 (Bosque SLDNF) Sea ∆ un programa general, G0 una meta general, y R una función de selección. El bosque SLDNF de G0 (usando ∆ y R) es el
bosque más pequeño, tal que:
1. G0 es la raı́z del árbol.
2. Si G es un nodo en el bosque cuya literal seleccionada es positiva, entonces para
cada cláusula α tal que G0 puede ser derivada de G y α (con MGU θ ), G tiene un
hijo etiquetado G0 . Si no existe tal cláusula, entonces G tiene un hijo etiquetado
FF (falla finita);
3. Si G es un nodo del bosque cuya literal seleccionada es de la forma ← ¬α (G es
de la forma ← α1 , . . . , Li−1 , αi , Li+1 , . . . , αn ), entonces:
El bosque contiene un árbol cuyo nodo raı́z es ← α.
Si el árbol con raı́z ← α tiene una hoja etiquetada como 2 con la substitución
computada vacı́a ε, entonces G tiene un sólo hijo etiquetado FF;
Si el árbol con raı́z ← α es finito y tiene todas sus hojas etiquetadas FF,
entonces G tiene un sólo hijo etiquetado (con substitución asociada vacı́a ε)
como ← α1 , · · · Li−1 , Li+1 , . . . , αn .
68
5 Negación
Observen que la literal negativa seleccionada ¬α falla sólo si ← α tiene una
refutación con la substitución computada vacı́a ε. Como veremos más adelante,
esta condición que no era necesaria cuando definimos la resolución-SLDNF para
programas definitivos, es vital para la correctez de esta resolución en los programas
generales.
Los arboles del bosque-SLDNF son llamados arboles-SLDNF completos; y la
secuencia de todas las metas en una rama de un árbol-SLDNF con raı́z G es llamada
derivación-SLDNF completa de G (bajo un programa ∆ y una función de selección
R). El árbol etiquetado por G0 es llamado árbol principal. Un árbol con la raı́z ← α
es llamado árbol subsidiario si ¬α es una literal seleccionada en el bosque (el árbol
principal puede ser a su vez subsidiario).
Ejemplo 20 Consideren el siguiente programa general estratificado ∆ :
base(X) ← en(Y, X), en la mesa(X).
en la mesa(X) ← ¬no en la mesa(X).
no en la mesa(X) ← en(X,Y ).
encima(X,Y ) ← en(X,Y ).
encima(X,Y ) ← en(X, Z), encima(Z,Y ).
en(c, b).
en(b, a).
El bosque-SLDNF para la meta ← base(X) se muestra en la figura 5.2. El
árbol principal contiene una derivación fallida y una refutación con la substitución
computada {X/a}.
Las ramas de un árbol-SLDNF en un bosque-SLDNF representan todas las
derivaciones-SLDNF completas de su raı́z, con base en la función de selección dada.
Hay cuatro clases de derivaciones-SLDNF completas:
1.
2.
3.
4.
derivaciones infinitas;
derivaciones finitas fallidas (terminan en FF);
refutaciones (terminan en 2); y
derivaciones plantadas (si ninguno de los casos anteriores aplica).
Ejemplo 21 Consideren el siguiente programa:
termina(X) ← ¬ciclo(X).
ciclo(X) ← ciclo(X).
El bosque-SLDNF para el ejemplo anterior se muestra en la figura 5.3. El bosque incluye una derivación plantada para termina(X) y una derivación infinita para
ciclo(X). Esto ilustra una de las razones por las cuales una derivación se planta: uno
de sus arboles subsidiarios contiene sólo derivaciones fallidas o infinitas.
5.5 Resolución SLDNF para programas generales
69
base(X).
← en(Y0 , X), en la mesa(X).
← en la mesa(b).
← en la mesa(a).
← ¬no en la mesa(b).
← ¬en la mesa(a).
FF
2
← no en la mesa(b). ← no en la mesa(a).
← en(b,Y0 ).
← en(a,Y0 ).
2
FF
Figura 5.2 Bosque-SLDNF para la meta ← base(X).
← ciclo(X).
← paro(X).
← ciclo(X).
← ¬ciclo(X).
∞
Figura 5.3 Bosque-SLDNF para la meta ← paro(X).
El siguiente programa también conduce a una derivación plantada (ciclo en el
cómputo de la negación):
parado ja(X) ← ¬ok(X).
ok(X) ← ¬parado ja(X).
Intenten construir el bosque-SLDNF de este programa y observaran también que
en este caso, la árbol principal es a su vez un árbol subsidiario.
La última razón para que una derivación quede plantada es ilustrada por el siguiente programa:
70
5 Negación
top(X) ← ¬bloqueado(X).
bloqueado(X) ← en(Y, X).
en(a, b).
Es evidente que top(a) deberı́a poder derivarse de este programa. Sin embargo, el
árbol-SLDNF de la meta ← top(X) no contiene refutaciones. De hecho, esta meta
se planta aún cuando ← bloqueado(X) tiene una refutación. La razón de esto es
que ← bloqueado(X) no tiene ninguna derivación que termine con una substitución
computada vacı́a. A la meta ← ¬top(X), Prolog no responde b, sino que ¡todos
los bloques no están en el tope de la pila! Esto se debe a la implementación de
la mayorı́a de los Prolog. La definición que dimos aquı́ de resolución-SLDNF es
correcta.
Teorema 8 (Correctez de la resolución-SLDNF) Sea ∆ un programa general y
← α1 , . . . , αn una meta general. Entonces:
Si ← α1 , . . . , αn tiene una substitución de respuesta computada θ , entonces
comp(∆ ) |= ∀(α1 θ ∧ · · · ∧ αn θ ).
Si ← α1 , . . . , αn tiene un árbol-SLDNF finito que falla, entonces comp(∆ ) |=
∀(¬(α1 ∧ · · · ∧ αn )).
La definición de bosque-SLDNF no debe verse como una implementación de la
resolución-SLDNF, sólo representa el espacio ideal de computación donde la correctez puede ser garantizada.
Capı́tulo 6
Corte y Aritmética
Resumen La computación de un programa lógico requiere la construcción y recorrido de un árbol-SLD. Esto no es necesariamente la forma más eficiente de computación, por lo que en esta clase abordaremos dos extensiones de la programación
lógica, implementadas en ISO Prolog, para acelerar las computaciones realizadas:
el corte y la aritmética. Por simplicidad, la presentación se basa en los programas
lógicos definitivos, aunque los temas discutidos son aplicables a las derivaciones y
arboles-SLDNF.
6.1.
Corte: podando el árbol-SLD
El árbol-SLD de una meta definitiva puede tener muchas ramas que conducen al
fallo de la meta y muy pocas, ó una sola rama, que conducen al éxito. Por ello, el programador podrı́a querer incluir información de control en sus programas, para evitar
que el intérprete construya ramas fallidas. Observen que esta meta-información se
basa en la semántica operacional del programa, por lo que el programador debe saber como se construyen y se recorren los arboles-SLD. El predicado !/0 denota la
operación de corte, y puede utilizarse como una literal en las metas definitivas. Su
presencia impide la construcción de ciertos sub-arboles.
Un intérprete de Prolog recorre los nodos de un árbol-SLD primero en profundidad. El orden de las ramas corresponde al orden textual de las cláusulas en el
programa. Cuando una hoja es alcanzada, el proceso de backtracking es ejecutado.
El proceso termina cuando no es posible hacer backtracking (todos los sub-arboles
de la raı́z del árbol han sido visitados).
Ejemplo 22 Asumamos el siguiente programa que define que el padre de una persona es su antecesor hombre:
71
72
6 Corte y Aritmética
padre(X,Y ) ← progenitor(X,Y ), hombre(X).
progenitor(ben jamin, antonio).
progenitor(maria, antonio).
progenitor(samuel, ben jamin).
progenitor(alicia, ben jamin).
hombre(ben jamin).
hombre(samuel).
El árbol-SLD de la meta ← padre(X, antonio) se muestra en la figura 6.1. Bajo la
función de selección implementada en Prolog, encontrará la solución X/ben jamin.
El intento por encontrar otra solución con X/maria, mediante el backtracking, fallará puesto que maria no satisface el predicado hombre/1.
← padre(X, antonio)
← progenitor(X, antonio), hombre(X).
hombre(ben jamin).
← hombre(maria)
2
Figura 6.1 Arbol de derivación-SLD para la meta ← padre(X, antonio)
Para detallar la semántica del corte, es necesario introducir algunos conceptos
auxiliares. En un árbol-SLD, cada nodo ni corresponde a una meta Gi de una derivación-SLD y tiene un átomo seleccionado asociado αi :
G0
α0
G1 . . . Gn−1
αn−1
Gn
Asumamos que para cierto nodo nk , αk no es una sub-meta de la meta inicial. Entonces αk es un átomo βi del cuerpo de una cláusula de la forma β0 ←
β1 , . . . , βi , . . . , βn cuya cabeza β0 unifica con la sub-meta seleccionada en algún nodo n0< j<k , es decir un nodo entre la raı́z del árbol y el nodo nk . El nodo n j se conoce
como el origen de αk y se denota como origen(αk ).
El predicado de corte ! se procesa como un átomo ordinario situado en el cuerpo de una cláusula. Sin embargo, cuando el corte es seleccionado para computar la
resolución, éste tiene éxito inmediatamente (con la substitución vacı́a ε como resultado). El nodo donde ! fue seleccionado es llamado el nodo de corte. Un nodo de
corte puede ser visitado nuevamente durante el backtracking. En este caso, el curso normal del recorrido del árbol es alterado (por definición el recorrido continua
6.1 Corte: podando el árbol-SLD
73
en el nodo superior a origen(!). Si el corte ocurre en la meta inicial, la ejecución
simplemente termina.
Ejemplo 23 La formulación del problema padre, nos dice que a lo más existe una
solución para nuestra meta. cuando la solución se encuentra, la búsqueda puede
deternerse pues ninguna persona tiene más de un padre. Para forzar esta situación,
el predicado de corte se agrega al final de padre/2:
padre(X,Y ) ← progenitor(X,Y ), hombre(X), !.
Observen que el programa modificado en el ejemplo anterior sólo puede computar un elemento de la relación padre/2. El corte detendrá la búsqueda después de
encontrar la primer respuesta para la meta ← padre(X,Y ). El origen del corte es
la raı́z del árbol, por lo que la búsqueda termina después de hacer backtracking al
nodo de corte. La otra rama del árbol no es recorrida. El árbol-SLD del programa
que incluye el corte se muestra en la figura 6.2.
← padre(X, antonio)
← progenitor(X, antonio), hombre(X), !.
hombre(ben).
←!
2
Figura 6.2 Arbol de derivación-SLD para la meta ← padre(X, antonio) con las ramas fallidas
podadas.
Observen que la versión modificada con el corte, no puede usarse para computar
más de un elemento de la relación “es padre de”. El corte detendrá la búsqueda
después de encontrar la primer respuesta a la meta definitiva.
A partir de la definición del corte, se sigue que los efectos del operador son:
1. Divide el cuerpo de la meta en dos partes, separando la ejecución de la reconsideración – después de éxito de !/0 , no es posible hacer backtracking hacı́a
las literales a la izquierda del corte. Sin embargo, a la derecha del corte todo
funciona de manera usual.
2. Poda las ramas sin explorar directamente bajo origen(!). En otras palabras, no
habrá más intentos de unificar la sub-meta seleccionada de origen(!) con el resto
de las cláusulas del programa.
74
6 Corte y Aritmética
El corte es controvertido. La intención al introducir el corte, es poder controlar la
ejecución de los programas, sin cambiar su significado lógico. Por tanto, la lectura
lógica del corte es “verdadero”. Operacionalmente, si el corte remueve sólo ramas
fallidas del árbol-SLD, no tiene influencia en el significado lógico de un programa. Pero el corte puede remover también ramas exitosas del árbol-SLD, atentando
contra la completitud de los programas definitivos, o la correctez de los programas
generales.
Ejemplo 24 Es bien sabido que los padres de un recién nacido están orgullosos.
La proposición puede representarse con la siguiente cláusula definitiva:
orgulloso(X) ← padre(X,Y ), recienNacido(Y ).
consideren las siguiente cláusulas adicionales:
padre(X,Y ) ← progenitor(X,Y ), hombre(X).
progenitor( juan, maria).
progenitor( juan, cristina).
hombre( juan).
recienNacido(cristina).
La respuesta a la meta ← orgulloso( juan) es “Si”, puesto que como describimos, juan es padre de cristina, que es un recién nacido. Ahora, si remplazamos la
primera cláusula, con su versión que utiliza corte:
padre(X,Y ) ← progenitor(X,Y ), hombre(X), !.
Y preguntamos nuevamente a Prolog, si
← orgulloso( juan).
la respuesta será “No”. Esto se debe a que la primer hija de juan en el programa
es maria. Una vez que esta respuesta se ha encontrado, no habrá más intentos de
satisfacer la meta en origen(!). No se considerarán más hijos de juan en la solución
computada.
El programa del ejemplo anterior se ha vuelto incompleto, algunas respuestas correctas no pueden ser computadas. Más grave aún es el caso de las metas generales,
donde se puede llegar a resultados incorrectos, por ejemplo, ← ¬orgulloso( juan)
tendrı́a éxito en la versión de nuestro programa que utiliza corte.
Hasta ahora hemos distinguido dos usos del corte: eliminar ramas fallidas en el
árbol-SLD; y podar ramas exitosas. Eliminar ramas fallidas se considera una práctica sin riesgo, porque no altera las respuestas producidas durante la ejecución de un
programa. Tales cortes se conocen como cortes verdes. Sin embargo, este uso del
operador corte, esta ligado al uso particular de un programa. Como se ilustra en
6.1 Corte: podando el árbol-SLD
75
los ejemplos anteriores, para algunas metas, el operador solo eliminará ramas fallidas; pero para otras podará ramas exitosas. Cortar ramas exitosas se considera una
práctica de riesgo. Por eso, tales cortes se conocen como cortes rojos.
Ejemplo 25 Consideremos un ejemplo de corte verde. Si en el ejemplo anterior maria es una recién nacida, agregarı́amos la cláusula recienNacido(maria) a nuestro
programa. Entonces la meta ← orgulloso(X) nos dirı́a que X/X/ juan está orgulloso. Esto es, juan tiene una doble razón para estar orgulloso. Pero a nosotros nos
basta con saber sólo una vez, que orgulloso está juan. Para evitar que Prolog nos
de la respuesta dos veces, definirı́amos:
orgulloso(X) ← padre(X,Y ), recienNacido(Y ), !.
Ejemplo 26 Ahora consideren un ejemplo de corte rojo:
min(X,Y, X) ← X < Y, !.
min(X,Y,Y ).
Aparentemente nuestro programa es correcto. De hecho, el programa responderı́a
de manera correcta a metas como ← min(2, 3, X) respondiendo que “Si” para X/2;
y para ← min(3, 2, X) responderı́a que “Si” para X/2. Sin embargo el programa
no es correcto. Consideren la meta ← min(2, 3, 3) y verán que Prolog responderı́a
“Si”. La razón de esto es que la segunda cláusula dice: el menor de X e Y es siempre
Y . El corte está eliminando algunas ramas fallidas, que serı́an útiles en la definición
de min. La definición correcta, usando corte, serı́a:
min(X,Y, X) ← X < Y, !.
min(X,Y,Y ) ← X ≥ Y.
Un comentario final. El corte puede usarse para implementar la negación en Prolog. Consideren las siguientes cláusulas donde f ail es un predicado de Prolog que
carece de definición y no puede ser definido por el usuario:
not(estudiante(X)) ← estudiante(X), !, f ail.
not(estudiante(X).
Esta definición descansa enteramente en la semantica operacional de Prolog. Esto es, las sub-metas se deben resolver de izquierda a derecha, y las cláusulas se
buscan en el orden en que aparecen en el texto del programa. Si queremos saber
si juan no es un estudiante, le meta a adoptar es ← not(estudiante( juan)). Ahora,
hay dos casos a considerar: Si la meta ← estudiante( juan) tiene éxito, el operador de corte eliminará la segunda cláusula y la meta original fallará; Si la meta
← not(estudiante( juan)) falla, la segunda cláusula será intentada en el backtracking y la meta negada tendrá éxito.
Podemos definir not/1 haciendo uso del meta-predicado estándar de Prolog
call/1:
76
6 Corte y Aritmética
not(X) ← call(X), !, f ail.
not(X).
El argumento a call/1 debe ser un átomo de base, de otra forma, el cómputo produce
substituciones y la implementación resulta lógicamente incorrecta.
Usar cortes aleatoriamente, para intentar obtener respuestas correctas a un problema, es una de las fuentes principales de errores entre los novatos de la programación lógica. Antes de intentar usar un corte, intenten escribir programas lógicamente
correctos.
6.2.
Aritmética
Hemos demostrado que los programas definitivos pueden describir cualquier relación computable. Esto es, cualquier máquina de Turing puede codificarse como
un programa lógico definitivo. Esto significa que, desde el punto de vista teórico, la
programación lógica es tan expresiva como otros paradigmas de programación. La
resolución y la búsqueda exhaustiva, proveen una herramienta universal de computación.
Desafortunadamente, desde el punto de vista práctico, ésta no es la mejor forma
de computar todo. Consideren las operaciones aritméticas sobre los números naturales. Existen implementaciones en hardware extremadamente eficientes de tales
operaciones. Ası́ que es deseable que Prolog tenga acceso a las operaciones del procesador y su aritmética de máquina. El problema es ¿Cuando es posible hacer cosas
parecidas sin destruir la naturaleza declarativa de los programas lógicos?
Observen primero que las operaciones como suma/2 y multiplicacion/2 pueden
describirse fácilmente en un programa definitivo. Los números naturales pueden
describirse mediante términos de base. Una forma estándar de hacer esto es utilizar
la constante 0 para representar el cero, y el functor unario s/1 para representar el
sucesor de un número. Los números naturales consecutivos están representados por:
0, s(0), s(s(0)), . . .
Las operaciones de adición y multiplicación son funciones binarias sobre los
números naturales. Los programas lógicos proveen únicamente un formalismo para
representar relaciones. Sin embargo, una función binaria puede verse como una relación ternaria consistente en todas las tripletas hX,Y, Zi tal que Z es el resultado de
aplicar la función a X e Y .
Ahora, es bien sabido que las operaciones de adición y multiplicación se caracterizan por los axiomas de Peano:
6.2 Aritmética
77
0+X = X
s(X) +Y = s(X +Y )
0×X = 0
s(X) ×Y = (X ×Y ) +Y
Estos axiomas relacionan argumentos y resultados de las operaciones. En forma
de programa lógico definitivo, se formuları́an como sigue:
suma(0, X, X).
suma(s(X),Y, s(Z)) ← suma(X,Y, Z).
mult(0, X, 0).
mult(s(X),Y, Z) ← mult(X,Y,W ), suma(W,Y, Z).
El programa puede usarse, por ejemplo, para sumar dos y tres:
← suma(s(s(0)), s(s(s(0))), X)
que darı́a como resultado “si” para X/s(s(s(s(s(0))))). Un árbol de refutación-SLD
es construido para obtener este resultado.
El programa puede usarse también para computar resta y una forma limitada
de división. Por ejemplo: ← suma(X, s(s(0)), s(s(s(0)))) representa la resta de tres
menos dos, dando como resultado “Si” para X = s(0).
Cuando comparamos estos ejemplos con la práctica común en programación,
resulta evidente que:
La representación de los números naturales como términos de base no es adecuada para los humanos.
Las computaciones de la aritmética no hacen uso del hardware, por lo que resultan lentas: Sumar X e Y requiere X + 1 llamada a suma.
No es posible construir expresiones aritméticas, puesto que suma/3 y mult/3
representan relaciones.
El primer problema se puede resolver fácilmente introduciendo arreglos sintácticos: sn (0) = n. También se conocen técnicas para compilar expresiones aritméticas a código ensamblador. El principal problema es cómo incorporar expresiones
aritméticas sin destruir el significado declarativo de los programas.
Asumamos que las expresiones aritméticas aparecerán como términos en los
programas definitivos. Las respuestas de estos programas deberán tomar en consideración la equivalente entre expresiones aritméticas. Por ejemplo, consideren la
siguiente regla para computar impuestos: si el ingreso anual es mayor que 150000
dolares, entonces el impuesto es 30 %, en otro caso se cobrará el 25 % del ingreso
menos 30000 dolares.
78
6 Corte y Aritmética
impuesto(Ingresos, 0,5 ∗ Ingresos) ← Ingresos > 150000.
impuesto(Ingresos, 0,25 ∗ (Ingresos − 30000)) ← Ingresos ≤ 150000.
Por lo tanto, una persona que gana 130000 dolares deberı́a obtener para la meta
← impuesto(130000, 25000) una respuesta de “Si”. Pero estas reglas no pueden
usarse para encontrar una refutación a la meta, ya que ninguna cabeza de regla
unifica con la sub-meta de la meta. La razón es que la unificación estándar es muy
débil, como para darse cuenta que 25000 y 0.25*(130000-30000) son equivalentes.
Por lo tanto, la equivalencia debe describirse mediante axiomas de igualdad para
la aritmética, los cuales no han sido incluidos en el programa anterior.
De esta discusión se sigue que es necesaria una extensión al concepto de programa lógico. Para nuestro ejemplo, el programa deberı́a consistir de dos partes: un
conjunto de cláusulas definitivas P, y un conjunto de axiomas de igualdad E, describiendo la equivalencia entre los términos. Este tipo de programas ha sido estudiado
en la literatura, donde el resultado más importante es el concepto de unificación generalizada, asociada a una teorı́a de igualdad E y llamada E-unificación. Lo que
sigue es una breve introducción a este tema.
Una teorı́a de igualdad en cláusulas definitivas es un conjunto (posiblemente
infinito) de cláusulas definitivas, donde cada átomo es de la forma s = t y s y t son
términos. Algunas veces, la forma de las cláusulas se restringe a hechos.
Un programa definitivo con igualdad es un par P, E donde P es un programa
definitivo, que no contiene ocurrencias del predicado = /2 y E es una teorı́a de
igualdad en cláusulas definitivas.
Sea E una teorı́a de igualdad en cláusulas definitivas. Una substitución θ es un
E-unificador de los términos s y t, si y sólo si sθ = tθ es una consecuencia lógica
de E.
Ejemplo 27 Sea E una teorı́a de igualdad que expresa las equivalencias propias
de las expresiones aritméticas. Consideren las expresiones:
t1 := (2 × X) + 1
t2 := Y + 2
Por ejemplo, la substitución θ = {Y /(2 × X − 1)} es un E-unificador de t1 y t2 .
Ahora, para un programa dado P, E y una meta ← α1 , . . . , αn la refutación de la
meta puede construirse de la misma manera que para los programas definitivos, con
la diferencia de que utilizaremos E-unificación, en lugar de la unificación normal.
Encontrar E-unificadores puede verse como la resolución de ecuaciones en un
álgebra definida por los axiomas de igualdad. Se sabe que el problema de la Eunificación es en general indecidible. Aún siendo decidible para alguna teorı́a E,
pueden existir muchas soluciones a una ecuación dada. La situación donde existe
un unificador más general, suele ser rara. Todo esto significa que, aún cuando es
posible construir E-unificadores, una nueva fuente de indeterminismo se introduce
con ellos.
6.2 Aritmética
79
Asumamos ahora que una teorı́a de igualdad E describe todas las funciones externas, incluidas las operaciones aritméticas, usadas en un programa lógico. Esto
significa que para cualquier par de términos de base s y t, cuyos functores principales denoten funciones externas, la fórmula s = t es una consecuencia lógica de E, si
y sólo si, la invocación de s regresa el mismo resultado que la invocación de t. Para
términos base, la E-unificación es decidible. Esto puede ser explotado de la siguiente
manera: cuando una llamada a una función externa es encontrada como término a Eunificar, es invocada y su forma reducida es unificada por el procedimiento normal.
Observen que las funciones externas sólo pueden ser invocadas sobre argumentos de
base. Si algunas variables de la función externa no tienen valores, la llamada externa
provocará un error y no se encontrará un E-unificador.
Esta idea es incorporada en el ISO Prolog. Los enteros son representados por
numerales como 0, 1, 2006, etc. También existe un soporte limitado para números
reales como 3.14, 0.333, etc. Lógicamente, los numerales son constantes. Un número limitado de functores con notación infija están predefinidos en Prolog: +, -, *, /
(división en reales), // (división entera), mod, etc.
Dos predicados predefinidos se ocupan de la E-unificación. El predicado =:= /2
checa si dos términos de base son E-unificables. Por ejemplo, la meta ← 2 ∗ 3 =:=
2 + 4 responde “Si” con la substitución vacı́a computada. Si los argumentos no son
términos de base, Prolog aborta la ejecución del programa con un mensaje de error.
El predicado is/2 unifica el primer argumento con la forma reducida del segundo. Por ejemplo ← X is 2 + 2 responde “Si” con la substitución {X/4}. El primer
argumento puede no ser una variable, en lo que constituye un caso especial de la
E-unificación =:= /2. Por ejemplo, ← X + 1 is 2 + 3 falla aunque exista un Eunificador {X/4}.
Otros predicados estándar incluyen = \ = /2 que checa cuando dos términos no
son E-unificables. El lenguaje también provee predicados para comparar números,
como >,<,≤ y ≥.
Parte II
Prolog
Capı́tulo 7
Introducción a Prolog
Este capı́tulo constituye una guı́a rápida al lenguaje prolog.
83
Capı́tulo 8
Estrategias básicas de resolución de problemas
Resumen En este capı́tulo revisaremos los conceptos de espacio de soluciones y
búsquedas en espacios de soluciones. Se revisara la representación de estos espacios
en Prolog y se ejemplificará el uso de estrategias como las búsquedas primero en
profundidad, primero en amplitud y guiadas por funciones de utilidad.
8.1.
Introducción
Esta sesión la dedicaremos a estudiar un esquema general de representación de
problemas y sus soluciones, ampliamente utilizado en la Inteligencia Artificial. Consideremos el ejemplo mostrado en la figura 8.1. El problema a resolver consiste en
encontrar un plan para colocar los cubos en una configuración determinada, partiendo de una configuración inicial. Sólo un bloque puede moverse a la vez y las
acciones del brazo son del tipo “pon A en la mesa”, “pon B en C”, etc. Dos conceptos aparecen en esta descripción: i) Situaciones o estados del problema; y ii)
acciones, o movimientos legales, que transforman un estado del problema en otro.
C
?
A
A
B
B
C
Figura 8.1 Un problema de ordenamiento de bloques.
Estados y acciones configuran un grafo dirigido conocido como espacio de estados (Fig. 8.2. El problema de encontar un plan para acomodar los cubos es equivalente a encontrar un camino en este grafo, entre un nodo representado el estado ini85
86
8 Estrategias básicas de resolución de problemas
cial del problema y un nodo representando la solución final, un nodo meta. ¿Cómo
podemos representar tal grafo en Prolog?
Figura 8.2 Espacio de estados para el problema de ordenamiento de bloques.
El espacio de estados será representado por una relación s(X,Y ) que será verdadera si existe un movimiento válido en el espacio de estados del nodo X al nodo
Y . El nodo Y recibe el nombre de sucesor de X. Si existe un costo asociado a las
acciones esto puede representarse por un tercer argumento de s, s(X,Y,Costo).
Esta relación puede ser especificada extensionalmente por un conjunto de hechos.
Sin embargo, para cualquier problema interesante, esto es irrealizable. La relación
s es normalmente definida intensionalmente mediante un conjunto de reglas que
computan el sucesor de un nodo dado.
Otro detalle importante tiene que ver con la representación de los estados del problema, los nodos. La representación debe de ser compacta y permitir la computación
eficiente de los nodos sucesores; y posiblemente el costo asociado a las acciones.
Tomemos como ejemplo el mundo de los bloques. Cada estado del problema
puede ser representado por una lista pilas. Cada pila a su vez puede ser representada
por una lista de bloques. El tope de cada pila es el primer elemento de cada lista
de bloques. La pila vacı́a está representada por la lista vacı́a. Ası́, el estado inicial
mostrado en la figura 8.1 es la lista: [[c,b,a],[],[]] (suponemos, que en la
mesa sólo hay espacio para 3 pilas de bloques).
Una meta es cualquier arreglo con los bloques en el orden deseado. Existen tres
soluciones en este caso:
[[a,b,c],[],[]],
[[],[a,b,c],[]] ó
[[],[],[a,b,c]].
La relación sucesor puede programarse de acuerdo a la siguiente regla: el
Estado2 es sucesor de Estado1 si hay dos pilas Pila1 y Pila2 en Estado1 y el
tope de la pila Pila1 puede moverse a Pila2. Esto se traduce a Prolog como:
8.2 Búsqueda primero en profundidad
1
2
3
87
s(Pilas, [Pila1, [Tope1|Pila2] | OtrasPilas ]) :quitar([Tope1|Pila1], Pilas, Pilas1),
quitar(Pila2, Pilas1, OtrasPilas).
4
5
6
7
quitar(X, [X|Ys], Ys).
quitar(X, [Y|Ys], [Y|Ys1]) :quitar(X,Ys,Ys1).
La relación s nos permite verificar si un nodo es sucesor de otro, por ejemplo:
1
2
3
4
?- s([[b],[a,c],[]],[[],[b,a,c],[]]).
Yes
?- s([[b],[a,c],[]],[[],[a,b,c],[]]).
No
Para representar los estados meta usamos:
1
2
meta(Estado) :member([a,b,c],Estado).
Un predicado solucion/2 se usa para plantear las metas, por ejemplo: solucion([[c,a,b],[],[]],Sol). La solución será encontrada buscando en el espacio de estados del problema.
8.2.
Búsqueda primero en profundidad
Dada la formulación de un problema en términos de su espacio de estados, existen diversas estrategias para encontrar un camino solución. Dos estrategias básica
son las búsquedas primero en profundidad y primero en amplitud. En esta sección
implementaremos la búsqueda primero en profundidad.
Comenzaremos con una idea simple. Para encontrar un camino solución Sol, de
un nodo dado N a un nodo meta:
Si N es un nodo meta, entonces Sol = [N], o
Si existe un nodo sucesor N1 tal que existe un camino Sol1 de N1 al nodo meta,
entonces Sol = [N|Sol1].
Lo cual traduce a Prolog como:
1
2
solucion(N,[N]) :meta(N).
3
4
5
6
solucion(N, [N|Sol1]) :s(N,N1),
solucion(N1,Sol1).
88
8 Estrategias básicas de resolución de problemas
De forma que para computar la solución al problema de los bloques, preguntamos
a Prolog:
?- solucion([[c,b,a],[],[]],Sol).
Sol = [[[c, b, a], [], []],
[[b, a], [c], []],
[[a], [b, c], []],
[[], [a, b, c], []]]
Yes
La solución se computa como sigue. En un principio, el estado inicial N =
[[c, b, a][][]], por lo que el programa se pregunta si N es una meta. La cláusula meta/1
funciona verificando si la solución [a, b, c] es miembro del estado N. Como esta meta
falla, Prolog intentará satisfacer su meta inicial con la segunda cláusula solucion/2.
Esto implica generar un sucesor de N (llamada a s(N, N1)). Ası́ que se computa
N1 = [[b, a], [c], []] y se verifica si esto es una solución. Como la meta falla, se genera un sucesor de N1 y ası́ hasta llegar a [[], [a, b, c], []].
Este proceso puede seguirse detalladamente utilizando el traceador gráfico de
SWI-Prolog. Para ello invoquen la meta guitracer. Al trazar una función verán una
ventana como la mostrada en la figura 8.3. La ventana superior izquierda muestra
las substituciones computadas, la derecha las pilas formadas, y la inferior muestra
el código del programa que está siendo trazado.
Figura 8.3 Traza gráfica de SWI-Prolog.
8.3 Búsqueda primero en amplitud
89
Una primera mejora a este algoritmo, consiste en evitar que los nodos visitados
vuelvan a ser expandidos, evitando ası́ caer en ciclos. La idea es llevar un registro
de los nodos visitados. El resultado se da del nodo final hacı́a el estado inicial:
1
2
solucion2(Nodo,Sol) :primeroProfundidad([],Nodo,Sol).
3
4
5
primeroProfundidad(Camino, Nodo, [Nodo|Camino]) :meta(Nodo).
6
7
8
9
10
primeroProfundidad(Camino, Nodo, Sol) :s(Nodo,Nodo1),
not(member(Nodo1, Camino)),
primeroProfunidad([Nodo|Camino],Nodo1,Sol).
Finalmente, para evitar caer en búsquedas infinitas sobre ramas no ciclicas, es
posible establecer un limite a la profunidad de la búsqueda. Para ello definiremos
primeroProfundidad2/3, donde el tercer argumento es la profunidad máxima de la
búsqueda.
1
2
solucion3(Nodo,Sol,MaxProf) :primeroProfundidad2(Nodo,Sol,MaxProf).
3
4
5
primeroProfundidad2(Nodo,[Nodo],_) :meta(Nodo).
6
7
8
9
10
11
primeroProfundidad2(Nodo,[Nodo|Sol],MaxProf):MaxProf > 0,
s(Nodo,Nodo1),
Max1 is MaxProf-1,
primeroProfundidad2(Nodo1,Sol,Max1).
8.3.
Búsqueda primero en amplitud
En contraste con la búsqueda primero en profundidad, la estrategia de búsqueda
primero en amplitud elige visitar primero los nodos que están más cercanos a la raı́z,
por lo que el árbol de búsqueda crece más en amplitud, que en profundidad.
Esta estrategia de búsqueda es más complicada de programar. La razón de ello es
que debemos mantener un conjunto de nodos candidatos alternativos, no únicamente
un nodo como lo hacı́amos al buscar en profundidad. Además, debemos mantener
los caminos, si queremos obtenerlos como parte de la solución computada. De forma
que:
?- primeroEnProfundidad(Caminos,Sol).
90
8 Estrategias básicas de resolución de problemas
es verdadera, si y sólo si algún camino miembro del conjunto de candidatos
Caminos, puede extenderse hasta un nodo meta. Sol es el camino solución.
El conjunto Caminos será representado como listas de caminos, donde cada camino se representará como una lista de nodos en el orden inverso en que fueron
visitados. Esto es, la cabeza de la lista que representa un camino tendrá el último
nodo generado; y el último nodo en la lista será el estado inicial de la búsqueda. Al
iniciar Caminos tiene un sólo camino candidato: [[NodoInicial]].
El algoritmo de la búsqueda primero en amplitud puede describirse como sigue,
dado un conjunto de caminos candidatos:
Si el primer camino contiene un nodo meta como su cabeza, entonces esta es la
solución al problema. De otra forma
Eliminar el primer camino del conjunto de caminos candidatos y generar el conjunto de todas las posibles extensiones de un paso de este camino. Agregar este
conjunto de extensiones al final del conjunto de candidatos. Ejecutar la búsqueda
primero en amplitud en este nuevo conjunto de caminos candidatos.
Para generar las extensiones de un sólo paso, dado un camino, podemos usar el
predicado predefinido bago f Veamos el programa:
1
2
%% % solucion(Inicio,Sol) Sol es un camino (en orden inverso)
%% % de Inicio a una meta
3
4
5
solucion(Inicio,Sol) :primeroEnAmplitud([[Inicio]],Sol).
6
7
8
%% % primeroEnAmplitud([Camino1,Camino2,...],Sol) Sol es una
%% % extensión hacı́a la meta de alguno de los caminos
9
10
11
primeroEnAmplitud([[Nodo|Camino]|_],[Nodo|Camino]) :meta(Nodo).
12
13
14
15
16
primeroEnAmplitud([Camino|Caminos],Sol) :extender(Camino,NuevosCaminos),
append(Caminos,NuevosCaminos,Caminos1),
primeroEnAmplitud(Caminos1,Sol).
17
18
19
20
21
22
extender([Nodo|Camino],NuevosCaminos) :bagof([NuevoNodo,Nodo|Camino],
(s(Nodo,NuevoNodo), not(member(NuevoNodo, [Nodo|
Camino]))),
NuevosCaminos),
!.
23
24
%% % Si extender falla, Camino no tiene sucesores (lista vacı́a)
25
26
extender(Camino_,[]).
Si aplicamos este programa de búsqueda al programa del mundo de los cubos,
obtendremos:
8.3 Búsqueda primero en amplitud
91
?- solucion([[c,b,a],[],[]],Sol).
Sol = [[[], [a, b, c], []], [[a], [b, c], []],
[[b, a], [c], []], [[c, b, a], [], []]]
Yes
Si queremos buscar en el espacio del gráfo de la figura 8.4, codificamos los sucesores y las metas como sigue:
1
2
3
4
5
6
7
8
9
10
s(a,b).
s(a,c).
s(b,d).
s(b,e).
s(d,h).
s(e,i).
s(e,j).
s(c,f).
s(c,g).
s(f,k).
11
12
13
meta(j).
meta(f).
a
d
h
j
b
c
e
f
i
g
k
Figura 8.4 Gráfico de un espacio de estados: las metas son f y j.
y buscamos las soluciones:
?- solucion(a,Sol).
Sol = [f, c, a] ;
Sol = [j, e, b, a] ;
No
Observen que al realizarse la búsqueda primero en amplitud, la primer solución
encontrada involucra la meta f más cercana al nodo raı́z.
92
8.4.
8 Estrategias básicas de resolución de problemas
Búsqueda primero el mejor
Un programa de búsqueda primero el mejor, puede verse como una mejora a la
búsqueda primero en amplitud. El algoritmo de primero el mejor comienza también
con un nodo inicial y mantiene una lista de caminos candidato. La búsqueda por
amplitud siempre elige para expandir el camino candidato más corto y la búsqueda
primero el mejor afina esta estrategia.
Asumamos que una función costo es definida para los arcos de un espacio de
estados de un problema. De forma que c(n, n0 ) es el costo de moverse de un nodo n
al nodo n0 en el espacio de estados.
Sea el estimador heurı́stico una función f tal que para cada nodo n en el espacio
de estados, f (n) estima la “dificultad” de llegar a n. De acuerdo a esto, el nodo
más promisorio será aquel que minimice f . Usaremos aquı́ una forma especial de la
función f que nos llevará al bien documentado algoritmo A*. f (n) será construida
para estimar el costo del mejor camino solución entre un nodo inicial s y un nodo
meta, con la restricción de que el camino pase por el nodo n. Supongamos que tal
camino existe y que un nodo meta que minimiza su costo es t. Entonces el estimado
de f (n) puede calcularse como la suma de dos términos:
f (n) = g(n) + h(n)
donde g(n) es el estimado del costo de un camino óptimo de s a n; y h(n) es el
estimado del costo de un camino óptimo de n a t (Fig. 8.5).
s
g(n)
n
n'
n''
h(n)
t
Figura 8.5 Estimado heurı́stico f (n) = g(n) + h(n).
Cuando un nodo n es encontrado en el proceso de búsqueda, tenemos la siguiente
situación: un camino de s a n debió ser encontrado, y su costo puede computarse como la suma del costo de cada arco en el camino. Este camino no es necesariamente
un camino óptimo entre s y n (puede haber caminos mejores no cubiertos aún por la
búsqueda), pero su costo puede servir como un estimador g(n) del costo mı́nimo de
8.4 Búsqueda primero el mejor
93
ir de s a n. El otro término, h(n), es más problemático, porque el espacio entre n y t
no ha sido explorado aún, por lo que su valor es una verdadera adivinanza heurı́stica, resuelta con base en el conocimiento general del algoritmo sobre la estructura
particular del problema a resolver. Como h depende del dominio del problema, no
existe un método universal para su construcción. Asumamos por el momento que
una función h nos es dada y concentrémonos en los detalles del programa primero
el mejor.
7
e
2
s
5
2
5
f
2
2
4
4
a
b
2
c
g
4
2
2
3
t
3
3
d
Figura 8.6 Mapa entre ciudades, sus distancias por carretera, y sus distancias lı́neales a la meta
(cuadros).
Como ejemplo consideren el siguiente problema. Dado un mapa (Fig. 8.6), la
tarea es encontrar la ruta más corta entre una ciudad inicial s y una ciudad meta t. Al
estimar el costo del resto del camino de la ciudad X a la meta, usamos simplemente
la distancia lineal denotada por dist(X,t). Entonces:
f (X) = g(X) + h(X) = g(X) + dist(X,t)
En este ejemplo, podemos imaginar la búsqueda de primero el mejor consistente
de dos procesos, cada uno de ellos explorando uno de los dos caminos alternativos:
el proceso 1 para el camino vı́a a y el proceso 2 para el camino vı́a e. En los pasos
iniciales el proceso 1 está más activo porque los valores f en ese camino son más
bajos que los del otro. En el momento en que el proceso 1 llega a c y el proceso 2
sigue en e, la situación cambia:
f (c) = g(c) + h(c) = 6 + 4 = 10
f (e) = g(e) + h(e) = 2 + 7 = 9
94
8 Estrategias básicas de resolución de problemas
De forma que f (e) < f (c) y ahora el proceso 2 procede al nodo f y el proceso 1
espera. Pero entonces:
f ( f ) = 7 + 4 + 11
f (c) = 10
f (c) < f ( f )
por lo que el proceso 2 es detenido y se le permite al proceso 1 continuar, pero sólo
hasta el nodo d ya que f (d) = 12 > 11. El proceso 2 continua corriéndo hasta llegar
a la meta t (Fig. 8.7).
s
f(a)=2+5=7
e
a
f(e)=2+7=9
f(b)=4+4=8
b
f(c)=6+4=10
f
f(f)=7+4=11
c
f(g)=9+2=11
g
d
f(t)=11+0=11
t
Figura 8.7 La búsqueda en el mapa de las ciudades.
Este proceso de búsqueda parte del nodo inicial (la ciudad s) y genera nuevos
nodos sucesores, expandiendose siempre en la dirección más promisora de acuerdo
a los valores de la función f . Esto genera un árbol que crece hasta llegar a un nodo
meta. Este árbol será representado en nuestro programa de búsqueda primero el
mejor por términos de dos formas:
1. l(N, F/G) representa una hoja del árbol, donde N es un nodo en el espacio de
estados, G es g(N) y F es f (N) = G + h(N).
2. t(N, F/G, Subs) representa un nodo interno del árbol, con una lista de subárboles
Subs no vacı́os. F es el valor f actualizado de N. El valor para f del sucesor más
prometedor de N. La lista Subs se ordena de acuerdo al valor creciente de los
valores para f de los subárboles.
8.4 Búsqueda primero el mejor
95
Por ejemplo, consideren nuevamente la búsqueda ilustrada en la figura 8.7. En el
momento en que el nodo s es expandido, el árbol de búsqueda consta de tres nodos:
el nodo s y sus hijos a y e. En nuestro programa, este árbol se representa como:
t(s,7/0,[l(a,7/2),l(e,9/2)]
El valor f para s es 7, esto es, el valor más promisorio de los hijos de s (a). El
árbol crece expandiendo el nodo más primisorio. El más cercano competidor de a
es e con un f valor de 9. Se permite que a crezca mientras su f valor no exceda 9.
Por lo tanto los nodos b y c son generados, pero c tiene un f valor de 10, lo cual
excede el umbral de crecimiento fijado en 9. En ese momento no se permite que a
crezca más. En ese momento el árbol es:
t(s,9/0,[l(e,9/2),t(a,10/2,[t(b,10/4,[l(c,10/6)])])])
Observen que ahora el valor f del nodo a es 10, mientras que el del nodo e es
9. Estos valores se actualizaron porque fueron generados los nodos b y c. Ahora el
nodo sucesor más promisorio de s es s es e con un valor f de 9.
La actualización de los f valores es necesaria para permitir al programa reconocer el subárbol más promisorio en cada nivel del árbol de búsqueda (esto es, el
subárbol que contiene la hoja más promisoria). Esta modificación de los estimados
de f , nos lleva a la generalización de la definición de f que extiende su definción de
nodos a árboles. Para una hoja n del árbol, mantenemos la definición original:
f (n) = g(n) + h(n)
Para un subárbol T , cuya raı́z es n y tiene como subárboles S1 , S2 , . . . :
f (T ) = mı́n f (Si )
i
El programa que implementa la búsqueda primero el mejor es como sigue. Primero definimos una función interfaz, que encuentre la solución Sol a partir de un
estado inicial Inicio. Para ello solucion/2 llama a expandir/6:
1
2
solucion(Inicio,Sol) :expandir([],l(Inicio,0/0),9999,_,si,Sol).
El predicado expandir/6 se encarga de hacer crecer el árbol de búsqueda. Sus
argumentos incluyen:
El Camino recorrido, inicialmente vacı́o;
El Arbol actual de búsqueda, inicialmente una hoja con el nodo Inicio y valor de
0 para F y G;
El Umbral o limite para la expansión del árbol ( f -valor máximo), para este ejemplo 9999 es suficiente (ningún costo en el árbol será mayor que este valor);
96
8 Estrategias básicas de resolución de problemas
El Arbol1 expandido bajo el Umbral (en consecuencia el f -valor de este árbol
es mayor, al menos que se halla encontrado la solución). Originalmente se pasa
una variable anónima en la llamada;
La bandera Solucionado que puede tomar los valores si, no, o nunca;
y la solución, si existe, al problema regresado en la variable Sol.
El crecimiento del árbol se programa por casos. El caso más simple corresponde
a aquel donde árbol de búsqueda es una hoja, y su Nodo es una meta del espacio
de estados. En ese caso [Nodo|Camino] es la solución Sol buscada. Observen la
bandera Solucionado = si.
1
2
expandir(Camino,l(Nodo,_),_,_,si,[Nodo|Camino]) :meta(Nodo).
El segundo caso corresponde a un árbol de búsqueda que es una hoja, cuyo Nodo
no es una meta del espacio de estados y tiene un f -valor menor (o igual) que el
Umbral. Para ello se generan los árboles sucesores del árbol de búsqueda actual
(Arboles) usando el predicado listaSucc/3. El árbol debe expandir/6 o fallar con
Solucionado = nunca.
1
2
3
4
5
6
7
8
9
10
expandir(Camino,l(Nodo,F/G),Umbral,Arbol1,Solucionado,Sol) :F =< Umbral,
(bagof(M/C, (s(Nodo,M,C), (not(member(M,P)))), Succ),
!,
listaSucc(G,Succ,Arboles),
mejorF(Arboles,F1),
expandir(Camino,t(N,F1/G,Arboles),
Umbral,Arbol1,Solucionado,Sol)
;
Solucionado=nunca).
El tercer caso es parecido, pero el Nodo es interno.
1
2
3
4
5
6
7
8
9
expandir(Camino,t(Nodo,F/G,[Arbol|Arboles]),
Umbral,Arbol1,Solucionado,Sol) :F =< Umbral,
mejorF(Arboles,MejorF),
min(Umbral,MejorF,Umbral1),
expandir([Nodo|Camino],Arbol,
Umbral1,Arbol1,Solucionado1,Sol),
continuar(Camino,t(Nodo,F/G,[Arbol1|Arboles]),
Umbral,Arbol1,Solucionado1,Solucionado,Sol).
El caso cuatro cubre los puntos muertos, cuando no hay solución al problema:
1
expandir(_,t(_,_,[]),_,_,nunca,_) :- !.
El caso cinco define la situación cuando el f -valor es mayor que el Umbral y se
inhibe el crecimiento del árbol:
8.4 Búsqueda primero el mejor
1
2
97
expandir(_,Arbol,Umbral,Arbol,no,_):f(Arbol,F),F>Umbral.
continuar/7 decide como procede la búsqueda de acuerdo al árbol expandido. Si
una solución Sol se ha encontrado, se regresa este valor. En cualquier otro caso, la
expansión continua dependiendo del valor de Solucionado (no o nunca).
1
continuar(_,_,_,_,si,si,Sol).
2
3
4
5
6
7
8
continuar(Camino,t(Nodo,F/G,[Arbol1|Arboles]),
Umbral,Arbol1,no,Solucionado,Sol) :insert(Arbol1,Arboles,NodoArboles),
mejorF(NodoArboles,MejorF),
expandir(Camino,t(Nodo,F/G,NodoArboles),
Umbral,Arbol1,Solucionado,Sol).
9
10
11
12
13
14
continuar(Camino,t(N,F/G,[_|Arboles]),
Umbral,Arbol1,nunca,Solucionado,Sol) :mejorF(Arboles,MejorF),
expandir(Camino,t(N,MejorF/G,Arboles),
Umbral,Arbol1,Solucionado,Sol).
Las siguientes funciones son auxiliares:
1
listaSucc(_,[],[]).
2
3
4
5
6
7
8
listaSucc(G0, [N/C|NCs], Arboles) :G is G0+C,
h(N,H),
F is G+H,
listaSucc(G0,NCs,Arboles1),
inserta(l(N,F/G,Arboles1),Arboles).
9
10
11
12
inserta(Arbol,Arboles,[Arbol|Arboles]) :f(Arbol,F), mejorF(Arboles,F1),
F =< F1, !.
13
14
15
inserta(Arbol,[Arbol1|Arboles], [Arbol1|Arboles1]) :inserta(Arbol,Arboles,Arboles1).
16
17
f(l(_,F/_),F).
18
19
f(t(_,F/_,_),F).
20
21
22
mejorF([Arbol|_],F) :f(Arbol,F).
23
24
mejorF([],9999).
25
26
27
min(X,Y,X) :X =< Y, !.
98
28
29
min(_,Y,Y).
8 Estrategias básicas de resolución de problemas
Capı́tulo 9
Sistemas Expertos
Resumen En este capı́tulo abordaremos uno de los productos tı́picos de la Inteligencia Artificial: los Sistemas Expertos. Normalmente, usamos herramientas de
desarrollo conocidas con shells para construir este tipo de sistemas, pero si necesitamos configurar un shell para una aplicación en particular, es necesario conocer
como es que un sistema experto se construye desde cero. El capı́tulo constituye el
segundo ejemplo del uso de Prolog para resolver problemas tı́picos de la Inteligencia
Artificial
9.1.
Introducción
Los sistemas expertos (SE) son aplicaciones de cómputo que involucran experiencia no algorı́tmica, para resolver cierto tipo de problema. Por ejemplo, los sistemas expertos se usan para el diagnóstico al servicio de humanos y máquinas. Existen
SE que juegan ajedrez, que planean decisiones financieras, que configuran computadoras, que supervisan sistemas de tiempo real, que deciden polı́ticas de seguros, y
llevan a cabo demás tareas que requieren de experiencia humana.
Los SE incluyen componentes del sistema en sı́ e interfaces con individuos con
varios roles. Esto se ilustra en la figura 9.1. Los componentes más importantes son:
Base de conocimientos. La representación declarativa de la experiencia, muchas
veces en forma de reglas IF-THEN.
Almacén de trabajo. Los datos especı́ficos al problema que se está resolviendo.
Máquina de inferencia. El código central del SE que deriva recomendaciones con
base en la base de conocimientos y los datos especı́ficos del problema.
Interfaz del usuario. El código que controla el diálogo entre el usuario y el SE.
Para entender un SE es necesario entender también el rol de los usuarios que
interaccionan con el sistema:
Experto del Dominio. El o los individuos que son los expertos en resolver el
problema que el SE intentará resolver.
99
100
9 Sistemas Expertos
Usuario
Experto en
el Dominio
Interface con
el Usuario
Experiencia
Ingeniero del
Conocimieno
Máquina de
Inferencia
Base de
Conocimiento
Ingeniero en
Sistemas
Almacén de
Trabajo
Figura 9.1 Componentes de un sistema experto e interfases humanas
Ingeniero de Conocimiento. El individuo que codifica el conocimiento de los
expertos en forma declarativa, para que pueda ser usado por el SE.
Usuario. El individuo que consultará el SE para obtener los consejos que esperarı́a de un experto del dominio.
Muchos SE se producen en ambientes de desarrollo conocidos como shells. Un
shell es un sistema que contiene la interfaz del usuario, un formato de conocimiento
declarativo para la base de conocimientos y una máquina de inferencia. El ingeniero
de conocimiento usa el shell para construir un SE que resuelve problemas en un
dominio particular.
Si el sistema se construye desde cero, o utilizando shells configurados para cierto
tipo de aplicaciones, otro individuo entra en escena:
Ingeniero de Sistemas. La persona que construye la interfaz del usuario, diseña
el formato declarativo de la base de conocimientos, e implementa la máquina de
inferencia ¿adivinan cual es su rol?
En realidad eso depende de la talla del proyecto: El ingeniero de conocimiento
y el ingeniero del sistema, pueden ser la misma persona. El diseño del formato
de la base de conocimientos y su codificación están ı́ntimamente relacionados. Al
proceso de codificar el conocimiento de los expertos, se le conoce como ingenierı́a
del conocimiento. Siendo ésta una tarea complicada, se espera el uso de los shells
haga posible la reutilización del conocimiento codificado. En estas sesiones nos
concentraremos en la programación en Prolog de los SE al margen del uso de las
shells.
9.2 Caracterı́sticas de los SE
9.2.
101
Caracterı́sticas de los SE
Los SE poseen las siguientes caracterı́sticas, en menor o mayor grado:
Razonamiento guiado por las metas y encadenamiento hacia atrás. Una técnica
de inferencia que usa las reglas IF-THEN para descomponer las metas en submetas más fáciles de probar.
Manejo de incertidumbre. La habilidad del SE para trabajar con reglas y datos
que no son conocidos con precisión.
Razonamiento guiado por los datos y encadenamiento hacia adelante. Una técnica de inferencia que usa las reglas IF-THEN para deducir soluciones a un problema a partir de los datos iniciales disponibles.
Representación de datos. La forma en que los datos especı́ficos a un problema
dado, son almacenados y accesados por el SE.
Interfaz del usuario. La parte del SE que se usa para una interacción más amigable con el usuario.
Explicación. La habilidad del SE para explicar sus procesos de razonamiento y
su uso en el cómputo de recomendaciones.
9.2.1.
Razonamiento basado en metas
El encadenamiento hacia adelante, o razonamiento basado en metas, es una forma eficiente de resolver problemas que pueden ser modelados como casos de “selección estructurada”; donde la meta del SE es elegir la mejor opción de entre varias
posibilidades enumeradas. Por ejemplo, los problemas de identificación caen en esta
categorı́a. Los problemas de diagnóstico tambien caben aquı́, pues se trata de elegir
el diagnóstico adecuado.
El conocimiento se codifica en reglas que describen como es que cada caso posible podrı́a ser seleccionado. La regla rompe el problema en sub-problemas. Por
ejemplo, las siguientes reglas formarı́an parte de un SE para identificar aves:
1
IF
2
familia es albatros AND
color es blanco
THEN
ave es albatros laysan.
3
4
5
6
7
IF
8
familia es albatros AND
color es negro
THEN
ave es albatros de pies negros.
9
10
11
El sistema puede usar otras reglas para resolver las sub-metas planteadas por las
reglas de alto nivel, por ejemplo:
102
9 Sistemas Expertos
1
IF
2
orden es tubonasales AND
tamaño es grande AND
alas es grandes anguladas
THEN
familia es albatros.
3
4
5
6
9.2.2.
Incertidumbre
Es muy común en la resolución de problemas de selección estructurada, que la
respuesta final no es conocida con total certeza. Las reglas del experto pueden ser
vagas, o el usuario puede estar inseguro sobre sus respuestas. Esto es fácilmente
observable en el diagnóstico médico. Los SE normalmente usan valores numéricos
para representar certidumbre. Existen diveras maneras de definirlos y usarlos en el
proceso de razonamiento.
9.2.3.
Razonamiento guiado por los datos
Para muchos problemas no es posible enumerar las soluciones alternativas a las
preguntas planteadas con antelación. Los problemas de configuración caen en esta
categorı́a. El encadenamiento hacia adelante, o razonamiento guiado por los datos,
usa reglas IF-THEN para explorar el estado actual en la solución del problema y
moverse a estados más cercanos a la solución.
Un SE para acomodar el mobiliario puede tener reglas para la ubicación de un
mueble en particular. Una vez que un mueble ha sido colocado, se puede proceder
con los demás. La regla para colocar la TV enfrente del sofá es como sigue:
1
IF
2
no_colocada tv AND
sofá en pared(X) AND
pared(Y) opuesta a pared(X)
THEN
colocar tv en pared(Y).
3
4
5
6
Esta regla toma un estado del problema con la televisión no situada y regresa un
estado nuevo, donde la televisión ya ha sido colocada. Puesto que la televisión ya
ha sido colocada en su lugar, esta regla no volverá a ser disparada por el SE. Otras
reglas serán usadas para colocar el resto de los muebles hasta terminar.
9.3 Usando la máquina de inferencia de Prolog
9.3.
103
Usando la máquina de inferencia de Prolog
Como pueden haber adivinado, Prolog posee una máquina de inferencia por encadenamiento hacı́a atrás. Esta máquina puede usarse parcialmente para implementar algunos SE. Las reglas de Prolog serán usadas para representar conocimiento
y su máquina de inferencia será usada para derivar conclusiones. Otras partes del
sistema, como la interfaz con el usuario deberán escribirse usando Prolog.
Usaremos el problema de identificación de aves norteamericanas para ilustrar la
construcción de un SE con Prolog. La experticia del SE se basa en un subconjunto de
las reglas reportadas en Birds of North America de Robbins, Bruum, Zim y Singer.
Las reglas del SE estarán diseñadas para ilustrar como se pueden representar varios
tipos de conocimiento, en vez de buscar una identificación precisa de las aves.
9.3.1.
Reglas
Las reglas de un SE normalmente toman el siguiente formato:
1
IF
2
primera premisa AND
segunda premisa AND
...
THEN
conclusión
3
4
5
6
La parte IF de la regla se conoce con el lado izquierdo de la regla (LHS), y la parte
del THEN se conoce como el lado derecho de la regla (RHS). Esto es equivalente a
la semantica de la regla Prolog:
1
2
3
4
conclusión :primera premisa,
segunda premisa,
...
Esto puede ser confuso pués la regla en prolog dice más THEN-IF que IF-THEN.
Retomemos los ejemplos anteriores, si queremos representar en Prolog la regla:
1
IF
2
familia es albatros AND
color es blanco
THEN
ave es albatros laysan
3
4
5
Tendrı́amos que escribir:
104
1
2
3
9 Sistemas Expertos
ave(albatros_laysan) :familia(albatros),
color(blanco).
Las siguientes reglas distinguen entre dos tipos de albatros y cisne. Todas son
cláusulas del predicado ave/1:
1
2
3
4
5
6
7
8
9
10
11
12
ave(albatros_laysan) :familia(albatros),
color(blanco).
ave(albatros_patas_negras)
familia(albatros),
color(obscuro).
ave(cisne_silbador) :familia(cisne),
voz(suave_musical).
ave(cisne_trompetero) :famila(cisne),
voz(alta_trompeta).
:-
Para que estas reglas tengan éxito al distinguir un ave, necesitamos almacenar
hechos acerca del ave que deseamos identificar con el SE. Por ejemplo, si agregamos
estos hechos al programa:
1
2
familia(albatros).
color(obscuro).
Ahora podemos usar la pregunta siguiente:
1
2
3
?- ave(X).
X = albatros_patas_negras
Yes
Observen que aún en esta etapa temprana tenemos un SE completo, donde la
experticia consiste en distinguir entre cuatro aves. La interfaz con el usuario es el
REPL de Prolog y los datos de entrada se almacenan directamente en el programa.
9.3.2.
Reglas para relaciones jerárquicas
El siguiente paso será representar la naturaleza jerárquica del sistema de clasificación de un ave. Esto incluirá reglas para identificar la familia y el orden del ave.
Continuando con el albatros y el cisne, los predicados para orden/1 y f amilia/1
son:
1
2
orden(nariz_tubular) :fosas(externas_tubulares),
9.3 Usando la máquina de inferencia de Prolog
3
4
5
6
7
8
9
10
11
12
13
14
15
16
105
habitat(mar),
pico(gancho).
orden(acuatico) :patas(membrana),
pico(plano).
familia(albatros) :orden(nariz_tubular),
tamaño(grande),
alas(muy_largas).
familia(cisne) :orden(acuatico),
cuello(largo),
color(blanco),
vuelo(pesado).
Ahora el SE puede identificar al albatros a partir de observaciones fundamentales
sobre el ave. En la primer versión, f amilia/0 fue implementada como un hecho.
Ahora f amilia/1 es implementada como una regla. Los hechos del SE ahora reflejan
más datos primitivos:
1
2
3
4
5
6
fosas(externas_tubulares).
habitat(mar).
pico(gancho).
tamaño(grande).
alas(muy_largas).
color(obscuro).
La consulta siguiente reporta:
1
2
3
?- ave(X).
X = albatros_patas_negras
Yes
9.3.3.
Reglas para otras relaciones
El ganso canadiense puede usarse para agregar complejidad al sistema. Debido a
que esta ave pasa los veranos en Canadá y los inviernos en los Estados Unidos, su
identificación se ve afectada por donde ha sido vista y en que estación. Dos reglas
serán necesarias para cubrir estas situaciones:
1
2
3
4
5
6
ave(ganso_canadiense) :familia(ganso),
estacion(invierno),
pais(estados_unidos),
cabeza(negra),
pecho(blanco).
106
7
8
9
10
11
12
9 Sistemas Expertos
ave(ganso_canadiense) :familia(ganso),
estacion(verano),
pais(canada),
cabeza(negra),
pecho(blanco).
Estas metas pueden hacer referencia a otros predicados en una jerarquı́a diferente:
1
2
3
4
5
6
pais(estados_unidos) :- region(oeste_medio).
pais(estados_unidos) :- region(sur_oeste).
pais(estados_unidos) :- region(nor_oeste).
pais(estados_unidos) :- region(atlantico_medio).
pais(canada) :- provincia(ontario).
pais(canada) :- provincia(quebec).
7
8
9
10
region(nueva_inglaterra) :estado(X),
member(X,[massachusetts, vermont, connecticut, maine]).
11
12
13
14
region(sur_oeste) :estado(X),
member(X,[florida, mississippi, alabama, nueva_orleans]).
Otras aves necesitarán de predicados múltiples para ser identificada. Por ejemplo,
el Mallard (Anas platyrhynchos), o pato común del norte, macho tiene la cabeza
verde con un anillo blanco; la hembra tiene la cabeza café moteada:
1
2
3
4
5
6
7
8
ave(mallard):familia(pato),
voz(graznido),
cabeza(verde).
ave(mallard) :familia(pato),
voz(graznido),
cabeza(cafe_moteada).
Basicamente, cualquier situación del libro de las aves norte americanas puede
ser expresado fácilmente en Prolog. Las reglas expresadas forman la base de conocimientos del SE. El único punto débil del programa es su interfaz con el usuario,
que requiere que los datos sean introducidos como hechos del programa.
9.4 Interfaz del usuario
9.4.
107
Interfaz del usuario
El sistema puede mejorarse considerablemente si proveemos una interfaz para
el usuario, que pregunte por la información cuando esto sea necesario, en lugar de
forzar al usuario a introducirla como hechos del programa.
Antes de pensar en un predicado pregunta, es necesario entender la estructura de
los datos que serán preguntados. Todos los datos, manejandos hasta ahora, han sido
de la forma atributo–valor. Por ejemplo, los atributos del pato del norte Mallard, son
mostrados en la figura 9.1.
atributo
familia
voz
cabeza
valor
pato
graznido
verde
Cuadro 9.1 Atributos valor para el mallard
Esta es una de las representaciones más simples usadas en los SE, pero es suficiente para muchas aplicaciones. Existen representaciones más expresivas, como
los tripletes objeto–atributo–valor, o las redes semánticas, o los marcos.
Como estamos programando en Prolog, la riqueza del lenguaje puede usarse directamente en el SE. Por ejemplo, los pares atributo–valor han sido representados
como predicados unarios de la forma atributo(valor): familia(pato), voz(graznido),
cabeza(verde). Pero en region/1 usamos la membresia en listas para su definición.
Usaremos el predicado pregunta para determinar con ayuda del usuario, cuando
un par atributo–valor es verdadero. El SE debe modificarse para determinar que
atributos son verificables por el usuario. Esto se logra con reglas para los atributos
que llaman a pregunta:
1
2
3
4
5
come(X) :- pregunta(come,X).
pies(X) :- pregunta(pies,X).
alas(X) :- pregunta(alas,X).
cuello(X) :- pregunta(cuello,X).
color(X) :- pregunta(color,X).
Ahora, si el SE tiene como meta probar color(blanco), llamará a pregunta/2
en lugar de consultar su base de conocimientos. Si pregunta(color, blanco) tiene
éxito, entonces color(blanco) también lo tiene. La versión más simple de pregunta
es como sigue:
1
2
3
4
pregunta(Atrib,Val):write(Atrib:Val),
write(’? ’),
read(si).
108
9 Sistemas Expertos
El predicado read/1 tendrá éxito sólo si el usuario responde “si” y falla si el
usuario responde cualquier otra cosa. Ahora el programa puede ser ejecutado sin
datos de trabajo iniciales. La misma llamada a ave/1 inicia la consulta al SE.
1
2
3
4
5
6
7
8
9
?- ave(X).
fosas_nasales : externas tubulares ? si.
habitat : mar ? si.
pico : ganchudo ? si.
tamaño : grande ? si.
alars : largas ? si.
color : blanco ? si.
X = albatros_laysan
Yes.
El problema con este enfoque es que si el usuario responde “no” a la última pregunta, la regla para ave(albratros laysan) falla, llevandonos a un backtracking. De
esta manera el SE nos preguntarı́a nuevamente información que ya sabe. De alguna
manera deberı́amos implementar un predicado pregunta que recuerde lo preguntado.
Definiremos un nuevo predicado conocido/3 que nos ayude a recordar las respuestas del usuario. Las respuestas no se guardarán directamente en memoria, sino
que serán guardadas dinámicamente con asserta/1 cuando pregunta provea información nueva para el SE:
1
pregunta(A,V) :- conocido(si,A,V), !.
2
3
pregunta(A,V) :- conocido(_,A,V), !, fail.
4
5
6
7
8
9
10
pregunta(A,V) :write(A:V),
write’? : ’),
read(Resp),
asserta(conocido(Resp,A,V)),
Resp == si.
También es posible utilizar menues contextuados para el caso de atributos multivariados. La idea es que para atributos de un solo valor, la interfaz por el usuario
pregunte una sola vez:
1
2
3
4
5
6
pregunta(A,V) :not(multivariado(A)),
conocido(si,A,V2),
V \== V2,
!,
fail.
Una guı́a sobre los valores válidos para un atributo se implementa con el predicado menu pregunta que trabaja de manera análoga a pregunta:
9.5 Un Shell simple
1
2
3
4
109
tamaño(X) :menu_pregunta(tamaño, X, [grande, mediano, pequeño]).
color(X) :menu_pregunta(color,X,[blanco,verde,cafe,negro]).
La definición de menu pregunta/3 es:
1
2
3
4
5
6
7
8
menu_pregunta(A,V,MenuLista) :write(’Cual es el valor para ’,
write(A), write(’? ’), nl,
write(MenuLista),nl,
read(Resp),
checar(Resp,A,V,MenuLista),
asserta(conocido(si,A,X)),
X == V.
9
10
11
checar(X,A,V,MenuLista) :member(X,MenuLista), !.
12
13
14
15
checar(X,A,V,MenuLista) :write(’Ese valor no es válido, intente nuevamente’), nl,
menu_pregunta(A,V,MenuLista).
9.5.
Un Shell simple
El ejemplo de identificación de aves tiene dos partes: una base de conocimientos,
que incluye la información especı́fica sobre las aves; y los predicados para controlar
la interfaz con el usuario. Al separar estas dos partes, podemos crear un shell de SE.
Con ello podemos crear un nuevo SE que identifique, por ejemplo, peces y reutilizar
la parte de control de la interfaz.
Un cambio mı́nimo es necesario para separar las dos partes de nuestro SE. Necesitamos un predicado de alto nivel que inicie el proceso de identificación. Puesto
que no sabemos de antemano lo que el SE va a identificar, el shell buscará satisfacer un predicado llamado meta. Cada base de conocimiento deberá tener definido
meta/1, por ejemplo, para el caso de identificación de aves tendrı́amos:
1
meta(X) :- ave(X).
como primer predicado en la base de conocimientos aves.
El shell tendrá un predicado solucion/0 que llevará a cabo labores de mantenimiento del SE, para luego resolver la meta/1:
1
2
solucion :abolish(conocido,3),
110
3
4
5
9 Sistemas Expertos
define(conocido,3),
meta(X),
write(’La respuesta es: ’), write(X), nl.
6
7
8
solucion :write(’No se encontró una respuesta.’), nl.
El predicado Prolog abolish/2 se usa para eliminar los hechos definidos previamente con conocido/3, cada vez que una consulta se va a ejecutar. Esto permite al
usuario ejecutar solucion multiples veces en una sola sesión. El predicado de f ine/2
permite indicarle a Prolog que conocido estará definido en el SE, de forma que no
cause error la primera utilización de este predicado. Este predicado puede variar
dependiendo de la versión de Prolog utilizada.
De esta manera tenemos que el SE ha sido dividido en dos partes. Los predicados
en el shell son:
solucion,
pregunta,
menu pregunta,
los predicados auxiliares de éstos.
Los predicados en la base de conocimientos son:
meta,
las reglas sobre el conocimiento del SE,
las reglas sobre los atributos provistos por el usuario,
las declaraciones de los atributos multi-variados.
Para usar este shell en Prolog, tanto el shell como la base de conocimientos deben
ser cargados:
1
2
3
4
5
6
?- consult(shell).
yes
?- consult(’aves.kb’).
yes
?- solucion.
fosas_nasales : externas_tubulares ? ...
9.5.1.
REPL
El shell puede ser mejorado construyendo un ciclo de comandos read-eval-print
loop. Para ello definiremos el predicado se:
1
2
se :bienvenida,
9.6 Encadenamiento hacı́a atrás con incertidumbre
3
4
5
6
7
111
repeat,
write(’> ’),
read(X),
do(X),
X == quit.
8
9
10
11
bienvenida :write(’Este es el shell de su SE.’), nl,
write(’Escriba: cargar, consultar, o salir en el promt.’), nl
12
13
14
do(cargar) :cargar_bd, !.
15
16
17
do(consultar) :solucion, !.
18
19
do(salir).
20
21
22
23
24
do(X) :write(X),
write(’ no es un comando válido.’), nl,
fail.
25
26
27
28
29
cargar_bd :write(’Nombre del archivo: ’),
read(F),
reconsult(F).
La arquitectura obtenida de esta forma se muestra en la figura 9.2.
9.6.
Encadenamiento hacı́a atrás con incertidumbre
Como hemos mencionado, el encadenamiento hacı́a adelante resulta conveniente
cuando los problemas a resolver son del tipo selección estructurada, como en el
ejemplo de la clasificación de aves. Sin embargo, en además de que hemos asumido
que la información completa está disponible para resolver el problema, también
hemos asumido que no hay incertidumbre, ni el los datos provistos por el usuario,
ni en las reglas de los expertos. Por ejemplo, el albatros puede ser observado en la
bruma, con lo que serı́a difı́cil precisar si su color es blanco u obscuro. Es de esperar
que un SE que maneje incertidumbre, pueda contender con este tipo de problemas.
Desarrollaremos un shell que permita manejar reglas con incertidumbre y encadenamiento de ellas hacı́a atrás. Evidentemente, este SE tendrá un formato de reglas
propio, diferente a las reglas de Prolog, y por lo tanto, una máquina de inferencia
propia.
112
9 Sistemas Expertos
Interfaz del Usuario
se
pregunta
menu_pregunta
Máquina de inferencia
solucion
cargar
Base de Conocimientos
Memoria de trabajo
meta
reglas
mulivaluado
preguntado
conocido
Figura 9.2 El shell del SE.
9.6.1.
Factores de certidumbre
La forma más común de trabajar con la incertidumbre consiste en asignar un factor de certidumbre a cada pieza de información en el SE. La máquina de inferencia
deberá mantener los factores de incertidumbre conforme el proceso de inferencia se
lleve a cabo.
Por ejemplo, asumamos que los factores de certidumbre (precedidos por cf) son
enteros entre -100 (definitivamente falso) y +100 (definitivamente verdadero). La siguiente base de conocimientos en formato del SE está diseñada para diagnosticar un
auto que no enciende. Esto ilustra el comportamiento de los factores de certidumbre:
1
GOAL problema.
2
3
4
5
6
RULE 1
IF not arranca AND
bateria_mala
THEN problema is bateria.
7
8
9
10
RULE 2
IF luces_debiles
THEN bateria_mala cf 50.
11
12
13
RULE 3
IF radio_debil
9.6 Encadenamiento hacı́a atrás con incertidumbre
14
113
THEN bateria_mala cf 50.
15
16
17
18
19
RULE 4
IF arranca AND
olor_gasolina
THEN problema is fuga cf 80.
20
21
22
23
24
RULE 5
IF arranca AND
indicador_gasolina is vacio
THEN problema is tanque_vacio cf 90.
25
26
27
28
29
RULE 6
IF arranca AND
indicador_gasolina is bajo
THEN problema is tanque_vacio cf 30.
30
31
32
33
ASK arranca
MENU (si no)
PROMPT ’Su motor arranca? ’.
34
35
36
37
ASK luces_debiles
MENU (si no)
PROMPT ’Sus luces están débiles? ’.
38
39
40
41
ASK radio_debile
MENU (si no)
PROMPT ’Su radio está débil? ’.
42
43
44
45
ASK olor_gasolina
MENU (si no)
PROMPT ’Huele a gasolina?’.
46
47
48
49
ASK indicador_gasolina
MENU (vacio, medio, lleno)
PROMPT ’Que indica al aguja de gasolina? ’.
Por el momento la inferencia usarı́a encadenamiento hacı́a atrás, similar al que
usa Prolog. La regla GOAL indica que el proceso buscará un valor para problema.
La regla 1 causará que la sub-meta bateria mala sea procesada, etc. Observen que
las reglas especifican también factores de certidumbre. Las reglas 2 y 3 proveen evidencia de que la baterı́a está en mal estado, pero ninguna es conclusiva al respecto.
Un diálogo con este sistema serı́a como sigue:
1
2
3
4
5
6
7
8
consultar, reiniciar, cargar, listar, trazar, cómo, salida
: consultar
Su motor arranca?
: si
Huele a gasolina?
: si
Qué indica la aguja de la gasolina?
: vacio
114
9
10
11
9 Sistemas Expertos
problema-tanque-vacio-cf-90
problema-fuga-cf-80
problema resuelto
Observen que a diferencia de Prolog, el sistema no se detiene al encontrar el
primer posible valor para problema. En este caso se computan todos los valores
razonables para problema y se reporta el valor de certidumbre asociado a estas
soluciones. Recordemos que estos factores de certidumbre no son probabilidades,
solo ponderan de alguna manera las respuestas.
De igual manera, el usuario podrı́a ofrecer factores de certidumbre sobre sus
respuestas, por ejemplo:
1
2
3
4
5
: consultar
...
Huele a gasolina?
si cf 50
...
Existen diversas maneras de capturar el concepto de factor de certidumbre, pero
todas ellas deben de confrontar las mismas situaciones básicas:
Reglas cuyas conclusiones son inciertas,
Reglas cuyas premisas son inciertas,
Datos provistos por el usuario inciertos,
Combinación de premisas inciertas con conclusiones inciertas,
Actualizar los factores de incertidumbre en los datos almacenados en el espacio
de trabajo,
Establecer un umbral sobre el cual las premisas se consideran conocidas.
9.6.2.
Factores de certidumbre à la MYCIN
MYCIN, uno de los SE más conocidos en IA, introduce factores de certidumbre
diseñados para producir resultados intuitivos desde la perspectiva de los expertos.
Revisemos el uso de estos factores por casos. El más simple, serı́a aquel donde las
premisas son totalmente ciertas:
1
2
arranca cf 100.
olor_gas cf 100.
disparan la regla 4 y por tanto, problema fuga cf 80 deberá agregarse al almacén de trabajo. Sin embargo, este es un caso poco probable. Normalmente no
estamos totalmente seguros de las premisas de una regla y lo normal serı́a tener
hechos como:
9.6 Encadenamiento hacı́a atrás con incertidumbre
1
2
115
arranca cf 80.
olor_gas cf 50.
Cuando esto sucede, la incertidumbre en las premisas de la regla debe combinarse
con las de la conclusión de la misma de la siguiente manera:
CF = CFregla × mı́nCF premisa/100
Dado el ejemplo, la regla 4 se activarı́a con un c f = 50 (el mı́nimo de las dos
premisas) y dada la fórmula anterior, agregarı́amos problema fuga cf 40 al
almacén de trabajo.
Para que una regla dispare, su factor de certidumbre debe superar un umbral que
normalmente se fija en 20. Ası́ que bajo la definición anterior, la regla 4 dispararı́a.
Si tuviésemos olor gas cf 15, entonces la regla no dispararı́a.
Ahora consideren el caso donde hay más de una regla que da soporte a cierta
conclusión. En ese caso, cada una de las reglas que disparan contribuirá al factor de
certidumbre de la conclusión. Si una regla dispara y la conclusión ya se encontraba
en el almacén de trabajo, las siguientes reglas aplican:
CF(X,Y ) = X +Y (100 − X)/100. Ambos X,Y > 0
CF(X,Y ) = X +Y /1 − mı́n(|X|, |Y |). Uno de X,Y < 0
CF(X,Y ) = −CF(−X, −Y ). Ambos X,Y < 0
Por ejemplo, si disparamos la regla 2 (luces débiles) con su premisa sin incertidumbre, tendrı́amos que agregar al almacén de trabajo bateria mala cf 50.
Luego si disparamos la regla 3 (radio débil), el factor de certidumbre de este hecho
debe modificarse a bateria mala cf 75. Lo cual resulta intuitivo (hay más
evidencia de que la baterı́a tiene problemas). Lo que también resulta intuitivo es que
necesitamos programar nuestra propia máquina de inferencia.
9.6.3.
Formato de las reglas
Como programaremos nuestra propia máquina de inferencia, podemos elegir la
estructura de hechos y reglas. Las reglas tendrán la estructura general:
regla(Nombre, Premisas,Conclusion).
El Nombre opera solo como un identificador de la regla. El lado izquierdo de
la misma Premisas implica al lado derecho Conclusion (conclusión). Como usaremos encadenamiento hacı́a atrás, cada regla será usada para validar una pieza de
información, de manera el RHS contiene una meta con su factor de certidumbre
asociado:
116
9 Sistemas Expertos
conclusion(Meta,CF).
mientras que las premisas toman la forma de una lista de metas:
premisas(ListaMetas).
Las metas serán representadas, para comenzar, como pares atributo–valor:
av(Atributo,Valor).
cuando Atributo y Valor son átomos, la estructura general de las reglas se ve
como:
1
2
3
regla(Nombre,
premisas( [av(A1,V1), av(A2,V2), ... ] ),
conclusion(av(Attr,Val), CF)).
Por ejemplo, la regla 5 quedarı́a representada como:
1
2
3
regla(5,
premisas([av(arranca,si), av(indicador_gasolina,vacio)]),
conclusion(av(problema,fuga), 80)).
Estas reglas no son fáciles de leer, pero tienen una estructura adecuada para ser
procesadas por Prolog. Otras herramientas de Prolog como las gramáticas de cláusula definitivas (DCG) o la definición de operadores, puede ayudarnos a simplificar
esta representación.
9.6.4.
La máquina de inferencia
Dado el formato de las reglas del SE deseamos que la inferencia tome en cuenta
los siguientes aspectos:
Combine los factores de certidumbre como se indico anteriormente.
Mantenga el espacio de trabajo con la información actualizada con las nuevas
evidencias obtenidas.
Encontrar toda la información acerca de un atributo en particular cuando se pregunte por él, y poner esa información en el espacio de trabajo.
Primero, los hechos serán almacenados en la memoria de trabajo de Prolog, con
el siguiente formato:
1
hecho(av(A,V),CF).
9.6 Encadenamiento hacı́a atrás con incertidumbre
117
De forma que un predicado meta/2 harı́a la llamada para resolver un problema
dado en estos términos. Por ejemplo, en el caso del arranque del auto, tendrı́amos
como meta:
1
?- meta(av(problema,X),CF).
El predicado meta/2 debe de contender con tres casos:
El atributo–valor se conoce de antemano;
Existen reglas para deducir el atributo–valor;
Se debe preguntar al usuario.
El sistema puede diseñarse para preguntar al usuario automáticamente por el valor de un atributo, ante la ausencia de reglas; o bien, se puede declarar que atributos
pueden ser preguntados al usuario. Este último enfoque hace que el manejo de la
base de conocimientos sea más explı́cito y provee mayor control sobre los diálogos
usuario – SE. Podemos definir un predicado pregunta/2 para declarar el atributo a
preguntar y la frase para ello:
1
pregunta(pais_residencia,’¿En qué paı́s vive? ’).
Veamos ahora los tres casos para meta/2. El primero de ellos ocurre cuando la
información ya está en la memoria de trabajo:
1
2
3
meta(av(Atr,Val),CF) :hecho( av(Atr,Val), CF),
!.
El segundo caso se da cuando el valor del atributo no se encuentra en la memoria
de trabajo, pero el es posible preguntar por ello al usuario:
1
2
3
4
5
6
meta(av(Atr,Val), CF) :\+ hecho( av(Atr,_),_),
pregunta(Atr,Msg),
preguntar(Atr,Msg),
!,
meta(av(Atr,Val), CF).
Para ello, el predicado preguntar/2 interroga al usuario. El usuario responde con
un valor para la atributo Atr y un factor de certidumbre asociado CF. El mensaje
Msg da la información necesaria para guiar al usuario en estas consultas:
1
2
3
4
5
preguntar(Atr,Msg) :write(Msg),
read(Val),
read(CF),
asserta(fact(av(Atr,Val),CF)).
118
9 Sistemas Expertos
El tercer caso para meta/2 es cuando el valor del atributo es desconocido, pero
se puede deducir usando las reglas definidas en el sistema, en ese caso la llamada
es:
1
2
meta(Meta,CFactual) :buscaReglas(Meta,CFactual).
Esta llamada hace uso de la máquina de inferencia que diseñaremos para nuestro
SE con incertidumbre. El factor de certidumbre se etiqueta como actual, porque es
posible que cambie de valor al ir aplicando las reglas definidas en el sistema.
El predicado buscaReglas/2 se encarga de encontrar aquellas reglas cuya conclusión unifica con la Meta en cuestión y de actualizar el factor de certidumbre con
base en las premisas de estas reglas. Si la Meta es un hecho conocido, no hay nada
que hacer, sólo regresar true:
1
2
3
4
5
6
7
8
buscaReglas(Meta,CFactual) :regla(N, premisas(ListaPremisas),
conclusion(Meta,CF)),
probar(ListaPremisas,Contador),
ajustar(CF,Contador,NuevoCF),
actualizar(Meta,NuevoCF,CFactual),
CFactual == 100,
!.
9
10
11
buscaReglas(Meta,CF) :hecho(Meta,CF).
Dada una lista de premisas pertenecientes a una regla encontrada para satisfacer
la Meta del SE, es necesario que buscaReglas/2 las pruebe. Para ello definimos
probar/2:
1
2
probar(ListaPremisas, Contador) :probAux(ListaPremisas, 100, Contador).
3
4
5
6
7
8
probAux([],Contador,Contador).
probAux([Premisa1|RestoPremisas],ContadorActual,Contador) :meta(Premisa1,CF,Cont),
Cont >= 20,
probAux(RestoPremisas,Cont,Contador).
El ajuste de los factores de certidumbre se lleva a cabo de la siguiente manera:
1
2
3
ajustar(CF1, CF2, CF) :X is CF1 * CF2 / 100,
int_redondear(X,CF).
4
5
6
7
int_redondear(X,I) :X >= 0,
I is integer(X + 0.5).
9.6 Encadenamiento hacı́a atrás con incertidumbre
119
8
9
10
11
int_redondear(X,I) :X < 0,
I is integer(X - 0.5).
La actualización de la memoria de trabajo se lleva a cabo de la siguiente manera:
1
2
3
4
5
6
actualizar(Meta,NuevoCF,CF) :hecho(Meta,ViejoCF),
combinar(NuevoCF,ViejoCF,CF),
retract(hecho(Meta,ViejoCF)),
asserta(hecho(Meta,CF)),
!.
7
8
9
actualizar(Meta,CF,CF) :asserta(hecho(Meta,CF)).
10
11
12
13
14
15
combinar(CF1, CF2, CF) :CF1 >= 0,
CF2 >= 0,
X is CF1 + CF2*(100 - CF1)/100,
int_redondear(X,CF).
16
17
18
19
20
21
22
combinar(CF1,CF2,CF) :CF1 < 0,
CF2 < 0,
X is -( -CF1-CF2*(100+CF1)/100),
int_redondear(X,CF).
23
24
25
26
27
28
29
combinar(CF1,CF2,CF) :(CF1 < 0 ; CF2 < 0),
(CF1 > 0 ; CF2 > 0),
abs_minimum(CF1,CF2,MCF),
X is 100 * (CF1 + CF2) / (100 - MCF),
int_redondear(X,CF).
9.6.5.
Interfaz con el usuario
La interfaz con el usuario es muy parecida a la definida en la sección anterior. Se
incluyen predicados auxiliares necesarios en su definición:
1
2
3
4
5
6
se :repeat,
write(’consultar, cargar, salir’), nl,
write(’: ’),
read_line(X),
ejec(X),
120
7
9 Sistemas Expertos
X == salir.
8
9
10
11
ejec(consultar) :metas_principales,
!.
12
13
14
15
ejec(cargar) :cargar_reglas,
!.
16
17
ejec(salir).
18
19
%% % Auxiliares
20
21
22
23
24
25
26
metas_principales :meta_principal(Atr),
principal(Atr),
imprime_meta(Atr),
fail.
metas_principales.
27
28
29
30
31
principal(Atr) :meta(av(Atr,Val,CF)),
!.
principal(_) :- true.
32
33
34
35
36
37
38
39
40
41
imprime_meta(Atr) :nl,
hecho(av(Atr,Val), CF),
CF >= 20,
salidap(av(Atr,Val),CF), nl
fail.
imprime_meta(Atr) :write (’Meta: ’), write(Attr), write(’ solucionada.’),
nl, nl.
42
43
44
45
46
47
48
salidap(av(Atr,Val),CF) :output(Atr,Val,ListaImprimir),
write(Atr-’cf’-CF),
imprimeLista(ListaImprimir), !.
salidap(av(Atr,Val),CF) :write(Atr-Val-’cf’-CF).
49
50
51
52
53
imprimeLista([]).
imprimeLista([X|Xs]) :write(X),
imprimeLista(Xs).
Capı́tulo 10
Arboles de Decisión
Resumen En este capı́tulo abordaremos la solución de problemas en el contexto del
aprendizaje automático, ejemplificado con el algoritmo ID3 [13] (Inductive Dicotomizer). Este algoritmo induce árboles de decisión a partir de ejemplos conformados
como un conjunto de pares atributo–valor, para predecir el valor de uno de los atributos, conocido como la clase. El aprendizaje de árboles de decisión es una de las
técnicas de inferencia inductiva más usadas. Se trata de un método para aproximar
funciones de valores discretos, capaz de expresar hipótesis disyuntivas y robusto
al ruido en los ejemplos de entrenamiento. La descripción que se presenta en este
capı́tulo, cubre una familia de algoritmos para la inducción de árboles de decisión
que incluyen ID3 y C4.5 [14]. Estos algoritmos llevan a cabo su búsqueda de hipótesis en un espacio completamente expresivo, evitando ası́ los problemas mencionados
con respecto a espacios de hipótesis incompletos. Como veremso, el sesgo inductivo
en este caso, consiste en la preferencia por árboles pequeños, sobre árboles grandes.
Un árbol ası́ aprendido, puede representarse también como un conjunto de reglas
si-entonces, más fáciles de entender para un usuario.
10.1.
Representación de los árboles de decisión
La figura 10.1 muestra un árbol de decisión tı́pico. Cada nodo del árbol está conformado por un atributo y puede verse como la pregunta: ¿Qué valor tiene este
atributo en el caso a clasificar? Las ramas que salen de los nodos, corresponden a
los posibles valores del atributo correspondiente.
Un árbol de decisión clasifica a un caso, filtrandolo de manera descendente, hasta encontrar una hoja, que corresponde a la clasificación buscada. Consideren el
proceso de clasificación del siguiente caso, que describe un dı́a en partı́cular:
h cielo = soleado,temperatura = caliente, humedad = alta, viento = f uerte i
121
122
10 Arboles de Decisión
Atributo
Cielo
Clase
Valor
nublado
lluvioso
soleado
Húmedad
alta
no
si
Viento
fuerte
normal
si
no
débil
si
Figura 10.1 Un ejemplo de arbol de decisión para el concepto “buen dı́a para jugar tenis”. Los
nodos representan un atributo a ser verificado por el clasificador. Las ramas son los posibles
valores para el atributo en cuestión. Los textos en cı́rculos, representan las clases consideradas,
i.e., los valores posibles del atributo objetivo.
Como el atributo Cielo, tiene el valor soleado en el caso, éste es filtrado hacı́a
abajo del árbol por la rama de la izquierda. Como el atributo Humedad, tiene el
valor alta, el ejemplo es filtrado nuevamente por rama de la izquierda, lo cual nos
lleva a la hoja que indica la clasificación del caso: Buen dı́a para jugar tenis = no.
El Algoritmo 2, define computacionalmente esta idea.
Algoritmo 2 El algoritmo clasifica, para árboles de decisión
1: function C LASIFICA(Ej, Arbol)
Require: E j: un ejemplo a clasificar, Arbol: un árbol de decisión
Ensure: Clase: la clase del ejemplo
2:
Clase ← tomaValor(raiz(Arbol), E j);
3:
if hoja(raı́z(Arbol)) then
4:
return Clase
5:
else
6:
clasi f ica(E j, subArbol(Arbol,Clase));
7:
end if
8: end function
La función toma-valor encuentra el valor de un atributo, en el caso que se
está clasificando. El predicado hoja es verdadero si su argumento es un nodo terminal del árbol y falso si se trata de un nodo interno. La función sub-árbol se
mueve por la rama del árbol que corresponde al valor del atributo probado en el caso. De esta forma, obtiene un sub-árbol. En nuestro ejemplo, a partir del nodo raı́z
cielo, esta función obtiene el sub-árbol que resulta de moverse por la rama soleado,
etc.
En general, un árbol de decisión representa una disyunción de conjunciones de
restricciones en los posibles valores de los atributos de los casos. Cada rama que va
10.2 Problemas apropiados para la aplicación de árboles de decisión
123
de la raı́z del árbol a una hoja, representa una conjunción de tales restricciones y el
árbol mismo representa la disyunción de esas conjunciones. Por ejemplo, el árbol
de la figura 10.1, puede expresarse como sigue:
(cielo = soleado ∧ humedad = normal)
∨ (cielo = nublado)
∨ (cielo = lluvia ∧ viento = d ébil)
10.2.
Problemas apropiados para la aplicación de árboles de
decisión
Aun cuando se han desarrollado diversos métodos para la inducción de árboles
de decisión, y cada uno de ellos ofrece diferentes capacidades, en general estos algoritmos son apropiados para solucionar problemas de aprendizaje conocidos como
problemas de clasificación. Estos problemas presentan las siguientes caracterı́sticas:
Ejemplos representados por pares atributo-valor. Los casos del problema están
representados como un conjunto fijo de atributos, por ejemplo Cielo y sus valores, por ej. Soleado. El caso más sencillo es cuando cada atributo toma valores de
un pequeño conjunto discreto y cada valor es disjunto, por ejemplo {Soleado, Nublado, Lluvia}. Existen extensiones para trabajar con atributos de valores reales,
por ejemplo, Temperatura expresado numéricamente.
La función objetivo tiene valores discretos. El árbol de decisión de la Figura 10.1,
asigna una clasificación binaria, por ejemplo si o no a cada caso. Un árbol de decisión puede ser extendido fácilmente, para representar funciones objetivos con
más de dos valores posibles. Una extensión menos simple consiste en considerar funciones objetivo de valores discretos, por ello la aplicación del método en
dominios discretos es menos común.
Se necesitan descripciones disyuntivas. Como se mencionó, los árboles de decisión representan naturalmente conceptos disyuntivos.
Ruido en los ejemplos de entrenamiento. El método es robusto al ruido en los
ejemplos de entrenamiento, tanto errores de clasificación, como errores en los
valores de los atributos.
Valores faltantes en los ejemplos. El método puede usarse aún cuando algunos
ejemplos de entrenamiento tengan valores desconocidos para algunos atributos.
Al igual que en el punto anterior, esto se debe a que el algoritmo computa estadı́sticas globales que minimizan el impacto del ruido o falta de información de
un ejemplo.
124
10.3.
10 Arboles de Decisión
El algoritmo básico de aprendizaje de árboles de decisión
La mayorı́a de los algoritmos para inferir árboles de decisión son variaciones de
un algoritmo básico que emplea una búsqueda descendente (top-down) y egoı́sta
(greedy) en el espacio de posibles árboles de decisión. La presentación de estos
algoritmos se centra en ID3 y C4.5.
El algoritmo básico ID3, construye el árbol de decisión de manera descendente,
comenzando por preguntarse: Qué atributo deberı́a ser colocado en la raı́z del árbol?
Para responder esta pregunta, cada atributo es evaulado usando un test estadı́stico
para determinar que tan bien clasifica él solo los ejemplos de entrenamiento. El mejor atributo es seleccionado y colocado en la raı́z del árbol. Una rama y su nodo
correspondiente es entonces creada para cada valor posible del atributo en cuestión.
Los ejemplos de entrenamiento son repartidos en los nodos descendentes de acuerdo al valor que tengan para el atributo de la raı́z. El proceso entonces se repite con
los ejemplos ya distribuidos, para seleccionar un atributo que será colocado en cada
uno de los nodos generados. Generalmente, el algoritmo se detiene si los ejemplos
de entrenamiento comparten el mismo valor para el atributo que está siendo probado. Sin embargo, otros criterios para finalizar la búsqueda son posibles: i) Covertura
mı́nima, el número de ejemplos cubiertos por cada nodo está por abajo de cierto
umbral; ii) Pruebas de significancia estadı́stica usando χ 2 para probar si las distribuciones de las clases en los sub-árboles difiere significativamente. Aunque, como
veremos, la poda del árbol se prefiere a las pruebas de significancia. Este algoritmo
lleva a cabo una búsqueda egoı́sta de un árbol de decisión aceptable, sin reconsiderar nunca las elecciones pasadas (backtracking). Una versión simplificada de él se
muestra en el Algoritmo 3.
10.3.1.
¿Qué atributo es el mejor clasificador?
La decisión central de ID3 consiste en seleccionar qué atributo colocará en cada
nodo del árbol de decisión. En el algoritmo presentado, esta opción la lleva a cabo
la función mejor-partición, que toma como argumentos un conjunto de ejemplos de entrenamiento y un conjunto de atributos, regresando la partición inducida
por el atributo, que sólo, clasifica mejor los ejemplos de entrenamiento. Considere
los ejemplos de entrenamiento del cuadro 10.1 para el concepto objetivo: buen dı́a
para jugar tenis? El encabezado del cuadro indica los atributos usados para describir
estos ejemplos, siendo jugar-tenis? el atributo objetivo.
Si queremos particionar este conjunto de ejemplos con respecto al atributo temperatura, obtendrı́amos:
?- partition(temperatura, Ejemplos).
Ejemplos= [[temperatura [frio 5 6 7 9]
[caliente 1 2 3 13]
[templado 4 8 10 11 12 14]]
10.3 El algoritmo básico de aprendizaje de árboles de decisión
125
Algoritmo 3 El algoritmo ID3
1: function ID3(Ejs, Atbs, Clase)
2:
Arbol ← 0;
/ De f ault ← claseMayoria(E js);
3:
if E js = 0/ then
4:
return De f ault;
5:
else if mismoValor(E js,Clase) then
6:
return Arbol ← tomaValor( f irst(E js).Clase);
7:
else if Atbs = 0/ then
8:
return Arbol ← valorMasComun(E js,Clase);
9:
else
10:
Me jorParticion ← Me jorParticion(E js, Atbs);
11:
Me jorAtributo ← f irst(Me jorParticion);
12:
Arbol ← Me jorAtributo;
13:
for all ParticionE js ∈ rest(Me jorParticion) do
14:
ValoAtributo ← f irst(ParticionE js);
15:
SubE js ← rest(ParticionE js);
16:
agregarRama(Arbol, alorAtributo, ID3(SubE js, {Atbs
Me jorAtributo},Clase));
17:
end for
18:
return Arbol
19:
end if
20: end function
Dı́a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
\
Cielo Temperatura Humedad Viento Jugar-tenis?
soleado
calor
alta
débil
no
soleado
calor
alta
fuerte
no
nublado
calor
alta
débil
si
lluvia
templado
alta
débil
si
lluvia
frı́o
normal débil
si
lluvia
frı́o
normal fuerte
no
nublado
frı́o
normal fuerte
si
soleado templado
alta
débil
no
soleado
frı́o
normal débil
si
lluvia
templado
normal débil
si
soleado templado
normal fuerte
si
nublado templado
alta
fuerte
si
nublado
calor
normal débil
si
lluvia
templado
alta
fuerte
no
Cuadro 10.1 Conjunto de ejemplos de entrenamiento para el concepto objetivo jugar-tenis? en
ID3, por Tom M. Mitchel [10].
Lo que significa que el atributo temperatura tiene tres valores diferentes en el
conjunto de entrenamiento: frı́o, caliente, y templado. Los casos d5, d6, d7, y d9,
tienen como valor del atributo temperatura= frı́o. La función mejor-partición encuentra el atributo que mejor separa los ejemplos de entrenamiento de acuerdo al
atributo objetivo. En qué consiste una buena medida cuantitativa de la bondad de un
atributo? Definiremos una propiedad estadı́stica llamada ganancia de información.
126
10.3.2.
10 Arboles de Decisión
Entropı́a y ganancia de información
Una manera de cuantificar la bondad de un atributo en este contexto, consiste en
considerar la cantidad de información que proveerá este atributo, tal y como ésto
es definido en teorı́a de información por Claude E. Shannon [18]. Un bit de información es suficiente para determinar el valor de un atributo booleano, por ejemplo,
si/no, verdader/falso, 1/0, etc., sobre el cual no sabemos nada. En general, si los
posibles valores del atributo vi , ocurren con probabilidades P(vi ), entonces en contenido de información, o entropia, E de la respuesta actuale está dado por:
n
E(P(vi ), . . . , P(vn )) = ∑ −P(vi ) log2 P(vi )
i=1
Consideren nuevamente el caso booleano, aplicando esta ecuación a un volado
con una moneda confiable, tenemos que la probabilidad de obtener aguila o sol es
de 1/2 para cada una:
1
1 1
1
1 1
E( , ) = − log2 − log2 = 1
2 2
2
2 2
2
Ejecutar el volado nos provee 1 bit de información, de hecho, nos provee la clasificación del experimento: si fue aguila o sol. Si los volados los ejecutamos con una
moneda cargada que da 99 % de las veces sol, entonces E(1/100, 99/100) = 0,08
bits de información, menos que en el caso de la moneda justa, porque ahora tenemos más evidencia sobre el posible resultado del experimento. Si la probabilidad de
que el volado de sol es del 100 %, entonces E(0, 1) = 0 bits de información, ejecutar el volado no provee información alguna. La gráfica de la función de entropı́a se
muestra en la figura 10.2.
Figura 10.2 Gráfica de la función entropia para clasificaciones booleanas.
Consideren nuevamente los ejemplos de entrenamiento del cuadro 10.1. De 14
ejemplos, 9 son positivos (si es un buen dı́a para jugar tenis) y 5 son negativos. La
entropia de este conjunto de entrenamiento es:
E(
9 5
, ) = 0,940
14 14
10.4 Espacio de hipótesis en el aprendizaje inductivo de árboles de decisión
127
Si todos los ejemplos son positivos o negativos, por ejemplo, pertencen todos a
la misma clase, la entropia será 0. Una posible interpretación de ésto, es considerar
la entropia como una medida de ruido o desorden en los ejemplos. Definimos la ganancia de información (GI) como la reducción de la entropı́a causada por particionar
un conjunto de entrenamiento S, con respecto a un atributo A:
|Sv |
E(Sv )
v∈A |S|
Ganancia(S, A) = E(S) − ∑
Observen que el segundo término de Ganancia, es la entropı́a con respecto al
atributo A. Al utilizar esta medida en ID3, sobre los ejemplos del cuadro 10.1, obtenemos:
Ganancia de informacion del atributo CIELO : 0.24674976
Ganancia de informacion del atributo TEMPERATURA : 0.029222548
Ganancia de informacion del atributo HUMEDAD : 0.15183544
Ganancia de informacion del atributo VIENTO : 0.048126936
Maxima ganancia de informacion: 0.24674976
Particion:
[cielo [soleado 1 2 8 9 11] [nublado 3 7 12 13]
[lluvia 4 5 6 10 14]]
Esto indica que el atributo con mayor ganancia de información fue cielo, de
ahı́ que esta parte del algoritmo genera la partición de los ejemplos de entrenamiento con respecto a este atributo. Si particionamos recursivamente los ejemplos
que tienen el atributo cielo = soleado, obtendrı́amos:
Ganancia de informacion del atributo TEMPERATURA : 0.5709506
Ganancia de informacion del atributo HUMEDAD : 0.9709506
Ganancia de informacion del atributo VIENTO : 0.01997304
Maxima ganancia de informacion: 0.9709506
Particion:
[humedad [normal 11 9] [alta 8 2 1]]
Lo cual indica que en el nodo debajo de soleado deberı́amos incluir el atributo
humedad. Todos los ejemplos con humedad = normal, tienen valor si para el concepto objetivo. De la misma forma, todos los ejemplos con valor humedad = alta,
tiene valor no para el concepto objetivo. Ası́ que ambas ramas descendiendo de nodo humedad, llevarán a clases terminales de nuestro problema de aprendizaje. El
algoritmo terminará por construir el árbol de la figura 10.1.
10.4.
Espacio de hipótesis en el aprendizaje inductivo de
árboles de decisión
Como los otros métodos de aprendizaje, ID3 puede concebirse como un proceso
de búsqueda en un espacio de hipótesis, para encontrar aquella hipótesis que se
ajusta mejor a los datos de entrenamiento. El espacio de hipótesis explorado por
ID3 es el espacio de todos los árboles de decisión posibles. El algoritmo lleva a
128
10 Arboles de Decisión
cabo una búsqueda de lo simple a lo complejo, comenzando por el árbol vacı́o, para
considerar cada vez hipótesis más complejas. La medida ganancia de información
guı́a esta búsqueda de ascenso de colina (hill-climbing), como ejemplificamos en la
sección anterior.
Considerando ID3 en términos de su espacio y estrategias de búsqueda, es posible
analizar sus capacidades y limitaciones:
El espacio de hipótesis de ID3 es completo con respecto a las funciones de valores
discretos que pueden definirse a partir de los atributos considerados. De manera
que no existe el riesgo que la función objetivo no se encuentre en el espacio de
hipótesis.
ID3 mantiene sólo una hipótesis mientras explora el espacio de hipótesis posibles. Esto contrasta, por ejemplo, con el algoritmo eliminación de candidatos,
que mantiene el conjunto de todas las hipótesis consistentes con el conjunto de
entrenamiento. Es por ello que ID3 es incapaz de determinar cuantos árboles de
decisión diferentes son consistentes con los datos.
El algoritmo básico ID3 no ejecuta vuelta atrás (backtracking) en su búsqueda.
Una vez que el algoritmo selecciona un atributo, nunca reconsiderará esta elección. Por lo tanto, es suceptible a los mismos riesgos que los algoritmos estilo
ascenso de colina, por ejemplo, caer máximos o mı́nimos locales. Como veremos, la vuelta atrás puede implementarse con alguna técnica de poda.
ID3 utiliza todos los ejemplos de entrenamiento en cada paso de su búsqueda
guiada por el estadı́stico ganancia de información. Esto contrasta con los métodos que usan los ejemplos incrementalmente, por ejemplo encuentra-S o eliminación de candidatos. Una ventaja de usar propiedades estadı́sticas de todos los
ejemplos es que la búsqueda es menos sensible al ruido en los datos.
10.5.
Sesgo inductivo en el aprendizaje de árboles de decisión
Recuerden que el sesgo inductivo es el conjunto de afirmaciones que, junto con
los datos de entrenamiento, justifican deductivamente la clasificación realizada por
un sistema de aprendizaje inductivo sobre casos futuros. Dado un conjunto de entrenamiento, por lo general hay muchos árboles de decisión consistentes con éste.
Describir el sesgo inductivo de ID3 equivale a explicar porqué este algoritmo prefiere ciertos árboles a otros, qué árbol eligirá.
Puesto que ID3 encontrará el primer árbol consistente con el conjunto de entrenamiento, producto de una búsqueda de ascenso de colina, de lo simple a lo complejo,
el algoritmo tiene preferencia por: i) árboles pequeños sobre árboles grandes, que
indican que la búsqueda termino en proximidad a la raı́z del árbol; y ii) debido a
su caracter egoista, árboles que colocan atributos más informativos cerca de la raı́z
del árbol. Sin embargo, observen que este sesgo es aproximado. Un algoritmo que
tuviera un sesgo idéntico al descrito aquı́, tendrı́a que realizar una búsqueda primero en amplitud y preferir los árboles de menor profundidad. ID3 busca primero en
profundidad.
10.6 Consideraciones sobre el aprendizaje inductivo de árboles de decisión
10.5.1.
129
Sesgo por restricción y sesgo por preferencia
Existe una diferencia interesante entre los sesgos que exhiben ID3 y el algoritmo eliminación de candidatos, discutido en la sesión anterior. El sesgo de ID3
es producto de su estratégia de búsqueda, mientras que el sesgo de eliminación de
candidatos es resultado de la definición del espacio de búsqueda. Por lo tanto, el
sesgo de ID3 es exhibe una preferencia por ciertas hipótesis, sobre otras, por ejemplo, hipótesis compactas. Este tipo de sesgo, que no impone restricciones sobre
las hipótesis que serán eventualmente consideradas, recibe el nombre de sesgo por
preferencia. Por otra parte, el sesgo de eliminación de candidatos que restringe el
conjunto de hipótesis a considerar, recibe el nombre de sesgo por restricción o sesgo
del lenguaje.
En general, es preferible trabajar con un sesgo por preferencia, puesto que éste
permite al sistema de aprendizaje explorar un espacio de hipótesis completo, asegurando que la representación del concepto objetivo se encuentra ahı́. Consideren
que es posible trabajar con ambos sesgos a la ves, por ejemplo, el sistema aprendiz
de damas chinas de la sesión de introducción, introduce un sesgo por restricciones
cuando se decide que la hipótesis tiene la forma de una combinación lineal de los
atributos del tablero, y un sesgo por preferencia cuando se introduce la búsqueda
ordenada por mı́nimos cuadrados (LMS) en el espacio de posibles parámetros wi .
10.5.2.
¿Porqué preferir hipótesis más compactas?
Es el sesgo inductivo de ID3, preferir las hipótesis más compactas, lo suficientemente robusto para generalizar más allá de los datos observados? Este es un debate
no resuelto iniciado por William de Occam1 circa 1320. Un argumento intuitivo
es que existen mucho menos hipótesis compactas que extensas, por lo que es más
difı́cil que una hipótesis compacta coincida accidentalmente con los datos observados. En cambio, hay muchas hipótesis extensas que se puede, ajustar a los datos
de entrenamiento, pero fallarán al generalizar. Aunque este argumento no es del
todo convincente, dejaremos la discusión sobre la navaja de Occam para la sesión
destinada a aprendizaje Bayesiano.
10.6.
Consideraciones sobre el aprendizaje inductivo de árboles
de decisión
Algunas consideraciones sobre la aplicación práctica del algoritmo básico de ID3
presentado aquı́, incluyen: mecanismos para determinar que tanto debe crecer el
1
El enunciado exacto de la navaja de Occam es: Non sunt multiplicanda entia prater necessitatem
(las entidades no deben multiplicarse más allá de lo necesario).
130
10 Arboles de Decisión
árbol en profundidad; para procesar atributos con valores contı́nuos; para procesar
ejemplos de entrenamiento con valores faltantes; para introducir costos diferentes
asociados a los atributos; ası́ como para determinar una buena métrica de selección
de los atributos y mejorar la eficiencia computacional del algoritmo. Cabe mencionar que, muchos de estos aspectos han sido incorporados en el sistema C4.5 [14].
10.6.1.
Evitando un sobreajuste con los datos de entrenamiento
El algoritmo básico de ID3 crece cada rama del árbol en profundidad hasta que
logra clasificar perfectamente los ejemplos de entrenamiento. Esta estrategia es razonable, pero puede introducir dificultades si los datos de entrenamiento presentan
ruido, o cuando el conjunto de entrenamiento es demasiado pequeño, como para
ofrecer un muestreo significativo del concepto objetivo. En estos casos, ID3 puede producir árboles que se sobreajustan a los datos de entrenamiento. Formalmente
definimos el sobreajuste como:
Definition 10.1. Dado un espacio de hipótesis H, se dice que una hipótsis h ∈ H
está sobreajustada a los ejemplos de entrenamiento, si existe una hipótesis alternativa h0 ∈ H, tal que h0 tiene un error de clasificación más pequeño que h sobre la
distribución completa de los casos del problema.
Es común observar que a medida que el tamaño del árbol crece, en término del
número de nodos usado2 , su precisión sobre el conjunto de entrenamiento mejora
monotonicamente, pero, sobre el conjunto de prueba primero crece y luego decae.
Como es esto posible que un árbol h que tiene mayor precisión que h0 sobre el
conjunto de entrenamiento, luego tenga un desempeño menor sobre el conjunto de
prueba? Una situación en la que esto ocurre es cuando el conjunto de entremiento contiene ruido, por ejemplo, elementos mal clasificados. Consideren agregar el
siguiente caso mal clasificado (clasificado como jugar-tenis? = no) al conjunto de
entrenamiento del cuadro 10.1:
h cielo = soleado,temperatura = caliente, humedad = normal, viento = f uerte i
Al ejecutar ID3 sobre el nuevo conjunto de entrenamiento, éste construirá un
árbol más complejo. En partı́cular, el ejemplo con ruido será filtrado junto con los
ejemplos d9 y d11 (cielo = soleado y humedad = normal), que son ejemplos positivos. Dado que el nuevo ejemplo es negativo, ID3 buscará refinar el árbol a partir del
nodo humedad, agregando un atributo más al árbol. Este nuevo árbol h0 tiene mayor precisión sobre los ejemplos de entrenamiento que h, puesto que se ha ajustado
al ejemplo con ruido. Pero h tendrá mejor desempeño al clasificar nuevos casos,
tomados de una misma distribución que los ejemplos de entrenamiento.
2
Obseven que esto refleja el número de atributos usado en la hipótesis, esto es, árboles más grandes
imponen más restricciones.
10.6 Consideraciones sobre el aprendizaje inductivo de árboles de decisión
131
Existe la posibilidad de sobreajuste, aún cuando el conjunto de entrenamiento
esté libre de ruido, por ejemplo, si el conjunto de entrenamiento tiene pocos elementos. En conjuntos de entrenamiento pequeños es fácil encontrar regularidades
accidentales en donde un atributo puede particionar muy bien los ejemplos dados,
aunque no esté relacionado con el concepto objetivo.
Puesto que el sobreajuste puede reducir la precisión de un árbol inducido por
ID3 entre un 10 a 25 %, diferentes enfoques han sido propuestos para evitar este
fenómeno. Los enfoques pueden agruparse en dos clases:
Enfoques que detienen el crecimiento del árbol anticipadamente, antes de que
alcance un punto donde clasifique perfectamente los ejemplos de entrenamiento.
Enfoques en donde se deja crecer el árbol para después podarlo.
Aunque el primer enfoque parezca más directo, la poda posterior del árbol ha
demostrado tener más éxito en la práctica. Esto se debe a la dificultad de estimar
en que momento debe detenerse el crecimiento del árbol. Independientemente del
enfoque usado, una pregunta interesante es: ¿Cual es el tamaño correcto de un árbol?
Algunos enfoques para responder a esta pregunta incluyen:
Usar un conjunto de ejemplos, diferentes de los usados en el entrenamiento, para
evaluar la utilidad de eliminar nodos del árbol.
Usar los ejemplos disponibles para el entrenamiento, pero aplicando una prueba
para estimar cuando agregar o eliminar un nodo, podrı́a producir una mejora al
clasificar nuevos casos, por ejemplo, usar el test χ 2 para evaluar si al expandir un
nodo, el cambio mejorará la clasificación sobre los ejemplos de entrenamiento, o
sobre toda la distribución.
Usar explı́citamente una medida de complejidad para codificar los ejemplos de
entrenamiento y el árbol de decisión, deteniéndo el crecimiento cuando el tamaño codificado sea minimizado. Por ejemplo, el principio de descripción mı́nima (MDL).
10.6.1.1.
Reduciendo el error por poda
¿Como podemos usar un conjunto de ejemplos de validación para prevenir el
sobre ajuste? Un enfoque llamado reduced-error pruning [?], consiste en considerar
cada nodo del árbol como candidato a ser podado. La poda consiste en eliminar todo
el subárbol que tiene como raı́z el nodo en cuestión, convirtiéndolo ası́ en una hoja,
cuya clase corresponde a valor más común de los casos asociados a ese nodo.
Un nodo solo es eliminado si el árbol podado que resulta de ello, no presenta
un desempeño peor que el árbol original sobre el conjunto de validación. El efecto
de esto, es que los nodos que se han colocado en el árbol por coincidencias fortuitas en los datos del entrenamiento, generalmente son eliminados debido a que las
coincidencias suelen no estar presentes en el conjunto de validación.
Este método es unicamente efectivo si contamos con suficientes ejemplos, de tal
forma que el conjunto de entrenamiento y el conjunto de validación sean significativos estadı́sticamente. De otra forma, tomar ejemplos para el conjunto de validación
132
10 Arboles de Decisión
reduce aún más el tamanõ del conjunto de entrenamiento, aumentando ası́ la posibilidad de sobre ajuste.
10.6.1.2.
Poda de reglas
En la práctica, un método exitoso para encontrar el árbol de mayor precisión se
conoce como rule post-prunning [14] y está incorporado en el sistema C4.5 de Ross
Quinlan. El procedimiento es el siguiente:
1. Inducir el árbol de decisión permitiendo sobre ajuste, por ejemplo, con nuestro
algoritmo básico ID3.
2. Convertir el árbol aprendido en un conjunto de reglas equivalente, esto es, una
conjunción por cada rama del árbol que va de la raı́z a una hoja.
3. Podar (generalizar) cada regla, eliminando las precondiciones que resulten en
una mejora de la precisión estimada.
4. Ordenar las reglas por su precisión estimada, y aplicarlas en ese orden al clasificar
nuevos casos.
Cabe mencionar que el método aplicado por C4.5 no es estadı́sticamente valido,
aunque ha demostrado ser una heurı́stica útil. En la sesión de evaluación de hipótesis, estudiamos técnicas estadı́sticamente robustas para estimar medias e intervalos
de confianza. Lo relevante aquı́ es que la conversión del árbol en reglas ayuda a distinguir los diferentes contextos en los que un atributo participa en la clasificación, es
decir, reglas diferentes; elimina la diferencia entre nodos ubicados cerca de la raı́z y
aquellos ubicados cerca de las hojas; y aumenta la fácilidad de comprehensión por
parte del usuario.
10.6.2.
Incorporando valores contı́nuos
En el algoritmo básico de ID3 tanto el concepto objetivo, como los atributos
usados para describir los casos, deben tener valores discretos. La segunda restricción puede ser eliminada fácilmente, permitiendo el uso de atributos con valores
contı́nuos. Esto se logra definiéndo dinámicamente nuevos atributos discretos que
particionan los atributos de valores contı́nuos, en intervalos discretos. Para un atributo contı́nuo A, el algoritmo puede crear dinámicamente un atributo discreto Ac que
es verdadero si A > c y falso en cualquier otro caso. La única consideración es como
seleccionar el mejor valor para el umbral c. Supongan que el atributo temperatura
toma valores discretos y que su relación con el concepto objetivo es la siguiente:
temperatura 40 48 60 72 80 90
jugar-tenis? No No Si Si Si No
Qué valor booleano basado en un umbral debemos definir para el atributo temperatura? Obviamente, necesitamos un umbral c, tal que éste produzca la mayor
10.6 Consideraciones sobre el aprendizaje inductivo de árboles de decisión
133
ganancia de información posible. Es posible generar candidatos a umbral, ordenando los ejemplos de acuerdo a su valor en el atributo temperatura e identificando
ejemplos adyacentes que difieren en el valor de su atributo objetivo. Se puede demostrar que los umbrales c que máximiza la ganancia de información, se encuentran
en estos sitios. Para el ejemplo presentado, dos umbrales pueden localizarse en los
puntos (48 + 60)/2 y (80 + 90/2). La ganancia de información puede entonces calcularse para los atributos temperatura>54 y temperatura>85 . El atributo con mayor
ganancia de información, en este caso el primero, puede ser usado entonces para
competir con otros atributos en la construcción del árbol de decisión. Por supuesto, es posible también mantener ambos atributos dinámicamente creados, usando
multiples intervalos [?].
10.6.3.
Medidas alternativas para la selección de atributos
Existe un sesgo natural en la medida de ganancia de información, el cual favorece
atributos con muchos valores, sobre aquellos que tienen poco valores. Por ejemplo,
un atributo fecha, tendrı́a mayor ganancia de información que cualquiera de los atributos en nuestro ejemplo. Esto se debe a que este atributo predice perfectamente
el valor del atributo objetivo. El problema es que este atributo tiene tantos valores
distintos que tiende a separar perfectamente los ejemplos de entrenamiento en pequeños subconjuntos, que se ajustan al concepto buscado. Por esto, el atributo fecha
tiene una ganancia de información elevada, a pesar de ser un predictor pobre.
Una solución a este problema es usar una métrica alternativa a la ganancia de información. Quinlan [?], propone una medida alternativa que ha sido usada con éxito,
gain ratio. Esta métrica penaliza atributos como fecha incorporando un término conocido como split information, que es sensible a qué tan amplia y uniforme es la
partición que un atributo induce en los datos:
c
|Si|
|Si|
log2
|S|
|S|
i=1
splitIn f ormation(S, A) = − ∑
Observen que este término es la entropia de S con respecto al atributo A. La
medida gain radio está definida como:
gainRatio(S, A) =
gain(S, A)
splitIn f ormatio(S, A)
Un problema práctico con este enfoque es que el denominador de esta medida
puede ser 0 o muy pequeño, si |Si| ≈ |S|, lo cual hace que la medida sea indefinida
para atributos que tienen casi el mismo valor para todos los ejemplos.
134
10.7.
10 Arboles de Decisión
Implementación el Prolog
Los ejemplos de entrenamiento se definirán mediante el predicado example/3
cuyos argumentos son el número de ejemplo, el valor para la clase, y los pares atributo valor. Para el ejemplo de jugar tenis, el conjunto de entrenamiento incluirá las
siguientes lı́neas:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ejemplo(1,no,[dia=soleado,temperatura=calor,humedad=alta,viento=debil]).
ejemplo(2,no,[dia=soleado,temperatura=calor,humedad=alta,viento=fuerte]).
ejemplo(3,si,[dia=nublado,temperatura=calor,humedad=alta,viento=debil]).
ejemplo(4,si,[dia=lluvia,temperatura=templado,humedad=alta,viento=debil]).
ejemplo(5,si,[dia=lluvia,temperatura=frio,humedad=normal,viento=debil]).
ejemplo(6,no,[dia=lluvia,temperatura=frio,humedad=normal,viento=fuerte]).
ejemplo(7,si,[dia=nublado,temperatura=frio,humedad=normal,viento=fuerte]).
ejemplo(8,no,[dia=soleado,temperatura=templado,humedad=alta,viento=fuerte]).
ejemplo(9,si,[dia=soleado,temperatura=frio,humedad=normal,viento=debil]).
ejemplo(10,si,[dia=lluvia,temperatura=templado,humedad=normal,viento=debil]).
ejemplo(11,si,[dia=soleado,temperatura=nublado,humedad=normal,viento=fuerte]).
ejemplo(12,si,[dia=nublado,temperatura=templado,humedad=alta,viento=fuerte]).
ejemplo(13,si,[dia=nublado,temperatura=calor,humeda=normal,viento=debil]).
ejemplo(14,no,[dia=lluvia,temperatura=templado,humedad=alta,viento=fuerte]).
El árbol se representará mediante el predicado nodo/3 cuyo primer elemento
puede ser el identificador de un ejemplo, o la constante ho ja para indicar que hemos
encontrado el valor de la clase. El segundo argumento de nodo es el test que se aplica
para llegar a él. El tercer argumento es el padre del nodo en cuestión. Por ejemplo:
1
nodo(13,(dia=lluvia),raiz).
indica que al ejemplo 13 se llega desde el nodo raiz si el atributo dia tiene como
valor lluvia.
La llamada principal al algoritmo es id3/1 cuyo argumento es el mı́nimo número
de casos que debe cubrir una hoja del árbol:
1
id3 :- id3(1).
% Umbral = 1, por default.
2
3
4
5
6
7
8
id3(Umbral) :retractall(nodo(_,_,_)),
findall(N,ejemplo(N,_,_),E),
ejemplo(_,_,L), !,
atributos(L,A),
idt(E,raiz,A,Umbral), !.
Con la llamada a id3 se borran los nodos construidos anteriormente y se construye una nueva lista de ejemplos E. Posteriormente se obtiene la lista de atributos
usados en los ejemplos A. El corte es para construir la lista de atributos solo con un
ejemplo (evita el reconsiderar con los demás ejemplos). Y se llama a idt/4 cuyos
argumentos son la lista de ejemplos E, el identificador raiz para el nodo raı́z del
árbol, la lista de atributos A y el Umbral que por defecto es igual a 1.
10.7 Implementación el Prolog
135
En la construcción del árbol hay varios casos que cubrir. Comencemos por los
casos terminales. Hay dos casos terminales a considerar, el primero es cuando el
número de ejemplos disponibles es menor que el Umbral en ese caso se guarda
un nodo ho ja con la distribución de la clase para los ejemplos como contenido. El
otro caso terminal es cuando todos los ejemplos pertenecen a la misma clase. En
ese caso la distribución de la clase para los ejemplos tomara la forma [C] donde
C = Clase/NumE js. Estos dos casos corresponde a:
1
2
3
4
5
idt(E,Padre,_,Umbral) :length(E,Lon),
Lon=<Umbral,
distr(E, Distr),
assertz(nodo(hoja,Distr,Padre)), !.
6
7
8
9
idt(E,Padre,_,_) :distr(E, [C]),
assertz(nodo(hoja,[C],Padre)).
Si no estamos en el caso terminal, es necesario elegir el mejor atributo y particionar los ejemplos de acuerdo a los valores para el atributo seleccionado:
1
2
3
idt(Es,Padre,As,Umbral) :elige_atributo(Es,As,A,Valores,Resto), !,
particion(Valores,A,Es,Padre,Resto,Umbral).
Si esto no es posible, es que los datos son inconsistentes:
1
2
3
4
5
idt(E,Padre,_,_) :- !,
nodo(Padre,Test,_),
write(’Datos inconsistentes: no es posible construir
partición de ’),
write(E), write(’ en el nodo ’), writeln(Test).
10.7.1.
Atributos
Veamos ahora en detalle estos procedimientos. El siguiente procedimiento extrae
los atributos de un ejemplo:
1
2
3
atributos([],[]) :- !.
atributos([A=_|T],[A|W]) :atributos(T,W).
De forma que, para el caso de jugar tenis, los atributos se pueden obtener con la
siguiente llamada:
136
1
2
3
10 Arboles de Decisión
?- ejemplo(_,_,L), !, atributos(L,A).
L = [dia=soleado, temperatura=calor, humedad=alta, viento=debil],
A = [dia, temperatura, humedad, viento].
10.7.2.
Distribución de clases
¿Cual es la distribución inicial de la clase para los ejemplos de jugar tenis? Esto
lo podemos consultar con:
1
2
3
?- findall(E,ejemplo(E,_,_),Ejs), distr(Ejs,Dist).
Ejs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
Dist = [no/5, si/9].
lo que indica que tenemos 5 ejemplos de la clase no y 9 de la clase si. Los ejemplos
1,2,6 y 8 son todos miembros de la clase no, por lo que:
1
2
?- distr([1,2,6,8],Dist).
Dist = [no/4].
La implementación de distr/2 es la siguiente:
1
2
3
distr(S,Dist) :setof(C,XˆLˆ(member(X,S),ejemplo(X,C,L)),Cs),
cuentac(Cs,S,Dist).
4
5
cuentac([],_,[]) :- !.
6
7
8
9
10
cuentac([C|L],E,[C/N|T]) :findall(X,(member(X,E),ejemplo(X,C,_)),W),
length(W,N), !,
cuentac(L,E,T).
10.7.3.
El mejor atributo
El mejor atributo es el que maximiza la ganancia de información Gain con respecto a los ejemplos Es y atributos As disponibles. El predicado elige atributo/4
computa los valores posibles para el atributo seleccionado y lo elimina de la lista de
atributos disponibles para construir el árbol:
1
2
elige_atributo(Es,As,A,Valores,Resto) :length(Es,LonEs),
10.7 Implementación el Prolog
3
4
5
6
7
8
9
10
11
12
137
contenido_informacion(Es,LonEs,I), !,
findall((A-Valores)/Gain,
(member(A,As),
valores(Es,A,[],Valores),
separa_en_subconjs(Valores,Es,A,Ess),
informacion_residual(Ess,LonEs,R),
Gain is I - R),
All),
maximo(All,(A-Valores)/_),
eliminar(A,As,Resto), !.
13
14
15
16
17
separa_en_subconjs([],_,_,[]) :- !.
separa_en_subconjs([V|Vs],Es,A,[Ei|Resto]) :subconj(Es,A=V,Ei), !,
separa_en_subconjs(Vs,Es,A,Resto).
18
19
20
21
22
23
24
informacion_residual([],_,0) :- !.
informacion_residual([Ei|Es],Lon,Res) :length(Ei,LonEi),
contenido_informacion(Ei,LonEi,I), !,
informacion_residual(Es,Lon,R),
Res is R + I*LonEi/Lon.
25
26
27
28
contenido_informacion(Es,Lon,I) :setof(C,EˆLˆ(member(E,Es),ejemplo(E,C,L)),Classes), !,
suma_terms(Classes,Es,Lon,I).
29
30
31
32
33
34
35
suma_terms([],_,_,0) :- !.
suma_terms([C|Cs],Es,Lon,Info) :findall(E,(member(E,Es),ejemplo(E,C,_)),InC),
length(InC,N),
suma_terms(Cs,Es,Lon,I),
Info is I - (N/Lon)*(log(N/Lon)/log(2)).
36
37
38
39
40
41
42
43
valores([],_,Valores,Valores) :- !.
valores([E|Es],A,Vs,Valores) :ejemplo(E,_,L),
member(A=V,L), !,
(member(V,Vs), !, valores(Es,A,Vs,Valores);
valores(Es,A,[V|Vs],Valores)
).
44
45
46
47
48
49
50
subconj([],_,[]) :- !.
subconj([E|Es],A,[E|W]) :ejemplo(E,_,L),
member(A,L), !,
subconj(Es,A,W).
subconj([_|Es],A,W) :- subconj(Es,A,W).
Por ejemplo, la siguiente meta computa el mejor atributo (dia), dados los ejemplos E y atributos conocidos:
138
10 Arboles de Decisión
?- findall(N,ejemplo(N,_,_),E), elige_atributo(E,[dia,temperatura,humedad,
viento],A,V,R).
E = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
A = dia,
V = [lluvia, nublado, soleado],
R = [temperatura, humedad, viento].
Como vimos en la primera parte de este capı́tulo, para computar la ganancia de
información necesitamos computar el contenido informacional de todos los ejemplos:
?- findall(N,ejemplo(N,_,_),E), length(E,L), contenido_informacion(E,L,I).
E = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
L = 14,
I = 0.940286.
Y por cada atributo, computar la información residual para restarsela al contenido
informacional, y ası́ obtener las ganancias de información:
?- findall(N,ejemplo(N,_,_),E),
findall((A-Valores)/Gain,
(member(A,[dia,temperatura,humedad,viento]),
valores(E,A,[],Valores),
separa_en_subconjs(Valores,E,A,Ess),
informacion_residual(Ess,14,R),
Gain is 0.940286 - R),
All).
E = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
All = [ (dia-[lluvia, nublado, soleado])/0.24675,
(temperatura-[nublado, frio, templado, calor])/0.0760099,
(viento-[fuerte, debil])/0.151836 ].
Solo resta obtener el atributo de All con la máxima ganancia de información y
eliminarlo de la lista de atributos disponibles regresada en Resto. La inducción del
árbol de decisión es un proceso recursivo. Los valores del atributo elegido inducen
una partición sobre los ejemplos. Esto se logra con el predicado particion/6.
1
2
3
4
5
6
7
particion([],_,_,_,_,_) :- !.
particion([V|Vs],A,Es,Padre,Resto,Umbral) :subconj(Es,A=V,Ei), !,
genera_nodo_id(Nodo),
assertz(nodo(Nodo,A=V,Padre)),
idt(Ei,Nodo,Resto,Umbral), !,
particion(Vs,A,Es,Padre,Resto,Umbral).
10.7.4.
El árbol
El árbol se guarda en la memoria de trabajo como un conjunto de nodos con la
forma nodo(Identi f icador, Atributo −Valor, NodoPadre). Se pueden recuperar con
la siguiente meta:
10.7 Implementación el Prolog
?- nodo(I,AV,P).
I = 1,
AV = (dia=lluvia),
P = raiz ;
I = 2,
AV = (viento=fuerte),
P = 1 ;
I = hoja,
AV = [no/2],
P = 2 ;
I = 3,
AV = (viento=debil),
P = 1 ;
I = hoja,
AV = [si/3],
P = 3 ;
I = 4,
AV = (dia=nublado),
P = raiz ;
I = hoja,
AV = [si/4],
P = 4 ;
I = 5,
AV = (dia=soleado),
P = raiz ;
I = 6,
AV = (humedad=normal),
P = 5 ;
I = hoja,
AV = [si/2],
P = 6 ;
I = 7,
AV = (humedad=alta),
P = 5 ;
I = hoja,
AV = [no/3],
P = 7.
10.7.5.
Imprimiendo el árbol construido.
Para imprimir el árbol construido de una manera legible definimos:
1
2
imprime_arbol :imprime_arbol(raiz,0).
3
4
5
6
7
8
9
10
imprime_arbol(Padre,_) :nodo(hoja,Class,Padre), !,
write(’ => ’),write(Class).
imprime_arbol(Padre,Pos) :findall(Son,nodo(Son,_,Padre),L),
Pos1 is Pos+2,
imprime_lista(L,Pos1).
11
12
imprime_lista([],_) :- !.
13
14
imprime_lista([N|T],Pos) :-
139
140
15
16
17
18
10 Arboles de Decisión
node(N,Test,_),
nl, tab(Pos), write(Test),
imprime_arbol(N,Pos),
imprime_lista(T,Pos).
10.7.6.
Ejecutando todo el experimento
Ası́ la sesión para construir el árbol de decisión para jugar tenis es (con id3.pl
ya cargado en Prolog):
?- [test3].
% test3 compiled 0.00 sec, 3,896 bytes
true.
?- id3.
true.
?- imprime_arbol.
dia=lluvia
viento=fuerte => [no/2]
viento=debil => [si/3]
dia=nublado => [si/4]
dia=soleado
humedad=normal => [si/2]
humedad=alta => [no/3]
true.
10.7.7.
Predicados auxiliares
Algunos predicados auxiliares incluyen:
1
2
3
4
genera_nodo_id(M) :retract(id(N)),
M is N+1,
assert(id(M)), !.
5
6
7
genera_nodo_id(1) :assert(id(1)).
8
9
eliminar(X,[X|T],T) :- !.
10
11
12
eliminar(X,[Y|T],[Y|Z]) :eliminar(X,T,Z).
13
14
subconjunto([],_) :- !.
15
16
subconjunto([X|T],L) :-
10.7 Implementación el Prolog
17
18
member(X,L), !,
subconjunto(T,L).
19
20
21
22
23
maximo([X],X) :- !.
maximo([X/M|T],Y/N) :maximo(T,Z/K),
(M>K,Y/N=X/M ; Y/N=Z/K), !.
141
Capı́tulo 11
Planeación
Resumen La planeación es un tema de interés tradicional en Inteligencia Artificial,
que involucra razonar acerca de los efectos de las acciones y la secuencia en que
estas se aplican para lograr un efecto acumulativo dado. En esta sesión desarrollaremos planificadores simples para ilustrar los principios de la planeación.
11.1.
Acciones
Cada acción posible es definida en términos de su condición y sus efectos, especı́ficamente:
Condición. La condición que debe satisfacerse para la acción pueda ejecutarse.
Agregar. Es una lista de cambios que se espera ocurran después de ejecutarse la
acción.
Quitar. Es una lista de observaciones que se espera dejen de ser verdaderas después de ejecutarse la acción.
Las condiciones, pueden definirse por un procedimiento
1
cond(Acc,Cond).
Los efectos de una acción pueden definirse de manera similar, por dos procedimientos:
1
2
add(Acc,ListaAdd).
del(Acc,ListaDel).
donde ListaAdd y ListaDel corresponden a las listas definidas para agregar y borrar.
Asumamos que vamos a realizar la planeación en el dominio del mundo de los
bloques. Ası́ que la única acción posible será:
143
144
1
11 Planeación
mover(Bloque,De,A).
La definición completa de esta acción es como sigue:
1
2
3
4
5
6
7
8
9
precond( mover( Bloque, De, A),
[ despejado( Bloque), despejado( A),
en( Bloque, De)] ) :bloque( Bloque),
objeto( A),
A \== Bloque,
objeto( De),
De \== A,
Bloque \== De.
10
11
agregar( mover(X,De,A), [ en(X,A), despejado(De)]).
12
13
borrar( mover(X,De,A), [ en(X,De), despejado(A)]).
De manera que para poder mover un bloque Bloque de la posición De a la posición A, es necesario que el bloque Bloque y la posición A estén despejados, lo mismo
que el bloque Bloque esté en la posición De. El resto del procedimiento cond/2 establece restricciones extras: que Bloque sea un bloque, y A y De sean objetos en el
universo de discurso; que A sea diferente de Bloque (no mover el bloque sobre si
mismo); que se debe mover el bloque a una nueva posición (A es diferente de De);
y no mover el bloque de sı́ mismo (Bloque es diferente de De). Las definiciones de
add/2 y del/2 completan la especificación de mover/3.
Las siguientes definiciones especifican un escenario en el mundo de los bloques:
1
2
3
4
objeto( X) :lugar( X)
;
bloque( X).
5
6
7
8
bloque( a).
bloque( b).
bloque( c).
9
10
11
12
13
lugar(
lugar(
lugar(
lugar(
1).
2).
3).
4).
14
15
16
estado1( [ despejado(2), despejado(4), despejado(b),
despejado(c), en(a,1), en(b,3), en(c,a) ] ).
17
18
metas1([en(a, b)]).
11.2 Análisis medios-fines
145
Tal definición de las acciones, establece también el espacio de planes posibles,
por lo que se le conoce como espacio de planeación. Las metas del planeador se
definen en términos de una lista de observaciones que se deben cumplir.
Ahora veremos como a partir de esta representación, es posible derivar los planes
mediante un procedimiento conocido como análisis medios-fines.
11.2.
Análisis medios-fines
Consideremos que el mundo de los bloques se encuentra en el estado inicial
especificado anteriormente (estado1). Sea la meta del planeador en(a, b). El trabajo
del planeador consiste en encontrar una secuencia de acciones que satisfagan esta
meta. Un planeador tı́pico razonarı́a de la siguiente forma:
1. Encontrar una acción que satisfaga en(a, b). Al buscar en la relación add, encontramos que tal acción es de la forma mover(a, De, b) a partir de cualquier
De. Tal acción deberá formar parte de nuestro plan, pero no podemos ejecutarla
inmediatamente dado nuestro estado inicial.
2. Hacer posible la acción mover(a, De, b). Al buscar en la relación cond encontramos que la condición para ejecutar esta acción es:
1
[ despejado(a), despejado(b), en(a,De) ]
en el estado inicial tenemos que despe jado(b) y que en(a, De) para De/1; pero
no que despe jado(a), ası́ que el planeador se concentra en esta fórmula como su
nueva meta.
3. Volvemos a buscar en la relación add para encontrar una acción que satisfaga
despe jado(a). Tal acción tiene la forma mover(Bloque, a, A). La condición para
ejecutar esta acción es:
1
[despejado(Bloque), despeado(A), en(Bloque,a) ]
la cual se satisface en nuestro estado inicial para Boque/c y A/2. De forma que
mover(c, a, 2) puede ejecutarse en el estado inicial, modificando el estado del
problema de la siguiente manera:
Eliminar del estado inicial las relaciones que la acción borra.
Incluir las relaciones que la acción agrega al estado inicial del problema.
esto produce la lista:
1
2
[ despejado(a), despejado(b), despejado(c), despejado(4),
en(a,1), en(b,3), en(c,2) ]
4. Ahora podemos ejecutar la acción mover(a, 1, b), con lo que la meta plantada se
satisface. El plan encontrado es:
146
11 Planeación
[ mover(c,a,2), mover(a,1,b) ]
1
Este estilo de razonamiento se conoce como análisis medios-fines. Observen que
el ejemplo planteado el plan se encontró directamente, sin necesidad de reconsiderar. Esto ilustra como el proceso de razonar sobre el efecto de las acciones y las metas guı́an la planeación en una dirección adecuada. Desafortunadamente, no siempre
se puede evitar reconsiderar. De hecho, la explosión combinatoria y la búsqueda son
tı́picas en la planeación.
El principio de planeación por análisis medios-fines se ilustra en la figura 11.1.
Puede plantearse como sigue: Para resolver una lista de metas Metas en un estado
Estado, que lleven a un estado Estado final, hacer:
Si todas las Metas son verdaderas en Estado, entonces Estado final = Estado. En
cualquier otro caso:
1.
2.
3.
4.
Seleccionar una Meta no solucionada en Metas.
Encontrar una Acción que agregue Meta al estado actual.
Hacer posible Acción resolviendo Condición para obtener el estado inter 1.
Aplicar la Acción en el estado inter 1 para obtener el estado inter 2 donde Meta
se cumple.
5. Resolver Metas en el estado inter 2 para llegar a Estado final.
Condición
prePlan
Estado
Meta
Acción
Estado
inter 1
Metas
postPlan
Estado
inter 2
Estado
final
Figura 11.1 Análisis medios-fines
El código del planeador medios fines es como sigue:
1
2
plan( Estado, Metas, [], Estado) :satisfecho( Estado, Metas).
3
4
5
6
7
8
9
10
11
12
plan( Estado, Metas, Plan, EstadoFinal) :append( PrePlan, [Accion | PostPlan], Plan),
seleccionar( Estado, Metas, Meta),
lograr( Accion, Meta),
precond( Accion, Condicion),
plan( Estado, Condicion, PrePlan, EstadoInter1),
aplicar( EstadoInter1, Accion, EstadoInter2),
plan( EstadoInter2, Metas, PostPlan, EstadoFinal).
11.3 Metas protegidas
13
147
satisfecho( _, []).
14
15
16
17
satisfecho( Estado, [Meta | Metas])
member( Meta, Estado),
satisfecho( Estado, Metas).
:-
18
19
20
21
seleccionar( Estado, Metas, Meta) :member( Meta, Metas),
not(member( Meta, Estado)).
22
23
24
25
lograr( Accion, Meta) :agregar( Accion, Metas),
member( Meta, Metas).
26
27
28
29
30
31
aplicar( Estado, Accion, NewEstado) :borrar( Accion, ListaBorrar),
borrar_todos( Estado, ListaBorrar, Estado1), !,
agregar( Accion, ListaAgregar),
append( ListaAgregar, Estado1, NewEstado).
32
33
borrar_todos( [], _, []).
34
35
36
37
borrar_todos( [X | L1], L2, Diff) :member( X, L2), !,
borrar_todos( L1, L2, Diff).
38
39
40
borrar_todos( [X | L1], L2, [X | Diff])
borrar_todos( L1, L2, Diff).
:-
Para invocar al planeador, ejecutamos en Prolog la siguiente meta:
1
2
3
4
5
6
7
?- estado1(E), metas1(M), plan(E,M,P,Efinal).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
M = [en(a, b)],
P = [mover(c, a, 2), mover(a, 1, b)],
Efinal = [en(a, b), despejado(1), en(c, 2), despejado(a),
despejado(4), despejado(c), en(b, 3)]
11.3.
Metas protegidas
Consideren ahora la siguiente llamada a plan/4:
1
2
3
4
5
?- estado1(E), plan(E,[en(a,b),en(b,c)],Plan,_).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
Plan = [mover(b, 3, c),
mover(b, c, 3),
148
6
7
8
9
10
11 Planeación
mover(c,
mover(a,
mover(a,
mover(b,
mover(a,
a,
1,
b,
3,
1,
2),
b),
1),
c),
b)]
Aunque el plan resultante cumple con su cometido, no es precisamente elegante.
De hecho, existe un plan de tres movimientos para lograr las metas de este caso! Esto
se debe a que el mundo de los bloques es más complejo de lo que parece, debido a
la combinatoria. En este problema, el planeador tiene acceso a más opciones entre
diferentes acciones que tienen sentido bajo el análisis medios-fines. Más opciones,
significa mayor complejidad combinatoria.
Regresemos al ejemplo, lo que sucede es que el planeador persigue diferentes
metas en diferentes etapas de la construcción del plan. Por ejemplo:
mover(b, 3, c) satisfacer en(b, c)
mover(b, c, 3) satisfacer clear(c) y ejecutar siguiente acción
mover(c, a, 2) satisfacer clear(a) y mover(a, 1, b)
mover(a, 1, b) satisfacer on(a, b)
mover(a, b, 1) satisfacer clear(b) y mover(b, 3, c)
mover(b, 3, c) satisfacer en(b, c) otra vez
mover(a, 1, b) satisfacer en(a, b) otra vez
Lo que esta tabla muestra es que a veces el planeador destruye metas que ya habı́a
satisfecho. El planeador logra fácilmente satisfacer una de las dos metas planteadas,
en(b, c) pero la destruye al buscar como satisfacer la otra meta en(a, b). Lo peor
es que está forma desorganizada de seleccionar las metas, puede incluso llevar al
fracaso en la búsqueda del plan, como en el siguiente ejemplo:
1
2
?- estado1(E), plan(E,[despejado(2), despejado(3)], Plan, _).
ERROR: Out of local stack
Hagan un trace de esta corrida, para saber porque la meta falla.
Una idea evidente para evitar este comportamiento en nuestro planeador, es mantener una lista de metas protegidas, de forma que las acciones que destruyen estas
metas no puedan ser seleccionadas. De forma que el planeador medios-fines con
metas protegidas se define como:
1
2
plan_metas_protegidas(EstadoInicial,Metas,Plan,EstadoFinal):plan_mp(EstadoInicial,Metas,[],Plan,EstadoFinal).
3
4
5
plan_mp(Estado,Metas,_,[],Estado) :satisfecho(Estado,Metas).
6
7
8
9
10
plan_mp(Estado,Metas,Protegido,Plan,EstadoFinal) :append( PrePlan, [Accion | PostPlan], Plan),
seleccionar( Estado, Metas, Meta),
lograr( Accion, Meta),
11.4 Aspectos procedimentales de la búsqueda en amplitud
149
precond( Accion, Condicion),
preservar(Accion,Protegido),
plan_mp( Estado, Condicion, Protegido, PrePlan,
EstadoInter1),
aplicar( EstadoInter1, Accion, EstadoInter2),
plan_mp( EstadoInter2, Metas, [Meta|Protegido],
PostPlan, EstadoFinal).
11
12
13
14
15
16
17
18
19
20
21
22
preservar(Accion,Metas) :borrar(Accion,ListaBorrar),
not( (member(Meta,ListaBorrar),
member(Meta,Metas))).
De forma que si ejecutamos la consulta:
1
2
3
4
5
?- estado1(E), plan_metas_protegidas(E,[despejado(2),
despejado(3)], P, _).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
P = [mover(b, 3, 2), mover(b, 2, 4)]
obtenemos una solución, aunque sigue sin ser la mejor. Un sólo movimiento mover(b, 2, 4)
era necesario para cumplir con las metas planeadas.
Los planes innecesariamente largos son resultado de la estrategia de búsqueda
usada por nuestro planeador.
11.4.
Aspectos procedimentales de la búsqueda en amplitud
Los planeadores implementados usan esencialmente una estrategia de búsqueda
primero en profundidad, pero no por completo. Para poder estudiar lo que está pasando, debemos poner atención al orden en que se generan los planes candidatos.
La meta
1
append(PrePlan, [Accion|PostPlan], Plan)
es central en este aspecto. La variable Plan no está instanciada cuando esta meta es
alcanzada. El predicado append/3 genera al reconsiderar, candidatos alternativos
para PrePlan en el siguiente orden:
1
2
3
4
5
PrePlan = [];
PrePlan = [_];
PrePlan = [_,_];
PrePlan = [_,_,_];
...
150
11 Planeación
Candidatos cortos para PrePlan son los primeros. PrePlan establece una condición para Accion. Esto permite encontrar una acción cuya condición puede satisfacerse por un plan tan corto como sea posible (simulando búsqueda primero en
amplitud). Por otra parte, la lista candidato para PostPlan está totalmente no instanciada, y por tanto su longitud es ilimitada. Por tanto, la estrategia de búsqueda
resultante es globalmente primero en profundidad, y localmente primero en amplitud. Con respecto al encadenamiento hacı́a adelante de las acciones que se agregan
al plan emergente, se trata de una búsqueda primero en profundidad. Cada acción es
validada por un PrePlan, este plan es por otra parte, buscado primero en amplitud.
Una forma de minimizar la longitud de los planes es forzar al planeador, en su
parte de búsqueda en amplitud, de forma que los planes cortos sean considerados
antes que los largos. Podemos imponer esta estrategia embebiendo nuestro planificador en un procedimiento que genere planes candidatos ordenados por tamaño
creciente. Por ejemplo:
1
2
3
plan_primero_amplitud(Estado, Metas, Plan, EstadoFinal) :candidato(Plan),
plan(Estado,Metas,Plan,EstadoFinal).
4
5
6
candidato([]).
7
8
9
candidato([Primero|Resto]) :candidato(Resto).
El mismo efecto puede lograrse de manera más elegante, insertando el generador
de planes directamente en el procedimiento plan/4 de forma que:
1
2
3
4
plan_metas_protegidas_amplitud(EstadoInicial,Metas,Plan,
EstadoFinal):plan_mp_amplitud(EstadoInicial,Metas,[],Plan,
EstadoFinal).
5
6
7
plan_mp_amplitud(Estado,Metas,_,[],Estado) :satisfecho(Estado,Metas).
8
9
10
11
12
13
14
15
16
17
18
19
20
plan_mp_amplitud(Estado,Metas,Protegido,Plan,EstadoFinal) :append(Plan,_,_),
append( PrePlan, [Accion | PostPlan], Plan),
seleccionar( Estado, Metas, Meta),
lograr( Accion, Meta),
precond( Accion, Condicion),
preservar(Accion,Protegido),
plan_mp_amplitud( Estado, Condicion, Protegido, PrePlan,
EstadoInter1),
aplicar( EstadoInter1, Accion, EstadoInter2),
plan_mp_amplitud( EstadoInter2, Metas, [Meta|Protegido],
PostPlan, EstadoFinal).
11.4 Aspectos procedimentales de la búsqueda en amplitud
151
Y por tanto podemos volver a computar la meta original, encontrando esta vez el
plan más corto:
1
2
3
4
5
6
?-
estado1(E),
plan_metas_protegidas_amplitud(E,[despejado(2),
despejado(3)], Plan,_).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
Plan = [mover(b, 3, 4)]
Este resultado es óptimo, sin embargo la meta:
1
2
3
4
5
6
7
8
?- estado1(E),
plan_metas_protegidas_amplitud(E,[en(a,b), en(b,c)], Plan, _).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
Plan = [mover(c, a, 2),
mover(b, 3, a),
mover(b, a, c),
mover(a, 1, b)]
sigue siendo problemática. Este resultado se obtiene con y sin protección de metas siguiendo la estrategia primero en amplitud. El segundo movimiento del plan
parece superfluo y aparentemente no tiene sentido. Investiguemos porque se le incluye en el plan y porque aún en el caso de la búsqueda primero en amplitud, el plan
resultante está lejos del óptimo.
Dos preguntas son interesantes en este problema: ¿Qué razones encuentra el planeador para construir este curioso plan? y ¿Por qué el planeador no encuentra el plan
óptimo e incluye la acción mover(b, 3, a)? Atendamos la primer pregunta. La última
acción mover(a, 1, b) atiende la meta en(a, b). Los tres primeros movimientos están
al servicio de cumplir las condiciones de esta acción, en particular la condición
despe jado(a). El tercer movimiento despeja a y una condición de este movimiento
es en(b, a). Esto se cumple gracias al curioso segundo movimiento mover(b, 3, a).
Esto ilustra la clase de exóticos planes que pueden emerger durante un razonamiento
medios-fines.
Con respecto a la segunda pregunta, ¿Por qué después de mover(c, a, 2), el planeador no considera inmediatamente mover(b, 3, c), lo que conduce a un plan óptimo? La razón es que el planeador estaba trabajando en la meta en(a, b) todo el
tiempo. La acción que nos interesa es totalmente superflua para esta meta, y por lo
tanto no es considerada. La cuarta acción logra en(a, b) y por pura suerte en(b, c)!
Este último resultado no es una decisión planeada de nuestro sistema.
De lo anterior se sigue, que el procedimiento medios-fines, tal y como lo hemos implementado es incompleto, no sugiere todas las acciones relevantes para el
proceso de planificación. Esto se debe a la localidad con que se computan las soluciones. Solo se sugerirán acciones relevantes para la meta actual del sistema. La
solución al problema está en este enunciado: se debe permitir la interacción entre
metas en el proceso de planificación. Antes de pasar al siguiente tema, consideren
152
11 Planeación
que al introducir la estrategia primero en amplitud para buscar planes más cortos,
hemos elevando considerablemente el tiempo de computación necesario para hallar
una solución.
11.5.
Regresión de metas
Supongan que estamos interesados en una lista de metas Metas que se cumplen
en cierto estado E. Sea el estado anterior a E, E0 y la acción ejecutada en E0 , A.
¿Qué metas Metas0 tienen que cumplirse en E0 para que Metas se cumpla en E?
Metas0 debe tener las siguientes propiedades:
1. La acción A debe ser posible en E0 , por lo que Metas0 debe implicar la condición
para A.
2. Para cada meta M en Metas, se cumple que:
la acción A agrega M; ó
M ∈ Metas0 y A no borra M.
El cómputo para determinar Metas0 a partir de Metas y la acción A se conoce
como regresión de metas. Por supuesto, sólo estamos interesados en aquellas acciones que agregan alguna meta M a Metas. Las relaciones entre varios conjuntos de
metas y condiciones se ilustra en la figura 11.2
Figura 11.2 Relaciones entre conjuntos de condiciones en la regresión de metas vı́a la acción A. El
área sombreada representa las metas Metas0 resultado de la regresión. Observen que la intersección
entre Metas y la lista borrar de A debe ser vacı́a.
El mecanismo de regresión de metas puede usarse como planeador de la siguiente
manera. Para satisfacer una lista de metas Metas a partir de un estado EstadoInicial,
hacer: Si Metas se cumple en EstadoInicial, entonces el plan vacı́o es suficiente; en
cualquier otro caso, seleccionar una meta M ∈ Metas y una acción A que agregue
11.5 Regresión de metas
153
M; entonces computar la regresión de Metas vı́a A obteniendo ası́ NuevasMetas y
buscar un plan para satisfacer NuevasMetas desde EstadoInicial.
El procedimiento puede mejorarse si observamos que algunas combinaciones de
metas son imposibles. Por ejemplo en(a, b) y despe jado(b) no pueden satisfacerse
al mismo tiempo. Esto se puede formular vı́a la relación:
1
imposible(Meta,Metas).
que indica que la Meta es imposible en combinación con las Metas. Para el caso del
mundo de los bloques la incompatibilidad entre las metas se define como:
1
imposible(en(X,X),_).
2
3
4
5
6
7
8
imposible(en(X,Y), Metas) :member(despejado(Y),Metas)
;
member(en(X,Y1),Metas), Y1 \== Y
;
member(en(X1,Y),Metas) X1 \== X.
9
10
11
imposible(despejado(X),Metas) :member(en(_,X),Metas).
El resto del planeador es como sigue:
1
2
plan(Estado, Metas, []) :satisfecho(Estado, Metas).
3
4
5
6
7
8
9
10
11
plan(Estado, Metas, Plan) :append( PrePlan, [Accion], Plan),
seleccionar( Estado, Metas, Meta),
lograr(Accion, Meta),
precond(Accion, Condicion),
preservar(Accion, Metas),
regresion(Metas, Accion, MetasReg),
plan(Estado, MetasReg, PrePlan).
12
13
14
satisfecho(Estado, Metas) :borrar_todos(Metas,Estado,[]).
15
16
17
seleccionar(_, Metas, Meta) :member( Meta, Metas).
18
19
20
21
lograr( Accion, Meta) :agregar( Accion, Metas),
member( Meta, Metas).
22
23
borrar_todos( [], _, []).
24
25
26
borrar_todos( [X | L1], L2, Diff)
member( X, L2), !,
:-
154
11 Planeación
borrar_todos( L1, L2, Diff).
27
28
29
30
borrar_todos( [X | L1], L2, [X | Diff])
borrar_todos( L1, L2, Diff).
:-
31
32
33
34
35
preservar(Accion,Metas) :borrar(Accion,ListaBorrar),
not( (member(Meta,ListaBorrar),
member(Meta,Metas))).
36
37
38
39
40
41
regresion(Metas, Accion, MetasReg) :agregar(Accion, NuevasRels),
borrar_todos(Metas, NuevasRels, RestoMetas),
precond(Accion, Condicion),
agregarNuevo(Condicion,RestoMetas,MetasReg).
42
43
agregarNuevo([],L,L).
44
45
46
47
48
agregarNuevo([Meta|_],Metas,_) :imposible(Meta,Metas),
!,
fail.
49
50
51
52
agregarNuevo([X|L1],L2,L3) :member(X,L2), !,
agregarNuevo(L1,L2,L3).
53
54
55
agregarNuevo([X|L1],L2,[X|L3]) :agregarNuevo(L1,L2,L3).
Ahora es posible encontrar el plan óptimo de tres movimientos para el problema
del mundo de los bloques:
1
2
3
4
?- estado1(E), plan(E,[en(a,b),en(b,c)],P).
E = [despejado(2), despejado(4), despejado(b),
despejado(c), en(a, 1), en(b, 3), en(c, a)],
P = [mover(c, a, 2), mover(b, 3, c), mover(a, 1, b)]
11.6.
Combinando planeación medios fines con primero el
mejor
Los planeadores construidos hasta ahora hacen uso de estrategias de búsqueda
básicas: primero en profundidad, o primero en amplitud, o una combinación de ambas. Estas estrategias son totalmente desinformadas, en el sentido que no pueden
usar información del dominio del problema para guiar su selección entre alternativas posibles. En consecuencia, estos planeadores son sumamente ineficientes, salvo
en casos muy especiales. Existen diversas maneras de introducir una guı́a heurı́stica,
11.6 Combinando planeación medios fines con primero el mejor
155
basada en el dominio del problema, en nuestros planeadores. Algunos lugares donde
esto puede hacerse son:
En la relación seleccionar(Estado, Metas, Meta) que decide el orden en que las
metas serán procesadas. Por ejemplo, una guı́a en el mundo de los bloques es que
las torres deben estar bien cimentadas, de forma que la relación en/2 más arriba
de la torre, deberı́a resolverse al último (o primero en el planeador por regresión,
que soluciona el plan en orden inverso). Otra guı́a es que las metas que ya se
cumplen en el medio ambiente, deberı́an postergarse.
En la relación lograr(Accion, Meta) que decide que acción alternativa será intentada para lograr una meta dada. Observen que nuestro planeador también genera
alternativas al procesar precond/2. Por ejemplo, algunas acciones son “mejores” porque satisfacen más de una meta simultáneamente. También, con base en
la experiencia, podemos saber que cierta condición es más fácil de satisfacer que
otras.
Decisiones acerca de qué conjunto de regresión de metas debe considerarse a
continuación. Por ejemplo, seguir trabajando en el que parezca más fácil de resolver, buscando ası́ el plan más corto.
Esta última idea muestra como podemos imponer una estrategia primero el mejor
en nuestro planeador. Esto implica computar un estimado heurı́stico de la dificultad
de conjuntos de regresión de metas alternativos, para expandir el más promisorio.
Recuerden que para usar este tipo de estrategia es necesario especificar:
1.
2.
3.
4.
Una relación s/3 entre nodos del espacio de búsqueda: s(Nodo1 , Nodo2 ,Costo).
Los nodos meta en el espacio: meta(Nodo).
Una función heurı́stica de la forma h(Nodo, Hestimado).
El nodo inicial de la búsqueda.
Una forma de definir estos requisitos es asumir que los conjuntos de regresión
de metas son nodos en el espacio de búsqueda. Esto es, en el espacio de búsqueda
hará una liga entre Metas1 y Metas2 si existe una acción A tal que:
1. A agrega alguna meta ∈ Metas1 .
2. A no destruye ninguna meta ∈ Metas1
3. Metas2 es el resultado de la regresión de Metas1 a través de A, tal y como definimos en nuestro planeador anterior: regresion(Metas1 , A, Metas2 ).
Por simplicidad, asumiremos que todas las acciones tienen el mismo costo, y en
consecuencia asignaremos Costo = 1 en todas las ligas del espacio de búsqueda. Por
lo que la relación s/3 se define como:
1
2
3
4
5
6
s(Metas1,Metas2) :member(Meta,Metas1),
lograr(Accion,Meta),
precond(Accion,Cond),
preservar(Accion,Metas1),
regresion(Metas1,Accion,Metas2).
156
11 Planeación
Cualquier conjunto de metas que sea verdadero en la situación inicial de un plan,
es un nodo meta en el espacio de búsqueda. El nodo inicial de la búsqueda es la lista
de metas que el plan debe lograr.
Aunque la representación anterior tiene todos los elementos requeridos, tiene un
pequeño defecto. Esto se debe a que nuestra búsqueda primero el mejor encuentra
un camino solución como una secuencia de estados y no incluye acciones entre los
estados. Por ejemplo, la secuencia de estados (listas de metas) para logra en(a, b) en
el estado inicial que hemos estado usando es:
1
2
3
[ [despejado(c), despejado(2), en(c,a), despejado(b), en(a,1)]
[despejado(a), despejado(b), en(a,1)]
[en(a,b)] ]
El primer estado es verdadero por la situación inicial, el segundo es resultado de
la acción mover(c, a, 2) y el tercero es resultado de la acción mover(a, 1, b).
Observen que la búsqueda primero el mejor regresa el camino solución en el
orden inverso. En nuestro caso es una ventaja, porque los planes son construidos en
la regresión hacı́a atrás, ası́ que al final obtendremos la secuencia de acciones en
el orden correcto. Sin embargo, es raro no tener mención explı́cita a las acciones
en el plan, aunque puedan reconstruirse de las diferencias entre listas de metas.
Podemos incluir las acciones en el camino solución fácilmente, basta con agregar
a cada estado la acción que se sigue de él. De forma que los nodos del espacio de
búsqueda tendrán la forma:
1
Metas -> Acción
Su implementación detallada es la siguiente:
1
:- op(300,xfy, ->).
2
3
4
5
6
7
8
s(Metas -> AccSiguiente,MetasNuevas -> Accion, 1) :member(Meta,Metas),
lograr(Accion,Meta),
precond(Accion,Cond),
preservar(Accion,Metas),
regresion(Metas,Accion,MetasNuevas).
9
10
11
12
meta(Metas -> Accion) :inicio(Estado),
satisfecho(Estado,Metas).
13
14
15
16
17
h(Metas -> Accion,H) :inicio(Estado),
borrar_todos(Metas,Estado,Insatisfecho),
length(Instatisfecho,H).
18
19
20
inicio([en(a,1),en(b,3),en(c,a),despejado(b),despejado(c),
despejado(2),despejado(4)]).
11.6 Combinando planeación medios fines con primero el mejor
157
Ahora podemos usar nuestro viejo buscador primero el mejor:
1
2
primeroMejor(Inicio,Solucion) :expandir([],hoja(Inicio,0/0),9999,_,si,Solucion).
3
4
5
6
7
8
9
%% % expandir(Camino,Arbol,Umbral,Arbol1,Solucionado,Solucion)
%% %
Camino es el recorrido entre Inicio y el nodo en Arbol
%% %
Arbol1 es Arbol expandido bajo el Umbral
%% %
Si la meta se encuentra, Solucion guarda el camino
solución
%% %
y Solucionado = si
10
11
%
Caso 1: la hoja con Nodo es una meta, construye una solución
12
13
14
expandir(Camino,hoja(Nodo,_),_,_,si,[Nodo|Camino]) :meta(Nodo).
15
16
17
%
%
Caso 2: una hoja con f-valor menor o igual al Umbral
Generar succesores de Nodo y expandirlos bajo el Umbral
18
19
20
21
22
23
24
25
26
27
28
29
expandir(Camino,hoja(Nodo,F/G),Umbral,Arbol1,Solucionado,Sol) :F =< Umbral,
(bagof( M/C,(s(Nodo,M,C),not(member(M,Camino))),Succ),
!,
% Nodo tiene sucesores
listaSuccs(G,Succ,As), % Encontras subárboles As
mejorF(As,F1),
% f-value of best successor
expandir(Camino,arbol(Nodo,F1/G,As),Umbral,Arbol1,
Solucionado,Sol)
;
Solucionado = nunca
% Nodo no tiene sucesores
).
30
31
32
33
%
%
%
Caso 3: Nodo interno con f-valor menor al Umbral
Expandir el subárbol más promisorio con cuyo
resultado, continuar/7 decidirá como proceder
34
35
36
37
38
39
40
41
expandir(Camino,arbol(Nodo,F/G,[A|As]),Umbral,Arbol1,
Solucionado,Sol) :F =< Umbral,
mejorF(As,MejorF), min(Umbral,MejorF,Umbral1),
expandir([Nodo|Camino],A,Umbral1,A1,Solucionado1,Sol),
continuar(Camino,arbol(Nodo,F/G,[A1|As]),Umbral,Arbol1,
Solucionado1,Solucionado,Sol).
42
43
44
%
%
Caso 4: Nodo interno con subárboles vacı́o
Punto muerto, el problema nunca será resuelto
45
46
expandir(_,arbol(_,_,[]),_,_,nunca,_) :- !.
47
48
49
50
%
%
Caso 5: f-valor mayor que el Umbral
Arbol no debe crecer
158
51
52
11 Planeación
expandir(_,Arbol,Umbral,Arbol,no,_)
f(Arbol,F), F > Umbral.
:-
53
54
55
%% % continuar(Camino,Arbol,Umbral,NuevoArbol,SubarbolSolucionado,
%% %
ArbolSolucionado,Solucion)
56
57
58
% Caso 1: el subartol y el arbol están solucionados
% la solución está en Sol
59
60
continuar(_,_,_,_,si,si,Sol).
61
62
63
64
65
66
67
continuar(Camino,arbol(Nodo,F/G,[A1|As]),Umbral,Arbol1,no,
Solucionado,Sol) :insertarArbol(A1,As,NAs),
mejorF(NAs,F1),
expandir(Camino,arbol(Nodo,F1/G,NAs),Umbral,Arbol1,
Solucionado,Sol).
68
69
70
71
72
73
continuar(Camino,arbol(Nodo,F/G,[_|As]),Umbral,Arbol1,nunca,
Solucionado,Sol) :mejorF(As,F1),
expandir(Camino,arbol(Nodo,F1/G,As),Umbral,Arbol1,
Solucionado,Sol).
74
75
76
%% % listaSuccs(G0,[Nodo1/Costo1, ...], [hoja(MejorNodo,MejorF/G),
...])
%% %
hace una lista de árboles sucesores ordendados por F-valor
77
78
listaSuccs(_,[],[]).
79
80
81
82
83
84
85
listaSuccs(G0,[Nodo/C|NCs],As) :G is G0 + C,
h(Nodo,H),
% Heuristic term h(N)
F is G + H,
listaSuccs(G0,NCs,As1),
insertarArbol(hoja(Nodo,F/G),As1,As).
86
87
%% % Inserta A en una lista de arboles As preservando el orden por
f-valor
88
89
90
91
insertarArbol(A,As,[A|As]) :f(A,F), mejorF(As,F1),
F =< F1, !.
92
93
94
insertarArbol(A,[A1|As], [A1|As1])
insertarArbol(A,As,As1).
:-
95
96
97
%% % Extraer f-valores
98
99
100
f(hoja(_,F/_),F).
f(arbol(_,F/_,_),F).
101
102
mejorF([A|_],F) :- f( A, F).
% f-valor de una hoja
% f-valor de un árbol
11.7 Variables y planes no lineales
103
159
mejorF([], 9999).
104
105
106
min(X,Y,X) :- X
min(_,Y,Y).
=<
Y, !.
De forma que podemos procesar el plan con la siguiente llamada:
1
2
3
4
5
6
7
8
?- primeroMejor([en(a,b), en(b,c)] -> stop, Plan).
Plan = [[despejado(2), en(c, a), despejado(c), en(b, 3),
despejado(b), en(a, 1)]->mover(c, a, 2),
[despejado(c), en(b, 3), despejado(a), despejado(b),
en(a, 1)]->mover(b, 3, c),
[despejado(a), despejado(b), en(a, 1), en(b, c)]
->mover(a, 1, b),
[en(a, b), en(b, c)]->stop]
La acción nula stop es necesaria pues todos los nodos deben incluir una acción.
Aunque la heurı́stica usada es simple, el programa debe ser más rápido que las versiones anteriores. Eso si, el precio a pagar es una mayor utilización de la memoria,
debido a que debemos mantener el conjunto de alternativas competitivas.
11.7.
Variables y planes no lineales
A manera de comentario final, consideraremos dos casos que pueden mejorar la
eficiencia de los planificadores construidos en esta sesión. El primer caso consiste
en permitir que las acciones y las metas contengan variables no instanciadas; el
segundo caso es considerar que los planes no son lineales.
11.7.1.
Acciones y metas no instanciadas
Las variables que ocurren en nuestros planeadores están siempre instanciadas.
Esto se logra, por ejemplo en la relación precond/2 cuyo cuerpo incluye la meta
block(Block) entre otras. Este tipo de meta hace que Block siempre esté instanciada.
Esto puede llevar a la generación de numerosos movimientos alternativos irrelevantes. Por ejemplo, cuando al planeador se le plantea como meta despe jar(a), éste
utiliza lograr/2 para generar movimientos que satisfagan despe jado(a):
1
mover(De,a,A)
Entonces se computan las condiciones necesarias para ejecutar esta acción:
1
precond(mover(De,a,A)),Cond)
160
11 Planeación
Lo cual fuerza, al reconsiderar, varias instanciaciones alternativas para De y A:
1
2
3
4
5
6
7
mover(b,a,1)
mover(b,a,2)
mover(b,a,3)
mover(b,a,4)
mover(b,a,c)
mover(b,a,1)
mover(b,a,2)
Para hacer más eficiente este paso del planeador, es posible permitir variables no
instanciadas en las metas. Para el ejemplo del mundo de los bloques, las condiciones
de mover serı́an definidas como:
1
2
precond(mover(Bloque,De,A),
[despejado(Bloque),despejado(A),en(Bloque,De)]).
Si reconsideramos con esta nueva definición la situación inicial, la lista de condiciones computadas serı́a:
1
[despejado(Bloque),despejado(A),en(Bloque,a)]
Observen que esta lista de metas puede ser satisfecha inmediatamente en la situación inicial de nuestro ejemplo si Bloque/c y A/2. Esta mejora en eficiencia se
logra postergando la decisión de cómo instanciar las variables, al momento en que
ya se cuenta con más información para ello.
Este ejemplo ilustra el poder de la representación con variables, pero el precio a
pagar es una mayor complejidad. Para empezar, nuestro intento por definir precond
para mover/3 es erróneo, pues permite movimientos como mover(c, a, c), que da
como resultado que !el bloque c esté en el bloque c! Esto podrı́a arreglarse si especificáramos que De y A deben ser diferentes:
1
2
3
4
precond(mover(Bloque,De,A),
[despejado(Bloque),despejado(A),en(Bloque,De),
diferente(Bloque,A), diferente(De,A),
diferente(Bloque,De)]).
donde di f erente/2 significa que los dos argumentos no denotan al mismo objeto
Prolog. Una condición como estas, no depende del estado del problema, de forma
que no puede volverse verdadero mediante acción alguna, pero debe verificarse evaluando el predicado correspondiente. Una manera de manejar estas cuasi-metas es
agregar al predicado satis f echo/2 la siguiente cláusula:
1
2
3
satisfecho(Estado,[Meta|Metas]) :satisface(Meta),
satisfecho(Estado,Metas).
11.7 Variables y planes no lineales
161
De forma que debemos definir también:
1
satisface(diferente(X,Y))
Tal relación tiene éxito si X y Y no se corresponden. Si X y Y son lo mismo,
la condición es falsa. Este caso deberı́a tratarse con imposible, pues la condición
deberá seguir siendo falsa, sin importar las acciones que serán adoptadas en el plan.
En otro caso, estamos ante falta de información y satis f ace se deberı́a postergar.
11.7.2.
Planes no lineales
Un problema con nuestro planeador es que considera todos los posibles ordenes
de las acciones, aún cuando las acciones son completamente independientes. Consideren el problema ilustrado en la figura 11.3, donde la meta es construir dos pilas
de bloques que están de antemano bien separados. Las dos pilas puede construirse
independientemente con los siguientes planes:
1
2
Plan1 = [mover(b,a,c), mover(a,1,b)]
Plan2 = [mover(e,d,f), mover(d,8,e)]
El punto importante aquı́ es que estos planes no interaccionan entre ellos, de
forma que el orden de las acciones sólo es relevante dentro de cada plan. Tampoco
importa si se ejecuta primero Plan1 o Plan2 y es incluso posible ejecutarlos de
manera alternada, por ejemplo:
1
[mover(b,a,c), mover(e,d,f), mover(d,8,e), mover(a,1,b)]
Sin embargo, nuestro planeador considerará las 24 permutaciones posibles de
las cuatro acciones, aunque existan solo 4 alternativas: 2 permutaciones para cada uno de los planes. El problema se debe a que el planeador insiste en el orden
total de las acciones en el plan. Una mejora se lograrı́a si, en los casos donde el
orden no es importante, la precedencia entre las acciones se mantiene indefinida.
Entonces nuestros planes serán conjuntos de acciones parcialmente ordenadas. Los
planeadores que aceptan este tipo de representación se conocen como planeadores
no lineales.
Consideremos nuevamente el ejemplo de la figura 11.3. Analizando las metas
en(a, b) y en(b, c) el planeador no lineal concluye que las siguientes dos acciones
son necesarias en el plan:
1
2
M1 = mover(a,X,b)
M2 = mover(b,Y,c)
162
11 Planeación
e
b
a
c
1
2
1
3
4
5
6
f
d
7
8
a
d
b
e
c
f
2
3
4
5
6
7
8
Figura 11.3 Una tarea de planeación con dos planes independientes
No hay otra forma de resolver ambas metas, pero el orden de estas acciones es
aún indeterminado. Ahora consideren las condiciones de ambas acciones. La condición de mover(a, X, b) incluye depe jado(a), la cual no se satisface en la situación
inicial, por lo que necesitamos una acción de la forma:
1
M3 = mover(Bloque,a,A).
que precede a M1. Ahora tenemos una restricción en el orden de las acciones:
1
antes(M3,M1)
Ahora revisamos si M3 y M1 pueden ser el mismo movimiento. Como este no
es el caso, el plan tendrá que incluir tres movimientos. Ahora el planeador debe
preguntarse si hay una permutación de [M1, M2, M3] tal que M3 preceda a M1, tal
que la permutación es ejecutable en el estado inicial del problema y las metas se
cumplen en el estado resultante. Dadas las restricciones de orden anteriores tres
permutaciones de seis, cumplen con los requisitos:
1
2
3
[M3,M1,M2]
[M3,M2,M1]
[M2,M3,M1]
Y de estas permutaciones, solo la del medio cumple con el requisito de ser ejecutable bajo la sustitución Bloque/c, A/2, X/1,Y /3. Como se puede intuir, la complejidad computacional no puede ser evitada del todo por un planeador no lineal, pero
puede ser aliviada considerablemente.
Referencias
163
Referencias
1. I. Bratko. Prolog programming for Artificial Intelligence. Addison-Wesley, 3rd edition, 2001.
2. Alonzo Church. A note on the entscheidungsproblem. Journal of Symbolic Logic, 1:40–41,
1936.
3. K. Clark. Negations as failure. In H. Gallaire and J. Minker, editors, Logic and Databases,
pages 293–322. Plenum Press, New York, USA, 1978.
4. A. Colmerauer and P. Roussel. The birth of Prolog. In T. H. Bergin and R. G. Gibson, editors,
History of Programming Languages, chapter The birth of Prolog, pages 331–367. ACM Press
/ Addison-Wesley, 1996.
5. M.R. Genesereth and N.J. Nilsson. Logical Foundations for Artificial Intelligence. Morgan
Kauffman Publishers, Inc., Palo Alto, CA., USA, 1987.
6. R. A. Kowalski. Predicate logic as a programming language. In J. L. Rosenfeld, editor,
Information Processing, pages 569–574. North-Holland, 1974.
7. Robert A. Kowalski and Donald Kuehner. Linear resolution with selection function. Artificial
Intelligence, 2(3/4):227–260, 1971.
8. J. McCarthy. Programs with common sense. In Proceedings of the Symposium on the Mechanization of Thought Processes, Teddington, England, 1958.
9. M. Minsky. The Society of Mind. Simon and Schuster, New York, NJ., USA, 1986.
10. T.M. Mitchell. Machine Learning. Computer Science Series. McGraw-Hill International
Editions, Singapore, 1997.
11. Shan-Hwei Nenhuys-Chen and Ronald de Wolf. Foundations of Inductive Logic Programming, volume 1228 of Lecture Notes in Artificial Intelligence. Springer-Verlag, Berlin Heidelberg, 1997.
12. Ulf Nilsson and Jan Maluszynski. Logic, Programming and Prolog. John Wiley & Sons Ltd,
2nd edition, 2000.
13. J. R. Quinlan. Induction of decision trees. Machine Learning, 1:81–106, 1986.
14. J.R. Quinlan. C4.5: Programs for Machine Learning. Morgan Kaufmann, San Mateo, CA.,
USA, 1993.
15. J. A. Robinson. A machine-oriented logic based on the resolution principle. Journal of the
ACM, 12(1):23–41, 1965.
16. J. A. Robinson. Logic and logic programming. Communications of the ACM, 35(3):40–65,
1992.
17. Stuart J. Russell and Peter Norvig. Artificial Intelligence, a modern approach. Prentice Hall,
New Jersey, USA, 2nd edition, 2003.
18. C. Shannon and W. Weaver. The mathematical theory of communication. The Bell System
Technical Journal, 27:623–656, July, October 1948.
19. Alan M. Turing. On the computable numbers, with applications to the entscheidungsproblem.
In Proceedints of the London Mathematical Society, volume 42 of series 2, pages 230–265,
1936.
20. D. H. D. Warren. An abstract Prolog instruction set. Technical Report 309, SRI, 1983.
Descargar