Interface to SnapPy¶
SnapPy is an open source software for low-dimensional topology. From the home-page:
Note
SnapPy is a program for studying the topology and geometry of 3-manifolds, with a focus on hyperbolic structures. It runs on Mac OS X, Linux, and Windows, and combines a link editor and 3D-graphics for Dirichlet domains and cusp neighborhoods with a powerful command-line interface based on the Python programming language. You can see it in action, learn how to install it, and watch the tutorial.
The SnapPy interface will only work if the optional Sage package SnapPy is installed. The interface lets you send certain Sage objects to SnapPy, run SnapPy functions, import certain SnapPy expressions to Sage, or any combination of the above.
To send a Sage object sobj to SnapPy, call snappy(sobj).
This exports the Sage object to SnapPy and returns a new Sage object
wrapping the SnapPy expression/variable, so that you can use the
SnapPy variable from within Sage. You can then call SnapPy
functions on the new object; for example:
sage: A = AbelianGroup([5,15,0,0]); A
Multiplicative Abelian group isomorphic to C5 x C15 x Z x Z
sage: As = snappy(A); As
Z/5 + Z/15 + Z + Z
sage: As.order()
'infinite'
>>> from sage.all import *
>>> A = AbelianGroup([Integer(5),Integer(15),Integer(0),Integer(0)]); A
Multiplicative Abelian group isomorphic to C5 x C15 x Z x Z
>>> As = snappy(A); As
Z/5 + Z/15 + Z + Z
>>> As.order()
'infinite'
In the above example the order of the group is obtained using SnapPy’s
order method.
To see SnapPy’s output you can simply print the SnapPy wrapper
object. However if you want to import SnapPy’s output back to Sage,
call the SnapPy wrapper object’s sage() method. This method returns
a native Sage object:
sage: K = Knots().from_table(8, 21); K
Knot represented by 8 crossings
sage: Ks = snappy(K); Ks
<Link: 1 comp; 8 cross>
sage: Ks.goeritz_matrix()
[-2 1 0]
[ 1 -4 2]
[ 0 2 1]
sage: Ks.sage() == K
True
>>> from sage.all import *
>>> K = Knots().from_table(Integer(8), Integer(21)); K
Knot represented by 8 crossings
>>> Ks = snappy(K); Ks
<Link: 1 comp; 8 cross>
>>> Ks.goeritz_matrix()
[-2 1 0]
[ 1 -4 2]
[ 0 2 1]
>>> Ks.sage() == K
True
If you want to run a SnapPy function and don’t already have the input
in the form of a Sage object, then it might be simpler to input a string
expr to snappy(expr). This string will be evaluated as if you had
typed it into SnapPy:
sage: M1 = snappy("Manifold('m125')"); M1
m125(0,0)(0,0)
>>> from sage.all import *
>>> M1 = snappy("Manifold('m125')"); M1
m125(0,0)(0,0)
Alternatively, all constructors of SnapPy classes can be used directly as attributes of the interface:
sage: M2 = snappy.Manifold('m125'); M2
m125(0,0)(0,0)
sage: M1 == M2
True
>>> from sage.all import *
>>> M2 = snappy.Manifold('m125'); M2
m125(0,0)(0,0)
>>> M1 == M2
True
Finally, if you just want to use a SnapPy command line from within
Sage, the IPython magic function %snappy dumps you into an interactive
command-line SnapPy session. As long as you work in this environment the
prompt is snappy:. To finish the environment type CTRL+D:
sage: %snappy # not tested
--> Switching to SnapPy <--
snappy: M = Manifold('9_42')
None
snappy: M.volume()
4.05686022423682
snappy: M.cusp_info('shape')
[-4.27893631592295 + 1.95728679749950*I]
--> Exiting back to Sage <--
sage: # not tested
>>> from sage.all import *
>>> %snappy # not tested
--> Switching to SnapPy <--
snappy: M = Manifold('9_42')
None
snappy: M.volume()
4.05686022423682
snappy: M.cusp_info('shape')
[-4.27893631592295 + 1.95728679749950*I]
--> Exiting back to Sage <--
>>> # not tested
Complicated translations¶
The sobj.sage() method tries to convert a SnapPy object to a Sage
object. In many cases, it will just work. In particular, it should be able to
convert expressions entirely consisting of:
numbers, i.e. integers, floats, complex numbers;
functions and named constants also present in Sage, where:
Sage knows how to translate the function or constant’s name from SnapPy’s, or
the Sage name for the function or constant is trivially related to SnapPy’s;
symbolic variables whose names don’t pathologically overlap with objects already defined in Sage.
This method will not work when SnapPy’s output includes:
strings;
functions unknown to Sage;
SnapPy functions with different parameters/parameter order to the Sage equivalent.
AUTHORS:
Sebastian Oehms (2026): first version.
- class sage.interfaces.snappy.SnapPy(high_precision=False)[source]¶
Bases:
PythonInternalInterfaceInterface to the SnapPy interpreter.
EXAMPLES:
sage: K = Knots().from_table(8, 21) sage: Ks = snappyhp(K); Ks <Link: 1 comp; 8 cross> sage: M = Ks.exterior() sage: success, rho = M.verify_hyperbolicity(); success True
>>> from sage.all import * >>> K = Knots().from_table(Integer(8), Integer(21)) >>> Ks = snappyhp(K); Ks <Link: 1 comp; 8 cross> >>> M = Ks.exterior() >>> success, rho = M.verify_hyperbolicity(); success True
More examples can be found in the module header.
- class sage.interfaces.snappy.SnapPyElement(parent, value, is_name=False, name=None)[source]¶
Bases:
PythonInternalElementElement class of the SnapPy interface.
Its instances are usually constructed via the instance call of its parent. It wrapes the SnapPy library for this object. In a session SnapPy methods can be obtained using tab completion.
EXAMPLES:
sage: K = Knots().from_table(8, 21) sage: Ks = snappy(K) sage: Ms = Ks.exterior(); Ms unnamed link(0,0) sage: type(Ms) <class 'sage.interfaces.snappy.SnapPyElement'> sage: Gs = Ms.fundamental_group(); Gs Generators: a,b Relators: aabABBAbABabbaBabbaBAbABBAb sage: type(Gs) <class 'sage.interfaces.snappy.SnapPyElement'> sage: G = Gs.sage(); G Finitely presented group < a, b | a^2*b*a^-1*b^-2*a^-1*b*a^-1*(b^-1*a*b^2*a)^2*b^-1*a^-1*b*a^-1*b^-2*a^-1*b > sage: Fs = Ks.faces(); Fs [[<CS 7, 3>, <CS 6, 3>, <CS 5, 1>, <CS 0, 0>], [<CS 7, 2>, <CS 0, 1>, <CS 1, 1>, <CS 2, 1>, <CS 3, 0>], [<CS 7, 1>, <CS 3, 1>, <CS 6, 1>], [<CS 7, 0>, <CS 6, 2>], [<CS 6, 0>, <CS 3, 2>, <CS 4, 0>, <CS 5, 0>], [<CS 5, 3>, <CS 4, 1>], [<CS 5, 2>, <CS 4, 2>, <CS 2, 3>, <CS 1, 3>, <CS 0, 3>], [<CS 4, 3>, <CS 3, 3>, <CS 2, 2>], [<CS 2, 0>, <CS 1, 2>], [<CS 1, 0>, <CS 0, 2>]] sage: type(Fs) <class 'sage.interfaces.snappy.SnapPyElement'> sage: F = Fs.sage(); F [[<CS 7, 3>, <CS 6, 3>, <CS 5, 1>, <CS 0, 0>], [<CS 7, 2>, <CS 0, 1>, <CS 1, 1>, <CS 2, 1>, <CS 3, 0>], [<CS 7, 1>, <CS 3, 1>, <CS 6, 1>], [<CS 7, 0>, <CS 6, 2>], [<CS 6, 0>, <CS 3, 2>, <CS 4, 0>, <CS 5, 0>], [<CS 5, 3>, <CS 4, 1>], [<CS 5, 2>, <CS 4, 2>, <CS 2, 3>, <CS 1, 3>, <CS 0, 3>], [<CS 4, 3>, <CS 3, 3>, <CS 2, 2>], [<CS 2, 0>, <CS 1, 2>], [<CS 1, 0>, <CS 0, 2>]] sage: Fs00 = Fs[0][0]; Fs00 <CS 7, 3> sage: type(Fs00) <class 'sage.interfaces.snappy.SnapPyElement'> sage: F00 = Fs00.sage() sage: type(F00) <class 'spherogram.links.links_base.CrossingStrand'> sage: Fs.sage()[0][0] == F00 True
>>> from sage.all import * >>> K = Knots().from_table(Integer(8), Integer(21)) >>> Ks = snappy(K) >>> Ms = Ks.exterior(); Ms unnamed link(0,0) >>> type(Ms) <class 'sage.interfaces.snappy.SnapPyElement'> >>> Gs = Ms.fundamental_group(); Gs Generators: a,b Relators: aabABBAbABabbaBabbaBAbABBAb >>> type(Gs) <class 'sage.interfaces.snappy.SnapPyElement'> >>> G = Gs.sage(); G Finitely presented group < a, b | a^2*b*a^-1*b^-2*a^-1*b*a^-1*(b^-1*a*b^2*a)^2*b^-1*a^-1*b*a^-1*b^-2*a^-1*b > >>> Fs = Ks.faces(); Fs [[<CS 7, 3>, <CS 6, 3>, <CS 5, 1>, <CS 0, 0>], [<CS 7, 2>, <CS 0, 1>, <CS 1, 1>, <CS 2, 1>, <CS 3, 0>], [<CS 7, 1>, <CS 3, 1>, <CS 6, 1>], [<CS 7, 0>, <CS 6, 2>], [<CS 6, 0>, <CS 3, 2>, <CS 4, 0>, <CS 5, 0>], [<CS 5, 3>, <CS 4, 1>], [<CS 5, 2>, <CS 4, 2>, <CS 2, 3>, <CS 1, 3>, <CS 0, 3>], [<CS 4, 3>, <CS 3, 3>, <CS 2, 2>], [<CS 2, 0>, <CS 1, 2>], [<CS 1, 0>, <CS 0, 2>]] >>> type(Fs) <class 'sage.interfaces.snappy.SnapPyElement'> >>> F = Fs.sage(); F [[<CS 7, 3>, <CS 6, 3>, <CS 5, 1>, <CS 0, 0>], [<CS 7, 2>, <CS 0, 1>, <CS 1, 1>, <CS 2, 1>, <CS 3, 0>], [<CS 7, 1>, <CS 3, 1>, <CS 6, 1>], [<CS 7, 0>, <CS 6, 2>], [<CS 6, 0>, <CS 3, 2>, <CS 4, 0>, <CS 5, 0>], [<CS 5, 3>, <CS 4, 1>], [<CS 5, 2>, <CS 4, 2>, <CS 2, 3>, <CS 1, 3>, <CS 0, 3>], [<CS 4, 3>, <CS 3, 3>, <CS 2, 2>], [<CS 2, 0>, <CS 1, 2>], [<CS 1, 0>, <CS 0, 2>]] >>> Fs00 = Fs[Integer(0)][Integer(0)]; Fs00 <CS 7, 3> >>> type(Fs00) <class 'sage.interfaces.snappy.SnapPyElement'> >>> F00 = Fs00.sage() >>> type(F00) <class 'spherogram.links.links_base.CrossingStrand'> >>> Fs.sage()[Integer(0)][Integer(0)] == F00 True