Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/sage0.py
8814 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
command = "python -u"
142
prompt = ">>>"
143
if init_code is None:
144
init_code = ['from sage.all import *', 'import cPickle']
145
else:
146
# Disable the IPython history (implemented as SQLite database)
147
# to avoid problems with locking.
148
command = "sage-ipython --HistoryManager.hist_file=:memory: --colors=NoColor"
149
prompt = "sage: "
150
if init_code is None:
151
init_code = ['import cPickle']
152
153
Expect.__init__(self,
154
name = 'sage',
155
prompt = prompt,
156
command = command,
157
restart_on_ctrlc = False,
158
logfile = logfile,
159
init_code = init_code,
160
server = server,
161
server_tmpdir = server_tmpdir,
162
remote_cleaner = remote_cleaner,
163
**kwds
164
)
165
self._preparse = preparse
166
167
def cputime(self, t=None):
168
"""
169
Return cputime since this Sage subprocess was started.
170
171
EXAMPLES::
172
173
sage: sage0.cputime() # random output
174
1.3530439999999999
175
sage: sage0('factor(2^157-1)')
176
852133201 * 60726444167 * 1654058017289 * 2134387368610417
177
sage: sage0.cputime() # random output
178
1.6462939999999999
179
"""
180
s = self.eval('cputime(%s)'%t)
181
i = s.rfind('m')
182
if i != -1:
183
s = s[i+1:-1]
184
return float(s)
185
186
def trait_names(self):
187
"""
188
EXAMPLES::
189
190
sage: t = sage0.trait_names()
191
sage: len(t) > 100
192
True
193
sage: 'gcd' in t
194
True
195
"""
196
return eval(self.eval('print repr(globals().keys())'))
197
198
def quit(self, verbose=False):
199
"""
200
EXAMPLES::
201
202
sage: s = Sage()
203
sage: s.eval('2+2')
204
'4'
205
sage: s.quit()
206
"""
207
import signal
208
if not self._expect is None:
209
pid = self._expect.pid
210
if verbose:
211
if self.is_remote():
212
print "Exiting spawned %s process (local pid=%s, running on %s)"%(self,pid,self._server)
213
else:
214
print "Exiting spawned %s process (pid=%s)."%(self, pid)
215
try:
216
for i in range(10): # multiple times, since clears out junk injected with ._get, etc.
217
self._expect.sendline(chr(3)) # send ctrl-c
218
self._expect.sendline('quit_sage(verbose=%s)'%verbose)
219
self._so_far(wait=0.2)
220
221
os.killpg(pid, 9)
222
os.kill(pid, 9)
223
224
except (RuntimeError, OSError), msg:
225
pass
226
227
try:
228
os.killpg(pid, 9)
229
os.kill(pid, 9)
230
except OSError:
231
pass
232
233
try:
234
self._expect.close(signal.SIGQUIT)
235
except Exception:
236
pass
237
self._expect = None
238
239
def __call__(self, x):
240
"""
241
EXAMPLES::
242
243
sage: a = sage0(4)
244
sage: a.parent()
245
Sage
246
sage: a is sage0(a)
247
True
248
249
TESTS::
250
251
sage: sage0(axiom(x^2+1)) #optional - axiom
252
x^2 + 1
253
254
"""
255
if isinstance(x, ExpectElement):
256
if x.parent() is self:
257
return x
258
else:
259
return self(x.sage())
260
261
if isinstance(x, str):
262
return SageElement(self, x)
263
264
if self.is_local():
265
open(self._local_tmpfile(),'w').write(cPickle.dumps(x,2))
266
return SageElement(self, 'cPickle.load(open("%s"))'%self._local_tmpfile())
267
else:
268
open(self._local_tmpfile(),'w').write(dumps(x)) # my dumps is compressed by default
269
self._send_tmpfile_to_server()
270
return SageElement(self, 'loads(open("%s").read())'%self._remote_tmpfile())
271
272
def __reduce__(self):
273
"""
274
EXAMPLES::
275
276
sage: sage0.__reduce__()
277
(<function reduce_load_Sage at 0x...>, ())
278
"""
279
return reduce_load_Sage, tuple([])
280
281
def _quit_string(self):
282
"""
283
EXAMPLES::
284
285
sage: sage0._quit_string()
286
''
287
"""
288
return ""
289
290
def preparse(self, x):
291
"""
292
Returns the preparsed version of the string s.
293
294
EXAMPLES::
295
296
sage: sage0.preparse('2+2')
297
'Integer(2)+Integer(2)'
298
"""
299
return sage.misc.preparser.preparse(x)
300
301
def eval(self, line, strip=True, **kwds):
302
"""
303
Send the code x to a second instance of the Sage interpreter and
304
return the output as a string.
305
306
This allows you to run two completely independent copies of Sage at
307
the same time in a unified way.
308
309
INPUT:
310
311
312
- ``line`` - input line of code
313
314
- ``strip`` - ignored
315
316
317
EXAMPLES::
318
319
sage: sage0.eval('2+2')
320
'4'
321
"""
322
if self._preparse:
323
line = self.preparse(line)
324
return Expect.eval(self, line, **kwds).strip()
325
326
def set(self, var, value):
327
"""
328
Set the variable var to the given value.
329
330
EXAMPLES::
331
332
sage: sage0.set('x', '2')
333
sage: sage0.get('x')
334
'2'
335
"""
336
cmd = '%s=%s'%(var,value)
337
out = self.eval(cmd)
338
if 'Traceback' in out:
339
raise TypeError, "Error executing code in Sage\nCODE:\n\t%s\nSage ERROR:\n\t%s"%(cmd, out)
340
341
def get(self, var):
342
"""
343
Get the value of the variable var.
344
345
EXAMPLES::
346
347
sage: sage0.set('x', '2')
348
sage: sage0.get('x')
349
'2'
350
"""
351
return self.eval('print %s'%var).strip()
352
353
def clear(self, var):
354
"""
355
Clear the variable named var.
356
357
Note that the exact format of the NameError for a cleared variable
358
is slightly platform dependent, see trac #10539.
359
360
EXAMPLES::
361
362
sage: sage0.set('x', '2')
363
sage: sage0.get('x')
364
'2'
365
sage: sage0.clear('x')
366
sage: 'NameError' in sage0.get('x')
367
True
368
"""
369
self.eval('del %s'%var)
370
371
def _contains(self, v1, v2):
372
"""
373
EXAMPLES::
374
375
sage: sage0._contains('2', 'QQ')
376
True
377
"""
378
return self.eval('%s in %s'%(v1,v2)) == "True"
379
380
def console(self):
381
"""
382
Spawn a new Sage command-line session.
383
384
EXAMPLES::
385
386
sage: sage0.console() #not tested
387
----------------------------------------------------------------------
388
| Sage Version ..., Release Date: ... |
389
| Type notebook() for the GUI, and license() for information. |
390
----------------------------------------------------------------------
391
...
392
"""
393
sage0_console()
394
395
def version(self):
396
"""
397
EXAMPLES::
398
399
sage: sage0.version()
400
'Sage Version ..., Release Date: ...'
401
sage: sage0.version() == version()
402
True
403
"""
404
return sage0_version()
405
406
def _object_class(self):
407
"""
408
EXAMPLES::
409
410
sage: sage0._object_class()
411
<class 'sage.interfaces.sage0.SageElement'>
412
"""
413
return SageElement
414
415
def new(self, x):
416
"""
417
EXAMPLES::
418
419
sage: sage0.new(2)
420
2
421
sage: _.parent()
422
Sage
423
"""
424
return SageElement(self, x)
425
426
427
class SageElement(ExpectElement):
428
429
def _graphics_(self):
430
"""
431
Disable graphical output.
432
433
This is necessary because otherwise our :meth:`__getattr__`
434
would be called.
435
436
EXAMPLES::
437
438
sage: m = sage0(4)
439
sage: m._graphics_()
440
False
441
"""
442
return False
443
444
def __getattr__(self, attrname):
445
"""
446
EXAMPLES::
447
448
sage: m = sage0(4)
449
sage: four_gcd = m.gcd
450
sage: type(four_gcd)
451
<class 'sage.interfaces.sage0.SageFunction'>
452
"""
453
self._check_valid()
454
return SageFunction(self, attrname)
455
456
def _sage_(self):
457
"""
458
Return local copy of self.
459
460
EXAMPLE::
461
462
sage: sr = mq.SR(allow_zero_inversions=True)
463
sage: F,s = sr.polynomial_system()
464
sage: F == sage0(F)._sage_()
465
True
466
"""
467
P = self.parent()
468
if P.is_remote():
469
P.eval('save(%s, "%s")'%(self.name(), P._remote_tmpfile()))
470
P._get_tmpfile_from_server(self)
471
return load(P._local_tmp_file())
472
else:
473
P.eval('save(%s, "%s")'%(self.name(), P._local_tmpfile()))
474
return load(P._local_tmpfile())
475
476
class SageFunction(FunctionElement):
477
def __call__(self, *args, **kwds):
478
"""
479
EXAMPLES::
480
481
sage: four_gcd = sage0(4).gcd
482
sage: four_gcd(6)
483
2
484
"""
485
P = self._obj.parent()
486
args = [P(x) for x in args]
487
args = ','.join([x.name() for x in args])
488
kwds = ",".join(["%s=%s"%(k,P(v).name()) for k,v in kwds.iteritems()])
489
if args != "" and kwds != "":
490
callstr = '%s.%s(%s,%s)'%(self._obj._name, self._name, args, kwds)
491
elif kwds != "":
492
callstr = '%s.%s(%s)'%(self._obj._name, self._name, kwds)
493
elif args != "":
494
callstr = '%s.%s(%s)'%(self._obj._name, self._name, args)
495
else:
496
callstr = '%s.%s()'%(self._obj._name, self._name)
497
z = SageElement(P, callstr)
498
499
return z
500
501
def __repr__(self):
502
"""
503
EXAMPLES::
504
505
sage: sage0(4).gcd
506
<function gcd>
507
"""
508
509
return str(self._obj.parent().eval('%s.%s'%(self._obj._name, self._name)))
510
511
512
513
sage0 = Sage()
514
515
def reduce_load_Sage():
516
"""
517
EXAMPLES::
518
519
sage: from sage.interfaces.sage0 import reduce_load_Sage
520
sage: reduce_load_Sage()
521
Sage
522
"""
523
return sage0
524
525
def reduce_load_element(s):
526
"""
527
EXAMPLES::
528
529
sage: from sage.interfaces.sage0 import reduce_load_element
530
sage: s = dumps(1/2)
531
sage: half = reduce_load_element(s); half
532
1/2
533
sage: half.parent()
534
Sage
535
"""
536
import base64
537
s = base64.b32encode(s)
538
sage0.eval('import base64')
539
return sage0('loads(base64.b32decode("%s"))'%s)
540
541
542
import os
543
def sage0_console():
544
"""
545
Spawn a new Sage command-line session.
546
547
EXAMPLES::
548
549
sage: sage0_console() #not tested
550
----------------------------------------------------------------------
551
| Sage Version ..., Release Date: ... |
552
| Type notebook() for the GUI, and license() for information. |
553
----------------------------------------------------------------------
554
...
555
"""
556
os.system('sage')
557
558
def sage0_version():
559
"""
560
EXAMPLES::
561
562
sage: from sage.interfaces.sage0 import sage0_version
563
sage: sage0_version() == version()
564
True
565
"""
566
return str(sage0('version()'))
567
568
#def irun(filename):
569
# """
570
# Run the script in filename step-by-step displaying each input line
571
#
572
# This does not work right with for loops, which span multiple lines.
573
# """
574
# print 'Interactive runing "%s"'%filename
575
# for L in open(filename).xreadlines():
576
# raw_input("sage: "+L[:-1])
577
# print sage0(L)[:-1]
578
579
580