pgr_contractionDeadEnd
- Proposed¶
pgr_contractionDeadEnd
— Performs graph contraction and returns the contracted
vertices and edges.
Warning
Proposed functions for next mayor release.
They are not officially in the current release.
They will likely officially be part of the next mayor release:
The functions make use of ANY-INTEGER and ANY-NUMERICAL
Name might not change. (But still can)
Signature might not change. (But still can)
Functionality might not change. (But still can)
pgTap tests have being done. But might need more.
Documentation might need refinement.
Availability
Version 3.8.0
New proposed function.
Description¶
Contraction reduces the size of the graph by removing some of the vertices and edges and, for example, might add edges that represent a sequence of original edges decreasing the total time and space used in graph algorithms.
The main Characteristics are:
Process is done only on edges with positive costs.
Does not return the full contracted graph.
Only changes on the graph are returned.
The returned values include:
The new edges generated by linear contraction.
The modified vertices generated by dead end contraction.
The returned values are ordered as follows:
column
id
ascending when its a modified vertex.column
id
with negative numbers descending when its a new edge.
A node is considered a dead end node when:
On undirected graphs:
The number of adjacent vertices is 1.
On directed graphs:
When there is only one adjacent vertex or
When all edges are incoming regardless of the number of adjacent vertices.
Signatures¶
[directed, forbidden_vertices]
(type, id, contracted_vertices, source, target, cost)
- Example:
Dead end contraction on an undirected graph.
SELECT * FROM pgr_contractionDeadEnd(
'SELECT id, source, target, cost, reverse_cost FROM edges',
directed => false);
type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
v | 4 | {2} | -1 | -1 | -1
v | 6 | {5} | -1 | -1 | -1
v | 7 | {1,3} | -1 | -1 | -1
v | 8 | {9} | -1 | -1 | -1
v | 14 | {13} | -1 | -1 | -1
(5 rows)
The green nodes are dead end nodes.
Node
is a dead end node after node is contracted.
![graph G {
splines=false;
1,2,3,5,9,13 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
4,6,7,8,10,11,12,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
4 [label="4,{2}"];
6 [label="6,{5}"];
7 [label="7,{1,3}"];
8 [label="8,{9}"];
14 [label="14,{13}"];
6 -- 10 [label="2",fontsize=8];
10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8];
10 -- 11 [label="5",fontsize=8];
7 -- 11 [label="8",fontsize=8];
11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8];
11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8];
12 -- 17 [label="13",fontsize=8];
16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
1 [pos="0,2!"]; 2 [pos="0.5,3.5!"];
3 [pos="1,2!"]; 4 [pos="2,3.5!"];
5 [pos="2,0!"]; 6 [pos="2,1!"];
7 [pos="2,2!"]; 8 [pos="2,3!"];
9 [pos="2,4!"]; 10 [pos="3,1!"];
11 [pos="3,2!"]; 12 [pos="3,3!"];
13 [pos="3.5,2.3!"]; 14 [pos="3.5,4!"];
15 [pos="4,1!"]; 16 [pos="4,2!"];
17 [pos="4,3!"];
}](_images/graphviz-09e10c04c727d31eec492eab5746af897f9fcb08.png)
Parameters¶
Parameter |
Type |
Description |
---|---|---|
|
Edges SQL as described below. |
Optional parameters¶
Column |
Type |
Default |
Description |
---|---|---|---|
|
|
|
|
Contraction optional parameters¶
Column |
Type |
Default |
Description |
---|---|---|---|
|
|
Empty |
Identifiers of vertices forbidden for contraction. |
Inner Queries¶
Edges SQL¶
Column |
Type |
Default |
Description |
---|---|---|---|
|
ANY-INTEGER |
Identifier of the edge. |
|
|
ANY-INTEGER |
Identifier of the first end point vertex of the edge. |
|
|
ANY-INTEGER |
Identifier of the second end point vertex of the edge. |
|
|
ANY-NUMERICAL |
Weight of the edge ( |
|
|
ANY-NUMERICAL |
-1 |
Weight of the edge (
|
Where:
- ANY-INTEGER:
SMALLINT
,INTEGER
,BIGINT
- ANY-NUMERICAL:
SMALLINT
,INTEGER
,BIGINT
,REAL
,FLOAT
Result columns¶
Returns set of (type, id, contracted_vertices, source, target, cost)
The function returns a single row. The columns of the row are:
Column |
Type |
Description |
---|---|---|
|
|
Value = |
|
|
A pseudo id of the edge.
|
|
|
Array of contracted vertex identifiers. |
|
|
Identifier of the source vertex of the current edge. |
|
|
Identifier of the target vertex of the current edge. |
|
|
Weight of the current edge. |
Additional Examples¶
Dead end vertex on undirected graph¶
The green nodes are dead end nodes.
They have only one adjacent node.
![graph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | {<5> 5 | <6> 6}}"; pos="2.5,1!"];
1,2,3,4 [shape=circle;fontsize=8;fixedsize=true;style=filled];
2,3 [color=deepskyblue];
1,4 [color=green];
{G:5,G:6} -- {2,3} [weight=1, penwidth=3];
1 -- 2 -- 1;
3 -- 4;
1 [pos="1,0!"]; 2 [pos="2,0!"]; 3 [pos="3,0!"]; 4 [pos="4,0!"];
}](_images/graphviz-1c24ab7e60f517bb9ef04765d3876cadce6a4c3d.png)
SELECT * FROM pgr_contractionDeadEnd(
$$SELECT * FROM (VALUES
(1, 1, 2, 1, 1),
(2, 3, 4, 1, -1),
(3, 2, 5, 1, 1), (4, 2, 6, 1, 1),
(5, 3, 5, 1, 1), (5, 3, 6, 1, 1))
AS edges(id,source,target,cost,reverse_cost)$$,
directed => true);
type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
v | 2 | {1} | -1 | -1 | -1
v | 3 | {4} | -1 | -1 | -1
(2 rows)
![graph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | {<5> 5 | <6> 6}}"; pos="2.5,1!"];
2,3 [shape=circle;fontsize=8;fixedsize=true;style=filled];
2,3 [color=deepskyblue];
2 [label="2, {1}"]; 3 [label="3, {4}"];
{G:5,G:6} -- {2,3} [weight=1, penwidth=3];
2 [pos="2,0!"]; 3 [pos="3,0!"];
}](_images/graphviz-3370f619564ce43a83c68cabc60595106c8a81f0.png)
Dead end vertex on directed graph¶
The green nodes are dead end nodes
The blue nodes have an unlimited number of incoming and/or outgoing edges.
![digraph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | { | | | | }}"; pos="2.5,3!"];
1,2,3,4,5,6,7,8,9,10 [shape=circle;fontsize=8;fixedsize=true;style=filled];
1,2,3,4,5,10 [color=deepskyblue];
6,7,8,9 [color=green];
{1,2,3,4,5} -> G [dir=both;weight=1, penwidth=3];
1 -> 6 -> 1;
2 -> 7;
{3, 2} -> 8;
9 -> 4;
10 -> {4, 5};
2 [pos="1,4!"]; 3 [pos="2,4!"];
G [pos="2.5,3!"];
1 [pos="1,1!"]; 2 [pos="2,1!"]; 3 [pos="3,1!"]; 4 [pos="4,1!"]; 5 [pos="5,1!"];
6 [pos="1,0!"]; 7 [pos="2,0!"]; 8 [pos="3,0!"]; 9 [pos="4,0!"]; 10 [pos="5,0!"];
}](_images/graphviz-6960d9a7cc004f22357ed2df4395c81f4453d60d.png)
Node |
Adjacent nodes |
Dead end |
Reason |
---|---|---|---|
Yes |
Has only one adjacent node. |
||
Yes |
Has only one adjacent node. |
||
Yes |
Has more than one adjacent node and all edges are incoming. |
||
Yes |
Has only one adjacent node. |
||
No |
Has more than one adjacent node and all edges are outgoing. |
||
Many adjacent nodes. |
No |
Has more than one adjacent node and some edges are incoming and some are outgoing. |
From above, nodes
When there are more than one adjacent vertex, all edges need to be all incoming edges otherwise it is not a dead end.
SELECT * FROM pgr_contractionDeadEnd(
$$SELECT * FROM (VALUES
(1, 1, 6, 1, 1),
(2, 2, 7, 1, -1),
(3, 2, 8, 1, -1),
(4, 3, 8, 1, -1),
(5, 9, 4, 1, -1),
(6, 10, 4, 1, 1),
(7, 10, 5, 1, 1),
/* Rest of the graph */
(8, 1, 25, 1, 1), (9, 1, 26, 1, 1),
(10, 2, 25, 1, 1), (11, 2, 26, 1, 1),
(12, 3, 25, 1, 1), (13, 3, 26, 1, 1),
(14, 4, 25, 1, 1), (15, 4, 26, 1, 1),
(16, 5, 25, 1, 1), (17, 5, 26, 1, 1)) AS edges(id,source,target,cost,reverse_cost)$$,
directed => true);
type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
v | 1 | {6} | -1 | -1 | -1
v | 2 | {7,8} | -1 | -1 | -1
v | 3 | {8} | -1 | -1 | -1
v | 4 | {9} | -1 | -1 | -1
(4 rows)
![digraph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | { | | | | }}"; pos="2.5,3!"];
1,2,3,4,5,10 [shape=circle;fontsize=8;fixedsize=true;style=filled];
1,2,3,4,5,10 [color=deepskyblue];
{1,2,3,4,5} -> G [dir=both;weight=1, penwidth=3];
10 -> {4, 5};
2 [pos="1,4!"]; 3 [pos="2,4!"];
G [pos="2.5,3!"];
1 [label="1, {6}";pos="1,1!"]; 2 [label="2, {7,8}";pos="2,1!"];
3 [label="3, {8}";pos="3,1!"]; 4 [label="4, {9}";pos="4,1!"]; 5 [pos="5,1!"];
10 [pos="5,0!"];
}](_images/graphviz-245db535af656e6524dab0622a0da5521f2f1e69.png)
Step by step dead end contraction¶
The dead end contraction will stop until there are no more dead end nodes.
For example, from the following graph where
![digraph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | { | | | | }}"; pos="2,3!"];
1,2,3 [shape=circle;fontsize=8;fixedsize=true;style=filled];
1,2 [color=deepskyblue];
3 [color=green];
{1} -> G [dir=both;weight=1, penwidth=3];
1 -> 2 -> 3;
G [pos="2.5,3!"];
1 [pos="1,1!"]; 2 [pos="2,1!"]; 3 [pos="3,1!"];
}](_images/graphviz-da4ed291fcc11b0540b23dbf3a5c9a4335487756.png)
After contracting
![digraph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | { | | | | }}"; pos="2,3!"];
1,2 [shape=circle;fontsize=8;fixedsize=true;style=filled];
1 [color=deepskyblue];
2 [color=green];
{1} -> G [dir=both;weight=1, penwidth=3];
1 -> 2;
G [pos="2.5,3!"];
1 [pos="1,1!"]; 2 [label="2, {3}";pos="2,1!"];
}](_images/graphviz-36beb2cf1db17be6d509dfca32d48b88fbdcb45a.png)
After contracting
![digraph G {
G [shape=record;style=filled;fillcolor=deepskyblue;
label="{Rest of the Graph | { | | | | }}"; pos="2,3!"];
1 [shape=circle;fontsize=8;fixedsize=true;style=filled];
1 [color=deepskyblue];
{1} -> G [dir=both;weight=1, penwidth=3];
G [pos="2.5,3!"];
1 [label="1, {2,3}";pos="2,1!"];
}](_images/graphviz-0dc0b6bfc522238b8ea2cedd8edc6126892ab49e.png)
SELECT * FROM pgr_contractionDeadEnd(
$$SELECT * FROM (VALUES
(1, 1, 2, 1, -1),
(2, 2, 3, 1, -1),
/* Rest of the graph */
(3, 1, 25, 1, 1), (4, 1, 26, 1, 1),
(5, 25, 25, 1, 1), (6, 25, 26, 1, 1)) AS edges(id,source,target,cost,reverse_cost)$$,
directed => true);
type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
v | 1 | {2,3} | -1 | -1 | -1
(1 row)
Creating the contracted graph¶
Steps for the creation of the contracted graph¶
Add additional columns.
ALTER TABLE vertices ADD is_contracted BOOLEAN DEFAULT false;
ALTER TABLE
ALTER TABLE vertices ADD contracted_vertices BIGINT[];
ALTER TABLE
Save results into a table.
SELECT * INTO contraction_results
FROM pgr_contractionDeadEnd(
'SELECT id, source, target, cost, reverse_cost FROM edges',
directed => false);
SELECT 5
Use is_contracted
column to indicate the vertices that are contracted.
UPDATE vertices
SET is_contracted = true
WHERE id IN (SELECT unnest(contracted_vertices) FROM contraction_results);
UPDATE 6
Fill contracted_vertices
with the information from the results that belong
to the vertices.
UPDATE vertices
SET contracted_vertices = contraction_results.contracted_vertices
FROM contraction_results
WHERE type = 'v' AND vertices.id = contraction_results.id;
UPDATE 5
The contracted vertices are not part of the contracted graph.
SELECT id, is_contracted
FROM vertices WHERE is_contracted ORDER BY id;
id | is_contracted
----+---------------
1 | t
2 | t
3 | t
5 | t
9 | t
13 | t
(6 rows)
The contracted graph¶
DROP VIEW IF EXISTS contracted_graph;
NOTICE: view "contracted_graph" does not exist, skipping
DROP VIEW
CREATE VIEW contracted_graph AS
WITH
vertices_in_graph AS (
SELECT id FROM vertices WHERE is_contracted = false
)
SELECT id, source, target, cost, reverse_cost
FROM edges
WHERE source IN (SELECT * FROM vertices_in_graph)
AND target IN (SELECT * FROM vertices_in_graph)
ORDER BY id;
CREATE VIEW
![graph G {
splines=false;
4,6,7,8,10,11,12,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
4 [label="4,{2}"];
6 [label="6,{5}"];
7 [label="7,{1,3}"];
8 [label="8,{9}"];
14 [label="14,{13}"];
6 -- 10 [label="2",fontsize=8];
10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8];
10 -- 11 [label="5",fontsize=8];
7 -- 11 [label="8",fontsize=8];
11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8];
11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8];
12 -- 17 [label="13",fontsize=8];
16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
4 [pos="2,3.5!"];
6 [pos="2,1!"];
7 [pos="2,2!"]; 8 [pos="2,3!"];
10 [pos="3,1!"];
11 [pos="3,2!"]; 12 [pos="3,3!"];
14 [pos="3.5,4!"];
15 [pos="4,1!"]; 16 [pos="4,2!"];
17 [pos="4,3!"];
}](_images/graphviz-0f73cc9d674c207beec7c0030b424cbd1b9dd24f.png)
Using when departure and destination are in the contracted graph¶
SELECT *
FROM pgr_dijkstra('SELECT * FROM contracted_graph', 6, 17);
seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost
-----+----------+-----------+---------+------+------+------+----------
1 | 1 | 6 | 17 | 6 | 4 | 1 | 0
2 | 2 | 6 | 17 | 7 | 8 | 1 | 1
3 | 3 | 6 | 17 | 11 | 11 | 1 | 2
4 | 4 | 6 | 17 | 12 | 13 | 1 | 3
5 | 5 | 6 | 17 | 17 | -1 | 0 | 4
(5 rows)
![graph G {
splines=false;
4,6,7,8,10,11,12,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
4 [label="4,{2}"];
6 [label="6,{5}"];
7 [label="7,{1,3}"];
8 [label="8,{9}"];
14 [label="14,{13}"];
6 -- 10 [label="2",fontsize=8];
10 -- 15 [label="3",fontsize=8]; 6 -- 7 [color=red;label="4",fontsize=8];
10 -- 11 [label="5",fontsize=8];
7 -- 11 [color=red;label="8",fontsize=8];
11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8];
11 -- 12 [color=red;label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8];
12 -- 17 [color=red;label="13",fontsize=8];
16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
4 [pos="2,3.5!"];
6 [pos="2,1!"];
7 [pos="2,2!"]; 8 [pos="2,3!"];
10 [pos="3,1!"];
11 [pos="3,2!"]; 12 [pos="3,3!"];
14 [pos="3.5,4!"];
15 [pos="4,1!"]; 16 [pos="4,2!"];
17 [pos="4,3!"];
}](_images/graphviz-fb96c71385be96c073e575d76eb1da8af92db50f.png)
Using when departure/destination is not in the contracted graph¶
SELECT * FROM pgr_dijkstra(
'WITH cul_de_sac AS (
SELECT contracted_vertices || id as v
FROM vertices WHERE 1 = ANY(contracted_vertices))
SELECT id, source, target, cost, reverse_cost FROM edges, cul_de_sac
WHERE source = ANY(v) AND target = ANY(v)
UNION
SELECT id, source, target, cost, reverse_cost FROM contracted_graph',
1, 17);
seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost
-----+----------+-----------+---------+------+------+------+----------
1 | 1 | 1 | 17 | 1 | 6 | 1 | 0
2 | 2 | 1 | 17 | 3 | 7 | 1 | 1
3 | 3 | 1 | 17 | 7 | 8 | 1 | 2
4 | 4 | 1 | 17 | 11 | 9 | 1 | 3
5 | 5 | 1 | 17 | 16 | 15 | 1 | 4
6 | 6 | 1 | 17 | 17 | -1 | 0 | 5
(6 rows)
![graph G {
splines=false;
1,3 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
4,6,7,8,10,11,12,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
4 [label="4,{2}"];
6 [label="6,{5}"];
7 [label="7,{1,3}"];
8 [label="8,{9}"];
14 [label="14,{13}"];
1 -- 3 [color=red;label="6",fontsize=8];
3 -- 7 [color=red;label="7",fontsize=8];
6 -- 10 [label="2",fontsize=8];
10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8];
10 -- 11 [label="5",fontsize=8];
7 -- 11 [color=red;label="8",fontsize=8];
11 -- 16 [color=red;label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8];
11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8];
12 -- 17 [label="13",fontsize=8];
16 -- 17 [color=red;label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
1 [pos="0,2!"];
3 [pos="1,2!"]; 4 [pos="2,3.5!"];
6 [pos="2,1!"];
7 [pos="2,2!"]; 8 [pos="2,3!"];
10 [pos="3,1!"];
11 [pos="3,2!"]; 12 [pos="3,3!"];
14 [pos="3.5,4!"];
15 [pos="4,1!"]; 16 [pos="4,2!"];
17 [pos="4,3!"];
}](_images/graphviz-1027eeee89fdb0afb0b1167084efcba514616b36.png)
Using when departure and destination are not in the contracted graph¶
SELECT * FROM pgr_dijkstra(
'WITH cul_de_sac AS (
SELECT contracted_vertices || id as v
FROM vertices WHERE 1 = ANY(contracted_vertices) OR 9 = ANY(contracted_vertices))
SELECT id, source, target, cost, reverse_cost FROM edges, cul_de_sac WHERE source = ANY(v) AND target = ANY(v)
UNION
SELECT id, source, target, cost, reverse_cost FROM contracted_graph',
1, 9);
seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost
-----+----------+-----------+---------+------+------+------+----------
1 | 1 | 1 | 9 | 1 | 6 | 1 | 0
2 | 2 | 1 | 9 | 3 | 7 | 1 | 1
3 | 3 | 1 | 9 | 7 | 10 | 1 | 2
4 | 4 | 1 | 9 | 8 | 14 | 1 | 3
5 | 5 | 1 | 9 | 9 | -1 | 0 | 4
(5 rows)
![graph G {
splines=false;
1,3,9 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
4,6,7,8,10,11,12,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
4 [label="4,{2}"];
6 [label="6,{5}"];
7 [label="7,{1,3}"];
8 [label="8,{9}"];
14 [label="14,{13}"];
1 -- 3 [color=red;label="6",fontsize=8];
3 -- 7 [color=red;label="7",fontsize=8];
8 -- 9 [color=red;label="7",fontsize=8];
6 -- 10 [label="2",fontsize=8];
10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8];
10 -- 11 [label="5",fontsize=8];
7 -- 11 [label="8",fontsize=8];
11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8];
11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8];
12 -- 17 [label="13",fontsize=8];
16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
1 [pos="0,2!"];
3 [pos="1,2!"]; 4 [pos="2,3.5!"];
6 [pos="2,1!"];
7 [pos="2,2!"]; 8 [pos="2,3!"];
9 [pos="2,4!"]; 10 [pos="3,1!"];
11 [pos="3,2!"]; 12 [pos="3,3!"];
14 [pos="3.5,4!"];
15 [pos="4,1!"]; 16 [pos="4,2!"];
17 [pos="4,3!"];
}](_images/graphviz-71ddba31051fac0c8694973c7d5a06026a36e97d.png)
See Also¶
Indices and tables