Versiones soportadas: latest (3.8) main dev

pgr_contractionLinear - Proposed

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

Disponibilidad

  • Version 3.8.0

    • New proposed function.

Descripción

La contracción reduce el tamaño del grafo eliminando algunos de los vértices y aristas, también por ejemplo, podría agregar aristas que representan una secuencia de aristas originales disminuyendo el tiempo total y el espacio utilizados en los algoritmos de grafo.

Las características principales son:

  • El proceso se realiza sólo en aristas con costos positivos.

  • 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.

  • Los valores devueltos se ordenan de la siguiente manera:

    • 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

Firmas

pgr_contractionLinear(Edges SQL, [options])
options: [directed, forbidden]
Regresa conjunto de (type, id, contracted_vertices, source, target, cost)
Ejemplo:

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!"];
}

Parámetros

Parámetro

Tipo

Descripción

SQL de aristas

TEXT

SQL de aristas descritas más adelante.

Orden de contracciones

ARRAY[ ANY-INTEGER ]

Operaciones de contracción ordenadas.

  • 1 = Contracción sin salida

  • 2 - Contracción lineal

Parámetros opcionales

Columna

Tipo

x Defecto

Descripción

directed

BOOLEAN

true

  • Cuando true el gráfo se considera Dirigido

  • Cuando false el gráfo se considera No Dirigido.

Parámetros opcionales de Contracción

Columna

Tipo

x Defecto

Descripción

forbidden

ARRAY[ ANY-INTEGER ]

vacío

Identificadores de vértices prohibidos para contracción.

cycles

INTEGER

1

Número de veces que se realizarán las operaciones de contracción en el orden contraction_order.

Consultas Internas

SQL aristas

Columna

Tipo

x Defecto

Descripción

id

ENTEROS

Identificador de la arista.

source

ENTEROS

Identificador del primer vértice de la arista.

target

ENTEROS

Identificador del segundo vértice de la arista.

cost

FLOTANTES

Peso de la arista (source, target)

reverse_cost

FLOTANTES

-1

Peso de la arista (target, source)

  • Cuando negativo: la arista (target, source) no existe, por lo tanto no es parte del grafo.

Donde:

ENTEROS:

SMALLINT, INTEGER, BIGINT

FLOTANTES:

SMALLINT, INTEGER, BIGINT, REAL, FLOAT

Columnas de resultados

Regresa conjunto de (type, id, contracted_vertices, source, target, cost)

La función devuelve una sola fila. Las columnas de la fila son:

Columna

Tipo

Descripción

type

TEXT

Value = e indicating the row is an edge.

id

BIGINT

A pseudo id of the edge.

  • Todos los números de esta columna son “”DISTINTOS””

  • Disminución de la secuencia a partir de -1.

contracted_vertices

ARRAY[BIGINT]

Arreglo de identificadores de vértices contraídos.

source

BIGINT

Identificador del vértice de origen de la arista actual.

target

BIGINT

Identificador del vértice destino de la arista actual.

cost

FLOAT

Weight of the current edge.

Ejemplos Adicionales

Bordes lineales

Grafo no dirigido

A node connects two (or more) linear edges when

  • El número de vértices adyacentes es 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!"];
}

En el caso de un grafo dirigido, un nodo se considera lineal cuando

  • El número de vértices adyacentes es 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

Grafo dirigido

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)

Grafo no dirigido

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!"];
}

La linealidad es simétrica

Grafo dirigido

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!"];
}

Grafo no dirigido

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

Usar la columna is_contracted para indicar los vértices contraídos.

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)

Inserte las nuevas aristas generadas por 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

El grafo contraído

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!"];
}

Ver también

Índices y tablas