]> Repositorios git - scryer-prolog.git/commitdiff
Add ugraphs library
authorAdrián Arroyo Calle <[email protected]>
Fri, 31 Dec 2021 11:56:24 +0000 (12:56 +0100)
committerAdrián Arroyo Calle <[email protected]>
Fri, 31 Dec 2021 11:56:24 +0000 (12:56 +0100)
README.md
src/lib/ugraphs.pl [new file with mode: 0644]

index 09878933388f470e461fed8289044535f637e09d..2f4a812dc66d4e543f36106a87db91ef7ee07b23 100644 (file)
--- a/README.md
+++ b/README.md
@@ -552,6 +552,7 @@ The modules that ship with Scryer&nbsp;Prolog are also called
 * [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
 * [`tls`](src/lib/tls.pl)
   Predicates for negotiating TLS connections explicitly.
+* [`ugraphs`](src/lib/ugraphs.pl) Graph manipulation library
 
 To use predicates provided by the `lists` library, write:
 
diff --git a/src/lib/ugraphs.pl b/src/lib/ugraphs.pl
new file mode 100644 (file)
index 0000000..257d4e0
--- /dev/null
@@ -0,0 +1,634 @@
+/*  Part of SWI-Prolog
+
+    Author:        R.A.O'Keefe, Vitor Santos Costa, Jan Wielemaker
+    E-mail:        [email protected]
+    WWW:           http://www.swi-prolog.org
+    Copyright (c)  1984-2021, VU University Amsterdam
+                              CWI, Amsterdam
+                              SWI-Prolog Solutions .b.v
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    1. Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in
+       the documentation and/or other materials provided with the
+       distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+*/
+
+:- module(ugraphs,
+          [ add_edges/3,                % +Graph, +Edges, -NewGraph
+            add_vertices/3,             % +Graph, +Vertices, -NewGraph
+            complement/2,               % +Graph, -NewGraph
+            compose/3,                  % +LeftGraph, +RightGraph, -NewGraph
+            del_edges/3,                % +Graph, +Edges, -NewGraph
+            del_vertices/3,             % +Graph, +Vertices, -NewGraph
+            edges/2,                    % +Graph, -Edges
+            neighbors/3,                % +Vertex, +Graph, -Vertices
+            neighbours/3,               % +Vertex, +Graph, -Vertices
+            reachable/3,                % +Vertex, +Graph, -Vertices
+            top_sort/2,                 % +Graph, -Sort
+            top_sort/3,                 % +Graph, -Sort0, -Sort
+            transitive_closure/2,       % +Graph, -Closure
+            transpose_ugraph/2,         % +Graph, -NewGraph
+            vertices/2,                 % +Graph, -Vertices
+            vertices_edges_to_ugraph/3, % +Vertices, +Edges, -Graph
+            ugraph_union/3,             % +Graph1, +Graph2, -Graph
+            connect_ugraph/3            % +Graph1, -Start, -Graph
+          ]).
+
+/** <module> Graph manipulation library
+
+The S-representation of a graph is  a list of (vertex-neighbours) pairs,
+where the pairs are in standard order   (as produced by keysort) and the
+neighbours of each vertex are also  in   standard  order (as produced by
+sort). This form is convenient for many calculations.
+
+A   new   UGraph   from    raw    data     can    be    created    using
+vertices_edges_to_ugraph/3.
+
+Adapted to support some of  the   functionality  of  the SICStus ugraphs
+library by Vitor Santos Costa.
+
+Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker.
+
+@author R.A.O'Keefe
+@author Vitor Santos Costa
+@author Jan Wielemaker
+@license BSD-2 or Artistic 2.0
+*/
+
+:- use_module(library(lists)).
+:- use_module(library(pairs)).
+:- use_module(library(ordsets)).
+
+%!  vertices(+Graph, -Vertices)
+%
+%   Unify Vertices with all vertices appearing in Graph. Example:
+%
+%       ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
+%       L = [1, 2, 3, 4, 5]
+
+vertices([], []) :- !.
+vertices([Vertex-_|Graph], [Vertex|Vertices]) :-
+    vertices(Graph, Vertices).
+
+
+%!  vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det.
+%
+%   Create a UGraph from Vertices and edges.   Given  a graph with a
+%   set of Vertices and a set of   Edges,  Graph must unify with the
+%   corresponding S-representation. Note that   the vertices without
+%   edges will appear in Vertices but not  in Edges. Moreover, it is
+%   sufficient for a vertice to appear in Edges.
+%
+%   ==
+%   ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L).
+%   L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]]
+%   ==
+%
+%   In this case all  vertices  are   defined  implicitly.  The next
+%   example shows three unconnected vertices:
+%
+%   ==
+%   ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L).
+%   L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]]
+%   ==
+
+vertices_edges_to_ugraph(Vertices, Edges, Graph) :-
+    sort(Edges, EdgeSet),
+    p_to_s_vertices(EdgeSet, IVertexBag),
+    append(Vertices, IVertexBag, VertexBag),
+    sort(VertexBag, VertexSet),
+    p_to_s_group(VertexSet, EdgeSet, Graph).
+
+
+%!  add_vertices(+Graph, +Vertices, -NewGraph)
+%
+%   Unify NewGraph with a new  graph  obtained   by  adding  the list of
+%   Vertices to Graph. Example:
+%
+%   ```
+%   ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG).
+%   NG = [0-[], 1-[3,5], 2-[], 9-[]]
+%   ```
+
+% replace with real msort/2 when available
+msort_(List, Sorted) :-
+    maplist(item_pair, List, Pairs),
+    keysort(Pairs, SortedPairs),
+    pairs_keys(SortedPairs, Sorted).
+
+item_pair(X, X-X).
+
+add_vertices(Graph, Vertices, NewGraph) :-
+    % msort/2 not available in Scryer Prolog yet: msort(Vertices, V1),
+    msort_(Vertices, V1),
+    add_vertices_to_s_graph(V1, Graph, NewGraph).
+
+add_vertices_to_s_graph(L, [], NL) :-
+    !,
+    add_empty_vertices(L, NL).
+add_vertices_to_s_graph([], L, L) :- !.
+add_vertices_to_s_graph([V1|VL], [V-Edges|G], NGL) :-
+    compare(Res, V1, V),
+    add_vertices_to_s_graph(Res, V1, VL, V, Edges, G, NGL).
+
+add_vertices_to_s_graph(=, _, VL, V, Edges, G, [V-Edges|NGL]) :-
+    add_vertices_to_s_graph(VL, G, NGL).
+add_vertices_to_s_graph(<, V1, VL, V, Edges, G, [V1-[]|NGL]) :-
+    add_vertices_to_s_graph(VL, [V-Edges|G], NGL).
+add_vertices_to_s_graph(>, V1, VL, V, Edges, G, [V-Edges|NGL]) :-
+    add_vertices_to_s_graph([V1|VL], G, NGL).
+
+add_empty_vertices([], []).
+add_empty_vertices([V|G], [V-[]|NG]) :-
+    add_empty_vertices(G, NG).
+
+%!  del_vertices(+Graph, +Vertices, -NewGraph) is det.
+%
+%   Unify NewGraph with a new graph obtained by deleting the list of
+%   Vertices and all the edges that start from  or go to a vertex in
+%   Vertices to the Graph. Example:
+%
+%   ==
+%   ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]],
+%                   [2,1],
+%                   NL).
+%   NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]]
+%   ==
+%
+%   @compat Upto 5.6.48 the argument order was (+Vertices, +Graph,
+%   -NewGraph). Both YAP and SWI-Prolog have changed the argument
+%   order for compatibility with recent SICStus as well as
+%   consistency with del_edges/3.
+
+del_vertices(Graph, Vertices, NewGraph) :-
+    sort(Vertices, V1),             % JW: was msort
+    (   V1 = []
+    ->  Graph = NewGraph
+    ;   del_vertices(Graph, V1, V1, NewGraph)
+    ).
+
+del_vertices(G, [], V1, NG) :-
+    !,
+    del_remaining_edges_for_vertices(G, V1, NG).
+del_vertices([], _, _, []).
+del_vertices([V-Edges|G], [V0|Vs], V1, NG) :-
+    compare(Res, V, V0),
+    split_on_del_vertices(Res, V,Edges, [V0|Vs], NVs, V1, NG, NGr),
+    del_vertices(G, NVs, V1, NGr).
+
+del_remaining_edges_for_vertices([], _, []).
+del_remaining_edges_for_vertices([V0-Edges|G], V1, [V0-NEdges|NG]) :-
+    ord_subtract(Edges, V1, NEdges),
+    del_remaining_edges_for_vertices(G, V1, NG).
+
+split_on_del_vertices(<, V, Edges, Vs, Vs, V1, [V-NEdges|NG], NG) :-
+    ord_subtract(Edges, V1, NEdges).
+split_on_del_vertices(>, V, Edges, [_|Vs], Vs, V1, [V-NEdges|NG], NG) :-
+    ord_subtract(Edges, V1, NEdges).
+split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG).
+
+%!  add_edges(+Graph, +Edges, -NewGraph)
+%
+%   Unify NewGraph with a new graph obtained by adding the list of Edges
+%   to Graph. Example:
+%
+%   ```
+%   ?- add_edges([1-[3,5],2-[4],3-[],4-[5],
+%                 5-[],6-[],7-[],8-[]],
+%                [1-6,2-3,3-2,5-7,3-2,4-5],
+%                NL).
+%   NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5],
+%         5-[7], 6-[], 7-[], 8-[]]
+%   ```
+
+add_edges(Graph, Edges, NewGraph) :-
+    p_to_s_graph(Edges, G1),
+    ugraph_union(Graph, G1, NewGraph).
+
+%!  ugraph_union(+Graph1, +Graph2, -NewGraph)
+%
+%   NewGraph is the union of Graph1 and Graph2. Example:
+%
+%   ```
+%   ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
+%   L = [1-[2], 2-[3,4], 3-[1,2,4]]
+%   ```
+
+ugraph_union(Set1, [], Set1) :- !.
+ugraph_union([], Set2, Set2) :- !.
+ugraph_union([Head1-E1|Tail1], [Head2-E2|Tail2], Union) :-
+    compare(Order, Head1, Head2),
+    ugraph_union(Order, Head1-E1, Tail1, Head2-E2, Tail2, Union).
+
+ugraph_union(=, Head-E1, Tail1, _-E2, Tail2, [Head-Es|Union]) :-
+    ord_union(E1, E2, Es),
+    ugraph_union(Tail1, Tail2, Union).
+ugraph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :-
+    ugraph_union(Tail1, [Head2|Tail2], Union).
+ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :-
+    ugraph_union([Head1|Tail1], Tail2, Union).
+
+%!  del_edges(+Graph, +Edges, -NewGraph)
+%
+%   Unify NewGraph with a new graph  obtained   by  removing the list of
+%   Edges from Graph. Notice that no vertices are deleted. Example:
+%
+%   ```
+%   ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]],
+%                [1-6,2-3,3-2,5-7,3-2,4-5,1-3],
+%                NL).
+%   NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]]
+%   ```
+
+del_edges(Graph, Edges, NewGraph) :-
+    p_to_s_graph(Edges, G1),
+    graph_subtract(Graph, G1, NewGraph).
+
+%!  graph_subtract(+Set1, +Set2, ?Difference)
+%
+%   Is based on ord_subtract
+
+graph_subtract(Set1, [], Set1) :- !.
+graph_subtract([], _, []).
+graph_subtract([Head1-E1|Tail1], [Head2-E2|Tail2], Difference) :-
+    compare(Order, Head1, Head2),
+    graph_subtract(Order, Head1-E1, Tail1, Head2-E2, Tail2, Difference).
+
+graph_subtract(=, H-E1,     Tail1, _-E2,     Tail2, [H-E|Difference]) :-
+    ord_subtract(E1,E2,E),
+    graph_subtract(Tail1, Tail2, Difference).
+graph_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :-
+    graph_subtract(Tail1, [Head2|Tail2], Difference).
+graph_subtract(>, Head1, Tail1, _,     Tail2, Difference) :-
+    graph_subtract([Head1|Tail1], Tail2, Difference).
+
+%!  edges(+Graph, -Edges)
+%
+%   Unify Edges with all edges appearing in Graph. Example:
+%
+%       ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
+%       L = [1-3, 1-5, 2-4, 4-5]
+
+edges(Graph, Edges) :-
+    s_to_p_graph(Graph, Edges).
+
+p_to_s_graph(P_Graph, S_Graph) :-
+    sort(P_Graph, EdgeSet),
+    p_to_s_vertices(EdgeSet, VertexBag),
+    sort(VertexBag, VertexSet),
+    p_to_s_group(VertexSet, EdgeSet, S_Graph).
+
+
+p_to_s_vertices([], []).
+p_to_s_vertices([A-Z|Edges], [A,Z|Vertices]) :-
+    p_to_s_vertices(Edges, Vertices).
+
+
+p_to_s_group([], _, []).
+p_to_s_group([Vertex|Vertices], EdgeSet, [Vertex-Neibs|G]) :-
+    p_to_s_group(EdgeSet, Vertex, Neibs, RestEdges),
+    p_to_s_group(Vertices, RestEdges, G).
+
+
+p_to_s_group([V1-X|Edges], V2, [X|Neibs], RestEdges) :- V1 == V2,
+    !,
+    p_to_s_group(Edges, V2, Neibs, RestEdges).
+p_to_s_group(Edges, _, [], Edges).
+
+
+
+s_to_p_graph([], []) :- !.
+s_to_p_graph([Vertex-Neibs|G], P_Graph) :-
+    s_to_p_graph(Neibs, Vertex, P_Graph, Rest_P_Graph),
+    s_to_p_graph(G, Rest_P_Graph).
+
+
+s_to_p_graph([], _, P_Graph, P_Graph) :- !.
+s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :-
+    s_to_p_graph(Neibs, Vertex, P, Rest_P).
+
+%!  transitive_closure(+Graph, -Closure)
+%
+%   Generate the graph Closure  as  the   transitive  closure  of Graph.
+%   Example:
+%
+%   ```
+%   ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L).
+%   L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]]
+%   ```
+
+transitive_closure(Graph, Closure) :-
+    warshall(Graph, Graph, Closure).
+
+warshall([], Closure, Closure) :- !.
+warshall([V-_|G], E, Closure) :-
+    memberchk(V-Y, E),      %  Y := E(v)
+    warshall(E, V, Y, NewE),
+    warshall(G, NewE, Closure).
+
+
+warshall([X-Neibs|G], V, Y, [X-NewNeibs|NewG]) :-
+    memberchk(V, Neibs),
+    !,
+    ord_union(Neibs, Y, NewNeibs),
+    warshall(G, V, Y, NewG).
+warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :-
+    !,
+    warshall(G, V, Y, NewG).
+warshall([], _, _, []).
+
+%!  transpose_ugraph(Graph, NewGraph) is det.
+%
+%   Unify NewGraph with a new graph obtained from Graph by replacing
+%   all edges of the form V1-V2 by edges of the form V2-V1. The cost
+%   is O(|V|*log(|V|)). Notice that an undirected   graph is its own
+%   transpose. Example:
+%
+%     ==
+%     ?- transpose([1-[3,5],2-[4],3-[],4-[5],
+%                   5-[],6-[],7-[],8-[]], NL).
+%     NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]]
+%     ==
+%
+%   @compat  This  predicate  used  to   be  known  as  transpose/2.
+%   Following  SICStus  4,  we  reserve    transpose/2   for  matrix
+%   transposition    and    renamed    ugraph    transposition    to
+%   transpose_ugraph/2.
+
+transpose_ugraph(Graph, NewGraph) :-
+    edges(Graph, Edges),
+    vertices(Graph, Vertices),
+    flip_edges(Edges, TransposedEdges),
+    vertices_edges_to_ugraph(Vertices, TransposedEdges, NewGraph).
+
+flip_edges([], []).
+flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :-
+    flip_edges(Pairs, Flipped).
+
+%!  compose(+LeftGraph, +RightGraph, -NewGraph)
+%
+%   Compose NewGraph by connecting the  _drains_   of  LeftGraph  to the
+%   _sources_ of RightGraph. Example:
+%
+%       ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
+%       L = [1-[4], 2-[1,2,4], 3-[]]
+
+compose(G1, G2, Composition) :-
+    vertices(G1, V1),
+    vertices(G2, V2),
+    ord_union(V1, V2, V),
+    compose(V, G1, G2, Composition).
+
+compose([], _, _, []) :- !.
+compose([Vertex|Vertices], [Vertex-Neibs|G1], G2,
+        [Vertex-Comp|Composition]) :-
+    !,
+    compose1(Neibs, G2, [], Comp),
+    compose(Vertices, G1, G2, Composition).
+compose([Vertex|Vertices], G1, G2, [Vertex-[]|Composition]) :-
+    compose(Vertices, G1, G2, Composition).
+
+
+compose1([V1|Vs1], [V2-N2|G2], SoFar, Comp) :-
+    compare(Rel, V1, V2),
+    !,
+    compose1(Rel, V1, Vs1, V2, N2, G2, SoFar, Comp).
+compose1(_, _, Comp, Comp).
+
+
+compose1(<, _, Vs1, V2, N2, G2, SoFar, Comp) :-
+    !,
+    compose1(Vs1, [V2-N2|G2], SoFar, Comp).
+compose1(>, V1, Vs1, _, _, G2, SoFar, Comp) :-
+    !,
+    compose1([V1|Vs1], G2, SoFar, Comp).
+compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :-
+    ord_union(N2, SoFar, Next),
+    compose1(Vs1, G2, Next, Comp).
+
+%!  top_sort(+Graph, -Sorted) is semidet.
+%!  top_sort(+Graph, -Sorted, ?Tail) is semidet.
+%
+%   Sorted is a  topological  sorted  list   of  nodes  in  Graph. A
+%   toplogical sort is possible  if  the   graph  is  connected  and
+%   acyclic. In the example we show   how  topological sorting works
+%   for a linear graph:
+%
+%   ==
+%   ?- top_sort([1-[2], 2-[3], 3-[]], L).
+%   L = [1, 2, 3]
+%   ==
+%
+%   The  predicate  top_sort/3  is  a  difference  list  version  of
+%   top_sort/2.
+
+top_sort(Graph, Sorted) :-
+    vertices_and_zeros(Graph, Vertices, Counts0),
+    count_edges(Graph, Vertices, Counts0, Counts1),
+    select_zeros(Counts1, Vertices, Zeros),
+    top_sort(Zeros, Sorted, Graph, Vertices, Counts1).
+
+top_sort(Graph, Sorted0, Sorted) :-
+    vertices_and_zeros(Graph, Vertices, Counts0),
+    count_edges(Graph, Vertices, Counts0, Counts1),
+    select_zeros(Counts1, Vertices, Zeros),
+    top_sort(Zeros, Sorted, Sorted0, Graph, Vertices, Counts1).
+
+
+vertices_and_zeros([], [], []) :- !.
+vertices_and_zeros([Vertex-_|Graph], [Vertex|Vertices], [0|Zeros]) :-
+    vertices_and_zeros(Graph, Vertices, Zeros).
+
+
+count_edges([], _, Counts, Counts) :- !.
+count_edges([_-Neibs|Graph], Vertices, Counts0, Counts2) :-
+    incr_list(Neibs, Vertices, Counts0, Counts1),
+    count_edges(Graph, Vertices, Counts1, Counts2).
+
+
+incr_list([], _, Counts, Counts) :- !.
+incr_list([V1|Neibs], [V2|Vertices], [M|Counts0], [N|Counts1]) :-
+    V1 == V2,
+    !,
+    N is M+1,
+    incr_list(Neibs, Vertices, Counts0, Counts1).
+incr_list(Neibs, [_|Vertices], [N|Counts0], [N|Counts1]) :-
+    incr_list(Neibs, Vertices, Counts0, Counts1).
+
+
+select_zeros([], [], []) :- !.
+select_zeros([0|Counts], [Vertex|Vertices], [Vertex|Zeros]) :-
+    !,
+    select_zeros(Counts, Vertices, Zeros).
+select_zeros([_|Counts], [_|Vertices], Zeros) :-
+    select_zeros(Counts, Vertices, Zeros).
+
+
+
+top_sort([], [], Graph, _, Counts) :-
+    !,
+    vertices_and_zeros(Graph, _, Counts).
+top_sort([Zero|Zeros], [Zero|Sorted], Graph, Vertices, Counts1) :-
+    graph_memberchk(Zero-Neibs, Graph),
+    decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
+    top_sort(NewZeros, Sorted, Graph, Vertices, Counts2).
+
+top_sort([], Sorted0, Sorted0, Graph, _, Counts) :-
+    !,
+    vertices_and_zeros(Graph, _, Counts).
+top_sort([Zero|Zeros], [Zero|Sorted], Sorted0, Graph, Vertices, Counts1) :-
+    graph_memberchk(Zero-Neibs, Graph),
+    decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
+    top_sort(NewZeros, Sorted, Sorted0, Graph, Vertices, Counts2).
+
+graph_memberchk(Element1-Edges, [Element2-Edges2|_]) :-
+    Element1 == Element2,
+    !,
+    Edges = Edges2.
+graph_memberchk(Element, [_|Rest]) :-
+    graph_memberchk(Element, Rest).
+
+
+decr_list([], _, Counts, Counts, Zeros, Zeros) :- !.
+decr_list([V1|Neibs], [V2|Vertices], [1|Counts1], [0|Counts2], Zi, Zo) :-
+    V1 == V2,
+    !,
+    decr_list(Neibs, Vertices, Counts1, Counts2, [V2|Zi], Zo).
+decr_list([V1|Neibs], [V2|Vertices], [N|Counts1], [M|Counts2], Zi, Zo) :-
+    V1 == V2,
+    !,
+    M is N-1,
+    decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
+decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :-
+    decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
+
+
+%!  neighbors(+Vertex, +Graph, -Neigbours) is det.
+%!  neighbours(+Vertex, +Graph, -Neigbours) is det.
+%
+%   Neigbours is a sorted list of  the   neighbours  of Vertex in Graph.
+%   Example:
+%
+%   ```
+%   ?- neighbours(4,[1-[3,5],2-[4],3-[],
+%                    4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
+%   NL = [1,2,7,5]
+%   ```
+
+neighbors(Vertex, Graph, Neig) :-
+    neighbours(Vertex, Graph, Neig).
+
+neighbours(V,[V0-Neig|_],Neig) :-
+    V == V0,
+    !.
+neighbours(V,[_|G],Neig) :-
+    neighbours(V,G,Neig).
+
+
+%!  connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det.
+%
+%   Adds Start as an additional vertex that is connected to all vertices
+%   in UGraphIn. This can be used to   create  an topological sort for a
+%   not connected graph. Start is before any   vertex in UGraphIn in the
+%   standard order of terms.  No vertex in UGraphIn can be a variable.
+%
+%   Can be used to order a not-connected graph as follows:
+%
+%   ```
+%   top_sort_unconnected(Graph, Vertices) :-
+%       (   top_sort(Graph, Vertices)
+%       ->  true
+%       ;   connect_ugraph(Graph, Start, Connected),
+%           top_sort(Connected, Ordered0),
+%           Ordered0 = [Start|Vertices]
+%       ).
+%   ```
+
+connect_ugraph([], 0, []) :- !.
+connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :-
+    vertices(Graph, Vertices),
+    Vertices = [First|_],
+    before(First, Start).
+
+%!  before(+Term, -Before) is det.
+%
+%   Unify Before to a term that comes   before  Term in the standard
+%   order of terms.
+%
+%   @error instantiation_error if Term is unbound.
+
+before(X, _) :-
+    var(X),
+    !,
+    instantiation_error(X).
+before(Number, Start) :-
+    number(Number),
+    !,
+    Start is Number - 1.
+before(_, 0).
+
+
+%!  complement(+UGraphIn, -UGraphOut)
+%
+%   UGraphOut is a ugraph with an  edge   between  all vertices that are
+%   _not_ connected in UGraphIn and  all   edges  from UGraphIn removed.
+%   Example:
+%
+%   ```
+%   ?- complement([1-[3,5],2-[4],3-[],
+%                  4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
+%   NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8],
+%         4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8],
+%         7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]]
+%   ```
+%
+%   @tbd Simple two-step algorithm. You could be smarter, I suppose.
+
+complement(G, NG) :-
+    vertices(G,Vs),
+    complement(G,Vs,NG).
+
+complement([], _, []).
+complement([V-Ns|G], Vs, [V-INs|NG]) :-
+    ord_add_element(Ns,V,Ns1),
+    ord_subtract(Vs,Ns1,INs),
+    complement(G, Vs, NG).
+
+%!  reachable(+Vertex, +UGraph, -Vertices)
+%
+%   True when Vertices is  an  ordered   set  of  vertices  reachable in
+%   UGraph, including Vertex.  Example:
+%
+%       ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V).
+%       V = [1, 3, 5]
+
+reachable(N, G, Rs) :-
+    reachable([N], G, [N], Rs).
+
+reachable([], _, Rs, Rs).
+reachable([N|Ns], G, Rs0, RsF) :-
+    neighbours(N, G, Nei),
+    ord_union(Rs0, Nei, Rs1, D),
+    append(Ns, D, Nsi),
+    reachable(Nsi, G, Rs1, RsF).