Claúsulas y resolución
En la práctica resulta incómodo operar con un sistema
deductivo
formal en que las expresiones lógicas utilizadas tienen formas
muy
variadas y se debe elegir entre muchas reglas de deducción
aplicables
en cada paso.
Esta situación se puede mejorar notablemente restringiendo el
tipo de las expresiones lógicas a un formato más simple
de
operar. En particular, la forma de cláusula sólo
utiliza
los conectivos ¬ (negación) y
(disyunción),
y sólo requiere de una regla de deducción, llamada regla
de resolución.
Veremos que al restringir un sistema deductivo de esta manera no se
pierde generalidad porque existe un mecanismo de normalización
que
permite plantear cualquier problema lógico mediante
cláusulas.
Cláusulas
Una cláusula es una fórmula lógica
constituida
por una disyunción de literales
en que cada literal
Li es una fórmula
atómica negada (literal negativo) o no negada (literal
positivo).
El orden en que aparecen los literales no es relevante, y se puede
por
lo tanto representar la misma cláusula como un conjunto de
literales:
{L1, L2,
... Li, ... Ln-1, Ln}
En el caso particular en que este conjunto es vacío, la
cláusula
se denomina cláusula vacía y se simboliza
por
.
Resolución
La regla de resolución binaria permite derivar una nueva
cláusula (el resolvente) a partir de dos cláusulas
padres. En su forma más simple, se define mediante el
siguiente
esquema, cuya validez puede verificarse utilizando una tabla de verdad:
Para aplicarla, es necesario que las cláusulas padres contengan
un literal que manifieste la misma fórmula atómica (P
en el esquema), pero en forma positiva en un padre y negativa en el
otro.
Este literal es eliminado en el resolvente, obteniéndose la
disyunción
del resto de los literales de las cláusulas padres. El esquema
general
de la regla es:
P
Q1
Q2
...
Qn
¬P
R1
R2
...
Rm
Q1
Q2
...
Qn
R1
R2
...
Rm
Nótese que la regla de resolución produce un resolvente
más
grande o del mismo tamaño que la más grande de las
cláusulas
padres, excepto si una de las cláusulas padres contiene
sólo
un literal.
Demostración por resolución de contradicción
Una manera de demostrar formalmente un teorema es encontrar una cadena
de derivaciones cuyo último elemento es el teorema buscado. Sin
embargo, existe otra manera de enfrentar el problema:
- suponer lo contrario de lo que se desea demostrar
- probar que esa suposición es contradictoria.
Si suponiendo algo se llega a una contradicción, lo contrario de
ese algo debe ser verdadero. Este es un principio muy utilizado en
matemática
en lo que se llama demostración por el absurdo. Se basa
en
la siguiente propiedad:
(
A)
(
+ {¬A} es insatisfacible)
Es decir, si A es consecuencia lógica de
,
entonces no existe ninguna interpretación en que se verifiquen
simultáneamente
todas las fórmulas de
y ¬A.
Cuando se opera con cláusulas y la regla de
resolución,
las contradicciones se detectan cuando se llega a un resolvente
vacío
(
). Esto resulta evidente si se considera que la
única manera de producir un resolvente vacío es tomando
dos
cláusulas padres de un sólo un literal, positivo en un
caso,
negativo en el otro.
La ventaja de la demostración por resolución de
contradicción,
o demostración por refutación, es que la
orientación
del proceso se mantiene invariable, independientemente del problema
lógico
abordado: llegar a una contradicción. Concretamente, en el caso
de operar con cláusulas, el objetivo es encontrar resolventes
cada
vez más pequeños hasta llegar a un resolvente
vacío.
Se puede demostrar que para lograr este objetivo basta la regla de
resolución
(la regla de resolución es completa para la
refutación).
Normalización a forma clausal
Para aplicar el método de demostración por
refutación
utilizando la regla de resolución binaria, es necesario que
todas
las fórmulas lógicas involucradas en el problema se
puedan
reescribir en forma de cláusulas semánticamente
equivalentes.
Esta normalización se puede llevar a cabo utilizando un
algoritmo
de transformación que consta de los siguientes pasos:
- se elimina el conectivo
utilizando
la
identidad
lógica (A
B) = (¬A
B)
- Se reduce el alcance de los conectivos ¬ aplicados a
fórmulas
no atómicas traspasándolo al interior de dichas
fórmulas.
Se utilizan las siguientes identidades lógicas:
¬¬A = A
¬(A
B) = (¬A
¬B)
¬(A
B) = (¬A
¬B)
¬
x A(x) =
x ¬A(x)
¬
x A(x) =
x ¬A(x)
- Se estandarizan los nombres de variables colocándole
nombres
distintos
a variables asociadas con cuantificadores distintos.
- Se eliminan los cuantificadores existenciales mediante un proceso
llamado skolemización
que consiste en reemplazar las variables cuantificadas existencialmente
por:
- una constante c si el cuantificador existencial
asociado no se
encuentra
dentro del alcance de ningún cuantificador universal
- una función f(x) si el cuantificador existencial
se
encuentra
dentro del alcance del cuantificador universal correspondiente a x
- una función f(x, y) si el cuantificador
existencial se
encuentra
dentro del alcance de los cuantificadores de x e y, etc.
- Se mueven todos los cuantificadores universales al principio de
la
fórmula.
Esto se puede hacer porque previamente se estandarizaron los nombres de
las variables.
Al cumplirse la etapa 5 del algoritmo, se dice que la
fórmula
resultante está en forma normal prenex porque todos los
cuantificadores
aparecen al principio como un prefijo (prefix) seguido del la
expresión
o matriz (matrix).
El algoritmo de normalización prosigue así:
- Se eliminan todos los cuantificadores universales. Esto
es posible
porque
las únicas variables que quedan están cuantificadas
universalmente
y no hay conflictos de nombre, por lo tanto no es necesario explicitar
los cuantificadores ni su alcance. Por omisión, todas las
variables
están cuantificadas universalmente.
- Se desarrolla la expresión resultante distribuyendo
sobre
para convertirla en una conjunción de
disyunciones. La distribución se apoya en la siguiente identidad
lógica:
- Cada elemento de la conjunción resultante se convierte en
una
cláusula
independiente constituida por una disyunción de fórmulas
atómicas negadas o no negadas.
- El último paso es estandarizar de nuevo las variables
poniéndole
nombres distintos a variables que se encuentren en cláusulas
distintas
Ejemplo de normalización a forma clausal
Se tiene la fórmula:
x ((hombre (x)
mujer
(x))
(
y progenitor
(x, y)
creado (x)))
Los pasos del proceso de normalización son:
- eliminación de
:
x (¬(hombre (x)
mujer (x))
(
y progenitor
(x, y)
creado (x))) - reducción de ¬ :
x ((¬hombre (x)
¬mujer (x))
(
y progenitor
(x, y)
creado (x))) - estandarización de
variables:
no cambia - skolemización:
x ((¬hombre (x)
¬mujer (x))
(progenitor (x, f (x))
creado
(x))) - mover cuantificadores al principio:
no cambia - eliminación de cuantificadores:
((¬hombre (x)
¬mujer (x))
(progenitor (x, f (x))
creado (x))) - distribución
de
sobre
:
((¬hombre (x)
(progenitor (x, f (x))
creado
(x)))
((¬mujer (x)
(progenitor (x, f (x))
creado
(x))) - separación de la conjunción:
clausula 1: ¬hombre (x)
progenitor (x, f (x))
creado
(x)
clausula 2: ¬mujer (x)
progenitor (x, f (x))
creado
(x) - estandarización de variables:
clausula 1: ¬hombre (x)
progenitor (x, f (x))
creado
(x)
clausula 2: ¬mujer (y)
progenitor (y, f (y))
creado
(y)
Ejemplo de resolución por refutación (lógica
proposicional)
| Planteamiento del problema |
| fórmulas lógicas |
cláusulas |
Base de conocimiento ( ):
- perro-fido
- perro-fido
animal-fido
- animal-fido
mortal-fido
|
Base de
conocimiento:
- perro-fido
- ¬perro-fido
animal-fido
- ¬animal-fido
mortal-fido
|
| Por demostrar: mortal-fido |
| Negación de hipótesis: 4. ¬mortal-fido |
| Solución |
| padre 1 |
padre 2 |
resolvente |
| (4) ¬mortal-fido |
(3) ¬animal-fido mortal-fido |
(5) ¬animal-fido |
| (5) ¬animal-fido |
(2) ¬perro-fido animal-fido |
(6) ¬perro-fido |
| (6) ¬perro-fido |
(1) perro-fido |
(contradicción) |
Resolución con unificación
En lógica proposicional, los literales de una cláusula
son
símbolos proposicionales negados o no negados. Para aplicar la
regla
de resolución se requiere que ambas cláusulas padres
involucradas
contengan un literal cuyo símbolo proposicional sea
idéntico,
pero que aparezca negado en una y no negado en la otra.
En lógica de predicados, los literales contienen una
fórmula
atómica (un símbolo de predicado con sus argumentos). La
regla de resolución se puede aplicar cuando ambas
cláusulas
padres contienen un literal, positivo en una y negativo en la otra, que
manifiesta la misma fórmula atómica. Sin embargo, si las
fórmulas atómicas que se intenta asimilar no son
idénticas
pero contienen variables que al ser convenientemente substituidas
producen
el mismo resultado, la regla de resolución también se
puede
aplicar después de hacer esta substitución. El proceso de
encontrar una asignación de variables que cumpla con este
requerimiento
se llama unificación.
Unificación
La unificación es un proceso que consiste en encontrar
una
asignación de variables que haga idénticas a las
fórmulas
que se desea unificar. Su resultado, el unificador, se expresa
como
un conjunto de pares
substitución/variable para cada
una de las variables asignadas (este conjunto recibe también el
nombre de substitución). El valor de substitución
para una variable puede ser cualquier término del lenguaje
lógico
utilizado (exceptuando términos con la misma variable). Por
ejemplo,
se pueden unificar las fórmulas
padre(X, hermano(Y))
padre(juan, Z)
utilizando el unificador { juan / X, hermano(Y)
/
Z }.
También se podría utilizar la substitución { juan
/ X, hermano(pedro) / Z,
pedro / Y },
pero aquí se introduce una asignación suplementaria que
no
es necesaria para unificar. Para evitar introducir substituciones
arbitrarias,
se utiliza el unificador más general, es decir, el que
minimiza
las restricciones impuestas a los valores de las variables (en este
caso
concreto, Y puede tomar cualquier valor).
La unicación es una operación sintáctica.
Por ejemplo, no se puede unificar
padre(X, hermano(Y)) con padre(pedro,
julio) porque no hay ninguna substitución de variables que
haga
sintácticamente iguales a
hermano(Y) y julio.
Composición de substituciones
La aplicación de una substitución s a una
fórmula
F se escribe F s y entrega como resultado una
nueva
fórmula en que las variables asignadas han sido reemplazadas por
sus respectivos valores. Por ejemplo:
F = padre(X, hermano(Y))
s = { juan / X, hermano(Y) / Z
}
F s = padre(pedro, hermano(Y))
Si F = padre(pedro, Z) se obtiene el mismo resultado
(ya
que s es el unificador de ambas fórmulas).
Dos substituciones s1 y s2
se pueden aplicar succesivamente con el mismo efecto que una
substitución
única equivalente
s que corresponde a la composición
de
s1 y s2:
Si los valores de substitución de s2
no hacen referencia a las variables asignadas en
s1,
se puede calcular la composición s de la siguiente
manera:
s1 = { a1
/ X1, a2
/ X2,... , an
/ Xn }
s2 = { b1
/ Y1, b2
/ Y2,... , bm
/ Ym }
Ninguna variable Xj aparece
en algún término bk.
s = { a1s2
/ X1, a2s2
/ X2,... , ans2
/ Xn, b1
/ Y1, b2
/ Y2,... , bm
/ Ym }
Algoritmo de unificación
Existe un algoritmo recursivo que entrega como resultado el unificador
más general de dos fórmulas lógicas, o, si no se
pueden
unificar, una indicación de falla. El algoritmo se escribe de la
siguiente manera:
function unify(t1, t2)
{
if (t1 = t2)
return {} // unificador
vacío
elsif (t1 es una
variable)
if (t1
no aparece en t2) return {t2
/ t1} // t2
substituye a t1
else return FAIL
// no se puede unificar
elsif (t2 es una
variable)
if (t2
no aparece en t1) return {t1
/ t2} // t1
substituye a t2
else return FAIL
elsif
(t1 y t2
son términos de la forma f1L1 y f2L2)
if (f1
= f2) return unify(L1, L2)
// falla si las listas son de largo diferente
else return FAIL
elsif
(t1 y t2
son listas no vacías) {
ct1
cabeza de t1 ; ct2
cabeza de t2
sc
unify(ct1,
ct2) ; if
(sc
= FAIL) return FAIL
rt1
(resto de t1) sc ;
rt2
(resto de t2) sc
sr
unify(rt1,
rt2) ; if
(sr
= FAIL) return FAIL
return composicion de sc
seguido de sr
}
else return FAIL
}
Regla de resolución con unificación
Cuando se opera en lógica de predicados (con variables) la regla
de unificación se expresa de la siguiente manera:
PA
Q1
Q2
...
Qn
¬PB
R1
R2
...
Rm
u = unify(PA, PB)
(Q1
Q2
...
Qn
R1
R2
...
Rm) u
Para aplicar la regla se requiere que los términos identificados
(PA y
PB)
sean unificables. El unificador u se aplica al resolvente.
Si se está utilizando la regla de resolución en una
demostración
por refutación, las asignaciones de variables encontradas al
terminar
el proceso corresponden a un caso particular en que la consulta negada
no se cumple, es decir, a un caso particular en que la consulta
original
se cumple.
Viendo al problema planteado como una ecuación lógica,
lo que se obtiene es una solución particular. Haciendo un uso
adecuado
de mecanismos de backtracking se pueden obtener todas las demás
soluciones del problema. Este es el principio fundamental de la
programación
lógica.
Ejemplo de resolución por refutación (lógica
de predicados)
| Planteamiento del problema |
| fórmulas lógicas |
cláusulas |
Base de conocimiento:
- perro (fido)
-
X (perro (X) animal (X))
-
Y (animal (Y) mortal (Y))
|
Base de conocimiento:
- perro (fido)
- ¬perro (X)
animal (X)
- ¬animal (Y)
mortal (Y)
|
Por demostrar: Z mortal
(Z)
Negación: Z ¬mortal
(Z) |
| Negación de hipótesis: 4. ¬mortal (Z) |
| Solución |
| padre 1 |
padre 2 |
unificador |
resolvente |
| (4) ¬mortal (Z) |
(3) ¬animal (Y) mortal (Y) |
{ Y / Z } |
(5) ¬animal (Y) |
| (5) ¬animal (Y) |
(2) ¬perro (X) animal (X) |
{ X / Y } |
(6) ¬perro (X) |
| (6) ¬perro (X) |
(1) perro (fido) |
{ fido / X } |
(contradicción) |
La consulta se cumple para
Z = Y = X = fido.
Como respuesta sólo interesa desplegar el valor de las
variables
de la consulta. En este caso concreto, la respuesta es Z = fido.
Estrategias de resolución
Para automatizar el proceso de resolución por refutación
se requiere precisar una manera de proceder que garantice llegar a una
contradicción si el conjunto de cláusulas es
inconsistente.
Las estrategias que cumplen con este requisito son llamadas estrategias
completas.
Un ejemplo de estrategia completa es hacer una búsqueda en
amplitud
que explora todas las posibilidades en paralelo. Inicialmente se
aparean
las cláusulas del conjunto original de todas las maneras
posibles
y se genera un conjunto de resolventes que corresponden a los casos en
que se pudo aplicar la regla de resolución. En una segunda
etapa,
estas nuevas cláusulas generadas se aparean entre ellas y con
las
cláusulas originales y se genera otro nuevo conjunto de
resolventes
con los cuales se vuelve a repetir la operación y así
succesivamente
hasta encontrar un resolvente vacío. En cada etapa se aparean
las
nuevas cláusulas generadas en la etapa anterior con todas las
cláusulas
generadas hasta la etapa y las cláusulas originales.
La estrategia de búsqueda en amplitud puede tornarse
rápidamente
inmanejable dada la cantidad siempre creciente de cláusulas
generadas.
Una manera de controlar esta proliferación sin perdida de
generalidad
(manteniendo la propiedad de ser una estrategia completa) se logra
restringiendo
los apareamientos entre cláusulas sólo a aquellos pares
en
que por lo menos una de las cláusulas involucradas sea derivada
de la negación de la consulta original. Esto se basa en la
intuición
de que si existe una contradicción, necesariamente debe provenir
de la consulta negada, ya que una buena teoría del dominio
modelado
no puede ser inconsistente.
En problemas grandes, las estrategias completas suelen ser demasiado
exhaustivas para obtener un resultado en un tiempo prudente y se hace
por
lo tanto necesario introducir heurísticas que permitan orientar
el proceso de resolución. Estas heurísticas sacrifican la
seguridad de encontrar la solución para acelerar el
cálculo
(las estrategias heurísticas generalmente no son completas). Las
heurísticas más utilizadas son:
- Preferir resolver con cláusulas de un solo literal. Es la
única
manera de obtener un resolvente más pequeño que las
cláusulas
padres, camino obligado para llegar a un resolvente vacío.
- Resolver la consulta negada con algún axioma, repetir la
operación
para el resolvente obtenido y así succesivamente hasta llegar a
un resolvente vacío. En cada etapa se resuelve la
cláusula
más recientemente generada con algún axioma original.
Cláusulas de Horn
La normalización a cláusulas y el uso de
demostración
por resolución de contradicción facilitan la
solución
automática de los problemas planteados mediante lógica.
Sin
embargo, existen muchas maneras de llevar a cabo una
demostración,
lo que implica elegir una estrategia cuya efectividad puede depender
del
problema abordado.
Una manera de simplificar este proceso de demostración es
restringiendo
el tipo de cláusulas con la cual se opera, tratando de mantener
una forma intuitivamente interpretable para un programador humano (para
que sea posible plantear directamente los problemas en ese formato) y
que
permita la aplicación de un algoritmo de resolución
más
facil de implementar.
Las cláusulas de Horn satisfacen ambos requerimientos y
están
en la base de la mayoría de los sistemas de programación
lógica, en particular de PROLOG. Una claúsula de Horn
es una cláusula con a lo más un literal positivo, es
decir:
P
¬Q1
¬Q2
...
¬Qn
en que P y Q1, Q2,... Qn
son fórmulas atómicas.
Una propiedad importante de las cláusulas de Horn es que al
resolver
dos cláusulas de Horn se obtiene como resolvente otra
cláusula
de Horn. Esto garantiza que siempre se estará operando con el
mismo
formato durante el proceso de demostración.
Interpretación intuitiva de cláusulas de Horn
Una cláusula de la forma
corresponde a una expresión lógica más
fácil
de leer como
lo que se suele escribir de la siguiente manera
P
Q1,
Q2,
... Qn.
Las cláusulas con este formato son llamadas reglas.
Desde
un punto de vista declarativo, una regla especifica la
definición
de una relación (P) en función de otras relaciones
(Q1,
Q2,
...
Qn). Desde un punto de vista
procedural, P se interpreta com un objetivo por cumplir que se
descompone
en Q1,
Q2,
...
Qn subobjetivos, los cuales
a su vez pueden admitir una descomposición basada en reglas.
Las cláusulas que se reducen a un solo literal positivo son
llamadas
hechos
y se escriben
P
.
Un hecho corresponde a la definición de una relación
elemental.
Las cláusulas que sólo contienen literales negativos
Q1, Q2,
... Qn.
corresponden a objetivos que se deben satisfacer, es decir, a una
consulta
por resolver. Nótese que la cláusula es la
negación
de la consulta original, por lo tanto los cuantificadores universales
implícitos
en la cláusula corresponden a cuantificadores existenciales en
la
consulta original.
Construir una teoría del universo mediante cláusulas
de
Horn equivale a definir relaciones relevantes, lo que puede hacerse
recursivamente
y especificando varias reglas o hechos por cada relación. Por
ejemplo:
ancestro(X,Y)
padre(X,Y).
ancestro(X,Y)
padre(Z,Y),
ancestro(X,Z).
Las consultas pueden verse como ecuaciones lógicas cuyas
variables
son las incógnitas por despejar.
Algoritmo de resolución con cláusulas de Horn
La consulta negada siempre se expresa mediante una cláusula que
sólo contiene literales negativos:
Q1,
Q2,
... Qi, ... Qn.
Las únicas contradicciones que es posible derivar mediante
resolución
deben originarse a partir de esta consulta negada, ya que la
teoría
del universo no puede ser inconsistente. Para aplicar resolución
se debe identificar entre las reglas (o hechos) pertenecientes a la
teoría
del universo, una regla de la forma
Q
R1,
R2,
... Rm.
tal que la cabeza Q de la regla sea unificable con algún
subobjetivo Qi de la
consulta. Si
u es el unificador más general para Q y
Qi,
entonces el resolvente de la regla es la cláusula
(Q1,
Q2,
... Qi-1, R1, R2,
... Rm, Qi+1,
... Qn) u.
que conserva la misma forma que la consulta negada y constituye un
nuevo
objetivo por satisfacer.
El algoritmo de resolución consiste entonces en iterar la
aplicación
de la regla de resolución mientras el objetivo resultante no sea
vacío. Nunca se resuelven las reglas o hechos de la
teoría
del universo entre sí. Siempre participa la cláusula de
consulta
o una cláusula derivada de ella.
La expresión anterior del algoritmo de resolución es
no
determinística. Para implementarlo en una máquina
secuencial
se requiere precisar un orden de evaluación, lo que se puede
lograr
mediante los siguientes criterios:
- Satisfacer siempre primero el primer subobjetivo de la consulta,
luego
el primer subobjetivo de la cláusula resultante, y así
succesivamente.
- Ordenar las reglas y hechos de la teoría del universo y
elegir
siempre
la primera regla unificable para resolver. Cada vez que se utiliza una
regla o un hecho, se reemplazan sus variables por variables frescas
creadas
dinámicamente.
- Cuando ya no se puede seguir resolviendo, volver al último
punto
de decisión y elegir la siguiente regla para resolver. Esto
constituye
un backtracking que permite recorrer todas las soluciones
posibles
(siempre que no haya recursiones infinitas).
Estos son los criterios definidos en el lenguaje de programación
lógica PROLOG. Además, cada vez que se obtiene un
resolvente
vacío, se imprimen los valores asociados a las variables de la
consulta,
componiendo todas las unificaciones que fueron necesarias para llegar a
ese resultado.