Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/gap.py
8814 views
1
r"""
2
Interface to GAP
3
4
Sage provides an interface to the GAP system. This system provides
5
extensive group theory, combinatorics, etc.
6
7
The GAP interface will only work if GAP is installed on your
8
computer; this should be the case, since GAP is included with Sage.
9
The interface offers three pieces of functionality:
10
11
12
#. ``gap_console()`` - A function that dumps you into
13
an interactive command-line GAP session.
14
15
#. ``gap(expr)`` - Evaluation of arbitrary GAP
16
expressions, with the result returned as a string.
17
18
#. ``gap.new(expr)`` - Creation of a Sage object that
19
wraps a GAP object. This provides a Pythonic interface to GAP. For
20
example, if ``f=gap.new(10)``, then
21
``f.Factors()`` returns the prime factorization of
22
`10` computed using GAP.
23
24
25
First Examples
26
--------------
27
28
We factor an integer using GAP::
29
30
sage: n = gap(20062006); n
31
20062006
32
sage: n.parent()
33
Gap
34
sage: fac = n.Factors(); fac
35
[ 2, 17, 59, 73, 137 ]
36
sage: fac.parent()
37
Gap
38
sage: fac[1]
39
2
40
41
GAP and Singular
42
----------------
43
44
This example illustrates conversion between Singular and GAP via
45
Sage as an intermediate step. First we create and factor a Singular
46
polynomial.
47
48
::
49
50
sage: singular(389)
51
389
52
sage: R1 = singular.ring(0, '(x,y)', 'dp')
53
sage: f = singular('9*x^16-18*x^13*y^2-9*x^12*y^3+9*x^10*y^4-18*x^11*y^2+36*x^8*y^4+18*x^7*y^5-18*x^5*y^6+9*x^6*y^4-18*x^3*y^6-9*x^2*y^7+9*y^8')
54
sage: F = f.factorize()
55
sage: print F
56
[1]:
57
_[1]=9
58
_[2]=x^6-2*x^3*y^2-x^2*y^3+y^4
59
_[3]=-x^5+y^2
60
[2]:
61
1,1,2
62
63
Next we convert the factor `-x^5+y^2` to a Sage
64
multivariate polynomial. Note that it is important to let
65
`x` and `y` be the generators of a polynomial ring,
66
so the eval command works.
67
68
::
69
70
sage: R.<x,y> = PolynomialRing(QQ,2)
71
sage: s = F[1][3].sage_polystring(); s
72
'-x**5+y**2'
73
sage: g = eval(s); g
74
-x^5 + y^2
75
76
Next we create a polynomial ring in GAP and obtain its
77
indeterminates::
78
79
sage: R = gap.PolynomialRing('Rationals', 2); R
80
PolynomialRing( Rationals, ["x_1", "x_2"] )
81
sage: I = R.IndeterminatesOfPolynomialRing(); I
82
[ x_1, x_2 ]
83
84
In order to eval `g` in GAP, we need to tell GAP to view
85
the variables ``x0`` and ``x1`` as the two
86
generators of `R`. This is the one tricky part. In the GAP
87
interpreter the object ``I`` has its own name (which
88
isn't ``I``). We can access its name using
89
``I.name()``.
90
91
::
92
93
sage: _ = gap.eval("x := %s[1];; y := %s[2];;"%(I.name(), I.name()))
94
95
Now `x_0` and `x_1` are defined, so we can
96
construct the GAP polynomial `f` corresponding to
97
`g`::
98
99
sage: R.<x,y> = PolynomialRing(QQ,2)
100
sage: f = gap(str(g)); f
101
-x_1^5+x_2^2
102
103
We can call GAP functions on `f`. For example, we evaluate
104
the GAP ``Value`` function, which evaluates `f`
105
at the point `(1,2)`.
106
107
::
108
109
sage: f.Value(I, [1,2])
110
3
111
sage: g(1,2) # agrees
112
3
113
114
Saving and loading objects
115
--------------------------
116
117
Saving and loading GAP objects (using the dumps method, etc.) is
118
*not* supported, since the output string representation of Gap
119
objects is sometimes not valid input to GAP. Creating classes that
120
wrap GAP objects *is* supported, via simply defining the a
121
_gap_init_ member function that returns a string that when
122
evaluated in GAP constructs the object. See
123
``groups/permutation_group.py`` for a nontrivial
124
example of this.
125
126
Long Input
127
----------
128
129
The GAP interface reads in even very long input (using files) in a
130
robust manner, as long as you are creating a new object.
131
132
.. note::
133
134
Using ``gap.eval`` for long input is much less robust, and is not
135
recommended.
136
137
::
138
139
sage: t = '"%s"'%10^10000 # ten thousand character string.
140
sage: a = gap(t)
141
142
Changing which GAP is used
143
--------------------------
144
145
Use this code to change which GAP interpreter is run. E.g.,
146
147
::
148
149
import sage.interfaces.gap
150
sage.interfaces.gap.gap_cmd = "/usr/local/bin/gap"
151
152
AUTHORS:
153
154
- David Joyner and William Stein: initial version(s)
155
156
- William Stein (2006-02-01): modified gap_console command so it uses
157
exactly the same startup command as Gap.__init__.
158
159
- William Stein (2006-03-02): added tab completions: gap.[tab], x =
160
gap(...), x.[tab], and docs, e.g., gap.function? and x.function?
161
"""
162
163
#*****************************************************************************
164
# Copyright (C) 2005 William Stein <[email protected]>
165
#
166
# Distributed under the terms of the GNU General Public License (GPL)
167
#
168
# This code is distributed in the hope that it will be useful,
169
# but WITHOUT ANY WARRANTY; without even the implied warranty of
170
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
171
# General Public License for more details.
172
#
173
# The full text of the GPL is available at:
174
#
175
# http://www.gnu.org/licenses/
176
#*****************************************************************************
177
178
import expect
179
from expect import Expect, ExpectElement, FunctionElement, ExpectFunction
180
from sage.env import SAGE_LOCAL, SAGE_EXTCODE, DOT_SAGE
181
from sage.misc.misc import is_64_bit, is_in_string
182
import re
183
import os
184
import pexpect
185
import time
186
import platform
187
188
GAP_DIR = os.path.join(DOT_SAGE, 'gap')
189
190
WORKSPACE = os.path.join(GAP_DIR, 'workspace-%s'%abs(hash(SAGE_LOCAL)))
191
192
GAP_BINARY = os.path.join(SAGE_LOCAL, 'bin', 'gap')
193
194
first_try = True
195
196
gap_cmd = "gap -r"
197
if platform.processor() == 'ia64' and os.path.exists('/usr/bin/prctl'):
198
# suppress unaligned access to 0x..., ip=0x... warnings
199
gap_cmd = 'prctl --unaligned=silent ' + gap_cmd
200
201
def gap_command(use_workspace_cache=True, local=True):
202
if use_workspace_cache:
203
if local:
204
return "%s -L %s"%(gap_cmd, WORKSPACE), False
205
else:
206
# TO DO: Use remote workspace
207
return gap_cmd, False
208
else:
209
return gap_cmd, True
210
211
############ Set the GAP memory pool size
212
213
# you should always use get_gap_memory_pool_size() to access this value
214
gap_memory_pool_size = None
215
216
def set_gap_memory_pool_size(size_in_bytes):
217
"""
218
Set the desired gap memory pool size.
219
220
Subsequently started GAP/libGAP instances will use this as
221
default. Currently running instances are unchanged.
222
223
GAP will only reserve ``size_in_bytes`` address space. Unless you
224
actually start a big GAP computation, the memory will not be
225
used. However, corresponding swap space will be reserved so that
226
GAP will always be able to use the reserved address space if
227
needed. While nothing is actually written to disc as long as you
228
don't run a big GAP computation, the reserved swap space will not
229
be available for other processes.
230
231
INPUT:
232
233
- ``size_in_bytes`` -- integer. The desired memory pool size.
234
235
EXAMPLES::
236
237
sage: from sage.interfaces.gap import \
238
... get_gap_memory_pool_size, set_gap_memory_pool_size
239
sage: n = get_gap_memory_pool_size()
240
sage: set_gap_memory_pool_size(n)
241
sage: n == get_gap_memory_pool_size()
242
True
243
sage: n # random output
244
1534059315
245
"""
246
global gap_memory_pool_size
247
gap_memory_pool_size = size_in_bytes
248
249
def get_gap_memory_pool_size():
250
"""
251
Get the gap memory pool size for new GAP processes.
252
253
EXAMPLES::
254
255
sage: from sage.interfaces.gap import \
256
... get_gap_memory_pool_size
257
sage: get_gap_memory_pool_size() # random output
258
1534059315
259
"""
260
global gap_memory_pool_size
261
if gap_memory_pool_size is not None:
262
return gap_memory_pool_size
263
from sage.misc.memory_info import MemoryInfo
264
mem = MemoryInfo()
265
suggested_size = max(int(mem.available_swap() / 10),
266
int(mem.available_ram() / 50))
267
# Don't eat all address space if the user set ulimit -v
268
suggested_size = min(suggested_size, int(mem.virtual_memory_limit()/10))
269
# ~220MB is the minimum for long doctests
270
suggested_size = max(suggested_size, 250 * 1024**2)
271
return suggested_size
272
273
def _get_gap_memory_pool_size_MB():
274
"""
275
Return the gap memory pool size suitable for usage on the GAP
276
command line.
277
278
The GAP 4.5.6 command line parser had issues with large numbers, so
279
we return it in megabytes.
280
281
OUTPUT:
282
283
String.
284
285
EXAMPLES:
286
287
sage: from sage.interfaces.gap import \
288
... _get_gap_memory_pool_size_MB
289
sage: _get_gap_memory_pool_size_MB() # random output
290
'1467m'
291
"""
292
pool = get_gap_memory_pool_size()
293
pool = (pool // (1024**2)) + 1
294
return str(pool)+'m'
295
296
297
############ Classes with methods for both the GAP3 and GAP4 interface
298
299
class Gap_generic(Expect):
300
r"""
301
Generic interface to the GAP3/GAP4 interpreters.
302
303
AUTHORS:
304
305
- William Stein and David Joyner (interface for GAP4)
306
307
- Franco Saliola (Feb 2010): refactored to separate out the generic
308
code
309
310
"""
311
312
def _synchronize(self, timeout=0.5, cmd='%s;'):
313
"""
314
Synchronize GAP pexpect interface.
315
316
See the base method
317
:meth:`~sage.interfaces.expect.Expect._synchronize` for more
318
details.
319
320
We override this method since we are looking at GAP package
321
mode output, which is quite different from the normal
322
(human-readable) interface.
323
324
EXAMPLES::
325
326
sage: gap('"ok"')
327
ok
328
sage: gap._expect.sendline() # now we are out of sync
329
1
330
sage: gap._synchronize()
331
sage: gap(123)
332
123
333
"""
334
if self._expect is None:
335
return
336
E = self._expect
337
from sage.misc.prandom import randrange
338
rnd = randrange(2147483647)
339
cmd = str(rnd)+';'
340
try:
341
E.sendline(cmd)
342
E.expect('@[nf][@J\s>]*'+str(rnd), timeout=timeout)
343
E.send(' ')
344
E.expect('@i', timeout=timeout)
345
except pexpect.TIMEOUT:
346
self.interrupt()
347
except pexpect.EOF:
348
self._crash_msg()
349
self.quit()
350
351
def interrupt(self, tries=None, timeout=1, quit_on_fail=True):
352
"""
353
Interrupt the GAP process
354
355
Gap installs a SIGINT handler, we call it directly instead of
356
trying to sent Ctrl-C. Unlike
357
:meth:`~sage.interfaces.expect.Expect.interrupt`, we only try
358
once since we are knowing what we are doing.
359
360
Sometimes GAP dies while interrupting.
361
362
EXAMPLES::
363
364
sage: gap._eval_line('while(1=1) do i:=1;; od;', wait_for_prompt=False);
365
''
366
sage: rc = gap.interrupt(timeout=1)
367
sage: [ gap(i) for i in range(10) ] # check that it is still working
368
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
369
370
TESTS::
371
372
sage: gap('"finished computation"'); gap.interrupt(); gap('"ok"')
373
finished computation
374
True
375
ok
376
"""
377
E = self._expect
378
if E is None:
379
return True
380
# GAP oddity: If a computation is running and we send Ctrl-C,
381
# it is stopped as expected. But if we are at the idle prompt,
382
# nothing is happening UNTIL we run the next command (which is
383
# then immediately interrupted).
384
# There is apparently also a race in GAP between the signal
385
# handler and input, if we don't wait a bit the result is
386
# unpredictable.
387
E.sendline(chr(3))
388
time.sleep(0.1)
389
E.sendline()
390
try:
391
# send a dummy command
392
E.sendline('224433409;')
393
# read everything up to the actual output of the command
394
E.expect('@[nf][@J\s>]*224433409', timeout=timeout)
395
E.send(' ')
396
# the following input prompt should be the current input
397
# prompt but GAP might be too confused to display it
398
# E.expect('@i', timeout=timeout)
399
# Ideally, we would be finished here. But sometimes GAP
400
# thinks it is still inside a do/od block. So we run some
401
# more plain commands to get back into sync. These might
402
# either complete successfully (output "@n+<number>") or
403
# return a "Syntax error: od expected@J@f +<number>"
404
E.sendline()
405
time.sleep(0.1)
406
E.sendline('224433437;')
407
E.expect('@[nf][@J\s>]*224433437', timeout=timeout)
408
E.sendline()
409
time.sleep(0.1)
410
E.sendline('224433479;')
411
E.expect('@[nf][@J\s>]*224433479', timeout=timeout)
412
E.send(' ')
413
# the following input prompt is now the current input prompt
414
E.expect('@i', timeout=timeout)
415
success = True
416
except (pexpect.TIMEOUT, pexpect.EOF), msg:
417
# GAP died or hangs indefinitely
418
# print 'GAP interrupt:', msg
419
success = False
420
421
if not success and quit_on_fail:
422
self.quit()
423
return success
424
425
def _assign_symbol(self):
426
r"""
427
Return the assign symbol in GAP.
428
429
TESTS::
430
431
sage: gap = Gap()
432
sage: print gap._assign_symbol()
433
:=
434
435
"""
436
return ":="
437
438
def _quit_string(self):
439
"""
440
Returns the string used to quit GAP.
441
442
EXAMPLES::
443
444
sage: gap._quit_string()
445
'quit;'
446
447
::
448
449
sage: g = Gap()
450
sage: a = g(2); g.is_running()
451
True
452
sage: g.quit()
453
sage: g.is_running()
454
False
455
"""
456
return 'quit;'
457
458
def _read_in_file_command(self, filename):
459
r"""
460
Returns the command use to read in a file in GAP.
461
462
EXAMPLES::
463
464
sage: gap._read_in_file_command('test')
465
'Read("test");'
466
467
::
468
469
sage: filename = tmp_filename()
470
sage: f = open(filename, 'w')
471
sage: f.write('xx := 22;\n')
472
sage: f.close()
473
sage: gap.read(filename)
474
sage: gap.get('xx').strip()
475
'22'
476
"""
477
return 'Read("%s");'%filename
478
479
def _continuation_prompt(self):
480
"""
481
Returns the continuation prompt in GAP.
482
483
EXAMPLES::
484
485
sage: gap._continuation_prompt()
486
'> '
487
"""
488
return '> '
489
490
def load_package(self, pkg, verbose=False):
491
"""
492
Load the Gap package with the given name.
493
494
If loading fails, raise a RuntimeError exception.
495
496
TESTS::
497
498
sage: gap.load_package("chevie")
499
Traceback (most recent call last):
500
...
501
RuntimeError: Error loading Gap package chevie. You may want to install the gap_packages SPKG.
502
"""
503
if verbose:
504
print "Loading GAP package %s"%pkg
505
x = self.eval('LoadPackage("%s")'%pkg)
506
if x == 'fail':
507
raise RuntimeError("Error loading Gap package "+str(pkg)+". "+
508
"You may want to install the gap_packages SPKG.")
509
510
def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds):
511
r"""
512
Send the code in the string s to the GAP interpreter and return the
513
output as a string.
514
515
INPUT:
516
517
- ``s`` - string containing GAP code.
518
519
- ``newlines`` - bool (default: True); if False,
520
remove all backslash-newlines inserted by the GAP output
521
formatter.
522
523
- ``strip`` - ignored
524
525
- ``split_lines`` -- bool (default: True); if True then each
526
line is evaluated separately. If False, then the whole
527
block of code is evaluated all at once.
528
529
EXAMPLES::
530
531
sage: gap.eval('2+2')
532
'4'
533
sage: gap.eval('Print(4); #test\n Print(6);')
534
'46'
535
sage: gap.eval('Print("#"); Print(6);')
536
'#6'
537
sage: gap.eval('4; \n 6;')
538
'4\n6'
539
sage: gap.eval('if 3>2 then\nPrint("hi");\nfi;')
540
'hi'
541
sage: gap.eval('## this is a test\nPrint("OK")')
542
'OK'
543
sage: gap.eval('Print("This is a test. Oh no, a #");# but this is a comment\nPrint("OK")')
544
'This is a test. Oh no, a #OK'
545
sage: gap.eval('if 4>3 then')
546
''
547
sage: gap.eval('Print("Hi how are you?")')
548
'Hi how are you?'
549
sage: gap.eval('fi')
550
''
551
"""
552
# '"
553
#We remove all of the comments: On each line, we try
554
#to find a pound sign. If we find it, we check to see if
555
#it is occurring in a string. If it is not in a string, we
556
#strip off the comment.
557
if not split_lines:
558
input_line=str(x)
559
else:
560
input_line = ""
561
for line in str(x).rstrip().split('\n'):
562
pound_position = line.find('#')
563
while pound_position != -1:
564
if not is_in_string(line, pound_position):
565
line = line[:pound_position]
566
pound_position = line.find('#',pound_position+1)
567
input_line += " "+line
568
if not input_line.endswith(';'):
569
input_line += ';'
570
result = Expect.eval(self, input_line, **kwds)
571
if not newlines:
572
result = result.replace("\\\n","")
573
return result.strip()
574
575
576
def _execute_line(self, line, wait_for_prompt=True, expect_eof=False):
577
if self._expect is None: # interface is down
578
self._start()
579
E = self._expect
580
# print "---- sending ", line
581
try:
582
if len(line) > 4095:
583
raise RuntimeError("Passing commands this long to gap would hang")
584
E.sendline(line)
585
except OSError:
586
raise RuntimeError("Error evaluating %s in %s"%(line, self))
587
if wait_for_prompt == False:
588
return ('','')
589
if len(line)==0:
590
return ('','')
591
try:
592
terminal_echo = [] # to be discarded
593
normal_outputs = [] # GAP stdout
594
error_outputs = [] # GAP stderr
595
current_outputs = terminal_echo
596
while True:
597
x = E.expect_list(self._compiled_full_pattern)
598
current_outputs.append(E.before)
599
#print "exec", x, current_outputs
600
if x == 0: # @p
601
if E.after != '@p1.':
602
print "Warning: possibly wrong version of GAP package interface\n"
603
print "Crossing fingers and continuing\n"
604
elif x == 1: #@@
605
current_outputs.append('@')
606
elif x == 2: #special char
607
current_outputs.append(chr(ord(E.after[1:2])-ord('A')+1))
608
elif x == 3: # garbage collection info, ignore
609
pass
610
elif x == 4: # @e -- break loop
611
E.sendline("quit;")
612
elif x == 5: # @c completion, doesn't seem to happen when -p is in use
613
print "I didn't think GAP could do this\n"
614
elif x == 6: # @f GAP error message
615
current_outputs = error_outputs;
616
elif x == 7: # @h help text, but this stopped happening with new help
617
print "I didn't think GAP could do this"
618
elif x == 8: # @i awaiting normal input
619
break;
620
elif x == 9: # @m finished running a child
621
pass # there is no need to do anything
622
elif x==10: #@n normal output line
623
current_outputs = normal_outputs;
624
elif x==11: #@r echoing input
625
current_outputs = terminal_echo
626
elif x==12: #@sN shouldn't happen
627
print "Warning: this should never happen"
628
elif x==13: #@w GAP is trying to send a Window command
629
print "Warning: this should never happen"
630
elif x ==14: #@x seems to be safely ignorable
631
pass
632
elif x == 15:#@z GAP starting a subprocess
633
pass # there is no need to do anything
634
except pexpect.EOF:
635
if not expect_eof:
636
raise RuntimeError("Unexpected EOF from %s executing %s"%(self,line))
637
except IOError:
638
raise RuntimeError("IO Error from %s executing %s"%(self,line))
639
return ("".join(normal_outputs),"".join(error_outputs))
640
641
def _keyboard_interrupt(self):
642
"""
643
TESTS:
644
645
We check that the gap interface behaves correctly after an
646
interrupt::
647
648
sage: gap(2)
649
2
650
sage: import sage.tests.interrupt
651
sage: try:
652
... sage.tests.interrupt.interrupt_after_delay()
653
... while True: SymmetricGroup(8).conjugacy_classes_subgroups()
654
... except KeyboardInterrupt:
655
... pass
656
Interrupting Gap...
657
sage: gap(2)
658
2
659
"""
660
print "Interrupting %s..."%self
661
self.quit()
662
raise KeyboardInterrupt, "Ctrl-c pressed while running %s"%self
663
664
def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if_needed=True):
665
"""
666
Evaluate a line of commands.
667
668
REMARK:
669
670
By default, a long command (length exceeding ``self._eval_using_file_cutoff``)
671
is evaluated using :meth:`_eval_line_using_file`.
672
673
If the command can not be evaluated since the interface
674
has crashed, it is automatically restarted and tried
675
again *once*.
676
677
If the optional ``wait_for_prompt`` is ``False`` then even a very long line
678
will not be evaluated by :meth:`_eval_line_using_file`, since this does not
679
support the ``wait_for_prompt`` option.
680
681
INPUT:
682
683
- ``line`` -- (string) a command.
684
- ``allow_use_file`` (optional bool, default ``True``) --
685
allow to evaluate long commands using :meth:`_eval_line_using_file`.
686
- ``wait_for_prompt`` (optional bool, default ``True``) --
687
wait until the prompt appears in the sub-process' output.
688
- ``restart_if_needed`` (optional bool, default ``True``) --
689
If it is ``True``, the command evaluation is evaluated
690
a second time after restarting the interface, if an
691
``EOFError`` occured.
692
693
TESTS::
694
695
sage: gap._eval_line('2+2;')
696
'4'
697
698
We test the ``wait_for_prompt`` option by sending a command that
699
creates an infinite loop in the GAP sub-process. But if we don't
700
wait for the prompt to appear in the output, we can interrupt
701
the loop without raising a KeyboardInterrupt. At the same time,
702
we test that the line is not forwarded to :meth:`_eval_line_using_file`,
703
since that method would not support the ``wait_for_prompt`` option::
704
705
sage: cutoff = gap._eval_using_file_cutoff
706
sage: gap._eval_using_file_cutoff = 4
707
sage: gap._eval_line('while(1=1) do i:=1;; od;', wait_for_prompt=False)
708
''
709
sage: rc = gap.interrupt(timeout=1)
710
sage: gap._eval_using_file_cutoff = cutoff
711
712
The following tests against a bug fixed at :trac:`10296`::
713
714
sage: gap(3)
715
3
716
sage: gap.eval('quit;')
717
''
718
sage: a = gap(3)
719
** Gap crashed or quit executing '$sage...:=3;;' **
720
Restarting Gap and trying again
721
sage: a
722
3
723
724
"""
725
#if line.find('\n') != -1:
726
# raise ValueError, "line must not contain any newlines"
727
E = None
728
try:
729
if self._expect is None:
730
self._start()
731
E = self._expect
732
#import pdb; pdb.set_trace()
733
if allow_use_file and wait_for_prompt and len(line) > self._eval_using_file_cutoff:
734
return self._eval_line_using_file(line)
735
(normal, error) = self._execute_line(line, wait_for_prompt=wait_for_prompt,
736
expect_eof= (self._quit_string() in line))
737
738
if len(error)> 0:
739
if 'Error, Rebuild completion files!' in error:
740
error += "\nRunning gap_reset_workspace()..."
741
self.quit()
742
gap_reset_workspace()
743
error = error.replace('\r','')
744
raise RuntimeError, "%s produced error output\n%s\n executing %s"%(self, error,line)
745
if len(normal) == 0:
746
return ''
747
748
if isinstance(wait_for_prompt, str) and normal.ends_with(wait_for_prompt):
749
n = len(wait_for_prompt)
750
elif normal.endswith(self._prompt):
751
n = len(self._prompt)
752
elif normal.endswith(self._continuation_prompt()):
753
n = len(self._continuation_prompt())
754
else:
755
n = 0
756
out = normal[:-n]
757
if len(out) > 0 and out[-1] == "\n":
758
out = out[:-1]
759
return out
760
761
except (RuntimeError,TypeError),message:
762
if 'EOF' in message[0] or E is None or not E.isalive():
763
print "** %s crashed or quit executing '%s' **"%(self, line)
764
print "Restarting %s and trying again"%self
765
self._start()
766
if line != '':
767
return self._eval_line(line, allow_use_file=allow_use_file)
768
else:
769
return ''
770
else:
771
raise RuntimeError, message
772
773
except KeyboardInterrupt:
774
self._keyboard_interrupt()
775
raise KeyboardInterrupt, "Ctrl-c pressed while running %s"%self
776
777
def unbind(self, var):
778
"""
779
Clear the variable named var.
780
781
EXAMPLES::
782
783
sage: gap.set('x', '2')
784
sage: gap.get('x')
785
'2'
786
sage: gap.unbind('x')
787
sage: gap.get('x')
788
Traceback (most recent call last):
789
...
790
RuntimeError: Gap produced error output
791
Error, Variable: 'x' must have a value
792
...
793
"""
794
self.eval('Unbind(%s)'%var)
795
self.clear(var)
796
797
def _contains(self, v1, v2):
798
"""
799
EXAMPLES::
800
801
sage: Integers = gap('Integers')
802
sage: two = gap(2)
803
sage: gap._contains(two.name(), Integers.name())
804
True
805
806
::
807
808
sage: 2 in gap('Integers')
809
True
810
"""
811
return self.eval('%s in %s'%(v1,v2)) == "true"
812
813
def _true_symbol(self):
814
"""
815
Returns the symbol for truth in GAP.
816
817
EXAMPLES::
818
819
sage: gap._true_symbol()
820
'true'
821
sage: gap(2) == gap(2)
822
True
823
"""
824
return "true"
825
826
def _false_symbol(self):
827
"""
828
Returns the symbol for falsity in GAP.
829
830
EXAMPLES::
831
832
sage: gap._false_symbol()
833
'false'
834
sage: gap(2) == gap(3)
835
False
836
"""
837
return "false"
838
839
def _equality_symbol(self):
840
"""
841
Returns the symbol for equality in GAP.
842
843
EXAMPLES::
844
845
sage: gap._equality_symbol()
846
'='
847
sage: gap(2) == gap(3)
848
False
849
sage: gap(2) == gap(2)
850
True
851
"""
852
return "="
853
854
def version(self):
855
"""
856
Returns the version of GAP being used.
857
858
EXAMPLES::
859
860
sage: gap.version()
861
'4.6.4'
862
"""
863
return self.eval('VERSION')[1:-1]
864
865
def function_call(self, function, args=None, kwds=None):
866
"""
867
Calls the GAP function with args and kwds.
868
869
EXAMPLES::
870
871
sage: gap.function_call('SymmetricGroup', [5])
872
SymmetricGroup( [ 1 .. 5 ] )
873
874
If the GAP function does not return a value, but prints something
875
to the screen, then a string of the printed output is returned.
876
877
::
878
879
sage: s = gap.function_call('Display', [gap.SymmetricGroup(5).CharacterTable()])
880
sage: type(s)
881
<class 'sage.interfaces.interface.AsciiArtString'>
882
sage: s.startswith('CT')
883
True
884
"""
885
args, kwds = self._convert_args_kwds(args, kwds)
886
self._check_valid_function_name(function)
887
888
#Here we have to do some magic because not all GAP
889
#functions return a value. If you try to store their
890
#results to a variable, then GAP will complain. Thus, before
891
#we evaluate the function, we make it so that the marker string
892
#is in the 'last' variable in GAP. If the function returns a
893
#value, then that value will be in 'last', otherwise it will
894
#be the marker.
895
marker = '"__SAGE_LAST__"'
896
self.eval('__SAGE_LAST__ := %s;;'%marker)
897
res = self.eval("%s(%s)"%(function, ",".join([s.name() for s in args]+
898
['%s=%s'%(key,value.name()) for key, value in kwds.items()])))
899
if self.eval('last') != marker:
900
return self.new('last')
901
else:
902
if res.strip():
903
from sage.interfaces.expect import AsciiArtString
904
return AsciiArtString(res)
905
906
def trait_names(self):
907
"""
908
EXAMPLES::
909
910
sage: c = gap.trait_names()
911
sage: len(c) > 100
912
True
913
sage: 'Order' in c
914
True
915
"""
916
return []
917
918
def get_record_element(self, record, name):
919
r"""
920
Return the element of a GAP record identified by ``name``.
921
922
INPUT:
923
924
- ``record`` -- a GAP record
925
- ``name`` -- str
926
927
OUTPUT:
928
929
- :class:`GapElement`
930
931
EXAMPLES::
932
933
sage: rec = gap('rec( a := 1, b := "2" )')
934
sage: gap.get_record_element(rec, 'a')
935
1
936
sage: gap.get_record_element(rec, 'b')
937
2
938
939
TESTS::
940
941
sage: rec = gap('rec( a := 1, b := "2" )')
942
sage: type(gap.get_record_element(rec, 'a'))
943
<class 'sage.interfaces.gap.GapElement'>
944
"""
945
return self('%s.%s' % (record.name(), name))
946
947
948
class GapElement_generic(ExpectElement):
949
r"""
950
Generic interface to the GAP3/GAP4 interpreters.
951
952
AUTHORS:
953
954
- William Stein and David Joyner (interface for GAP4)
955
956
- Franco Saliola (Feb 2010): refactored to separate out the generic
957
code
958
959
"""
960
def __repr__(self):
961
"""
962
EXAMPLES::
963
964
sage: gap(2)
965
2
966
"""
967
s = ExpectElement.__repr__(self)
968
if s.find('must have a value') != -1:
969
raise RuntimeError, "An error occurred creating an object in %s from:\n'%s'\n%s"%(self.parent().name(), self._create, s)
970
return s
971
972
def bool(self):
973
"""
974
EXAMPLES::
975
976
sage: bool(gap(2))
977
True
978
sage: gap(0).bool()
979
False
980
sage: gap('false').bool()
981
False
982
"""
983
P = self._check_valid()
984
return self != P(0) and repr(self) != 'false'
985
986
987
def __len__(self):
988
"""
989
EXAMPLES::
990
991
sage: v = gap('[1,2,3]'); v
992
[ 1, 2, 3 ]
993
sage: len(v)
994
3
995
996
len is also called implicitly by if::
997
998
sage: if gap('1+1 = 2'):
999
... print "1 plus 1 does equal 2"
1000
1 plus 1 does equal 2
1001
1002
::
1003
1004
sage: if gap('1+1 = 3'):
1005
... print "it is true"
1006
... else:
1007
... print "it is false"
1008
it is false
1009
"""
1010
P = self.parent()
1011
if P.eval('%s = true'%self.name()) == 'true':
1012
return 1
1013
elif P.eval('%s = false'%self.name()) == 'true':
1014
return 0
1015
else:
1016
return int(self.Length())
1017
1018
def _matrix_(self, R):
1019
r"""
1020
Return matrix over the (Sage) ring R determined by self, where self
1021
should be a Gap matrix.
1022
1023
EXAMPLES::
1024
1025
sage: s = gap("(Z(7)^0)*[[1,2,3],[4,5,6]]"); s
1026
[ [ Z(7)^0, Z(7)^2, Z(7) ], [ Z(7)^4, Z(7)^5, Z(7)^3 ] ]
1027
sage: s._matrix_(GF(7))
1028
[1 2 3]
1029
[4 5 6]
1030
1031
::
1032
1033
sage: s = gap("[[1,2], [3/4, 5/6]]"); s
1034
[ [ 1, 2 ], [ 3/4, 5/6 ] ]
1035
sage: m = s._matrix_(QQ); m
1036
[ 1 2]
1037
[3/4 5/6]
1038
sage: parent(m)
1039
Full MatrixSpace of 2 by 2 dense matrices over Rational Field
1040
1041
::
1042
1043
sage: s = gap('[[Z(16),Z(16)^2],[Z(16)^3,Z(16)]]')
1044
sage: s._matrix_(GF(16,'a'))
1045
[ a a^2]
1046
[a^3 a]
1047
"""
1048
P = self.parent()
1049
v = self.DimensionsMat()
1050
n = int(v[1])
1051
m = int(v[2])
1052
1053
from sage.matrix.matrix_space import MatrixSpace
1054
M = MatrixSpace(R, n, m)
1055
entries = [[R(self[r,c]) for c in range(1,m+1)] for r in range(1,n+1)]
1056
return M(entries)
1057
1058
############
1059
1060
class Gap(Gap_generic):
1061
r"""
1062
Interface to the GAP interpreter.
1063
1064
AUTHORS:
1065
1066
- William Stein and David Joyner
1067
"""
1068
def __init__(self, max_workspace_size=None,
1069
maxread=100000, script_subdirectory=None,
1070
use_workspace_cache=True,
1071
server=None,
1072
server_tmpdir=None,
1073
logfile=None):
1074
"""
1075
EXAMPLES::
1076
1077
sage: gap == loads(dumps(gap))
1078
True
1079
"""
1080
self.__use_workspace_cache = use_workspace_cache
1081
cmd, self.__make_workspace = gap_command(use_workspace_cache, server is None)
1082
cmd += " -b -p -T"
1083
if max_workspace_size == None:
1084
max_workspace_size = _get_gap_memory_pool_size_MB()
1085
cmd += ' -o ' + str(max_workspace_size)
1086
cmd += ' -s ' + str(max_workspace_size)
1087
cmd += ' -m 64m ' # attempt at a workaround for http://tracker.gap-system.org/issues/224
1088
cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','sage.g')
1089
Expect.__init__(self,
1090
name='gap',
1091
prompt='gap> ',
1092
command=cmd,
1093
maxread=maxread,
1094
server=server,
1095
server_tmpdir=server_tmpdir,
1096
script_subdirectory=script_subdirectory,
1097
restart_on_ctrlc=True,
1098
verbose_start=False,
1099
logfile=logfile,
1100
eval_using_file_cutoff=100)
1101
self.__seq = 0
1102
1103
def __reduce__(self):
1104
"""
1105
EXAMPLES::
1106
1107
sage: gap.__reduce__()
1108
(<function reduce_load_GAP at 0x...>, ())
1109
sage: f, args = _
1110
sage: f(*args)
1111
Gap
1112
"""
1113
return reduce_load_GAP, tuple([])
1114
1115
def _next_var_name(self):
1116
"""
1117
Returns the next unused variable name.
1118
1119
EXAMPLES::
1120
1121
sage: g = Gap()
1122
sage: g._next_var_name()
1123
'$sage1'
1124
sage: g(2)^2
1125
4
1126
sage: g._next_var_name()
1127
'$sage...'
1128
"""
1129
if len(self._available_vars) != 0:
1130
v = self._available_vars[0]
1131
del self._available_vars[0]
1132
return v
1133
self.__seq += 1
1134
return '$sage%s'%self.__seq
1135
1136
def _start(self):
1137
"""
1138
EXAMPLES::
1139
1140
sage: g = Gap()
1141
sage: g.is_running()
1142
False
1143
sage: g._start()
1144
sage: g.is_running()
1145
True
1146
sage: g.quit()
1147
"""
1148
if self.__use_workspace_cache:
1149
try:
1150
# Check to see if we need to auto-regenerate the gap
1151
# workspace, i.e., if the gap script is more recent
1152
# than the saved workspace, which signals that gap has
1153
# been upgraded.
1154
if os.path.getmtime(WORKSPACE) < os.path.getmtime(GAP_BINARY):
1155
raise OSError("GAP workspace too old")
1156
# Set the modification time of the workspace to the
1157
# current time. This ensures the workspace doesn't
1158
# get deleted too soon by gap_reset_workspace().
1159
os.utime(WORKSPACE, None)
1160
except OSError:
1161
gap_reset_workspace(verbose=False)
1162
1163
global first_try
1164
n = self._session_number
1165
try:
1166
Expect._start(self, "Failed to start GAP.")
1167
except Exception:
1168
if self.__use_workspace_cache and first_try:
1169
first_try = False
1170
self.quit(timeout=0)
1171
expect.failed_to_start.remove(self.name())
1172
gap_reset_workspace(verbose=False)
1173
Expect._start(self, "Failed to start GAP.")
1174
self._session_number = n
1175
self.__make_workspace = False
1176
else:
1177
raise
1178
1179
if self.__use_workspace_cache and self.__make_workspace:
1180
self.save_workspace()
1181
# Now, as self._expect exists, we can compile some useful pattern:
1182
self._compiled_full_pattern = self._expect.compile_pattern_list([
1183
'@p\d+\.','@@','@[A-Z]','@[123456!"#$%&][^+]*\+',
1184
'@e','@c','@f','@h','@i','@m','@n','@r','@s\d','@w.*\+','@x','@z'])
1185
# read everything up to the first "ready" prompt
1186
self._expect.expect("@i")
1187
1188
def _function_class(self):
1189
"""
1190
Returns the GapFunction class.
1191
1192
EXAMPLES::
1193
1194
sage: gap._function_class()
1195
<class 'sage.interfaces.gap.GapFunction'>
1196
1197
::
1198
1199
sage: type(gap.Order)
1200
<class 'sage.interfaces.gap.GapFunction'>
1201
"""
1202
return GapFunction
1203
1204
1205
def cputime(self, t=None):
1206
r"""
1207
Returns the amount of CPU time that the GAP session has used. If
1208
``t`` is not None, then it returns the difference
1209
between the current CPU time and ``t``.
1210
1211
EXAMPLES::
1212
1213
sage: t = gap.cputime()
1214
sage: t #random
1215
0.13600000000000001
1216
sage: gap.Order(gap.SymmetricGroup(5))
1217
120
1218
sage: gap.cputime(t) #random
1219
0.059999999999999998
1220
"""
1221
if t is not None:
1222
return self.cputime() - t
1223
else:
1224
self.eval('_r_ := Runtimes();')
1225
r = sum(eval(self.eval('[_r_.user_time, _r_.system_time, _r_.user_time_children, _r_.system_time_children]')))
1226
return r/1000.0
1227
1228
def save_workspace(self):
1229
r"""
1230
Save the GAP workspace.
1231
1232
TESTS:
1233
1234
We make sure that #9938 (GAP does not start if the path to the GAP
1235
workspace file contains more than 82 characters) is fixed::
1236
1237
sage: ORIGINAL_WORKSPACE = sage.interfaces.gap.WORKSPACE
1238
sage: sage.interfaces.gap.WORKSPACE = os.path.join(SAGE_TMP, "gap" + "0"*(80-len(SAGE_TMP)))
1239
sage: gap = Gap()
1240
sage: gap('3+2') # long time (4s on sage.math, 2013)
1241
5
1242
sage: sage.interfaces.gap.WORKSPACE = ORIGINAL_WORKSPACE
1243
"""
1244
# According to the GAP Reference Manual,
1245
# [http://www.gap-system.org/Manuals/doc/htm/ref/CHAP003.htm#SSEC011.1]
1246
# SaveWorkspace can only be used at the main gap> prompt. It cannot
1247
# be included in the body of a loop or function, or called from a
1248
# break loop.
1249
from sage.misc.temporary_file import atomic_write
1250
with atomic_write(WORKSPACE) as f:
1251
f.close()
1252
self.eval('SaveWorkspace("%s");'%(f.name), allow_use_file=False)
1253
1254
# Todo -- this -- but there is a tricky "when does it end" issue!
1255
# Maybe do via a file somehow?
1256
def help(self, s, pager=True):
1257
"""
1258
Print help on a given topic.
1259
1260
EXAMPLES::
1261
1262
sage: print gap.help('SymmetricGroup', pager=False)
1263
<BLANKLINE>
1264
50 Group Libraries
1265
<BLANKLINE>
1266
When you start GAP, it already knows several groups. Currently GAP initially
1267
knows the following groups:
1268
...
1269
"""
1270
tmp_to_use = self._local_tmpfile()
1271
if self.is_remote():
1272
tmp_to_use = self._remote_tmpfile()
1273
else:
1274
tmp_to_use = self._local_tmpfile()
1275
self.eval('SetGAPDocTextTheme("none")')
1276
self.eval('$SAGE.tempfile := "%s";'%tmp_to_use)
1277
line = Expect.eval(self, "? %s"%s)
1278
Expect.eval(self, "? 1")
1279
match = re.search("Page from (\d+)", line)
1280
if match == None:
1281
print line
1282
else:
1283
(sline,) = match.groups()
1284
if self.is_remote():
1285
self._get_tmpfile()
1286
F = open(self._local_tmpfile(),"r")
1287
help = F.read()
1288
if pager:
1289
from IPython.core.page import page
1290
page(help, start = int(sline)-1)
1291
else:
1292
return help
1293
1294
def set(self, var, value):
1295
"""
1296
Set the variable var to the given value.
1297
1298
EXAMPLES::
1299
1300
sage: gap.set('x', '2')
1301
sage: gap.get('x')
1302
'2'
1303
"""
1304
cmd = ('%s:=%s;;'%(var,value)).replace('\n','')
1305
out = self._eval_line(cmd, allow_use_file=True)
1306
1307
def get(self, var, use_file=False):
1308
"""
1309
Get the string representation of the variable var.
1310
1311
EXAMPLES::
1312
1313
sage: gap.set('x', '2')
1314
sage: gap.get('x')
1315
'2'
1316
"""
1317
if use_file:
1318
tmp = self._local_tmpfile()
1319
if os.path.exists(tmp):
1320
os.unlink(tmp)
1321
self.eval('PrintTo("%s", %s);'%(tmp,var), strip=False)
1322
r = open(tmp).read()
1323
r = r.strip().replace("\\\n","")
1324
os.unlink(tmp)
1325
return r
1326
else:
1327
return self.eval('Print(%s);'%var, newlines=False)
1328
1329
def _pre_interact(self):
1330
"""
1331
EXAMPLES::
1332
1333
sage: gap._pre_interact()
1334
sage: gap._post_interact()
1335
"""
1336
self._eval_line("$SAGE.StartInteract();")
1337
1338
def _post_interact(self):
1339
"""
1340
EXAMPLES::
1341
1342
sage: gap._pre_interact()
1343
sage: gap._post_interact()
1344
"""
1345
self._eval_line("$SAGE.StopInteract();")
1346
1347
def _eval_line_using_file(self, line):
1348
i = line.find(':=')
1349
if i != -1:
1350
j = line.find('"')
1351
if j >= 0 and j < i:
1352
i = -1
1353
if i == -1:
1354
line0 = 'Print( %s );'%line.rstrip().rstrip(';')
1355
try: # this is necessary, since Print requires something as input, and some functions (e.g., Read) return nothing.
1356
return Expect._eval_line_using_file(self, line0)
1357
except RuntimeError, msg:
1358
#if not ("Function call: <func> must return a value" in msg):
1359
# raise RuntimeError, msg
1360
return ''
1361
return Expect._eval_line_using_file(self, line)
1362
1363
def console(self):
1364
"""
1365
Spawn a new GAP command-line session.
1366
1367
EXAMPLES::
1368
1369
sage: gap.console() # not tested
1370
********* GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
1371
* GAP * http://www.gap-system.org
1372
********* Architecture: x86_64-unknown-linux-gnu-gcc-default64
1373
Libs used: gmp, readline
1374
Loading the library and packages ...
1375
Packages: GAPDoc 1.5.1
1376
Try '?help' for help. See also '?copyright' and '?authors'
1377
gap>
1378
"""
1379
gap_console()
1380
1381
def _object_class(self):
1382
"""
1383
Returns the GapElement class.
1384
1385
EXAMPLES::
1386
1387
sage: gap._object_class()
1388
<class 'sage.interfaces.gap.GapElement'>
1389
sage: type(gap(2))
1390
<class 'sage.interfaces.gap.GapElement'>
1391
"""
1392
return GapElement
1393
1394
def _function_element_class(self):
1395
"""
1396
Returns the GapFunctionElement class.
1397
1398
EXAMPLES::
1399
1400
sage: gap._function_element_class()
1401
<class 'sage.interfaces.gap.GapFunctionElement'>
1402
sage: type(gap.SymmetricGroup(4).Order)
1403
<class 'sage.interfaces.gap.GapFunctionElement'>
1404
"""
1405
return GapFunctionElement
1406
1407
def trait_names(self):
1408
"""
1409
EXAMPLES::
1410
1411
sage: c = gap.trait_names()
1412
sage: len(c) > 100
1413
True
1414
sage: 'Order' in c
1415
True
1416
"""
1417
try:
1418
return self.__trait_names
1419
except AttributeError:
1420
self.__trait_names = eval(self.eval('NamesSystemGVars()')) + \
1421
eval(self.eval('NamesUserGVars()'))
1422
return self.__trait_names
1423
1424
1425
############
1426
1427
def gap_reset_workspace(max_workspace_size=None, verbose=False):
1428
r"""
1429
Call this to completely reset the GAP workspace, which is used by
1430
default when Sage first starts GAP.
1431
1432
The first time you start GAP from Sage, it saves the startup state
1433
of GAP in a file ``$HOME/.sage/gap/workspace-HASH``, where ``HASH``
1434
is a hash of the directory where Sage is installed.
1435
1436
This is useful, since then subsequent startup of GAP is at least 10
1437
times as fast. Unfortunately, if you install any new code for GAP,
1438
it won't be noticed unless you explicitly load it, e.g., with
1439
gap.load_package("my_package")
1440
1441
The packages sonata, guava, factint, gapdoc, grape, design, toric,
1442
and laguna are loaded in all cases before the workspace is saved,
1443
if they are available.
1444
1445
TESTS:
1446
1447
Check that ``gap_reset_workspace`` still works when ``GAP_DIR``
1448
doesn't exist, see :trac:`14171`::
1449
1450
sage: ORIGINAL_GAP_DIR = sage.interfaces.gap.GAP_DIR
1451
sage: ORIGINAL_WORKSPACE = sage.interfaces.gap.WORKSPACE
1452
sage: sage.interfaces.gap.GAP_DIR = os.path.join(tmp_dir(), "test_gap_dir")
1453
sage: sage.interfaces.gap.WORKSPACE = os.path.join(sage.interfaces.gap.GAP_DIR, "test_workspace")
1454
sage: os.path.isfile(sage.interfaces.gap.WORKSPACE) # long time
1455
False
1456
sage: gap_reset_workspace() # long time
1457
sage: os.path.isfile(sage.interfaces.gap.WORKSPACE) # long time
1458
True
1459
sage: sage.interfaces.gap.GAP_DIR = ORIGINAL_GAP_DIR
1460
sage: sage.interfaces.gap.WORKSPACE = ORIGINAL_WORKSPACE
1461
1462
Check that the race condition from :trac:`14242` has been fixed.
1463
We temporarily need to change the worksheet filename. ::
1464
1465
sage: ORIGINAL_WORKSPACE = sage.interfaces.gap.WORKSPACE
1466
sage: sage.interfaces.gap.WORKSPACE = tmp_filename()
1467
sage: from multiprocessing import Process
1468
sage: import time
1469
sage: gap = Gap() # long time (reset GAP session)
1470
sage: P = [Process(target=gap, args=("14242",)) for i in range(4)]
1471
sage: for p in P: # long time, indirect doctest
1472
....: p.start()
1473
....: time.sleep(0.2)
1474
sage: for p in P: # long time
1475
....: p.join()
1476
sage: os.unlink(sage.interfaces.gap.WORKSPACE) # long time
1477
sage: sage.interfaces.gap.WORKSPACE = ORIGINAL_WORKSPACE
1478
"""
1479
# Make sure GAP_DIR exists
1480
try:
1481
os.makedirs(GAP_DIR)
1482
msg = "It is OK to delete all these cache files. They will be recreated as needed.\n"
1483
open(os.path.join(GAP_DIR, 'README.txt'), 'w').write(msg)
1484
except OSError:
1485
if not os.path.isdir(GAP_DIR):
1486
raise
1487
1488
# Delete all gap workspaces that haven't been used in the last
1489
# week, to avoid needless cruft. I had an install on sage.math
1490
# with 90 of these, since I run a lot of different versions of
1491
# Sage, and it totalled 1.3GB of wasted space! See trac #4936.
1492
# We only do this after creating a new workspace, since this cruft
1493
# issue is only a problem if workspaces get created every so
1494
# often. We don't want to have to do this on every startup.
1495
now = time.time()
1496
for F in os.listdir(GAP_DIR):
1497
if F.startswith('workspace-'):
1498
W = os.path.join(GAP_DIR, F)
1499
try:
1500
age = now - os.path.getatime(W)
1501
if age >= 604800: # 1 week in seconds
1502
os.unlink(W)
1503
except OSError:
1504
# It's not a problem if W doesn't exist, everything
1505
# else is an error.
1506
if os.path.exists(W):
1507
raise
1508
1509
# Create new workspace with filename WORKSPACE
1510
g = Gap(use_workspace_cache=False, max_workspace_size=None)
1511
for pkg in ['GAPDoc', 'ctbllib', 'sonata', 'guava', 'factint', \
1512
'gapdoc', 'grape', 'design', \
1513
'toric', 'laguna', 'braid']:
1514
# NOTE: Do *not* autoload hap - it screws up PolynomialRing(Rationals,2)
1515
try:
1516
g.load_package(pkg, verbose=verbose)
1517
except RuntimeError, msg:
1518
if verbose:
1519
print '*** %s'%msg
1520
pass
1521
# end for
1522
g.save_workspace()
1523
g.quit()
1524
1525
1526
class GapElement(GapElement_generic):
1527
def __getitem__(self, n):
1528
"""
1529
EXAMPLES::
1530
1531
sage: a = gap([1,2,3])
1532
sage: a[1]
1533
1
1534
"""
1535
self._check_valid()
1536
if not isinstance(n, tuple):
1537
return self.parent().new('%s[%s]'%(self._name, n))
1538
else:
1539
return self.parent().new('%s%s'%(self._name, ''.join(['[%s]'%x for x in n])))
1540
1541
def __reduce__(self):
1542
"""
1543
Note that GAP elements cannot be pickled.
1544
1545
EXAMPLES::
1546
1547
sage: gap(2).__reduce__()
1548
(<function reduce_load at 0x...>, ())
1549
sage: f, args = _
1550
sage: f(*args)
1551
Traceback (most recent call last):
1552
...
1553
ValueError: The session in which this object was defined is no longer running.
1554
"""
1555
return reduce_load, () # default is an invalid object
1556
1557
def str(self, use_file=False):
1558
"""
1559
EXAMPLES::
1560
1561
sage: print gap(2)
1562
2
1563
"""
1564
if use_file:
1565
P = self._check_valid()
1566
return P.get(self.name(), use_file=True)
1567
else:
1568
return self.__repr__()
1569
1570
def _latex_(self):
1571
r"""
1572
EXAMPLES::
1573
1574
sage: s = gap("[[1,2], [3/4, 5/6]]")
1575
sage: latex(s)
1576
\left(\begin{array}{rr} 1&2\\ 3/4&\frac{5}{6}\\ \end{array}\right)
1577
"""
1578
P = self._check_valid()
1579
try:
1580
s = P.eval('LaTeXObj(%s)'%self.name())
1581
s = s.replace('\\\\','\\').replace('"','')
1582
s = s.replace('%\\n',' ')
1583
return s
1584
except RuntimeError:
1585
return str(self)
1586
1587
def trait_names(self):
1588
"""
1589
EXAMPLES::
1590
1591
sage: s5 = gap.SymmetricGroup(5)
1592
sage: 'Centralizer' in s5.trait_names()
1593
True
1594
"""
1595
if '__trait_names' in self.__dict__:
1596
return self.__trait_names
1597
import string
1598
from sage.misc.misc import uniq
1599
P = self.parent()
1600
v = P.eval('$SAGE.OperationsAdmittingFirstArgument(%s)'%self.name())
1601
v = v.replace('Tester(','').replace('Setter(','').replace(')','').replace('\n', '')
1602
v = v.split(',')
1603
v = [ oper.split('"')[1] for oper in v ]
1604
v = [ oper for oper in v if all(ch in string.ascii_letters for ch in oper) ]
1605
v = uniq(v)
1606
self.__trait_names = v
1607
return v
1608
1609
1610
1611
class GapFunctionElement(FunctionElement):
1612
def _sage_doc_(self):
1613
"""
1614
EXAMPLES::
1615
1616
sage: print gap(4).SymmetricGroup._sage_doc_()
1617
<BLANKLINE>
1618
50 Group Libraries
1619
<BLANKLINE>
1620
When you start GAP, it already knows several groups. Currently GAP initially
1621
knows the following groups:
1622
...
1623
"""
1624
M = self._obj.parent()
1625
help = M.help(self._name, pager=False)
1626
return help
1627
1628
1629
class GapFunction(ExpectFunction):
1630
def _sage_doc_(self):
1631
"""
1632
EXAMPLES::
1633
1634
sage: print gap.SymmetricGroup._sage_doc_()
1635
<BLANKLINE>
1636
50 Group Libraries
1637
<BLANKLINE>
1638
When you start GAP, it already knows several groups. Currently GAP initially
1639
knows the following groups:
1640
...
1641
"""
1642
M = self._parent
1643
help = M.help(self._name, pager=False)
1644
return help
1645
1646
1647
def is_GapElement(x):
1648
"""
1649
Returns True if x is a GapElement.
1650
1651
EXAMPLES::
1652
1653
sage: from sage.interfaces.gap import is_GapElement
1654
sage: is_GapElement(gap(2))
1655
True
1656
sage: is_GapElement(2)
1657
False
1658
"""
1659
return isinstance(x, GapElement)
1660
1661
def gfq_gap_to_sage(x, F):
1662
"""
1663
INPUT:
1664
1665
1666
- ``x`` - gap finite field element
1667
1668
- ``F`` - Sage finite field
1669
1670
1671
OUTPUT: element of F
1672
1673
EXAMPLES::
1674
1675
sage: x = gap('Z(13)')
1676
sage: F = GF(13, 'a')
1677
sage: F(x)
1678
2
1679
sage: F(gap('0*Z(13)'))
1680
0
1681
sage: F = GF(13^2, 'a')
1682
sage: x = gap('Z(13)')
1683
sage: F(x)
1684
2
1685
sage: x = gap('Z(13^2)^3')
1686
sage: F(x)
1687
12*a + 11
1688
sage: F.multiplicative_generator()^3
1689
12*a + 11
1690
1691
AUTHOR:
1692
1693
- David Joyner and William Stein
1694
"""
1695
from sage.rings.finite_rings.constructor import FiniteField
1696
1697
s = str(x)
1698
if s[:2] == '0*':
1699
return F(0)
1700
i1 = s.index("(")
1701
i2 = s.index(")")
1702
q = eval(s[i1+1:i2].replace('^','**'))
1703
if q == F.order():
1704
K = F
1705
else:
1706
K = FiniteField(q, F.variable_name())
1707
if s.find(')^') == -1:
1708
e = 1
1709
else:
1710
e = int(s[i2+2:])
1711
if F.degree() == 1:
1712
g = int(gap.eval('Int(Z(%s))'%q))
1713
else:
1714
g = K.multiplicative_generator()
1715
return F(K(g**e))
1716
1717
def intmod_gap_to_sage(x):
1718
r"""
1719
INPUT:
1720
1721
- x -- Gap integer mod ring element
1722
1723
EXAMPLES::
1724
1725
sage: a = gap(Mod(3, 18)); a
1726
ZmodnZObj( 3, 18 )
1727
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
1728
3
1729
sage: b.parent()
1730
Ring of integers modulo 18
1731
1732
sage: a = gap(Mod(3, 17)); a
1733
Z(17)
1734
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
1735
3
1736
sage: b.parent()
1737
Ring of integers modulo 17
1738
1739
sage: a = gap(Mod(0, 17)); a
1740
0*Z(17)
1741
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
1742
0
1743
sage: b.parent()
1744
Ring of integers modulo 17
1745
1746
sage: a = gap(Mod(3, 65537)); a
1747
ZmodpZObj( 3, 65537 )
1748
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
1749
3
1750
sage: b.parent()
1751
Ring of integers modulo 65537
1752
"""
1753
from sage.rings.finite_rings.integer_mod import Mod
1754
from sage.rings.finite_rings.integer_mod_ring import Zmod
1755
s = str(x)
1756
m = re.search(r'Z\(([0-9]*)\)', s)
1757
if m:
1758
return gfq_gap_to_sage(x, Zmod(m.group(1)))
1759
m = re.match(r'Zmod[np]ZObj\( ([0-9]*), ([0-9]*) \)', s)
1760
if m:
1761
return Mod(m.group(1), m.group(2))
1762
raise ValueError, "Unable to convert Gap element '%s'" % s
1763
1764
#############
1765
1766
gap = Gap()
1767
1768
def reduce_load_GAP():
1769
"""
1770
Returns the GAP interface object defined in sage.interfaces.gap.
1771
1772
EXAMPLES::
1773
1774
sage: from sage.interfaces.gap import reduce_load_GAP
1775
sage: reduce_load_GAP()
1776
Gap
1777
"""
1778
return gap
1779
1780
def reduce_load():
1781
"""
1782
Returns an invalid GAP element. Note that this is the object
1783
returned when a GAP element is unpickled.
1784
1785
EXAMPLES::
1786
1787
sage: from sage.interfaces.gap import reduce_load
1788
sage: reduce_load()
1789
Traceback (most recent call last):
1790
...
1791
ValueError: The session in which this object was defined is no longer running.
1792
sage: loads(dumps(gap(2)))
1793
Traceback (most recent call last):
1794
...
1795
ValueError: The session in which this object was defined is no longer running.
1796
"""
1797
return GapElement(None, None)
1798
1799
def gap_console():
1800
"""
1801
Spawn a new GAP command-line session.
1802
1803
Note that in gap-4.5.7 you cannot use a workspace cache that had
1804
no commandline to restore a gap session with commandline.
1805
1806
EXAMPLES::
1807
1808
sage: gap_console() # not tested
1809
********* GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
1810
* GAP * http://www.gap-system.org
1811
********* Architecture: x86_64-unknown-linux-gnu-gcc-default64
1812
Libs used: gmp, readline
1813
Loading the library and packages ...
1814
Packages: GAPDoc 1.5.1
1815
Try '?help' for help. See also '?copyright' and '?authors'
1816
gap>
1817
1818
TESTS::
1819
1820
sage: import subprocess
1821
sage: from sage.interfaces.gap import gap_command
1822
sage: cmd = 'echo "quit;" | ' + gap_command(use_workspace_cache=False)[0]
1823
sage: gap_startup = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
1824
sage: 'http://www.gap-system.org' in gap_startup
1825
True
1826
sage: 'Error' not in gap_startup
1827
True
1828
sage: 'sorry' not in gap_startup
1829
True
1830
"""
1831
cmd, _ = gap_command(use_workspace_cache=False)
1832
cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','console.g')
1833
os.system(cmd)
1834
1835
def gap_version():
1836
"""
1837
Returns the version of GAP being used.
1838
1839
EXAMPLES::
1840
1841
sage: gap_version()
1842
doctest:...: DeprecationWarning: use gap.version() instead
1843
See http://trac.sagemath.org/13211 for details.
1844
'4.6.4'
1845
"""
1846
from sage.misc.superseded import deprecation
1847
deprecation(13211, 'use gap.version() instead')
1848
return gap.eval('VERSION')[1:-1]
1849
1850
1851
1852