Supported versions: latest (3.8) main dev

pgr_contractionLinear - Proposed

pgr_contractionLinear — Performs graph contraction and returns the contracted vertices and edges.

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.

Boost Graph Inside Boost Graph Inside

Signatures

pgr_contractionLinear(Edges SQL, [options])
options: [directed, max_cycles, forbidden_vertices]
Returns set of (type, id, contracted_vertices, source, target, cost)
Example:

Linear contraction on an undirected graph.

SELECT * FROM pgr_contractionLinear(
  'SELECT id, source, target, cost, reverse_cost FROM edges',
  directed => false);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
 e    | -1 | {3}                 |      1 |      7 |    2
 e    | -2 | {17}                |     12 |     16 |    2
 e    | -3 | {15}                |     10 |     16 |    2
(3 rows)

  • The green nodes are linear nodes and will not be part of the contracted graph.

    • All edges adjacent will not be part of the contracted graph.

  • The red lines will be new edges of the contracted graph.

graph G {
  splines=false;
  3,15,17 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,2,4,5,6,7,8,9,10,11,12,13,14,16 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1:n -- 7:n   [label="-1",fontsize=8,color=red];
  12:s -- 17:sw -- 16:w [label="-2",fontsize=8,color=red];
  10:n -- 15:nw -- 16:w [label="-3",fontsize=8,color=red];
  5 -- 6 [label="1",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];   1 -- 3 [label="6",fontsize=8];
  3 -- 7 [label="7",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];  8 -- 9 [label="",fontsize=8];
  16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8];
  2 -- 4 [label="17",fontsize=8];   13 -- 14 [label="18",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!"];
}

Parameters

Parameter

Type

Description

Edges SQL

TEXT

Edges SQL as described below.

contraction Order

ARRAY[ ANY-INTEGER ]

Ordered contraction operations.

  • 1 = Dead end contraction

  • 2 = Linear contraction

Optional parameters

Column

Type

Default

Description

directed

BOOLEAN

true

  • When true the graph is considered Directed

  • When false the graph is considered as Undirected.

Contraction optional parameters

Column

Type

Default

Description

forbidden_vertices

ARRAY[ ANY-INTEGER ]

Empty

Identifiers of vertices forbidden for contraction.

max_cycles

INTEGER

1

Number of times the contraction operations on contraction_order will be performed.

Inner Queries

Edges SQL

Column

Type

Default

Description

id

ANY-INTEGER

Identifier of the edge.

source

ANY-INTEGER

Identifier of the first end point vertex of the edge.

target

ANY-INTEGER

Identifier of the second end point vertex of the edge.

cost

ANY-NUMERICAL

Weight of the edge (source, target)

reverse_cost

ANY-NUMERICAL

-1

Weight of the edge (target, source)

  • When negative: edge (target, source) does not exist, therefore it’s not part of the graph.

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

type

TEXT

Value = e indicating the row is an edge.

id

BIGINT

A pseudo id of the edge.

  • All numbers on this column are DISTINCT

  • Decreasing sequence starting from -1.

contracted_vertices

ARRAY[BIGINT]

Array of contracted vertex identifiers.

source

BIGINT

Identifier of the source vertex of the current edge.

target

BIGINT

Identifier of the target vertex of the current edge.

cost

FLOAT

Weight of the current edge.

Additional Examples

Linear edges

Undirected graph

A node connects two (or more) linear edges when

  • The number of adjacent vertices is 2.

graph G {
  label = "Linear edges"
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -- 2 -- 3 -- 2;
  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}
graph G {
  label = "Non linear edges"
  4,5,6,7 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  4 -- 5 -- 6 -- 5; 5 --7;
  4 [pos="0,0!"];   5 [pos="1,0!"];   6 [pos="2,0!"];
                  7 [pos="1,1!"];
}

In case of a directed graph, a node is considered a linear node when

  • The number of adjacent vertices is 2.

  • Linearity is symmetrical.

digraph G {
  label = "Linearity is symmetrical."
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -> 2 -> 3 -> 2 -> 1;
  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}
digraph G {
  label = "Linearity is not symmetrical."
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -> 2 -> 3 -> 2;
  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}

Linearity is not symmetrical

Directed graph

Graph where linearity is not symmetrical.

digraph G {
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -> 2 [label="1",fontsize=8];
  2 -> 3 [label="3",fontsize=8];
  3 -> 2 [label="4",fontsize=8];

  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}

When the graph is processed as a directed graph, linearity is not symmetrical, therefore the graph can not be contracted.

SELECT * FROM pgr_contractionLinear(
$$SELECT * FROM (VALUES
  (1, 1, 2, 1, -1),
  (2, 2, 3, 3, 4))
  AS edges(id,source,target,cost,reverse_cost)$$,
  directed => true);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
(0 rows)

Undirected graph

When the same graph is processed as an undirected graph, linearity is symmetrical, therefore the graph can be contracted.

graph G {
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -- 2 [label="1",fontsize=8];
  2 -- 3 [label="3",fontsize=8];
  3 -- 2 [label="4",fontsize=8];
  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}
SELECT * FROM pgr_contractionLinear(
$$SELECT * FROM (VALUES
  (1, 1, 2, 1, -1),
  (2, 2, 3, 3, 4))
  AS edges(id,source,target,cost,reverse_cost)$$,
  directed => false);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
 e    | -1 | {2}                 |      1 |      3 |    4
(1 row)

The three edges can be replaced by one undirected edge

  • Edge 13.

    • With cost: 4.

    • Contracted vertices in the edge: {2}.

graph G {
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -- 3 [label="4, {2}",fontsize=8;color=red];
  1 [pos="0,2!"]; 3 [pos="2,2!"];
}

Linearity is symmetrical

Directed graph

Graph where linearity is symmetrical.

digraph G {
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -> 2 [label="1",fontsize=8];
  2 -> 1 [label="2",fontsize=8];
  2 -> 3 [label="3",fontsize=8];
  3 -> 2 [label="4",fontsize=8];

  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}

When the graph is processed as a directed graph, linearity is not symmetrical, therefore the graph can not be contracted.

SELECT * FROM pgr_contractionLinear(
$$SELECT * FROM (VALUES
  (1, 1, 2, 1, 2),
  (2, 2, 3, 3, 4))
  AS edges(id,source,target,cost,reverse_cost)$$,
  directed => true);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
 e    | -1 | {2}                 |      1 |      3 |    4
 e    | -2 | {2}                 |      3 |      1 |    6
(2 rows)

The four edges can be replaced by two directed edges.

  • Edge 13.

    • With cost: 4.

    • Contracted vertices in the edge: {2}.

  • Edge 31.

    • With cost: 6.

    • Contracted vertices in the edge: {2}.

digraph G {
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -> 3 [label="4, {2}",fontsize=8;color=red];
  3 -> 1 [label="6, {2}",fontsize=8;color=red];

  1 [pos="0,2!"]; 3 [pos="2,2!"];
}

Undirected graph

When the same graph is processed as an undirected graph, linearity is symmetrical, therefore the graph can be contracted.

graph G {
  2 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];
  1 -- 2 [label="1",fontsize=8];
  2 -- 1 [label="2",fontsize=8];
  2 -- 3 [label="3",fontsize=8];
  3 -- 2 [label="4",fontsize=8];
  1 [pos="0,2!"];   2 [pos="1,2!"];   3 [pos="2,2!"];
}
SELECT * FROM pgr_contractionLinear(
$$SELECT * FROM (VALUES
  (1, 1, 2, 1, 2),
  (2, 2, 3, 3, 4))
  AS edges(id,source,target,cost,reverse_cost)$$,
  directed => false);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
 e    | -1 | {2}                 |      1 |      3 |    4
(1 row)

The four edges can be replaced by one undirected edge.

  • Edge 13.

    • With cost: 4.

    • Contracted vertices in the edge: {2}.

graph G {
  1,3 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -- 3 [label="4, {2}",fontsize=8;color=red];

  1 [pos="0,2!"]; 3 [pos="2,2!"];
}

Step by step linear contraction

The linear contraction will stop when there are no more linear edges. For example from the following graph there are linear edges

digraph G {
    1, 2, 3, 4, G [fontsize=8;fixedsize=true;style=filled];
    1, 2, 3, 4 [shape=circle];
    1, 4 [color=deepskyblue];
    2, 3 [color=green];
    G [shape=tripleoctagon;width=1.5;color=deepskyblue;label = "Rest of the Graph"];
    G -> {1, 4} [dir=none, weight=1, penwidth=3];
    1 -> 2 [label="1";fontsize=8;fixedsize=true];
    2 -> 3 [label="1";fontsize=8;fixedsize=true];
    3 -> 4 [label="1";fontsize=8;fixedsize=true];
    G [pos="1,1!"];
    1 [pos="0,0!"]; 2 [pos="1,0!"]; 3 [pos="2,0!"]; 4 [pos="3,0!"];
}

Contracting vertex 3,

  • The vertex 3 is removed from the graph

  • The edges 23 and wz are removed from the graph.

  • A new edge 24 is inserted represented with red color.

digraph G {
    1, 2, 4, G [fontsize=8;fixedsize=true;style=filled];
    1, 2, 4 [shape=circle];
    1, 4 [color=deepskyblue];
    2 [color=green];
    G [shape=tripleoctagon;width=1.5;color=deepskyblue;label = "Rest of the Graph"];
    G -> {1, 4} [dir=none, weight=1, penwidth=3];
    1 -> 2 [label="1";fontsize=8;fixedsize=true];
    2 -> 4 [label="2, {3}";color=red;fontsize=8;fixedsize=true];
    G [pos="1,1!"];
    1 [pos="0,0!"]; 2 [pos="1,0!"]; 4 [pos="3,0!"];
}

Contracting vertex 2:

  • The vertex 2 is removed from the graph

  • The edges 12 and 23 are removed from the graph.

  • A new edge 13 is inserted represented with red color.

digraph G {
    1, 4, G [fontsize=8;fixedsize=true;style=filled];
    1, 4 [shape=circle];
    1, 4 [color=deepskyblue];
    G [shape=tripleoctagon;width=1.5;color=deepskyblue;label = "Rest of the Graph"];
    G -> {1, 4} [dir=none, weight=1, penwidth=3];
    1 -> 4 [label="3, {2,3}";color=red;fontsize=8;fixedsize=true]
    G [pos="1,1!"];
    1 [pos="0,0!"]; 4 [pos="3,0!"];
}

Edge 13 has the information of cost and the nodes that were contracted.

SELECT * FROM pgr_contractionLinear(
$$SELECT * FROM (VALUES
  (1, 1, 2, 1),
  (2, 2, 3, 1),
  (2, 3, 4, 1))
  AS edges(id,source,target,cost)$$);
 type | id | contracted_vertices | source | target | cost
------+----+---------------------+--------+--------+------
 e    | -1 | {2,3}               |      1 |      4 |    3
(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 edges ADD is_new BOOLEAN DEFAULT false;
ALTER TABLE
ALTER TABLE edges ADD contracted_vertices BIGINT[];
ALTER TABLE

Save results into a table.

SELECT * INTO contraction_results
FROM pgr_contractionLinear(
  'SELECT id, source, target, cost, reverse_cost FROM edges',
  directed => false);
SELECT 3

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 3

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
----+---------------
  3 | t
 15 | t
 17 | t
(3 rows)

Insert the new edges generated by pgr_contraction.

INSERT INTO edges(source, target, cost, reverse_cost, contracted_vertices, is_new)
SELECT source, target, cost, -1, contracted_vertices, true
FROM contraction_results;
INSERT 0 3

Create the contracted graph.

CREATE VIEW contracted_graph AS
WITH
vertices_in_graph AS (
  SELECT id FROM vertices WHERE NOT is_contracted
)
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

The contracted graph

SELECT * FROM contracted_graph ORDER by id;
 id | source | target | cost | reverse_cost
----+--------+--------+------+--------------
  1 |      5 |      6 |    1 |            1
  2 |      6 |     10 |   -1 |            1
  4 |      6 |      7 |    1 |            1
  5 |     10 |     11 |    1 |           -1
  8 |      7 |     11 |    1 |            1
  9 |     11 |     16 |    1 |            1
 10 |      7 |      8 |    1 |            1
 11 |     11 |     12 |    1 |           -1
 12 |      8 |     12 |    1 |           -1
 14 |      8 |      9 |    1 |            1
 17 |      2 |      4 |    1 |            1
 18 |     13 |     14 |    1 |            1
 19 |      1 |      7 |    2 |           -1
 20 |     12 |     16 |    2 |           -1
 21 |     10 |     16 |    2 |           -1
(15 rows)

graph G {
  splines=false;
  1,2,4,5,6,7,8,9,10,11,12,13,14,16 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -- 7   [label="19, (2, {3})",fontsize=8];
  12 -- 16 [label="20, (2, {17})",fontsize=8];
  10 -- 16 [label="21, (2, {15})",fontsize=8];
  5 -- 6 [label="1",fontsize=8];     6 -- 10 [label="2",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];
  8 -- 9 [label="",fontsize=8];
  2 -- 4 [label="17",fontsize=8];   13 -- 14 [label="18",fontsize=8];

  1 [pos="0,2!"];       2 [pos="0.5,3.5!"];
  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!"];
  16 [pos="4,2!"];
}

Using when departure and destination are in the contracted graph

SELECT *
FROM pgr_dijkstra('SELECT * FROM contracted_graph', 7, 16);
 seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost
-----+----------+-----------+---------+------+------+------+----------
   1 |        1 |         7 |      16 |    7 |    8 |    1 |        0
   2 |        2 |         7 |      16 |   11 |    9 |    1 |        1
   3 |        3 |         7 |      16 |   16 |   -1 |    0 |        2
(3 rows)

graph G {
  splines=false;
  1,2,4,5,6,7,8,9,10,11,12,13,14,16 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -- 7   [label="19, (2, {3})",fontsize=8];
  12 -- 16 [label="20, (2, {17})",fontsize=8];
  10 -- 16 [label="21, (2, {15})",fontsize=8];
  5 -- 6 [label="1",fontsize=8];     6 -- 10 [label="2",fontsize=8];
  6 -- 7 [label="4",fontsize=8];
  10 -- 11 [label="5",fontsize=8];
  7 -- 11 [label="8",fontsize=8;color=red];
  11 -- 16 [label="9",fontsize=8;color=red];   7 -- 8 [label="10",fontsize=8];
  11 -- 12 [label="11",fontsize=8];  8 -- 12 [label="12",fontsize=8];
  8 -- 9 [label="",fontsize=8];
  2 -- 4 [label="17",fontsize=8];   13 -- 14 [label="18",fontsize=8];

  1 [pos="0,2!"];       2 [pos="0.5,3.5!"];
  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!"];
  16 [pos="4,2!"];
}

Using when departure/destination is not in the contracted graph

SELECT * FROM pgr_dijkstra(
  'WITH in_line AS (SELECT contracted_vertices FROM edges WHERE 17 = ANY(contracted_vertices))
   SELECT id, source, target, cost, reverse_cost
   FROM edges, in_line
   WHERE source = ANY(in_line.contracted_vertices) OR target = ANY(in_line.contracted_vertices)

  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 |   19 |    2 |        0
   2 |        2 |         1 |      17 |    7 |    8 |    1 |        2
   3 |        3 |         1 |      17 |   11 |    9 |    1 |        3
   4 |        4 |         1 |      17 |   16 |   15 |    1 |        4
   5 |        5 |         1 |      17 |   17 |   -1 |    0 |        5
(5 rows)

graph G {
  17 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true];
  1,2,4,5,6,7,8,9,10,11,12,13,14,16 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true];

  1 -- 7   [label="19, (2, {3})",fontsize=8;color=red];
  12 -- 16 [label="20, (2, {17})",fontsize=8];
  10 -- 16 [label="21, (2, {15})",fontsize=8];
  5 -- 6 [label="1",fontsize=8];     6 -- 10 [label="2",fontsize=8];
  6 -- 7 [label="4",fontsize=8];
  10 -- 11 [label="5",fontsize=8];
  7 -- 11 [label="8",fontsize=8;color=red]; 12 -- 17 [label="13",fontsize=8];
  11 -- 16 [label="9",fontsize=8;color=red];   7 -- 8 [label="10",fontsize=8];
  11 -- 12 [label="11",fontsize=8];  8 -- 12 [label="12",fontsize=8];
  8 -- 9 [label="",fontsize=8]; 16 -- 17 [label="15",fontsize=8;color=red];
  2 -- 4 [label="17",fontsize=8];   13 -- 14 [label="18",fontsize=8];

  1 [pos="0,2!"];       2 [pos="0.5,3.5!"];
  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!"];
  16 [pos="4,2!"]; 17 [pos="4,3!"];
}

See Also

Indices and tables