Definition of Reflexive Transitive Closure

前端 未结 1 410
执笔经年
执笔经年 2020-11-22 04:19

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

1条回答
  •  栀梦
    栀梦 (楼主)
    2020-11-22 04:48

    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.
    

    0 讨论(0)
提交回复
热议问题