Notas sobre programación lógica Edelmira Pasarella Mayo del 2004 1 Índice 1. Definiciones básicas 3 2. Semántica de los programas lógicos 4 3. Resolución SLD 6 4. Búsqueda y backtracking 9 2 1. Definiciones básicas Definición 1 Sean A1 . . . An , B1 . . . Bm átomos y x1 . . . xs las variables que ocurren en esos átomos. 1. Un literal es una fórmula atómica o su negación. Las fórmulas atómicas son llamadas literales positivos y sus negaciones, literales negativos. 2. Una cláusula es una fórmula de la forma ∀x1 . . . ∀xs (A1 ∨ . . . ∨ An ∨ ¬B1 ∨ . . . ∨ ¬Bm ) 3. Una cláusula es una cláusula de Horn si contiene a lo sumo un literal positivo. 4. Una cláusula de programa es una cláusula con exactamente un literal positivo. Si una cláusula de programa contiene al menos un literal negativo es una regla: ∀x1 . . . ∀xs (A ∨ ¬B1 ∨ . . . ∨ ¬Bm ) ≡ ∀x1 . . . ∀xs (A ← B1 ∧ . . . ∧ Bm ) De otro modo, es un hecho: ∀x1 . . . ∀xs A 5. Un objetivo es una cláusula que no tiene literales positivos. Es decir, ∀y1 . . . ∀ys (¬B1 ∨ . . . ∨ ¬Bm ) ≡ ¬∃y1 . . . ∃ys (B1 ∧ . . . ∧ Bm ) 6. Una cláusula definida es una cláusula cuya forma clausal viene dada por una secuencia de literales. Es decir, el orden en el cual ocurren los literales en la cláusula es importante. 7. Un programa lógico definido es un conjunto finito de cláusulas definidas de programa. 3 Notación Para simplificar, en el resto de este documento cuando hablamos de cláusulas y programas nos referimos a cláusulas y programas definidos, respectivamente. Además, utilizaremos la siguiente notación: 1. Hechos (o cláusulas unitarias): A← 2. Reglas:A←B1 , . . . , Bm A se llama la cabeza y B1 , . . . , Bm la cola de la regla (cláusula), respectivamente. 3. Objetivos: ←A1 . . . , Ak 2. Semántica de los programas lógicos La semántica da significado a los programas y nos permite describir formalmente lo que calculan. Hay tres maneras bien conocidas de dar significado o semántica a los programas lógicos: la semántica declarativa, la semántica operacional y la semántica denotacional (comúnmente llamada semántica de punto fijo). En esta sección presentamos algunas nociones y teoremas básicos relacionados a la semántica de los programas lógicos definidos (para mayor detalle, ver [2, 1, 4, 5]). Donde sea posible seguiremos la terminologı́a de Lloyd [4]. Definición 2 Sea L un lenguaje de primer orden. 1. El universo de Herbrand de L, denotado HL , es el conjunto de todos los términos de base que pueden formarse a partir de las constantes y los sı́mbolos de función que ocurren en L. Ejemplo Sea L = {0, suc, nat} donde 0 es una constante, suc es un sı́mbolo de función de aridad 1 y nat es un predicado de aridad 1. En los próximos tres ejemplos nos referiremos a este lenguaje. El universo de Herbrand de L es: HL = {0, suc(0), suc(suc(0), . . . , suci (0), . . .} 2. La base de Herbrand de L, denotada BL , es el conjunto de todos los átomos que pueden formarse a partir de los predicados que ocurren en L y los términos en HL . 4 Ejemplo La base de Herbrand de L es: BL = {nat(0), nat(suc(0)), . . . , nat(suci (0)), . . .}. 3. Una estructura A para L es una estructura de Herbrand si su dominio es HL y, para cada sı́mbolo de función f de L y elementos t1 . . . , tn de A, f A (t1 , . . . , tn ) = f (t1 , . . . , tn ). Para cada constante c en L, cA = c. Ejemplo Una estructura de Herbrand para L es: A = hHL , 0A , sucA , BL i, donde 0A = 0 y sucA (t) = suc(t) para todo t ∈ HL . 4. Si Γ un conjunto de sentencias, un modelo de Herbrand de Γ es una estructura de Herbrand que es un modelo para Γ. Debido a que en los modelos de Herbrand la interpretación de las constantes y los sı́mbolos de función son fijas, es posible identificar un modelo de Herbrand con un subconjunto de la base de Herbrand. Consideremos un programa lógico P . P induce un lenguaje de primer orden donde las constantes, los sı́mbolos de función y los predicados son, respectivamente, las constantes, los sı́mbolos de función y los predicados que ocurren en P . Entonces, podemos hablar del universo de Herbrand de P , denotado HP . Asimismo podemos hablar de la base de Herbrand de P , denotada BP . Ejemplo Sea P el siguiente programa: p(a)← p(b)← q(a)← r(f (x))←p(x), q(x) El universo y la base de Herbrand de P son, respectivamente: HP = {a, b, , f (a), f (b), f (f (a)), f (f (b)), f (f (f (a))), . . .} BP = {p(a), p(b), q(a), q(b), p(f (a)), p(f (b)), q(f (a)), p(f (b)), p(f (f (a))), p(f (f (b))), q(f (f (a)), q(f (f (b)) . . .} 5 Semántica declarativa. Desde el punto de vista lógico, un programa P puede verse como una teorı́a lógica formada por las cláusulas del programa. Los modelos de Herbrand de esta teorı́a son considerados los modelos del programa P . Por ejemplo, la base de Herbrand del programa P , BP , es un modelo de P . Entre las estructuras de Herbrand que son modelos de P , se destaca el que contiene exactamente los átomos que son consecuencia lógica de P . Este modelo corresponde al significado “entendido” o “estándar” del programa y es llamado el modelo mı́nimo de P , MP . El modelo MP se define como sigue: MP = {A ∈ BP : P |= A} Ejemplo El modelo mı́nimo del programa P es: MP = {p(a), p(b), q(a), r(f (a))} Semántica operacional. Está definida por el proceso de inferencia utilizado para probar que un objetivo puede ser derivado del programa. En la próxima sección estudiaremos en detalle este punto. Semántica denotacional. Esta semántica asigna significado a un programa asociándole un función sobre el dominio calculado por el programa. El significado viene dado entonces por el punto fijo de la función, si existe. En este documento no entramos en detalle de este tema (remitimos al lector interesado a [7]). 3. Resolución SLD La semántica operacional estándar de los programas lógicos está dada por un procedimiento de refutación basado en la regla de resolución, llamado resolución SLD [2], definido originalmente por Kowalski [3]. Resolución SLD significa “Linear resolution with Selection function for Definite clauses”. Definición 3 La función de selección, llamada regla de computación, es una función que va de un conjunto de objetivos a un conjunto de átomos de forma tal que el valor de la función para un objetivo es un átomo, llamado el átomo seleccionado en ese objetivo. 6 Definición 4 Sea P un programa, G = ←A1 , ..., Aj , ..., An (n ≥ 1) y R una regla de computación. 1. Supongamos que R(G) = Aj y C = A←B1 , ..., Bk es una cláusula de programa con las variables renombradas. Si A y Aj son unificables con unificador más general (umg) θ, entonces el objetivo G0 = ←(A1 , ..., Aj−1 , B1 , ..., Bk , Aj+1 , ..., An )θ es derivado de G y C vı́a R usando θ. También podemos decir que G0 es SLD-derivado de G en P vı́a R usando θ. Es decir, G0 es un resolvente de las cláusulas G y C. 2. Una SLD-derivación de G con respecto a P vı́a R es una secuencia (posiblemente infinita) hG0 , θ0 i, hG1 , θ1 i, ..., donde G = G0 , θ0 es la sustitución identidad y Gi+1 se deriva de Gi y alguna cláusula en P vı́a R usando θi+1 . En general, llamamos SLD-derivación a una SLDderivación vı́a una regla de computación. Una SLD-derivación puede tener éxito o fallar. 3. Una SLD-derivación con éxito es una SLD-derivación finita que finaliza con la cláusula vacı́a. En este caso la SLD-derivación se llama una SLD-refutación. Una SLD-derivación con fallo es una SLDderivación finita que finaliza con un objetivo diferente a la cláusula vacı́a en el cual el átomo seleccionado por la regla de computación utilizada no unifica con la cabeza de ninguna de las cláusulas del programa. 4. Sea hG0 , θ0 i, ..., hGn , θn i una SLD-refutación de G con respecto a P donde G = G0 . La SLD-respuesta calculada de G con respecto a P se define como θ = θ1 ...θn |var(G) 1 . Esto es, θ es la composición de los umg’s utilizados en la SLD-refutación considerada, restringida a las variables que ocurren libres en G. La resolución SLD es independiente de la regla de computación. Esto es, si P ∪ {G} es insatisfactible, encontraremos una refutación utilizando cualquier regla de computación. En lo sucesivo, sin pérdida de generalidad, suponemos que la regla de computación selecciona el primer literal en el objetivo. Por convención, en los ejemplos subrayamos el literal seleccionado por la regla de computación en cada caso. 1 var(G) es el conjunto de variables que ocurren libres en G y la notación θ1 ...θn |var(G) representa la restricción de la sustitución resultante de la composición θ1 ...θn a las variables que aparecen en var(G). 7 Ejemplo 1. Sea G = ←r(f (a)), la siguiente derivación es una refutación de G con respecto a P , h←r(f (a)), i, h←p(a), q(a), {a/x}i, h←q(a), i, h, i En esta refutación la respuesta calculada es debido que la variable x no ocurre en el objetivo. 2. Sea G = ←r(f (x)), la siguiente derivación es una refutación de G con respecto a P . h←r(f (x)), i, h←(p(y), q(y)), {y/x}i, h←q(a), {a/y}i, h, i con respuesta calculada θ = {y/x}{a/y}|{x} = {a/x} 3. Sea G = ←r(x), la siguiente derivación es una refutación de G con respecto a P . h←r(x), i, h←(p(y), q(y)), {f (y)/x}i, h←q(a), {a/y}i, h, i con respuesta calculada θ = {f (y)/x}{a/y}|{x} = {f (a)/x} Corrección y completitud Sea G =← B1 , ..., Bm un objetivo. Desde el punto de vista lógico, una respuesta de G con respecto a un programa lógico P es una sustitución para var(G). Sea θ una respuesta de G con respecto Vm a P , ∀θ es una respuesta co∀ rrecta de G con respecto a P si P |= (( i=1 Bi )θ) . Su contrapartida desde el punto de vista procedimental es la noción de SLD-respuesta calculada. Clark estableció la corrección y la completitud de la SLD-resolución relacionando las respuestas correctas (salida declarativa) y las SLD-respuestas calculadas (salida procedural). Abajo presentamos los correspondientes teoremas. Teorema 1 (Teorema de corrección) Sea P un programa y G un objetivo. Entonces, cada SLD-respuesta calculada de G con respecto a P es una respuesta correcta de G con respecto a P . 8 Teorema 2 (Teorema de Completitud) Sea P un programa y G un objetivo. Para cada respuesta correcta θ de G con respecto a P , hay una SLDrespuesta calculada θ1 de G con respecto a P y una sustitución σ tal que θ = θ1 σ. El teorema de completitud nos dice que cada respuesta correcta de un objetivo G con respecto a un programa P es una instancia de una respuesta calculada de G con respecto a P . Para ilustrar esta propiedad consideremos el siguiente ejemplo. Ejemplo Sea P = {menor(0, suc(x))←}. Tenemos que P |= menor(0, y){suc(suc(0))/y}. Esto es, θ = {suc(suc(0))/y} es una respuesta correcta de ←menor(0, y) con respecto a P . Una SLDrespuesta calculada de ←menor(0, y) con respecto a P es {suc(x)/y} y tenemos entonces que θ = {suc(x)/y}{suc(0)/x}. 4. Búsqueda y backtracking A continuación presentamos conceptos relacionados con los procedimientos que implementan la SLD-resolución. Lo primero que hay que decir es que el espacio de búsqueda para SLD-refutaciones viene dado por una estructura de árbol conocido como SLD-árbol. Para ampliar detalles remitimos al lector a [5, 6]. Definición 5 Un SLD-árbol de G con respecto a P vı́a R es un árbol tal que cada nodo es un objetivo (que podrı́a ser la cláusula vacı́a), cuya raı́z es G. Sea ←A1 , ..., Aj , ..., An (n ≥ 1) un nodo de un SLD-árbol. Supongamos que R(←A1 , ..., Aj , ..., An ) = Aj . Entonces, para cada cláusula del programa P , A ← B1 , ..., Bk tal que Aj y A son unificables con umg θ, el nodo tiene un hijo ←(A1 , ..., Aj−1 , B1 , ..., Bk , Aj+1 , ..., An )θ. La cláusula vacı́a, si ocurre en el árbol, no tiene hijos. Es importante destacar aquı́ la independencia de la regla ya que permite reducir sustancialmente el espacio de búsqueda al poder fijar una regla de computación a priori y usarla para construir el SLD-árbol. Ejemplo Consideremos el programa P1 = P ∪ {r(x)←q(x)}, es decir, 9 (1) (2) (3) (4) (5) p(a)← p(b)← q(a)← r(f (x))←p(x), q(x) r(x)←q(x) y el objetivo G = ←r(x). El SLD-árbol de G con respecto a P1 es el siguiente: ←r(x) ←(p(y), q(y)){f (y)/x} ←(q(y)){a/y} ←q(y){y/x} ←(q(y)){b/y} {y/x}{a/y} {f (y)/x}{a/y} Note que cada camino del árbol SLD corresponde a una derivación SLD. A continuación presentamos las derivaciones del árbol de arriba: I II III ←r(x) ←r(x) ←r(x) | (4) | ←(p(y), q(y)){f (y)/x} | (1) | ←(q(y)){a/y} | (3) | {f (y)/x}{a/y} | (4) | ←(p(y), q(y)){f (y)/x} | (2) | ←(q(y)){b/y} | (5) | ←q(y){y/x} | (3) | {y/x}{a/y} 10 Las derivaciones I y III son refutaciones mientras que II es una derivación con fallo. En la implementación de la SLD-resolución, además de la unificación, hay dos problemas importantes a resolver: Definir la regla de computación. En las implementaciones de Prolog, la regla de computación selecciona siempre el literal más a la izquierda del objetivo considerado. Cuál cláusula del programa utilizar2 para resolver con el literal seleccionado. En las implementaciones de Prolog, las cláusulas se escogen siguiendo el orden en el cual aparecen listadas en el programs. Ejemplo Sea programa P2 : (1) (2) (3) (4) p(b)← p(a)← q(x)←r(x) r(b)← y el objetivo ←p(x), q(x). La regla de computación selecciona el literal p(x) y comienza tratando de resolver con la cláusula (1). El esquema generado por la regla de computación y la escogencia de la cláusula de programa para aplicar la regla de resolución se traduce en un recorrido en profundidad del SLD-árbol correspondiente. Esto es, la búsqueda de una refutación de un objetivo G con respecto a un programa P , se inicia siempre por el camino más a la izquierda del SLD-árbol de G con respecto a P (asumiendo que este árbol refleja el orden en el que ocurren las cláusulas en el programa P ). 2 Recordemos que en realidad se utilizan variantes de las cláusulas de programa. 11 Ejemplo Consideremos el SLD-árbol de ←p(x), q(x) con respecto al programa P2 : ←p(x), q(x) ←q(x){b/x} ←q(x){a/x} r(u){b/u} r(u){a/u} En este árbol, el camino más a la izquierda es una refutación de ←p(x), q(x) con respecto a P2 . Veamos ahora qué sucede si modificamos el orden de las cláusulas (1) y (2) del P2 : (1) (2) (3) (4) p(a)← p(b)← q(x)←r(x) r(b)← Llamemos a este programa P3 . El SLD-árbol de ←p(x), q(x) con respecto a P3 es: ←p(x), q(x) ←q(x){a/x} ←q(x){b/x} r(u){a/u} r(u){b/u} En este árbol, a diferencia del anterior, la refutación viene dada por el camino más a la derecha. Prolog, intenta conseguirla probando por el camino más a la izquierda y falla. Qué hace entonces para encontrar otro camino que le dé una refutación (si la hay)? En esta situación de fallo, Prolog utiliza un mecanismo llamado bactracking que, en este caso, consiste en devolverse en el camino considerado hasta encontrar un nodo con una rama a la derecha. Si se encuentra 12 tal nodo, se reintenta por esta rama tomando siempre el camino más a la izquierda. Prolog realiza backtracking hasta encontrar un camino con éxito, si lo hay. Nótese que en la medida que Prolog hace backtracking las sustituciones que se habı́an obtenido por el camino que falló se van deshaciendo también para reconstruirlas en el nuevo intento. Veamos esto en el árbol de ←p(x), q(x) con respecto a P3 obteniendo la sustitución {b/x}. Al llegar al nodo r(u){a/u} no hay ninguna cláusula cuya cabeza unifique con r(a), falla y se devuelve al nodo ←q(x){a/y} deshaciendo la sustitución. En este nodo no hay ramas a la derecha para explorar. Entonces, se devuelve otra vez y llega al nodo raı́z, es decir, ←p(x), q(x). Reintenta nuevamente con otra cláusula cuya cabeza unifique con p(x) y encuentra la cláusula (2) y sigue ”‘bajando”’ por esta rama hasta llegar a la cláusula , con lo cual, finalmente encuentra una refutación de ←p(x), q(x) con respecto a P3 . En los SLD-trees pueden ocurrir ramas infinitas. Para ver esto basta considerar el SLD-árbol del objetivo ←p con respecto al programa {p←p}. El estudio de este tópico está fuera del alcance de este documento por lo que remitimos al lector a [4]. Para terminar esta sección presentamos un intérprete abstracto para programas lógicos [6]: Entrada: Salida: Algoritmo: Un objetivo G y un programa P Una instancia de G que es una consecuencia lógica de P , o no en caso contrario Inicializa el resolvente con G mientras el resolvente no sea vacı́o hacer escoger un literal A del resolvente escoger una variante de una cláusula A0 ←B1 , . . . , Bn de P tal que A y A0 unifiquen con umg θ (si no existe tal cláusula, salir del lazo mientras reemplazar A por B1 , . . . , Bn en el resolvente aplicar θ al resolvente y a G fin-mientras si el resolvente es vacı́o entonces dar como salida G sino dar como salida no 13 Ejercicios 1. Sea P un programa. Demuestre que la estructura de Herbrand vacı́a (∅ ⊂ BP ) es un modelo de P si, y sólo si, cada regla en P tiene una cola no vacı́a. Es decir, P no contiene hechos. 2. Demuestre que BP es un modelo de P para todo programa P . 3. Considere el siguiente programa: (1) q(a)← (2) r(b)← (3) p(x)←r(x) 4. a) Dé el lenguaje subyacente. b) Dé el universo y la base de Herbrand correspondientes. c) Dé el modelo mı́nimo. d) Dé un modelo de Herbrand que sea diferente al modelo mı́nimo y a la base de Herbrand. Considere el siguiente programa: (1) sum(x, 0, x)← (2) sum(x, suc(y), suc(z))←sum(x, y, z) 5. a) Dé el lenguaje subyacente. b) Dé el universo y la base de Herbrand correspondientes. c) Dé un modelo de Herbrand que sea diferente a la base de Herbrand. Considere el siguiente programa en Prolog: (1) pertenece(X, [X| ]). (2) pertenece(X, [ |L]) : −pertenece(X, L). Dé el SLD-árbol de la consulta ? − pertenece(X, [a, b, c]). con respecto al programa de arriba. Simule la búsqueda de la resolución que harı́a Prolog. Cuál es la sustitución resultante para X? Hay otras? Si la respuesta es sı́, cómo podrı́amos obtenerlas? 14 6. Considere el siguiente programa en Prolog: (1) conc([ ], L, L). (2) conc([X|L1], L2, [X|L3]) : −conc(L1, L2, L3). Dé el SLD-árbol de la consulta ? − conc(L1, L2, [a, b, c]). con respecto al programa de arriba. Simule la búsqueda de la resolución que harı́a Prolog. Dé todas las posibles respuestas calculadas. 7. Considere el universo y la base Herbrand para los programas de los ejercicios 5 y 6. Dé ejemplos de respuestas correctas y de respuestas calculadas. Discuta al respecto y dé conclusiones. 15 Referencias [1] K. R. Apt. Logic programming. In J.van Leeuwen., editor, Handbook of Theoretical Computer Science, pages 493–574. Elsevier Science Publishers B.V., 1990. [2] K. R. Apt and M.H. van Emden. Contributions to the theory of logic programming. The Journal of ACM, 29(3):841–862, 1982. [3] R.A. Kowalski. Predicate logic as a programming language. Information Processing Letters 74, pages 569–574, 1974. [4] J. W. Lloyd. Foundations of Logic Programming. Springer-Verlag, 2nd edition, 1987. [5] Anil Nerode and Shore Richard A. Logic for applications. SpringerVerlag, 1993. [6] L. Sterling and EG. Shapiro. The art of Prolog. The MIT Press, 1994. [7] van Emden, M.H. and R.A. Kowalski. The semantics of predicate logic as a programming language. Journal of ACM, 23(4):733–742, 1976. 16