853 lines
23 KiB
Prolog
853 lines
23 KiB
Prolog
% File : GRAPHS.PL
|
|
% Author : R.A.O'Keefe
|
|
% Updated: 20 March 1984
|
|
% Purpose: Graph-processing utilities.
|
|
|
|
%
|
|
% adapted to support some of the functionality of the SICStus ugraphs library
|
|
% by Vitor Santos Costa.
|
|
%
|
|
|
|
|
|
/** @defgroup UGraphs Unweighted Graphs
|
|
@ingroup library
|
|
@{
|
|
|
|
The following graph manipulation routines are based in code originally
|
|
written by Richard O'Keefe. The code was then extended to be compatible
|
|
with the SICStus Prolog ugraphs library. The routines assume directed
|
|
graphs, undirected graphs may be implemented by using two edges. Graphs
|
|
are represented in one of two ways:
|
|
|
|
+ The P-representation of a graph is a list of (from-to) vertex
|
|
pairs, where the pairs can be in any old order. This form is
|
|
convenient for input/output.
|
|
|
|
The S-representation of a graph is a list of (vertex-neighbors)
|
|
pairs, where the pairs are in standard order (as produced by keysort)
|
|
and the neighbors of each vertex are also in standard order (as
|
|
produced by sort). This form is convenient for many calculations.
|
|
|
|
|
|
These built-ins are available once included with the
|
|
`use_module(library(ugraphs))` command.
|
|
|
|
*/
|
|
|
|
/* The P-representation of a graph is a list of (from-to) vertex
|
|
pairs, where the pairs can be in any old order. This form is
|
|
convenient for input/output.
|
|
|
|
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.
|
|
|
|
p_to_s_graph(Pform, Sform) converts a P- to an S- representation.
|
|
s_to_p_graph(Sform, Pform) converts an S- to a P- representation.
|
|
|
|
warshall(Graph, Closure) takes the transitive closure of a graph
|
|
in S-form. (NB: this is not the reflexive transitive closure).
|
|
|
|
s_to_p_trans(Sform, Pform) converts Sform to Pform, transposed.
|
|
|
|
p_transpose transposes a graph in P-form, cost O(|E|).
|
|
s_transpose transposes a graph in S-form, cost O(|V|^2).
|
|
*/
|
|
|
|
/** @pred vertices_edges_to_ugraph(+ _Vertices_, + _Edges_, - _Graph_)
|
|
|
|
|
|
Given a graph with a set of vertices _Vertices_ and a set of edges
|
|
_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
|
|
vertex to appear in _Edges_.
|
|
|
|
~~~~~{.prolog}
|
|
?- 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 edges are defined implicitly. The next example shows
|
|
three unconnected edges:
|
|
|
|
~~~~~{.prolog}
|
|
?- 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-[]] ?
|
|
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred add_edges(+ _Graph_, + _Edges_, - _NewGraph_)
|
|
|
|
|
|
Unify _NewGraph_ with a new graph obtained by adding the list of
|
|
edges _Edges_ to the graph _Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- 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-[]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred add_vertices(+ _Graph_, + _Vertices_, - _NewGraph_)
|
|
|
|
|
|
Unify _NewGraph_ with a new graph obtained by adding the list of
|
|
vertices _Vertices_ to the graph _Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- add_vertices([1-[3,5],2-[4],3-[],4-[5],
|
|
5-[],6-[],7-[],8-[]],
|
|
[0,2,9,10,11],
|
|
NG).
|
|
|
|
NG = [0-[],1-[3,5],2-[4],3-[],4-[5],5-[],
|
|
6-[],7-[],8-[],9-[],10-[],11-[]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred complement(+ _Graph_, - _NewGraph_)
|
|
|
|
|
|
Unify _NewGraph_ with the graph complementary to _Graph_.
|
|
In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- 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]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred compose(+ _LeftGraph_, + _RightGraph_, - _NewGraph_)
|
|
|
|
|
|
Compose the graphs _LeftGraph_ and _RightGraph_ to form _NewGraph_.
|
|
In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
|
|
|
|
L = [1-[4],2-[1,2,4],3-[]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred del_edges(+ _Graph_, + _Edges_, - _NewGraph_)
|
|
|
|
|
|
Unify _NewGraph_ with a new graph obtained by removing the list of
|
|
edges _Edges_ from the graph _Graph_. Notice that no vertices
|
|
are deleted. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- 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-[]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred del_vertices(+ _Graph_, + _Vertices_, - _NewGraph_)
|
|
|
|
|
|
Unify _NewGraph_ with a new graph obtained by deleting the list of
|
|
vertices _Vertices_ and all the edges that start from or go to a
|
|
vertex in _Vertices_ to the graph _Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- del_vertices([2,1],[1-[3,5],2-[4],3-[],
|
|
4-[5],5-[],6-[],7-[2,6],8-[]],NL).
|
|
|
|
NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred edges(+ _Graph_, - _Edges_)
|
|
|
|
|
|
Unify _Edges_ with all edges appearing in graph
|
|
_Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], V).
|
|
|
|
L = [1,2,3,4,5]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred neighbors(+ _Vertex_, + _Graph_, - _Vertices_)
|
|
|
|
|
|
Unify _Vertices_ with the list of neighbors of vertex _Vertex_
|
|
in _Graph_. If the vertice is not in the graph fail. In the next
|
|
example:
|
|
|
|
~~~~~{.prolog}
|
|
?- neighbors(4,[1-[3,5],2-[4],3-[],
|
|
4-[1,2,7,5],5-[],6-[],7-[],8-[]],
|
|
NL).
|
|
|
|
NL = [1,2,7,5]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred neighbours(+ _Vertex_, + _Graph_, - _Vertices_)
|
|
|
|
|
|
Unify _Vertices_ with the list of neighbours of vertex _Vertex_
|
|
in _Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- neighbours(4,[1-[3,5],2-[4],3-[],
|
|
4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
|
|
|
|
NL = [1,2,7,5]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred reachable(+ _Node_, + _Graph_, - _Vertices_)
|
|
|
|
|
|
Unify _Vertices_ with the set of all vertices in graph
|
|
_Graph_ that are reachable from _Node_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V).
|
|
|
|
V = [1,3,5]
|
|
~~~~~
|
|
|
|
|
|
|
|
|
|
*/
|
|
/** @pred top_sort(+ _Graph_, - _Sort0_, - _Sort_)
|
|
|
|
Generate the difference list _Sort_- _Sort0_ as a topological
|
|
sorting of graph _Graph_, if one is possible.
|
|
|
|
|
|
*/
|
|
/** @pred top_sort(+ _Graph_, - _Sort_)
|
|
|
|
|
|
Generate the set of nodes _Sort_ as a topological sorting of graph
|
|
_Graph_, if one is possible.
|
|
In the next example we show how topological sorting works for a linear graph:
|
|
|
|
~~~~~{.prolog}
|
|
?- top_sort([_138-[_219],_219-[_139], _139-[]],L).
|
|
|
|
L = [_138,_219,_139]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred transitive_closure(+ _Graph_, + _Closure_)
|
|
|
|
|
|
Generate the graph _Closure_ as the transitive closure of graph
|
|
_Graph_.
|
|
In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L).
|
|
|
|
L = [1-[2,3,4,5,6],2-[4,5,6],4-[6]]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
/** @pred vertices(+ _Graph_, - _Vertices_)
|
|
|
|
|
|
Unify _Vertices_ with all vertices appearing in graph
|
|
_Graph_. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], V).
|
|
|
|
L = [1,2,3,4,5]
|
|
~~~~~
|
|
|
|
|
|
*/
|
|
:- module(ugraphs,
|
|
[
|
|
add_vertices/3,
|
|
add_edges/3,
|
|
complement/2,
|
|
compose/3,
|
|
del_edges/3,
|
|
del_vertices/3,
|
|
edges/2,
|
|
neighbours/3,
|
|
neighbors/3,
|
|
reachable/3,
|
|
top_sort/2,
|
|
top_sort/3,
|
|
transitive_closure/2,
|
|
transpose/2,
|
|
vertices/2,
|
|
vertices_edges_to_ugraph/3,
|
|
ugraph_union/3
|
|
]).
|
|
|
|
:- use_module(library(lists), [
|
|
append/3,
|
|
member/2,
|
|
memberchk/2
|
|
]).
|
|
|
|
:- use_module(library(ordsets), [
|
|
ord_add_element/3,
|
|
ord_subtract/3,
|
|
ord_union/3,
|
|
ord_union/4
|
|
]).
|
|
|
|
|
|
/*
|
|
|
|
:- public
|
|
p_to_s_graph/2,
|
|
s_to_p_graph/2, % edges
|
|
s_to_p_trans/2,
|
|
p_member/3,
|
|
s_member/3,
|
|
p_transpose/2,
|
|
s_transpose/2,
|
|
compose/3,
|
|
top_sort/2,
|
|
vertices/2,
|
|
warshall/2.
|
|
|
|
:- mode
|
|
vertices(+, -),
|
|
p_to_s_graph(+, -),
|
|
p_to_s_vertices(+, -),
|
|
p_to_s_group(+, +, -),
|
|
p_to_s_group(+, +, -, -),
|
|
s_to_p_graph(+, -),
|
|
s_to_p_graph(+, +, -, -),
|
|
s_to_p_trans(+, -),
|
|
s_to_p_trans(+, +, -, -),
|
|
p_member(?, ?, +),
|
|
s_member(?, ?, +),
|
|
p_transpose(+, -),
|
|
s_transpose(+, -),
|
|
s_transpose(+, -, ?, -),
|
|
transpose_s(+, +, +, -),
|
|
compose(+, +, -),
|
|
compose(+, +, +, -),
|
|
compose1(+, +, +, -),
|
|
compose1(+, +, +, +, +, +, +, -),
|
|
top_sort(+, -),
|
|
vertices_and_zeros(+, -, ?),
|
|
count_edges(+, +, +, -),
|
|
incr_list(+, +, +, -),
|
|
select_zeros(+, +, -),
|
|
top_sort(+, -, +, +, +),
|
|
decr_list(+, +, +, -, +, -),
|
|
warshall(+, -),
|
|
warshall(+, +, -),
|
|
warshall(+, +, +, -).
|
|
|
|
*/
|
|
|
|
|
|
% vertices(S_Graph, Vertices)
|
|
% strips off the neighbours lists of an S-representation to produce
|
|
% a list of the vertices of the graph. (It is a characteristic of
|
|
% S-representations that *every* vertex appears, even if it has no
|
|
% neighbours.)
|
|
|
|
vertices([], []) :- !.
|
|
vertices([Vertex-_|Graph], [Vertex|Vertices]) :-
|
|
vertices(Graph, Vertices).
|
|
|
|
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) :-
|
|
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).
|
|
|
|
%
|
|
% unmark a set of vertices plus all edges leading to them.
|
|
%
|
|
del_vertices(Graph, Vertices, NewGraph) :-
|
|
msort(Vertices, V1),
|
|
(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) :-
|
|
p_to_s_graph(Edges, G1),
|
|
graph_union(Graph, G1, NewGraph).
|
|
|
|
% graph_union(+Set1, +Set2, ?Union)
|
|
% is true when Union is the union of Set1 and Set2. This code is a copy
|
|
% of set union
|
|
|
|
graph_union(Set1, [], Set1) :- !.
|
|
graph_union([], Set2, Set2) :- !.
|
|
graph_union([Head1-E1|Tail1], [Head2-E2|Tail2], Union) :-
|
|
compare(Order, Head1, Head2),
|
|
graph_union(Order, Head1-E1, Tail1, Head2-E2, Tail2, Union).
|
|
|
|
graph_union(=, Head-E1, Tail1, _-E2, Tail2, [Head-Es|Union]) :-
|
|
ord_union(E1, E2, Es),
|
|
graph_union(Tail1, Tail2, Union).
|
|
graph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :-
|
|
graph_union(Tail1, [Head2|Tail2], Union).
|
|
graph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :-
|
|
graph_union([Head1|Tail1], Tail2, Union).
|
|
|
|
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) :-
|
|
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).
|
|
|
|
|
|
|
|
s_to_p_trans([], []) :- !.
|
|
s_to_p_trans([Vertex-Neibs|G], P_Graph) :-
|
|
s_to_p_trans(Neibs, Vertex, P_Graph, Rest_P_Graph),
|
|
s_to_p_trans(G, Rest_P_Graph).
|
|
|
|
|
|
s_to_p_trans([], _, P_Graph, P_Graph) :- !.
|
|
s_to_p_trans([Neib|Neibs], Vertex, [Neib-Vertex|P], Rest_P) :-
|
|
s_to_p_trans(Neibs, Vertex, P, Rest_P).
|
|
|
|
|
|
|
|
transitive_closure(Graph, Closure) :-
|
|
warshall(Graph, Graph, Closure).
|
|
|
|
warshall(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([], _, _, []).
|
|
|
|
|
|
|
|
p_transpose([], []) :- !.
|
|
p_transpose([From-To|Edges], [To-From|Transpose]) :-
|
|
p_transpose(Edges, Transpose).
|
|
|
|
|
|
/** @pred transpose(+ _Graph_, - _NewGraph_)
|
|
|
|
|
|
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|^2)`. In the next example:
|
|
|
|
~~~~~{.prolog}
|
|
?- 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-[]]
|
|
~~~~~
|
|
Notice that an undirected graph is its own transpose.
|
|
|
|
|
|
*/
|
|
transpose(S_Graph, Transpose) :-
|
|
s_transpose(S_Graph, Base, Base, Transpose).
|
|
|
|
s_transpose(S_Graph, Transpose) :-
|
|
s_transpose(S_Graph, Base, Base, Transpose).
|
|
|
|
s_transpose([], [], Base, Base) :- !.
|
|
s_transpose([Vertex-Neibs|Graph], [Vertex-[]|RestBase], Base, Transpose) :-
|
|
s_transpose(Graph, RestBase, Base, SoFar),
|
|
transpose_s(SoFar, Neibs, Vertex, Transpose).
|
|
|
|
transpose_s([Neib-Trans|SoFar], [Neib|Neibs], Vertex,
|
|
[Neib-[Vertex|Trans]|Transpose]) :- !,
|
|
transpose_s(SoFar, Neibs, Vertex, Transpose).
|
|
transpose_s([Head|SoFar], Neibs, Vertex, [Head|Transpose]) :- !,
|
|
transpose_s(SoFar, Neibs, Vertex, Transpose).
|
|
transpose_s([], [], _, []).
|
|
|
|
|
|
|
|
% p_member(X, Y, P_Graph)
|
|
% tests whether the edge (X,Y) occurs in the graph. This always
|
|
% costs O(|E|) time. Here, as in all the operations in this file,
|
|
% vertex labels are assumed to be ground terms, or at least to be
|
|
% sufficiently instantiated that no two of them have a common instance.
|
|
|
|
p_member(X, Y, P_Graph) :-
|
|
nonvar(X), nonvar(Y), !,
|
|
memberchk(X-Y, P_Graph).
|
|
p_member(X, Y, P_Graph) :-
|
|
member(X-Y, P_Graph).
|
|
|
|
% s_member(X, Y, S_Graph)
|
|
% tests whether the edge (X,Y) occurs in the graph. If either
|
|
% X or Y is instantiated, the check is order |V| rather than
|
|
% order |E|.
|
|
|
|
s_member(X, Y, S_Graph) :-
|
|
var(X), var(Y), !,
|
|
member(X-Neibs, S_Graph),
|
|
member(Y, Neibs).
|
|
s_member(X, Y, S_Graph) :-
|
|
var(X), !,
|
|
member(X-Neibs, S_Graph),
|
|
memberchk(Y, Neibs).
|
|
s_member(X, Y, S_Graph) :-
|
|
var(Y), !,
|
|
memberchk(X-Neibs, S_Graph),
|
|
member(Y, Neibs).
|
|
s_member(X, Y, S_Graph) :-
|
|
memberchk(X-Neibs, S_Graph),
|
|
memberchk(Y, Neibs).
|
|
|
|
|
|
% compose(G1, G2, Composition)
|
|
% calculates the composition of two S-form graphs, which need not
|
|
% have the same set of vertices.
|
|
|
|
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).
|
|
|
|
|
|
/* NOT USED AFTER ALL
|
|
% raakau(Vertices, InitialValue, Tree)
|
|
% takes an *ordered* list of verticies and an initial value, and
|
|
% makes a very special sort of tree out of them, which represents
|
|
% a function sending each vertex to the initial value. Note that
|
|
% in the third clause for raakau/6 Z can never be 0, this means
|
|
% that it doesn't matter *what* "greatest member" is reported for
|
|
% empty trees.
|
|
|
|
raakau(Vertices, InitialValue, Tree) :-
|
|
length(Vertices, N),
|
|
raakau(N, Vertices, _, _, InitialValue, Tree).
|
|
|
|
|
|
raakau(0, Vs, Vs, 0, I, t) :- !.
|
|
raakau(1, [V|Vs], Vs, V, I, t(V,I)) :- !.
|
|
raakau(N, Vi, Vo, W, I, t(V,W,I,L,R)) :-
|
|
A is (N-1)/2,
|
|
Z is (N-1)-A, % Z >= 1
|
|
raakau(A, Vi, [V|Vm], _, I, L),
|
|
raakau(Z, Vm, Vo, W, I, R).
|
|
|
|
|
|
% incdec(OldTree, Labels, Incr, NewTree)
|
|
% adds Incr to the value associated with each element of Labels
|
|
% in OldTree, producing a new tree. OldTree must have been produced
|
|
% either by raakau or by incdec, Labels must be in ascedning order,
|
|
% and must be a subset of the labels of the tree.
|
|
|
|
incdec(OldTree, Labels, Incr, NewTree) :-
|
|
incdec(OldTree, NewTree, Labels, _, Incr).
|
|
|
|
|
|
incdec(t(V,M), t(V,N), [V|L], L, I) :- !,
|
|
N is M+I.
|
|
incdec(t(V,W,M,L1,R1), t(V,W,N,L2,R2), Li, Lo, I) :-
|
|
( Li = [Hi|_], Hi @< V, !,
|
|
incdec(L1, L2, Li, Lm, I)
|
|
; L2 = L1, Lm = Li
|
|
),
|
|
( Lm = [V|Lr], !,
|
|
N is M+I
|
|
; Lr = Lm, N = M
|
|
),
|
|
( Lr = [Hr|_], Hr @=< W, !,
|
|
incdec(R1, R2, Lr, Lo, I)
|
|
; R2 = R1, Lo = Lr
|
|
).
|
|
/* END UNUSED CODE */
|
|
|
|
|
|
|
|
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(V,[V0-Neig|_],Neig) :- V == V0, !.
|
|
neighbors(V,[_|G],Neig) :-
|
|
neighbors(V,G,Neig).
|
|
|
|
neighbours(V,[V0-Neig|_],Neig) :- V == V0, !.
|
|
neighbours(V,[_|G],Neig) :-
|
|
neighbours(V,G,Neig).
|
|
|
|
|
|
%
|
|
% 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(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).
|
|
|
|
%% ugraph_union(+Set1, +Set2, ?Union)
|
|
%
|
|
% Is true when Union is the union of Set1 and Set2. This code is a
|
|
% copy of set union
|
|
|
|
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).
|
|
|