Many predicates essentially use some form of transitive closure, only to discover that termination has to be addressed too. Why not solve this once and forever with cl
It's useful, but in my opinion not yet ideal because I cannot cut duplicate paths at the point of their creation.
Consider, with the complete graph K_n:
n_complete(N, Es) :-
numlist(1, N, Ns),
phrase(pairs(Ns), Es).
adjacent(Edges, X, Y) :- member(edge(X, Y), Edges).
pairs([]) --> [].
pairs([N|Ns]) --> edges(Ns, N), pairs(Ns).
edges([], _) --> [].
edges([N|Ns], X) --> [edge(X,N),edge(N,X)], edges(Ns, X).
The following query now has super-exponential runtime, although the closure can actually be found in polynomial time:
?- length(_, N), n_complete(N, Es), portray_clause(N),
time(findall(Y, closure0(adjacent(Es), 1, Y), Ys)),
false.
1.
16 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 1982161 Lips)
2.
54 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 4548901 Lips)
3.
259 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 14499244 Lips)
4.
1,479 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 16219595 Lips)
5.
9,599 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 27691393 Lips)
6.
70,465 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 28911161 Lips)
7.
581,283 inferences, 0.020 CPU in 0.020 seconds (100% CPU, 29397339 Lips)
8.
5,343,059 inferences, 0.181 CPU in 0.181 seconds (100% CPU, 29488001 Lips)
9.
54,252,559 inferences, 1.809 CPU in 1.808 seconds (100% CPU, 29994536 Lips)
10.
603,682,989 inferences, 19.870 CPU in 19.865 seconds (100% CPU, 30381451 Lips)
It would be great if a more efficient way to determine the closure could also be expressed with this meta-predicate.
For example, one would normally simply use Warshall's algorithm to compute the closure in cubic time, with code similar to:
node_edges_closure(Node, Edges, Closure) :-
warshall_fixpoint(Edges, [Node], Closure).
warshall_fixpoint(Edges, Nodes0, Closure) :-
findall(Y, (member(X, Nodes0), adjacent(Edges, X, Y)), Nodes1, Nodes0),
sort(Nodes1, Nodes),
( Nodes == Nodes0 -> Closure = Nodes0
; warshall_fixpoint(Edges, Nodes, Closure)
).
Yielding (with all drawbacks in comparison to the nicely declarative closure0/3
):
?- length(_, N), n_complete(N, Es), portray_clause(N),
time(node_edges_closure(1, Es, Ys)),
false.
1.
% 16 inferences, 0.000 CPU in 0.000 seconds (75% CPU, 533333 Lips)
2.
% 43 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 1228571 Lips)
3.
% 69 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 1769231 Lips)
4.
% 115 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 2346939 Lips)
5.
% 187 inferences, 0.000 CPU in 0.000 seconds (91% CPU, 2968254 Lips)
6.
% 291 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 3548780 Lips)
7.
% 433 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 3866071 Lips)
8.
% 619 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 4268966 Lips)
9.
% 855 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 4500000 Lips)
10.
% 1,147 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 4720165 Lips)
etc.