libGAP shared library Interface to GAP¶
This module implements a fast C library interface to GAP. To use
libGAP you simply call libgap
(the parent of all
GapElement
instances) and use it to
convert Sage objects into GAP objects.
EXAMPLES:
sage: a = libgap(10)
sage: a
10
sage: type(a)
<type 'sage.libs.gap.element.GapElement_Integer'>
sage: a*a
100
sage: timeit('a*a') # random output
625 loops, best of 3: 898 ns per loop
Compared to the expect interface this is >1000 times faster:
sage: b = gap('10')
sage: timeit('b*b') # random output; long time
125 loops, best of 3: 2.05 ms per loop
If you want to evaluate GAP commands, use the Gap.eval()
method:
sage: libgap.eval('List([1..10], i->i^2)')
[ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ]
not to be confused with the libgap
call, which converts Sage
objects to GAP objects, for example strings to strings:
sage: libgap('List([1..10], i->i^2)')
"List([1..10], i->i^2)"
sage: type(_)
<type 'sage.libs.gap.element.GapElement_String'>
You can usually use the sage()
method to convert the resulting GAP element back to its Sage
equivalent:
sage: a.sage()
10
sage: type(_)
<type 'sage.rings.integer.Integer'>
sage: libgap.eval('5/3 + 7*E(3)').sage()
7*zeta3 + 5/3
sage: generators = libgap.AlternatingGroup(4).GeneratorsOfGroup().sage()
sage: generators # a Sage list of Sage permutations!
[(1,2,3), (2,3,4)]
sage: PermutationGroup(generators).cardinality() # computed in Sage
12
sage: libgap.AlternatingGroup(4).Size() # computed in GAP
12
So far, the following GAP data types can be directly converted to the corresponding Sage datatype:
- GAP booleans
true
/false
to Sage booleansTrue
/False
. The third GAP boolean valuefail
raises aValueError
. - GAP integers to Sage integers.
- GAP rational numbers to Sage rational numbers.
- GAP cyclotomic numbers to Sage cyclotomic numbers.
- GAP permutations to Sage permutations.
- The GAP containers
List
andrec
are converted to Sage containerslist
anddict
. Furthermore, thesage()
method is applied recursively to the entries.
Special support is available for the GAP container classes. GAP lists can be used as follows:
sage: lst = libgap([1,5,7]); lst
[ 1, 5, 7 ]
sage: type(lst)
<type 'sage.libs.gap.element.GapElement_List'>
sage: len(lst)
3
sage: lst[0]
1
sage: [ x^2 for x in lst ]
[1, 25, 49]
sage: type(_[0])
<type 'sage.libs.gap.element.GapElement_Integer'>
Note that you can access the elements of GAP List
objects as you
would expect from Python (with indexing starting at 0), but the
elements are still of type
GapElement
. The other GAP container
type are records, which are similar to Python dictionaries. You can
construct them directly from Python dictionaries:
sage: libgap({'a':123, 'b':456})
rec( a := 123, b := 456 )
Or get them as results of computations:
sage: rec = libgap.eval('rec(a:=123, b:=456, Sym3:=SymmetricGroup(3))')
sage: rec['Sym3']
Sym( [ 1 .. 3 ] )
sage: dict(rec)
{'Sym3': Sym( [ 1 .. 3 ] ), 'a': 123, 'b': 456}
The output is a Sage dictionary whose keys are Sage strings and whose
Values are instances of GapElement()
. So,
for example, rec['a']
is not a Sage integer. To recursively
convert the entries into Sage objects, you should use the
sage()
method:
sage: rec.sage()
{'Sym3': NotImplementedError('cannot construct equivalent Sage object',),
'a': 123,
'b': 456}
Now rec['a']
is a Sage integer. We have not implemented the
conversion of the GAP symmetric group to the Sage symmetric group yet,
so you end up with a NotImplementedError
exception object. The
exception is returned and not raised so that you can work with the
partial result.
While we don’t directly support matrices yet, you can convert them to
Gap List of Lists. These lists are then easily converted into Sage
using the recursive expansion of the
sage()
method:
sage: M = libgap.eval('BlockMatrix([[1,1,[[1, 2],[ 3, 4]]], [1,2,[[9,10],[11,12]]], [2,2,[[5, 6],[ 7, 8]]]],2,2)')
sage: M
<block matrix of dimensions (2*2)x(2*2)>
sage: M.List() # returns a GAP List of Lists
[ [ 1, 2, 9, 10 ], [ 3, 4, 11, 12 ], [ 0, 0, 5, 6 ], [ 0, 0, 7, 8 ] ]
sage: M.List().sage() # returns a Sage list of lists
[[1, 2, 9, 10], [3, 4, 11, 12], [0, 0, 5, 6], [0, 0, 7, 8]]
sage: matrix(ZZ, _)
[ 1 2 9 10]
[ 3 4 11 12]
[ 0 0 5 6]
[ 0 0 7 8]
Using the libGAP C library from Cython¶
The lower-case libgap_foobar
functions are ones that we added to
make the libGAP C shared library. The libGAP_foobar
methods are
the original GAP methods simply prefixed with the string
libGAP_
. The latter were originally not designed to be in a
library, so some care needs to be taken to call them.
In particular, you must call libgap_mark_stack_bottom()
in every
function that calls into the libGAP C functions. The reason is that
the GAP memory manager will automatically keep objects alive that are
referenced in local (stack-allocated) variables. While convenient,
this requires to look through the stack to find anything that looks
like an address to a memory bag. But this requires vigilance against
the following pattern:
cdef f()
libgap_mark_stack_bottom()
libGAP_function()
cdef g()
libgap_mark_stack_bottom();
f() # f() changed the stack bottom marker
libGAP_function() # boom
The solution is to re-order g()
to first call f()
. In order to
catch this error, it is recommended that you wrap calls into libGAP in
libgap_enter
/ libgap_exit
blocks and not call
libgap_mark_stack_bottom
manually. So instead, always write
- cdef f()
- libgap_enter() libGAP_function() libgap_exit()
- cdef g()
- f() libgap_enter() libGAP_function() libgap_exit()
If you accidentally call libgap_enter()
twice then an error
message is printed to help you debug this:
sage: from sage.libs.gap.util import error_enter_libgap_block_twice
sage: error_enter_libgap_block_twice()
Traceback (most recent call last):
...
RuntimeError: Entered a critical block twice
AUTHORS:
- William Stein, Robert Miller (2009-06-23): first version
- Volker Braun, Dmitrii Pasechnik, Ivan Andrus (2011-03-25, Sage Days 29): almost complete rewrite; first usable version.
- Volker Braun (2012-08-28, GAP/Singular workshop): update to gap-4.5.5, make it ready for public consumption.
-
class
sage.libs.gap.libgap.
Gap
¶ Bases:
sage.structure.parent.Parent
The libgap interpreter object.
Note
This object must be instantiated exactly once by the libgap. Always use the provided
libgap
instance, and never instantiateGap
manually.EXAMPLES:
sage: libgap.eval('SymmetricGroup(4)') Sym( [ 1 .. 4 ] )
TESTS:
sage: TestSuite(libgap).run(skip=['_test_category', '_test_elements', '_test_pickling'])
-
Element
¶ alias of
GapElement
-
collect
()¶ Manually run the garbage collector
EXAMPLES:
sage: a = libgap(123) sage: del a sage: libgap.collect()
-
count_GAP_objects
()¶ Return the number of GAP objects that are being tracked by libGAP
OUTPUT:
An integer
EXAMPLES:
sage: libgap.count_GAP_objects() # random output 5
-
eval
(gap_command)¶ Evaluate a gap command and wrap the result.
INPUT:
gap_command
– a string containing a valid gap command without the trailing semicolon.
OUTPUT:
A
GapElement
.EXAMPLES:
sage: libgap.eval('0') 0 sage: libgap.eval('"string"') "string"
-
function_factory
(function_name)¶ Return a GAP function wrapper
This is almost the same as calling
libgap.eval(function_name)
, but faster and makes it obvious in your code that you are wrapping a function.INPUT:
function_name
– string. The name of a GAP function.
OUTPUT:
A function wrapper
GapElement_Function
for the GAP function. Calling it from Sage is equivalent to calling the wrapped function from GAP.EXAMPLES:
sage: libgap.function_factory('Print') <Gap function "Print">
-
get_global
(variable)¶ Get a GAP global variable
INPUT:
variable
– string. The variable name.
OUTPUT:
A
GapElement
wrapping the GAP output. AValueError
is raised if there is no such variable in GAP.EXAMPLES:
sage: libgap.set_global('FooBar', 1) sage: libgap.get_global('FooBar') 1 sage: libgap.unset_global('FooBar') sage: libgap.get_global('FooBar') Traceback (most recent call last): ... ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar
-
global_context
(variable, value)¶ Temporarily change a global variable
INPUT:
variable
– string. The variable name.value
– anything that defines a GAP object.
OUTPUT:
A context manager that sets/reverts the given global variable.
EXAMPLES:
sage: libgap.set_global('FooBar', 1) sage: with libgap.global_context('FooBar', 2): ....: print(libgap.get_global('FooBar')) 2 sage: libgap.get_global('FooBar') 1
-
mem
()¶ Return information about libGAP memory usage
The GAP workspace is partitioned into 5 pieces (see gasman.c in the GAP sources for more details):
- The masterpointer area contains all the masterpointers of the bags.
- The old bags area contains the bodies of all the bags that survived at least one garbage collection. This area is only scanned for dead bags during a full garbage collection.
- The young bags area contains the bodies of all the bags that have been allocated since the last garbage collection. This area is scanned for dead bags during each garbage collection.
- The allocation area is the storage that is available for allocation of new bags. When a new bag is allocated the storage for the body is taken from the beginning of this area, and this area is correspondingly reduced. If the body does not fit in the allocation area a garbage collection is performed.
- The unavailable area is the free storage that is not available for allocation.
OUTPUT:
This function returns a tuple containing 5 integers. Each is the size (in bytes) of the five partitions of the workspace. This will potentially change after each GAP garbage collection.
EXAMPLES:
sage: libgap.collect() sage: libgap.mem() # random output (1048576, 6706782, 0, 960930, 0) sage: libgap.FreeGroup(3) <free group on the generators [ f1, f2, f3 ]> sage: libgap.mem() # random output (1048576, 6706782, 47571, 913359, 0) sage: libgap.collect() sage: libgap.mem() # random output (1048576, 6734785, 0, 998463, 0)
-
one
()¶ Return (integer) one in GAP.
EXAMPLES:
sage: libgap.one() 1 sage: parent(_) C library interface to GAP
-
set_global
(variable, value)¶ Set a GAP global variable
INPUT:
variable
– string. The variable name.value
– anything that defines a GAP object.
EXAMPLES:
sage: libgap.set_global('FooBar', 1) sage: libgap.get_global('FooBar') 1 sage: libgap.unset_global('FooBar') sage: libgap.get_global('FooBar') Traceback (most recent call last): ... ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar
-
show
()¶ Print statistics about the GAP owned object list
Slight complication is that we want to do it without accessing libgap objects, so we don’t create new GapElements as a side effect.
EXAMPLES:
sage: a = libgap(123) sage: b = libgap(456) sage: c = libgap(789) sage: del b sage: libgap.show() # random output 11 LibGAP elements currently alive rec( full := rec( cumulative := 122, deadbags := 9, deadkb := 0, freekb := 7785, livebags := 304915, livekb := 47367, time := 33, totalkb := 68608 ), nfull := 3, npartial := 14 )
-
unset_global
(variable)¶ Remove a GAP global variable
INPUT:
variable
– string. The variable name.
EXAMPLES:
sage: libgap.set_global('FooBar', 1) sage: libgap.get_global('FooBar') 1 sage: libgap.unset_global('FooBar') sage: libgap.get_global('FooBar') Traceback (most recent call last): ... ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar
-
zero
()¶ Return (integer) zero in GAP.
OUTPUT:
A
GapElement
.EXAMPLES:
sage: libgap.zero() 0
TESTS:
sage: libgap.zero_element() doctest:...: DeprecationWarning: zero_element is deprecated. Please use zero instead. See http://trac.sagemath.org/17694 for details. 0
-
zero_element
(*args, **kwds)¶ Deprecated: Use
zero()
instead. See trac ticket #17694 for details.
-