TEMA 4

Aplicaciones del Lenguaje PROLOG


1 Aplicaciones actuales del Lenguaje PROLOG

1.1 Inteligencia Artificial

1.2 Sistemas Expertos

1.3 Compiladores

1.4 Miscelánea

2 Líneas Futuras

3 Bibliografía


 

Aplicaciones actuales del Lenguaje PROLOG

Como hemos visto, programar en Prolog abre nuestras mentes a un nuevo modo de ver la computación. Puede que nos resulte difícil comprender esta técnica, pero nos preocupa, finalmente, más que su dificultad, su utilidad, ya que muchas veces nos preguntamos para qué aprender conceptos que puede que en el futuro no sirvan.

Este capítulo trata de mostrar de forma general la importancia actual que este tipo de paradigmas tiene. En concreto, el paradigma declarativo lógico y, por supuesto, el lenguaje Prolog.

Prolog se puede utilizar para resolver, básicamente, cualquier tipo de problema. Principalmente es útil en la gestión de Juegos, en Inteligencia Artificial y Sistemas Expertos, como lenguaje especialmente pensado para construir bases de conocimientos basados en la lógica que forman parte importante de cualquier agente inteligente, en la construcción de Compiladores e Intérpretes, en el Reconocimiento del Lenguaje Natural, etc.

[ÍNDICE]

Inteligencia Artificial

La resolución de juegos y la planificación así como la construcción de agentes inteligentes constituyen amplios campos que abarca la rama de la Inteligencia Artificial. A continuación, veremos algunos ejemplos de resolución de juegos y problemas típicos de planificación implementados en Prolog. Podremos observar la facilidad con la que podemos plasmar las especificaciones de los problemas directamente, utilizando una sintaxis que nos proporciona un alto grado de abstracción. Esto nos aporta una gran ventaja a la hora de realizar el desarrollo de la aplicación una vez analizado el problema y diseñada su solución.

[ÍNDICE]

PROBLEMA DE LAS OCHO REINAS

El problema de las ocho reinas y en general de las N reinas, se basa en colocar 8 reinas en un tablero de 8´ 8 (o N en un tablero de N´ N, si el problema se generaliza), de forma que en no puede haber dos piezas en la misma línea horizontal, vertical o diagonal, ver Figura 1.

Figura 1

Posible solución para el problema de las ocho reinas

Este programa has sido muy estudiado por los matemáticos. Se trata de un problema NP-Completo que no tiene solución para N=2 y N=3. Para N=4 tiene una única solución. Para N=8 hay más de 80 soluciones dependiendo de las restricciones que se impongan.

Una forma de abordar el problema se lleva a cabo mediante la construcción de un predicado en Prolog del tipo siguiente: reinas(N, Solucion), donde N representa las dimensiones del tablero y el número de reinas y Solucion es una lista que contiene la permutación de la lista de números que resuelven el problema. Los índices de dicha lista representan la columna en la que se encuentra una reina y el número que almacena la posición o índice representa la fila donde la reina está colocada. Así, para el ejemplo mostrado en la Figura 1, tenemos que R=[2,4,1,3].

Este problema es resuelto, de modo clásico, por el método de prueba y error, luego se adapta perfectamente a un algoritmo de backtracking. Básicamente, el problema se reduce a colocar una reina, e intentar repetir el proceso teniendo en cuenta la reina colocada. Si logramos poner todas las reinas el problema se ha resuelto, en caso contrario, deshacemos lo que llevamos hasta ahora y probamos con otra combinación. Por tanto, hemos de generar un conjunto de permutaciones y seleccionar aquella que cumpla los requisitos impuestos por el juego.

Veamos el código que resuelve el problema:

domains

lista=integer*

predicates

rango(integer, integer, lista)

nondeterm dame(integer, lista, lista)

nondeterm permuta(lista, lista)

atacada(integer, integer, lista)

correcta(lista)

nondeterm reina(integer, lista)

clauses

rango(N, N, [N]):-!.

rango(N, M, [N|Cola]):-N<M, Aux=N+1, rango(Aux, M, Cola).

dame(X,[X|Xs],Xs).

dame(X,[Y|Ys],[Y|Zs]):-dame(X,Ys,Zs).

permuta([],[]).

permuta(L,[Z|Zs]):-dame(Z,L,Ys), permuta(Ys,Zs).

 

atacada(X,N,[Y|Ys]):-X=Y+N, !; X=Y-N, !.

atacada(X,N,[_|Ys]):-N1=N+1, atacada(X,N1,Ys).

correcta([]):-!.

correcta([X|Y]):-correcta(Y), not (atacada(X, 1, Y)).

reina(N, Solucion):-rango(1,N,L1), permuta(L1,Solucion), correcta(Solucion).

goal

write("Tamaño tablero = "), readint(N),

nl,

write("Soluciones: "), nl,

reina(N, Solucion),

write(Solucion),nl, fail.

[FUENTES EN FORMATO ZIP] [EJECUTABLE EXE]

Es muy importante comprender como funciona cada uno de los predicados para entender el funcionamiento del algoritmo general.

Prolog permite implementar los programas casi directamente a partir de las especificaciones realizadas a partir de un análisis y diseño de la solución desde un alto nivel de abstracción. Además el procedimiento de backtracking está implícito en el propio motor de inferencia, luego este paradigma se adapta perfectamente a nuestro problema.

Si analizamos y diseñamos nuestro problema tenemos que la forma de resolverlo se resume en los pasos siguientes:

  1. Para N, obtenemos una lista de números comprendidos entre 1 y N: [1,2,3,4,...,N].
  2. Obtenemos una permutación del conjunto de números de la lista.
  3. Comprobamos que la permutación es correcta.
  4. Si la permutación no es correcta, lo que debemos hacer es volver al paso 2 para generar una permutación nueva.

Comencemos a analizar la solución implementada. El problema se resuelve con el predicado reina(N, Solucion):-rango(1,N,L1), permuta(L1,Solucion), correcta(Solucion). Como vemos es, sencillamente, una copia de las especificaciones realizadas más arriba. Se genera el rango entre 1 y N, se obtiene una permutación y se comprueba si la permutación es, o no, correcta. En el caso de que cualquier predicado del consecuente falle, la propia máquina Prolog se encarga de realizar el proceso de backtracking. Con lo cual ya tenemos cubiertos los cuatro pasos fundamentales del algoritmo.

Para tener más claras las ideas, observemos el árbol de ejecución general del objetivo reina(4,Solucion) en la Figura 2.

Figura 2

Árbol de ejecución para el objetivo reina(4,Solucion)

Para N=4, existe una única solución, aunque si ejecutamos el programa, veremos que se obtienen dos soluciones. La segunda solución es la primera girada, ver Figura 3.

Figura 3

Soluciones al problema de las 4 reinas

[ÍNDICE]

ANALOGÍA

Analogía es un problema clásico de Inteligencia Artificial diseñado por Evans para resolver cuestiones de analogía geométrica en tests de Inteligencia.

En este tipo de tests de Inteligencia se presentan varias figuras geométricas complejas. Cada figura compleja está formada por figuras más pequeñas unas dentro de otras, a este conjunto lo denominamos Origen. Por otro lado, tenemos otro conjunto de figuras complejas que forman parte del conjunto Respuesta. Se trata, pues, de encontrar una similitud entre las figuras de Origen y las figuras del conjunto Respuesta. La relación entre las figuras de Origen es del tipo A es a B como C es a X, siendo X la solución que puede encontrarse, o no, en el conjunto Respuesta. Si existe en dicho conjunto una figura que satisfaga la anterior afirmación, X se empareja con dicha figura, en caso contrario, hemos de responder que no existe analogía entre el conjunto Origen y el conjunto Respuesta.

Veamos un ejemplo que ilustre todos estos conceptos. Dada la situación de la Figura 4, la respuesta correcta sería 2.

Figura 4

Problema de Analogía

La solución como vemos consiste en aplicar la operación invertir a la figura C. Teniendo en cuenta esto, podemos escribir un programa que resuelva, de forma general, este tipo de analogías. Los pasos fundamentales se exponen a continuación.

  1. A debe ser el inverso de B.
  2. Eso implica que debemos calcular el inverso de C.
  3. Si el inverso de C está en la lista de respuestas, hemos encontrado la respuesta.
  4. En caso contrario, volvemos al paso 2 para encontrar otra solución.

La implementación de las figuras complejas es sumamente importante. Por tanto, vamos a definirlas. Las figuras complejas están formadas por dos figuras más simples una dentro de otra. Así la figura A se puede representar mediante el predicado: dentro(cuadro,triangulo).

Una vez que sabemos plasmar dichas figuras, veamos el código fuente del programa Prolog que resuelve este tipo de analogías.

domains

disposicion= dentro(symbol, symbol);

fuera(symbol, symbol)

lista=disposicion*

test=determ (integer, disposicion) - (i, o)

predicates

figuras(integer, disposicion, disposicion, disposicion)

respuestas(integer, lista)

esta(disposicion, lista)

casar(disposicion, disposicion)

analogia(disposicion, disposicion, disposicion, disposicion, lista)

test_analogia: test

clauses

figuras(1, dentro(cuadro, triangulo), dentro(triangulo, cuadro), dentro(circulo, cuadro)):-!.

figuras(2, dentro(cuadro, triangulo), dentro(triangulo, cuadro), dentro(circulo, cuadro)).

respuestas(1, [dentro(circulo, triangulo), dentro(cuadro, circulo), dentro(triangulo, cuadro)]):-!.

respuestas(2, [dentro(circulo, triangulo), dentro(circulo, cuadro), dentro(triangulo, cuadro)]).

esta(X, [X|_]):-!.

esta(X, [_|Y]):-esta(X,Y).

casar(dentro(F1, F2), dentro(F2, F1)):-!.

casar(fuera(F1, F2), fuera(F2, F1)).

analogia(A, B, C, X, Respuestas):-casar(A, B),

casar(C, X),

esta(X, Respuestas).

test_analogia(Numero, X):-figuras(Numero, A, B, C),

respuestas(Numero, Respuestas),

analogia(A, B, C, X, Respuestas).

goal

write("Dame número de test: [1,2]: "),

readint(N),

test_analogia(N, X),

write(X).

[FUENTES EN FORMATO ZIP] [EJECUTABLE EXE]

Si nos centramos en el predicado analogia vemos que se ajusta, perfectamente, a los pasos generales de resolución del problema expuestos en la fase de análisis-diseño:

analogia(es_a(A, B), como_es_a(C, X), Respuestas):-casar(A, B),

casar(C, X),

esta(X, Respuestas).

Por un lado intentamos casar A y B, esto implica que A debe ser el inverso de B. Si este predicado se cumple, continuamos con la siguiente cláusula del consecuente. Como X no está aún instanciada, X se instancia al valor inverso de C. A continuación, se comprueba si X está en la lista de respuestas. En caso de que esté, analogia termina, si no, analogia mediante backtracking busca una nueva solución, hasta que la encuentre o demuestre que no existe.

test_analogia(Numero, X) es un predicado que permite probar el funcionamiento de analogia para un conjunto de figuras dado. Para testear el predicado diseñado se han seleccionado los tests de la Figura 5.

(a)

(b)

Figura 5

Tests del ejemplo codificado en Prolog

[ÍNDICE]

Sistemas Expertos

Los agentes y sistemas expertos se pueden considerar entes capaces de actuar como lo haría un experto humano en la resolución de un determinado problema. Pueden percibir el ambiente mediante sensores y actúan sobre ese ambiente por medio de efectores. En los agentes hardware, los sensores son sustituidos por cámaras y telémetros y los efectores son reemplazados mediante motores. En los agentes software, las percepciones y acciones vienen a ser las cadenas de bits codificados. La Figura 6 muestra un agente genérico.

Figura 6

Estructura general de un agente inteligente

Pero, tanto si un agente es hardware o software, necesita un programa que permita transformar los datos provenientes del entorno de usuario en acciones adecuadas para la resolución del problema para el que han sido diseñados.

Estos programas se pueden construir siguiendo multitud de paradigmas, y uno de ellos es la programación lógica, que, como hemos visto, se adapta perfectamente a la representación del conocimiento.

Existen muchos tipos de sistemas expertos: de diagnóstico médico, para el análisis de imágenes de satélite, clasificadores, controladores, asesores, agentes económicos, etc.

Como ejemplo de sistema experto tenemos el programa Prolog que permite conocer la velocidad de un vehículo dadas sus características y las de la vía por la que circula.

Dado que hemos hablado repetidas veces de estos sistemas a lo largo del curso, no vamos a profundizar más en esta cuestión.

[ÍNDICE]

Compiladores

La comprensión del lenguaje natural y la construcción de compiladores e intérpretes son campos de desarrollo muy adecuados para Prolog.

En esta sección vamos a ver una aplicación de la programación lógica a la teoría de autómatas y lenguajes formales.

En Prolog se puede especificar un autómata finito mediante tres hechos, simplemente. El predicado inicial, inicial(Q), es true si Q es el estado inicial. El predicado final(Q) es true si Q es el estado final. Y el predicado delta(Q, X, Q1) que funciona del siguiente modo: es true si el autómata cambia del estado Q al estado Q1 al recibir el símbolo X. El autómata recibe una cadena de símbolos del alfabeto S *. El autómata reconoce el lenguaje si comenzó en el estado inicial y acabó en el estado final tras seguir las transiciones especificadas por d .

Veamos un ejemplo concreto utilizando el lenguaje (ab)*. La Figura 7 representa el autómata que reconoce el lenguaje.

Figura 7

Reconocedor del lenguaje (ab)*

El código del programa Prolog para implementar el reconocedor se expone a continuación.

domains

lista=char*

predicates

inicial(symbol)

final(symbol)

delta(symbol, char, symbol)

aceptar(lista, symbol)

acepto(lista)

clauses

inicial(q0).

final(q0).

delta(q0, 'a', q1).

delta(q1, 'b', q0).

acepto(L):-inicial(Q), aceptar(L,Q).

aceptar([X|Y], Q):-delta(Q, X, Q1), aceptar(Y, Q1).

aceptar([],Q):-final(Q).

goal

write("Dame lista de símbolos: "), readterm(lista, L),

acepto(L),

nl,

write("Pertenece al lenguaje").

[FUENTES EN FORMATO ZIP] [EJECUTABLE EXE]

Las trazas de los programas para los casos ab y aa se muestran en la Figura 8.

(a)

(b)

Figura 8

Árboles de ejecución del predicado reconocedor del lenguaje (ab)*

[ÍNDICE]

Miscelánea

Como ya hemos comentado, Prolog se puede adaptar a la resolución de casi cualquier tipo de problema con gran facilidad. Aunque, en mi opinión, es ideal como lenguaje de implementación de aplicaciones provenientes del campo de la Inteligencia Artificial.

En este punto, vamos a proponer y resolver algunos problemas típicos que se pueden resolver en Prolog de un modo sumamente sencillo.

[ÍNDICE]

TORRES DE HANOI

El problema de las torres de Hanoi consiste en mover un conjunto de discos de un palo a otro palo utilizando un palo auxiliar situado en medio. Las reglas para mover los discos son las siguientes:

El pseudocódigo del algoritmo queda como sigue:

hanoi(N)Þ mover(N,izquierda,medio,derecha).

mover(1,A,_,C)Þ Pasar disco de A a C.

mover(N,A,B,C)Þ mover(N-1,A,C,B), mover(1,A,C), mover(N-1,B,A,C).

La solución del problema de las torres de Hanoi para tres discos y tres palos se muestra en la Figura 9.

Figura 9

Solución al problema de las Torres de Hanoi, con tres palos y tres discos

[ÍNDICE]

Líneas Futuras

Durante el curso hemos hablado varias veces de agentes inteligentes. Los agentes que funcionan como sistemas de razonamiento son necesarios para obtener resultados eficaces. La principal ventaja que ofrecen es su alto grado de modularidad. Además, es posible independizar la estructura de control del conocimiento, con lo que cada porción del mismo mantiene total independencia entre sí. Actualmente se trabaja en la construcción de agentes inteligentes verdaderamente eficientes tanto hardware como software. Para comprender cuales son las líneas futuras que se pretenden trazar, conviene saber qué se está haciendo en la actualidad, por tanto, vamos a describir brevemente los cuatro grupos principales que componen la clasificicación de los sistemas de razonamiento automático, cada uno diseñado específicamente para resolver distintos tipos de problemas:

Durante el curso, nos hemos centrado en el estudio del primer grupo de la clasificación citada. Los lenguajes lógicos, como ya se ha comentado, ofrecen muchas ventajas y, evidentemente, plantean inconvenientes que deben ser subsanados.

En concreto, si utilizamos un entorno de programación interpretado de Prolog notaremos que la eficiencia, en cuanto a velocidad de ejecución se refiere, disminuye, al igual que en cualquier lenguaje interpretado, frente a un lenguaje compilado.

El conjunto de instrucciones de las computadoras actuales resulta muy pobre en relación con la semántica de Prolog, por lo que la mayoría de los compiladores de Prolog, que también existen, realizan una compilación a un lenguaje intermedio en vez de hacerlo directamente a lenguaje máquina. El lenguaje más popular es el Warren Abstract Machine (WAM), que es un conjunto de instrucciones abstractas utilizable en Prolog y que se puede interpretar o traducir a lenguaje máquina. Existen otros compiladores que traducen Prolog a un lenguaje de alto nivel como Lisp o C, y utilizan dicho compilador para traducir a lenguaje máquina.

Antes del trabajo de Warren sobre la compilación de la inferencia en Prolog, la programación lógica resultaba excesivamente lenta para su uso generalizado. Los compiladores diseñados por Warren y otros permitieron a Prolog alcanzar velocidades adecuadas en estaciones de trabajo promedio modelo 1990. En fechas más recientes, la aplicación de la moderna tecnología de compilación, incluidos inferencia de tipo, codificación abierta y análisis de flujo de datos entre procedimientos ha permitido a Prolog alcanzar velocidades tan óptimas que lo hace competir con C en cuanto a diversos aspectos estándar (Van Roy, 1990). Por supuesto, el hecho de poder escribir un planificador o un analizador de lenguaje natural en unas cuantas decenas de líneas de Prolog, hacen que éste sea más preferible que C en la realización de los prototipos de gran parte de los proyectos de investigación de Inteligencia Artificial a escala reducida.

Aparte de las mejoras que se están realizando en cuanto a velocidad de ejecución también se han desarrollado y se están desarrollando ampliaciones de Prolog que permiten su adaptación a muchos problemas que han surgido recientemente: programación lógica y orientación a objetos, programación concurrente, programación visual y construcción de razonadores completos de lógica de primer orden.

[ÍNDICE]

Bibliografía

[Russell, 1996] Russell, Stuart. Norvig, Peter. "Inteligencia Artificial. Un enfoque moderno". Editorial Prentice Hall. 1996.

[Shapiro, 1986] E. Shapiro y L. Sterling. "The art of Prolog". The MIT Press, 1986.

[ÍNDICE]