Interface to GAP#

Sage provides an interface to the GAP system. This system provides extensive group theory, combinatorics, etc.

The GAP interface will only work if GAP is installed on your computer; this should be the case, since GAP is included with Sage. The interface offers three pieces of functionality:

  1. gap_console() – A function that dumps you into an interactive command-line GAP session.

  2. gap(expr) – Evaluation of arbitrary GAP expressions, with the result returned as a string.

  3. gap.new(expr) – Creation of a Sage object that wraps a GAP object. This provides a Pythonic interface to GAP. For example, if f=gap.new(10), then f.Factors() returns the prime factorization of \(10\) computed using GAP.

First Examples#

We factor an integer using GAP:

sage: n = gap(20062006); n
20062006
sage: n.parent()
Gap
sage: fac = n.Factors(); fac
[ 2, 17, 59, 73, 137 ]
sage: fac.parent()
Gap
sage: fac[1]
2
>>> from sage.all import *
>>> n = gap(Integer(20062006)); n
20062006
>>> n.parent()
Gap
>>> fac = n.Factors(); fac
[ 2, 17, 59, 73, 137 ]
>>> fac.parent()
Gap
>>> fac[Integer(1)]
2

GAP and Singular#

This example illustrates conversion between Singular and GAP via Sage as an intermediate step. First we create and factor a Singular polynomial.

sage: singular(389)
389
sage: R1 = singular.ring(0, '(x,y)', 'dp')
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')
sage: F = f.factorize()
sage: print(F)
[1]:
   _[1]=9
   _[2]=x^6-2*x^3*y^2-x^2*y^3+y^4
   _[3]=-x^5+y^2
[2]:
   1,1,2
>>> from sage.all import *
>>> singular(Integer(389))
389
>>> R1 = singular.ring(Integer(0), '(x,y)', 'dp')
>>> 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')
>>> F = f.factorize()
>>> print(F)
[1]:
   _[1]=9
   _[2]=x^6-2*x^3*y^2-x^2*y^3+y^4
   _[3]=-x^5+y^2
[2]:
   1,1,2

Next we convert the factor \(-x^5+y^2\) to a Sage multivariate polynomial. Note that it is important to let \(x\) and \(y\) be the generators of a polynomial ring, so the eval command works.

sage: R.<x,y> = PolynomialRing(QQ,2)
sage: s = F[1][3].sage_polystring(); s
'-x**5+y**2'
sage: g = eval(s); g
-x^5 + y^2
>>> from sage.all import *
>>> R = PolynomialRing(QQ,Integer(2), names=('x', 'y',)); (x, y,) = R._first_ngens(2)
>>> s = F[Integer(1)][Integer(3)].sage_polystring(); s
'-x**5+y**2'
>>> g = eval(s); g
-x^5 + y^2

Next we create a polynomial ring in GAP and obtain its indeterminates:

sage: R = gap.PolynomialRing('Rationals', 2); R
PolynomialRing( Rationals, ["x_1", "x_2"] )
sage: I = R.IndeterminatesOfPolynomialRing(); I
[ x_1, x_2 ]
>>> from sage.all import *
>>> R = gap.PolynomialRing('Rationals', Integer(2)); R
PolynomialRing( Rationals, ["x_1", "x_2"] )
>>> I = R.IndeterminatesOfPolynomialRing(); I
[ x_1, x_2 ]

In order to eval \(g\) in GAP, we need to tell GAP to view the variables x0 and x1 as the two generators of \(R\). This is the one tricky part. In the GAP interpreter the object I has its own name (which isn’t I). We can access its name using I.name().

sage: _ = gap.eval("x := %s[1];; y := %s[2];;"%(I.name(), I.name()))
>>> from sage.all import *
>>> _ = gap.eval("x := %s[1];; y := %s[2];;"%(I.name(), I.name()))

Now \(x_0\) and \(x_1\) are defined, so we can construct the GAP polynomial \(f\) corresponding to \(g\):

sage: R.<x,y> = PolynomialRing(QQ,2)
sage: f = gap(str(g)); f
-x_1^5+x_2^2
>>> from sage.all import *
>>> R = PolynomialRing(QQ,Integer(2), names=('x', 'y',)); (x, y,) = R._first_ngens(2)
>>> f = gap(str(g)); f
-x_1^5+x_2^2

We can call GAP functions on \(f\). For example, we evaluate the GAP Value function, which evaluates \(f\) at the point \((1,2)\).

sage: f.Value(I, [1,2])
3
sage: g(1,2)        # agrees
3
>>> from sage.all import *
>>> f.Value(I, [Integer(1),Integer(2)])
3
>>> g(Integer(1),Integer(2))        # agrees
3

Saving and loading objects#

Saving and loading GAP objects (using the dumps method, etc.) is not supported, since the output string representation of Gap objects is sometimes not valid input to GAP. Creating classes that wrap GAP objects is supported, via simply defining the a _gap_init_ member function that returns a string that when evaluated in GAP constructs the object. See groups/perm_gps/permgroup.py for a nontrivial example of this.

Long Input#

The GAP interface reads in even very long input (using files) in a robust manner, as long as you are creating a new object.

Note

Using gap.eval for long input is much less robust, and is not recommended.

sage: t = '"%s"'%10^10000   # ten thousand character string.
sage: a = gap(t)
>>> from sage.all import *
>>> t = '"%s"'%Integer(10)**Integer(10000)   # ten thousand character string.
>>> a = gap(t)

Changing which GAP is used, and how#

Set the environment variable SAGE_GAP_COMMAND to specify how GAP executable is called. E.g.

$ SAGE_GAP_COMMAND = "/usr/local/bin/gap -s 4G" ./sage

will use GAP installed in \(/usr/local/bin\), with 4Gb RAM.

Set the environment variable SAGE_GAP_MEMORY to specify the amount of RAM allocated to libgap and to the GAP executable. If SAGE_GAP_COMMAND is set, as well, then SAGE_GAP_MEMORY is only used for \(libgap\).

sage: gap.eval('GAPInfo.CommandLineOptions.s') # not tested
'"42m"'
>>> from sage.all import *
>>> gap.eval('GAPInfo.CommandLineOptions.s') # not tested
'"42m"'

After the GAP interface initialisation, setting SAGE_GAP_MEMORY has no effect:

sage: os.environ['SAGE_GAP_MEMORY'] = '24M'
sage: gap.eval('GAPInfo.CommandLineOptions.s') # not tested
'"4g"'
>>> from sage.all import *
>>> os.environ['SAGE_GAP_MEMORY'] = '24M'
>>> gap.eval('GAPInfo.CommandLineOptions.s') # not tested
'"4g"'

AUTHORS:

  • David Joyner and William Stein: initial version(s)

  • William Stein (2006-02-01): modified gap_console command so it uses exactly the same startup command as Gap.__init__.

  • William Stein (2006-03-02): added tab completions: gap.[tab], x = gap(…), x.[tab], and docs, e.g., gap.function? and x.function?

class sage.interfaces.gap.Gap(max_workspace_size=None, maxread=None, script_subdirectory=None, use_workspace_cache=True, server=None, server_tmpdir=None, logfile=None, seed=None, env={})[source]#

Bases: Gap_generic

Interface to the GAP interpreter.

AUTHORS:

  • William Stein and David Joyner

console()[source]#

Spawn a new GAP command-line session.

EXAMPLES:

sage: gap.console()  # not tested
*********   GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
*  GAP  *   https://www.gap-system.org
*********   Architecture: x86_64-unknown-linux-gnu-gcc-default64
Libs used:  gmp, readline
Loading the library and packages ...
Packages:   GAPDoc 1.5.1
Try '?help' for help. See also  '?copyright' and  '?authors'
gap>
>>> from sage.all import *
>>> gap.console()  # not tested
*********   GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
*  GAP  *   https://www.gap-system.org
*********   Architecture: x86_64-unknown-linux-gnu-gcc-default64
Libs used:  gmp, readline
Loading the library and packages ...
Packages:   GAPDoc 1.5.1
Try '?help' for help. See also  '?copyright' and  '?authors'
gap>
cputime(t=None)[source]#

Returns the amount of CPU time that the GAP session has used. If t is not None, then it returns the difference between the current CPU time and t.

EXAMPLES:

sage: t = gap.cputime()
sage: t  #random
0.13600000000000001
sage: gap.Order(gap.SymmetricGroup(5))
120
sage: gap.cputime(t)  #random
0.059999999999999998
>>> from sage.all import *
>>> t = gap.cputime()
>>> t  #random
0.13600000000000001
>>> gap.Order(gap.SymmetricGroup(Integer(5)))
120
>>> gap.cputime(t)  #random
0.059999999999999998
get(var, use_file=False)[source]#

Get the string representation of the variable var.

EXAMPLES:

sage: gap.set('x', '2')
sage: gap.get('x')
'2'
>>> from sage.all import *
>>> gap.set('x', '2')
>>> gap.get('x')
'2'
help(s, pager=True)[source]#

Print help on a given topic.

EXAMPLES:

Note: In order to ensure consistent unicode handling from GAP we start a GAP instance with a forced UTF-8 locale:

sage: gap = Gap(env={'LC_CTYPE': 'en_US.UTF-8'})
sage: print(gap.help('SymmetricGroup', pager=False))

  50.1-... SymmetricGroup

  ‣ SymmetricGroup( [filt, ]deg ) ─────────────────────────────────── function
...
>>> from sage.all import *
>>> gap = Gap(env={'LC_CTYPE': 'en_US.UTF-8'})
>>> print(gap.help('SymmetricGroup', pager=False))
<BLANKLINE>
  50.1-... SymmetricGroup
<BLANKLINE>
  ‣ SymmetricGroup( [filt, ]deg ) ─────────────────────────────────── function
...
<BLANKLINE>
save_workspace()[source]#

Save the GAP workspace.

set(var, value)[source]#

Set the variable var to the given value.

EXAMPLES:

sage: gap.set('x', '2')
sage: gap.get('x')
'2'
>>> from sage.all import *
>>> gap.set('x', '2')
>>> gap.get('x')
'2'
set_seed(seed=None)[source]#

Set the seed for gap interpreter.

The seed should be an integer.

EXAMPLES:

sage: g = Gap()
sage: g.set_seed(0)
0
sage: [g.Random(1,10) for i in range(5)]
[2, 3, 3, 4, 2]
>>> from sage.all import *
>>> g = Gap()
>>> g.set_seed(Integer(0))
0
>>> [g.Random(Integer(1),Integer(10)) for i in range(Integer(5))]
[2, 3, 3, 4, 2]
class sage.interfaces.gap.GapElement(parent, value, is_name=False, name=None)[source]#

Bases: GapElement_generic, GapElement

str(use_file=False)[source]#

EXAMPLES:

sage: print(gap(2))
2
>>> from sage.all import *
>>> print(gap(Integer(2)))
2
class sage.interfaces.gap.GapElement_generic(parent, value, is_name=False, name=None)[source]#

Bases: ModuleElement, ExtraTabCompletion, ExpectElement

Generic interface to the GAP3/GAP4 interpreters.

AUTHORS:

  • William Stein and David Joyner (interface for GAP4)

  • Franco Saliola (Feb 2010): refactored to separate out the generic code

is_string()[source]#

Tell whether this element is a string.

EXAMPLES:

sage: gap('"abc"').is_string()
True
sage: gap('[1,2,3]').is_string()
False
>>> from sage.all import *
>>> gap('"abc"').is_string()
True
>>> gap('[1,2,3]').is_string()
False
class sage.interfaces.gap.GapFunction(parent, name)[source]#

Bases: ExpectFunction

class sage.interfaces.gap.GapFunctionElement(obj, name)[source]#

Bases: FunctionElement

class sage.interfaces.gap.Gap_generic(name, prompt, command=None, env={}, server=None, server_tmpdir=None, ulimit=None, maxread=None, script_subdirectory=None, restart_on_ctrlc=False, verbose_start=False, init_code=[], max_startup_time=None, logfile=None, eval_using_file_cutoff=0, do_cleaner=True, remote_cleaner=False, path=None, terminal_echo=True)[source]#

Bases: ExtraTabCompletion, Expect

Generic interface to the GAP3/GAP4 interpreters.

AUTHORS:

  • William Stein and David Joyner (interface for GAP4)

  • Franco Saliola (Feb 2010): refactored to separate out the generic code

eval(x, newlines=False, strip=True, split_lines=True, **kwds)[source]#

Send the code in the string s to the GAP interpreter and return the output as a string.

INPUT:

  • s – string containing GAP code.

  • newlines – bool (default: True); if False, remove all backslash-newlines inserted by the GAP output formatter.

  • strip – ignored

  • split_lines – bool (default: True); if True then each line is evaluated separately. If False, then the whole block of code is evaluated all at once.

EXAMPLES:

sage: gap.eval('2+2')
'4'
sage: gap.eval('Print(4); #test\n Print(6);')
'46'
sage: gap.eval('Print("#"); Print(6);')
'#6'
sage: gap.eval('4; \n 6;')
'4\n6'
sage: gap.eval('if 3>2 then\nPrint("hi");\nfi;')
'hi'
sage: gap.eval('## this is a test\nPrint("OK")')
'OK'
sage: gap.eval('Print("This is a test. Oh no, a #");# but this is a comment\nPrint("OK")')
'This is a test. Oh no, a #OK'
sage: gap.eval('if 4>3 then')
''
sage: gap.eval('Print("Hi how are you?")')
'Hi how are you?'
sage: gap.eval('fi')
''
>>> from sage.all import *
>>> gap.eval('2+2')
'4'
>>> gap.eval('Print(4); #test\n Print(6);')
'46'
>>> gap.eval('Print("#"); Print(6);')
'#6'
>>> gap.eval('4; \n 6;')
'4\n6'
>>> gap.eval('if 3>2 then\nPrint("hi");\nfi;')
'hi'
>>> gap.eval('## this is a test\nPrint("OK")')
'OK'
>>> gap.eval('Print("This is a test. Oh no, a #");# but this is a comment\nPrint("OK")')
'This is a test. Oh no, a #OK'
>>> gap.eval('if 4>3 then')
''
>>> gap.eval('Print("Hi how are you?")')
'Hi how are you?'
>>> gap.eval('fi')
''
function_call(function, args=None, kwds=None)[source]#

Calls the GAP function with args and kwds.

EXAMPLES:

sage: gap.function_call('SymmetricGroup', [5])
SymmetricGroup( [ 1 .. 5 ] )
>>> from sage.all import *
>>> gap.function_call('SymmetricGroup', [Integer(5)])
SymmetricGroup( [ 1 .. 5 ] )

If the GAP function does not return a value, but prints something to the screen, then a string of the printed output is returned.

sage: s = gap.function_call('Display', [gap.SymmetricGroup(5).CharacterTable()])
sage: type(s)
<class 'sage.interfaces.interface.AsciiArtString'>
sage: s.startswith('CT')
True
>>> from sage.all import *
>>> s = gap.function_call('Display', [gap.SymmetricGroup(Integer(5)).CharacterTable()])
>>> type(s)
<class 'sage.interfaces.interface.AsciiArtString'>
>>> s.startswith('CT')
True
get_record_element(record, name)[source]#

Return the element of a GAP record identified by name.

INPUT:

  • record – a GAP record

  • name – str

OUTPUT:

EXAMPLES:

sage: rec = gap('rec( a := 1, b := "2" )')
sage: gap.get_record_element(rec, 'a')
1
sage: gap.get_record_element(rec, 'b')
2
>>> from sage.all import *
>>> rec = gap('rec( a := 1, b := "2" )')
>>> gap.get_record_element(rec, 'a')
1
>>> gap.get_record_element(rec, 'b')
2
interrupt(tries=None, timeout=1, quit_on_fail=True)[source]#

Interrupt the GAP process

Gap installs a SIGINT handler, we call it directly instead of trying to sent Ctrl-C. Unlike interrupt(), we only try once since we are knowing what we are doing.

Sometimes GAP dies while interrupting.

EXAMPLES:

sage: gap._eval_line('while(1=1) do i:=1;; od;', wait_for_prompt=False)
''
sage: rc = gap.interrupt(timeout=1)
sage: [ gap(i) for i in range(10) ]   # check that it is still working
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> from sage.all import *
>>> gap._eval_line('while(1=1) do i:=1;; od;', wait_for_prompt=False)
''
>>> rc = gap.interrupt(timeout=Integer(1))
>>> [ gap(i) for i in range(Integer(10)) ]   # check that it is still working
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
load_package(pkg, verbose=False)[source]#

Load the Gap package with the given name.

If loading fails, raise a RuntimeError exception.

unbind(var)[source]#

Clear the variable named var.

EXAMPLES:

sage: gap.set('x', '2')
sage: gap.get('x')
'2'
sage: gap.unbind('x')
sage: gap.get('x')
Traceback (most recent call last):
...
RuntimeError: Gap produced error output
Error, Variable: 'x' must have a value
...
>>> from sage.all import *
>>> gap.set('x', '2')
>>> gap.get('x')
'2'
>>> gap.unbind('x')
>>> gap.get('x')
Traceback (most recent call last):
...
RuntimeError: Gap produced error output
Error, Variable: 'x' must have a value
...
version()[source]#

Returns the version of GAP being used.

EXAMPLES:

sage: print(gap.version())
4...
>>> from sage.all import *
>>> print(gap.version())
4...
sage.interfaces.gap.gap_command(use_workspace_cache=True, local=True)[source]#
sage.interfaces.gap.gap_console()[source]#

Spawn a new GAP command-line session.

Note that in gap-4.5.7 you cannot use a workspace cache that had no commandline to restore a gap session with commandline.

EXAMPLES:

sage: gap_console()  # not tested
*********   GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
*  GAP  *   https://www.gap-system.org
*********   Architecture: x86_64-unknown-linux-gnu-gcc-default64
Libs used:  gmp, readline
Loading the library and packages ...
Packages:   GAPDoc 1.5.1
Try '?help' for help. See also  '?copyright' and  '?authors'
gap>
>>> from sage.all import *
>>> gap_console()  # not tested
*********   GAP, Version 4.5.7 of 14-Dec-2012 (free software, GPL)
*  GAP  *   https://www.gap-system.org
*********   Architecture: x86_64-unknown-linux-gnu-gcc-default64
Libs used:  gmp, readline
Loading the library and packages ...
Packages:   GAPDoc 1.5.1
Try '?help' for help. See also  '?copyright' and  '?authors'
gap>
sage.interfaces.gap.gap_reset_workspace(max_workspace_size=None, verbose=False)[source]#

Call this to completely reset the GAP workspace, which is used by default when Sage first starts GAP.

The first time you start GAP from Sage, it saves the startup state of GAP in a file $HOME/.sage/gap/workspace-gap-HASH, where HASH is a hash of the directory where Sage is installed. This is useful because the subsequent startup of GAP is at least ten times as fast. But if you update GAP or any of its packages, those changes won’t take effect until the workspace is reset.

sage.interfaces.gap.gfq_gap_to_sage(x, F)[source]#

INPUT:

  • x – GAP finite field element

  • F – Sage finite field

OUTPUT: element of F

EXAMPLES:

sage: x = gap('Z(13)')
sage: F = GF(13, 'a')
sage: F(x)
2
sage: F(gap('0*Z(13)'))
0
sage: F = GF(13^2, 'a')
sage: x = gap('Z(13)')
sage: F(x)
2
sage: x = gap('Z(13^2)^3')
sage: F(x)
12*a + 11
sage: F.multiplicative_generator()^3
12*a + 11
>>> from sage.all import *
>>> x = gap('Z(13)')
>>> F = GF(Integer(13), 'a')
>>> F(x)
2
>>> F(gap('0*Z(13)'))
0
>>> F = GF(Integer(13)**Integer(2), 'a')
>>> x = gap('Z(13)')
>>> F(x)
2
>>> x = gap('Z(13^2)^3')
>>> F(x)
12*a + 11
>>> F.multiplicative_generator()**Integer(3)
12*a + 11

AUTHOR:

  • David Joyner and William Stein

sage.interfaces.gap.intmod_gap_to_sage(x)[source]#

INPUT:

  • x – Gap integer mod ring element

EXAMPLES:

sage: a = gap(Mod(3, 18)); a
ZmodnZObj( 3, 18 )
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
sage: b.parent()
Ring of integers modulo 18

sage: a = gap(Mod(3, 17)); a
Z(17)
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
sage: b.parent()
Finite Field of size 17

sage: a = gap(Mod(0, 17)); a
0*Z(17)
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
0
sage: b.parent()
Finite Field of size 17

sage: a = gap(Mod(3, 65537)); a
ZmodpZObj( 3, 65537 )
sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
sage: b.parent()
Ring of integers modulo 65537
>>> from sage.all import *
>>> a = gap(Mod(Integer(3), Integer(18))); a
ZmodnZObj( 3, 18 )
>>> b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
>>> b.parent()
Ring of integers modulo 18

>>> a = gap(Mod(Integer(3), Integer(17))); a
Z(17)
>>> b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
>>> b.parent()
Finite Field of size 17

>>> a = gap(Mod(Integer(0), Integer(17))); a
0*Z(17)
>>> b = sage.interfaces.gap.intmod_gap_to_sage(a); b
0
>>> b.parent()
Finite Field of size 17

>>> a = gap(Mod(Integer(3), Integer(65537))); a
ZmodpZObj( 3, 65537 )
>>> b = sage.interfaces.gap.intmod_gap_to_sage(a); b
3
>>> b.parent()
Ring of integers modulo 65537
sage.interfaces.gap.is_GapElement(x)[source]#

Return True if x is a GapElement

This function is deprecated; use isinstance() (of sage.interfaces.abc.GapElement) instead.

EXAMPLES:

sage: from sage.interfaces.gap import is_GapElement
sage: is_GapElement(gap(2))
doctest:...: DeprecationWarning: the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead
See https://github.com/sagemath/sage/issues/34823 for details.
True
sage: is_GapElement(2)
False
>>> from sage.all import *
>>> from sage.interfaces.gap import is_GapElement
>>> is_GapElement(gap(Integer(2)))
doctest:...: DeprecationWarning: the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead
See https://github.com/sagemath/sage/issues/34823 for details.
True
>>> is_GapElement(Integer(2))
False
sage.interfaces.gap.reduce_load_GAP()[source]#

Returns the GAP interface object defined in sage.interfaces.gap.

EXAMPLES:

sage: from sage.interfaces.gap import reduce_load_GAP
sage: reduce_load_GAP()
Gap
>>> from sage.all import *
>>> from sage.interfaces.gap import reduce_load_GAP
>>> reduce_load_GAP()
Gap