need help with PROLOG, if anyone can give direction to any of the questions:
introduction:
I have a water supply network, two water sources (river and lake).
Three repositories.
Three automatic sampling stations.
I converted the raw data to PROLOG information.
Now I need to write down a code to find the following:
1.What is the water source of each point - river or lake (assuming the source is the closest).
The user enters a point number. The answer is the source of the water.
What are the shortest routes from each point to each of the sources.
The user enters a point number. The answer is two lists.
What is the total consumer from the water source to each point.
The user enters a point number. The answer is total consumption.
Assuming a water quality alert was received from a monitoring station, which nodes should be stopped at
Water supply.
j-junction ,r-river, l-lake ,p-pump, t-tank
/* Water junction's database */
flowcalc(source,l,0).
flowcalc(source,r,0).
flowcalc(l,p10,0).
flowcalc(p10,j10,0).
flowcalc(r,p335,1231).
flowcalc(p335,j61,0).
flowcalc(t3,j20,99).
flowcalc(t1,j40,99).
flowcalc(t2,j50,99).
flowcalc(r,j60,1231).
flowcalc(j10,j101,14200).
flowcalc(j101,j103,1350).
flowcalc(j101,j105,2540).
flowcalc(j105,j107,1470).
flowcalc(j103,j109,3940).
flowcalc(j109,j111,2000).
flowcalc(j111,j115,1160).
flowcalc(j111,j113,1680).
flowcalc(j113,j115,2000).
flowcalc(j107,j115,1950).
flowcalc(j113,j193,1660).
flowcalc(j105,j263,2725).
flowcalc(j115,j117,2180).
flowcalc(j120,j119,730).
flowcalc(j117,j120,1870).
flowcalc(j120,j121,2050).
flowcalc(j121,j119,2000).
flowcalc(j121,j123,1500).
flowcalc(j121,j125,930).
flowcalc(j125,j127,3240).
flowcalc(j127,j20,785).
flowcalc(j127,j129,900).
flowcalc(j129,j131,6480).
flowcalc(j129,j139,2750).
flowcalc(j139,j141,2050).
flowcalc(j141,j143,1400).
flowcalc(j15,j143,1650).
flowcalc(j141,j145,3510).
flowcalc(j145,j147,2200).
flowcalc(j147,j149,880).
flowcalc(j149,j151,1020).
flowcalc(j151,j153,1170).
flowcalc(j153,j125,4560).
flowcalc(j151,j119,3460).
flowcalc(j119,j157,2080).
flowcalc(j157,j159,2910).
flowcalc(j159,j161,2000).
flowcalc(j161,163,430).
flowcalc(j163,j164,150).
flowcalc(j164,j166,490).
flowcalc(j265,j169,590).
flowcalc(j169,j167,60).
flowcalc(j187,j204,99.9).
flowcalc(j169,j171,1270).
flowcalc(j171,j173,50).
flowcalc(j171,j271,760).
flowcalc(j181,j35,30).
flowcalc(j181,j177,30).
flowcalc(j177,j179,30).
flowcalc(j179,j183,210).
flowcalc(j179,j40,1190).
flowcalc(j185,j184,99.9).
flowcalc(j185,183,510).
flowcalc(j184,j205,4530).
flowcalc(j185,j204,1325).
flowcalc(j189,j183,1350).
flowcalc(j187,j189,500).
flowcalc(j169,j269,646).
flowcalc(j191,j187,2560).
flowcalc(j267,j189,1230).
flowcalc(j191,j193,520).
flowcalc(j193,j195,360).
flowcalc(j195,j161,2300).
flowcalc(j197,j191,1150).
flowcalc(j111,j197,2790).
flowcalc(j173,j199,4000).
flowcalc(j199,j201,630).
flowcalc(j201,j203,120).
flowcalc(j199,j273,725).
flowcalc(j205,j207,1200).
flowcalc(j207,j206,450).
flowcalc(j207,j275,1430).
flowcalc(j206,j208,510).
flowcalc(j208,j209,885).
flowcalc(j209,j211,1210).
flowcalc(j211,j213,990).
flowcalc(j213,j215,4285).
flowcalc(j215,j217,1660).
flowcalc(j217,j219,2050).
flowcalc(j217,j225,1560).
flowcalc(j213,j229,2200).
flowcalc(j229,j231,1960).
flowcalc(j211,j237,2080).
flowcalc(j237,j229,790).
flowcalc(j237,j239,510).
flowcalc(j239,j241,35 ).
flowcalc(j241,j243,2200).
flowcalc(j241,j247,445).
flowcalc(j239,j249,430).
flowcalc(j247,j249,10).
flowcalc(j247,j255,1390).
flowcalc(j255,j50,925).
flowcalc(j255,j253,1100).
flowcalc(j255,j251,1100).
flowcalc(j251,j249,1450).
flowcalc(j257,j120,645).
flowcalc(j259,j257,350).
flowcalc(j259,j263,1400).
flowcalc(j257,j261,1400).
flowcalc(j161,j117,645).
flowcalc(j261,j263,350).
flowcalc(j267,j265,1580).
flowcalc(j267,j163,1170).
flowcalc(j189,j269,646).
flowcalc(j181,j271,260).
flowcalc(j273,j275,2230).
flowcalc(j205,j273,645).
flowcalc(j265,j163,1200).
flowcalc(j201,j275,300).
flowcalc(j269,j271,1290).
flowcalc(j61,j123,45500).
flowcalc(j60,j601,1).
flowcalc(j601,j61,1).
/* best route Finding (use shortest distance) */
/* Stage one find all possible routes */
go:- write('Enter place to start from:'),read(Start),
write('Enter place to end at:'),read(End),
getroute(Start,[End],route,Dist),
assert(route(route,Dist)),nl,fail.
/* Chose the best one */
go:- findall(Dist,route(_,Dist),Distlist),
min(Distlist,Min),route(route,Min),
write('best rout is:'),nl,
writelist(route),retracall(route(_,_)).
/* find routes sections */
getroute(Start,[Start|rest],[Start|rest],0).
getroute(Start,[junction|rest],route,Dist):-flowcalc(Next,junction,D1),not_member(Next,rest),getroute(Start,[Next,junction|rest],route,D2),Dist is D1 + D2.
getroute(Start,[junction|rest],route,Dist):-flowcalc(junction,Next,D1),not_member(Next,rest),getroute(Start,[Next,junction|rest],route,D2),Dist is D1 + D2.
/* Check if not member */
not_member(_,[]):-!.
not_member(X,[Y|l]):-X\=Y,not_member(X,l).
/* find the minimum length route */
min(last,last).
min([l|lt],Min):-min(lt,SubMin),(l<SubMin,Min=l);Min=SubMin.
/* display the route */
writelist([]).
writelist([l|lt]):-write(l),nl,writelist(lt).
Friends I was able to answer the first 3 questions, but I have a very difficult performance problem, each time it takes the software to search for a route and a lot of time. I would love if you could direct, maybe change search method?
for sample check connection between two water sources
water_shortest_path(l, r, MinCost, Path).
takes hour to response.
path(X, Y, N, Path) :- path(X, Y, N, [], Path).
path(X, Y, N, Seen, [X]) :-
\+ check_member(X, Seen),
edge(X, Y, N).
path(X, Z, N, Seen, [X|T]) :-
\+ check_member(X, Seen),
edge(X, Y, N0),
path(Y, Z, N1, [X|Seen], T),
\+ check_member(X, T),
N is N0 + N1.
check_member(X, L) :- once(member(X, L)).
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
water_shortest_path(X, Y, MinCost, Path) :-
path(X, Y, MinCost, Path),
\+ (path(X, Y, LowerCost, OtherPath),
OtherPath \= Path,
LowerCost =< MinCost).
edge(l,l,0).
edge(r,r,0).
edge(l,p10,0).
edge(p10,j10,0).
edge(r,p335,1231).
edge(p335,j61,0).
edge(t3,j20,99).
edge(t1,j40,99).
edge(t2,j50,99).
edge(r,j60,1231).
edge(j10,j101,14200).
edge(j101,j103,1350).
edge(j101,j105,2540).
edge(j105,j107,1470).
edge(j103,j109,3940).
edge(j109,j111,2000).
edge(j111,j115,1160).
edge(j111,j113,1680).
edge(j113,j115,2000).
edge(j107,j115,1950).
edge(j113,j193,1660).
edge(j105,j263,2725).
edge(j115,j117,2180).
edge(j120,j119,730).
edge(j117,j120,1870).
edge(j120,j121,2050).
edge(j121,j119,2000).
edge(j121,j123,1500).
edge(j121,j125,930).
edge(j125,j127,3240).
edge(j127,j20,785).
edge(j127,j129,900).
edge(j129,j131,6480).
edge(j129,j139,2750).
edge(j139,j141,2050).
edge(j141,j143,1400).
edge(j15,j143,1650).
edge(j141,j145,3510).
edge(j145,j147,2200).
edge(j147,j149,880).
edge(j149,j151,1020).
edge(j151,j153,1170).
edge(j153,j125,4560).
edge(j151,j119,3460).
edge(j119,j157,2080).
edge(j157,j159,2910).
edge(j159,j161,2000).
edge(j161,j163,430).
edge(j163,j164,150).
edge(j164,j166,490).
edge(j265,j169,590).
edge(j169,j167,60).
edge(j187,j204,99.9).
edge(j169,j171,1270).
edge(j171,j173,50).
edge(j171,j271,760).
edge(j181,j35,30).
edge(j181,j177,30).
edge(j177,j179,30).
edge(j179,j183,210).
edge(j179,j40,1190).
edge(j185,j184,99.9).
edge(j185,j183,510).
edge(j184,j205,4530).
edge(j185,j204,1325).
edge(j189,j183,1350).
edge(j187,j189,500).
edge(j169,j269,646).
edge(j191,j187,2560).
edge(j267,j189,1230).
edge(j191,j193,520).
edge(j193,j195,360).
edge(j195,j161,2300).
edge(j197,j191,1150).
edge(j111,j197,2790).
edge(j173,j199,4000).
edge(j199,j201,630).
edge(j201,j203,120).
edge(j199,j273,725).
edge(j205,j207,1200).
edge(j207,j206,450).
edge(j207,j275,1430).
edge(j206,j208,510).
edge(j208,j209,885).
edge(j209,j211,1210).
edge(j211,j213,990).
edge(j213,j215,4285).
edge(j215,j217,1660).
edge(j217,j219,2050).
edge(j217,j225,1560).
edge(j213,j229,2200).
edge(j229,j231,1960).
edge(j211,j237,2080).
edge(j237,j229,790).
edge(j237,j239,510).
edge(j239,j241,35 ).
edge(j241,j243,2200).
edge(j241,j247,445).
edge(j239,j249,430).
edge(j247,j249,10).
edge(j247,j255,1390).
edge(j255,j50,925).
edge(j255,j253,1100).
edge(j255,j251,1100).
edge(j251,j249,1450).
edge(j257,j120,645).
edge(j259,j257,350).
edge(j259,j263,1400).
edge(j257,j261,1400).
edge(j161,j117,645).
edge(j261,j263,350).
edge(j267,j265,1580).
edge(j267,j163,1170).
edge(j189,j269,646).
edge(j181,j271,260).
edge(j273,j275,2230).
edge(j205,j273,645).
edge(j265,j163,1200).
edge(j201,j275,300).
edge(j269,j271,1290).
edge(j61,j123,45500).
edge(j60,j601,1).
edge(j601,j61,1).
edge(p10,l,0).
edge(j10,p10,0).
edge(p335,r,1231).
edge(j61,p335,0).
edge(j20,t3,99).
edge(j40,t1,99).
edge(j50,t2,99).
edge(j60,r,1231).
edge(j101,j10,14200).
edge(j103,j101,1350).
edge(j105,j101,2540).
edge(j107,j105,1470).
edge(j109,j103,3940).
edge(j111,j109,2000).
edge(j115,j111,1160).
edge(j113,j111,1680).
edge(j115,j113,2000).
edge(j115,j107,1950).
edge(j193,j113,1660).
edge(j263,j105,2725).
edge(j117,j115,2180).
edge(j119,j120,730).
edge(j120,j117,1870).
edge(j121,j120,2050).
edge(j119,j121,2000).
edge(j123,j121,1500).
edge(j125,j121,930).
edge(j127,j125,3240).
edge(j20,j127,785).
edge(j129,j127,900).
edge(j131,j129,6480).
edge(j139,j129,2750).
edge(j141,j139,2050).
edge(j143,j141,1400).
edge(j143,j15,1650).
edge(j145,j141,3510).
edge(j147,j145,2200).
edge(j149,j147,880).
edge(j151,j149,1020).
edge(j153,j151,1170).
edge(j125,j153,4560).
edge(j119,j151,3460).
edge(j157,j119,2080).
edge(j159,j157,2910).
edge(j161,j159,2000).
edge(j163,j161,430).
edge(j164,j163,150).
edge(j166,j164,490).
edge(j169,j265,590).
edge(j167,j169,60).
edge(j204,j187,99.9).
edge(j171,j169,1270).
edge(j173,j171,50).
edge(j271,j171,760).
edge(j35,j181,30).
edge(j177,j181,30).
edge(j179,j177,30).
edge(j183,j179,210).
edge(j40,j179,1190).
edge(j184,j185,99.9).
edge(j183,j185,510).
edge(j205,j184,4530).
edge(j204,j185,1325).
edge(j183,j189,1350).
edge(j189,j187,500).
edge(j269,j169,646).
edge(j187,j191,2560).
edge(j189,j267,1230).
edge(j193,j191,520).
edge(j195,j193,360).
edge(j161,j195,2300).
edge(j191,j197,1150).
edge(j197,j111,2790).
edge(j199,j173,4000).
edge(j201,j199,630).
edge(j203,j201,120).
edge(j273,j199,725).
edge(j207,j205,1200).
edge(j206,j207,450).
edge(j275,j207,1430).
edge(j208,j206,510).
edge(j209,j208,885).
edge(j211,j209,1210).
edge(j213,j211,990).
edge(j215,j213,4285).
edge(j217,j215,1660).
edge(j219,j217,2050).
edge(j225,j217,1560).
edge(j229,j213,2200).
edge(j231,j229,1960).
edge(j237,j211,2080).
edge(j229,j237,790).
edge(j239,j237,510).
edge(j241,j239,35 ).
edge(j243,j241,2200).
edge(j247,j241,445).
edge(j249,j239,430).
edge(j249,j247,10).
edge(j255,j247,1390).
edge(j50,j255,925).
edge(j253,j255,1100).
edge(j251,j255,1100).
edge(j249,j251,1450).
edge(j120,j257,645).
edge(j257,j259,350).
edge(j263,j259,1400).
edge(j261,j257,1400).
edge(j117,j161,645).
edge(j263,j261,350).
edge(j265,j267,1580).
edge(j163,j267,1170).
edge(j269,j189,646).
edge(j271,j181,260).
edge(j275,j273,2230).
edge(j273,j205,645).
edge(j163,j265,1200).
edge(j275,j201,300).
edge(j271,j269,1290).
edge(j123,j61,45500).
edge(j601,j60,1).
edge(j61,j601,1).
I am relatively new to Prolog. Currently I am trying to do an AI for a Minichess Game, named Diana chess or Ladies chess (https://en.wikipedia.org/wiki/Minichess). It's basically chess but with a 6x6 Board where you don't have a queen and only one knight.
For my AI, I am mainly looking at the book Prolog programming for artificial intelligence by Ivan Bratko (3rd Edition). There he shows an implementation of the alpha-beta algorithm.I am also looking at the Prolog-Code someone posted on GitHub, where he implemented the same code for his Checkers game. (https://github.com/migafgarcia/prolog-checkers/blob/master/checkers.pl)
The code should think a certain number of turns ahead and choose the best move out of them. Before I started programming in Prolog I did a lot oh Java programming, that's why "Zug" in the first line of my Code is the return of NextMove. I hope this all makes some sense.
My Problem is that when I run my code and, for example, I let the AI run against a human (me), when I start as white and do my first move (b2b3), the AI just doesn't work, the backtracking kinda stops right before NextMove and that way I can't get the best move back. Furthermore when the AI thinks for the minimizing player and needs to choose his move, he always takes the move with the highest value, but, when my understanding from the algorithm is not wrong, shouldn't it take the lowest Value?
I am grateful for every comment on this or for any feedback at all.
Here is the code: https://pastebin.com/sc4gLdQe
alphabeta(Player,MaxiPlayer,Alpha,Beta,Board,NextMove,Eval,Depth) :-
Depth<2,
NewDepth is Depth+1,
findall(
(Player,Figur,VonL,VonR,NachL,NachR,Gegner,Board),
zugbewegung(Player,Figur,VonL,VonR,NachL,NachR,Gegner,Board),
Moves
),
write('All Moves:'),
sysoutMoves(Moves),nl,
bounded_best(Player,MaxiPlayer,Alpha,Beta,Board,Moves,NextMove,Eval,NewDepth),
write('NextMove: '),
write(NextMove),nl,
!.
alphabeta(Player,_,_,_,Board,_,Eval,_) :-
value(Board,Eval,Player),
!.
bounded_best(Player,MaxiPlayer,Alpha,Beta,Board,
[(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_)|Moves],
BestMove,BestEval,Depth) :-
sysoutMove((Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_)),
sysoutMoves(Moves),
ziehen(Board,(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_),NewBoard),
gegner(Player,NextPlayer),
alphabeta(NextPlayer,MaxiPlayer,Alpha,Beta,NewBoard,_,Eval,Depth),
good_enough(Player,MaxiPlayer,Moves,Alpha,Beta,Board,
(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_),
Eval,BestMove,BestEval,Depth).
good_enough(_,_,[],_,_,_,_,_,_,_) :- !.
good_enough(Player,MaxiPlayer,_,_,Beta,_,Move,Eval,Move,Eval,_) :-
minimizing(Player,MaxiPlayer),
Eval > Beta,
!.
good_enough(Player,MaxiPlayer,_,Alpha,_,_,Move,Eval,Move,Eval,_) :-
maximizing(Player,MaxiPlayer),
Eval < Alpha,
!.
good_enough(Player,MaxiPlayer,Moves,Alpha,Beta,Board,Move,Eval,BestMove,BestEval,Depth) :-
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,NewAlpha,NewBeta),
bounded_best(Player,MaxiPlayer,NewAlpha,NewBeta,Board,Moves,Move1,Eval1,Depth),
better_of(Player,MaxiPlayer,Move,Eval,Move1,Eval1,BestMove,BestEval).
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,Eval,Beta) :-
minimizing(Player,MaxiPlayer),
Eval > Alpha,
!.
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,Alpha,Eval) :-
maximizing(Player,MaxiPlayer),
Eval < Beta,
!.
new_bounds(_,_,Alpha,Beta,_,Alpha,Beta).
better_of(Player,MaxiPlayer,Move1,Eval1,_,Eval2,Move1,Eval1) :-
maximizing(Player,MaxiPlayer),
Eval1 >= Eval2,
!.
better_of(Player,MaxiPlayer,_,Eval1,Move2,Eval2,Move2,Eval2) :-
maximizing(Player,MaxiPlayer),
Eval2 >= Eval1,
!.
better_of(Player,MaxiPlayer,Move1,Eval1,_,Eval2,Move1,Eval1) :-
minimizing(Player,MaxiPlayer),
Eval1 =< Eval2,
!.
better_of(Player,MaxiPlayer,_,Eval1,Move2,Eval2,Move2,Eval2) :-
minimizing(Player,MaxiPlayer),
Eval2 =< Eval1,
!.
maximizing(Player,MaxiPlayer) :-
Player == MaxiPlayer.
minimizing(Player,MaxiPlayer) :-
Player \== MaxiPlayer.
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...