6

I've been looking all over the place on how the .g6 or graph6 format works and I have no idea how it even works I swear it's like magic.

F?B~w

That is a graph represented in ASCII form. It can be interpreted by Wolfram Mathematica, Sage, and Maple to name a few and give us a visual. However, after hours of diving into the open source code of Sage, I cannot for the life of me figure out how they can read that as a graph.

I want to know if it is possible to search the above graph for Hamiltonian Cycles without having to convert them into Adjacency Matrices? Or if that is not possible how do we even convert it into an Adjacency Matrix?

Any help would be appreciated.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Sailanarmo
  • 1,139
  • 15
  • 41
  • 1
    http://users.cecs.anu.edu.au/~bdm/data/formats.txt – Oliver Charlesworth Jun 13 '17 at 22:20
  • @OliverCharlesworth I did see this, but this is what I am having a hard time understanding. Does it get converted into binary and then stored as an adjacency matrix from that? Is it converted to Decimal and then that is read from and converted to something that is searchable? It says, the graph is represented as N(n) R(x), but that doesn't look like the above format, nor am I really sure how to use that in a program to find all hamiltonian paths. – Sailanarmo Jun 13 '17 at 22:24

3 Answers3

7

The reference to the format description was already provided by Oliver Charlesworth, but in short the basic idea is to encode the graph size and the upper triangle of the adjacency matrix in ASCII-printable characters.

  1. From your original undirected graph, compute the adjacency matrix. This is guaranteed to be symmetric so all we care about is the upper triangle of this matrix, excluding the diagonal which will be identically zero.

  2. Next, build a bit vector of size n*(n-1)/2 by traversing the upper triangle of the matrix row by row. For example, for a 4x4 matrix the traversal would be (1,2),(1,3),(1,4),(2,3),(2,4),(3,4).

  3. Prepend your bit vector with the graph size n (as a binary word) and break the resulting bit vector into chunks of 6 bits each.

  4. Transform each 6-bit chunk into an integer in the range 63 to 126, then transform each of these to the corresponding ASCII character and concatenate them.

Note the graph6 format does not support directed or weighted graphs. I believe it was created by Brendan McKay (there's an implementation of it in the nauty source) and there are two related formats: sparse6 (for sparse graphs) and digraph6 (for directed graphs).

The digraph6 format appears to be fairly new (added in nauty 2.6) and is similar to graph6 except that in order to handle directed graphs, the format encodes the entire adjacency matrix minus the diagonal, not just the upper triangle.

saforrest
  • 196
  • 3
2

Just in case anyone is wondering: contrary to what is written in the specification and in the accepted answer, it is NOT the upper triangle that is traversed row by row, but the lower triangle.

Or equivalently, the upper matrix is traversed column by column.

So the sequence of encoded matrix entries is: (2,1), (3,1), (3,2), (4,1), (4,2), (4,3) or equivalently (1,2), (1,3), (2,3), (1,4), (2,4), (3,4).

This is correctly implemented in the answer by @broboy763.

return true
  • 7,839
  • 2
  • 19
  • 35
1

I struggled a lot with this even after reading the above answer. Here is some pseudocode and Python code for converting graph6 to an adjacency matrix and vice versa.

  1. Get adjacency matrix
  2. Take lower left diagonal matrix and append rows to string
  3. Pad on right with 0s until len is a multiple of 6
  4. Split into 6 bit pieces
  5. vertices + 63 = first char of graph6
  6. Take chr of the int each piece and add to graph6
def adjToGraph6(adjMatrix):
    elements_from_row = 0
    bin_list = ""

    # Take lower left diagonal matrix and append rows to string
    for row in adjMatrix:
        elements_from_row += 1
        if elements_from_row == 1:  # Ignore first row
            continue
        row_elements = list(map(str, row[0:elements_from_row - 1]))
        bin_list += "".join(row_elements)

    # Pad on right with 0s until len is a multiple of 6
    if len(bin_list) % 6 != 0:
        bin_list += "0" * (6 - len(bin_list) % 6)

    # Split into 6 bit pieces
    chunks = [bin_list[i:i + 6] for i in range(0, len(bin_list), 6)]

    # vertices + 63 = first char
    graph6 = chr(len(adjMatrix) + 63)

    # Take chr of the int each piece and add to graph6
    for i in chunks:
        graph6 += chr(int(i, 2) + 63)

    return graph6


def graph6ToAdj(graph6):
    # vertices + 63 = first char
    vertices = ord(graph6[0]) - 63

    bin_list = ""

    # Turn into 6 bit pieces
    for i in graph6[1:]:
        bin_list += ("{0:06b}".format(ord(i) - 63))

    adjMatrix = []

    # Unpad on right until have bottom left diag
    num_in_bot_left_diag = 0
    for i in range(vertices):
        num_in_bot_left_diag += i

    bot_left_diag = bin_list[:num_in_bot_left_diag]

    for i in range(0, vertices):
        sub_adjMatrix = [0 for i in range(vertices)]
        for j in range(i):
            sub_adjMatrix[j] = int(bin_list[0])
            bin_list = bin_list[1:]
        adjMatrix.append(sub_adjMatrix)

    addTranspose(adjMatrix)

    return vertices, adjMatrix


def addTranspose(adjMatrix):
    for j in range(len(adjMatrix)):
        for i in range(j):
            adjMatrix[i][j] = adjMatrix[j][i]


if __name__ == "__main__":
    print(adjToGraph6([
        [0, 0, 1, 0, 1],
        [0, 0, 0, 1, 0],
        [1, 0, 0, 0, 0],
        [0, 1, 0, 0, 1],
        [1, 0, 0, 1, 0],
    ]))

    print(graph6ToAdj("DQc")[0])
    for i in (graph6ToAdj("DQc")[1]):
        print(i)
broboy763
  • 11
  • 1