gappy — a Python interface to GAP¶
gappy provides a Python interface to the GAP computer algebra system by linking to its library interface.
It allows calling functions in GAP directly from Python, and passing supported Python objects back to GAP.
gappy is based on SageMath’s LibGAP interface to GAP, originally developed by Volker Braun, but is completely independent of Sage–it does not require or use Sage at all, and can be used in any Python code. If there is enough interest, it may also be enhanced with a complementary GAP package for interacting with Python from within GAP.
To start using GAP functions from Python, just run:
>>> from gappy import gap
Then any global variable in GAP, including functions, can be accessed as
>>> gap.Cite() Please use one of the following samples to cite GAP version from this installation Text: [GAP] GAP – Groups, Algorithms, and Programming, Version 4.dev, The GAP Group, https://www.gap-system.org. ...
All global variables that would be available in a GAP session can be accessed in this way:
>>> GAPInfo.Version "4.dev"
Most basic Python types have direct equivalents in GAP, and can be passed directly to GAP functions without explicit conversion to their equivalent GAP types:
>>> S4 = gap.SymmetricGroup(4) >>> S4 Sym( [ 1 .. 4 ] )
You can also call “methods” on
GapObjs. This is just syntactic sugar
for calling a GAP function with that object as its first argument, in cases
where that function supports the object bound to the method. For example:
>>> S4.GeneratorsOfGroup() [ (1,2,3,4), (1,2) ]
Values returned from GAP functions are GAP objects wrapped in a Python class
for containing them called
>>> type(S4) <class 'gappy.gapobj.GapObj'>
There are also specialized subclasses of
GapObj for many types of objects
in GAP. To explicitly convert a Python object directly to its GAP
equivalent, you can call
>>> one = gap(1) >>> type(one) <class 'gappy.gapobj.GapInteger'>
GAP objects are displayed (with
repr) or stringified (with
str) the same
way they would be in GAP, when displaying the object in the REPL or when
Print() function on the object, respectively:
>>> one 1 >>> s = gap("Hello GAP!") >>> s "Hello GAP!" >>> print(s) Hello GAP!
Not all GAP objects have an equivalent in basic Python types, so there is no implicit conversion from GAP back to Python. However, all Python types that can be converted to GAP objects can be converted back to their equivalent Python types in a symmetrical manner:
>>> int(one) 1 >>> type(int(one)) <class 'int'> >>> str(s) 'Hello GAP!' >>> type(str(s)) <class 'str'>
You can also call
obj.python() to convert to its equivalent Python type
if one exists:
>>> type(one.python()) <class 'int'>
To register your own converters for GAP objects to custom Python types, see
Finally, you can execute arbitrary GAP code directly with
This is often the easiest way to construct more complicated GAP objects,
especially if you are more familiar with GAP syntax. The return value of
gap.eval is the result of evaluating the same statement in GAP (the
semicolon is optional when evaluating a single statement):
>>> rec = gap.eval('rec(a:=123, b:=456, Sym3:=SymmetricGroup(3))') >>> rec['Sym3'] Sym( [ 1 .. 3 ] )
This is also an easy way to declare new GAP functions from gappy:
>>> sign = gap.eval("""sign := function(n) ... if n < 0 then ... return -1; ... elif n = 0 then ... return 0; ... else ... return 1; ... fi; ... end;""") >>> sign <GAP function "sign"> >>> sign(0) 0 >>> sign(-99) -1
See the full API documentation for many additional examples of how to use
gap object as well as the built-in
These instructions will be updated once there are releases on PyPI.
Supported platforms: Linux, MacOS, Cygwin.
Likely works with most other *BSD flavors but has not been tested.
Python 3.6 or up with development headers installed. On Debian-based systems this means:
$ sudo apt-get install python3.7-dev
GAP 4.10.2 or greater
It is possible to install from PyPI (note the distribution name gappy-system, do not install the package “gappy” which is an unrelated obsolete package):
$ pip install gappy-system
or from source:
$ git clone https://github.com/embray/gappy.git $ cd gappy/ $ pip install .
However, depending on how GAP is installed, some extra steps may be required. In particular, if you installed GAP from source using the typical instructions on the GAP website you will need to make sure the libgap shared library is built by running:
$ make install-libgap
in the GAP source directory.
You will also need to point to the location of your GAP installation by
GAP_ROOT environment variable like:
$ GAP_ROOT=<path/to/gap/root> pip install .
If you needed to provide
GAP_ROOT for the installation, it is also
generally necessary to set this environment variable before using gappy,
so that it can find the path to your GAP installation. See the
documentation for the
Gap class for more information.
If using GAP from a distribution system such as APT on Debian/Ubuntu or from
Conda, however, the GAP library (libgap) is typically installed in a
standard system location, and it may not be necessary to provide
GAP_ROOT. See the next section for example.
To give an example of the above point, you can install gappy in a Conda environment as follows:
$ conda create -n gap $ conda activate gap $ conda install -c conda-forge gap-defaults==4.11 python==3.8 $ pip install .
Alternatively, you can create the conda environment using the supplied environment.yml file:
$ conda env create
With Conda and other distributions that install libgap to a standard
system location (e.g.
/usr/lib/libgap.so) it may not be necessary to
GAP_ROOT environment variable, as the library can locate
your GAP root automatically in most cases.
The conda package for GAP 4.11 had dependency conflicts with Python 3.7 so you must use Python 3.8 or above, or GAP 4.10.2 with Python 3.7.
Additional notes for installation on Cygwin:
psutildoes not support Cygwin. However, there is an unofficial fork which does at: https://github.com/embray/psutil/tree/cygwin/v3. You can install it by running:
$ pip install git+https://github.com/embray/psutil.git@cygwin/v3
The path to the libgap DLL (filename
cyggap-0.dll) needs to be on your
PATHenvironment variable in order for gappy to be importable. To do this you can either copy it from your GAP installation to a standard location like:
$ cp /path/to/gap_root/.libs/cyggap-0.dll /usr/local/bin
or you can modify your environment to point to where GAP places the built DLL:
$ export PATH="/path/to/gap_root/.libs:$PATH"
and add this to your
Prevent needless line-wrapping of exception messages. This was inspired by similar code in GAP.jl.
Renamed the special method
_gap_, for converting arbitrary Python objects to GAP objects, to
__gap__as inspired by the discussion at https://trac.sagemath.org/ticket/31297#comment:23
Likewise, the special method
_gap_init_is now named
__gap_eval__to emphasize that it returns a string to be passed to
Gap.eval(). It still does not take any arguments.
GapObj.python()method for converting a
GapObjto its equivalent type if one exists (it does not always, but it does in the cases where there is an equivalent type built into Python).
GapRecord.python()also recursively convert the values they contain to equivalent Python types if possible.
New interface for registering converters to/from GAP object types:
Gap.register_converteris replaced with the
GapObj.convert_todecorator can be used to register new conversion methods on
GapObj, or specific subclasses thereof.
Added some C-level utility methods on
GapIntegerto help convert to different integer types (C long ints and mpz_t, depending on the size of the int). This helps with more efficient conversion to Sage Integers without having to pass through an intermediary Python
__neg__magic methods for
Implemented a default
Falseif its value is equal to zero.
.pyxsources so that Cython tracebacks can work better.
When converting a
dict(rec)the keys remain as
str. This is more consistent with the fact that the values are not converted to Python equivalents.
If an arbitrary GAP error occurs while looking up a global variable with
Gap.__getattr__it is handled and re-raised as an
Gap.__repr__method displays names of subclasses correctly.
Added LRU cache for functions defined with
gap.gap_functions, restoring some of the caching functionality from Sage’s
Fixed bug in multi-indexing of nested lists.
Fixed minor formatting difference in the IndexError message when indexing single lists versus multi-indexing nested lists.
Fixed a bug when using functions defined with
gap.gap_functionas arguments to another GAP function before they have been called once.