Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/graph_database.py
4036 views
1
"""
2
Graph database
3
4
INFO:
5
6
This module implements classes (GraphDatabase, GraphQuery,
7
GenericGraphQuery) for interfacing with the sqlite database
8
graphs.db.
9
10
The GraphDatabase class interfaces with the sqlite database
11
graphs.db. It is an immutable database that inherits from
12
SQLDatabase (see sage.databases.database.py).
13
14
The database contains all unlabeled graphs with 7 or fewer nodes.
15
This class will also interface with the optional database package
16
containing all unlabeled graphs with 8 or fewer nodes. The
17
database(s) consists of five tables, and has the structure given by
18
the function graph_info. (For a full description including column
19
data types, create a GraphDatabase instance and call the method
20
get_skeleton).
21
22
AUTHORS:
23
24
- Emily A. Kirkman (2008-09-20): first version of interactive queries,
25
cleaned up code and generalized many elements to
26
sage.databases.database.py
27
28
- Emily A. Kirkman (2007-07-23): inherits GenericSQLDatabase, also
29
added classes: GraphQuery and GenericGraphQuery
30
31
- Emily A. Kirkman (2007-05-11): initial sqlite version
32
33
- Emily A. Kirkman (2007-02-13): initial version (non-sqlite)
34
35
REFERENCES:
36
37
- Data provided by Jason Grout (Brigham Young University). [Online]
38
Available: http://math.byu.edu/~grout/graphs/
39
"""
40
41
################################################################################
42
# Copyright (C) 2007 Emily A. Kirkman
43
#
44
#
45
# Distributed under the terms of the GNU General Public License (GPL)
46
# http://www.gnu.org/licenses/
47
################################################################################
48
49
import graph
50
import re
51
from sage.rings.integer import Integer
52
from sqlite3 import dbapi2 as sqlite # if anyone would like to explain why dbapi2...
53
from sage.databases.sql_db import SQLDatabase, SQLQuery
54
from sage.misc.misc import SAGE_DATA
55
from sage.graphs.graph import Graph
56
dblocation = SAGE_DATA + '/graphs/graphs.db'
57
58
def degseq_to_data(degree_sequence):
59
"""
60
Takes a degree sequence list (of Integers) and converts to a sorted
61
(max-min) integer data type, as used for faster access in the
62
underlying database.
63
64
EXAMPLE::
65
66
sage: from sage.graphs.graph_database import degseq_to_data
67
sage: degseq_to_data([2,2,3,1])
68
3221
69
"""
70
degree_sequence.sort()
71
return sum(degree_sequence[i]*10**i for i in range(len(degree_sequence)))
72
73
def data_to_degseq(data, graph6=None):
74
"""
75
Takes the database integer data type (one digit per vertex
76
representing its degree, sorted high to low) and converts it to
77
degree sequence list. The graph6 identifier is required for all
78
graphs with no edges, so that the correct number of zeros will be
79
returned.
80
81
EXAMPLE::
82
83
sage: from sage.graphs.graph_database import data_to_degseq
84
sage: data_to_degseq(3221)
85
[1, 2, 2, 3]
86
sage: data_to_degseq(0,'D??')
87
[0, 0, 0, 0, 0]
88
"""
89
degseq = Integer(data).digits(10)
90
if not degseq:
91
# compute number of 0's in list from graph6 string
92
from sage.graphs.generic_graph_pyx import N_inverse
93
return N_inverse(str(graph6))[0]*[0]
94
else:
95
return degseq
96
97
def graph6_to_plot(graph6):
98
"""
99
Constructs a graph from a graph6 string and returns a Graphics
100
object with arguments preset for show function.
101
102
EXAMPLE::
103
104
sage: from sage.graphs.graph_database import graph6_to_plot
105
sage: type(graph6_to_plot('D??'))
106
<class 'sage.plot.graphics.Graphics'>
107
"""
108
g = Graph(str(graph6))
109
return g.plot(layout='circular',vertex_size=30,vertex_labels=False,graph_border=False)
110
111
def subgraphs_to_query(subgraphs, db):
112
"""
113
Constructs and returns a GraphQuery object respecting the special
114
input required for the induced_subgraphs parameter. This input can
115
be an individual graph6 string (in which case it is evaluated
116
without the use of this method) or a list of strings. In the latter
117
case, the list should be of one of the following two formats: 1.
118
['one_of',String,...,String] Will search for graphs containing a
119
subgraph isomorphic to any of the graph6 strings in the list. 2.
120
['all_of',String,...,String] Will search for graphs containing a
121
subgraph isomorphic to each of the graph6 strings in the list.
122
123
This is a helper method called by the GraphQuery constructor to
124
handle this special format. This method should not be used on its
125
own because it doesn't set any display columns in the query string,
126
causing a failure to fetch the data when run.
127
128
EXAMPLE::
129
130
sage: from sage.graphs.graph_database import subgraphs_to_query
131
sage: gd = GraphDatabase()
132
sage: q = subgraphs_to_query(['all_of','A?','B?','C?'],gd)
133
sage: q.get_query_string()
134
'SELECT ,,,,, FROM misc WHERE ( ( misc.induced_subgraphs regexp ? ) AND (
135
misc.induced_subgraphs regexp ? ) ) AND ( misc.induced_subgraphs regexp ? )'
136
"""
137
q = GraphQuery(graph_db=db,induced_subgraphs=subgraphs[1])
138
if subgraphs[0] == 'all_of':
139
for i in range(len(subgraphs))[2:]:
140
q.intersect(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True)
141
elif subgraphs[0] == 'one_of':
142
for i in range(len(subgraphs))[2:]:
143
q.union(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True)
144
else:
145
raise KeyError, 'Unable to initiate query: Illegal input format for induced_subgraphs.'
146
return q
147
148
# tables columns input data type sqlite data type
149
# -----------------------------------------------------------------------------
150
aut_grp = ['aut_grp_size', # Integer INTEGER
151
'num_orbits', # Integer INTEGER
152
'num_fixed_points', # Integer INTEGER
153
'vertex_transitive', # bool BOOLEAN
154
'edge_transitive'] # bool BOOLEAN
155
degrees = ['degree_sequence', # list INTEGER (see degseq_to_data module function)
156
'min_degree', # Integer INTEGER
157
'max_degree', # Integer INTEGER
158
'average_degree', # Real REAL
159
'degrees_sd', # Real REAL
160
'regular'] # bool BOOLEAN
161
misc = ['vertex_connectivity', # Integer INTEGER
162
'edge_connectivity', # Integer INTEGER
163
'num_components', # Integer INTEGER
164
'girth', # Integer INTEGER
165
'radius', # Integer INTEGER
166
'diameter', # Integer INTEGER
167
'clique_number', # Integer INTEGER
168
'independence_number', # Integer INTEGER
169
'num_cut_vertices', # Integer INTEGER
170
'min_vertex_cover_size', # Integer INTEGER
171
'num_spanning_trees', # Integer INTEGER
172
'induced_subgraphs'] # String STRING
173
spectrum = ['spectrum', # String STRING
174
'min_eigenvalue', # Real REAL
175
'max_eigenvalue', # Real REAL
176
'eigenvalues_sd', # Real REAL
177
'energy'] # Real REAL
178
graph_data=['complement_graph6', # String STRING
179
'eulerian', # bool BOOLEAN
180
'graph6', # String STRING
181
'lovasz_number', # Real REAL
182
'num_cycles', # Integer INTEGER
183
'num_edges', # Integer INTEGER
184
'num_hamiltonian_cycles', # Integer INTEGER
185
'num_vertices', # Integer INTEGER
186
'perfect', # bool BOOLEAN
187
'planar'] # bool BOOLEAN
188
189
valid_kwds = aut_grp + degrees + misc + spectrum + graph_data
190
191
def graph_db_info(tablename=None):
192
"""
193
Returns a dictionary of allowed table and column names.
194
195
INPUT:
196
197
198
- ``tablename`` - restricts the output to a single
199
table
200
201
202
EXAMPLE::
203
204
sage: graph_db_info().keys()
205
['graph_data', 'degrees', 'spectrum', 'misc', 'aut_grp']
206
207
::
208
209
sage: graph_db_info(tablename='graph_data')
210
['complement_graph6',
211
'eulerian',
212
'graph6',
213
'lovasz_number',
214
'num_cycles',
215
'num_edges',
216
'num_hamiltonian_cycles',
217
'num_vertices',
218
'perfect',
219
'planar']
220
"""
221
info = {'graph_data': graph_data,
222
'aut_grp': aut_grp,
223
'degrees': degrees,
224
'misc': misc,
225
'spectrum': spectrum}
226
if tablename is not None:
227
info = info[tablename]
228
return info
229
230
class GenericGraphQuery(SQLQuery):
231
232
def __init__(self, query_string, database=None, param_tuple=None):
233
"""
234
A query for a GraphDatabase.
235
236
INPUT:
237
238
239
- ``database`` - the GraphDatabase instance to query
240
(if None then a new instance is created)
241
242
- ``query_string`` - a string representing the SQL
243
query
244
245
- ``param_tuple`` - a tuple of strings - what to
246
replace question marks in query_string with (optional, but a good
247
idea)
248
249
250
.. note::
251
252
This query class is generally intended for developers and
253
more advanced users. It allows you to execute any query,
254
and so may be considered unsafe.
255
256
See GraphDatabase class docstrings or enter::
257
258
sage: G = GraphDatabase()
259
sage: G.get_skeleton()
260
{...
261
262
to see the underlying structure of the database. Also see
263
SQLQuery in sage.databases.database for more info and a
264
tutorial.
265
266
A piece of advice about '?' and param_tuple: It is generally
267
considered safer to query with a '?' in place of each value
268
parameter, and using a second argument (a tuple of strings) in a
269
call to the sqlite database. Successful use of the param_tuple
270
argument is exemplified::
271
272
sage: G = GraphDatabase()
273
sage: q = 'select graph_id,graph6,num_vertices,num_edges from graph_data where graph_id<=(?) and num_vertices=(?)'
274
sage: param = (22,5)
275
sage: Q = SQLQuery(G,q,param)
276
sage: Q.show()
277
graph_id graph6 num_vertices num_edges
278
--------------------------------------------------------------------------------
279
18 D?? 5 0
280
19 D?C 5 1
281
20 D?K 5 2
282
21 D@O 5 2
283
22 D?[ 5 3
284
"""
285
if database == None: database = GraphDatabase()
286
if not isinstance(database, GraphDatabase):
287
raise TypeError('%s is not a valid GraphDatabase'%database)
288
SQLQuery.__init__(self,database,query_string,param_tuple)
289
290
class GraphQuery(GenericGraphQuery):
291
292
def __init__(self, graph_db=None, query_dict=None, display_cols=None, **kwds):
293
"""
294
A query for an instance of GraphDatabase. This class nicely wraps
295
the SQLQuery class located in sage.databases.database.py to make
296
the query constraints intuitive and with as many pre-definitions as
297
possible. (i.e.: since it has to be a GraphDatabase, we already know
298
the table structure and types; and since it is immutable, we can
299
treat these as a guarantee).
300
301
.. note::
302
303
SQLQuery functions are available for GraphQuery. See
304
sage.dataabases.database.py for more details.
305
306
INPUT:
307
308
309
- ``graph_db`` - The GraphDatabase instance to apply
310
the query to. (If None, then a new instance is created).
311
312
- ``query_dict`` - A dictionary specifying the query
313
itself. Format is: 'table_name': 'tblname', 'display_cols':
314
['col1', 'col2'], 'expression':[col, operator, value] If not None,
315
query_dict will take precedence over all other arguments.
316
317
- ``display_cols`` - A list of column names (strings)
318
to display in the result when running or showing a query.
319
320
- ``kwds`` - The columns of the database are all
321
keywords. For a database table/column structure dictionary, call
322
graph_db_info. Keywords accept both single values and lists of
323
length 2. The list allows the user to specify an expression other
324
than equality. Valid expressions are strings, and for numeric
325
values (i.e. Reals and Integers) are: '=','','','=','='. String
326
values also accept 'regexp' as an expression argument. The only
327
keyword exception to this format is induced_subgraphs, which
328
accepts one of the following options: 1.
329
['one_of',String,...,String] Will search for graphs containing a
330
subgraph isomorphic to any of the graph6 strings in the list. 2.
331
['all_of',String,...,String] Will search for graphs containing a
332
subgraph isomorphic to each of the graph6 strings in the list.
333
334
335
EXAMPLES::
336
337
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
338
sage: Q.number_of()
339
35
340
sage: Q.show()
341
Graph6 Num Vertices Degree Sequence
342
------------------------------------------------------------
343
A_ 2 [1, 1]
344
BW 3 [1, 1, 2]
345
CK 4 [1, 1, 1, 1]
346
CF 4 [1, 1, 1, 3]
347
CL 4 [1, 1, 2, 2]
348
CN 4 [1, 2, 2, 3]
349
D_K 5 [1, 1, 1, 1, 2]
350
D?{ 5 [1, 1, 1, 1, 4]
351
D@s 5 [1, 1, 1, 2, 3]
352
DBg 5 [1, 1, 2, 2, 2]
353
D`K 5 [1, 1, 2, 2, 2]
354
D@{ 5 [1, 1, 2, 2, 4]
355
DIk 5 [1, 2, 2, 2, 3]
356
DBk 5 [1, 1, 2, 3, 3]
357
DK[ 5 [1, 2, 2, 2, 3]
358
E@Q? 6 [1, 1, 1, 1, 1, 1]
359
E_?w 6 [1, 1, 1, 1, 1, 3]
360
E?N? 6 [1, 1, 1, 1, 2, 2]
361
E_Cg 6 [1, 1, 1, 1, 2, 2]
362
E?Bw 6 [1, 1, 1, 1, 1, 5]
363
E?Fg 6 [1, 1, 1, 1, 2, 4]
364
E@FG 6 [1, 1, 1, 2, 2, 3]
365
E?NG 6 [1, 1, 1, 1, 3, 3]
366
E@N? 6 [1, 1, 2, 2, 2, 2]
367
E@YO 6 [1, 1, 2, 2, 2, 2]
368
E@QW 6 [1, 1, 1, 2, 2, 3]
369
E_Cw 6 [1, 1, 1, 2, 2, 3]
370
E_Ko 6 [1, 1, 2, 2, 2, 2]
371
FK??W 7 [1, 1, 1, 1, 1, 1, 2]
372
F_?@w 7 [1, 1, 1, 1, 1, 1, 4]
373
F??^? 7 [1, 1, 1, 1, 1, 2, 3]
374
F_?Hg 7 [1, 1, 1, 1, 1, 2, 3]
375
F?LCG 7 [1, 1, 1, 1, 2, 2, 2]
376
F_?XO 7 [1, 1, 1, 1, 2, 2, 2]
377
FK?GW 7 [1, 1, 1, 1, 2, 2, 2]
378
"""
379
if graph_db == None: graph_db = GraphDatabase()
380
if query_dict is not None:
381
if query_dict['expression'][0] == 'degree_sequence':
382
query_dict['expression'][3] = degseq_to_data(query_dict['expression'][3])
383
elif query_dict['expression'][0] == 'induced_subgraphs':
384
query_dict['expression'][3] = subgraphs_to_data(query_dict['expression'][3])
385
SQLQuery.__init__(self, graph_db, query_dict)
386
else:
387
# construct a query from the given parameters
388
SQLQuery.__init__(self, graph_db)
389
390
#if display_cols is None:
391
# raise TypeError, 'Nonetype display_cols cannot retrieve data.'
392
393
master_join = {}
394
395
for key in kwds:
396
# check validity
397
if not key in valid_kwds:
398
raise KeyError, '%s is not a valid key for this database.'%str(key)
399
400
# designate a query_dict
401
qdict = {'display_cols': None} # reserve display cols until end
402
# (database.py currently concatenates
403
# them including repeats)
404
405
# set table name
406
if key in graph_data: qdict['table_name'] = 'graph_data'
407
elif key in aut_grp: qdict['table_name'] = 'aut_grp'
408
elif key in degrees: qdict['table_name'] = 'degrees'
409
elif key in misc: qdict['table_name'] = 'misc'
410
elif key in spectrum: qdict['table_name'] = 'spectrum'
411
412
# set expression
413
if not isinstance(kwds[key],list):
414
if key == 'induced_subgraphs':
415
qdict['expression'] = [key, 'regexp', '.*%s.*'%(graph.Graph(kwds[key]).canonical_label()).graph6_string()]
416
else:
417
qdict['expression'] = [key, '=', kwds[key]]
418
elif key == 'degree_sequence':
419
qdict['expression'] = [key, '=', degseq_to_data(kwds[key])]
420
elif key != 'induced_subgraphs':
421
qdict['expression'] = [key] + kwds[key]
422
423
# add key parameter to query
424
join_dict = {qdict['table_name']: ('graph_id', 'graph_id')}
425
if key == 'induced_subgraphs' and isinstance(kwds[key],list):
426
self.intersect(subgraphs_to_query(kwds[key], graph_db),
427
'graph_data', join_dict, in_place=True)
428
else:
429
self.intersect(SQLQuery(graph_db, qdict), 'graph_data', join_dict,in_place=True)
430
431
# include search params (keys) in join clause
432
# again, we exclude graph_data because it is the base table
433
if qdict['table_name'] != 'graph_data':
434
master_join[qdict['table_name']] = ('graph_id', 'graph_id')
435
436
# display columns from each table
437
aut_grp_disp = ['aut_grp']
438
degrees_disp = ['degrees']
439
misc_disp = ['misc']
440
spectrum_disp = ['spectrum']
441
graph_data_disp = ['graph_data']
442
443
disp_tables = [aut_grp_disp, degrees_disp, misc_disp, spectrum_disp]
444
# graph_data intentionally left out because it is always called
445
446
# organize display
447
if display_cols is not None:
448
for col in display_cols:
449
if col in graph_data: graph_data_disp.append(col)
450
elif col in aut_grp: aut_grp_disp.append(col)
451
elif col in degrees: degrees_disp.append(col)
452
elif col in misc: misc_disp.append(col)
453
elif col in spectrum: spectrum_disp.append(col)
454
455
# finish filling master join with display tables
456
for tab in disp_tables:
457
if len(tab) > 1:
458
master_join[tab[0]] = ('graph_id', 'graph_id')
459
460
# join clause for display tables
461
join_str = 'FROM graph_data '
462
for tab in master_join:
463
join_str += 'INNER JOIN %s ON graph_data.graph_id=%s.graph_id '%(tab, tab)
464
465
# construct sql syntax substring for display cols
466
disp_str = 'SELECT graph_data.graph6, '
467
for col in graph_data_disp[1:]:
468
if col != 'graph6': disp_str += 'graph_data.%s, '%col
469
for col in aut_grp_disp[1:]: disp_str += 'aut_grp.%s, '%col
470
for col in degrees_disp[1:]: disp_str += 'degrees.%s, '%col
471
for col in misc_disp[1:]: disp_str += 'misc.%s, '%col
472
for col in spectrum_disp[1:]: disp_str += 'spectrum.%s, '%col
473
disp_str = disp_str.rstrip(', ') + ' '
474
475
# substitue disp_str and join_str back into self's query string
476
self.__query_string__ = re.sub('SELECT.*WHERE ', disp_str + join_str + \
477
'WHERE ', self.__query_string__)
478
479
def query_iterator(self):
480
"""
481
Returns an iterator over the results list of the GraphQuery.
482
483
EXAMPLE::
484
485
sage: Q = GraphQuery(display_cols=['graph6'],num_vertices=7, diameter=5)
486
sage: for g in Q:
487
... print g.graph6_string()
488
F@?]O
489
F@OKg
490
F?`po
491
F?gqg
492
FIAHo
493
F@R@o
494
FA_pW
495
FGC{o
496
FEOhW
497
498
sage: Q = GraphQuery(display_cols=['graph6'],num_vertices=7, diameter=5)
499
sage: it = iter(Q)
500
sage: while True:
501
... try: print it.next().graph6_string()
502
... except StopIteration: break
503
F@?]O
504
F@OKg
505
F?`po
506
F?gqg
507
FIAHo
508
F@R@o
509
FA_pW
510
FGC{o
511
FEOhW
512
513
"""
514
return iter(self.get_graphs_list())
515
516
__iter__ = query_iterator
517
518
def show(self, max_field_size=20, with_picture=False):
519
"""
520
Displays the results of a query in table format.
521
522
INPUT:
523
524
525
- ``max_field_size`` - width of fields in command
526
prompt version
527
528
- ``with_picture`` - whether or not to display
529
results with a picture of the graph (available only in the
530
notebook)
531
532
533
EXAMPLES::
534
535
sage: G = GraphDatabase()
536
sage: Q = GraphQuery(G, display_cols=['graph6','num_vertices','aut_grp_size'], num_vertices=4, aut_grp_size=4)
537
sage: Q.show()
538
Graph6 Num Vertices Aut Grp Size
539
------------------------------------------------------------
540
C@ 4 4
541
C^ 4 4
542
543
::
544
545
sage: R = GraphQuery(G, display_cols=['graph6','num_vertices','degree_sequence'], num_vertices=4)
546
sage: R.show()
547
Graph6 Num Vertices Degree Sequence
548
------------------------------------------------------------
549
C? 4 [0, 0, 0, 0]
550
C@ 4 [0, 0, 1, 1]
551
CB 4 [0, 1, 1, 2]
552
CK 4 [1, 1, 1, 1]
553
CF 4 [1, 1, 1, 3]
554
CJ 4 [0, 2, 2, 2]
555
CL 4 [1, 1, 2, 2]
556
CN 4 [1, 2, 2, 3]
557
C] 4 [2, 2, 2, 2]
558
C^ 4 [2, 2, 3, 3]
559
C~ 4 [3, 3, 3, 3]
560
561
Show the pictures (in notebook mode only)::
562
563
sage: S = GraphQuery(G, display_cols=['graph6','aut_grp_size'], num_vertices=4)
564
sage: S.show(with_picture=True)
565
Traceback (most recent call last):
566
...
567
NotImplementedError: Cannot display plot on command line.
568
569
Note that pictures can be turned off::
570
571
sage: S.show(with_picture=False)
572
Graph6 Aut Grp Size
573
----------------------------------------
574
C? 24
575
C@ 4
576
CB 2
577
CK 8
578
CF 6
579
CJ 6
580
CL 2
581
CN 2
582
C] 8
583
C^ 4
584
C~ 24
585
586
Show your own query (note that the output is not reformatted for
587
generic queries)::
588
589
sage: (GenericGraphQuery('select degree_sequence from degrees where max_degree=2 and min_degree >= 1',G)).show()
590
degree_sequence
591
--------------------
592
211
593
222
594
2211
595
2222
596
21111
597
22211
598
22211
599
22222
600
221111
601
221111
602
222211
603
222211
604
222211
605
222222
606
222222
607
2111111
608
2221111
609
2221111
610
2221111
611
2222211
612
2222211
613
2222211
614
2222211
615
2222222
616
2222222
617
"""
618
relabel = {}
619
for col in valid_kwds:
620
relabel[col] = ' '.join([word.capitalize() for word in col.split('_')])
621
622
if re.search('SELECT .*degree_sequence.* FROM',self.__query_string__):
623
format_cols = {'degree_sequence': (lambda x,y: data_to_degseq(x,y))}
624
else: format_cols = {}
625
if with_picture:
626
SQLQuery.show(self, max_field_size=max_field_size, \
627
plot_cols={'graph6': (lambda x: graph6_to_plot(x))}, \
628
format_cols=format_cols, id_col='graph6', \
629
relabel_cols=relabel)
630
else:
631
SQLQuery.show(self, max_field_size=max_field_size, \
632
format_cols=format_cols, relabel_cols=relabel, \
633
id_col='graph6')
634
635
def get_graphs_list(self):
636
"""
637
Returns a list of Sage Graph objects that satisfy the query.
638
639
EXAMPLES::
640
641
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
642
sage: L = Q.get_graphs_list()
643
sage: L[0]
644
Graph on 2 vertices
645
sage: len(L)
646
35
647
"""
648
from sage.graphs.graph_list import from_graph6
649
650
s = self.__query_string__
651
re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
652
q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
653
graph6_list = q.query_results()
654
return [Graph(str(g[0])) for g in graph6_list]
655
656
def number_of(self):
657
"""
658
Returns the number of graphs in the database that satisfy the
659
query.
660
661
EXAMPLES::
662
663
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
664
sage: Q.number_of()
665
35
666
"""
667
# run graphs_list and return len
668
s = self.__query_string__
669
re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
670
q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
671
return len(q.query_results())
672
673
class GraphDatabase(SQLDatabase):
674
675
def __init__(self):
676
"""
677
Graph Database
678
679
INFO:
680
681
This class interfaces with the sqlite database graphs.db. It is an
682
immutable database that inherits from SQLDatabase (see
683
sage.databases.database.py). The display functions and get_graphs_list
684
create their own queries, but it is also possible to query the
685
database by constructing either a SQLQuery.
686
687
The database contains all unlabeled graphs with 7 or fewer nodes.
688
This class will also interface with the optional database package
689
containing all unlabeled graphs with 8 or fewer nodes. The database
690
consists of five tables. For a full table and column structure,
691
call graph_db_info.
692
693
USE: The tables are associated by the unique primary key graph_id
694
(int).
695
696
To query this database, we create a GraphQuery. This can be done
697
directly with the query method or by initializing one of 1.
698
GenericGraphQuery - allows direct entry of a query string and tuple
699
of parameters. This is the route for more advanced users that are
700
familiar with SQL. 2. GraphQuery - is a wrapper of SQLQuery, a
701
general database/query wrapper of SQLite for new users.
702
703
REFERENCES:
704
705
- Data provided by Jason Grout (Brigham Young
706
University). [Online] Available:
707
http://math.byu.edu/~grout/graphs/
708
709
EXAMPLE::
710
711
sage: G = GraphDatabase()
712
sage: G.get_skeleton()
713
{u'aut_grp': {u'edge_transitive': {'index': True,
714
'unique': False,
715
'primary_key': False,
716
'sql': u'BOOLEAN'},
717
u'vertex_transitive': {'index': True,
718
'unique': False,
719
'primary_key': False,
720
'sql': u'BOOLEAN'},
721
u'aut_grp_size': {'index': True,
722
'unique': False,
723
'primary_key': False,
724
'sql': u'INTEGER'},
725
u'graph_id': {'index': False,
726
'unique': False,
727
'primary_key': False,
728
'sql': u'INTEGER'},
729
u'num_orbits': {'index': True,
730
'unique': False,
731
'primary_key': False,
732
'sql': u'INTEGER'},
733
u'num_fixed_points': {'index': True,
734
'unique': False,
735
'primary_key': False,
736
'sql': u'INTEGER'}},
737
u'degrees': {u'graph_id': {'index': False,
738
'unique': False,
739
'primary_key': False,
740
'sql': u'INTEGER'},
741
u'degrees_sd': {'index': True,
742
'unique': False,
743
'primary_key': False,
744
'sql': u'REAL'},
745
u'max_degree': {'index': True,
746
'unique': False,
747
'primary_key': False,
748
'sql': u'INTEGER'},
749
u'regular': {'index': True,
750
'unique': False,
751
'primary_key': False,
752
'sql': u'BOOLEAN'},
753
u'average_degree': {'index': True,
754
'unique': False,
755
'primary_key': False,
756
'sql': u'REAL'},
757
u'degree_sequence': {'index': False,
758
'unique': False,
759
'primary_key': False,
760
'sql': u'INTEGER'},
761
u'min_degree': {'index': True,
762
'unique': False,
763
'primary_key': False,
764
'sql': u'INTEGER'}},
765
u'spectrum': {u'max_eigenvalue': {'index': True,
766
'unique': False,
767
'primary_key': False,
768
'sql': u'REAL'},
769
u'energy': {'index': True,
770
'unique': False,
771
'primary_key': False,
772
'sql': u'REAL'},
773
u'spectrum': {'index': False,
774
'unique': False,
775
'primary_key': False,
776
'sql': u'TEXT'},
777
u'eigenvalues_sd': {'index': True,
778
'unique': False,
779
'primary_key': False,
780
'sql': u'REAL'},
781
u'graph_id': {'index': False,
782
'unique': False,
783
'primary_key': False,
784
'sql': u'INTEGER'},
785
u'min_eigenvalue': {'index': True,
786
'unique': False,
787
'primary_key': False,
788
'sql': u'REAL'}},
789
u'misc': {u'diameter': {'index': True,
790
'unique': False,
791
'primary_key': False,
792
'sql': u'INTEGER'},
793
u'vertex_connectivity': {'index': True,
794
'unique': False,
795
'primary_key': False,
796
'sql': u'BOOLEAN'},
797
u'graph_id': {'index': False,
798
'unique': False,
799
'primary_key': False,
800
'sql': u'INTEGER'},
801
u'num_components': {'index': True,
802
'unique': False,
803
'primary_key': False,
804
'sql': u'INTEGER'},
805
u'min_vertex_cover_size': {'index': True,
806
'unique': False,
807
'primary_key': False,
808
'sql': u'INTEGER'},
809
u'edge_connectivity': {'index': True,
810
'unique': False,
811
'primary_key': False,
812
'sql': u'BOOLEAN'},
813
u'num_spanning_trees': {'index': True,
814
'unique': False,
815
'primary_key': False,
816
'sql': u'INTEGER'},
817
u'induced_subgraphs': {'index': True,
818
'unique': False,
819
'primary_key': False,
820
'sql': u'TEXT'},
821
u'radius': {'index': True,
822
'unique': False,
823
'primary_key': False,
824
'sql': u'INTEGER'},
825
u'num_cut_vertices': {'index': True,
826
'unique': False,
827
'primary_key': False,
828
'sql': u'INTEGER'},
829
u'clique_number': {'index': True,
830
'unique': False,
831
'primary_key': False,
832
'sql': u'INTEGER'},
833
u'independence_number': {'index': True,
834
'unique': False,
835
'primary_key': False,
836
'sql': u'INTEGER'},
837
u'girth': {'index': True,
838
'unique': False,
839
'primary_key': False,
840
'sql': u'INTEGER'}},
841
u'graph_data': {u'perfect': {'index': True,
842
'unique': False,
843
'primary_key': False,
844
'sql': u'BOOLEAN'},
845
u'planar': {'index': True,
846
'unique': False,
847
'primary_key': False,
848
'sql': u'BOOLEAN'},
849
u'graph_id': {'index': True,
850
'unique': True,
851
'primary_key': False,
852
'sql': u'INTEGER'},
853
u'complement_graph6': {'index': True,
854
'unique': False,
855
'primary_key': False,
856
'sql': u'TEXT'},
857
u'num_edges': {'index': True,
858
'unique': False,
859
'primary_key': False,
860
'sql': u'INTEGER'},
861
u'num_cycles': {'index': True,
862
'unique': False,
863
'primary_key': False,
864
'sql': u'INTEGER'},
865
u'graph6': {'index': True,
866
'unique': False,
867
'primary_key': False,
868
'sql': u'TEXT'},
869
u'num_hamiltonian_cycles': {'index': True,
870
'unique': False,
871
'primary_key': False,
872
'sql': u'INTEGER'},
873
u'lovasz_number': {'index': True,
874
'unique': False,
875
'primary_key': False,
876
'sql': u'REAL'},
877
u'eulerian': {'index': True,
878
'unique': False,
879
'primary_key': False,
880
'sql': u'BOOLEAN'},
881
u'num_vertices': {'index': True,
882
'unique': False,
883
'primary_key': False,
884
'sql': u'INTEGER'}}}
885
"""
886
SQLDatabase.__init__(self,dblocation)
887
888
def _gen_interact_func(self, display, **kwds):
889
"""
890
Generates and returns a function to interact with GraphQuery
891
parameters and results. This is a helper method for the
892
interactive_query method and should not be called directly.
893
894
EXAMPLE::
895
896
sage: D = GraphDatabase()
897
sage: D.interactive_query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],max_degree=3)
898
<html>...</html>
899
900
::
901
902
sage: G = GraphDatabase()
903
sage: f = G._gen_interact_func(display=['graph6'], num_vertices=3)
904
sage: type(f)
905
<type 'function'>
906
sage: interact(f)
907
<html>...
908
"""
909
from sage.server.notebook.interact import input_grid
910
arg=['%s=%s'%(word,kwds[word]) for word in kwds]
911
boxes=["%s=input_grid(1,2,['=',%s])"%(word,kwds[word]) for word in kwds]
912
params = ['%s=%s[0]'%tuple(2*[arg[i].split('=')[0]]) for i in range(len(arg))]
913
914
s = 'def _(%s):'%','.join(boxes)
915
t = """
916
print '<html><h2>Query Results:</h2></html>'
917
GraphQuery(display_cols=%s,%s).show(with_picture=True)
918
"""%tuple([display,','.join(params)])
919
s += '\t'+'\n\t'.join(t.split('\n'))+'\n'
920
exec(s)
921
return _
922
923
def query(self, query_dict=None, display_cols=None, **kwds):
924
"""
925
Creates a GraphQuery on this database. For full class details, type
926
GraphQuery? and press shift+enter.
927
928
EXAMPLE::
929
930
sage: D = GraphDatabase()
931
sage: q = D.query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5])
932
sage: q.show()
933
Graph6 Num Vertices Degree Sequence
934
------------------------------------------------------------
935
@ 1 [0]
936
A? 2 [0, 0]
937
A_ 2 [1, 1]
938
B? 3 [0, 0, 0]
939
BG 3 [0, 1, 1]
940
BW 3 [1, 1, 2]
941
Bw 3 [2, 2, 2]
942
C? 4 [0, 0, 0, 0]
943
C@ 4 [0, 0, 1, 1]
944
CB 4 [0, 1, 1, 2]
945
CK 4 [1, 1, 1, 1]
946
CF 4 [1, 1, 1, 3]
947
CJ 4 [0, 2, 2, 2]
948
CL 4 [1, 1, 2, 2]
949
CN 4 [1, 2, 2, 3]
950
C] 4 [2, 2, 2, 2]
951
C^ 4 [2, 2, 3, 3]
952
D?? 5 [0, 0, 0, 0, 0]
953
D?C 5 [0, 0, 0, 1, 1]
954
D?K 5 [0, 0, 1, 1, 2]
955
D@O 5 [0, 1, 1, 1, 1]
956
D?[ 5 [0, 1, 1, 1, 3]
957
D@K 5 [0, 0, 2, 2, 2]
958
D_K 5 [1, 1, 1, 1, 2]
959
D@S 5 [0, 1, 1, 2, 2]
960
D?{ 5 [1, 1, 1, 1, 4]
961
D@[ 5 [0, 1, 2, 2, 3]
962
D@s 5 [1, 1, 1, 2, 3]
963
DBg 5 [1, 1, 2, 2, 2]
964
DBW 5 [0, 2, 2, 2, 2]
965
D`K 5 [1, 1, 2, 2, 2]
966
D@{ 5 [1, 1, 2, 2, 4]
967
DB[ 5 [0, 2, 2, 3, 3]
968
DIk 5 [1, 2, 2, 2, 3]
969
DBk 5 [1, 1, 2, 3, 3]
970
DK[ 5 [1, 2, 2, 2, 3]
971
DLo 5 [2, 2, 2, 2, 2]
972
E??? 6 [0, 0, 0, 0, 0, 0]
973
E??G 6 [0, 0, 0, 0, 1, 1]
974
E??W 6 [0, 0, 0, 1, 1, 2]
975
E?C_ 6 [0, 0, 1, 1, 1, 1]
976
E??w 6 [0, 0, 1, 1, 1, 3]
977
E?CW 6 [0, 0, 0, 2, 2, 2]
978
EG?W 6 [0, 1, 1, 1, 1, 2]
979
E?Cg 6 [0, 0, 1, 1, 2, 2]
980
E@Q? 6 [1, 1, 1, 1, 1, 1]
981
E?@w 6 [0, 1, 1, 1, 1, 4]
982
E?Cw 6 [0, 0, 1, 2, 2, 3]
983
E?Dg 6 [0, 1, 1, 1, 2, 3]
984
E_?w 6 [1, 1, 1, 1, 1, 3]
985
E?LO 6 [0, 1, 1, 2, 2, 2]
986
E?N? 6 [1, 1, 1, 1, 2, 2]
987
E?Ko 6 [0, 0, 2, 2, 2, 2]
988
EGCW 6 [0, 1, 1, 2, 2, 2]
989
E_Cg 6 [1, 1, 1, 1, 2, 2]
990
E?Bw 6 [1, 1, 1, 1, 1, 5]
991
E?Dw 6 [0, 1, 1, 2, 2, 4]
992
E?Fg 6 [1, 1, 1, 1, 2, 4]
993
E?Kw 6 [0, 0, 2, 2, 3, 3]
994
E@HW 6 [0, 1, 2, 2, 2, 3]
995
E@FG 6 [1, 1, 1, 2, 2, 3]
996
E?LW 6 [0, 1, 1, 2, 3, 3]
997
E?NG 6 [1, 1, 1, 1, 3, 3]
998
E@N? 6 [1, 1, 2, 2, 2, 2]
999
E@YO 6 [1, 1, 2, 2, 2, 2]
1000
E@QW 6 [1, 1, 1, 2, 2, 3]
1001
E@Ow 6 [0, 1, 2, 2, 2, 3]
1002
E_Cw 6 [1, 1, 1, 2, 2, 3]
1003
E@T_ 6 [0, 2, 2, 2, 2, 2]
1004
E_Ko 6 [1, 1, 2, 2, 2, 2]
1005
F???? 7 [0, 0, 0, 0, 0, 0, 0]
1006
F???G 7 [0, 0, 0, 0, 0, 1, 1]
1007
F???W 7 [0, 0, 0, 0, 1, 1, 2]
1008
F??G_ 7 [0, 0, 0, 1, 1, 1, 1]
1009
F???w 7 [0, 0, 0, 1, 1, 1, 3]
1010
F??GW 7 [0, 0, 0, 0, 2, 2, 2]
1011
F@??W 7 [0, 0, 1, 1, 1, 1, 2]
1012
F??Gg 7 [0, 0, 0, 1, 1, 2, 2]
1013
F?Ca? 7 [0, 1, 1, 1, 1, 1, 1]
1014
F??@w 7 [0, 0, 1, 1, 1, 1, 4]
1015
F??Gw 7 [0, 0, 0, 1, 2, 2, 3]
1016
F??Hg 7 [0, 0, 1, 1, 1, 2, 3]
1017
FG??w 7 [0, 1, 1, 1, 1, 1, 3]
1018
F??XO 7 [0, 0, 1, 1, 2, 2, 2]
1019
F??Z? 7 [0, 1, 1, 1, 1, 2, 2]
1020
F??Wo 7 [0, 0, 0, 2, 2, 2, 2]
1021
F@?GW 7 [0, 0, 1, 1, 2, 2, 2]
1022
FK??W 7 [1, 1, 1, 1, 1, 1, 2]
1023
FG?Gg 7 [0, 1, 1, 1, 1, 2, 2]
1024
F??Bw 7 [0, 1, 1, 1, 1, 1, 5]
1025
F??Hw 7 [0, 0, 1, 1, 2, 2, 4]
1026
F??Jg 7 [0, 1, 1, 1, 1, 2, 4]
1027
F_?@w 7 [1, 1, 1, 1, 1, 1, 4]
1028
F??Ww 7 [0, 0, 0, 2, 2, 3, 3]
1029
F?CPW 7 [0, 0, 1, 2, 2, 2, 3]
1030
F?CJG 7 [0, 1, 1, 1, 2, 2, 3]
1031
F??^? 7 [1, 1, 1, 1, 1, 2, 3]
1032
F??XW 7 [0, 0, 1, 1, 2, 3, 3]
1033
F??ZG 7 [0, 1, 1, 1, 1, 3, 3]
1034
F?CZ? 7 [0, 1, 1, 2, 2, 2, 2]
1035
F_?Hg 7 [1, 1, 1, 1, 1, 2, 3]
1036
F?CqO 7 [0, 1, 1, 2, 2, 2, 2]
1037
F?CaW 7 [0, 1, 1, 1, 2, 2, 3]
1038
F?LCG 7 [1, 1, 1, 1, 2, 2, 2]
1039
F?C_w 7 [0, 0, 1, 2, 2, 2, 3]
1040
FG?Gw 7 [0, 1, 1, 1, 2, 2, 3]
1041
F?Ch_ 7 [0, 0, 2, 2, 2, 2, 2]
1042
FG?Wo 7 [0, 1, 1, 2, 2, 2, 2]
1043
F_?XO 7 [1, 1, 1, 1, 2, 2, 2]
1044
FK?GW 7 [1, 1, 1, 1, 2, 2, 2]
1045
"""
1046
return GraphQuery(self, query_dict, display_cols, **kwds)
1047
1048
def interactive_query(self, display_cols, **kwds):
1049
"""
1050
TODO: This function could use improvement. Add full options of
1051
typical GraphQuery (i.e.: have it accept list input); and update
1052
options in interact to make it less annoying to put in operators.
1053
1054
Generates an interact shell (in the notebook only) that allows the
1055
user to manipulate query parameters and see the updated results.
1056
1057
EXAMPLE::
1058
1059
sage: D = GraphDatabase()
1060
sage: D.interactive_query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=5,max_degree=3)
1061
<html>...</html>
1062
"""
1063
from sage.server.notebook.interact import interact
1064
print '<html><h1>Interactive Graph Query</h1></html>'
1065
f = self._gen_interact_func(display=display_cols,**kwds)
1066
interact(f)
1067
1068
1069