List all reachable nodes - prolog

I have a predicate that is bi-directional and tells, whether one node is connected to another.
E.g.
has(a, b).
has(b, c).
has(d, b).
Now I would like to list all nodes which can be reached (from a specific node) with a given number of steps.
connected_nodes(a, T, 2).
should therefore output
T = c
T = d
My current code looks like this:
connected_nodes(A, B, 0) :- write(A).
connected_nodes(A, B, N) :-
N > 0, M is N - 1,
has(A, X),
connected_nodes(X, B, M).
This works for T = c but not for T = d, as this is a bi-directional relation.
Am I thinking correctly in terms of recursion or how in which other way does this problem have to be solved?

Your connected predicate seems fine. It might have made more sense to make the base case rely on has, but it's up to you.
However, you say your has predicate is "bi-directional" but you haven't explicitly created a rule that indicates this.
The following code is a "bi-directional" version of your code:
has(a, b).
has(b, c).
has(d, b).
bi_has(X,Y) :- has(X,Y).
bi_has(X,Y) :- has(Y,X).
connected_nodes(A, B, 0) :- write(A).
connected_nodes(A, B, N) :-
N > 0, M is N - 1,
bi_has(A, X),
connected_nodes(X, B, M).
Note however that your query now returns a as a possible answer too. This makes since, because you never explicitly stated you don't want to return back to the same element after 2 steps.
If you don't want this behaviour, then you need to also keep a list of elements visited so far (i.e. an accumulator), and confirm that you're not revisiting elements.

Related

Finding second min value in list

I have defined the predicate that finds the minimum value in list e.g
greater(X,Y):- X > Y.
isLower(X,Y):- X < Y.
findmin( [X] , X ).
findmin( [H|T] , P ):- findmin(T,P1) , isLower(H,P1), P is H.
findmin( [H|T] , P) :- findmin(T,P1) , greater(H , P1), P is P1.
However i have hard time modifying this code to find second minimum value including nested lists.
How could i assure that the second minimum value will be returned?
I mean this mostly as a joke, but here it is:
find_second_min(L, Min2) :- sort(L, [_, Min2|_]).
So, sorting will definitely put all your items in order. You could get the minimum by just looking at the top one. If you want want the two smallest, you can look at the first two elements:
find_mins(L, Min1, Min2) :- sort(L, [Min1, Min2|_]).
As you know, sorting is O(N log N) while you can find just the minimum in O(N). So to find just one value this is probably too much work. But it's cute.
You could simply make P a list containing the two smallest numbers. Then you would need to check for each element if it is smaller than the larger one in P, and if so, replace it. Then in the end, the larger of the two is the second largest element.
By the way, you really don't need to define your own greater/isLower – why not just use the operators that are built in, >/< in their place?
This would also make it a bit easier to spot the bug you have in your code if H is exactly as large as P1.
So, here's how I would do it:
find2nd([H1, H2 | T], M) :- H1 < H2, !, find2nd_i(T, M, [H1, H2]).
find2nd([H1, H2 | T], M) :- H1 >= H2, find2nd_i(T, M, [H2, H1]).
find2nd_i([], M2, [_, M2]).
find2nd_i([H | T], M, [M1, M2]) :- H >= M2, !, find2nd_i(T, M, [M1, M2]).
find2nd_i([H | T], M, [M1, M2]) :- H < M2, H >= M1, !, find2nd_i(T, M, [M1, H]).
find2nd_i([H | T], M, [M1, M2]) :- H < M2, H < M1, find2nd_i(T, M, [H, M1]).
First of all, your implementation of findmin/2 is not very efficient:
findmin([H|T],P):- findmin(T,P1) , isLower(H,P1), P is H.
Since it does not use tail recursion. You better transform this into a clause with tail recursion and an accumulator.
You can - as is suggested by #DanielLyons sort the list and then retrieve the second element, but this requires O(n log n) time complexity for the sorting step. The sorting is however done in low level C on most Prolog systems; but eventually, if the list is very large, a linear apporach will outperform it.
You can also use #FelixDombek's approach by storing the two values into a list. One can slightly improve the efficiency of this approach by:
not using a list, but simply two parameters since it will save on pattern matching and tuple construction; and
use an if-then-else approach to save on comparisons.
This results in transforming the program into:
find2nd([H1,H2|T],Min2) :-
H1 =< H2 ->
find2nd(T,H1,H2,Min2)
;find2nd(T,H2,H1,Min2).
find2nd([],_,Min2,Min2).
find2nd([H|T],H1,H2,Min2) :-
H >= H2 ->
find2nd(T,H1,H2,Min2)
;( H >= H1 ->
find2nd(T,H1,H,Min2)
;find2nd(T,H,H1,Min2)
).

Easy prolog queries

I am very new to prolog and although I’ve read some books I can definitely tell that my programming brain can’t think the Prolog way. The problem I would like to solve is pretty simple (I believe). I will describe it via an example.
Let’s say that I have a graph that contains 4 “types” of nodes and 3 edges that connect the nodes. The types can be A, B, C or D and as you can see from the image below (see Figure 1), A can be connected with B and C (A_To_B and A_To_C edges respectively), while C can be connected to D (C_To_D edge). There’s also an additional rule not shown on the picture: A can be connected to at most 1 C.
I would like to express these simple rules in Prolog to solve the problem shown in the second picture. There are 3 nodes which type is missing (labeled X?, Y? and Z?). By applying the above rules in my mind I can easily find that X? and Z? are of B type (as A can connect to no more than 1 Cs) and Y? is of type D as C can only connect to D.
Could please provide me any help on that? I am not writing just to pick the solution. I would like to learn Prolog as well so any suggestion on a book that explains Prolog to people who have never worked on such concepts before like me would be very welcome.
EDIT: Example that fails
I came up with the following two examples:
For example 1, the rules are
can_connect(a,b,_).
can_connect(a,c,1).
link(1,2).
type(1,a).
type(2,_).
The possible solutions returned are [b,c] which is correct as we request at most 1 link from A to C meaning that 0 links is also acceptable.
In example 2 the rules change to the following:
can_connect(a,b,_).
can_connect(a,c,**2**).
link(1,2).
link(1,3).
type(1,a).
type(2,_).
type(3,c).
Running the code here returns [c] which is wrong. b is also an acceptable solution as we require again at most 2 A to C links which means that having only 1 is OK.
I spent this weekend trying to figure out the solution. First of all, I believe that it works as intended in Example 1 simply because there's no link from A to C instantiated in the proposed solution (where checking if 2 can be b), so the can_connect(a,c,1) is not checked so the proposed solution is getting accepted. In Example 2, there's one A to C link already there so the can_connect(a,c,2) is checked and the solution where node 2 has type b is rejected as the rule checks if there are exactly 2 and not at most 2 links from A to C.
I find a solution which works at these scenarios but fails at some others. Here it is:
% value #3 is the lower bound and #4 is the upper bound.
can_connect(a,b,0,500).
% A C node can be connected by 0, 1 or 2 A nodes
can_connect(a,c,0,2).
can_connect(d,c,1,1).
can_connect(c,e,0,1).
%The same as previous solution
link(1,2).
link(1,3).
% No change here
type(1,a).
type(2,_).
type(3,c).
% No change here
node_type(N, NT) :-
type(N, NT),
nonvar(NT),
!. % assume a node has only one type
% No change here
node_type(N, NT) :-
assoc_types(Typed),
maplist(check_connections(Typed), Typed),
memberchk(N:NT, Typed).
% No change here
assoc_types(Typed) :-
findall(N, type(N, _), L),
maplist(typed, L, Typed).
% No change here
typed(N, N:T) :-
type(N, T),
member(T, [a,b,c]).
% Changes here
check_connections(Graph, N:NT) :-
forall(link(N, M), (
memberchk(M:MT, Graph),
can_connect(NT, MT, L, U),
findall(X, (link(N, X), memberchk(X:MT, Graph)), Ts),
mybetween(L, U, Ts),
forall(can_connect(NT, Y, LM, UM), (
findall(P, (link(N,P),memberchk(P:Y, Graph)), Ss),
length(Ss, SsSize ),
SsSize>=LM,
SsSize=<UM
))
)).
% It is used to find if the length of a list is between two limits.
mybetween(Lower, Upper, MyList) :-
length(MyList, MySize),
MySize=<Upper,
MySize>=Lower.
This solution fails in this example
In this example, X? must be always b, Y? must always be C and Z? must always be D. It finds X? and Y? correctly but not Z?. I believe after some debugging that this is due the fact that in the current implementation I only check the can_connect rules that are related with links that start from a node and not that end to a node. However, I am not sure at all about that.
Any help is appreciated.
the representation of the problem needs to disambiguate nodes names, so we can express the links appropriately
now we can write
can_connect(a,b,_).
can_connect(a,c,1).
can_connect(c,d,_).
link(1,2).
link(1,3).
link(1,4).
link(4,5).
link(4,6).
link(7,4).
link(7,8).
type(1,a).
type(2,b).
type(3,_).
type(4,c).
type(5,d).
type(6,_).
type(7,a).
type(8,_).
The underscore (anonymous variable) in Prolog plays a role similar to NULL in SQL, it can assume any value.
So, a first snippet
node_type(N, NT) :- type(N, NT), nonvar(NT), !. % assume a node has only one type
can be used to express what we know about the problem.
Facts can_connect/3 then can be read like
a can connect to any number of b
a can connect to just 1 c
etc
Where we don't know the node type, a complex rule is needed, that infers the type of source node from the type of target node, and accounts for the counting constraint, something like
node_type(N, NT) :-
link(M, N),
type(M, MT),
can_connect(MT, NT, C),
aggregate(count, Y^(link(M, Y), type(Y, NT)), C).
?- forall(between(1,8,N), (node_type(N,T),writeln(N:T))).
1:a
2:b
3:b
4:c
5:d
6:d
7:a
8:b
true.
edit if your Prolog doesn't have library(aggregate), from where aggregate/3 has been loaded, you can try
node_type(N, NT) :-
link(M, N),
type(M, MT),
can_connect(MT, NT, C),
findall(t, (link(M, Y), type(Y, NT)), Ts), length(Ts, C).
edit first of all, the updated graph, marked with types where known:
my previous code worked only under very restricted assumptions. Here is something more general, that checks the constraints over the full graph (as was suggested by #false comment), with a 'generate and test' approach.
node_type(N, NT) :-
assoc_types(Typed),
maplist(check_connections(Typed), Typed),
memberchk(N:NT, Typed).
assoc_types(Typed) :-
findall(N, type(N, _), L),
maplist(typed, L, Typed).
typed(N, N:T) :- type(N, T), member(T, [a,b,c,d]).
check_connections(Graph, N:NT) :-
forall(link(N, M), (
memberchk(M:MT, Graph),
can_connect(NT, MT, C),
aggregate(count, X^(link(N, X), memberchk(X:MT, Graph)), C)
)).
now ?- node_type(4,X). fails...

Prolog confusion. Can someone explain how minimal works?

I understand the code up to minimal but after that there's too many variables and I keep losing track of what each one is doing. If someone could explain it or do it with renamed variables that would be an amazing help, as I think this code will probably come up on my Christmas exam and I want to be able to explain what's going on.
road(a, b, 1).
road(b, c, 1).
road(a, c, 13).
road(d, a, 1).
/*Getting from A to B through a list of places R in N kms*/
route(A,B,R,N) :- travel(A,B,[A],Q,N), reverse(Q,R).
/*Travelling from A to B through P a list of towns B|P of distance L*/
travel(A,B,P,[B|P],Dist) :- road(A,B,Dist).
/*Travelling from A to B through Visited, on your Route R with distance Distance*/
/*Find if there is a road from A to b and store the distance. Make sure C is not equal to B*/
travel(A,B,Visited,R,Distance) :-
road(A,C,Dist), C \== B,
/*Make sure C is not in Visited to avoid infinite loop,
use recursion to find the full route and distance */
\+member(C,Visited), travel(C,B,[C|Visited],R,Dist1), Distance is Dist + Dist1.
/*Find the shortest route from A to B*/
shortest(A,B,R,N) :-
setof([Route,Dist],route(A,B,Route,Dist),Set),
Set = [_|_], minimal(Set,[R,N]).
minimal([F|R],M) :- min(R,F,M).
/*The shortest path*/
min([],M,M).
min([[P,L]|R],[_,M],Min):- L < M, !, min(R,[P,L],Min).
min([_|R],M,Min) :- min(R,M,Min).
since setof gives a sorted list of solutions, it's sufficient to produce solutions of appropriate 'shape', placing first the value you want to minimize: try
shortest(A,B,R,N) :-
setof((Dist,Route), route(A,B,Route,Dist), [(N,R)|_]).

update nth element of a list

I am new to prolog , I have a list in prolog like A=[1,2,3,4], and than I accessed nth element using nth(N,[_|T],R). Now I have Nth element in R, than I have done some calculation on R. Now what I want is to update that nth element in list.
Because of I am doing a lot of calculations with each element in list I can't make a new list each time.
I didn't find any method to update list.
With regard to our conversation, you can add two lists together, creating a third, by specifying that the two head elements of the source lists, added together, make the head element of the result list, and that this applies to the remainder of the lists.
There is also a need for a base case, that is, when the two source lists are empty, so should the result list.
addLists([X|A], [Y|B], [Z|C]) :- Z is X+Y, addLists(A, B, C).
addLists([], [], []).
Remember you are always aiming to specify the constraints of the answer, more than the method of answering it. Prolog is very different to other programming languages in that you do not tell it how to do something, you simply tell it conditions that are true for the answer and let it extrapolate it.
From the comments you exchanged with #Orbling seems that what you need is a kind of maplist/4
process_list(A, B, C) :-
maplist(process_elem, A, B, C).
process_elem(A, B, C) :- C is A + B. % or whatever needed
If you are using the index in process_elem then this is not appropriate. Then make a recursive visit of list, passing down the index
process_list(A, B, C) :-
process_list(1, A, B, C).
process_list(I, [A|As], [B|Bs], [C|Cs]) :-
C is A + B * I, % or whatever needed
J is I + 1,
!, process_list(J, As, Bs, Cs).
process_list(_, [], [], []).
edit Just to add to the various ways exposed in answers to the question #Orbling suggests, here a way using nth0/4
?- I = 6, nth0(I,"hello world",_,T), nth0(I,U,0'W,T), format('~s',[U]).
hello World

How to add polynoms in Prolog?

I have the following task:
Write a method that will add two polynoms. I.e 0+2*x^3 and 0+1*x^3+2*x^4 will give 0+3*x^3+2*x^4.
I also wrote the following code:
add_poly(+A1*x^B1+P1,+A2*x^B2+P2,+A3*x^B3+P3):-
(
B1=B2,
B3 = B2,
A3 is A1+A2,
add_poly(P1,P2,P3)
;
B1<B2,
B3=B1,
A3=A1,
add_poly(P1,+A2*x^B2+P2,P3)
;
B1>B2,
B3=B2,
A3=A2,
add_poly(+A1*x^B1+P1,P2,P3)
).
add_poly(X+P1,Y+P2,Z+P3):-
Z is X+Y,
add_poly(P1,P2,P3).
My problem is that I don't know how to stop. I would like to stop when one the arguments is null and than to append the second argument to the third one. But how can I check that they are null?
Thanks.
Several remarks:
Try to avoid disjunctions (;)/2 in the beginning. They need special indentation to be readable. And they make reading a single rule more complex — think of all the extra (=)/2 goals you have to write and keep track of.
Then, I am not sure what you can assume about your polynomials. Can you assume they are written in canonical form?
And for your program: Consider the head of your first rule:
add_poly(+A1*x^B1+P1,+A2*x^B2+P2,+A3*x^B3+P3):-
I will generalize away some of the arguments:
add_poly(+A1*x^B1+P1,_,_):-
and some of the subterms:
add_poly(+_+_,_,_):-
This corresponds to:
add_poly(+(+(_),_),_,_) :-
Not sure you like this.
So this rule applies only to terms starting with a prefix + followed by an infix +. At least your sample data did not contain a prefix +.
Also, please remark that the +-operator is left associative. That means that 1+2+3+4 associates to the left:
?- write_canonical(1+2+3+4).
+(+(+(1,2),3),4)
So if you have a term 0+3*x^3+2*x^4 the first thing you "see" is _+2*x^4. The terms on the left are nested deeper.
For your actual question (how to stop) - you will have to test explicitly that the leftmost subterm is an integer, use integer/1 - or maybe a term (*)/2 (that depends on your assumptions).
I assume that polynomials you are speaking of are in 1 variable and with integer exponents.
Here a procedure working on normal polynomial form: a polynomial can be represented as a list (a sum) of factors, where the (integer) exponent is implicitly represented by the position.
:- [library(clpfd)].
add_poly(P1, P2, Sum) :-
normalize(P1, N1),
normalize(P2, N2),
append(N1, N2, Nt),
aggregate_all(max(L), (member(M, Nt), length(M, L)), LMax),
maplist(rpad(LMax), Nt, Nn),
clpfd:transpose(Nn, Tn),
maplist(sumlist, Tn, NSum),
denormalize(NSum, Sum).
rpad(LMax, List, ListN) :-
length(List, L),
D is LMax - L,
zeros(D, Z),
append(List, Z, ListN).
% the hardest part is of course normalization: here a draft
normalize(Ts + T, [N|Ns]) :-
normalize_fact(T, N),
normalize(Ts, Ns).
normalize(T, [N]) :-
normalize_fact(T, N).
% build a list with 0s left before position E
normalize_fact(T, Normal) :-
fact_exp(T, F, E),
zeros(E, Zeros),
nth0(E, Normal, F, Zeros).
zeros(E, Zeros) :-
length(Zeros, E),
maplist(copy_term(0), Zeros).
fact_exp(F * x ^ E, F, E).
fact_exp(x ^ E, 1, E).
fact_exp(F * x, F, 1).
fact_exp(F, F, 0).
% TBD...
denormalize(NSum, NSum).
test:
?- add_poly(0+2*x^3, 0+1*x^3+2*x^4, P).
P = [0, 0, 0, 3, 2]
the answer is still in normal form, denormalize/2 should be written...

Resources