Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Path: gt.sage
Views: 190
1
#GRAPH INVARIANTS
2
3
def barrus_bound(g):
4
"""
5
returns n - barrus q
6
defined in: Barrus, Michael D. "Havel–Hakimi residues of unigraphs." Information Processing Letters 112.1 (2012): 44-48.
7
sage: barrus_bound(k4):
8
1
9
2
10
"""
11
return g.order() - barrus_q(g)
12
13
def distinct_degrees(g):
14
"""
15
returns the number of distinct degrees of a graph
16
sage: distinct_degrees(p4)
17
2
18
sage: distinct_degrees(k4)
19
1
20
"""
21
return len(set(g.degree()))
22
23
#inspired by the Friendship Theorem
24
def common_neighbors(g,v,w):
25
"""
26
returns the Set of common neighbors of v and w in graph g
27
sage: common_neighbors(p4,0,3)
28
{}
29
sage: common_neighbors(p4,0,2)
30
{1}
31
"""
32
Nv = Set(g.neighbors(v))
33
Nw = Set(g.neighbors(w))
34
return Nv.intersection(Nw)
35
36
def max_common_neighbors(g):
37
"""
38
returns the maximum number of common neighbors of any pair of distinct vertices in g
39
sage: max_common_neighbors(p4)
40
1
41
sage: max_common_neighbors(k4)
42
2
43
"""
44
max = 0
45
V = g.vertices()
46
n = g.order()
47
for i in range(n):
48
for j in range(n):
49
if i < j:
50
temp = len(common_neighbors(g, V[i], V[j]))
51
if temp > max:
52
max = temp
53
return max
54
55
def min_common_neighbors(g):
56
"""
57
returns the minimum number of common neighbors of any pair of distinct vertices in g,
58
which is necessarily 0 for disconnected graphs
59
sage: min_common_neighbors(p4)
60
0
61
sage: min_common_neighbors(k4)
62
2
63
"""
64
n = g.order()
65
min = n
66
V = g.vertices()
67
for i in range(n):
68
for j in range(n):
69
if i < j:
70
temp = len(common_neighbors(g, V[i], V[j]))
71
#if temp == 0:
72
#print "i={}, j={}".format(i,j)
73
if temp < min:
74
min = temp
75
return min
76
77
def mean_common_neighbors(g):
78
"""
79
returns the average number of common neighbors of any pair of distinct vertices in g
80
sage: mean_common_neighbors(p4)
81
1/3
82
sage: mean_common_neighbors(k4)
83
2
84
"""
85
V = g.vertices()
86
n = g.order()
87
sum = 0
88
for i in range(n):
89
for j in range(n):
90
if i < j:
91
sum += len(common_neighbors(g, V[i], V[j]))
92
return 2*sum/(n*(n-1))
93
94
def domination_number(g):
95
"""
96
Returns the domination number of the graph g, i.e., the size of a maximum
97
dominating set.
98
99
A complete graph is dominated by any of its vertices::
100
101
sage: domination_number(graphs.CompleteGraph(5))
102
1
103
104
A star graph is dominated by its central vertex::
105
106
sage: domination_number(graphs.StarGraph(5))
107
1
108
109
The domination number of a cycle of length n is the ceil of n/3.
110
111
sage: domination_number(graphs.CycleGraph(5))
112
2
113
"""
114
return g.dominating_set(value_only=True)
115
116
def min_degree(g):
117
"""
118
Returns the minimum of all degrees of the graph g.
119
120
sage: min_degree(graphs.CompleteGraph(5))
121
4
122
sage: min_degree(graphs.CycleGraph(5))
123
2
124
sage: min_degree(graphs.StarGraph(5))
125
1
126
sage: min_degree(graphs.CompleteBipartiteGraph(3,5))
127
3
128
"""
129
return min(g.degree())
130
131
def max_degree(g):
132
"""
133
Returns the maximum of all degrees of the graph g.
134
135
sage: max_degree(graphs.CompleteGraph(5))
136
4
137
sage: max_degree(graphs.CycleGraph(5))
138
2
139
sage: max_degree(graphs.StarGraph(5))
140
5
141
sage: max_degree(graphs.CompleteBipartiteGraph(3,5))
142
5
143
"""
144
return max(g.degree())
145
146
def eulerian_faces(g):
147
"""
148
Returns 2 - order + size, which is the number of faces if the graph is planar,
149
a consequence of Euler's Formula
150
151
sage: eulerian_faces(graphs.CycleGraph(5))
152
2
153
sage: eulerian_faces(graphs.DodecahedralGraph())
154
12
155
"""
156
n = g.order()
157
m = g.size()
158
return 2 - n + m
159
160
def barrus_q(g):
161
"""
162
If the degrees sequence is in non-increasing order, with index starting at 1,
163
barrus_q = max(k:d_k >= k)
164
165
Defined by M. Barrus in "Havel-Hakimi Residues of Unigraphs", 2012
166
167
sage: barrus_q(graphs.CompleteGraph(5))
168
4
169
sage: barrus_q(graphs.StarGraph(3))
170
1
171
172
"""
173
Degrees = g.degree()
174
Degrees.sort()
175
Degrees.reverse()
176
return max(k for k in range(g.order()) if Degrees[k] >= (k+1)) + 1
177
178
def matching_number(g):
179
"""
180
Returns the matching number of the graph g, i.e., the size of a maximum
181
matching. A matching is a set of independent edges.
182
183
sage: matching_number(graphs.CompleteGraph(5))
184
2
185
sage: matching_number(graphs.CycleGraph(5))
186
2
187
sage: matching_number(graphs.StarGraph(5))
188
1
189
sage: matching_number(graphs.CompleteBipartiteGraph(3,5))
190
3
191
"""
192
return int(g.matching(value_only=True, use_edge_labels=False))
193
194
def residue(g):
195
seq = g.degree_sequence()
196
197
while seq[0] > 0:
198
d = seq.pop(0)
199
seq[:d] = [k-1 for k in seq[:d]]
200
seq.sort(reverse=True)
201
202
return len(seq)
203
204
def annihilation_number(g):
205
seq = sorted(g.degree())
206
207
a = 0
208
while sum(seq[:a+1]) <= sum(seq[a+1:]):
209
a += 1
210
211
return a
212
213
def fractional_alpha(g):
214
if len(g.vertices()) == 0:
215
return 0
216
p = MixedIntegerLinearProgram(maximization=True)
217
x = p.new_variable(nonnegative=True)
218
p.set_objective(sum(x[v] for v in g.vertices()))
219
220
for v in g.vertices():
221
p.add_constraint(x[v], max=1)
222
223
for (u,v) in g.edge_iterator(labels=False):
224
p.add_constraint(x[u] + x[v], max=1)
225
226
return p.solve()
227
228
def fractional_covering(g):
229
if len(g.vertices()) == 0:
230
return 0
231
p = MixedIntegerLinearProgram(maximization=False)
232
x = p.new_variable(nonnegative=True)
233
p.set_objective(sum(x[v] for v in g.vertices()))
234
235
for v in g.vertices():
236
p.add_constraint(x[v], min=1)
237
238
for (u,v) in g.edge_iterator(labels=False):
239
p.add_constraint(x[u] + x[v], min=1)
240
241
return p.solve()
242
243
244
245
def cvetkovic(g):
246
eigenvalues = g.spectrum()
247
positive = 0
248
negative = 0
249
zero = 0
250
for e in eigenvalues:
251
if e > 0:
252
positive += 1
253
elif e < 0:
254
negative += 1
255
else:
256
zero += 1
257
258
return zero + min([positive, negative])
259
260
def cycle_space_dimension(g):
261
return g.size()-g.order()+g.connected_components_number()
262
263
def card_center(g):
264
return len(g.center())
265
266
def cycle_space_dimension(g):
267
return g.size()-g.order()+g.connected_components_number()
268
269
def card_periphery(g):
270
return len(g.periphery())
271
272
def max_eigenvalue(g):
273
return max(g.adjacency_matrix().change_ring(RDF).eigenvalues())
274
275
def resistance_distance_matrix(g):
276
L = g.laplacian_matrix()
277
n = g.order()
278
J = ones_matrix(n,n)
279
temp = L+(1.0/n)*J
280
X = temp.inverse()
281
R = (1.0)*ones_matrix(n,n)
282
for i in range(n):
283
for j in range(n):
284
R[i,j] = X[i,i] + X[j,j] - 2*X[i,j]
285
return R
286
287
288
def kirchhoff_index(g):
289
R = resistance_distance_matrix(g)
290
return .5*sum(sum(R))
291
292
def largest_singular_value(g):
293
A = matrix(RDF,g.adjacency_matrix())
294
svd = A.SVD()
295
sigma = svd[1]
296
return sigma[0,0]
297
298
def independence_number(g):
299
return g.independent_set(value_only=True)
300
301
302
def chromatic_index(g):
303
if g.size() == 0:
304
return 0
305
import sage.graphs.graph_coloring
306
return sage.graphs.graph_coloring.edge_coloring(g, value_only=True)
307
308
309
def chromatic_num(g):
310
return g.chromatic_number()
311
312
def card_max_cut(g):
313
return g.max_cut(value_only=True)
314
315
316
def clique_covering_number(g):
317
# Finding the chromatic number of the complement of a fullerene
318
# is extremely slow, even when using MILP as the algorithm.
319
# Therefore we check to see if the graph is triangle-free.
320
# If it is, then the clique covering number is equal to the
321
# number of vertices minus the size of a maximum matching.
322
if g.is_triangle_free():
323
return g.order() - matching_number(g)
324
gc = g.complement()
325
return gc.chromatic_number(algorithm="MILP")
326
327
def welsh_powell(g):
328
n= g.order()
329
D = g.degree()
330
D.sort(reverse=True)
331
mx = 0
332
for i in range(n):
333
temp = min({i,D[i]})
334
if temp > mx:
335
mx = temp
336
return mx + 1
337
338
#outputs upper bound from Brooks Theorem: returns Delta + 1 for complete and odd cycles
339
def brooks(g):
340
Delta = max(g.degree())
341
delta = min(g.degree())
342
n = g.order()
343
if is_complete(g):
344
return Delta + 1
345
elif n%2 == 1 and g.is_connected() and Delta == 2 and delta == 2: #same as if all degrees are 2
346
return Delta + 1
347
else:
348
return Delta
349
350
#wilf's upper bound for chromatic number
351
def wilf(g):
352
return max_eigenvalue(g) + 1
353
354
def n_over_alpha(g):
355
n = g.order() + 0.0
356
return n/independence_number(g)
357
358
def median_degree(g):
359
return median(g.degree())
360
361
362
#a measure of irregularity
363
def different_degrees(g):
364
return len(set(g.degree()))
365
366
def szekeres_wilf(g):
367
#removes a vertex, if possible, of degree <= i
368
def remove_vertex_of_degree(gc,i):
369
Dc = gc.degree()
370
V = gc.vertices()
371
#print "Dc is %s, V is %s" %(Dc,V)
372
mind = min(Dc)
373
#print "mind is %s" %mind
374
if mind <= i:
375
376
ind = Dc.index(mind)
377
#print "ind is %s, vertex is %s" %(ind,V[ind])
378
return gc.delete_vertex(V[ind])
379
else:
380
return gc
381
D = g.degree()
382
delta = min(D)
383
Delta = max(D)
384
for i in range(delta,Delta+1):
385
gc = copy(g)
386
value = g.order() + 1
387
while gc.size() > 0 and gc.order() < value:
388
#gc.show()
389
value = gc.order()
390
remove_vertex_of_degree(gc,i)
391
if gc.size() == 0:
392
return i + 1
393
394
def average_vertex_temperature(g):
395
D = g.degree()
396
n = g.order()
397
return sum([D[i]/(n-D[i]+0.0) for i in range(n)])/n
398
399
def sum_temperatures(g):
400
D = g.degree()
401
n = g.order()
402
return sum([D[i]/(n-D[i]+0.0) for i in range(n)])
403
404
def randic(g):
405
D = g.degree()
406
V = g.vertices()
407
if min(D) == 0:
408
return oo
409
sum = 0
410
for e in g.edges():
411
v = e[0]
412
i = V.index(v)
413
w = e[1]
414
j = V.index(w)
415
sum += 1.0/(D[i]*D[j])**0.5
416
return sum
417
418
#a solution of the invariant interpolation problem for upper bound of chromatic number for c8chords
419
#all upper bounds in theory have value at least 3 for c8chords
420
#returns 2 for bipartite graphs, order for non-bipartite
421
def bipartite_chromatic(g):
422
if g.is_bipartite():
423
return 2
424
else:
425
return g.order()
426
427
#a very good lower bound for alpha
428
def max_even_minus_even_horizontal(g):
429
"""
430
finds def max_even_minus_even_horizontal for each component and adds them up.
431
"""
432
mx_even=0
433
Gcomps=g.connected_components_subgraphs()
434
435
while Gcomps != []:
436
H=Gcomps.pop()
437
temp=max_even_minus_even_horizontal_component(H)
438
mx_even+=temp
439
#print "temp = {}, mx_even = {}".format(temp,mx_even)
440
441
return mx_even
442
443
def max_even_minus_even_horizontal_component(g):
444
"""
445
for each vertex v, find the number of vertices at even distance from v,
446
and substract the number of edges induced by these vertices.
447
this number is a lower bound for independence number.
448
take the max. returns 0 if graph is not connected
449
"""
450
if g.is_connected()==False:
451
return 0
452
453
distances = g.distance_all_pairs()
454
mx=0
455
n=g.order()
456
for v in g.vertices():
457
Even=[]
458
for w in g.vertices():
459
if distances[v][w]%2==0:
460
Even.append(w)
461
462
#print len(Even), len(g.subgraph(Even).edges())
463
l=len(Even)-len(g.subgraph(Even).edges())
464
if l>mx:
465
mx=l
466
return mx
467
468
def median_degree(g):
469
return median(g.degree())
470
471
def laplacian_energy(g):
472
L = g.laplacian_matrix().change_ring(RDF).eigenvalues()
473
Ls = [1/lam**2 for lam in L if lam > 0]
474
return 1 + sum(Ls)
475
476
#sum of the positive eigenvalues of a graph
477
def gutman_energy(g):
478
L = g.adjacency_matrix().change_ring(RDF).eigenvalues()
479
Ls = [lam for lam in L if lam > 0]
480
return sum(Ls)
481
482
#the second smallest eigenvalue of the Laplacian matrix of a graph, also called the "algebraic connectivity" - the smallest should be 0
483
def fiedler(g):
484
L = g.laplacian_matrix().change_ring(RDF).eigenvalues()
485
L.sort()
486
return L[1]
487
488
def average_degree(g):
489
return mean(g.degree())
490
491
def degree_variance(g):
492
mu = mean(g.degree())
493
s = sum((x-mu)**2 for x in g.degree())
494
return s/g.order()
495
496
def number_of_triangles(g):
497
E = g.edges()
498
D = g.distance_all_pairs()
499
total = 0
500
for e in E:
501
v = e[0]
502
w = e[1]
503
S = [u for u in g.vertices() if D[u][v] == 1 and D[u][w] == 1]
504
total += len(S)
505
return total/3
506
507
def graph_rank(g):
508
return g.adjacency_matrix().rank()
509
510
def inverse_degree(g):
511
return sum([(1.0/d) for d in g.degree() if d!= 0])
512
513
def card_positive_eigenvalues(g):
514
return len([lam for lam in g.adjacency_matrix().eigenvalues() if lam > 0])
515
516
def card_zero_eigenvalues(g):
517
return len([lam for lam in g.adjacency_matrix().eigenvalues() if lam == 0])
518
519
def card_negative_eigenvalues(g):
520
return len([lam for lam in g.adjacency_matrix().eigenvalues() if lam < 0])
521
522
def card_cut_vertices(g):
523
return len((g.blocks_and_cut_vertices())[1])
524
525
def independent_dominating_set_number(g):
526
return g.dominating_set(value_only=True, independent=True)
527
528
#returns average of distances between *distinct* vertices, return infinity is graph is not connected
529
def average_distance(g):
530
if not g.is_connected():
531
return Infinity
532
V = g.vertices()
533
D = g.distance_all_pairs()
534
n = g.order()
535
return sum([D[v][w] for v in V for w in V if v != w])/(n*(n-1))
536
537
#return number of leafs or pendants
538
def card_pendants(g):
539
return sum([x for x in g.degree() if x == 1])
540
541
542
def vertex_con(g):
543
return g.vertex_connectivity()
544
545
546
def edge_con(g):
547
return g.edge_connectivity()
548
549
#returns number of bridges in graph
550
def card_bridges(g):
551
gs = g.strong_orientation()
552
bridges = []
553
for scc in gs.strongly_connected_components():
554
bridges.extend(gs.edge_boundary(scc))
555
return len(bridges)
556
557
#upper bound for the domination number
558
def alon_spencer(g):
559
delta = min(g.degree())
560
n = g.order()
561
return n*((1+log(delta + 1.0)/(delta + 1)))
562
563
#lower bound for residue and, hence, independence number
564
def caro_wei(g):
565
return sum([1.0/(d + 1) for d in g.degree()])
566
567
#equals 2*size, the 1st theorem of graph theory
568
def degree_sum(g):
569
return sum(g.degree())
570
571
#smallest sum of degrees of non-adjacent degrees, invariant in ore condition for hamiltonicity
572
#default for complete graph?
573
def sigma_2(g):
574
if g.size() == g.order()*(g.order()-1)/2:
575
return g.order()
576
Dist = g.distance_all_pairs()
577
return min(g.degree(v) + g.degree(w) for v in g for w in g if Dist[v][w] > 1)
578
579
#cardinality of the automorphism group of the graph
580
def order_automorphism_group(g):
581
return g.automorphism_group(return_group=False, order=True)
582
583
#in sufficient condition for graphs where vizing's independence theorem holds
584
def brinkmann_steffen(g):
585
E = g.edges()
586
if len(E) == 0:
587
return 0
588
Dist = g.distance_all_pairs()
589
return min(g.degree(v) + g.degree(w) for v in g for w in g if Dist[v][w] == 1)
590
591
#cardinality of the automorphism group of the graph
592
def order_automorphism_group(g):
593
return g.automorphism_group(return_group=False, order=True)
594
595
def alpha_critical_optimum(g, alpha_critical_names):
596
597
n = g.order()
598
V = g.vertices()
599
#g.show()
600
601
alpha_critical_graph_names = []
602
603
#get alpha_critical graphs with order <= n
604
for name in alpha_critical_names:
605
h = Graph(name)
606
if h.order() <= n:
607
alpha_critical_graph_names.append(h.graph6_string())
608
609
#print alpha_critical_graphs
610
611
LP = MixedIntegerLinearProgram(maximization=True)
612
b = LP.new_variable(nonnegative=True)
613
614
# We define the objective
615
LP.set_objective(sum([b[v] for v in g]))
616
617
# For any edge, we define a constraint
618
for (u,v) in g.edges(labels=None):
619
LP.add_constraint(b[u]+b[v],max=1)
620
#LP.add_constraint(b[u]+b[v],min=1)
621
622
#search through all subsets of g with order >= 3
623
#and look for *any* subgraph isomorphic to an alpha critical graph
624
#for any match we define a constraint
625
626
i = 3
627
while i <= n:
628
SS = Subsets(Set(V),i)
629
for S in SS:
630
L = [g6 for g6 in alpha_critical_graph_names if Graph(g6).order() == i]
631
#print L
632
for g6 in L:
633
h = Graph(g6)
634
if g.subgraph(S).subgraph_search(h, induced=False):
635
636
#print S
637
#add constraint
638
alpha = independence_number(h)
639
#print h.graph6_string(), alpha
640
LP.add_constraint(sum([b[j] for j in S]), max = alpha, name = h.graph6_string())
641
i = i + 1
642
643
#for c in LP.constraints():
644
#print c
645
646
# The .solve() functions returns the objective value
647
LP.solve()
648
649
#return LP
650
651
b_sol = LP.get_values(b)
652
return b_sol, sum(b_sol.values())
653
654
655
###several invariants and auxiliary functions related to the Independence Decomposition Theorem
656
657
#finds all vertices with weight 1 in some max weighted stable set with wieghts in {0,1,1/2}
658
#had problem that LP solver has small numerical errors, fixed with kludgy if condition
659
def find_stable_ones_vertices(g):
660
F = []
661
alpha_f = fractional_alpha(g)
662
for v in g.vertices():
663
gc = copy(g)
664
gc.delete_vertices(closed_neighborhood(gc, v))
665
alpha_f_prime = fractional_alpha(gc)
666
if abs(alpha_f - alpha_f_prime - 1) < .01:
667
F.append(v)
668
return F
669
670
def find_max_critical_independent_set(g):
671
S = find_stable_ones_vertices(g)
672
H = g.subgraph(S)
673
return H.independent_set()
674
675
def critical_independence_number(g):
676
return len(find_max_critical_independent_set(g))
677
678
def card_independence_irreducible_part(g):
679
return len(find_independence_irreducible_part(g))
680
681
def find_independence_irreducible_part(g):
682
X = find_KE_part(g)
683
SX = Set(X)
684
Vertices = Set(g.vertices())
685
return list(Vertices.difference(SX))
686
687
#returns KE part guaranteed by Independence Decomposition Theorem
688
def find_KE_part(g):
689
return closed_neighborhood(g, find_max_critical_independent_set(g))
690
691
def card_KE_part(g):
692
return len(find_KE_part(g))
693
694
def find_independence_irreducible_subgraph(g):
695
return g.subgraph(find_independence_irreducible_part(g))
696
697
def find_KE_subgraph(g):
698
return g.subgraph(find_KE_part(g))
699
700
701
#make invariant from property
702
def make_invariant_from_property(property, name=None):
703
"""
704
This function takes a property as an argument and returns an invariant
705
whose value is 1 if the object has the property, else 0
706
Optionally a name for the new property can be provided as a second argument.
707
"""
708
def boolean_valued_invariant(g):
709
if property(g):
710
return 1
711
else:
712
return 0
713
714
if name is not None:
715
boolean_valued_invariant.__name__ = name
716
elif hasattr(property, '__name__'):
717
boolean_valued_invariant.__name__ = '{}_value'.format(property.__name__)
718
else:
719
raise ValueError('Please provide a name for the new function')
720
721
return boolean_valued_invariant
722
723
efficiently_computable_invariants = [average_distance, Graph.diameter, Graph.radius,
724
Graph.girth, Graph.order, Graph.size, Graph.szeged_index, Graph.wiener_index,
725
min_degree, max_degree, matching_number, residue, annihilation_number, fractional_alpha,
726
Graph.lovasz_theta, cvetkovic, cycle_space_dimension, card_center, card_periphery,
727
max_eigenvalue, kirchhoff_index, largest_singular_value, vertex_con, edge_con,
728
Graph.maximum_average_degree, Graph.density, welsh_powell, wilf, brooks,
729
different_degrees, szekeres_wilf, average_vertex_temperature, randic, median_degree,
730
max_even_minus_even_horizontal, fiedler, laplacian_energy, gutman_energy, average_degree,
731
degree_variance, number_of_triangles, graph_rank, inverse_degree, sum_temperatures,
732
card_positive_eigenvalues, card_negative_eigenvalues, card_zero_eigenvalues,
733
card_cut_vertices, Graph.clustering_average, Graph.connected_components_number,
734
Graph.spanning_trees_count, card_pendants, card_bridges, alon_spencer, caro_wei,
735
degree_sum, order_automorphism_group, sigma_2, brinkmann_steffen,
736
card_independence_irreducible_part, critical_independence_number, card_KE_part,
737
fractional_covering, eulerian_faces, barrus_q, mean_common_neighbors,
738
max_common_neighbors, min_common_neighbors, distinct_degrees, barrus_bound]
739
740
intractable_invariants = [independence_number, domination_number, chromatic_index,
741
Graph.clique_number, clique_covering_number, n_over_alpha, chromatic_num,
742
independent_dominating_set_number]
743
744
#for invariants from properties and INVARIANT_PLUS see below
745
746
#FAST ENOUGH (tested for graphs on 140921): lovasz_theta, clique_covering_number, all efficiently_computable
747
#SLOW but FIXED for SpecialGraphs
748
749
invariants = efficiently_computable_invariants + intractable_invariants
750
751
#removed for speed: Graph.treewidth, card_max_cut
752
753
#GRAPH PROPERTIES
754
755
def has_star_center(g):
756
"""
757
tests if graph has vertex adjacent to all others
758
sage: has_star_center(flower_with_3_petals)
759
True
760
sage: has_star_center(c4)
761
False
762
"""
763
n = g.order()
764
return max_degree(g) == (n-1)
765
766
767
#split graphs have the property that their complements are chordal
768
def is_complement_of_chordal(g):
769
"""
770
tests is a graph is a complement of a chordal graph
771
sage: is_complement_of_chordal(p4)
772
True
773
sage: is_complement_of_chordal(p5)
774
False
775
"""
776
h = g.complement()
777
return h.is_chordal()
778
779
#a consequence of the Friendship Theorem:
780
#the only connected graphs where every pair of vertices has a unique neghbor are flowers
781
def pairs_have_unique_common_neighbor(g):
782
"""
783
tests if a graph is a collection of disjoint triangles with a single identified vertex
784
sage: pairs_have_unique_common_neighbor(flower(5))
785
True
786
sage: pairs_have_unique_common_neighbor(k3)
787
True
788
sage: pairs_have_unique_common_neighbor(k4)
789
False
790
"""
791
if max_common_neighbors(g) != 1:
792
return False
793
elif min_common_neighbors(g) != 1:
794
return False
795
else:
796
return True
797
798
#sufficient condition for hamiltonicity
799
def is_dirac(g):
800
n = g.order()
801
deg = g.degree()
802
delta=min(deg)
803
if n/2 <= delta and n > 2:
804
return True
805
else:
806
return False
807
808
#sufficient condition for hamiltonicity
809
def is_ore(g):
810
A = g.adjacency_matrix()
811
n = g.order()
812
D = g.degree()
813
for i in range(n):
814
for j in range(i):
815
if A[i][j]==0:
816
if D[i] + D[j] < n:
817
return False
818
return True
819
820
#sufficient condition for hamiltonicity
821
def is_haggkvist_nicoghossian(g):
822
k = vertex_con(g)
823
n = g.order()
824
delta = min(g.degree())
825
if k >= 2 and delta >= (1.0/3)*(n+k):
826
return True
827
else:
828
return False
829
830
#sufficient condition for hamiltonicity
831
def is_fan(g):
832
k = vertex_con(g)
833
if k < 2:
834
return False
835
D = g.degree()
836
Dist = g.distance_all_pairs()
837
V = g.vertices()
838
n = g.order()
839
for i in range(n):
840
for j in range (i):
841
if Dist[V[i]][V[j]]==2 and max(D[i],D[j]) < n/2.0:
842
return False
843
return True
844
845
#sufficient condition for hamiltonicity
846
def is_planar_transitive(g):
847
if g.order() > 2 and g.is_planar() and g.is_vertex_transitive():
848
return True
849
else:
850
return False
851
852
def neighbors_set(g,S):
853
N = set()
854
for v in S:
855
for n in g.neighbors(v):
856
N.add(n)
857
return list(N)
858
859
#sufficient condition for hamiltonicity
860
def is_generalized_dirac(g):
861
n = g.order()
862
k = vertex_con(g)
863
if k < 2:
864
return False
865
for p in Subsets(g.vertices(),2):
866
if g.is_independent_set(p):
867
if len(neighbors_set(g,p)) < (2.0*n-1)/3:
868
return False
869
return True
870
871
#necessary condition for hamiltonicity
872
def is_van_den_heuvel(g):
873
n = g.order()
874
lc = sorted(graphs.CycleGraph(n).laplacian_matrix().eigenvalues())
875
lg = sorted(g.laplacian_matrix().eigenvalues())
876
877
for lci, lgi in zip(lc, lg):
878
if lci > lgi:
879
return False
880
881
def Q(g):
882
A = g.adjacency_matrix()
883
884
D = A.parent(0)
885
886
if A.is_sparse():
887
row_sums = {}
888
for (i,j), entry in A.dict().iteritems():
889
row_sums[i] = row_sums.get(i, 0) + entry
890
for i in range(A.nrows()):
891
D[i,i] += row_sums.get(i, 0)
892
else:
893
row_sums=[sum(v) for v in A.rows()]
894
for i in range(A.nrows()):
895
D[i,i] += row_sums[i]
896
897
return D + A
898
899
qc = sorted(Q(graphs.CycleGraph(n)).eigenvalues())
900
qg = sorted(Q(g).eigenvalues())
901
902
for qci, qgi in zip(qc, qg):
903
if qci > qgi:
904
return False
905
906
return True
907
908
#necessary condition for hamiltonicity
909
def is_two_connected(g):
910
"""
911
Returns True if the graph is 2-connected and False otherwise. A graph is
912
2-connected if the removal of any single vertex gives a connected graph.
913
By definition a graph on 2 or less vertices is not 2-connected.
914
915
sage: is_two_connected(graphs.CycleGraph(5))
916
True
917
sage: is_two_connected(graphs.PathGraph(5))
918
False
919
sage: is_two_connected(graphs.CompleteGraph(2))
920
False
921
sage: is_two_connected(graphs.CompleteGraph(1))
922
False
923
"""
924
if g.order() <= 2:
925
return False
926
from itertools import combinations
927
for s in combinations(g.vertices(), g.order() - 1):
928
if not g.subgraph(s).is_connected():
929
return False
930
return True
931
932
#part of pebbling class0 sufficient condition
933
def is_three_connected(g):
934
"""
935
Returns True if the graph is 3-connected and False otherwise. A graph is
936
3-connected if the removal of any single vertex or any pair of vertices
937
gives a connected graph. By definition a graph on 3 or less vertices is
938
not 3-connected.
939
940
sage: is_three_connected(graphs.PetersenGraph())
941
True
942
sage: is_three_connected(graphs.CompleteGraph(4))
943
True
944
sage: is_three_connected(graphs.CycleGraph(5))
945
False
946
sage: is_three_connected(graphs.PathGraph(5))
947
False
948
sage: is_three_connected(graphs.CompleteGraph(3))
949
False
950
sage: is_three_connected(graphs.CompleteGraph(2))
951
False
952
sage: is_three_connected(graphs.CompleteGraph(1))
953
False
954
"""
955
if g.order() <= 3:
956
return False
957
from itertools import combinations
958
for s in combinations(g.vertices(), g.order() - 2):
959
if not g.subgraph(s).is_connected():
960
return False
961
return True
962
963
#sufficient condition for hamiltonicity
964
def is_lindquester(g):
965
k = vertex_con(g)
966
if k < 2:
967
return False
968
D = g.distance_all_pairs()
969
n = g.order()
970
V = g.vertices()
971
for i in range(n):
972
for j in range(i):
973
if D[V[i]][V[j]] == 2:
974
if len(neighbors_set(g,[V[i],V[j]])) < (2*n-1)/3.0:
975
return False
976
return True
977
978
def is_complete(g):
979
n = g.order()
980
e = g.size()
981
if not g.has_multiple_edges():
982
return e == n*(n-1)/2
983
984
D = g.distance_all_pairs()
985
for i in range(n):
986
for j in range(i):
987
if D[V[i]][V[j]] != 1:
988
return False
989
return True
990
991
def has_c4(g):
992
return g.subgraph_search(c4, induced=True) is not None
993
994
def is_c4_free(g):
995
return not has_c4(g)
996
997
def has_paw(g):
998
return g.subgraph_search(paw, induced=True) is not None
999
1000
def is_paw_free(g):
1001
return not has_paw(g)
1002
1003
def has_dart(g):
1004
return g.subgraph_search(dart, induced=True) is not None
1005
1006
def is_dart_free(g):
1007
return not has_dart(g)
1008
1009
def is_p4_free(g):
1010
return not has_p4(g)
1011
1012
def has_p4(g):
1013
return g.subgraph_search(p4, induced=True) is not None
1014
1015
def has_kite(g):
1016
return g.subgraph_search(kite, induced=True) is not None
1017
1018
def is_kite_free(g):
1019
return not has_kite(g)
1020
1021
def has_claw(g):
1022
return g.subgraph_search(graphs.ClawGraph(), induced=True) is not None
1023
1024
def is_claw_free(g):
1025
return not has_claw(g)
1026
1027
def has_H(g):
1028
return g.subgraph_search(killer, induced=True) is not None
1029
1030
def is_H_free(g):
1031
return not has_H(g)
1032
1033
def has_fork(g):
1034
return g.subgraph_search(fork, induced=True) is not None
1035
1036
def is_fork_free(g):
1037
return not has_fork(g)
1038
1039
def is_biclique(g):
1040
"""
1041
a graph is a biclique if the vertices can be partitioned into 2 sets that induce cliques
1042
sage: is_biclique(p4)
1043
True
1044
sage: is_biclique(bow_tie)
1045
True
1046
"""
1047
gc = g.complement()
1048
return gc.is_bipartite()
1049
1050
def has_perfect_matching(g):
1051
n = g.order()
1052
if n%2 == 1:
1053
return False
1054
nu = g.matching(value_only=True)
1055
if 2*nu == n:
1056
return True
1057
else:
1058
return False
1059
1060
#true if radius equals diameter
1061
def has_radius_equal_diameter(g):
1062
return g.radius() == g.diameter()
1063
1064
#true if residue equals independence number
1065
def has_residue_equals_alpha(g):
1066
return residue(g) == independence_number(g)
1067
1068
def is_not_forest(g):
1069
return not g.is_forest()
1070
1071
1072
def bipartite_double_cover(g):
1073
return g.tensor_product(graphs.CompleteGraph(2))
1074
1075
def closed_neighborhood(g, verts):
1076
if isinstance(verts, list):
1077
neighborhood = []
1078
for v in verts:
1079
neighborhood += [v] + g.neighbors(v)
1080
return list(set(neighborhood))
1081
else:
1082
return [verts] + g.neighbors(verts)
1083
1084
#replaced with faster LP-solver is_independence_irreducible
1085
#has no non-empty critical independent set (<=> the only solution to the LP independence number relaxation is all 1/2's)
1086
def has_empty_KE_part(g):
1087
b = bipartite_double_cover(g)
1088
alpha = b.order() - b.matching(value_only=True)
1089
for v in g.vertices():
1090
test = b.copy()
1091
test.delete_vertices(closed_neighborhood(b,[(v,0), (v,1)]))
1092
alpha_test = test.order() - test.matching(value_only=True) + 2
1093
if alpha_test == alpha:
1094
return False
1095
return True
1096
1097
def is_bicritical(g):
1098
return has_empty_KE_part(g)
1099
1100
1101
# Vizing's Theorem: chromatic index of any graph is either Delta or Delta+1
1102
def is_class1(g):
1103
return chromatic_index(g) == max(g.degree())
1104
1105
def is_class2(g):
1106
return not(chromatic_index(g) == max(g.degree()))
1107
1108
def is_cubic(g):
1109
D = g.degree()
1110
return min(D) == 3 and max(D) == 3
1111
1112
#a property that applied to all entered hamiltonian graphs (before c60) but not the tutte graph, false for tutte graph
1113
def is_anti_tutte(g):
1114
if not g.is_connected():
1115
return False
1116
return independence_number(g) <= g.diameter() + g.girth()
1117
1118
#a property that applied to all entered hamiltonian graphs upto c6cc6 but not the tutte graph, false for tutte graph
1119
def is_anti_tutte2(g):
1120
if not g.is_connected():
1121
return False
1122
return independence_number(g) <= domination_number(g) + g.radius()- 1
1123
1124
#for any graph diam <= 2*radius. this property is true in the extremal case
1125
def diameter_equals_twice_radius(g):
1126
if not g.is_connected():
1127
return False
1128
return g.diameter() == 2*g.radius()
1129
1130
#for any graph diam >= radius. this property is true in the extremal case
1131
def diameter_equals_radius(g):
1132
if not g.is_connected():
1133
return False
1134
return g.diameter() == g.radius()
1135
1136
#almost all graphs have diameter equals 2
1137
def diameter_equals_two(g):
1138
if not g.is_connected():
1139
return False
1140
return g.diameter() == 2
1141
1142
def has_lovasz_theta_equals_alpha(g):
1143
if g.lovasz_theta() == independence_number(g):
1144
return True
1145
else:
1146
return False
1147
1148
def has_lovasz_theta_equals_cc(g):
1149
if g.lovasz_theta() == clique_covering_number(g):
1150
return True
1151
else:
1152
return False
1153
1154
#sufficient condition for hamiltonicity
1155
def is_chvatal_erdos(g):
1156
return independence_number(g) <= vertex_con(g)
1157
1158
#locally connected if the neighborhood of every vertex is connected (stronger than claw-free)
1159
def is_locally_connected(g):
1160
V = g.vertices()
1161
for v in V:
1162
N = g.neighbors(v)
1163
if len(N) > 0:
1164
GN = g.subgraph(N)
1165
if not GN.is_connected():
1166
return False
1167
return True
1168
1169
1170
#matching_covered if every edge is in a maximum matching (generalization of factor-covered which requires perfect matching)
1171
def matching_covered(g):
1172
g = g.copy()
1173
nu = matching_number(g)
1174
E = g.edges()
1175
for e in E:
1176
g.delete_edge(e)
1177
nu2 = matching_number(g)
1178
if nu != nu2:
1179
return False
1180
g.add_edge(e)
1181
return True
1182
1183
#a property that separates tutte from known hamiltonian examples, must be connected
1184
#radius(tutte) > center(tutte)
1185
def radius_greater_than_center(g):
1186
if not g.is_connected() or g.radius() <= card_center(g):
1187
return False
1188
else:
1189
return True
1190
1191
#a property that separates tutte from known hamiltonian examples, must be connected
1192
#avg_dist(tutte) > girth(tutte)
1193
def avg_distance_greater_than_girth(g):
1194
if not g.is_connected() or g.average_distance() <= g.girth():
1195
return False
1196
else:
1197
return True
1198
1199
#chromatic number equals min of known chi upper bounds
1200
def chi_equals_min_theory(g):
1201
chromatic_upper_theory = [brooks, wilf, welsh_powell, szekeres_wilf]
1202
min_theory = min([f(g) for f in chromatic_upper_theory])
1203
chi = chromatic_num(g)
1204
if min_theory == chi:
1205
return True
1206
else:
1207
return False
1208
1209
def is_heliotropic_plant(g):
1210
return (independence_number(g) == card_positive_eigenvalues(g))
1211
1212
def is_geotropic_plant(g):
1213
return (independence_number(g) == card_negative_eigenvalues(g))
1214
1215
#means has hamiltonian path, true iff g join a single vertex has hamiltonian cycle
1216
def is_traceable(g):
1217
gadd = g.join(Graph(1),labels="integers")
1218
return gadd.is_hamiltonian()
1219
1220
def has_residue_equals_two(g):
1221
return residue(g) == 2
1222
1223
#a necessary condition for being even hole free
1224
def is_chordal_or_not_perfect(g):
1225
if g.is_chordal():
1226
return true
1227
else:
1228
return not g.is_perfect()
1229
1230
def has_alpha_residue_equal_two(g):
1231
if residue(g) != 2:
1232
return false
1233
else:
1234
return independence_number(g) == 2
1235
1236
# for vizing's independence number conjecture
1237
def alpha_leq_order_over_two(g):
1238
return (2*independence_number(g) <= g.order())
1239
1240
1241
#in a luo-zhao sufficient condition for alpha <= n/2 (vizing's ind number conj)
1242
def order_leq_twice_max_degree(g):
1243
return (g.order() <= 2*max(g.degree()))
1244
1245
#not in properties list until meredith graph is computed
1246
#critical if connected, class 2, and removal of any edge decreases chromatic number
1247
def is_chromatic_index_critical(g):
1248
if not g.is_connected():
1249
return False
1250
Delta = max(g.degree())
1251
chi_e = chromatic_index(g)
1252
if chi_e != Delta + 1:
1253
return False
1254
1255
lg=g.line_graph()
1256
equiv_lines = lg.automorphism_group(return_group=False,orbits=true)
1257
equiv_lines_representatives = [orb[0] for orb in equiv_lines]
1258
1259
for e in equiv_lines_representatives:
1260
gc = copy(g)
1261
gc.delete_edge(e)
1262
chi_e_prime = chromatic_index(gc)
1263
if not chi_e_prime < chi_e:
1264
return False
1265
return True
1266
1267
#not in properties list
1268
#alpha(g-e) > alpha(g) for *every* edge g
1269
def is_alpha_critical(g):
1270
#if not g.is_connected():
1271
#return False
1272
alpha = independence_number(g)
1273
for e in g.edges():
1274
gc = copy(g)
1275
gc.delete_edge(e)
1276
alpha_prime = independence_number(gc)
1277
if alpha_prime <= alpha:
1278
return False
1279
return True
1280
1281
#graph is KE if matching number + independence number = n, test does *not* compute alpha
1282
def is_KE(g):
1283
return g.order() == len(find_KE_part(g))
1284
1285
#graph is KE if matching number + independence number = n, test comoutes alpha
1286
#def is_KE(g):
1287
# return (g.matching(value_only = True) + independence_number(g) == g.order())
1288
1289
#possibly faster version of is_KE (not currently in invariants)
1290
#def is_KE2(g):
1291
# return (independence_number(g) == critical_independence_number(g))
1292
1293
def is_independence_irreducible(g):
1294
return g.order() == card_independence_irreducible_part(g)
1295
1296
1297
def is_factor_critical(g):
1298
"""
1299
a graph is factor-critical if order is odd and removal of any vertex gives graph with perfect matching
1300
is_factor_critical(p3)
1301
False
1302
sage: is_factor_critical(c5)
1303
True
1304
"""
1305
if g.order() % 2 == 0:
1306
return False
1307
for v in g.vertices():
1308
gc = copy(g)
1309
gc.delete_vertex(v)
1310
if not has_perfect_matching(gc):
1311
return False
1312
return True
1313
1314
#returns a list of (necessarily non-adjacent) vertices that have the same neighbors as v if a pair exists or None
1315
def find_twins_of_vertex(g,v):
1316
L = []
1317
V = g.vertices()
1318
D = g.distance_all_pairs()
1319
for i in range(g.order()):
1320
w = V[i]
1321
if D[v][w] == 2 and g.neighbors(v) == g.neighbors(w):
1322
L.append(w)
1323
return L
1324
1325
def has_twin(g):
1326
t = find_twin(g)
1327
if t == None:
1328
return False
1329
else:
1330
return True
1331
1332
def is_twin_free(g):
1333
return not has_twin(g)
1334
1335
#returns twin pair (v,w) if one exists, else returns None
1336
#can't be adjacent
1337
def find_twin(g):
1338
1339
V = g.vertices()
1340
for v in V:
1341
Nv = set(g.neighbors(v))
1342
for w in V:
1343
Nw = set(g.neighbors(w))
1344
if v not in Nw and Nv == Nw:
1345
return (v,w)
1346
return None
1347
1348
def find_neighbor_twins(g):
1349
V = g.vertices()
1350
for v in V:
1351
Nv = g.neighbors(v)
1352
for w in Nv:
1353
if set(closed_neighborhood(g,v)) == set(closed_neighborhood(g,w)):
1354
return (v,w)
1355
return None
1356
1357
#given graph g and subset S, looks for any neighbor twin of any vertex in T
1358
#if result = T, then no twins, else the result is maximal, but not necessarily unique
1359
def find_neighbor_twin(g, T):
1360
gT = g.subgraph(T)
1361
for v in T:
1362
condition = False
1363
Nv = set(g.neighbors(v))
1364
#print "v = {}, Nv = {}".format(v,Nv)
1365
NvT = set(gT.neighbors(v))
1366
for w in Nv:
1367
NwT = set(g.neighbors(w)).intersection(set(T))
1368
if w not in T and NvT.issubset(NwT):
1369
twin = w
1370
T.append(w)
1371
condition = True
1372
#print "TWINS: v = {}, w = {}, sp3 = {}".format(v,w,sp3)
1373
break
1374
if condition == True:
1375
break
1376
1377
#if result = T, then no twins, else the result is maximal, but not necessarily unique
1378
def iterative_neighbor_twins(g, T):
1379
T2 = copy(T)
1380
find_neighbor_twin(g, T)
1381
while T2 != T:
1382
T2 = copy(T)
1383
find_neighbor_twin(g, T)
1384
return T
1385
1386
def is_cycle(g):
1387
return g.is_isomorphic(graphs.CycleGraph(g.order()))
1388
1389
1390
#can't compute membership in this class directly. instead testing isomorhism for 400 known class0 graphs
1391
def is_pebbling_class0(g):
1392
for hkey in class0graphs_dict:
1393
h = Graph(class0graphs_dict[hkey])
1394
if g.is_isomorphic(h):
1395
return True
1396
return False
1397
1398
def girth_greater_than_2log(g):
1399
return bool(g.girth() > 2*log(g.order(),2))
1400
1401
def szekeres_wilf_equals_chromatic_number(g):
1402
return szekeres_wilf(g) == g.chromatic_number()
1403
1404
1405
def localise(f):
1406
"""
1407
This function takes a property (i.e., a function taking only a graph as an argument) and
1408
returns the local variant of that property. The local variant is True if the property is
1409
True for the neighbourhood of each vertex and False otherwise.
1410
"""
1411
#create a local version of f
1412
def localised_function(g):
1413
return all((f(g.subgraph(g.neighbors(v))) if g.neighbors(v) else True) for v in g.vertices())
1414
1415
#we set a nice name for the new function
1416
if hasattr(f, '__name__'):
1417
if f.__name__.startswith('is_'):
1418
localised_function.__name__ = 'is_locally' + f.__name__[2:]
1419
elif f.__name__.startswith('has_'):
1420
localised_function.__name__ = 'has_locally' + f.__name__[2:]
1421
else:
1422
localised_function.__name__ = 'localised_' + f.__name__
1423
1424
return localised_function
1425
1426
is_locally_dirac = localise(is_dirac)
1427
is_locally_bipartite = localise(Graph.is_bipartite)
1428
1429
#old versioncted), can't seem to memoize that
1430
1431
def is_locally_two_connected(g):
1432
V = g.vertices()
1433
for v in V:
1434
N = g.neighbors(v)
1435
if len(N) > 0:
1436
GN = g.subgraph(N)
1437
if not is_two_connected(GN):
1438
return False
1439
return True
1440
1441
def has_equal_invariants(invar1, invar2, name=None):
1442
"""
1443
This function takes two invariants as an argument and returns the property that these invariants are equal.
1444
Optionally a name for the new function can be provided as a third argument.
1445
"""
1446
def equality_checker(g):
1447
return invar1(g) == invar2(g)
1448
1449
if name is not None:
1450
equality_checker.__name__ = name
1451
elif hasattr(invar1, '__name__') and hasattr(invar2, '__name__'):
1452
equality_checker.__name__ = 'has_{}_equals_{}'.format(invar1.__name__, invar2.__name__)
1453
else:
1454
raise ValueError('Please provide a name for the new function')
1455
1456
return equality_checker
1457
1458
def has_leq_invariants(invar1, invar2, name=None):
1459
"""
1460
This function takes two invariants as an argument and returns the property that the first invariant is
1461
less than or equal to the second invariant.
1462
Optionally a name for the new function can be provided as a third argument.
1463
"""
1464
def comparator(g):
1465
return invar1(g) <= invar2(g)
1466
1467
if name is not None:
1468
comparator.__name__ = name
1469
elif hasattr(invar1, '__name__') and hasattr(invar2, '__name__'):
1470
comparator.__name__ = 'has_{}_leq_{}'.format(invar1.__name__, invar2.__name__)
1471
else:
1472
raise ValueError('Please provide a name for the new function')
1473
1474
return comparator
1475
1476
def has_Havel_Hakimi_property(g, v):
1477
"""
1478
This function returns whether the vertex v in the graph g has the Havel-Hakimi
1479
property as defined in [1]. A vertex has the Havel-Hakimi property if it has
1480
maximum degree and the minimum degree of its neighbours is at least the maximum
1481
degree of its non-neigbours.
1482
1483
[1] Graphs with the strong Havel-Hakimi property, M. Barrus, G. Molnar, Graphs
1484
and Combinatorics, 2016, http://dx.doi.org/10.1007/s00373-015-1674-7
1485
1486
Every vertex in a regular graph has the Havel-Hakimi property::
1487
1488
sage: P = graphs.PetersenGraph()
1489
sage: for v in range(10):
1490
....: has_Havel_Hakimi_property(P,v)
1491
True
1492
True
1493
True
1494
True
1495
True
1496
True
1497
True
1498
True
1499
True
1500
True
1501
sage: has_Havel_Hakimi_property(Graph([[0,1,2,3],lambda x,y: False]),0)
1502
True
1503
sage: has_Havel_Hakimi_property(graphs.CompleteGraph(5),0)
1504
True
1505
"""
1506
if max_degree(g) > g.degree(v): return False
1507
1508
#handle the case where the graph is an independent set
1509
if len(g.neighbors(v)) == 0: return True
1510
1511
#handle the case where v is adjacent with all vertices
1512
if len(g.neighbors(v)) == len(g.vertices()) - 1: return True
1513
1514
return (min(g.degree(nv) for nv in g.neighbors(v)) >=
1515
max(g.degree(nnv) for nnv in g.vertices() if nnv != v and nnv not in g.neighbors(v)))
1516
1517
def has_strong_Havel_Hakimi_property(g):
1518
"""
1519
This function returns whether the graph g has the strong Havel-Hakimi property
1520
as defined in [1]. A graph has the strong Havel-Hakimi property if in every
1521
induced subgraph H of G, every vertex of maximum degree has the Havel-Hakimi
1522
property.
1523
1524
[1] Graphs with the strong Havel-Hakimi property, M. Barrus, G. Molnar, Graphs
1525
and Combinatorics, 2016, http://dx.doi.org/10.1007/s00373-015-1674-7
1526
1527
The graph obtained by connecting two cycles of length 3 by a single edge has
1528
the strong Havel-Hakimi property::
1529
1530
sage: has_strong_Havel_Hakimi_property(Graph('E{CW'))
1531
True
1532
"""
1533
for S in Subsets(g.vertices()):
1534
if len(S)>2:
1535
H = g.subgraph(S)
1536
Delta = max_degree(H)
1537
if any(not has_Havel_Hakimi_property(H, v) for v in S if H.degree(v) == Delta):
1538
return False
1539
return True
1540
1541
#add all properties derived from pairs of invariants
1542
invariant_relation_properties = [has_leq_invariants(f,g) for f in invariants for g in invariants if f != g]
1543
1544
1545
efficiently_computable_properties = [Graph.is_regular, Graph.is_planar,
1546
Graph.is_forest, Graph.is_eulerian, Graph.is_connected, Graph.is_clique,
1547
Graph.is_circular_planar, Graph.is_chordal, Graph.is_bipartite,
1548
Graph.is_cartesian_product,Graph.is_distance_regular, Graph.is_even_hole_free,
1549
Graph.is_gallai_tree, Graph.is_line_graph, Graph.is_overfull, Graph.is_perfect,
1550
Graph.is_split, Graph.is_strongly_regular, Graph.is_triangle_free,
1551
Graph.is_weakly_chordal, is_dirac, is_ore, is_haggkvist_nicoghossian,
1552
is_generalized_dirac, is_van_den_heuvel, is_two_connected, is_three_connected,
1553
is_lindquester, is_claw_free, has_perfect_matching, has_radius_equal_diameter,
1554
is_not_forest, is_fan, is_cubic, diameter_equals_twice_radius,
1555
diameter_equals_radius, is_locally_connected, matching_covered, is_locally_dirac,
1556
is_locally_bipartite, is_locally_two_connected, Graph.is_interval, has_paw,
1557
is_paw_free, has_p4, is_p4_free, has_dart, is_dart_free, has_kite, is_kite_free,
1558
has_H, is_H_free, has_residue_equals_two, order_leq_twice_max_degree,
1559
alpha_leq_order_over_two, is_factor_critical, is_independence_irreducible,
1560
has_twin, is_twin_free, diameter_equals_two, girth_greater_than_2log, is_cycle,
1561
pairs_have_unique_common_neighbor, has_star_center, is_complement_of_chordal, has_c4, is_c4_free]
1562
1563
intractable_properties = [Graph.is_hamiltonian, Graph.is_vertex_transitive,
1564
Graph.is_edge_transitive, has_residue_equals_alpha, Graph.is_odd_hole_free,
1565
Graph.is_semi_symmetric, Graph.is_line_graph, is_planar_transitive, is_class1,
1566
is_class2, is_anti_tutte, is_anti_tutte2, has_lovasz_theta_equals_cc,
1567
has_lovasz_theta_equals_alpha, is_chvatal_erdos, is_heliotropic_plant,
1568
is_geotropic_plant, is_traceable, is_chordal_or_not_perfect,
1569
has_alpha_residue_equal_two]
1570
1571
removed_properties = [is_pebbling_class0]
1572
1573
#speed notes
1574
#FAST ENOUGH (tested for graphs on 140921): is_hamiltonian, is_vertex_transitive,
1575
# is_edge_transitive, has_residue_equals_alpha, is_odd_hole_free, is_semi_symmetric,
1576
# is_line_graph, is_line_graph, is_anti_tutte, is_planar_transitive
1577
#SLOW but FIXED for SpecialGraphs: is_class1, is_class2
1578
1579
properties = efficiently_computable_properties + intractable_properties
1580
properties_plus = efficiently_computable_properties + intractable_properties + invariant_relation_properties
1581
1582
1583
invariants_from_properties = [make_invariant_from_property(property) for property in properties]
1584
invariants_plus = invariants + invariants_from_properties
1585
1586
# Graph.is_prime removed as faulty 9/2014
1587
# built in Graph.is_transitively_reduced removed 9/2014
1588
# is_line_graph is theoretically efficient - but Sage's implementation is not 9/2014
1589
1590
# weakly_chordal = weakly chordal, i.e., the graph and its complement have no induced cycle of length at least 5
1591
1592
1593
1594
#GRAPH OBJECTS
1595
1596
p3 = graphs.PathGraph(3)
1597
p3.name(new = "p3")
1598
1599
k3_3=graphs.CompleteBipartiteGraph(3,3)
1600
k3_3.name(new = "k3_3")
1601
1602
#two c4's joined at a vertex
1603
c4c4=graphs.CycleGraph(4)
1604
for i in [4,5,6]:
1605
c4c4.add_vertex()
1606
c4c4.add_edge(3,4)
1607
c4c4.add_edge(5,4)
1608
c4c4.add_edge(5,6)
1609
c4c4.add_edge(6,3)
1610
c4c4.name(new="c4c4")
1611
1612
#two c5's joined at a vertex: eulerian, not perfect, not hamiltonian
1613
c5c5=graphs.CycleGraph(5)
1614
for i in [5,6,7,8]:
1615
c5c5.add_vertex()
1616
c5c5.add_edge(0,5)
1617
c5c5.add_edge(0,8)
1618
c5c5.add_edge(6,5)
1619
c5c5.add_edge(6,7)
1620
c5c5.add_edge(7,8)
1621
c5c5.name(new="c5c5")
1622
1623
#triangle plus pendant: not hamiltonian, not triangle-free
1624
c3p2=graphs.CycleGraph(3)
1625
c3p2.add_vertex()
1626
c3p2.add_edge(0,3)
1627
c3p2.name(new="c3p2")
1628
1629
K4a=graphs.CompleteGraph(4)
1630
K4b=graphs.CompleteGraph(4)
1631
K4a.delete_edge(0,1)
1632
K4b.delete_edge(0,1)
1633
regular_non_trans = K4a.disjoint_union(K4b)
1634
regular_non_trans.add_edge((0,0),(1,1))
1635
regular_non_trans.add_edge((0,1),(1,0))
1636
regular_non_trans.name(new="regular_non_trans")
1637
1638
c6ee = graphs.CycleGraph(6)
1639
c6ee.add_edges([(1,5), (2,4)])
1640
c6ee.name(new="c6ee")
1641
1642
#c5 plus a chord
1643
c5chord = graphs.CycleGraph(5)
1644
c5chord.add_edge(0,3)
1645
c5chord.name(new="c5chord")
1646
1647
#c6ee plus another chord: hamiltonian, regular, vertex transitive
1648
c6eee = copy(c6ee)
1649
c6eee.add_edge(0,3)
1650
c6eee.name(new="c6eee")
1651
1652
#c8 plus one long vertical chord and 3 parallel horizontal chords
1653
c8chorded = graphs.CycleGraph(8)
1654
c8chorded.add_edge(0,4)
1655
c8chorded.add_edge(1,7)
1656
c8chorded.add_edge(2,6)
1657
c8chorded.add_edge(3,5)
1658
c8chorded.name(new="c8chorded")
1659
1660
#c8 plus 2 parallel chords: hamiltonian, tri-free, not vertex-transitive
1661
c8chords = graphs.CycleGraph(8)
1662
c8chords.add_edge(1,6)
1663
c8chords.add_edge(2,5)
1664
c8chords.name(new="c8chords")
1665
1666
#c8 plus 2 parallel chords: hamiltonian, tri-free, not vertex-transitive
1667
c8chords = graphs.CycleGraph(8)
1668
c8chords.add_edge(1,6)
1669
c8chords.add_edge(2,5)
1670
c8chords.name(new="c8chords")
1671
1672
prism = graphs.CycleGraph(6)
1673
prism.add_edge(0,2)
1674
prism.add_edge(3,5)
1675
prism.add_edge(1,4)
1676
prism.name(new="prism")
1677
1678
prismsub = copy(prism)
1679
prismsub.subdivide_edge(1,4,1)
1680
prismsub.name(new="prismsub")
1681
1682
# ham, not vertex trans, tri-free, not cartesian product
1683
prismy = graphs.CycleGraph(8)
1684
prismy.add_edge(2,5)
1685
prismy.add_edge(0,3)
1686
prismy.add_edge(4,7)
1687
prismy.name(new="prismy")
1688
1689
#c10 with chords, ham, tri-free, regular, planar, vertex transitive
1690
sixfour = graphs.CycleGraph(10)
1691
sixfour.add_edge(1,9)
1692
sixfour.add_edge(0,2)
1693
sixfour.add_edge(3,8)
1694
sixfour.add_edge(4,6)
1695
sixfour.add_edge(5,7)
1696
sixfour.name(new="sixfour")
1697
1698
#unique 24-vertex fullerene: hamiltonian, planar, not vertex transitive
1699
c24 = Graph('WsP@H?PC?O`?@@?_?GG@??CC?G??GG?E???o??B???E???F')
1700
c24.name(new="c24")
1701
1702
#unique 26-atom fullerene: hamiltonian, planar, not vertex trans, radius=5, diam=6
1703
c26 = Graph('YsP@H?PC?O`?@@?_?G?@??CC?G??GG?E??@_??K???W???W???H???E_')
1704
c26.name(new="c26")
1705
1706
"""
1707
The Holton-McKay graph is the smallest planar cubic hamiltonian graph with an edge
1708
that is not contained in a hamiltonian cycle. It has 24 vertices and the edges (0,3)
1709
and (4,7) are not contained in a hamiltonian cycle. This graph was mentioned in
1710
D. A. Holton and B. D. McKay, Cycles in 3-connected cubic planar graphs II, Ars
1711
Combinatoria, 21A (1986) 107-114.
1712
1713
sage: holton_mckay
1714
holton_mckay: Graph on 24 vertices
1715
sage: holton_mckay.is_planar()
1716
True
1717
sage: holton_mckay.is_regular()
1718
True
1719
sage: max(holton_mckay.degree())
1720
3
1721
sage: holton_mckay.is_hamiltonian()
1722
True
1723
sage: holton_mckay.radius()
1724
4
1725
sage: holton_mckay.diameter()
1726
6
1727
"""
1728
holton_mckay = Graph('WlCGKS??G?_D????_?g?DOa?C?O??G?CC?`?G??_?_?_??L')
1729
holton_mckay.name(new="holton_mckay")
1730
1731
#z1 is a graph that shows up in a sufficient condition for hamiltonicity
1732
z1 = graphs.CycleGraph(3)
1733
z1.add_edge(0,3)
1734
z1.name(new="z1")
1735
1736
#an example of a bipartite, 1-tough, not van_den_heuvel, not hamiltonian graph
1737
kratsch_lehel_muller = graphs.PathGraph(12)
1738
kratsch_lehel_muller.add_edge(0,5)
1739
kratsch_lehel_muller.add_edge(6,11)
1740
kratsch_lehel_muller.add_edge(4,9)
1741
kratsch_lehel_muller.add_edge(1,10)
1742
kratsch_lehel_muller.add_edge(2,7)
1743
kratsch_lehel_muller.name(new="kratsch_lehel_muller")
1744
1745
#ham, not planar, not anti_tutte
1746
c6xc6 = graphs.CycleGraph(6).cartesian_product(graphs.CycleGraph(6))
1747
c6xc6.name(new="c6xc6")
1748
1749
#non-ham, 2-connected, eulerian (4-regular)
1750
gould = Graph('S~dg?CB?wC_L????_?W?F??c?@gOOOGGK')
1751
gould.name(new="gould")
1752
1753
#two k5s with single edge removed from each and lines joining these 4 points to a new center point, non-hamiltonian
1754
throwing = Graph('J~wWGGB?wF_')
1755
throwing.name(new="throwing")
1756
1757
#k4 plus k2 on one side, open k5 on other, meet at single point in center, non-hamiltonian
1758
throwing2 = Graph("K~wWGKA?gB_N")
1759
throwing2.name(new="throwing2")
1760
1761
#similar to throwing2 with pair of edges swapped, non-hamiltonian
1762
throwing3 = Graph("K~wWGGB?oD_N")
1763
throwing3.name(new="throwing3")
1764
1765
#graph has diameter != radius but is hamiltonian
1766
tent = graphs.CycleGraph(4).join(Graph(1),labels="integers")
1767
tent.name(new="tent")
1768
1769
#c6 with a k4 subgraph, eulerain, diameter = 3, radius=2, hamiltonian
1770
c6subk4 = graphs.CycleGraph(6)
1771
c6subk4.add_edge(1,5)
1772
c6subk4.add_edge(1,4)
1773
c6subk4.add_edge(2,5)
1774
c6subk4.add_edge(2,4)
1775
c6subk4.name(new="c6subk4")
1776
1777
#C5 with chords from one vertex to other 2 (showed up in auto search for CE's): hamiltonian
1778
bridge = Graph("DU{")
1779
bridge.name(new="bridge")
1780
1781
#nico found the smallest hamiltonian overfull graph
1782
non_ham_over = Graph("HCQRRQo")
1783
non_ham_over.name(new="non_ham_over")
1784
1785
ryan = Graph("WxEW?CB?I?_R????_?W?@?OC?AW???O?C??B???G?A?_??R")
1786
ryan.name(new="ryan")
1787
1788
inp = Graph('J?`FBo{fdb?')
1789
inp.name(new="inp")
1790
1791
#p10 joined to 2 points of k4, a CE to conjecture: chromatic_number<=avg degree + 1
1792
p10k4=Graph('MhCGGC@?G?_@_B?B_')
1793
p10k4.name(new="p10k4")
1794
1795
#star on 13 points with added edge: CE to alpha <+ dom + girth^2
1796
s13e = Graph('M{aCCA?_C?O?_?_??')
1797
s13e.name(new="s13e")
1798
1799
#rp CE to alpha<=2*chi+2*residue, has alpha=25,chi=2,residue=10
1800
ryan2=graphs.CirculantGraph(50,[1,3])
1801
ryan2.name(new="circulant_50_1_3")
1802
1803
#CE to alpha <= 2*girth^2+2, star with 22 rays plus extra edge
1804
s22e = graphs.StarGraph(22)
1805
s22e.add_edge(1,2)
1806
s22e.name(new="s22e")
1807
1808
#the unique 100-atom fullerene with minimum independence number of 43 (and IPR, tetrahedral symmetry)
1809
c100 = Graph("~?@csP@@?OC?O`?@?@_?O?A??W??_??_G?O??C??@_??C???G???G@??K???A????O???@????A????A?G??B?????_????C?G???O????@_?????_?????O?????C?G???@_?????E??????G??????G?G????C??????@???????G???????o??????@???????@????????_?_?????W???????@????????C????????G????????G?G??????E????????@_????????K?????????_????????@?@???????@?@???????@_?????????G?????????@?@????????C?C????????W??????????W??????????C??????????@?@?????????G???????????_??????????@?@??????????_???????????O???????????C?G??????????O???????????@????????????A????????????A?G??????????@_????????????W????????????@_????????????E?????????????E?????????????E?????????????B??????????????O?????????????A@?????????????G??????????????OG?????????????O??????????????GC?????????????A???????????????OG?????????????@?_?????????????B???????????????@_???????????????W???????????????@_???????????????F")
1810
c100.name(new="c100")
1811
1812
dc64_g6string ="~?@?JXxwm?OJ@wESEYMMbX{VDokGxAWvH[RkTAzA_Tv@w??wF]?oE\?OAHoC_@A@g?PGM?AKOQ??ZPQ?@rgt??{mIO?NSD_AD?mC\
1813
O?J?FG_FOOEw_FpGA[OAxa?VC?lWOAm_DM@?Mx?Y{A?XU?hwA?PM?PW@?G@sGBgl?Gi???C@_FP_O?OM?VMA_?OS?lSB??PS?`sU\
1814
??Gx?OyF_?AKOCN`w??PA?P[J??@C?@CU_??AS?AW^G??Ak?AwVZg|?Oy_@?????d??iDu???C_?D?j_???M??[Bl_???W??oEV?\
1815
???O??_CJNacABK?G?OAwP??b???GNPyGPCG@???"
1816
dc64 = Graph(dc64_g6string)
1817
dc64.name(new="dc64")
1818
1819
try:
1820
s = load(os.environ['HOME'] +'/objects-invariants-properties/dc1024_g6string.sobj')
1821
print "loaded graph dc1024"
1822
dc1024 = Graph(s)
1823
dc1024.name(new="dc1024")
1824
except:
1825
print "couldn't load dc1024_g6string.sobj"
1826
1827
try:
1828
s = load(os.environ['HOME'] +'/objects-invariants-properties/dc2048_g6string.sobj')
1829
print "loaded graph dc2048"
1830
dc2048 = Graph(s)
1831
dc2048.name(new="dc2048")
1832
except:
1833
print "couldn't load dc2048_g6string.sobj"
1834
1835
#graph from delavina's jets paper
1836
starfish = Graph('N~~eeQoiCoM?Y?U?F??')
1837
starfish.name(new="starfish")
1838
1839
#difficult graph from INP: order=11, alpha=4, best lower bound < 3
1840
difficult11 = Graph('J?`FBo{fdb?')
1841
difficult11.name(new="difficult11")
1842
1843
#c4 joined to K# at point: not KE, alpha=theta=nu=3, delting any vertex gives KE graph
1844
c5k3=Graph('FheCG')
1845
c5k3.name(new="c5k3")
1846
1847
#mycielskian of a triangle: CE to conj that chi <= max(clique, nu), chi=4, nu = clique = 3
1848
c3mycielski = Graph('FJnV?')
1849
c3mycielski.name(new="c3mycieski")
1850
1851
#4th mycielskian of a triangle, CE to conj chi <= clique + girth, chi = 7, clique = girth = 3
1852
c3mycielski4 = Graph('~??~??GWkYF@BcuIsJWEo@s?N?@?NyB`qLepJTgRXkAkU?JPg?VB_?W[??Ku??BU_??ZW??@u???Bs???Bw???A??F~~_B}?^sB`o[MOuZErWatYUjObXkZL_QpWUJ?CsYEbO?fB_w[?A`oCM??DL_Hk??DU_Is??Al_Dk???l_@k???Ds?M_???V_?{????oB}?????o[M?????WuZ?????EUjO?????rXk?????BUJ??????EsY??????Ew[??????B`o???????xk???????FU_???????\\k????????|_????????}_????????^_?????????')
1853
c3mycielski4.name(new="c3mycielski4")
1854
1855
# a PAW is a traingle with a pendant, same as a Z1
1856
paw=Graph('C{')
1857
paw.name(new="paw")
1858
1859
binary_octahedron = Graph('L]lw??B?oD_Noo')
1860
#2 octahedrons, remove one edge from each, add vertex, connect it to deleted edge vertices
1861
#its regular of degree 4
1862
binary_octahedron.name(new = "binary_octahedron")
1863
1864
#this graph shows that the cartesian product of 2 KE graphs is not necessarily KE
1865
# appears in Abay-Asmerom, Ghidewon, et al. "Notes on the independence number in the Cartesian product of graphs." Discussiones Mathematicae Graph Theory 31.1 (2011): 25-35.
1866
paw_x_paw = paw.cartesian_product(paw)
1867
paw_x_paw.name(new = "paw_x_paw")
1868
1869
#a KITE is a C4 with a chord
1870
kite = Graph('Cn')
1871
kite.name(new="kite")
1872
1873
#a DART is a kite with a pendant
1874
dart = Graph('DnC')
1875
dart.name(new="dart")
1876
1877
#P4 is a path on 4 vertices
1878
p4=Graph('Ch')
1879
p4.name(new="p4")
1880
1881
p5 = graphs.PathGraph(5)
1882
p5.name(new = "p5")
1883
1884
c9 = graphs.CycleGraph(9)
1885
c9.name(new = "c9")
1886
1887
ce3=Graph("Gr`HOk")
1888
ce3.name(new = "ce3")
1889
#ce3 is a ce to (((is_planar)&(is_regular))&(is_bipartite))->(has_residue_equals_alpha)
1890
1891
ce2=Graph("HdGkCA?")
1892
#ce2 is a ce to ((is_chordal)^(is_forest))->(has_residue_equals_alpha)
1893
ce2.name(new = "ce2")
1894
1895
c6 = graphs.CycleGraph(6)
1896
c6.name(new = "c6")
1897
1898
ce4=Graph("G~sNp?")
1899
ce4.name(new = "ce4")
1900
#ce4 is a ce to ((~(is_planar))&(is_chordal))->(has_residue_equals_alpha)
1901
1902
ce5=Graph("X~}AHKVB{GGPGRCJ`B{GOO`C`AW`AwO`}CGOO`AHACHaCGVACG^")
1903
ce5.name(new = "ce5")
1904
#ce5 is a ce to: (((is_line_graph)&(is_cartesian_product))|(is_split))->(has_residue_equals_alpha)
1905
1906
ce6 = Graph("H??E@cN")
1907
#ce6 is a ce to: (is_split)->((order_leq_twice_max_degree)&(is_chordal))
1908
ce6.name(new = "ce6")
1909
1910
ce7 = Graph("FpGK?")
1911
#counterexample to: (has_residue_equals_alpha)->((is_bipartite)->(order_leq_twice_max_degree))
1912
ce7.name(new = "ce7")
1913
1914
ce8 = Graph('IxCGGC@_G')
1915
#counterexample to: ((has_paw)&(is_circular_planar))->(has_residue_equals_alpha)
1916
ce8.name(new = "ce8")
1917
1918
ce9 = Graph('IhCGGD?G?')
1919
#counterexample to: ((has_H)&(is_forest))->(has_residue_equals_alpha)
1920
ce9.name(new = "ce9")
1921
1922
ce10=Graph('KxkGGC@?G?o@')
1923
#counterexample to: (((is_eulerian)&(is_planar))&(has_paw))->(has_residue_equals_alpha)
1924
ce10.name(new = "ce10")
1925
1926
ce11 = Graph("E|OW")
1927
#counterexample to: (has_alpha_residue_equal_two)->((is_perfect)|(is_regular))
1928
ce11.name(new = "ce11")
1929
1930
ce12 = Graph("Edo_")
1931
#counterexample to: (((is_cubic)&(is_triangle_free))&(is_H_free))->(has_residue_equals_two)
1932
ce12.name(new = "ce12")
1933
1934
ce13 = Graph("ExOG")
1935
#counterexample to: ((diameter_equals_twice_radius)&(is_claw_free))->(has_residue_equals_two)
1936
ce13.name(new = "ce13")
1937
1938
ce14 = Graph('IhCGGC_@?')
1939
#counterexample to: (~(matching_covered))->(has_residue_equals_alpha)
1940
ce14.name(new = "IhCGGC_@?")
1941
1942
#a K5 with a pendant, CE to dirac => regular or planar conjecture
1943
k5pendant = Graph('E~}?')
1944
k5pendant.name(new="k5pendant")
1945
1946
#same as H
1947
killer = Graph('EgSG')
1948
killer.name(new="killer")
1949
1950
#alon_seymour graph: CE to the rank-coloring conjecture, 56-regular, vertex_trans, alpha=2, omega=22, chi=chi'=edge_connect=56
1951
alon_seymour=Graph([[0..63], lambda x,y : operator.xor(x,y) not in (0,1,2,4,8,16,32,63)])
1952
alon_seymour.name(new="alon_seymour")
1953
1954
k3 = graphs.CompleteGraph(3)
1955
k3.name(new="k3")
1956
1957
k4 = graphs.CompleteGraph(4)
1958
k4.name(new="k4")
1959
1960
k5 = graphs.CompleteGraph(5)
1961
k5.name(new="k5")
1962
1963
k6 = graphs.CompleteGraph(6)
1964
k6.name(new="k6")
1965
1966
c4 = graphs.CycleGraph(4)
1967
c4.name(new="c4")
1968
1969
p2 = graphs.PathGraph(2)
1970
p2.name(new="p2")
1971
1972
p6 = graphs.PathGraph(6)
1973
p6.name(new="p6")
1974
1975
#star with 3 rays, order = 4
1976
s3 = graphs.StarGraph(3)
1977
s3.name(new="s3")
1978
1979
k10 = graphs.CompleteGraph(10)
1980
k10.name(new="k10")
1981
1982
c60 = graphs.BuckyBall()
1983
c60.name(new="c60")
1984
1985
#moser spindle
1986
moser = Graph('Fhfco')
1987
moser.name(new = "moser")
1988
1989
#Holt graph is smallest graph which is edge-transitive but not arc-transitive
1990
holt = graphs.HoltGraph()
1991
holt.name(new = "holt")
1992
1993
golomb = Graph("I?C]dPcww")
1994
golomb.name(new = "golomb")
1995
1996
edge_critical_5=graphs.CycleGraph(5)
1997
edge_critical_5.add_edge(0,3)
1998
edge_critical_5.add_edge(1,4)
1999
edge_critical_5.name(new="edge_critical_5")
2000
2001
#a CE to alpha >= min{e-n+1,diameter}
2002
heather = graphs.CompleteGraph(4)
2003
heather.add_vertex()
2004
heather.add_vertex()
2005
heather.add_edge(0,4)
2006
heather.add_edge(5,4)
2007
heather.name(new="heather")
2008
2009
pete = graphs.PetersenGraph()
2010
2011
#residue = alpha = 3, a CE to conjecture that residue=alpha => is_ore
2012
ryan3=graphs.CycleGraph(15)
2013
for i in range(15):
2014
for j in [1,2,3]:
2015
ryan3.add_edge(i,(i+j)%15)
2016
ryan3.add_edge(i,(i-j)%15)
2017
ryan3.name(new="ryan3")
2018
2019
#sylvester graph: 3-reg, 3 bridges, no perfect matching (why Petersen theorem requires no more than 2 bridges)
2020
sylvester = Graph('Olw?GCD@o??@?@?A_@o`A')
2021
sylvester.name(new="sylvester")
2022
2023
fork=graphs.PathGraph(4)
2024
fork.add_vertex()
2025
fork.add_edge(1,4)
2026
fork.name(new="fork")
2027
2028
#one of the 2 order 11 chromatic edge-critical graphs discovered by brinkmann and steffen
2029
edge_critical_11_1 = graphs.CycleGraph(11)
2030
edge_critical_11_1.add_edge(0,2)
2031
edge_critical_11_1.add_edge(1,6)
2032
edge_critical_11_1.add_edge(3,8)
2033
edge_critical_11_1.add_edge(5,9)
2034
edge_critical_11_1.name(new="edge_critical_11_1")
2035
2036
#one of the 2 order 11 chromatic edge-critical graphs discovered by brinkmann and steffen
2037
edge_critical_11_2 = graphs.CycleGraph(11)
2038
edge_critical_11_2.add_edge(0,2)
2039
edge_critical_11_2.add_edge(3,7)
2040
edge_critical_11_2.add_edge(6,10)
2041
edge_critical_11_2.add_edge(4,9)
2042
edge_critical_11_2.name(new="edge_critical_11_2")
2043
2044
#chromatic_index_critical but not overfull
2045
pete_minus=graphs.PetersenGraph()
2046
pete_minus.delete_vertex(9)
2047
pete_minus.name(new="pete_minus")
2048
2049
bow_tie = Graph(5)
2050
bow_tie.add_edge(0,1)
2051
bow_tie.add_edge(0,2)
2052
bow_tie.add_edge(0,3)
2053
bow_tie.add_edge(0,4)
2054
bow_tie.add_edge(1,2)
2055
bow_tie.add_edge(3,4)
2056
bow_tie.name(new = "bow_tie")
2057
2058
2059
2060
"""
2061
The Haemers graph was considered by Haemers who showed that alpha(G)=theta(G)<vartheta(G).
2062
The graph is a 108-regular graph on 220 vertices. The vertices correspond to the 3-element
2063
subsets of {1,...,12} and two such vertices are adjacent whenever the subsets
2064
intersect in exactly one element.
2065
2066
sage: haemers
2067
haemers: Graph on 220 vertices
2068
sage: haemers.is_regular()
2069
True
2070
sage: max(haemers.degree())
2071
108
2072
"""
2073
haemers = Graph([Subsets(12,3), lambda s1,s2: len(s1.intersection(s2))==1])
2074
haemers.relabel()
2075
haemers.name(new="haemers")
2076
2077
"""
2078
The Pepper residue graph was described by Ryan Pepper in personal communication.
2079
It is a graph which demonstrates that the residue is not monotone. The graph is
2080
constructed by taking the complete graph on 3 vertices and attaching a pendant
2081
vertex to each of its vertices, then taking two copies of this graph, adding a
2082
vertex and connecting it to all the pendant vertices. This vertex has degree
2083
sequence [6, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2] which gives residue equal to 4.
2084
By removing the central vertex with degree 6, you get a graph with degree
2085
sequence [3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1] which has residue equal to 5.
2086
2087
sage: pepper_residue_graph
2088
pepper_residue_graph: Graph on 13 vertices
2089
sage: sorted(pepper_residue_graph.degree(), reverse=True)
2090
[6, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2]
2091
sage: residue(pepper_residue_graph)
2092
4
2093
sage: residue(pepper_residue_graph.subgraph(vertex_property=lambda v:pepper_residue_graph.degree(v)<6))
2094
5
2095
"""
2096
pepper_residue_graph = graphs.CompleteGraph(3)
2097
pepper_residue_graph.add_edges([(i,i+3) for i in range(3)])
2098
pepper_residue_graph = pepper_residue_graph.disjoint_union(pepper_residue_graph)
2099
pepper_residue_graph.add_edges([(0,v) for v in pepper_residue_graph.vertices() if pepper_residue_graph.degree(v)==1])
2100
pepper_residue_graph.relabel()
2101
pepper_residue_graph.name(new="pepper_residue_graph")
2102
2103
"""
2104
The Barrus graph was suggested by Mike Barrus in "Havel-Hakimi residues of Unigraphs" (2012) as an example of a graph whose residue (2) is
2105
less than the independence number of any realization of the degree sequence. The degree sequence is [4^8,2].
2106
The realization is the one given by reversing the Havel-Hakimi process.
2107
2108
sage: barrus_graph
2109
barrus_graph: Graph on 9 vertices
2110
sage: residue(barrus_graph)
2111
2
2112
sage: independence_number(barrus_graph)
2113
3
2114
"""
2115
barrus_graph = Graph('HxNEG{W')
2116
barrus_graph.name(new = "barrus_graph")
2117
2118
#CE to conjecture: (is_split)->((is_eulerian)->(is_regular))
2119
#split graph from k4 and e2 that is eulerian but not regular
2120
k4e2split = graphs.CompleteGraph(4)
2121
k4e2split.add_vertices([4,5])
2122
k4e2split.add_edge(4,0)
2123
k4e2split.add_edge(4,1)
2124
k4e2split.add_edge(5,2)
2125
k4e2split.add_edge(5,3)
2126
k4e2split.name(new = "k4e2split")
2127
2128
houseX=graphs.HouseXGraph()
2129
houseX.name(new = "houseX")
2130
2131
triangle_star = Graph("H}qdB@_")
2132
#a counterexample to: (has_residue_equals_alpha)->((is_eulerian)->(alpha_leq_order_over_two))
2133
triangle_star.name(new = "triangle_star")
2134
2135
#flower with n petals
2136
def flower(n):
2137
g = graphs.StarGraph(2*n)
2138
for x in range(n):
2139
v = 2*x+1
2140
g.add_edge(v,v+1)
2141
return g
2142
2143
flower_with_3_petals = flower(3)
2144
flower_with_3_petals.name(new = "flower_with_3_petals")
2145
2146
flower_with_4_petals = flower(4)
2147
flower_with_4_petals.name(new = "flower_with_4_petals")
2148
2149
#GRAPH LISTS
2150
2151
#all with order 3 to 9, a graph is chroamtic_index_critical if it is class 2 removing any edge increases chromatic index
2152
2153
#all with order 3 to 9, a graph is alpha_critical if removing any edge increases independence number
2154
#all alpha critical graphs of orders 2 to 9, 53 in total
2155
alpha_critical_graph_names = ['A_','Bw', 'C~', 'Dhc', 'D~{', 'E|OW', 'E~~w', 'FhCKG', 'F~[KG', 'FzEKW', 'Fn[kG', 'F~~~w', 'GbL|TS', 'G~?mvc', 'GbMmvG', 'Gb?kTG', 'GzD{Vg', 'Gb?kR_', 'GbqlZ_', 'GbilZ_', 'G~~~~{', 'GbDKPG', 'HzCGKFo', 'H~|wKF{', 'HnLk]My', 'HhcWKF_', 'HhKWKF_', 'HhCW[F_', 'HxCw}V`', 'HhcGKf_', 'HhKGKf_', 'Hh[gMEO', 'HhdGKE[', 'HhcWKE[', 'HhdGKFK', 'HhCGGE@', 'Hn[gGE@', 'Hn^zxU@', 'HlDKhEH', 'H~~~~~~', 'HnKmH]N', 'HnvzhEH', 'HhfJGE@', 'HhdJGM@', 'Hj~KHeF', 'HhdGHeB', 'HhXg[EO', 'HhGG]ES', 'H~Gg]f{', 'H~?g]vs', 'H~@w[Vs', 'Hn_k[^o']
2156
alpha_critical_easy = []
2157
for s in alpha_critical_graph_names:
2158
g = Graph(s)
2159
g.name(new="alpha_critical_"+ s)
2160
alpha_critical_easy.append(g)
2161
2162
#all order-7 chromatic_index_critical_graphs (and all are overfull)
2163
L = ['FhCKG', 'FzCKW', 'FzNKW', 'FlSkG', 'Fn]kG', 'FlLKG', 'FnlkG', 'F~|{G', 'FnlLG', 'F~|\\G', 'FnNLG', 'F~^LW', 'Fll\\G', 'FllNG', 'F~l^G', 'F~|^w', 'F~~^W', 'Fnl^W', 'FlNNG', 'F|\\Kg', 'F~^kg', 'FlKMG']
2164
chromatic_index_critical_7 = []
2165
for s in L:
2166
g=Graph(s)
2167
g.name(new="chromatic_index_critical_7_" + s)
2168
chromatic_index_critical_7.append(g)
2169
2170
#class 0 pebbling graphs
2171
import pickle, os, os.path
2172
try:
2173
class0graphs_dict = pickle.load(open(os.environ['HOME'] + "/objects-invariants-properties/class0graphs_dictionary.pickle","r"))
2174
except:
2175
class0graphs_dict = {}
2176
class0graphs = []
2177
for d in class0graphs_dict:
2178
g = Graph(class0graphs_dict[d])
2179
g.name(new = d)
2180
class0graphs.append(g)
2181
class0small = [g for g in class0graphs if g.order() < 30]
2182
2183
c5=graphs.CycleGraph(5)
2184
c5.name(new = "c5")
2185
2186
graph_objects = [paw, kite, p4, dart, k3, k4, k5, c6ee, c5chord, graphs.DodecahedralGraph(), c8chorded, c8chords, graphs.ClebschGraph(), prismy, c24, c26, c60, c6xc6, holton_mckay, sixfour, c4, graphs.PetersenGraph(), p2, graphs.TutteGraph(), non_ham_over, throwing, throwing2, throwing3, kratsch_lehel_muller, graphs.BlanusaFirstSnarkGraph(), graphs.BlanusaSecondSnarkGraph(), graphs.FlowerSnark(), s3, ryan3, k10, graphs.MycielskiGraph(5), c3mycielski, s13e, ryan2, s22e, difficult11, graphs.BullGraph(), graphs.ChvatalGraph(), graphs.ClawGraph(), graphs.DesarguesGraph(), graphs.DiamondGraph(), graphs.FlowerSnark(), graphs.FruchtGraph(), graphs.HoffmanSingletonGraph(), graphs.HouseGraph(), graphs.OctahedralGraph(), graphs.ThomsenGraph(), pete , graphs.PappusGraph(), graphs.GrotzschGraph(), graphs.GrayGraph(), graphs.HeawoodGraph(), graphs.HerschelGraph(), graphs.CoxeterGraph(), graphs.BrinkmannGraph(), graphs.TutteCoxeterGraph(), graphs.TutteGraph(), graphs.RobertsonGraph(), graphs.FolkmanGraph(), graphs.Balaban10Cage(), graphs.PappusGraph(), graphs.TietzeGraph(), graphs.SylvesterGraph(), graphs.SzekeresSnarkGraph(), graphs.MoebiusKantorGraph(), ryan, inp, c4c4, regular_non_trans, bridge, p10k4, c100, starfish, c5k3, k5pendant, graphs.ShrikhandeGraph(), sylvester, fork, edge_critical_5, edge_critical_11_1, edge_critical_11_2, pete_minus, c5, bow_tie, pepper_residue_graph, barrus_graph, p5, c6, c9, ce3, ce4, ce5, k4e2split, flower_with_3_petals, flower_with_4_petals, paw_x_paw, graphs.WagnerGraph(), houseX, ce7, triangle_star, ce8, ce9, ce10, p3, binary_octahedron, ce11, prism, ce12, ce13, ce14] + alpha_critical_easy
2187
2188
alpha_critical_hard = [Graph('Hj\\x{F{')]
2189
2190
chromatic_index_critical_graphs = chromatic_index_critical_7 + [edge_critical_5, edge_critical_11_1, edge_critical_11_2, pete_minus]
2191
2192
#graphs where some computations are especially slow
2193
problem_graphs = [graphs.MeredithGraph(), graphs.SchlaefliGraph(), haemers, c3mycielski4, alon_seymour] + chromatic_index_critical_7 + class0small + alpha_critical_hard
2194
#meredith graph is 4-reg, class2, non-hamiltonian: http://en.wikipedia.org/wiki/Meredith_graph
2195
2196
2197
#graph_objects: all graphs with no duplicates
2198
2199
#obvious way to remove duplicates in list of ALL objects
2200
2201
"""
2202
graph_objects = []
2203
for g in union_objects, idfun=Graph.graph6_string:
2204
if not g in graph_objects:
2205
graph_objects.append(g)
2206
"""
2207
2208
#fast way to remove duplicates in list of ALL objects
2209
#from : http://www.peterbe.com/plog/uniqifiers-benchmark
2210
2211
2212
def remove_duplicates(seq, idfun=None):
2213
# order preserving
2214
if idfun is None:
2215
def idfun(x): return x
2216
seen = {}
2217
result = []
2218
for item in seq:
2219
marker = idfun(item)
2220
# in old Python versions:
2221
# if seen.has_key(marker)
2222
# but in new ones:
2223
if marker in seen: continue
2224
seen[marker] = 1
2225
result.append(item)
2226
return result
2227
2228
#could run this occasionally to check there are no duplicates
2229
#graph_objects = remove_duplicates(union_objects, idfun=Graph.graph6_string)
2230
2231
2232
2233
#TESTING
2234
2235
#check for invariant relation that separtates G from class defined by property
2236
def find_separating_invariant_relation(g, objects, property, invariants):
2237
L = [x for x in objects if (property)(x)]
2238
for inv1 in invariants:
2239
for inv2 in invariants:
2240
if inv1(g) > inv2(g) and all(inv1(x) <= inv2(x) for x in L):
2241
return inv1.__name__, inv2.__name__
2242
print "no separating invariants"
2243
2244
2245
2246
#finds "difficult" graphs for necessary conditions, finds graphs which don't have property but which have all necessary conditions
2247
def test_properties_upper_bound_theory(objects, property, theory):
2248
for g in objects:
2249
if not property(g) and all(f(g) for f in theory):
2250
print g.name()
2251
2252
#finds "difficult" graphs for sufficient conditions, finds graphs which dont have any sufficient but do have property
2253
def test_properties_lower_bound_theory(objects, property, theory):
2254
for g in objects:
2255
if property(g) and not any(f(g) for f in theory):
2256
print g.name()
2257
2258
def find_coextensive_properties(objects, properties):
2259
for p1 in properties:
2260
for p2 in properties:
2261
if p1 != p2 and all(p1(g) == p2(g) for g in objects):
2262
print p1.__name__, p2.__name__
2263
print "DONE!"
2264
2265
2266
#load graph property data dictionary, if one exists
2267
try:
2268
graph_property_data = load(os.environ['HOME'] +'/objects-invariants-properties/graph_property_data.sobj')
2269
print "loaded graph properties data file"
2270
except IOError:
2271
print "can't load graph properties sobj file"
2272
graph_property_data = {}
2273
2274
2275
2276
#this version will open existing data file, and update as needed
2277
def update_graph_property_data(new_objects,properties):
2278
global graph_property_data
2279
#try to open existing sobj dictionary file, else initialize empty one
2280
try:
2281
graph_property_data = load(os.environ['HOME'] +'/objects-invariants-properties/graph_property_data.sobj')
2282
except IOError:
2283
print "can't load properties sobj file"
2284
graph_property_data = {}
2285
2286
#check for graph key, if it exists load the current dictionary, if not use empty prop_value_dict as *default*
2287
for g in new_objects:
2288
print g.name()
2289
if g.name not in graph_property_data.keys():
2290
graph_property_data[g.name()] = {}
2291
2292
#check for property key, if it exists load the current dictionary, if not initialize an empty dictionary for property
2293
for prop in properties:
2294
try:
2295
graph_property_data[g.name()][prop.__name__]
2296
except KeyError:
2297
graph_property_data[g.name()][prop.__name__] = prop(g)
2298
2299
save(graph_property_data, "graph_property_data.sobj")
2300
print "DONE"
2301
2302
#load graph property data dictionary, if one exists
2303
try:
2304
graph_invariant_data = load(os.environ['HOME'] +'/objects-invariants-properties/graph_invariant_data.sobj')
2305
print "loaded graph invariants data file"
2306
except IOError:
2307
print "can't load graph invariant sobj file"
2308
graph_invariant_data = {}
2309
2310
2311
#this version will open existing data file, and update as needed
2312
def update_graph_invariant_data(new_objects,invariants):
2313
#try to open existing sobj dictionary file, else initialize empty one
2314
global graph_invariant_data
2315
try:
2316
graph_invariant_data = load(os.environ['HOME'] +'/objects-invariants-properties/graph_invariant_data.sobj')
2317
print "loaded graph invariants data file"
2318
except IOError:
2319
print "can't load invariant sobj file"
2320
graph_invariant_data = {}
2321
2322
#check for graph key, if it exists load the current dictionary, if not use empty prop_value_dict as *default*
2323
for g in new_objects:
2324
print g.name()
2325
if g.name not in graph_invariant_data.keys():
2326
graph_invariant_data[g.name()] = {}
2327
2328
#check for property key, if it exists load the current dictionary, if not initialize an empty dictionary for property
2329
for inv in invariants:
2330
try:
2331
graph_invariant_data[g.name()][inv.__name__]
2332
except KeyError:
2333
graph_invariant_data[g.name()][inv.__name__] = inv(g)
2334
2335
save(graph_invariant_data, "graph_invariant_data.sobj")
2336
print "DONE"
2337
2338