"""
A collection of graph recipes
"""
# Some strongly regular graphs
# I need the following because I need to control the vertex set for the Chang graphs
def LKn(n):
vxs = [(i,j) for i in [0..n-1] for j in [i+1..n-1]]
def incident(p1,p2):
return p1[0]==p2[0] or p1[0]==p2[1] or p1[1]==p2[0] or p1[1]==p2[1]
return Graph([vxs, lambda p1,p2: incident(p1,p2)])
def Paley(q):
K. = GF(q)
return Graph([K, lambda i,j: i != j and (i-j).is_square()])
# Paley49 = Paley(49)
K. = GF(49)
pows = [a^(4*i) for i in [1..12]] + [a^(4*i+1) for i in [1..12]]
# Peisert graph: arc transitive and self complementary, not Paley
Peis49 = Graph([K, lambda i,j: i != j and i-j in pows])
def cubelike(vecls): # input a list of binary vectors of length d
d = len(vecls[0])
VS = VectorSpace(GF(2),d)
return Graph([[0..(2^d-1)], lambda i,j: VS[i]-VS[j] in vecls])
def LatinSqGrf(n, fun):
vxs = [tuple([i,j]) for i in [0..n-1] for j in [0..n-1]]
return Graph([vxs, lambda t1, t2: fun(t1,t2,n)], loops=False)
def cyclicLSfun(tpl1,tpl2,n):
return (tpl1[0] == tpl2[0]) or (tpl1[1] == tpl2[1])\
or ((tpl1[0]+tpl1[1]-tpl2[0]-tpl2[1]).mod(n) == 0)
Shrikande = LatinSqGrf(4, cyclicLSfun).complement()
LS25a = LatinSqGrf(5, cyclicLSfun) # isomorphic to Paley graph on GF(25)
LS36a = LatinSqGrf(6, cyclicLSfun)
# switch on the subset sub of the vertices of G
def switch(G,sub):
H = G.copy()
rest = Set(H.vertices()).difference(Set(sub))
for u in sub:
for v in rest:
if H.has_edge(u,v): H.delete_edge(u,v)
else: H.add_edge(u,v)
return H
def Schlaefli():
L8 = LK8.copy()
S0 = switch(L8, L8.neighbors(L8.vertices()[0]))
S0.delete_vertex((0,1))
return S0
# Chang graphs on 28 vertices
# switch on C8, 2C4, C3+C5
#### This is not working properly ####
LK8 = (graphs.CompleteGraph(8)).line_graph()
C8 = LK8.subgraph(vertices = [LK8.vertices()[i] for i in [0, 7, 13, 18, 22, 25, 27, 6]])
ChangC8 = switch(LK8,C8)
C44 = LK8.subgraph(vertices = [LK8.vertices()[i] for i in [0, 7, 13, 2, 22, 25, 27, 24]])
ChangC44 = switch(LK8,C44)
C35 = LK8.subgraph(vertices = [LK8.vertices()[i] for i in [0, 7, 1, 18, 22, 25, 27, 21]])
ChangC35 = switch(LK8,C35)
def HoffmanSingleton():
hsvxs = [(x,y,i) for x in GF(5) for y in GF(5) for i in [0,1]]
def hosi(s,t):
if s[2] == t[2] == 0:
return (s[0]==t[0]) and (s[1]-t[1] in [1,4])
elif s[2] == t[2] == 1:
return (s[0]==t[0]) and (s[1]-t[1] in [2,3])
elif s[2] == 0:
return s[1] == t[0]*s[0]+t[1]
else:
return t[1] == s[0]*t[0]+s[1]
return Graph([hsvxs, hosi])
# Note: this calls HoffmanSingleton()
# vertices are cocliques of size 15 in HoffmanSingleton - perhaps they should be compacted?
def HigmanSims():
hs = HoffmanSingleton()
clqs = (hs.complement()).cliques_maximum()
alqs = [Set(cl) for cl in clqs]
return Graph([alqs, lambda s, t: len(s.intersection(t)) in [0,8]])
hisi = HigmanSims()
vxs_m22 = [v for v in hisi.vertices() if hisi.distance(v,hisi.vertices()[0])==2]
vxs_gw = [v for v in hisi.vertices() if hisi.distance(v,hisi.vertices()[0])==2 and\
hisi.distance(v,hisi.vertices()[2])==2]
# the M_22 graph on 77 vertices
M22 = hisi.subgraph( vertices = vxs_m22)
# the Gewirtz graph on 56 vertices
Gewirtz = hisi.subgraph( vertices=vxs_gw)
# another construction of M22
xs
#########################################################################################
## Now some distance-regular graphs:
# J(v,k,r), J(v,k), Kneser(v,k)
# the first version is much slower: too many calls to Set()
# def gen_johnson2(v,k,r):
# combs = Combinations([0..v-1], k)
# return Graph([[0..binomial(v,k)-1], lambda i,j:\
# len(set(combs[i]).intersection(set(combs[j])))==r])
def gen_johnson(v,k,r):
combs = [Set(it) for it in Combinations([0..v-1], k)]
return Graph([[0..binomial(v,k)-1], lambda i,j: len((combs[i]).intersection(combs[j]))==r])
def Johnson(v,k):
return gen_johnson(v,k,k-1)
def Kneser(v,k):
return gen_johnson(v,k,0)
def Coxeter():
kk = Kneser(7,3)
clqs = (kk.complement()).cliques()
dlqs = filter(lambda cl: len(cl)==7,clqs)
cl0 = dlqs[0]
sub_vs = [vx for vx in kk.vertices() if vx not in cl0]
Cox = kk.subgraph(sub_vs)
Cox.relabel()
return(Cox)
#######################################################################################
nbrs=[[1,2,3,11], [0,2,4,5], [0,1,3,9], [0,2,6,7], [1,5,7,8], [1,4,6,8],\
[3,5,7,10],[3,4,6,10],[4,5,9,11],[2,8,10,11],[6,7,9,11],[0,8,9,10]]
d = dict(zip([0..11],nbrs))
# probably the smallest graph that is walk regular but not vertex transitive
G = Graph(dict(zip([0..11],nbrs)))
H = (graphs.CubeGraph(3)).line_graph()
# G and H are cospectral:
# H.characteristic_polynomial().factor()
# (x - 4) * (x - 2)^3 * x^3 * (x + 2)^5
# G.characteristic_polynomial().factor()
# (x - 4) * (x - 2)^3 * x^3 * (x + 2)^5
#
# construct a Cayley graph for Sym(4)
# P = PermutationGroup([[(1,2)], [(2,3)], [(3,4)]])
# gp = P.cayley_graph()
# gq = gp.to_undirected()
# gq.spectrum()
ds1 = [(1,0,0), (4,0,0),\
(0,1,1), (0,4,1), (2,0,1), (4,2,1), (4,3,1),\
(0,1,2), (0,4,2), (1,2,2), (1,3,2), (2,0,2), (3,1,2), (3,2,2),\
(3,3,2), (3,4,2), (4,0,2),\
(0,1,3), (0,4,3), (1,0,3), (2,2,3), (2,3,3)]
H1 = PermutationGroup([[(1,2,3,4,5)],[(6,7,8,9,10)],[(2,3,5,4)]])
y,z,x = H1.gens()
C = [ x^it[0]*y^it[1]*z^it[2] for it in ds1]
HS = H1.cayley_graph(generators=C).to_undirected() # HiSi again
H2 = PermutationGroup([[(1,2,3,4,5)],[(6,7,8,9,10)],[(2,3,5,4),(7,10),(9,8)]])
b,c,a = H2.gens()
D = [ a^it[0]*b^it[1]*c^it[2] for it in ds1]
H3 = PermutationGroup([[(1,2,3,4,5)],[(6,7,8,9,10)],[(2,4,5,3),(7,9,10,8)]])
y,z,x = H3.gens()
ds3 = [(1,2,0),(2,1,0),(2,2,0),(3,3,0),(3,4,0),(4,3,0),\
(0,4,1),(1,1,1),(2,2,1),(2,3,1),(2,4,1),(3,2,1),(3,3,1),(4,0,1),(4,2,1),(4,4,1),\
(0,1,2),(0,2,2),(0,3,2),(1,0,2),(1,3,2),(2,0,2),(2,2,2),(3,0,2),(3,1,2),(3,3,2),\
(0,3,3),(1,1,3),(1,4,3),(2,2,3),(3,0,3),(3,3,3),(3,4,3),(4,1,3),(4,3,3),(4,4,3)]
E = [ x^it[0]*y^it[1]*z^it[2] for it in ds3]
HW = H3.cayley_graph(generators=E)# Hall-Wales
##
# generalized quadrangle
##
def beta(u,v):
return u[0]*v[2]+u[1]*v[3] -u[2]*v[0] -u[3]*v[1]
V = VectorSpace( GF(3), 4)
Points = [u[1] for u in V.subspaces(1)]
Lines = [sub for sub in V.subspaces(2)\
if beta(sub.matrix()[0],sub.matrix()[1])==0]
W3 = Graph([range(len(Points)),\
lambda i,j: beta(Points[i],Points[j])==0], loops=False)
W3d = Graph( [range(len(Lines)), lambda i,j: i != j\
and det( Lines[i].matrix().stack(Lines[j].matrix())) == 0])
##
# McLaughlin graph
##
def McL():
C = codes.ExtendedBinaryGolayCode()
D = C.punctured([0])
words = [ Set(it.support()) for it in D if hamming_weight(it)==7]
MG = Graph( [words, lambda a,b: len(a.intersection(b))==1])
MM = MG.copy()
MM.add_vertices([0..22])
edges = [ (i,a) for i in [0..22] for a in words if i not in a]
MM.add_edges( edges)
McL = switch( MM, MM[0])
McL.delete_vertex(0)
return McL
#############
## cfi graphs
#############
def cfi( grf):
VA = [(v,w,i) for v in grf.vertices() for w in grf[v] for i in [0,1]]
VM = [(v,evns) for v in grf.vertices() for evns in Set(grf[v]).subsets()\
if len(evns) % 2 == 0]
VAVA = [(x,y) for x in VA for y in VA if x[2]==y[2] and x[0]==y[1] and x[1]==y[0]]
VAVM = [(x,z) for x in VA for z in VM if x[0]==z[0]\
and ((x[2]==0 and x[1] in z[1]) or (x[2]==1 and x[1] not in z[1]))]
G = Graph()
G.add_vertices(VA+VM)
G.add_edges(VAVA+VAVM)
return G
def twist( cfigrf, i, j):
H = cfigrf.copy()
H.delete_edges( [((i,j,0),(j,i,0)), ((i,j,1),(j,i,1))] )
H.add_edges( [((i,j,0),(j,i,1)), ((i,j,1),(j,i,0))] )
return H
######
# get the symmetric square of a graph
def symm_sq( grf):
P=list(Subsets(grf.vertices(),2))
n=len(P)
return Graph([P, lambda pa,pb: len(pa.symmetric_difference(pb))==2 and grf.has_edge(pa.symmetric_difference(pb))])