Calculating syntactic complexity of a prolog predicate - prolog

Currently working on an exercise where, given some predicate, the syntactic complexity has to be calculated. The syntactic complexity of some predicate is calculated as follows:
If the predicate is atomic or a function, its complexity is 2.
If the predicate is a variable, its complexity is 1.
For example, the syntactic complexity of loyalty(father(bob, Y), X) is worked out as follows:
loyalty = 2 (function)
father = 2 (function)
bob = 2 (atom)
Y = 1 (variable)
X = 1 (variable)
Total = 8
The approach taken was calculating such complexity if the predicate was in the form of a nested list, i.e. loyalty(father(bob, Y), X) = [loyalty, father, bob, Y, X], as follows:
complexity([], 0).
complexity([H|L], C) :- atomic(H), complexity(L, C1), C is C1+2.
complexity([H|L], C) :- var(H), complexity(L, C1), C is C1+1.
The remaining issue is converting the predicate to a flat list, as shown above. The ..= is useful, but its output is not complete, that is:
loyalty(father(bob, Y), X) ..= ["loyalty", "father(bob, Y)", "X"]
Any help would be appreciated.

You must apply =.. recursively as follows:
% term_to_list(+Term, -List)
term_to_list(Term, [Term]) :- var(Term), !.
term_to_list(Term, [Term]) :- atomic(Term), !.
term_to_list(Term, List) :-
compound(Term),
Term =.. Components,
maplist(term_to_list, Components, ListOfLists),
flatten(ListOfLists, List).
Example:
?- term_to_list(loyalty(father(bob, Y), X), L).
L = [loyalty, father, bob, Y, X].
Alternatively, you can define complexity/2 as follows:
% complexity(+Term, -Complexity)
complexity(Term, 1) :- var(Term), !.
complexity(Term, 2) :- atomic(Term), !.
complexity(Term, Complexity) :-
compound(Term),
Term =.. Components,
maplist(complexity, Components, Complexities),
sum_list(Complexities, Complexity).
Example:
?- complexity(loyalty(father(bob, Y), X), L).
L = 8.
Remark SWI-Prolog defines maplist/3 and sum_list/2 as follows:
maplist(Goal, List1, List2) :-
maplist_(List1, List2, Goal).
maplist_([], [], _).
maplist_([Elem1|Tail1], [Elem2|Tail2], Goal) :-
call(Goal, Elem1, Elem2),
maplist_(Tail1, Tail2, Goal).
sum_list(Xs, Sum) :-
sum_list(Xs, 0, Sum).
sum_list([], Sum, Sum).
sum_list([X|Xs], Sum0, Sum) :-
Sum1 is Sum0 + X,
sum_list(Xs, Sum1, Sum).

Related

Write a Prolog predicate next(X,List,List1)

Prolog predicate next(X, List,List1), that returns in List1 the next element(s) from List that follows X, e.g., next(a,[a,b,c,a,d],List1), will return List1=[b,d].
I have tried following:
next(X, [X,Y|List], [Y|List1]) :- % X is the head of the list
next(X, [Y|List], List1).
next(X, [Y|List], List1) :- % X is not the head of the list
X \== Y,
next(X, List, List1).
next(_,[], []).
First, whenever possible, use prolog-dif for expressing term inequality!
Second, the question you asked is vague about corner cases: In particular, it is not clear how next(E,Xs,Ys) should behave if there are multiple neighboring Es in Xs or if Xs ends with E.
That being said, here's my shot at your problem:
next(E,Xs,Ys) :-
list_item_nexts(Xs,E,Ys).
list_item_nexts([],_,[]).
list_item_nexts([E],E,[]).
list_item_nexts([I|Xs],E,Ys) :-
dif(E,I),
list_item_nexts(Xs,E,Ys).
list_item_nexts([E,X|Xs],E,[X|Ys]) :-
list_item_nexts(Xs,E,Ys).
Let's see some queries!
?- next(a,[a,b,c,a,d],List1).
List1 = [b,d] ;
false.
?- next(a,[a,a,b,c,a,d],List1).
List1 = [a,d] ;
false.
?- next(a,[a,a,b,c,a,d,a],List1).
List1 = [a,d] ;
false.
Note that above queries succeed, but leave behind useless choicepoints.
This inefficiency can be dealt with, but I suggest figuring out more complete specs first:)
This version is deterministic for the cases given by #repeat using if_/3 and (=)/3. It shows how purity and efficiency can coexist in one and the same Prolog program.
next(E, Xs, Ys) :-
xs_e_(Xs, E, Ys).
xs_e_([], _E, []).
xs_e_([X|Xs], E, Ys) :-
if_(X = E, xs_e_xys(Xs, E, Ys), xs_e_(Xs, E, Ys)).
xs_e_xys([], _E, []).
xs_e_xys([X|Xs], E, [X|Ys]) :-
xs_e_(Xs, E, Ys).
%xs_e_xys([X|Xs], E, [X|Ys]) :- % alternate interpretation
% xs_e_([X|Xs], E, Ys).

How to calculate all the possible divisions in a list?

I found this predicate for the calculation of all possible sums.
subset_sum(0,[],[]).
subset_sum(N,[_|Xs],L) :-
subset_sum(N,Xs,L).
subset_sum(N,[X|Xs],[X|Rest]) :-
R is N-X,
subset_sum(R,Xs,Rest).
Knowing that the division does not have the commutative property, how do I get the same result for the division?
This predicate only works for the division between the two elements and in order.
subset_div(1,[],[]).
subset_div(N,[_|Xs],L) :-
subset_div(N,Xs,L).
subset_div(N,[X|Xs],[X|Rest]) :-
R is X/N,
subset_div(R,Xs,Rest).
how you can get this result?
?-subset_div(20,[10,100,90,3,5],L).
L=[100,5].
?-subset_div(5,[10,4,59,200,12],L).
L=[200,10,4].
5= (200/10)/4 or 5 = (200/4)/10 but 5 \= (4/200)/10 or 5\= (10/4)/200
Thanks.
You can do it in terms of a product if you only care about left-associative solutions. Solutions when you can do, say [20 / (10 / 2) / 5] are harder, and would require a more complicated output format.
subset_prod(1, [], []).
subset_prod(N, [_|Xs], L) :-
subset_prod(N, Xs, L).
subset_prod(N, [X|Xs], [X|Rest]) :-
R is N/X,
subset_prod(R, Xs, Rest).
subset_div1(N, [X|Xs], [X|L]) :-
X1 is X / N,
integer(X1),
subset_prod(X1, Xs, L).
subset_div1(N, [_|Xs], L) :-
subset_div(N, Xs, L).
subset_div(N, L, M) :-
sort(L, L1),
reverse(L1, L2),
subset_div1(N, L2, M).

how to stochastic search n-queen in prolog?

i'm implement stochastic search in prolog.
code is
queens_rand([],Qs,Qs) :- !.
queens_rand(UnplacedQs,SafeQs,Qs) :-
random_sort(UnplacedQs, UnplacedQs1),
select(UnplacedQs,UnplacedQs1,Q),
not_attack(SafeQs,Q,1),
queens_rand(UnplacedQs1,[Q|SafeQs],Qs),
!.
queen_solve_rand(N) :-
alloc(1,N,Ns),
queens_rand(Ns,[], Q),
write(Q), nl.
random_sort([],_) :- !.
random_sort(_,[]) :- !.
random_sort(Xs, Ys) :-
length(Ys, L),
rnd_select(Xs,L, Ys),
write('Ys : '),write(Ys),nl.
remove_at(X,[X|Xs],1,Xs).
remove_at(X,[Y|Xs],K,[Y|Ys]) :- K > 1,
K1 is K - 1, remove_at(X,Xs,K1,Ys).
rnd_select(_,0,[]).
rnd_select(Xs,N,[X|Zs]) :- N > 0,
length(Xs,L),
I is random(L) + 1,
remove_at(X,Xs,I,Ys),
N1 is N - 1,
rnd_select(Ys,N1,Zs).
not_attack([],_,_) :- !.
not_attack([Y|Ys],X,N) :-
X =\= Y+N, X =\= Y-N,
N1 is N+1,
not_attack(Ys,X,N1).
select([X|Xs],Xs,X).
select([Y|Ys],[Y|Zs],X) :- select(Ys,Zs,X).
but it returns false. i can't understand prolog well, but i have to implement it. and i cant find where is wrong.
Yyou should remove this rule : random_sort(_,[]) :- !.. It means that whatever is the first arg, the result is [].

Prolog. How to check if two math expressions are the same

I'm writing a prolog program that will check if two math expressions are actually the same. For example, if my math expression goal is: (a + b) + c then any of the following expressions are considered the same:
(a+b)+c
a+(b+c)
(b+a)+c
(c+a)+b
a+(c+b)
c+(a+b)
and other combinations
Certainly, I don't expect to check the combination of possible answers because the expression can be more complex than that.
Currently, this is my approach:
For example, if I want to check if a + b *c is the same with another expression such as c*b+a, then I store both expression recursively as binary expressions, and I should create a rule such as ValueOf that will give me the "value" of the first expression and the second expression. Then I just check if the "value" of both expression are the same, then I can say that both expression are the same. Problem is, because the content of the expression is not number, but identifier, I cannot use the prolog "is" keyword to get the value.
Any suggestion?
many thanks
% represent a + b * c
binExprID(binEx1).
hasLeftArg(binEx1, a).
hasRightArg(binEx1, binEx2).
hasOperator(binEx1, +).
binExprID(binEx2).
hasLeftArg(binEx2, b).
hasRightArg(binEx2, c).
hasOperator(binEx2, *).
% represent c * b + a
binExprID(binEx3).
hasLeftArg(binEx3, c).
hasRightArg(binEx3, b).
hasOperator(binEx3, *).
binExprID(binEx4).
hasLeftArg(binEx4, binEx3).
hasRightArg(binEx4, a).
hasOperator(binEx4, +).
goal:- valueOf(binEx1, V),
valueOf(binEx4, V).
Math expressions can be very complex, I presume you are referring to arithmetic instead. The normal form (I hope my wording is appropriate) is 'sum of monomials'.
Anyway, it's not an easy task to solve generally, and there is an ambiguity in your request: 2 expressions can be syntactically different (i.e. their syntax tree differ) but still have the same value. Obviously this is due to operations that leave unchanged the value, like adding/subtracting 0.
From your description, I presume that you are interested in 'evaluated' identity. Then you could normalize both expressions, before comparing for equality.
To evaluate syntactical identity, I would remove all parenthesis, 'distributing' factors over addends. The expression become a list of multiplicative terms. Essentially, we get a list of list, that can be sorted without changing the 'value'.
After the expression has been flattened, all multiplicative constants must be accumulated.
a simplified example:
a+(b+c)*5 will be [[1,a],[b,5],[c,5]] while a+5*(c+b) will be [[1,a],[5,c],[5,b]]
edit after some improvement, here is a very essential normalization procedure:
:- [library(apply)].
arith_equivalence(E1, E2) :-
normalize(E1, N),
normalize(E2, N).
normalize(E, N) :-
distribute(E, D),
sortex(D, N).
distribute(A, [[1, A]]) :- atom(A).
distribute(N, [[1, N]]) :- number(N).
distribute(X * Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
% distribute over factors
findall(Mono, (member(Xm, Xn), member(Ym, Yn), append(Xm, Ym, Mono)), L).
distribute(X + Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
append(Xn, Yn, L).
sortex(L, R) :-
maplist(msort, L, T),
maplist(accum, T, A),
sumeqfac(A, Z),
exclude(zero, Z, S),
msort(S, R).
accum(T2, [Total|Symbols]) :-
include(number, T2, Numbers),
foldl(mul, Numbers, 1, Total),
exclude(number, T2, Symbols).
sumeqfac([[N|F]|Fs], S) :-
select([M|F], Fs, Rs),
X is N+M,
!, sumeqfac([[X|F]|Rs], S).
sumeqfac([F|Fs], [F|Rs]) :-
sumeqfac(Fs, Rs).
sumeqfac([], []).
zero([0|_]).
mul(X, Y, Z) :- Z is X * Y.
Some test:
?- arith_equivalence(a+(b+c), (a+c)+b).
true .
?- arith_equivalence(a+b*c+0*77, c*b+a*1).
true .
?- arith_equivalence(a+a+a, a*3).
true .
I've used some SWI-Prolog builtin, like include/3, exclude/3, foldl/5, and msort/2 to avoid losing duplicates.
These are basic list manipulation builtins, easily implemented if your system doesn't have them.
edit
foldl/4 as defined in SWI-Prolog apply.pl:
:- meta_predicate
foldl(3, +, +, -).
foldl(Goal, List, V0, V) :-
foldl_(List, Goal, V0, V).
foldl_([], _, V, V).
foldl_([H|T], Goal, V0, V) :-
call(Goal, H, V0, V1),
foldl_(T, Goal, V1, V).
handling division
Division introduces some complexity, but this should be expected. After all, it introduces a full class of numbers: rationals.
Here are the modified predicates, but I think that the code will need much more debug. So I allegate also the 'unit test' of what this micro rewrite system can solve. Also note that I didn't introduce the negation by myself. I hope you can work out any required modification.
/* File: arith_equivalence.pl
Author: Carlo,,,
Created: Oct 3 2012
Purpose: answer to http://stackoverflow.com/q/12665359/874024
How to check if two math expressions are the same?
I warned that generalizing could be a though task :) See the edit.
*/
:- module(arith_equivalence,
[arith_equivalence/2,
normalize/2,
distribute/2,
sortex/2
]).
:- [library(apply)].
arith_equivalence(E1, E2) :-
normalize(E1, N),
normalize(E2, N), !.
normalize(E, N) :-
distribute(E, D),
sortex(D, N).
distribute(A, [[1, A]]) :- atom(A).
distribute(N, [[N]]) :- number(N).
distribute(X * Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
% distribute over factors
findall(Mono, (member(Xm, Xn), member(Ym, Yn), append(Xm, Ym, Mono)), L).
distribute(X / Y, L) :-
normalize(X, Xn),
normalize(Y, Yn),
divide(Xn, Yn, L).
distribute(X + Y, L) :-
distribute(X, Xn),
distribute(Y, Yn),
append(Xn, Yn, L).
sortex(L, R) :-
maplist(dsort, L, T),
maplist(accum, T, A),
sumeqfac(A, Z),
exclude(zero, Z, S),
msort(S, R).
dsort(L, S) :- is_list(L) -> msort(L, S) ; L = S.
divide([], _, []).
divide([N|Nr], D, [R|Rs]) :-
( N = [Nn|Ns],
D = [[Dn|Ds]]
-> Q is Nn/Dn, % denominator is monomial
remove_common(Ns, Ds, Ar, Br),
( Br = []
-> R = [Q|Ar]
; R = [Q|Ar]/[1|Br]
)
; R = [N/D] % no simplification available
),
divide(Nr, D, Rs).
remove_common(As, [], As, []) :- !.
remove_common([], Bs, [], Bs).
remove_common([A|As], Bs, Ar, Br) :-
select(A, Bs, Bt),
!, remove_common(As, Bt, Ar, Br).
remove_common([A|As], Bs, [A|Ar], Br) :-
remove_common(As, Bs, Ar, Br).
accum(T, [Total|Symbols]) :-
partition(number, T, Numbers, Symbols),
foldl(mul, Numbers, 1, Total), !.
accum(T, T).
sumeqfac([[N|F]|Fs], S) :-
select([M|F], Fs, Rs),
X is N+M,
!, sumeqfac([[X|F]|Rs], S).
sumeqfac([F|Fs], [F|Rs]) :-
sumeqfac(Fs, Rs).
sumeqfac([], []).
zero([0|_]).
mul(X, Y, Z) :- Z is X * Y.
:- begin_tests(arith_equivalence).
test(1) :-
arith_equivalence(a+(b+c), (a+c)+b).
test(2) :-
arith_equivalence(a+b*c+0*77, c*b+a*1).
test(3) :-
arith_equivalence(a+a+a, a*3).
test(4) :-
arith_equivalence((1+1)/x, 2/x).
test(5) :-
arith_equivalence(1/x+1, (1+x)/x).
test(6) :-
arith_equivalence((x+a)/(x*x), 1/x + a/(x*x)).
:- end_tests(arith_equivalence).
running the unit test:
?- run_tests(arith_equivalence).
% PL-Unit: arith_equivalence ...... done
% All 6 tests passed
true.

Prolog Programming

I have made two programs in Prolog for the nqueens puzzle using hill climbing and beam search algorithms.
Unfortunately I do not have the experience to check whether the programs are correct and I am in dead end.
I would appreciate if someone could help me out on that.
Unfortunately the program in hill climbing is incorrect. :(
The program in beam search is:
queens(N, Qs) :-
range(1, N, Ns),
queens(Ns, [], Qs).
range(N, N, [N]) :- !.
range(M, N, [M|Ns]) :-
M < N,
M1 is M+1,
range(M1, N, Ns).
queens([], Qs, Qs).
queens(UnplacedQs, SafeQs, Qs) :-
select(UnplacedQs, UnplacedQs1,Q),
not_attack(SafeQs, Q),
queens(UnplacedQs1, [Q|SafeQs], Qs).
not_attack(Xs, X) :-
not_attack(Xs, X, 1).
not_attack([], _, _) :- !.
not_attack([Y|Ys], X, N) :-
X =\= Y+N,
X =\= Y-N,
N1 is N+1,
not_attack(Ys, X, N1).
select([X|Xs], Xs, X).
select([Y|Ys], [Y|Zs], X) :- select(Ys, Zs, X).
I would like to mention this problem is a typical constraint satisfaction problem and can be efficiency solved using the CSP module of SWI-Prolog. Here is the full algorithm:
:- use_module(library(clpfd)).
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
N is the number of queens or the size of the board (), and , where , being the position of the queen on the line .
Let's details each part of the algorithm above to understand what happens.
:- use_module(library(clpfd)).
It indicates to SWI-Prolog to load the module containing the predicates for constraint satisfaction problems.
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
The queens predicate is the entry point of the algorithm and checks if the terms are properly formatted (number range, length of the list). It checks if the queens are on different lines as well.
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
It checks if there is a queen on the descendant diagonal of the current queen that is iterated.
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
Same as previous, but it checks if there is a queen on the ascendant diagonal.
Finally, the results can be found by calling the predicate queens/2, such as:
?- findall(X, queens(4, X), L).
L = [[2, 4, 1, 3], [3, 1, 4, 2]]
If I read your code correctly, the algorithm you're trying to implement is a simple depth-first search rather than beam search. That's ok, because it should be (I don't see how beam search will be effective for this problem and it can be hard to program).
I'm not going to debug this code for you, but I will give you a suggestion: build the chess board bottom-up with
queens(0, []).
queens(N, [Q|Qs]) :-
M is N-1,
queens(M, Qs),
between(1, N, Q),
safe(Q, Qs).
where safe(Q,Qs) is true iff none of Qs attack Q. safe/2 is then the conjunction of a simple memberchk/2 check (see SWI-Prolog manual) and your not_attack/2 predicate, which on first sight seems to be correct.
A quick check on Google has found a few candidates for you to compare with your code and find what to change.
My favoured solution for sheer clarity would be the second of the ones linked to above:
% This program finds a solution to the 8 queens problem. That is, the problem of placing 8
% queens on an 8x8 chessboard so that no two queens attack each other. The prototype
% board is passed in as a list with the rows instantiated from 1 to 8, and a corresponding
% variable for each column. The Prolog program instantiates those column variables as it
% finds the solution.
% Programmed by Ron Danielson, from an idea by Ivan Bratko.
% 2/17/00
queens([]). % when place queen in empty list, solution found
queens([ Row/Col | Rest]) :- % otherwise, for each row
queens(Rest), % place a queen in each higher numbered row
member(Col, [1,2,3,4,5,6,7,8]), % pick one of the possible column positions
safe( Row/Col, Rest). % and see if that is a safe position
% if not, fail back and try another column, until
% the columns are all tried, when fail back to
% previous row
safe(Anything, []). % the empty board is always safe
safe(Row/Col, [Row1/Col1 | Rest]) :- % see if attack the queen in next row down
Col =\= Col1, % same column?
Col1 - Col =\= Row1 - Row, % check diagonal
Col1 - Col =\= Row - Row1,
safe(Row/Col, Rest). % no attack on next row, try the rest of board
member(X, [X | Tail]). % member will pick successive column values
member(X, [Head | Tail]) :-
member(X, Tail).
board([1/C1, 2/C2, 3/C3, 4/C4, 5/C5, 6/C6, 7/C7, 8/C8]). % prototype board
The final link, however, solves it in three different ways so you can compare against three known solutions.

Resources