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.
Related
The following is the classic "textbook" vanilla meta-interpreter for prolog.
% simplest meta-interpreter
solve(true) :- !.
solve((A,B)):- !, solve(A), solve(B).
solve(A) :- clause(A,B), solve(B).
The following is simple program which establishes facts two relations which are "positive" and one relation which makes use of negation by failure \+.
% fruit
fruit(apple).
fruit(orange).
fruit(banana).
% colour
yellow(banana).
% Mary likes all fruit
likes(mary, X) :- fruit(X).
% James likes all fruit, as long as it is yellow
likes(james, X) :- fruit(X), yellow(X).
% Sally likes all fruit, except yellow fruit
likes(sally, X) :- fruit(X), \+ (yellow(X)).
The meta-interpeter can handle goals related to the first two relations ?-solve(likes(mary,X)) and ?- solve(likes(james,X)_.
However it fails with a goal related to the third relation ?- solve(likes(sally,X). The swi-prolog reports a stack limit being reached before the program crashes.
Question 1: What is causing the meta-interpreter to fail? Can it be easily adjusted to cope with the \+ negation? Is this related to the sometimes discussed issue of built-ins not being executed by the vanilla meta-interpreter?
Question 2: Where can I read about the need for those cuts in the vanilla meta-interpreter?
Tracing suggests the goal is being grown endlessly:
clause(\+call(call(call(call(yellow(apple))))),_5488)
Exit:clause(\+call(call(call(call(yellow(apple))))),\+call(call(call(call(call(yellow(apple)))))))
Call:solve(\+call(call(call(call(call(yellow(apple)))))))
Call:clause(\+call(call(call(call(call(yellow(apple)))))),_5508)
Exit:clause(\+call(call(call(call(call(yellow(apple)))))),\+call(call(call(call(call(call(yellow(apple))))))))
Call:solve(\+call(call(call(call(call(call(yellow(apple))))))))
Change solve(A) into:
solve(Goal) :-
writeln(Goal),
sleep(1),
clause(Goal, Body),
solve(Body).
... and we see:
?- solve_mi(likes(sally,X)).
likes(sally,_8636)
fruit(_8636)
\+yellow(apple)
\+call(yellow(apple))
\+call(call(yellow(apple)))
\+call(call(call(yellow(apple))))
...
clause/2 determines the body of \+yellow(apple) to be \+call(yellow(apple)), which is not a simplification.
Can use instead:
solve_mi(true) :-
!.
solve_mi((Goal1, Goal2)):-
!,
solve_mi(Goal1),
solve_mi(Goal2).
solve_mi(\+ Goal) :-
!,
\+ solve_mi(Goal).
solve_mi(Goal) :-
clause(Goal, Body),
solve_mi(Body).
Result in swi-prolog:
?- solve_mi(likes(sally,X)).
X = apple ;
X = orange ;
false.
I'm using solve_mi because solve conflicts with e.g. clpBNR, and I'm not using variable names A and B because they convey no meaning.
For understanding the cuts, I'd recommend gtrace, to see the unwanted unification with other goals that would otherwise take place.
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.
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)).
Basically when I try to transform a list into a polynomial or vice-versa, it always shows up with parenthesis (in case of the polynomials). Here is the code, the function not working is the poly2list, the other one are just to define what a monomial/polinomial is.
pvars([x,y,z]).
pvar(X):-pvars(V),member(X,V).
polinomial(X) :- monomial(X).
polinomial(P+M) :- monomial(M), polinomial(P).
monomial(X) :- pvar(X).
monomial(N) :- number(N).
monomial(X) :- power(X),!.
monomial(K*X) :- coefficient(K), power(X),!.
coefficient(N) :- number(N).
power(X) :- pvar(X),!.
power(X^Y) :- pvar(X), integer(Y), Y>1,!.
poly2list(X,[X]) :- monomial(X),!.
poly2list(X+P,[X|Y]) :- monomial(X), poly2list(P,Y).
For example, when i ask:
poly2list(X,[2*x^2,3,y]).
The result is:
X = 2*x^2+(3+y)
And I'm trying to get:
X = 2*x^2+3+y
Thanks in advance :)
I am trying to create a rewrite predicate in SWI-Prolog that checks if an equation can be simplified then replaces the old one with the new one. I have tried to do the following:
Lets say I have the following equation x+0 and I want to replace/rewrite it with x.
I have tried the following:
simplify(X,X) :- primitive(X).
simplify(X,Y) :- evaluable(X), Y is X.
simplify_exp(X,Y) :- rewrite(X,X1), simplify(X1,Y).
simplify_exp(X,X).
primitive(X) :- atom(X).
rewrite(X+0,X).
rewrite(0+X,X).
rewrite(x+1+(y-1),x+y).
rewrite(X*X,X^2).
rewrite(X^0,1).
rewrite(0*X,0).
rewrite(X*N,N*X) :- number(N).
simplify(X) will return x to me, then I need to rewrite which is fine.
However when I have a longer equation lets say (power(a)+b)-(x+0), it won't find simplify(X) hence I cannot rewrite it.
Can I get any recommendation/help please?
The rewrite predicate can be re-written more concisely:
:- initialization(main).
:- set_prolog_flag(double_quotes, chars).
main :- rewrite(x*0,Output),writeln(Output).
rewrite(X+0,X1) :- rewrite(X,X1).
rewrite(0+X,X+0).
rewrite(X,X) :- atom(X);number(X).
rewrite(X+1+(Y-1),X1+Y1) :- rewrite(X,X1),rewrite(Y,Y1).
rewrite(X*X,X1^2) :- rewrite(X,X1).
rewrite(X^0,1).
rewrite(0*X,0).
rewrite(X*0,Output) :- rewrite(0*X,Output).
rewrite(X*N,N*X1) :- number(N),(\+number(X)),rewrite(X,X1).
rewrite(N*X,N*X1) :- rewrite(X*N,N*X1).
To simplify more complicated arithmetic expressions, you can use the Reduce-Algebraic-Expressions library.