Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/databases/sloane.py
6915 views
1
"""
2
Interface to Sloane On-Line Encyclopedia of Integer Sequences
3
4
To look up sequence A060843, type one of the following::
5
6
sage: sloane_sequence(60843) # optional - internet
7
Searching Sloane's online database...
8
[60843, 'Busy Beaver problem: maximal number of steps that an n-state Turing machine can make on an initially blank tape before eventually halting.', [1, 6, 21, 107]]
9
10
::
11
12
sage: sloane_sequence("60843") # optional - internet
13
Searching Sloane's online database...
14
[60843, 'Busy Beaver problem: maximal number of steps that an n-state Turing machine can make on an initially blank tape before eventually halting.', [1, 6, 21, 107]]
15
16
::
17
18
sage: sloane_sequence("060843") # optional - internet
19
Searching Sloane's online database...
20
[60843, 'Busy Beaver problem: maximal number of steps that an n-state Turing machine can make on an initially blank tape before eventually halting.', [1, 6, 21, 107]]
21
22
Do not prefix an integer with a 0 or it will be interpreted in
23
octal. Results are of the form [number, description, list], and
24
invalid numbers will cause ``sloane_sequence`` to
25
raise an ValueError exception::
26
27
sage: sloane_sequence('sage') # optional - internet
28
Traceback (most recent call last):
29
...
30
ValueError: sequence 'sage' not found
31
32
To look up the sequence "2, 3, 5, 7", simply put the numbers in a list.
33
The second argument specifies that at most 2 results will be returned.
34
35
::
36
37
sage: sloane_find([2,3,5,7], 2) # optional - internet
38
Searching Sloane's online database...
39
[[40, 'The prime numbers.', [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271]], [41, 'a(n) = number of partitions of n (the partition numbers).', [1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604, 6842, 8349, 10143, 12310, 14883, 17977, 21637, 26015, 31185, 37338, 44583, 53174, 63261, 75175, 89134]]]
40
41
To return no more than 3 results (default is 30), type
42
43
::
44
45
sage: len(sloane_find([1,2,3,4,5], 3)) # optional - internet
46
Searching Sloane's online database...
47
3
48
49
Sequence A137443 includes Sage code, and a "b file" that was computed
50
with Sage::
51
52
sage: sloane_find([7, 71, 281, 4523, 74713]) # optional - internet
53
Searching Sloane's online database...
54
[[137443, 'First n-digit prime in consecutive digits of e.', [7, 71, 281, 4523, 74713, 904523, 6028747, 72407663, 360287471, 7427466391, 75724709369, 749669676277, 8284590452353, 99959574966967, 724709369995957, 2470936999595749, 28459045235360287, 571382178525166427]]]
55
56
Note that the OEIS (http://oeis.org/classic/) claims to limit the number of
57
results to 100. Results are lists of the form [[number, description, list]],
58
and invalid input will cause sloane_find to return [].
59
60
In some cases, these functions may return [] even though the inputs
61
are legal. These cases correspond to errors from the OEIS server,
62
and calling the functions again may fix the problem.
63
64
Alternatively, the SloaneEncyclopedia object provides access to a
65
local copy of the database containing only the sequences and their
66
names. To use this you must download and install the database using
67
``SloaneEncyclopedia.install()``, or
68
``SloaneEncyclopedia.install_from_gz()`` if you have already
69
downloaded the database manually.
70
71
To look up a sequence, type
72
73
::
74
75
sage: SloaneEncyclopedia[60843] # optional - sloane_database
76
[1, 6, 21, 107]
77
78
To get the name of a sequence, type
79
80
::
81
82
sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
83
'Number of groups of order n.'
84
85
To search locally for a particular subsequence, type
86
87
::
88
89
sage: SloaneEncyclopedia.find([1,2,3,4,5], 1) # optional - sloane_database
90
[(15, [1, 2, 3, 4, 5, 7, 7, 8, 9, 11, 11, 13, 13, 16, 16, 16, 17, 19, 19, 23, 23, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31, 32, 37, 37, 37, 37, 37, 41, 41, 41, 41, 43, 43, 47, 47, 47, 47, 49, 49, 53, 53, 53, 53, 59, 59, 59, 59, 59, 59, 61, 61, 64, 64, 64, 67, 67, 67, 71, 71, 71, 71, 73])]
91
92
The default maximum number of results is 30, but to return up to
93
100, type
94
95
::
96
97
sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database
98
[(15, [1, 2, 3, 4, 5, 7, 7, 8, 9, 11, 11, ...
99
100
Results in either case are of the form [ (number, list) ].
101
102
TODO:
103
104
- When this program gets a sloane sequence from the database it
105
actually downloads a huge amount of information about it, then
106
throws most of it away. Also, it returns the data to the user as a
107
very simple tuple. It would be much better to return an instance of
108
a class::
109
110
class SloaneSequence: ...
111
112
and the class should have methods for each of the things that
113
Sloane records about a sequence. Also, when possible, it should be
114
able to compute more terms.
115
116
AUTHORS:
117
118
- Steven Sivek (2005-12-22): first version
119
120
- Steven Sivek (2006-02-07): updated to correctly handle the new
121
search form on the Sloane website, and it's now also smarter about
122
loading the local database in that it doesn't convert a sequence
123
from string form to a list of integers until absolutely necessary.
124
This seems to cut the loading time roughly in half.
125
126
- Steven Sivek (2009-12-22): added the SloaneEncyclopedia functions
127
install() and install_from_gz() so users can get the latest versions
128
of the OEIS without having to get an updated spkg; added
129
sequence_name() to return the description of a sequence; and changed
130
the data type for elements of each sequence from int to Integer.
131
132
"""
133
134
#*****************************************************************************
135
#
136
# Sage: Copyright (C) 2005-2006 William Stein <[email protected]>
137
# and Steven Sivek <[email protected]>
138
#
139
# Distributed under the terms of the GNU General Public License (GPL)
140
#
141
# http://www.gnu.org/licenses/
142
#*****************************************************************************
143
144
import bz2, os, re, urllib
145
146
from sage.misc.all import verbose
147
import sage.rings.integer_ring
148
ZZ = sage.rings.integer_ring.IntegerRing()
149
150
class SloaneEncyclopediaClass:
151
"""
152
A local copy of the Sloane Online Encyclopedia of Integer Sequences
153
that contains only the sequence numbers and the sequences
154
themselves.
155
"""
156
def __init__(self):
157
"""
158
Initialize the database but do not load any of the data.
159
"""
160
self.__path__ = "%s/data/sloane/"%os.environ["SAGE_ROOT"]
161
self.__file__ = "%ssloane-oeis.bz2"%self.__path__
162
self.__file_names__ = "%ssloane-names.bz2"%self.__path__
163
self.__loaded__ = False
164
self.__loaded_names__ = False
165
166
def __repr__(self):
167
"""
168
String representation of this database. OUTPUT: str
169
"""
170
return "Sloane Online Encyclopedia of Integer Sequences"
171
172
def __iter__(self):
173
"""
174
Returns an iterator through the encyclopedia. Elements are of the
175
form [number, sequence].
176
"""
177
for i in self.__data__:
178
yield [i, self[i]]
179
180
def __getitem__(self, N):
181
"""
182
Return sequence N in the encyclopedia. If sequence N does not
183
exist, return [].
184
185
INPUT:
186
187
188
- ``N`` - int
189
190
191
OUTPUT: list
192
"""
193
self.load()
194
if not N in self.__data__: # sequence N does not exist
195
return []
196
if self.__data__[N][1] is None: # list N has not been created yet
197
list = self.__data__[N][2].strip(',').split(',')
198
self.__data__[N][1] = [ZZ(n) for n in list]
199
return self.__data__[N][1]
200
201
def __len__(self):
202
"""
203
Return the number of sequences in the encyclopedia.
204
"""
205
self.load()
206
return len(self.__data__)
207
208
209
def find(self, seq, maxresults=30):
210
"""
211
Return a list of all sequences which have seq as a subsequence, up
212
to maxresults results. Sequences are returned in the form (number,
213
list).
214
215
INPUT:
216
217
218
- ``seq`` - list
219
220
- ``maxresults`` - int
221
222
223
OUTPUT: list of 2-tuples (i, v), where v is a sequence with seq as
224
a subsequence.
225
"""
226
self.load()
227
228
answer, nanswer = [], 0
229
pattern = re.sub(r'[\[\]]', ',', str(seq).replace(' ',''))
230
for i in self.__data__:
231
if self.__data__[i][2].find(pattern) != -1:
232
answer.append((i, self[i]))
233
nanswer = nanswer + 1
234
if nanswer == maxresults:
235
return answer
236
237
return answer
238
239
def install(self, oeis_url="http://oeis.org/classic/stripped.gz", names_url="http://oeis.org/classic/names.gz", overwrite=False):
240
"""
241
Download and install the online encyclopedia, raising an IOError if
242
either step fails.
243
244
INPUT:
245
246
- ``oeis_url`` - string (default: "http://www.research.att.com...")
247
The URL of the stripped.gz encyclopedia file.
248
249
- ``names_url`` - string (default: "http://www.research.att.com...")
250
The URL of the names.gz encyclopedia file. If you do not want to
251
download this file, set names_url=None.
252
253
- ``overwrite`` - boolean (default: False) If the encyclopedia is
254
already installed and overwrite=True, download and install the latest
255
version over the installed one.
256
"""
257
### See if the encyclopedia already exists
258
if not overwrite and os.path.exists(self.__file__):
259
raise IOError, "Sloane encyclopedia is already installed"
260
261
tm = verbose("Downloading stripped version of Sloane encyclopedia")
262
try:
263
fname, _ = urllib.urlretrieve(oeis_url);
264
except IOError, msg:
265
raise IOError, "%s\nError fetching the following website:\n %s\nTry checking your internet connection."%(msg, oeis_url)
266
267
if not names_url is None:
268
try:
269
nname, _ = urllib.urlretrieve(names_url);
270
except IOError, msg:
271
raise IOError, "%s\nError fetching the following website:\n %s\nTry checking your internet connection."%(msg, names_url)
272
else:
273
nname = None
274
verbose("Finished downloading", tm)
275
276
self.install_from_gz(fname, nname, overwrite)
277
# Delete the temporary downloaded files
278
os.remove(fname)
279
if not nname is None:
280
os.remove(nname)
281
282
def install_from_gz(self, stripped_file, names_file, overwrite=False):
283
"""
284
Install the online encyclopedia from a local stripped.gz file.
285
286
INPUT:
287
288
- ``stripped_file`` - string. The name of the stripped.gz OEIS file.
289
290
- ``names_file`` - string. The name of the names.gz OEIS file, or
291
None if the user does not want it installed.
292
293
- ``overwrite`` - boolean (default: False) If the encyclopedia is
294
already installed and overwrite=True, install 'filename' over the
295
old encyclopedia.
296
"""
297
if not overwrite and os.path.exists(self.__file__):
298
raise IOError, "Sloane encyclopedia is already installed"
299
300
copy_gz_file(stripped_file, self.__file__)
301
302
if not names_file is None:
303
copy_gz_file(names_file, self.__file_names__)
304
else:
305
# Delete old copies of names.gz since their sequence numbers
306
# probably won't match the newly installed stripped.gz
307
if os.path.exists(self.__file_names__):
308
os.remove(self.__file_names__)
309
310
# Remove the old database from memory so the new one will be
311
# automatically loaded next time the user tries to access it
312
self.unload()
313
314
315
def load(self):
316
"""
317
Load the entire encyclopedia into memory from a file. This is done
318
automatically if the user tries to perform a lookup or a search.
319
"""
320
if self.__loaded__ == True:
321
return
322
try:
323
file_seq = bz2.BZ2File(self.__file__, 'r')
324
except IOError:
325
raise IOError, "The Sloane Encyclopedia database must be installed. Use e.g. 'SloaneEncyclopedia.install()' to download and install it."
326
327
self.__data__ = {}
328
329
tm = verbose("Loading Sloane encyclopedia from disk")
330
entry = re.compile(r'A(?P<num>\d{6}) ,(?P<body>.*),$');
331
for L in file_seq:
332
if len(L) == 0:
333
continue
334
m = entry.search(L)
335
if m:
336
seqnum = int(m.group('num'))
337
msg = m.group('body').strip()
338
self.__data__[seqnum] = [seqnum, None, ','+msg+',', None]
339
file_seq.close()
340
341
try:
342
file_names = bz2.BZ2File(self.__file_names__, 'r')
343
entry = re.compile(r'A(?P<num>\d{6}) (?P<body>.*)$');
344
for L in file_names:
345
if len(L) == 0: continue
346
m = entry.search(L)
347
if m:
348
seqnum = int(m.group('num'))
349
self.__data__[seqnum][3] = m.group('body').strip()
350
file_names.close()
351
self.__loaded_names__ = True
352
except KeyError:
353
### Some sequence in the names file isn't in the database
354
raise KeyError, "Sloane OEIS sequence and name files do not match. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True)."
355
except IOError, msg:
356
### The names database is not installed
357
self.__loaded_names__ = False
358
359
verbose("Finished loading", tm)
360
self.__loaded__ = True
361
362
def sequence_name(self, N):
363
"""
364
Return the name of sequence N in the encyclopedia. If sequence N
365
does not exist, return ''. If the names database is not installed,
366
raise an IOError.
367
368
INPUT:
369
370
- ``N`` - int
371
372
OUTPUT: string
373
374
EXAMPLES:
375
376
sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
377
'Number of groups of order n.'
378
"""
379
self.load()
380
if not self.__loaded_names__:
381
raise IOError, "The Sloane OEIS names file is not installed. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True)."
382
383
if not N in self.__data__: # sequence N does not exist
384
return ''
385
return self.__data__[N][3]
386
387
def unload(self):
388
"""
389
Remove the database from memory.
390
"""
391
if self.__loaded__ == False:
392
return
393
del self.__data__
394
self.__loaded__ = False
395
self.__loaded_names__ = False
396
397
SloaneEncyclopedia = SloaneEncyclopediaClass()
398
399
def copy_gz_file(gz_source, bz_destination):
400
"""
401
Decompress a gzipped file and install the bzipped verson. This is
402
used by SloaneEncyclopedia.install_from_gz to install several
403
gzipped OEIS database files.
404
405
INPUT:
406
407
- ``gz_source`` - string. The name of the gzipped file.
408
409
- ``bz_destination`` - string. The name of the newly compressed file.
410
"""
411
import gzip
412
from sage.misc.misc import sage_makedirs
413
414
# Read the gzipped input
415
try:
416
gz_input = gzip.open(gz_source, 'r')
417
db_text = gz_input.read()
418
gz_input.close()
419
except IOError, msg:
420
raise IOError, "Error reading gzipped input file:\n%s"%msg
421
422
# Write the bzipped output
423
try:
424
sage_makedirs(os.path.dirname(bz_destination))
425
bz2_output = bz2.BZ2File(bz_destination, 'w')
426
bz2_output.write(db_text)
427
bz2_output.close()
428
except IOError, msg:
429
raise IOError, "Error writing bzipped output file:\n%s"%msg
430
431
432
def parse_sequence(text):
433
entry = re.compile(r'%(?P<letter>[A-Za-z]) A(?P<num>\d{6}) (?P<body>.*)$')
434
unsigned, signed, list = [], [], []
435
description = ''
436
seqnum = -1
437
438
# Fix broken lines: the next line is indented.
439
text = text.replace('\n ', '');
440
441
for line in re.split(r'[\s,]*\n', text):
442
m = entry.search(line)
443
if m:
444
seqnum = ZZ(m.group('num').lstrip('0'))
445
type = m.group('letter')
446
msg = m.group('body').lstrip().rstrip()
447
if type == 'S' or type == 'T' or type == 'U':
448
unsigned += msg.split(',')
449
elif type == 'V' or type == 'W' or type == 'X':
450
signed += msg.split(',')
451
elif type == 'N':
452
description = msg
453
454
if signed == []:
455
list = unsigned
456
else:
457
list = signed
458
return [seqnum, description, [ZZ(n) for n in list]]
459
460
def sloane_sequence(number, verbose = True):
461
"""
462
Returns a list with the number, name, and values for the sequence
463
``number`` in Sloane's online database of integer
464
sequences.
465
466
INPUT:
467
468
- ``number`` (integer) corresponding to the sequence.
469
470
- ``verbose`` - (boolean) print a string to let the
471
user know that it is working and not hanging.
472
473
Set to ``True`` by default.
474
475
476
EXAMPLES::
477
478
sage: sloane_sequence(22) # optional - internet
479
Searching Sloane's online database...
480
[22,
481
'Number of centered hydrocarbons with n atoms.',
482
[0,
483
1,
484
0,
485
1,
486
...
487
36201693122]]
488
489
The input must not be a sequence itself::
490
491
sage: sloane_sequence(prime_range(100))
492
Traceback (most recent call last):
493
...
494
TypeError: input must be an integer or string that specifies the id of the Sloane sequence to download
495
"""
496
if not isinstance(number, str):
497
try:
498
number = str(ZZ(number))
499
except TypeError:
500
raise TypeError, "input must be an integer or string that specifies the id of the Sloane sequence to download"
501
results = sloane_find('id:A%s'%number, verbose = verbose)
502
if len(results) == 0:
503
raise ValueError, "sequence '%s' not found"%number
504
return results[0]
505
506
def sloane_find(list, nresults = 30, verbose=True):
507
"""
508
Searches Sloane's Online Encyclopedia of Integer Sequences for a
509
sequence containing the number provided in ``list``.
510
511
INPUT:
512
513
514
- ``list`` - (list) a list of integers to search
515
Sloane's for
516
517
- ``nresults`` - (integer) the maximum number of
518
results to return default: 30
519
520
- ``verbose`` - (boolean) print a string to let the
521
user know that it is working and not hanging. default: True
522
523
524
OUTPUT: A list of matches in Sloane's database. Each match consists
525
of a list of the sequence number, the name of the sequence, and
526
some initial terms of the sequence.
527
528
EXAMPLES::
529
530
sage: sloane_find([1,1,2,3,5,8,13,21], nresults=1) #optional - internet
531
Searching Sloane's online database...
532
[[45,
533
'Fibonacci numbers: F(n) = F(n-1) + F(n-2), F(0) = 0, F(1) = 1, F(2) = 1, ...',
534
[0,
535
1,
536
1,
537
2,
538
3,
539
5,
540
8,
541
13,
542
21,
543
...
544
39088169]]]
545
"""
546
liststr = re.sub(r'[\[\] ]', '', str(list))
547
urlparams = urllib.urlencode({'q': liststr,
548
'p': 1,
549
'n': nresults,
550
'fmt': 2,
551
'sort': 0});
552
553
554
try:
555
if verbose:
556
print "Searching Sloane's online database..."
557
url = "http://oeis.org/classic/"
558
f = urllib.urlopen(url+'?'+urlparams);
559
s = f.read()
560
f.close()
561
except IOError, msg:
562
raise IOError, "%s\nError fetching the following website:\n %s\nTry checking your internet connection."%(msg, url)
563
564
t = s.lower()
565
i = t.find("<pre>")
566
j = t.find("</pre>")
567
if i == -1 or j == -1:
568
#raise IOError, "Error parsing data (missing pre tags)."
569
return []
570
text = s[i+5:j].strip()
571
572
results = []
573
for line in text.split('\n\n'):
574
if line.find('%') != -1: # ignore text at top of results
575
results.append(parse_sequence(line))
576
577
return results
578
579