Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/databases/sloane.py
8814 views
1
"""
2
Local copy of Sloane On-Line Encyclopedia of Integer Sequences
3
4
The SloaneEncyclopedia object provides access to a local copy of the database
5
containing only the sequences and their names. To use this you must download
6
and install the database using ``SloaneEncyclopedia.install()``, or
7
``SloaneEncyclopedia.install_from_gz()`` if you have already downloaded the
8
database manually.
9
10
To look up a sequence, type
11
12
::
13
14
sage: SloaneEncyclopedia[60843] # optional - sloane_database
15
[1, 6, 21, 107]
16
17
To get the name of a sequence, type
18
19
::
20
21
sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
22
'Number of groups of order n.'
23
24
To search locally for a particular subsequence, type
25
26
::
27
28
sage: SloaneEncyclopedia.find([1,2,3,4,5], 1) # optional - sloane_database
29
[(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])]
30
31
The default maximum number of results is 30, but to return up to
32
100, type
33
34
::
35
36
sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database
37
[(15, [1, 2, 3, 4, 5, 7, 7, 8, 9, 11, 11, ...
38
39
Results in either case are of the form [ (number, list) ].
40
41
42
.. SEEALSO::
43
44
- If you want to get more informations relative to a sequence (references,
45
links, examples, programs, ...), you can use the On-Line Encyclopedia of
46
Integer Sequences provided by the :mod:`OEIS <sage.databases.oeis>`
47
module.
48
- Some infinite OEIS sequences are implemented in Sage, via the
49
:mod:`sloane_functions <sage.combinat.sloane_functions>` module.
50
51
52
AUTHORS:
53
54
- Steven Sivek (2005-12-22): first version
55
56
- Steven Sivek (2006-02-07): updated to correctly handle the new
57
search form on the Sloane website, and it's now also smarter about
58
loading the local database in that it doesn't convert a sequence
59
from string form to a list of integers until absolutely necessary.
60
This seems to cut the loading time roughly in half.
61
62
- Steven Sivek (2009-12-22): added the SloaneEncyclopedia functions
63
install() and install_from_gz() so users can get the latest versions
64
of the OEIS without having to get an updated spkg; added
65
sequence_name() to return the description of a sequence; and changed
66
the data type for elements of each sequence from int to Integer.
67
68
- Thierry Monteil (2012-02-10): deprecate dead code and update related doc and
69
tests.
70
71
Classes and methods
72
-------------------
73
"""
74
75
#*****************************************************************************
76
#
77
# Sage: Copyright (C) 2005-2006 William Stein <[email protected]>
78
# and Steven Sivek <[email protected]>
79
#
80
# Distributed under the terms of the GNU General Public License (GPL)
81
#
82
# http://www.gnu.org/licenses/
83
#*****************************************************************************
84
85
import bz2, os, re, urllib
86
87
from sage.misc.all import verbose
88
from sage.misc.misc import SAGE_SHARE
89
import sage.rings.integer_ring
90
ZZ = sage.rings.integer_ring.IntegerRing()
91
from sage.misc.superseded import deprecation
92
93
class SloaneEncyclopediaClass:
94
"""
95
A local copy of the Sloane Online Encyclopedia of Integer Sequences
96
that contains only the sequence numbers and the sequences
97
themselves.
98
"""
99
def __init__(self):
100
"""
101
Initialize the database but do not load any of the data.
102
"""
103
self.__path__ = os.path.join(SAGE_SHARE, 'sloane')
104
self.__file__ = os.path.join(self.__path__, 'sloane-oeis.bz2')
105
self.__file_names__ = os.path.join(self.__path__, 'sloane-names.bz2')
106
self.__loaded__ = False
107
self.__loaded_names__ = False
108
109
def __repr__(self):
110
"""
111
String representation of this database. OUTPUT: str
112
"""
113
return "Local copy of Sloane Online Encyclopedia of Integer Sequences"
114
115
def __iter__(self):
116
"""
117
Returns an iterator through the encyclopedia. Elements are of the
118
form [number, sequence].
119
"""
120
for i in self.__data__:
121
yield [i, self[i]]
122
123
def __getitem__(self, N):
124
"""
125
Return sequence N in the encyclopedia. If sequence N does not
126
exist, return [].
127
128
INPUT:
129
130
131
- ``N`` - int
132
133
134
OUTPUT: list
135
"""
136
self.load()
137
if not N in self.__data__: # sequence N does not exist
138
return []
139
if self.__data__[N][1] is None: # list N has not been created yet
140
list = self.__data__[N][2].strip(',').split(',')
141
self.__data__[N][1] = [ZZ(n) for n in list]
142
return self.__data__[N][1]
143
144
def __len__(self):
145
"""
146
Return the number of sequences in the encyclopedia.
147
"""
148
self.load()
149
return len(self.__data__)
150
151
152
def find(self, seq, maxresults=30):
153
"""
154
Return a list of all sequences which have seq as a subsequence, up
155
to maxresults results. Sequences are returned in the form (number,
156
list).
157
158
INPUT:
159
160
161
- ``seq`` - list
162
163
- ``maxresults`` - int
164
165
166
OUTPUT: list of 2-tuples (i, v), where v is a sequence with seq as
167
a subsequence.
168
"""
169
self.load()
170
171
answer, nanswer = [], 0
172
pattern = re.sub(r'[\[\]]', ',', str(seq).replace(' ',''))
173
for i in self.__data__:
174
if self.__data__[i][2].find(pattern) != -1:
175
answer.append((i, self[i]))
176
nanswer = nanswer + 1
177
if nanswer == maxresults:
178
return answer
179
180
return answer
181
182
def install(self, oeis_url="http://oeis.org/stripped.gz", names_url="http://oeis.org/names.gz", overwrite=False):
183
"""
184
Download and install the online encyclopedia, raising an IOError if
185
either step fails.
186
187
INPUT:
188
189
- ``oeis_url`` - string (default: "http://www.research.att.com...")
190
The URL of the stripped.gz encyclopedia file.
191
192
- ``names_url`` - string (default: "http://www.research.att.com...")
193
The URL of the names.gz encyclopedia file. If you do not want to
194
download this file, set names_url=None.
195
196
- ``overwrite`` - boolean (default: False) If the encyclopedia is
197
already installed and overwrite=True, download and install the latest
198
version over the installed one.
199
"""
200
### See if the encyclopedia already exists
201
if not overwrite and os.path.exists(self.__file__):
202
raise IOError("Sloane encyclopedia is already installed")
203
204
tm = verbose("Downloading stripped version of Sloane encyclopedia")
205
try:
206
fname, _ = urllib.urlretrieve(oeis_url);
207
except IOError as msg:
208
raise IOError("%s\nError fetching the following website:\n %s\nTry checking your internet connection."%(msg, oeis_url))
209
210
if not names_url is None:
211
try:
212
nname, _ = urllib.urlretrieve(names_url);
213
except IOError as msg:
214
raise IOError("%s\nError fetching the following website:\n %s\nTry checking your internet connection."%(msg, names_url))
215
else:
216
nname = None
217
verbose("Finished downloading", tm)
218
219
self.install_from_gz(fname, nname, overwrite)
220
# Delete the temporary downloaded files
221
os.remove(fname)
222
if not nname is None:
223
os.remove(nname)
224
225
def install_from_gz(self, stripped_file, names_file, overwrite=False):
226
"""
227
Install the online encyclopedia from a local stripped.gz file.
228
229
INPUT:
230
231
- ``stripped_file`` - string. The name of the stripped.gz OEIS file.
232
233
- ``names_file`` - string. The name of the names.gz OEIS file, or
234
None if the user does not want it installed.
235
236
- ``overwrite`` - boolean (default: False) If the encyclopedia is
237
already installed and overwrite=True, install 'filename' over the
238
old encyclopedia.
239
"""
240
if not overwrite and os.path.exists(self.__file__):
241
raise IOError("Sloane encyclopedia is already installed")
242
243
copy_gz_file(stripped_file, self.__file__)
244
245
if not names_file is None:
246
copy_gz_file(names_file, self.__file_names__)
247
else:
248
# Delete old copies of names.gz since their sequence numbers
249
# probably won't match the newly installed stripped.gz
250
if os.path.exists(self.__file_names__):
251
os.remove(self.__file_names__)
252
253
# Remove the old database from memory so the new one will be
254
# automatically loaded next time the user tries to access it
255
self.unload()
256
257
258
def load(self):
259
"""
260
Load the entire encyclopedia into memory from a file. This is done
261
automatically if the user tries to perform a lookup or a search.
262
"""
263
if self.__loaded__ == True:
264
return
265
try:
266
file_seq = bz2.BZ2File(self.__file__, 'r')
267
except IOError:
268
raise IOError("The Sloane Encyclopedia database must be installed. Use e.g. 'SloaneEncyclopedia.install()' to download and install it.")
269
270
self.__data__ = {}
271
272
tm = verbose("Loading Sloane encyclopedia from disk")
273
entry = re.compile(r'A(?P<num>\d{6}) ,(?P<body>.*),$');
274
for L in file_seq:
275
if len(L) == 0:
276
continue
277
m = entry.search(L)
278
if m:
279
seqnum = int(m.group('num'))
280
msg = m.group('body').strip()
281
self.__data__[seqnum] = [seqnum, None, ','+msg+',', None]
282
file_seq.close()
283
284
try:
285
file_names = bz2.BZ2File(self.__file_names__, 'r')
286
entry = re.compile(r'A(?P<num>\d{6}) (?P<body>.*)$');
287
for L in file_names:
288
if len(L) == 0: continue
289
m = entry.search(L)
290
if m:
291
seqnum = int(m.group('num'))
292
self.__data__[seqnum][3] = m.group('body').strip()
293
file_names.close()
294
self.__loaded_names__ = True
295
except KeyError:
296
### Some sequence in the names file isn't in the database
297
raise KeyError("Sloane OEIS sequence and name files do not match. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
298
except IOError as msg:
299
### The names database is not installed
300
self.__loaded_names__ = False
301
302
verbose("Finished loading", tm)
303
self.__loaded__ = True
304
305
def sequence_name(self, N):
306
"""
307
Return the name of sequence N in the encyclopedia. If sequence N
308
does not exist, return ''. If the names database is not installed,
309
raise an IOError.
310
311
INPUT:
312
313
- ``N`` - int
314
315
OUTPUT: string
316
317
EXAMPLES:
318
319
sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
320
'Number of groups of order n.'
321
"""
322
self.load()
323
if not self.__loaded_names__:
324
raise IOError("The Sloane OEIS names file is not installed. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
325
326
if not N in self.__data__: # sequence N does not exist
327
return ''
328
return self.__data__[N][3]
329
330
def unload(self):
331
"""
332
Remove the database from memory.
333
"""
334
if self.__loaded__ == False:
335
return
336
del self.__data__
337
self.__loaded__ = False
338
self.__loaded_names__ = False
339
340
SloaneEncyclopedia = SloaneEncyclopediaClass()
341
342
def copy_gz_file(gz_source, bz_destination):
343
"""
344
Decompress a gzipped file and install the bzipped verson. This is
345
used by SloaneEncyclopedia.install_from_gz to install several
346
gzipped OEIS database files.
347
348
INPUT:
349
350
- ``gz_source`` - string. The name of the gzipped file.
351
352
- ``bz_destination`` - string. The name of the newly compressed file.
353
"""
354
import gzip
355
from sage.misc.misc import sage_makedirs
356
357
# Read the gzipped input
358
try:
359
gz_input = gzip.open(gz_source, 'r')
360
db_text = gz_input.read()
361
gz_input.close()
362
except IOError as msg:
363
raise IOError("Error reading gzipped input file:\n%s"%msg)
364
365
# Write the bzipped output
366
try:
367
sage_makedirs(os.path.dirname(bz_destination))
368
bz2_output = bz2.BZ2File(bz_destination, 'w')
369
bz2_output.write(db_text)
370
bz2_output.close()
371
except IOError as msg:
372
raise IOError("Error writing bzipped output file:\n%s"%msg)
373
374
def parse_sequence(text=''):
375
r"""
376
This internal function was only used by the sloane_find function,
377
which is now deprecated.
378
379
TESTS::
380
sage: from sage.databases.sloane import parse_sequence
381
sage: parse_sequence()
382
doctest:1: DeprecationWarning: The function parse_sequence is not used anymore (2012-01-01).
383
See http://trac.sagemath.org/10358 for details.
384
"""
385
deprecation(10358, "The function parse_sequence is not used anymore (2012-01-01).")
386
387
def sloane_sequence(number=1, verbose=True):
388
r"""
389
This function is broken. It is replaced by the
390
:mod:`OEIS <sage.databases.oeis>` module.
391
392
Type ``oeis?`` for more information.
393
394
TESTS::
395
396
sage: sloane_sequence(123)
397
doctest:1: DeprecationWarning: The function sloane_sequence is deprecated. Use oeis() instead (2012-01-01).
398
See http://trac.sagemath.org/10358 for details.
399
"""
400
deprecation(10358,
401
"The function sloane_sequence is deprecated. "
402
"Use oeis() instead (2012-01-01).")
403
404
def sloane_find(list=[], nresults=30, verbose=True):
405
r"""
406
This function is broken. It is replaced by the
407
:mod:`OEIS <sage.databases.oeis>` module.
408
409
Type ``oeis?`` for more information.
410
411
TESTS::
412
413
sage: sloane_find([1,2,3])
414
doctest:1: DeprecationWarning: The function sloane_find is deprecated. Use oeis() instead (2012-01-01).
415
See http://trac.sagemath.org/10358 for details.
416
"""
417
deprecation(10358,
418
"The function sloane_find is deprecated. "
419
"Use oeis() instead (2012-01-01).")
420
421
422