Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/libs/gap/util.pyx
8815 views
1
"""
2
Utility functions for libGAP
3
"""
4
5
###############################################################################
6
# Copyright (C) 2012, Volker Braun <[email protected]>
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
# as published by the Free Software Foundation; either version 2 of
10
# the License, or (at your option) any later version.
11
# http://www.gnu.org/licenses/
12
###############################################################################
13
14
from sage.env import SAGE_LOCAL
15
from sage.misc.misc import is_64_bit
16
from libc.stdint cimport uintptr_t
17
from element cimport *
18
19
20
21
############################################################################
22
### Hooking into the GAP memory management #################################
23
############################################################################
24
25
cdef class ObjWrapper(object):
26
"""
27
Wrapper for GAP master pointers
28
29
EXAMPLES::
30
31
sage: from sage.libs.gap.util import ObjWrapper
32
sage: x = ObjWrapper()
33
sage: y = ObjWrapper()
34
sage: x == y
35
True
36
"""
37
38
def __richcmp__(ObjWrapper self, ObjWrapper other, int op):
39
r"""
40
Comparison wrapped libGAP_Obj.
41
42
INPUT:
43
44
- ``lhs``, ``rhs`` -- :class:`ObjWrapper`.
45
46
- ``op`` -- integer. The comparison operation to be performed.
47
48
OUTPUT:
49
50
Boolean.
51
52
EXAMPLES::
53
54
sage: from sage.libs.gap.util import ObjWrapper
55
sage: x = ObjWrapper()
56
sage: y = ObjWrapper()
57
sage: x == y
58
True
59
"""
60
cdef result
61
cdef libGAP_Obj self_value = self.value
62
cdef libGAP_Obj other_value = other.value
63
if op==0: # < 0
64
return self_value < other_value
65
elif op==1: # <= 1
66
return self_value <= other_value
67
elif op==2: # == 2
68
return self_value == other_value
69
elif op==4: # > 4
70
return self_value > other_value
71
elif op==5: # >= 5
72
return self_value >= other_value
73
elif op==3: # != 3
74
return self_value != other_value
75
else:
76
assert False # unreachable
77
78
def __hash__(self):
79
"""
80
Return a hash value
81
82
EXAMPLES::
83
84
sage: from sage.libs.gap.util import ObjWrapper
85
sage: x = ObjWrapper()
86
sage: hash(x)
87
0
88
"""
89
return <int>(self.value)
90
91
92
cdef ObjWrapper wrap_obj(libGAP_Obj obj):
93
"""
94
Constructor function for :class:`ObjWrapper`
95
"""
96
cdef ObjWrapper result = ObjWrapper.__new__(ObjWrapper)
97
result.value = obj
98
return result
99
100
101
owned_objects_refcount = dict()
102
103
cpdef get_owned_objects():
104
"""
105
Helper to access the refcount dictionary from Python code
106
"""
107
return owned_objects_refcount
108
109
110
cdef void reference_obj(libGAP_Obj obj):
111
"""
112
Reference ``obj``
113
"""
114
cdef ObjWrapper wrapped = wrap_obj(obj)
115
global owned_objects_refcount
116
if wrapped in owned_objects_refcount:
117
owned_objects_refcount[wrapped] += 1
118
else:
119
owned_objects_refcount[wrapped] = 1
120
121
122
cdef void dereference_obj(libGAP_Obj obj):
123
"""
124
Reference ``obj``
125
"""
126
cdef ObjWrapper wrapped = wrap_obj(obj)
127
global owned_objects_refcount
128
refcount = owned_objects_refcount.pop(wrapped)
129
if refcount > 1:
130
owned_objects_refcount[wrapped] = refcount - 1
131
132
133
cdef void gasman_callback():
134
"""
135
Callback before each GAP garbage collection
136
"""
137
global owned_objects_refcount
138
for obj in owned_objects_refcount.iterkeys():
139
libGAP_MARK_BAG((<ObjWrapper>obj).value)
140
141
142
143
144
145
############################################################################
146
### Initialization of libGAP ###############################################
147
############################################################################
148
149
def gap_root():
150
"""
151
Find the location of the GAP root install which is stored in the gap
152
startup script.
153
154
EXAMPLES::
155
156
sage: from sage.libs.gap.util import gap_root
157
sage: gap_root() # random output
158
'/home/vbraun/opt/sage-5.3.rc0/local/gap/latest'
159
"""
160
import os.path
161
gapdir = os.path.join(SAGE_LOCAL, 'gap', 'latest')
162
if os.path.exists(gapdir):
163
return gapdir
164
print 'The gap-4.5.5.spkg (or later) seems to be not installed!'
165
gap_sh = open(os.path.join(SAGE_LOCAL, 'bin', 'gap')).read().splitlines()
166
gapdir = filter(lambda dir:dir.strip().startswith('GAP_DIR'), gap_sh)[0]
167
gapdir = gapdir.split('"')[1]
168
gapdir = gapdir.replace('$SAGE_LOCAL', SAGE_LOCAL)
169
return gapdir
170
171
172
cdef initialize():
173
"""
174
Initialize the GAP library, if it hasn't already been
175
initialized. It is safe to call this multiple times.
176
177
TESTS::
178
179
sage: libgap(123) # indirect doctest
180
123
181
"""
182
global _gap_is_initialized
183
if _gap_is_initialized: return
184
185
# Define argv and environ variables, which we will pass in to
186
# initialize GAP. Note that we must pass define the memory pool
187
# size!
188
cdef char* argv[12]
189
argv[0] = "sage"
190
argv[1] = "-l"
191
s = gap_root()
192
argv[2] = s
193
194
from sage.interfaces.gap import _get_gap_memory_pool_size_MB
195
memory_pool = _get_gap_memory_pool_size_MB()
196
argv[3] = "-o"
197
argv[4] = memory_pool
198
argv[5] = "-s"
199
argv[6] = memory_pool
200
201
argv[7] = "-m"
202
argv[8] = "64m"
203
204
argv[9] = "-q" # no prompt!
205
argv[10] = "-T" # no debug loop
206
argv[11] = NULL
207
cdef int argc = 11 # argv[argc] must be NULL
208
209
# Initialize GAP and capture any error messages
210
# The initialization just prints error and does not use the error handler
211
libgap_start_interaction('')
212
libgap_initialize(argc, argv)
213
gap_error_msg = str(libgap_get_output())
214
libgap_finish_interaction()
215
if gap_error_msg:
216
raise RuntimeError('libGAP initialization failed\n' + gap_error_msg)
217
218
# The error handler is called if a GAP evaluation fails, e.g. 1/0
219
libgap_set_error_handler(&error_handler)
220
221
# Prepare global GAP variable to hold temporary GAP objects
222
global reference_holder
223
libgap_enter()
224
reference_holder = libGAP_GVarName("$SAGE_libgap_reference_holder")
225
libgap_exit()
226
227
# Finished!
228
_gap_is_initialized = True
229
230
231
232
############################################################################
233
### Evaluate string in GAP #################################################
234
############################################################################
235
236
cdef libGAP_Obj gap_eval(str gap_string) except? NULL:
237
r"""
238
Evaluate a string in GAP.
239
240
INPUT:
241
242
- ``gap_string`` -- string. A valid statement in GAP.
243
244
OUTPUT:
245
246
The resulting GAP object or NULL+Python Exception in case of error.
247
248
EXAMPLES::
249
250
sage: libgap.eval('if 4>3 then\nPrint("hi");\nfi')
251
NULL
252
sage: libgap.eval('1+1') # testing that we have sucessfully recovered
253
2
254
255
sage: libgap.eval('if 4>3 thenPrint("hi");\nfi')
256
Traceback (most recent call last):
257
...
258
ValueError: libGAP: Syntax error: then expected
259
if 4>3 thenPrint("hi");
260
fi;
261
^
262
sage: libgap.eval('1+1') # testing that we have sucessfully recovered
263
2
264
"""
265
initialize()
266
cdef libGAP_ExecStatus status
267
268
cmd = gap_string + ';\n'
269
try:
270
libgap_enter()
271
libgap_start_interaction(cmd)
272
try:
273
sig_on()
274
status = libGAP_ReadEvalCommand(libGAP_BottomLVars)
275
if status != libGAP_STATUS_END:
276
libgap_call_error_handler()
277
sig_off()
278
except RuntimeError, msg:
279
raise ValueError('libGAP: '+str(msg).strip())
280
281
if libGAP_Symbol != libGAP_S_SEMICOLON:
282
raise ValueError('did not end with semicolon')
283
libGAP_GetSymbol()
284
if libGAP_Symbol != libGAP_S_EOF:
285
raise ValueError('can only evaluate a single statement')
286
287
finally:
288
libgap_finish_interaction()
289
libgap_exit()
290
291
if libGAP_ReadEvalResult != NULL:
292
libgap_enter()
293
libGAP_AssGVar(libGAP_Last3, libGAP_VAL_GVAR(libGAP_Last2))
294
libGAP_AssGVar(libGAP_Last2, libGAP_VAL_GVAR(libGAP_Last))
295
libGAP_AssGVar(libGAP_Last, libGAP_ReadEvalResult)
296
libgap_exit()
297
298
return libGAP_ReadEvalResult # may be NULL, thats ok
299
300
301
############################################################################
302
### Helper to protect temporary objects from deletion ######################
303
############################################################################
304
305
cdef void hold_reference(libGAP_Obj obj):
306
"""
307
Hold a reference (inside the GAP kernel) to obj
308
309
This ensures that the GAP garbage collector does not delete
310
``obj``. This works by assigning it to a global variable. This is
311
very simple, but you can't use it to keep two objects alive. Be
312
careful.
313
"""
314
libgap_enter()
315
global reference_holder
316
libGAP_AssGVar(reference_holder, obj)
317
libgap_exit()
318
319
320
############################################################################
321
### Error handler ##########################################################
322
############################################################################
323
324
include 'sage/ext/interrupt.pxi'
325
from cpython.exc cimport PyErr_SetObject
326
327
cdef void error_handler(char* msg):
328
"""
329
The libgap error handler
330
331
We call ``sig_error()`` which causes us to jump back to the Sage
332
signal handler. Since we wrap libGAP C calls in ``sig_on`` /
333
``sig_off`` blocks, this then jumps back to the ``sig_on`` where
334
the ``RuntimeError`` we raise here will be seen.
335
"""
336
msg_py = msg
337
msg_py = msg_py.replace('For debugging hints type ?Recovery from NoMethodFound\n', '')
338
PyErr_SetObject(RuntimeError, msg_py)
339
sig_error()
340
341
342
############################################################################
343
### Debug functions ########################################################
344
############################################################################
345
346
cdef inline void DEBUG_CHECK(libGAP_Obj obj):
347
"""
348
Check that ``obj`` is valid.
349
350
This function is only useful for debugging.
351
"""
352
libgap_enter()
353
libGAP_CheckMasterPointers()
354
libgap_exit()
355
if obj == NULL:
356
print 'DEBUG_CHECK: Null pointer!'
357
358
359
360
361
cpdef memory_usage():
362
"""
363
Return information about the memory useage.
364
365
See :meth:`~sage.libs.gap.libgap.Gap.mem` for details.
366
"""
367
cdef size_t SizeMptrsArea = libGAP_OldBags - libGAP_MptrBags
368
cdef size_t SizeOldBagsArea = libGAP_YoungBags - libGAP_OldBags
369
cdef size_t SizeYoungBagsArea = libGAP_AllocBags - libGAP_YoungBags
370
cdef size_t SizeAllocationArea = libGAP_StopBags - libGAP_AllocBags
371
cdef size_t SizeUnavailableArea = libGAP_EndBags - libGAP_StopBags
372
return (SizeMptrsArea, SizeOldBagsArea, SizeYoungBagsArea, SizeAllocationArea, SizeUnavailableArea)
373
374
375
cpdef error_enter_libgap_block_twice():
376
"""
377
Demonstrate that we catch errors from entering a block twice.
378
379
EXAMPLES::
380
381
sage: from sage.libs.gap.util import error_enter_libgap_block_twice
382
sage: error_enter_libgap_block_twice()
383
Traceback (most recent call last):
384
...
385
RuntimeError: Entered a critical block twice
386
"""
387
from sage.libs.gap.libgap import libgap
388
try:
389
# The exception will be seen by this sig_on() after being
390
# raised by the second libgap_enter().
391
sig_on()
392
libgap_enter()
393
libgap_enter()
394
sig_off()
395
finally:
396
libgap_exit()
397
398
399
cpdef error_exit_libgap_block_without_enter():
400
"""
401
Demonstrate that we catch errors from omitting libgap_enter.
402
403
EXAMPLES::
404
405
sage: from sage.libs.gap.util import error_exit_libgap_block_without_enter
406
sage: error_exit_libgap_block_without_enter()
407
Traceback (most recent call last):
408
...
409
RuntimeError: Called libgap_exit without previous libgap_enter
410
"""
411
from sage.libs.gap.libgap import libgap
412
sig_on()
413
libgap_exit()
414
sig_off()
415
416
############################################################################
417
### Auxilliary functions ###################################################
418
############################################################################
419
420
421
def command(command_string):
422
"""
423
Playground for accessing Gap via libGap.
424
425
You should not use this function in your own programs. This is
426
just here for convenience if you want to play with the libgap
427
libray code.
428
429
EXAMPLES::
430
431
sage: from sage.libs.gap.util import command
432
sage: command('1')
433
Output follows...
434
1
435
436
sage: command('1/0')
437
Traceback (most recent call last):
438
...
439
ValueError: libGAP: Error, Rational operations: <divisor> must not be zero
440
441
sage: command('NormalSubgroups')
442
Output follows...
443
<Attribute "NormalSubgroups">
444
445
sage: command('rec(a:=1, b:=2)')
446
Output follows...
447
rec( a := 1, b := 2 )
448
"""
449
initialize()
450
cdef libGAP_ExecStatus status
451
452
cmd = command_string + ';\n'
453
try:
454
libgap_enter()
455
libgap_start_interaction(cmd)
456
try:
457
sig_on()
458
status = libGAP_ReadEvalCommand(libGAP_BottomLVars)
459
if status != libGAP_STATUS_END:
460
libgap_call_error_handler()
461
sig_off()
462
except RuntimeError, msg:
463
raise ValueError('libGAP: '+str(msg).strip())
464
465
assert libGAP_Symbol == libGAP_S_SEMICOLON, 'Did not end with semicolon?'
466
libGAP_GetSymbol()
467
if libGAP_Symbol != libGAP_S_EOF:
468
raise ValueError('command() expects a single statement.')
469
470
if libGAP_ReadEvalResult:
471
libGAP_ViewObjHandler(libGAP_ReadEvalResult)
472
s = libgap_get_output()
473
print 'Output follows...'
474
print s.strip()
475
else:
476
print 'No output.'
477
478
finally:
479
libgap_exit()
480
libgap_finish_interaction()
481
482
DEBUG_CHECK(libGAP_ReadEvalResult)
483
484
if libGAP_ReadEvalResult != NULL:
485
libgap_enter()
486
libGAP_AssGVar(libGAP_Last3, libGAP_VAL_GVAR(libGAP_Last2))
487
libGAP_AssGVar(libGAP_Last2, libGAP_VAL_GVAR(libGAP_Last))
488
libGAP_AssGVar(libGAP_Last, libGAP_ReadEvalResult)
489
libgap_exit()
490
491
492