Académique Documents
Professionnel Documents
Culture Documents
Índice
1 Conceptos previos 1
1.1 Susbtituciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Unificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Algoritmo de unificación 4
3 Implementación en Python 8
y se quisiera aplicar modus ponens, tal como están no se podría. Pero, las
variables, dado que están cuantificadas universalmente, se pueden sustituir
por cualquier elemento. Por tanto, sustituyendo y por a, v por g(a) y x por
g(g(a)), podríamos transformarlas en:
1. Conceptos previos
1.1. Susbtituciones
Definición 1 (Ligadura). Una ligadura es un par de elementos: por una parte una variable
vi y por otra un término ti . Se representa por ti /vi . Y se lee: «la variable vi queda ligada
al término ti ». A ti se le denomina término de ligadura, y a vi variable de ligadura.
Algoritmo de unificación 1.1 Susbtituciones
Definición 3 (Particulariazación por sustitución). Sea θ = {t1 /v1 , t2 /v2 , . . . , tn /vn } una
sustitución, y E una expresión, entonces Eθ es la expresión que se obtiene de E reempla-
zando simultáneamente cada ocurrencia de las variables vi en E por los correspondientes
términos ti . Eθ es una particularización por sustitución o instancia de E. Si la sustitución θ
es una sustitución básica, Eθ recibe el nombre de particularización básica de E.
De estas condiciones se deduce que no es lo mismo decir «θ1 es distinta de θ1 » que decir
«θ2 es distinta de θ1 ».
Ejemplo 1.3. Sean θ1 = {f (x, y)/w} y θ2 = {a/x, b/y, c/z}, θ1 es distinta de θ2 pero
en cambio θ2 no es distinta de θ1 .
1. Aplicar θ2 sobre θ1 .
2. Añadir las ligaduras de θ2 al resultado del primer paso.
Ejemplo 1.5. Sea Ω = {f (x), f (y), z}, θ1 = {f (x)/z} y θ2 = {f (a)/z, a/x, a/y}. Las
particularizaciones de Ω a θ1 y a θ2 son respectivamente Ωθ1 = {f (x), f (y)} y Ωθ2 =
{f (a)}.
1.2. Unificación
Definición 8 (Unificador). Sea Ω = {E1 , E2 , . . . , En } y θ una sustitución. Se dice que θ
es un unificador de Ω si y sólo si E1 θ = E2 θ = · · · = En θ, o de otro modo si y sólo si la
cardinalidad del conjunto Ωθ es uno. Si existe la sustitución que unifica un conjunto, se
dice del conjunto que es unificable.
Ejemplo 1.6. Sea Ω = {P (a, y, z), P (x, b, z)}. Tanto θ1 = {a/x, b/y} como θ2 =
{a/x, b/y, c/z} son unificadores para Ω.
Ejemplo 1.7. Considérese el conjunto de expresiones {R(x, f (a, g(y))), R(b, f (z, w))}.
Algunos posibles unificadores para este conjunto son:
2. Algoritmo de unificación
Antes de ver el algoritmo de unificación, hay que tener claro el concepto de conjunto
de desacuerdo.
el primer símbolo en que difieren las expresiones ocupa la posición quinta, ya que los
cuatro primeros símbolos «P (x,» son los mismos para todas ellas. Por tanto el conjunto
de desacuerdo consiste en las subexpresiones que comienzan en la posición quinta. Dicho
conjunto será {f (y, z), a, g(h(k(x)))}.
Algoritmo de unificación
Paso 1: Inicializar: k = 0, Wk = W , σk = .
Paso 2: Si la cardinalidad de Wk es uno, terminar; σk es el unificador más general de W .
En caso contrario, encontrar el conjunto de desacuerdo Dk de Wk .
Paso 3: Si existen los elementos vk y tk en Dk tales que vk es una variable que no ocurre en
tk , ir al paso 4. En caso contrario, parar; W no es unificable.
Paso 4: Hacer sk+1 = sk {tk /vk } y Wk+1 = Wk {tk /vk } (Obsérvese que Wk+1 = W σk+1 ).
Paso 5: Hacer k = k + 1, e ir al paso 2.
W1 = W0 {t0 /v0 }
= {P (a, x, f (g(y))), P (z, f (z), f (u))}{a/z}
= {P (a, x, f (g(y))), P (a, f (a), f (u))}
4 W1 no es de cardinalidad uno, por tanto hay que hallar el conjunto de desacuerdo D1
de W1 : D1 = {x, f (a)}.
5 De D1 se obtiene v1 = x y t1 = f (a).
6 Hacer:
σ2 = σ1 ◦ {t1 /v1 } = {a/z} ◦ {f (a)/x}
= {a/z, f (a)/x}
W2 = W1 {t1 /v1 }
= {P (a, x, f (g(y))), P (z, f (z), f (u))}{f (a)/x}
= {P (a, f (a), f (g(y))), P (a, f (a), f (u))}
7 W2 no es de cardinalidad uno, por tanto hay que hallar el conjunto de desacuerdo D2
de W2 : D2 = {u, g(y)}. De D2 se obtiene v2 = u y t2 = g(y).
8 Hacer:
σ3 = σ2 ◦ {t2 /v2 } = {a/z, f (a)/x} ◦ {g(y)/u}
= {a/z, f (a)/x, g(y)/u}
W3 = W2 {t2 /v2 }
= {P (a, f (a), f (g(y))), P (a, f (a), f (u))}{g(y)/u}
= {P (a, f (a), f (g(y))), P (a, f (a), f (g(y)))}
= {P (a, f (a), f (g(y)))}.
Se obtiene finalmente que para todo k ≥ 0, hay una sustitución λk tal que θ =
σk ◦ λk . Puesto que el algoritmo de unificación debe terminar, y como no termina
en el paso 3, debe terminar en el paso 2. Como además θ = σk ◦ λk para todo k,
el último σk debe ser un unificador más general para W . Con lo que el teorema
queda demostrado.
3
Todo esto es bastante obvio si se tiene en cuenta que normalmente antes de la aplicación del algoritmo
de unificación se lleva a cabo un renombrado de variables, para que en dos expresiones distintas no
aparezca la misma variable.
4
Aquí no se está más que definiendo un nuevo elemento (un conjunto resultado de la diferencia de otros
dos). Si el elemento tn λn /vn no pertenece al conjunto λn , entonces λn+1 = λn . (No hay que perder de
vista que lo que se está intentando hacer es «pasar una ligadura» de la sustitución λ a la sustitución σ).
5
Como vn no ocurre en tn , el efecto de aplicar una sustitución a tn no va a depender de que la variable vn
aparezca o no aparezca ligada en la sustitución.
3. Implementación en Python
La implementación en Python del algoritmo de unificación es relativamente inmediata.
Lo más relevante quizás sea la elección de cómo representar las fórmulas. El que sigue es
el código para una propuesta de implementación (diseñada siguiendo la estructura para
este algoritmo en Common Lisp de [Tan93]) donde las fórmulas se representan como
listas de listas en las que el primer elemento es el símbolo de predicado, representado
como una cadena, y los restantes son sus argumentos, representados como sublistas
para los argumentos que sean funciones, y como cadenas para los argumentos que sean
contantes o variables. Las variables se diferencian de las constantes porque la cadena
que las representa comienza por el carácter «?».
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """Implementación de la unificación en Python."""
4
5 DEBUG = False
6
7 def subst(new, old, lst):
8 "Sustituye las ocurrencias de old por new in lst"
9 return [new if e == old else
10 e if type(e) != list else
11 subst(new, old, e) for e in lst]
12
13 def isVar(x):
14 "Comprueba si x comienza por ?"
15 return type(x) == str and x[0] == '?'
16
17 def disagreement(p, q):
18 "Devuelve las primeras subexpresiones en que difieren P y Q"
19 # Si P y Q son variables distintas, el desacuerdo es [P, Q]
20 if isVar(p):
21 if p == q: return None
22 else: return [p, q]
23 if isVar(q):
24 if p == q: return None
25 else: return [q, p]
26 # Si tanto P como Q listas vacias, no hay desacuerdo
27 if p == [] and q == []: return None
28 if q == [] or p == []: return ['fail', 'fail'] # no son unificables
29 # Si tanto P como Q no son ni NIL ni listas y son
30 # distintos ya he encontrado el conjunto de
31 # desacuerdo, aunque no ser'an unificables porque
32 # ninguno es una variable.
33 if type(p) != list or type(q) != list:
34 if p == q: return None
35 else: return [p, q]
36 d1 = disagreement(p[0], q[0])
37 if d1 != None:
38 return d1
39 return disagreement(p[1:], q[1:])
40
41 def occursp(x, y):
42 "Comprueba si x ocurre en y."
43 if y == []: return False
44 if isVar(y): return x == y
45 if type(y)!=list: return False
46 return occursp(x, y[0]) or occursp(x, y[1:])
47
48 def unify(p, q, bdgs=[[True, True]], t=""):
49 "Implementación del algoritmo de unificación."
50 d = disagreement(p, q)
51 if DEBUG:
52 print(t+"unify( ", end="")
53 print(term2str(p), end=", ")
54 print(term2str(q)+" )")
55 # Si no hay desacuerdo, son unificables
56 if d == None: return bdgs
57 # Si hay desacuerdo, pero el primer elemento no es
58 # una variable, no puede deshacer el desacuerdo,
59 # p y q no son unificables
60 if not isVar(d[0]): return None
61 # Si el primer elemento es una variable, pero ocurre
62 # en el segundo no es posible la unificaci'on
63 if occursp(d[0], d[1]): return None
64 res = unify(subst(d[1], d[0], p),
65 subst(d[1], d[0], q),
66 [d]+subst(d[1], d[0], bdgs), "\t"+t)
67 if DEBUG:
68 print(t+subst2str(res))
69 return res
70
71
72
73 def list2str(lst):
74 """Los términos en los que el símbolo de función es un '.' se
75 consideran que representan una lista de cabeza el primer argumento
76 y de cola el segundo. Esta función los imprime en 'bonito'.
77 Los términos:
78 ['.','a',['.','b','nil']], ['.','1',['2','3']]
79 los imprime como:
80 [a, b] [1, 2 | 3 ]
81 """
82 l = lst
83 res = '['+term2str(l[1])
84 l = l[2]
85 while l[0] == '.':
86 res+=', '+term2str(l[1])
87 l = l[2]
88 if l == 'nil':
89 res+=']'
90 else:
91 res += ' | '+l+']'
92 return res
93
94 def term2str(x):
95 "Transforma un término en una cadena para su impresión."
96 if type(x) != list:
97 return x
98 if len(x) > 1:
99 if x[0] == '.': # list format
100 return list2str(x)
101 else:
102 args = [term2str(a) for a in x[1:]]
103 return x[0]+'('+', '.join(args)+')'
104 else: return x[0]
105
106 def term2strOld(x): # Old version of term2str
107 if type(x) != list: return x
108 if len(x) > 1:
109 args = [term2strOld(a) for a in x[1:]]
110 return x[0]+'('+', '.join(args)+')'
111 else: return x[0]
112
113 def subst2str(x):
114 "Transforma una substitución en una cadena para su impresión."
115 if x is None: return '{}'
116 else: return '{ '+', '.join([lig2str(l) for l in x[-2::-1]])+' }'
117
118 def lig2str(x):
119 "Transforma una ligadura en una cadena para su impresión."
120 return term2str(x[1])+'/'+x[0]
121
122 def testTerm2str(x):
123 "Función para comprobar el funcionamiento de term2str."
124 print("El término", x, "como cadena es", term2str(x))
125
126 def testDisagreement(a, b):
127 "Función para comprobar el funcionamiento de disagreement."
128 print("El desacuerdo de", a, "y", b, "es", disagreement(a, b))
129
130 def testUnify(a, b):
131 "Función para comprobar el funcionamiento de unify."
132 print("El u.m.g. de", term2str(a), "y", term2str(b), "es", end=' ')
133 print(subst2str(unify(a, b)))
134
135
136
137
138 # Comprueba el funcionamiento de las funciones
139 if __name__ == '__main__':
140 if DEBUG:
141 testTerm2str(['P', ['f', '?x', 'a'], ['g', '?x']])
142 testTerm2str(['P', '?y', ['g', 'b']])
143 testTerm2str(['P', '?x', ['g', '?y']])
144 testTerm2str(['P', ['g', '?v'], '?v'])
145 testTerm2str(['P', [', ', '1', ['.', '2', '32']]])
146 if DEBUG:
147 testDisagreement(['f', '?x'], ['f', 'a'])
148 testDisagreement(['P', '?x', 'b'], ['P', '?x', '?y'])
149 testDisagreement(['f', 'a'], ['f', 'a'])
150 testDisagreement(['P', '?x', 'b'], ['P', ['f', 'a'], '?y'])
151 if True:
152 testUnify(['f', '?x'], ['f', 'a', 'b'])
153 testUnify(['f', '?x'], ['f', 'a'])
154 testUnify(['P', '?x', '?y'], ['P', 'a', 'b'])
155 testUnify(['P', '?x', 'b'], ['P', 'a', '?y'])
156 testUnify(['P', ['f', '?x', 'a'], ['g', '?x']],
157 ['P', '?y', ['g', 'b']])
158 testUnify(['P', '?x', ['g', '?y']],
159 ['P', ['g', '?v'], '?v'])
160 print(unify(['P', '?x', ['g', '?y']], ['P', ['g', '?v'], '?v']))
161 testUnify(['L', ['.','?h','?t'],['i','?tl']],
162 ['L',['.','1',['.','2','n']],'?l'])
Referencias
[CR73] Chin-Liang Chang and Char-Tung Lee Richard. Symbolic Logic and Mechanical
Theorem Proving. Computer science classics. Academic Press, 1973.
[LM88] J-L. Lassez and K. Marriott. Unification revisited. In Jack Minker, editor,
Foundations of Deductive Databases and Logic Programming, volume 1, pages
587–625. Morgan Kauffmann, 1 edition, 1988.
[LVDG91] Peter Lucas and Linda Van Der Gaag. Principles of expert systems. Addison-
Wesley, 1991.
[Tan93] Steven L Tanimoto. The Elements of Artificial Intelligence Using Common LISP.
WH Freeman & Co., 1993.