Thank you both for the illuminating comments, they helped getting the automorphism working!
Now that it is working, I have to admit that I am not quite sure how to use this to achieve what I want thoughā¦
As mentioned earlier I have diffrent types of particles on a 2D grid, and I want to generate a unique canonical string representation of the complete graph for each of these particle systems.
Below I show 6 simple examples, where the first 4 should all be isomorph and the last two should be different.
coordinates = [[0,0],[-1,0],[1,0],[-1,1]]
node_colors = [0,1,1,1]
coordinates = [[0,0],[1,0],[-1,0],[-1,1]] #Just a simple permutation of nodes, so should be isomorph
node_colors = [0,1,1,1]
coordinates = [[1,1],[0,1],[2,1],[0,2]] #Just a simple translation of all nodes, so should be isomorph
node_colors = [0,1,1,1]
coordinates = [[0,0],[-1,0],[1,0],[1,1]] # This is a mirroring so should also be isomorph
node_colors = [0,1,1,1]
coordinates = [[0,0],[-2,0],[2,0],[-2,2]] # All distances are now twice as long, so this should be a new graph
node_colors = [0,1,1,1]
coordinates = [[0,0],[-1,0],[1,0],[-1,1]] # Different node colors, so different graph
node_colors = [3,1,1,1]
I can compute the automorphism of each of these using the code below, as well as the canonical representation (though that does not take into account the edge_colors, which I guess is irrelevant?):
import torch
import numpy as np
import igraph as ig
def dist_matrix(x,precision=3):
x = np.asarray(x)
dist = np.sqrt((x**2).sum(axis=1)[:, None] - 2 * x.dot(x.transpose()) + ((x**2).sum(axis=1)[None, :]))
return np.round(dist,decimals=precision)
def generate_all_possible_edge_len_dict(n,precision=3):
x = torch.arange(n).repeat(n)
y = torch.arange(n).repeat_interleave(n)
xy = torch.stack((x,y),dim=1).numpy()
D = dist_matrix(xy,precision)
all_possible_edge_lens = np.unique(D)
val = np.arange(len(all_possible_edge_lens))
edge_color_lookup = dict(zip(all_possible_edge_lens, val))
return edge_color_lookup
edge_color_lookup = generate_all_possible_edge_len_dict(5)#.tolist()
def generate_edges_and_edge_colors(coordinates):
n = len(coordinates)
D = dist_matrix(coordinates)
edges = torch.combinations(torch.arange(n), 2).numpy()
edge_distances = [D[edge[0], edge[1]] for edge in edges]
edge_colors = [edge_color_lookup[edge] for edge in edge_distances]
return edges.tolist(), edge_colors
coordinates = [[0,0],[-1,0],[1,0],[-1,1]]
node_colors = [0,1,1,1]
edges, edge_colors = generate_edges_and_edge_colors(coordinates)
g = ig.Graph(n=len(coordinates), edges=edges,directed=False)
print(g.get_automorphisms_vf2(color=node_colors,edge_color=edge_colors))
permute = g.canonical_permutation(color=node_colors)
canonical_representation=g.permute_vertices(permute)
Given the automorphism and the canonical representation I am sure I should be able to build some uniquely identifying string expression for these different kinds of graphs, but I am not really sure how to go about this? Is there any function/method in igraph for something like this? or does anyone have any recommendations about how to build such a thing?
Iām imagining the string would be some kind of unique ordering of the nodes, their colors as well as the edge_colors. Something like:
permute = g.canonical_permutation(color=node_colors)
canonical_representation=g.permute_vertices(permute)
coordinates_c = [coordinates[p] for p in permute]
edges_c = canonical_representation.get_edgelist()
node_colors_c = [node_colors[p] for p in permute]
_, edge_colors_c = generate_edges_and_edge_colors(coordinates_c)
rep = f"node_colors={node_colors_c},edges={edges_c},edge_colors={edge_colors_c}"
which might look like:
ānode_colors=[0, 1, 1, 1],edges=[(0, 3), (0, 2), (0, 1), (2, 3), (1, 3), (1, 2)],edge_colors=[2, 1, 1, 4, 1, 3]ā
However, this does not use the automorphism method at all, so I think there must be something wrong with it?