pgr_contractionLinear - Proposed - pgRouting Manual (3.8)
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.
-
Signatures
[directed,
forbidden]
(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!"];
}](images/graphviz-249b3ec8d7cd844e48abfa82a9d5121eafdb3246.png)
Parameters
Parameter |
Type |
Description |
---|---|---|
|
Edges SQL as described below. |
|
contraction Order |
|
Ordered contraction operations.
|
Optional parameters
Column |
Type |
Default |
Description |
---|---|---|---|
|
|
|
|
Contraction optional parameters
Column |
Type |
Default |
Description |
---|---|---|---|
|
|
Empty |
Identifiers of vertices forbidden for contraction. |
|
|
\(1\) |
Number of times the contraction operations on
|
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
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!"];
}](images/graphviz-93d86210e6e87060c533d020412da84b33f6cfa3.png)
![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!"];
}](images/graphviz-696bc15bbec94b0747f2de898c3a7f594133cf6d.png)
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!"];
}](images/graphviz-764f2c596d8081e8b639553d3eb670dd29e13eef.png)
![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!"];
}](images/graphviz-130cddbfe2305ecbdcc8eccad91d0370c5aa75d0.png)
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!"];
}](images/graphviz-41179398a6e44ad375b2b4d44ae815720427fa14.png)
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!"];
}](images/graphviz-fd3ee2a4fd315478a6823b77b23bb8bb961f128f.png)
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 \(1 - 3\) .
-
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!"];
}](images/graphviz-bf78c995ae58c5a0f886631556ad203803a07ef7.png)
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!"];
}](images/graphviz-3746f02b90d7092fb27548d5c395ad9369cc629d.png)
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 \(1 - 3\) .
-
With cost: \(4\) .
-
Contracted vertices in the edge: \(\{2\}\) .
-
-
Edge \(3 - 1\) .
-
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!"];
}](images/graphviz-bcfbe65fbcc43213dce7fa15de9627ba78e17c51.png)
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!"];
}](images/graphviz-3bdb379cd6a4acbe75c1b38505fadf7555821a49.png)
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 \(1 - 3\) .
-
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!"];
}](images/graphviz-9a8776a854b6651bfcb28fc2482ce979c72ce1d4.png)
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!"];
}](images/graphviz-096741a0068557b4dd13ada8e0a3348b23b06957.png)
Contracting vertex \(3\) ,
-
The vertex \(3\) is removed from the graph
-
The edges \(2 \rightarrow 3\) and \(w \rightarrow z\) are removed from the graph.
-
A new edge \(2 \rightarrow 4\) 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!"];
}](images/graphviz-af961067ccf49cfa971c5542e58cc23f9785c0f0.png)
Contracting vertex \(2\) :
-
The vertex \(2\) is removed from the graph
-
The edges \(1 \rightarrow 2\) and \(2 \rightarrow 3\) are removed from the graph.
-
A new edge \(1 \rightarrow 3\) 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!"];
}](images/graphviz-cebb70cdb2d798d311f379a035a50aff266e1da0.png)
Edge \(1 \rightarrow 3\) 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!"];
}](images/graphviz-b9066c51369be6253decbc48d5ac45d0149aa2ed.png)
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!"];
}](images/graphviz-d08c93375d34a312cd973c391eae8cf89f707705.png)
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!"];
}](images/graphviz-444f86b4008dbeec7a252d5bb7c5a7e7f9983efa.png)
See Also
Indices and tables