Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/interfaces/sage0.py
4056 views
1
r"""
2
Interface to Sage
3
4
This is an expect interface to *another* copy of the Sage
5
interpreter.
6
"""
7
8
#*****************************************************************************
9
# Copyright (C) 2005 William Stein <[email protected]>
10
#
11
# Distributed under the terms of the GNU General Public License (GPL)
12
#
13
# The full text of the GPL is available at:
14
#
15
# http://www.gnu.org/licenses/
16
#*****************************************************************************
17
18
import cPickle, os
19
20
from expect import Expect, ExpectElement, FunctionElement
21
import sage.misc.preparser
22
23
from sage.structure.sage_object import dumps, load
24
25
26
class Sage(Expect):
27
r"""
28
Expect interface to the Sage interpreter itself.
29
30
INPUT:
31
32
33
- ``server`` - (optional); if specified runs Sage on a
34
remote machine with address. You must have ssh keys setup so you
35
can login to the remote machine by typing "ssh remote_machine" and
36
no password, call _install_hints_ssh() for hints on how to do
37
that.
38
39
40
The version of Sage should be the same as on the local machine,
41
since pickling is used to move data between the two Sage process.
42
43
EXAMPLES: We create an interface to a copy of Sage. This copy of
44
Sage runs as an external process with its own memory space, etc.
45
46
::
47
48
sage: s = Sage()
49
50
Create the element 2 in our new copy of Sage, and cube it.
51
52
::
53
54
sage: a = s(2)
55
sage: a^3
56
8
57
58
Create a vector space of dimension `4`, and compute its
59
generators::
60
61
sage: V = s('QQ^4')
62
sage: V.gens()
63
((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
64
65
Note that V is a not a vector space, it's a wrapper around an
66
object (which happens to be a vector space), in another running
67
instance of Sage.
68
69
::
70
71
sage: type(V)
72
<class 'sage.interfaces.sage0.SageElement'>
73
sage: V.parent()
74
Sage
75
sage: g = V.0; g
76
(1, 0, 0, 0)
77
sage: g.parent()
78
Sage
79
80
We can still get the actual parent by using the name attribute of
81
g, which is the variable name of the object in the child process.
82
83
::
84
85
sage: s('%s.parent()'%g.name())
86
Vector space of dimension 4 over Rational Field
87
88
Note that the memory space is completely different.
89
90
::
91
92
sage: x = 10
93
sage: s('x = 5')
94
5
95
sage: x
96
10
97
sage: s('x')
98
5
99
100
We can have the child interpreter itself make another child Sage
101
process, so now three copies of Sage are running::
102
103
sage: s3 = s('Sage()')
104
sage: a = s3(10)
105
sage: a
106
10
107
108
This `a=10` is in a subprocess of a subprocesses of your
109
original Sage.
110
111
::
112
113
sage: _ = s.eval('%s.eval("x=8")'%s3.name())
114
sage: s3('"x"')
115
8
116
sage: s('x')
117
5
118
sage: x
119
10
120
121
The double quotes are needed because the call to s3 first evaluates
122
its arguments using the s interpreter, so the call to s3 is passed
123
``s('"x"')``, which is the string ``"x"``
124
in the s interpreter.
125
"""
126
def __init__(self, logfile = None,
127
preparse = True,
128
python = False,
129
init_code = None,
130
server = None,
131
server_tmpdir = None,
132
remote_cleaner = True,
133
**kwds):
134
"""
135
EXAMPLES::
136
137
sage: sage0 == loads(dumps(sage0))
138
True
139
"""
140
if python:
141
if server:
142
command = "sage -python -u"
143
else:
144
command = "sage -python -u"
145
prompt = ">>>"
146
if init_code is None:
147
init_code = ['from sage.all import *', 'import cPickle']
148
else:
149
command = "sage"
150
prompt = "sage: "
151
if init_code is None:
152
init_code = ['import cPickle', '%colors NoColor']
153
154
Expect.__init__(self,
155
name = 'sage',
156
prompt = prompt,
157
command = command,
158
restart_on_ctrlc = False,
159
logfile = logfile,
160
init_code = init_code,
161
server = server,
162
server_tmpdir = server_tmpdir,
163
remote_cleaner = remote_cleaner,
164
**kwds
165
)
166
self._preparse = preparse
167
168
def cputime(self, t=None):
169
"""
170
Return cputime since this Sage subprocess was started.
171
172
EXAMPLES::
173
174
sage: sage0.cputime() # random output
175
1.3530439999999999
176
sage: sage0('factor(2^157-1)')
177
852133201 * 60726444167 * 1654058017289 * 2134387368610417
178
sage: sage0.cputime() # random output
179
1.6462939999999999
180
"""
181
s = self.eval('cputime(%s)'%t)
182
i = s.rfind('m')
183
if i != -1:
184
s = s[i+1:-1]
185
return float(s)
186
187
def trait_names(self):
188
"""
189
EXAMPLES::
190
191
sage: t = sage0.trait_names()
192
sage: len(t) > 100
193
True
194
sage: 'gcd' in t
195
True
196
"""
197
return eval(self.eval('print repr(globals().keys())'))
198
199
def quit(self, verbose=False):
200
"""
201
EXAMPLES::
202
203
sage: s = Sage()
204
sage: s.eval('2+2')
205
'4'
206
sage: s.quit()
207
"""
208
import signal
209
if not self._expect is None:
210
pid = self._expect.pid
211
if verbose:
212
if self.is_remote():
213
print "Exiting spawned %s process (local pid=%s, running on %s)"%(self,pid,self._server)
214
else:
215
print "Exiting spawned %s process (pid=%s)."%(self, pid)
216
try:
217
for i in range(10): # multiple times, since clears out junk injected with ._get, etc.
218
self._expect.sendline(chr(3)) # send ctrl-c
219
self._expect.sendline('quit_sage(verbose=%s)'%verbose)
220
self._so_far(wait=0.2)
221
222
os.killpg(pid, 9)
223
os.kill(pid, 9)
224
225
except (RuntimeError, OSError), msg:
226
pass
227
228
try:
229
os.killpg(pid, 9)
230
os.kill(pid, 9)
231
except OSError:
232
pass
233
234
try:
235
self._expect.close(signal.SIGQUIT)
236
except Exception:
237
pass
238
self._expect = None
239
240
def __call__(self, x):
241
"""
242
EXAMPLES::
243
244
sage: a = sage0(4)
245
sage: a.parent()
246
Sage
247
sage: a is sage0(a)
248
True
249
250
TESTS::
251
252
sage: sage0(axiom(x^2+1)) #optional - axiom
253
x^2 + 1
254
255
"""
256
if isinstance(x, ExpectElement):
257
if x.parent() is self:
258
return x
259
else:
260
return self(x.sage())
261
262
if isinstance(x, str):
263
return SageElement(self, x)
264
265
if self.is_local():
266
open(self._local_tmpfile(),'w').write(cPickle.dumps(x,2))
267
return SageElement(self, 'cPickle.load(open("%s"))'%self._local_tmpfile())
268
else:
269
open(self._local_tmpfile(),'w').write(dumps(x)) # my dumps is compressed by default
270
self._send_tmpfile_to_server()
271
return SageElement(self, 'loads(open("%s").read())'%self._remote_tmpfile())
272
273
def __reduce__(self):
274
"""
275
EXAMPLES::
276
277
sage: sage0.__reduce__()
278
(<function reduce_load_Sage at 0x...>, ())
279
"""
280
return reduce_load_Sage, tuple([])
281
282
def _quit_string(self):
283
"""
284
EXAMPLES::
285
286
sage: sage0._quit_string()
287
'from sage.misc.misc import delete_tmpfiles; delete_tmpfiles()'
288
"""
289
return 'from sage.misc.misc import delete_tmpfiles; delete_tmpfiles()'
290
291
def preparse(self, x):
292
"""
293
Returns the preparsed version of the string s.
294
295
EXAMPLES::
296
297
sage: sage0.preparse('2+2')
298
'Integer(2)+Integer(2)'
299
"""
300
return sage.misc.preparser.preparse(x)
301
302
def eval(self, line, strip=True, **kwds):
303
"""
304
Send the code x to a second instance of the Sage interpreter and
305
return the output as a string.
306
307
This allows you to run two completely independent copies of Sage at
308
the same time in a unified way.
309
310
INPUT:
311
312
313
- ``line`` - input line of code
314
315
- ``strip`` - ignored
316
317
318
EXAMPLES::
319
320
sage: sage0.eval('2+2')
321
'4'
322
"""
323
if self._preparse:
324
line = self.preparse(line)
325
return Expect.eval(self, line, **kwds).strip()
326
327
def set(self, var, value):
328
"""
329
Set the variable var to the given value.
330
331
EXAMPLES::
332
333
sage: sage0.set('x', '2')
334
sage: sage0.get('x')
335
'2'
336
"""
337
cmd = '%s=%s'%(var,value)
338
out = self.eval(cmd)
339
if 'Traceback' in out:
340
raise TypeError, "Error executing code in Sage\nCODE:\n\t%s\nSage ERROR:\n\t%s"%(cmd, out)
341
342
def get(self, var):
343
"""
344
Get the value of the variable var.
345
346
EXAMPLES::
347
348
sage: sage0.set('x', '2')
349
sage: sage0.get('x')
350
'2'
351
"""
352
return self.eval('print %s'%var).strip()
353
354
def clear(self, var):
355
"""
356
Clear the variable named var.
357
358
Note that the exact format of the NameError for a cleared variable
359
is slightly platform dependent, see trac #10539.
360
361
EXAMPLES::
362
363
sage: sage0.set('x', '2')
364
sage: sage0.get('x')
365
'2'
366
sage: sage0.clear('x')
367
sage: 'NameError' in sage0.get('x')
368
True
369
"""
370
self.eval('del %s'%var)
371
372
def _contains(self, v1, v2):
373
"""
374
EXAMPLES::
375
376
sage: sage0._contains('2', 'QQ')
377
True
378
"""
379
return self.eval('%s in %s'%(v1,v2)) == "True"
380
381
def console(self):
382
"""
383
Spawn a new Sage command-line session.
384
385
EXAMPLES::
386
387
sage: sage0.console() #not tested
388
----------------------------------------------------------------------
389
| Sage Version ..., Release Date: ... |
390
| Type notebook() for the GUI, and license() for information. |
391
----------------------------------------------------------------------
392
...
393
"""
394
sage0_console()
395
396
def version(self):
397
"""
398
EXAMPLES::
399
400
sage: sage0.version()
401
'Sage Version ..., Release Date: ...'
402
sage: sage0.version() == version()
403
True
404
"""
405
return sage0_version()
406
407
def _object_class(self):
408
"""
409
EXAMPLES::
410
411
sage: sage0._object_class()
412
<class 'sage.interfaces.sage0.SageElement'>
413
"""
414
return SageElement
415
416
def new(self, x):
417
"""
418
EXAMPLES::
419
420
sage: sage0.new(2)
421
2
422
sage: _.parent()
423
Sage
424
"""
425
return SageElement(self, x)
426
427
class SageElement(ExpectElement):
428
def __getattr__(self, attrname):
429
"""
430
EXAMPLES::
431
432
sage: m = sage0(4)
433
sage: four_gcd = m.gcd
434
sage: type(four_gcd)
435
<class 'sage.interfaces.sage0.SageFunction'>
436
"""
437
self._check_valid()
438
return SageFunction(self, attrname)
439
440
def _sage_(self):
441
"""
442
Return local copy of self.
443
444
EXAMPLE::
445
446
sage: sr = mq.SR(allow_zero_inversions=True)
447
sage: F,s = sr.polynomial_system()
448
sage: F == sage0(F)._sage_()
449
True
450
"""
451
P = self.parent()
452
if P.is_remote():
453
P.eval('save(%s, "%s")'%(self.name(), P._remote_tmpfile()))
454
P._get_tmpfile_from_server(self)
455
return load(P._local_tmp_file())
456
else:
457
P.eval('save(%s, "%s")'%(self.name(), P._local_tmpfile()))
458
return load(P._local_tmpfile())
459
460
class SageFunction(FunctionElement):
461
def __call__(self, *args, **kwds):
462
"""
463
EXAMPLES::
464
465
sage: four_gcd = sage0(4).gcd
466
sage: four_gcd(6)
467
2
468
"""
469
P = self._obj.parent()
470
args = [P(x) for x in args]
471
args = ','.join([x.name() for x in args])
472
kwds = ",".join(["%s=%s"%(k,P(v).name()) for k,v in kwds.iteritems()])
473
if args != "" and kwds != "":
474
callstr = '%s.%s(%s,%s)'%(self._obj._name, self._name, args, kwds)
475
elif kwds != "":
476
callstr = '%s.%s(%s)'%(self._obj._name, self._name, kwds)
477
elif args != "":
478
callstr = '%s.%s(%s)'%(self._obj._name, self._name, args)
479
else:
480
callstr = '%s.%s()'%(self._obj._name, self._name)
481
z = SageElement(P, callstr)
482
483
return z
484
485
def __repr__(self):
486
"""
487
EXAMPLES::
488
489
sage: sage0(4).gcd
490
<built-in method gcd of sage.rings.integer.Integer object at 0x...>
491
"""
492
493
return str(self._obj.parent().eval('%s.%s'%(self._obj._name, self._name)))
494
495
496
497
sage0 = Sage()
498
499
def reduce_load_Sage():
500
"""
501
EXAMPLES::
502
503
sage: from sage.interfaces.sage0 import reduce_load_Sage
504
sage: reduce_load_Sage()
505
Sage
506
"""
507
return sage0
508
509
def reduce_load_element(s):
510
"""
511
EXAMPLES::
512
513
sage: from sage.interfaces.sage0 import reduce_load_element
514
sage: s = dumps(1/2)
515
sage: half = reduce_load_element(s); half
516
1/2
517
sage: half.parent()
518
Sage
519
"""
520
import base64
521
s = base64.b32encode(s)
522
sage0.eval('import base64')
523
return sage0('loads(base64.b32decode("%s"))'%s)
524
525
526
import os
527
def sage0_console():
528
"""
529
Spawn a new Sage command-line session.
530
531
EXAMPLES::
532
533
sage: sage0_console() #not tested
534
----------------------------------------------------------------------
535
| Sage Version ..., Release Date: ... |
536
| Type notebook() for the GUI, and license() for information. |
537
----------------------------------------------------------------------
538
...
539
"""
540
os.system('sage')
541
542
def sage0_version():
543
"""
544
EXAMPLES::
545
546
sage: from sage.interfaces.sage0 import sage0_version
547
sage: sage0_version() == version()
548
True
549
"""
550
return str(sage0('version()'))
551
552
#def irun(filename):
553
# """
554
# Run the script in filename step-by-step displaying each input line
555
#
556
# This does not work right with for loops, which span multiple lines.
557
# """
558
# print 'Interactive runing "%s"'%filename
559
# for L in open(filename).xreadlines():
560
# raw_input("sage: "+L[:-1])
561
# print sage0(L)[:-1]
562
563
564