Meta-interpreting logical cut in Prolog (Edited to accommodate comments) - prolog

I am implementing a geometric theorem prover in Sicstus Prolog; and in order to get over the problem of backtracking over I/O, I am using a meta-interpreter. This last however does not seem to handle cuts as expected.
My meta-interpreter looks like the following. The first argument to solve/5 is the goal to be evaluated, the second and third arguments allow me to control the search depth, and the fourth and fifth arguments are for handling backtracking over output.
solve(true,_,_, O, O) :-
!.
solve(_,CurrentDepth,DepthLimit, _, _) :-
CurrentDepth > DepthLimit,
!,
fail.
solve(nl,_,_, O, [nl|O]):- !.
solve(write(X),_,_, O, [X|O]):- !.
solve((A,B),CurrentDepth,DepthLimit, O0, O2) :-
!,
solve(A,CurrentDepth,DepthLimit, O0, O1),
solve(B,CurrentDepth,DepthLimit, O1, O2).
solve((A;B),CurrentDepth,DepthLimit, O0, O2) :-
!,
(
(solve(A,CurrentDepth,DepthLimit, O0, O2);
solve(B,CurrentDepth,DepthLimit, O0, O2))
).
solve(A,_,_, O, O) :-
(
predicate_property(A, built_in);
predicate_property(A, imported_from(lists))
),
!,
call(A).
solve(Goal,CurrentDepth,DepthLimit, O0, O1) :-
!,
predicate_property(Goal, interpreted),!,
NewDepth is CurrentDepth+1,!,
clause(Goal,SubGoals),
solve(SubGoals,NewDepth,DepthLimit, O0, O1).
I have followed the instructions in the thread Implementing cut in tracing meta interpreter prolog
and changed the meta-interpreter to the following.
solve(true,_,_, O, O) :-
!.
solve(_,CurrentDepth,DepthLimit, _, _) :-
CurrentDepth > DepthLimit,
!,
fail.
solve(nl,_,_, O, [nl|O]):- !.
solve(write(X),_,_, O, [X|O]):- !.
solve(!, _,_,_,_):- !, ( true ; throw(cut)).
solve((A,B),CurrentDepth,DepthLimit, O0, O2) :-
!,
solve(A,CurrentDepth,DepthLimit, O0, O1),
solve(B,CurrentDepth,DepthLimit, O1, O2).
solve((A;B),CurrentDepth,DepthLimit, O0, O2) :-
!,
(
(solve(A,CurrentDepth,DepthLimit, O0, O2);
solve(B,CurrentDepth,DepthLimit, O0, O2))
).
solve(A,_,_, O, O) :-
(
predicate_property(A, built_in);
predicate_property(A, imported_from(lists))
),
!,
call(A).
solve(Goal,CurrentDepth,DepthLimit, O0, O1) :-
!,
predicate_property(Goal, interpreted),!,
NewDepth is CurrentDepth+1,!,
clause(Goal,SubGoals),
catch(
(
solve(SubGoals,NewDepth,DepthLimit, O0, O1)
),
cut,
fail
).
However now, solve/5 fails for some of the problems fed to the theorem prover. It is worth noting that I do not have any of the predicates call/1, catch/3, or throw/1 in my theorem prover. An example problem is the following.
:- dynamic test_goal/2.
:- dynamic predicate_with_small_depth/2.
:- dynamic predicate_with_large_depth/2.
:- dynamic p_1/2.
:- dynamic p_2/2.
:- dynamic p_3/2.
:- dynamic p_4/2.
:- dynamic p_5/2.
test_goal(X,Y):-
predicate_with_small_depth(X,Y),!,
predicate_with_large_depth(X,Y).
predicate_with_small_depth(X,Y):-
X < Y,
write('Small Depth Outcome 1'), nl.
predicate_with_small_depth(X,Y):-
X > Y,
write('Small Depth Outcome 2'), nl.
predicate_with_large_depth(X,Y):-
p_1(X,Y).
p_1(X,Y):- p_2(X,Y).
p_2(X,Y):- p_3(X,Y).
p_3(X,Y):- p_4(X,Y).
p_4(X,Y):- p_5(X,Y).
p_5(X,Y):-
predicate_with_small_depth(X,Y),!,
write('Large Depth Outcome: '), write(X), write(' '), write(Y), nl.
If the goal solve(test_goal(1,2),0,8,O1,O2) is evaluated, Prolog's answer using the modified meta-interpreter is:
O2 = [nl,2,' ',1,'Large Depth Outcome: '|_A] ?
but the answer should be
O2 = [nl,2,' ',1,'Large Depth Outcome: ',nl,'Small Depth Outcome 1',nl,'Small Depth Outcome 1'|O1] ?
which is the one given by my meta-interpreter prior to adding the cut adaptation.
Does the new meta-interpreter implementation look correct in terms of handling the cut operator?
Many thanks.
EDIT
In trying to identify the problem set and way to solve the problems in the comments below the following was made apparent:
the TGTP contains the kind of problems that my program tackles
Geometric Theorem Proving by Pedro Quaresma, Days in Logic 2012, University of Evora, 6-8 February 2012
(Gelernter's work) best capture my program.
Slies: 27/99
and 28/99
also
The cut is necessary for me as it allows the program to stop working
on alternative solutions of a given goal once one solution is found.
For example, if I have
prove(X):-(p1(X),!);(p2(X),!);p3(X), if p1(X) is satisfiable,
I do not want other solutions via p2/1 and p3/1 to be
found. This saves a lot of computation.

The clause for cut is incorrect. The clause for cut should record the written data the same way as the clause for true. I think you want:
solve(!, _,_,O,O):- !, ( true ; throw(cut)).

Related

How to link constant with variable by assert?

I want to add in the DB a constant and a linked variable:
?- assertz(my(x, A))
So that in the future I can define A and get the only one entry. Sth like that:
?- assertz(my(x, A)), ..., A = 2.
?- my(A, B).
A = x,
B = 2.
Can this be done?
As I noted in the comments your idea of a link like a pointer is not the way to approach solving your problem.
A common solution is to walk the tree and construct a new tree as you walk the tree by replacing the leaf of the tree with a new leaf that contains the value from the input tree along with the associated value, what you are thinking should be linked.
Since you are somewhat new to Prolog I will do this with two examples. The first will just walk a tree and only return true when successfully walked. It can be used to understand how to walk a tree and run with gtrace to single step the code to understand it.
The second example will expand on the tree walk and add the type (link as you think) to the leaf item. The the old leaf for something simple like an atom a, will become a new leaf in the tree like (a,atom).
Also this was quickly written as a demonstration only. I am sure it will have problems if pressed into doing anything more than the single example.
:- module(example,
[
example/1
]).
example(walk) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
walk(Term).
example(infer_type) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
infer_type(Term,Is),
write(Is).
walk([]) :- !.
walk([T]) :- var(T), !.
walk(L) :- is_list(L), !, L = [H|T], walk(H), walk(T).
walk(T) :- compound(T), !, T =.. [_|Args], !, walk(Args).
walk(T) :- integer(T), !.
walk(T) :- var(T), !.
walk(T) :- atomic(T), !.
walk(T) :- T =.. [Arg|Args], !, walk(Arg), walk(Args).
infer_type([],[]) :- !.
infer_type([T],[(T,var)]) :- var(T), !.
infer_type(L,S) :- is_list(L), !, L = [H|T], infer_type(H,I), infer_type(T,Is), S = [I|Is].
infer_type(T,S) :- compound(T), !, T =.. [F|Args], !, infer_type(Args,Is), S =.. [F|Is].
infer_type(T,(T,integer)) :- integer(T), !.
infer_type(T,(T,var)) :- var(T), !.
infer_type(T,(T,atom)) :- atomic(T), !.
infer_type(T,S) :- T =.. [Arg|Args], !, infer_type(Arg,I), infer_type(Args,Is), S =.. [I|Is].
Example run
Note: I know there are warnings; it is a demo not production code.
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.3)
?- working_directory(_,'C:/Users/Groot').
true.
?- [example].
Warning: c:/users/Groot/example.pl:20:
Warning: Singleton variables: [T]
Warning: c:/users/Groot/example.pl:24:
Warning: Singleton variables: [T]
true.
?- example(walk).
true.
?- example(infer_type).
term_size(a((1,integer),(Hello,atom),(Atom,atom),(1,integer)+(2,integer),[(a,atom),(($),atom),(_25642,var)]))
true.
As an exercise I did not identify the string as a string, the change should be easy.

How do I see a detailed order (execution) for a Prolog query?

Let's say I have this Prolog program:
loves(vincent, mia).
loves(marcellus, mia).
jealous(A, B) :- loves(A, C), loves(B, C).
With query jealous(A,B). I'm very new to Prolog and I'd like to know how is it possible to see the exact order the program will be running and taking its ways for this query? I have tried using trace, jealous(A,B). command but it has only given me that:
Isn't there any more detailed solution for that? :/
Have you seen the Prolog Visualizer?
When you get to the page be sure to click on the icons in the upper right to learn more.
Enjoy.
Screenshot after step 10 of 49.
Screenshot for example given after all steps.
The Prolog Visualizer uses a slightly nonstandard way to enter a query by ending the query with a question mark (?), e.g.
jealous(A,B)?
If you do not post a query in the input area on the left you will receive an error, e.g.
The input for the Prolog Visualizer for your example is
loves(vincent, mia).
loves(marcellus, mia).
jealous(A, B) :- loves(A, C), loves(B, C).
jealous(A,B)?
When the Prolog Visualizer completes your example, notice the four results in green on the right
If you are using SWI-Prolog and after you understand syntactic unification, backtracking and write more advanced code you will find this of use:
Overview of the SWI Prolog Graphical Debugger
For other useful Prolog references see: Useful Prolog references
If the Prolog system has callable_property/2 and sys_rule/3, then one can code
a smart "unify" port as follows, showing most general unifiers (mgu's`):
:- op(1200, fx, ?-).
% solve(+Goal, +Assoc, +Integer, -Assoc)
solve(true, L, _, L) :- !.
solve((A, B), L, P, R) :- !, solve(A, L, P, H), solve(B, H, P, R).
solve(H, L, P, R) :- functor(H, F, A), sys_rule(F/A, J, B),
callable_property(J, sys_variable_names(N)),
number_codes(P, U), atom_codes(V, [0'_|U]), shift(N, V, W),
append(L, W, M), H = J, reverse(M, Z), triage(M, Z, I, K),
offset(P), write_term(I, [variable_names(Z)]), nl,
O is P+1, solve(B, K, O, R).
% triage(+Assoc, +Assoc, -Assoc, -Assoc)
triage([V=T|L], M, R, [V=T|S]) :- var(T), once((member(W=U, M), U==T)), W==V, !,
triage(L, M, R, S).
triage([V=T|L], M, [V=T|R], S) :-
triage(L, M, R, S).
triage([], _, [], []).
% shift(+Assoc, +Atom, -Assoc)
shift([V=T|L], N, [W=T|R]) :-
atom_concat(V, N, W),
shift(L, N, R).
shift([], _, []).
% offset(+Integer)
offset(1) :- !.
offset(N) :- write('\t'), M is N-1, offset(M).
% ?- Goal
(?- G) :-
callable_property(G, sys_variable_names(N)),
shift(N, '_0', M),
solve(G, M, 1, _).
Its not necessary to modify mgu's retrospectively, since a solution to a
Prolog query is the sequential composition of mgu's. Here is an example run:
?- ?- jealous(A,B).
[A_0 = X_1, B_0 = Y_1]
[H_1 = mia, X_1 = vincent]
[Y_1 = vincent]
A = vincent,
B = vincent ;
[Y_1 = marcellus]
A = vincent,
B = marcellus ;
Etc..
This is a preview of Jekejeke Prolog 1.5.0 the new
predicate sys_rule/3, its inspired by the new
predicate rule/2 of SWI-Prolog, but keeps the
clause/2 argument of head and body and uses a predicate
indicator.

How add finitely failed branches to a Prolog visualizer?

Assume we want to visualize this Prolog execution. No goals from the fidschi islands, or something else exotic assumed, only good old SLDNF
with the default selection rule:
p(a).
p(b).
?- \+ p(c).
Yes
But we have only a Prolog visualizer that can show derivations
without negation as failure, like here. How can we boost
the Prolog visualizer to also show negation as failure?
The good thing about negation as failure, writing a meta interpreter for negation as failure is much easier, than writing a meta interpreter for cut (!). So basically the vanilla interpreter for SLDNF can be derived from the vanilla interpreter for SLD by inserting one additional rule:
solve(true) :- !.
solve((A,B)) :- !, solve(A), solve(B).
solve((\+ A)) :- !, \+ solve(A). /* new */
solve(H) :- functor(H, F, A), sys_rule(F/A, H, B), solve(B).
We can now go on and extend solve/3 from here in the same vain. But we do something more, we also write out failure branches in the search tree, similar like Prolog visualizer does by strikethrough of a clause. So the amended solve/3 is as follows:
% solve(+Goal, +Assoc, +Integer, -Assoc)
solve(true, L, _, L) :- !.
solve((A, B), L, P, R) :- !, solve(A, L, P, H), solve(B, H, P, R).
solve((\+ A), L, P, L) :- !, \+ solve(A, L, P, _). /* new */
solve(H, L, P, R) :- functor(H, F, A), sys_rule(F/A, J, B),
callable_property(J, sys_variable_names(N)),
number_codes(P, U), atom_codes(V, [0'_|U]), shift(N, V, W),
append(L, W, M),
(H = J -> true; offset(P), write(fail), nl, fail), /* new */
reverse(M, Z), triage(M, Z, I, K),
offset(P), write_term(I, [variable_names(Z)]), nl,
O is P+1, solve(B, K, O, R).
Here is an example run:
?- ?- \+ p(c).
fail
fail
Yes
See also:
AI Algorithms, Data Structures and Idioms
CH6: Three Meta-Interpreters
Georg F. Luger - Addison-Wesley 2009
https://www.cs.unm.edu/~luger/

Execution tree meta interpreting

I have tracing meta-interpreter made from my previous questions here and I would like to make similar meta-interpreter but this time for making execution trees.
I've made something like this below using similar to same code found on the web and techniques from my previous questions.
clause_tree(true,_,true) :- !, true.
clause_tree((G,R),Trail,(TG,TR)) :-
!,
clause_tree(G,Trail,TG),
clause_tree(R,Trail,TR).
clause_tree(G,_,prolog(G)) :-
(predicate_property(G,built_in) ;
predicate_property(G,compiled) ),
call(G).
clause_tree(G,Trail,tree(G,T)) :-
clause(G,Body),
clause_tree(Body,[G|Trail],T).
why(G) :-
call_with_depth_limit(
catch(
clause_tree(G,[],T),
cut,
fail),
30,
_Message),
nl,
draw_tree(T,0).
draw_tree(tree(Root,Branches),Tab) :- !,
tab(Tab),
write(Tab),
write(': '),
write(Root),
nl,
Tab1 is Tab + 1,
draw_tree(Branches,Tab1).
draw_tree((B,Bs),Tab) :- !,
draw_tree(B,Tab),
draw_tree(Bs,Tab).
draw_tree(Node,Tab) :-
tab(Tab),
write(Tab),
write(': '),
write(Node),
nl.
%example program for testing
%?-p(X).
p(X) :- a(X).
p(X) :- b(X),c(X), d(X),e(X).
p(X) :- f(X).
b(Y) :- g(Y), h(Y).
b(1).
b(2).
a(1).
c(1).
c(2).
d(1).
d(2).
e(2).
f(3).
g(2).
g(1).
h(2).
How can I alter this interpreter that it displays branches that fails and that tree is only one with all solutions? Consider that trees are done only for similar programs like example program written in code if that matters.
I am using swi-prolog.
Edit : I am trying to achieve something like this but in textual form.

An Insert algorithm test for a binary tree in Prolog

This is a homework question, i have pretty much the rest of the code done out, and the last part that i have to do is create an insert algorithm.
insert(I,T1,T2) - is true if T2 is the binary tree resulting from I being
inserted into binary tree T1.
so far my code for this part is...
insert(I,T1,T2) :- bTTree(T1(X,L,_), bTTree(T2(X,L,I).
insert(I,T1,T2) :- bTTree(T1(nil,nil,nil),bTTree(T2(I,nil,nil).
insert(I,T1,T2) :- bTTree(T1(X,L,_),bTTree(T2(X,L,I).
I don't know if i'm going in the right direction with this or not.
Any help would be greatly appreciated.
My completed Code (if you need it):
isempty(nil) :- !.
isempty(tree(nil,nil,nil)).
bTTree(tree(_,Left,Right)) :- binaryTree(Left), binaryTree(Right).
%traversals.
%preorder -- N,Left,Right
preorder(tree(N,_,_),N).
preorder(tree(_,Left,_),N) :- preorder(Left,N).
preorder(tree(_,_,Right),N) :- preorder(Right,N).
%inorder -- Left,N,Right.
inorder(tree(_,Left,_), N) :- inorder(Left,N).
inorder(tree(N,_,_), N).
inorder(tree(_,_,Right), N) :- inorder(Right,N).
%postorder -- Left,Right,N
postorder(tree(_,Left,_),N) :- postorder(Left,N).
postorder(tree(_,_,Right),N) :- postorder(Right,N).
postorder(tree(N,_,_),N).
search(t,I) :- bTTree(t(I,_,_)).
search(t,I) :- bTTree(t(_,I,_)).
search(t,I) :- bTTree(t(_,_,I)).
search(t,I) :- bTTree(t(_,N,_)), search(N,I).
search(t,I) :- bTTree(t(_,_,N)), search(N,I).
height(t,H) :- bTTree(t(nil,nil,nil)), H is 0.
height(t,H) :- bTTree(t(N,nil,nil)), H is 1.
height(t,H) :- bTTree(t(_,Left,Right)),
height(Left, H1),
height(Right, H2),
H is max(H1,H2) + 1.
insert(I,t1,t2) :- bTTree(t1(X,L,_)),
bTTree(t2(X,L,I)).
insert(I,t1,t2) :- bTTree(t1(nil,nil,nil)),
bTTree(t2(I,nil,nil)).
insert(I,t1,t2) :- bTTree(t1(X,L,_)),
bTTree(t2(X,L,I)).
bTTree(T1(X,L,_)) is a syntax error (apart in SWI-Prolog, after enabling the extension set_prolog_flag(allow_variable_name_as_functor, true) ).
Another problem is that bTTree/1 misses the base case, it will always fail after it completed the recursion. What's its purpose ? In Prolog usually functors are assumed as formally declared symbols, there is no point in having an 'indirection' like bBBtree/1 just to introduce a term like tree/3.
And of course, you should stick to the same representation for a data structure in a program: now you use tree, or t, or t1, or t2, ...
I think that before working on insertion those problems should be solved...

Resources