Explicitar el "éxito determinista" de los objetivos de Prolog


La cuestión del éxito determinista de algún objetivo Prolog ha surgido una y otra vez en-al menos-las siguientes preguntas:

Se utilizaron diferentes métodos (por ejemplo, provocar ciertos errores de recursos, o mirar de cerca las respuestas exactas dadas por el nivel superior del Prolog), pero todos me parecen un poco ad-hack.

Estoy buscando una forma genérica, portátil y conforme a la ISO para averiguar si la ejecución de algún objetivo Prolog (que tuvo éxito) dejó algún punto de elección detrás. ¿Algún meta predicado, tal vez?

¿Podría indicarme en la dirección correcta? Gracias ¡avancen!

 29
Author: Community, 2015-04-23

2 answers

Buenas noticias para todos: setup_call_cleanup/3 (actualmente un proyecto de propuesta para ISO) le permite hacer eso de una manera bastante portátil y hermosa.

Ver el ejemplo:

setup_call_cleanup(true, (X=1;X=2), Det=yes)

Tiene éxito con Det == yes cuando no quedan más puntos de elección.


EDIT : Permítanme ilustrar la genialidad de esta construcción, o más bien del predicado muy estrechamente relacionado call_cleanup/2, con un simple ejemplo:

En el excelente CLP (B) documentación de SICStus Prolog , encontramos en la descripción de labeling/1 una garantía muy fuerte:

Enumera todas las soluciones retrocediendo, pero crea puntos de elección solo si es necesario.

Esto es realmente una garantía fuerte, y al principio puede ser difícil de creer que siempre se mantiene. Afortunadamente para nosotros, es extremadamente fácil formular y generar casos de prueba sistemáticos en Prolog para verificar tales propiedades, en esencia utilizando el sistema Prolog para pruébese a sí mismo.

Comenzamos describiendo sistemáticamente cómo se ve una expresión booleana en CLP (B):

:- use_module(library(clpb)).
:- use_module(library(lists)).

sat(_) --> [].
sat(a) --> [].
sat(~_) --> [].
sat(X+Y) --> [_], sat(X), sat(Y).
sat(X#Y) --> [_], sat(X), sat(Y).

De hecho, hay muchos más casos, pero limitémonos al subconjunto anterior de expresiones CLP(B) por ahora.

¿Por qué estoy usando un DCG para esto? Porque me permite describir convenientemente (un subconjunto de) todas las expresiones booleanas de profundidad específica, y por lo tanto enumerarlas todas. Por ejemplo:

?- length(Ls, _), phrase(sat(Sat), Ls).
Ls = [] ;
Ls = [],
Sat = a ;
Ls = [],
Sat = ~_G475 ;
Ls = [_G475],
Sat = _G478+_G479 .

Así, yo soy usar el DCG solo para indicar cuántos "tokens" disponibles ya se han consumido al generar expresiones, limitando la profundidad total de las expresiones resultantes.

A continuación, necesitamos un pequeño predicado auxiliar labeling_nondet/1, que actúa exactamente como labeling/1, pero solo es cierto si un punto de elección todavía permanece. Aquí es donde call_cleanup/2 entra:

labeling_nondet(Vs) :-
        dif(Det, true),
        call_cleanup(labeling(Vs), Det=true).

Nuestro caso de prueba (y con esto, en realidad nos referimos a una secuencia infinita de casos de prueba pequeños, que podemos muy convenientemente describir con Prolog) ahora tiene como objetivo verificar la propiedad anterior, es decir:

Si hay un punto de elección, entonces hay una solución adicional.

En otras palabras:

El conjunto de soluciones de labeling_nondet/1 es un subconjunto de labeling/1.

Describamos así cómo se ve un contraejemplo de la propiedad anterior:

counterexample(Sat) :-
        length(Ls, _),
        phrase(sat(Sat), Ls),
        term_variables(Sat, Vs),
        sat(Sat),
        setof(Vs, labeling_nondet(Vs), Sols),
        setof(Vs, labeling(Vs), Sols).

Y ahora usamos esta especificación ejecutable para encontrar tal contraejemplo. Si el solucionador funciona como está documentado, entonces nunca encontraremos un contraejemplo. Pero en este caso, inmediatamente obtenemos:

| ?- counterexample(Sat).
Sat = a+ ~_A,
sat(_A=:=_B*a) ? ;

Así que, de hecho, la propiedad no retener. Desglosado a la esencia, aunque no quedan más soluciones en la siguiente consulta, Det no está unificado con true:

| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no

En SWI-Prolog, el punto de elección superfluo es obvio: {[28]]}

?- sat(a + ~X), labeling([X]).
X = 0 ;
false.

Estoy no dando este ejemplo para criticar el comportamiento de cualquiera SICStus Prolog o SWI: A nadie le importa realmente si se deja o no un punto de elección superfluo en labeling/1, menos aún en un ejemplo artificial que involucra variables universalmente cuantificadas (lo cual es atípico para tareas en las que se usa labeling/1).

I am dando este ejemplo para mostrar cuán agradable y convenientemente las garantías que están documentadas y previstas pueden ser probadas con predicados de inspección tan poderosos...

... suponiendo que los implementadores están interesados en estandarice sus esfuerzos, para que estos predicados realmente funcionen de la misma manera en diferentes implementaciones. El lector atento habrá notado que la búsqueda de contraejemplos produce resultados bastante diferentes cuando se usa en SWI-Prolog.

En un giro inesperado de los acontecimientos, el caso de prueba anterior ha encontrado una discrepancia en las implementaciones call_cleanup/2 de SWI-Prolog y SICStus. En SWI-Prolog (7.3.11):

?- dif(Det, true), call_cleanup(true, Det=true).
dif(Det, true).

?- call_cleanup(true, Det=true), dif(Det, true).
false.

Mientras que ambas consultas fallan en SICStus Prolog (4.3.2).

Este es el caso bastante típico: Una vez que está interesado en probar una propiedad específica, encuentra muchos obstáculos que están en el camino de probar la propiedad real.

En el proyecto de propuesta de la ISO , vemos:

El fracaso de [el objetivo de limpieza] es ignorado.

En la documentación SICStus de call_cleanup / 2 , vemos:

La limpieza tiene éxito determinadamente después de realizar algún efecto secundario; de lo contrario, puede resultar un comportamiento inesperado.

Y en la variante SWI , vemos:

El éxito o el fracaso de la limpieza es ignorado

Por lo tanto, para la portabilidad, deberíamos escribir labeling_nondet/1 como:

labeling_nondet(Vs) :-
        call_cleanup(labeling(Vs), Det=true),
        dif(Det, true).
 13
Author: mat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-01 09:45:14

En setup_call_cleanup/3 no hay garantía de que detecte determinismo, es decir, que falte puntos de elección en el éxito de un objetivo. La descripción de 7.8.11.1 el proyecto de propuesta solo dice:

C) El controlador de limpieza se llama exactamente una vez; a más tardar
sobre el fracaso de G. Los momentos anteriores son:
Si G es verdadero o falso, se llama a C en una implementación
momento dependiente después de la última solución y después de la última
efecto observable de G.

Así que actualmente no hay ningún requisito que:

setup_call_cleanup(true, true, Det=true)

Devuelve Det=true en primer lugar. Esto también se refleja en los casos de prueba 7.8.11.4 Ejemplos que el draf propuesta da, encontramos un caso de prueba que dice:

setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.

Así que es una implementación válida, detectar determinismo y no detectar determinismo.

 1
Author: j4n bur53,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-26 10:49:55