SageMath interface to the KnotInfo and LinkInfo databases¶

Introduction¶

Instructions for use¶

This tutorial comes as a Jupyter notebook (ipynb) that may have been exported to a pdf, html, md or a sage-file. The purpose of the html, md and pdf formats is for reading only. But, if you're using it in a Jupyter client (i.e. the ipynb-version) you may experiment with the examples by running the appropriate cells.

Working with the Jupyter notebook¶

Using your local device¶

If you have a SageMath distribution with a version of at least 9.4 then you can also run the cells of this tutorial on your own computer. To do this, enter

sage -n

in a bash shell located in a directory where you downloaded the sage_knotinfo_interface_tutorial.ipynb file (or rather the entire contents of the folder containing this file, so that local links work). This will open a new tab in your default browser showing the contents of that directory. Clicking on the tutorial file there will open another tab showing the file in a Jupyter client.

Note, that some of the examples require optional packages (as explained in the context of the examples). These examples will fail unless you install the corresponding package.

If you don't have a running Sage version of 9.4 (or newer) on your computer and are considering installing it, please see the installation instructions.

Using Notebook Player¶

You can run all cells that don't rely on optional packages in the Notebook Player. To do this first download the ipynb file. Then use the Browse-Button of the Notebook Player to select the file from your local device.

Using Docker¶

If you don't want to install Sage on your machine but have Docker on it, you can run all cells of this notebook (including those depending on optional packages) after entering

docker run -p8888:8888 -w /home/sage/tutorials soehms/sagemath_knots:latest sage-jupyter

and following the instructions shown in the shell to open it in your browser. For more information see the Docker repository.

Using Gitpod¶

Open this pinned and shared Gitpod workspace in your browser (this may take some minutes). Then click on the Open File menu and select /workspace/sage/tutorials/sage_knotinfo_interface_tutorial.ipynb. After the notebook has opened select the SageMath kernel in the right top corner of the sheet (see the screenshots on this page). As for the Docker image you may run all cells of the notebook, here.

You're welcome to make your own experiments there. However, be aware that you share this workspace with others. Thus, please make a copy of the original file for this (for example by saving it under a different name).

Working in the command-line¶

Using your local device¶

You can also copy-paste the contents of the cells into a Sage command line session if the Sage version is at least 9.4. If you just want to use the tutorial examples for your own experiments then you can import its variables by loading the corresponding sage file, that is

sage: load('https://raw.githubusercontent.com/soehms/database_knotinfo/main/tutorials/sage_knotinfo_interface_tutorial.sage')

If this fails with a FeatureNotPresentError error install the missing optional packages or use the reduced version of the file:

sage: load('https://raw.githubusercontent.com/soehms/database_knotinfo/main/tutorials/sage_knotinfo_interface_tutorial_reduced.sage')
Using SageMathCell¶

If you don't want to install Sage on your machine you can also run most of the examples in the SageMathCell. To pre-define the variables of this tutorial evaluate first

load('https://raw.githubusercontent.com/soehms/database_knotinfo/main/tutorials/sage_knotinfo_interface_tutorial_reduced.sage')
Using Docker¶

You can also use the above Docker image to run all examples in a Sage command line by typing

docker run -it soehms/sagemath_knots:latest

To have the variable declarations of the tutorial available load the sage_interface_knotinfo_tutorial.sage file as described above. For more information see the Docker repository.

Using Gitpod¶

The command line version can also be used in the terminal of the Gitpod workspace. This is similar as for the Docker case.

About¶

This tutorial is about a class from the SageMath library which is available since release 9.4. It implements an interface to the databases provided at the web-pages KnotInfo and LinkInfo which contain a classification of mathematically knots and links.

The tutorial follows a talk held at the LKS-Seminar, University of Regensburg, on 2021/03/18

Knots, links and braids in SageMath¶

Reference manual as pdf

Sage has a mostly native implementation of knots and links but also uses some third party software for this as libbraiding and libhomfly for the braid group class. Furthermore, many other interfaces are used indirectly, for example Gap (for the braid group, as well) but also interfaces for graph theory, polynomial rings or plotting.

Links¶

Construction¶

Links can be constructed using pd_code:

In [1]:
L3 = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]])
L3.plot()
Out[1]:

using braid_notation:¶

In [2]:
B = BraidGroup(4)
b = B([-1, -1, -1, -2, 1, -2, 3, -2])
Lb = Link(b)
Lb.plot()
Out[2]:

Links can also be constructed from oriented Gauss code.

Knots¶

Construction¶

Construction methods for links work for knots, too. For example using the pd_code:

In [3]:
K3 = Knot([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]])
K3 == L3
Out[3]:
False

As element of a different parent it differs from the corresponding link, even though they are isotopic:

In [4]:
sage: L3.is_isotopic(K3)
Out[4]:
True
In [5]:
K3.parent(), L3.parent()
Out[5]:
(Knots, <class 'sage.knots.link.Link'>)

Some special things only apply to knots:¶

In [6]:
unicode_art(K3)
Out[6]:
 ╭─╮ 
 │╭│╮
╭─│╯│
│╰╯ │
╰───╯
In [7]:
unicode_art(L3)
Out[7]:
Link with 1 component represented by 3 crossings

In addition to the construction methods for links, knots can be obtained using classical Gauss code or the Dowker-Thistlethwaite code.

If the input represents a proper link¶

In this case a ValueError is raised:

In [8]:
try:
    Knot(b)
except ValueError as err:
    print('Wrong input:', err)
Wrong input: the input has more than 1 connected component

Further examples¶

Obtaining polynomial invariants¶

  • HOMFLY-PT polynomial
  • Jones polynomial
  • colored Jones polynomial
  • Alexander polynomial
  • Conway polynomial
  • Links-Gould polynomial
  • Khovanov polynomial

For example:

In [9]:
h = L3.homfly_polynomial(); h
Out[9]:
-L^4 + L^2*M^2 - 2*L^2
In [10]:
h.parent()
Out[10]:
Multivariate Laurent Polynomial Ring in L, M over Integer Ring
In [11]:
type(h)
Out[11]:
<class 'sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair'>
In [12]:
h == K3.homfly_polynomial()
Out[12]:
True
In [13]:
h.variables()
Out[13]:
(M, L)
Other invariants¶
In [14]:
L3.links_gould_polynomial()
Out[14]:
1 - t1^-1 - t0^-1 + t1^-2 + 2*t0^-1*t1^-1 + t0^-2 - t0^-1*t1^-2 - t0^-2*t1^-1
In [15]:
L3.khovanov_polynomial()
Out[15]:
q^-1 + q^-3 + q^-5*t^-2 + q^-9*t^-3
In [16]:
L3.khovanov_polynomial(base_ring=GF(2))
Out[16]:
q^-1 + q^-3 + q^-5*t^-2 + q^-7*t^-2 + q^-9*t^-3
In [17]:
L3.khovanov_homology()
Out[17]:
{-9: {-3: Z},
 -7: {-3: 0, -2: C2},
 -5: {-3: 0, -2: Z, -1: 0, 0: 0},
 -3: {-3: 0, -2: 0, -1: 0, 0: Z},
 -1: {0: Z}}

Using braid and braid group functionality:¶

In [18]:
br = L3.braid(); br
Out[18]:
s^-3
In [19]:
brm = br.burau_matrix(); brm
Out[19]:
[           -t^-2 + t^-1         t^-2 - t^-1 + 1]
[     t^-3 - t^-2 + t^-1 -t^-3 + t^-2 - t^-1 + 1]
In [20]:
brm.parent()
Out[20]:
Full MatrixSpace of 2 by 2 dense matrices over Univariate Laurent Polynomial Ring in t over Integer Ring
In [21]:
B = br.parent(); B
Out[21]:
Braid group on 2 strands
In [22]:
mirr = B.mirror_involution(); mirr
Out[22]:
Group endomorphism of Braid group on 2 strands
In [23]:
mirr(br) == br.mirror_image()
Out[23]:
True

How to access knots and links in Sage by name?¶

Internally there is just a small list of Knots from the Rolfsen Table:

In [24]:
K10_165 = Knots().from_table(10, 165)
K10_165m = K10_165.mirror_image()
K10_165.is_isotopic(K10_165m)
Out[24]:
False

More links can be defined by name if the optional package SnapPy is installed:

In [25]:
import snappy
K10_166 = snappy.Link('10_166').sage_link()
K10_166.is_isotopic(K10_165)
Plink failed to import tkinter.
Out[25]:
True

Access to the KnotInfo and LinkInfo databases¶

This is possible since release 9.4.

Lets have a look at some of the examples:

Declaration using list selection (tab-completion)¶

The easiest way to define an interface instance for a special link or knot is to select it as an item of the KnotInfo class. This can be done by tab-completion. For example if you type KnotInfo.L6a4 and than hit the Tab-key you can select a link whose name starts with L6a4 from a list.

In [26]:
L6 = KnotInfo.L6a4_0_0; L6
Out[26]:
<KnotInfo.L6a4_0_0: 'L6a4{0,0}'>

The properties of the link can be obtained by the methods of the interface instance. Available methods can be viewed by tab-completion, too:

In [27]:
L6pd = L6.pd_notation(); L6pd
Out[27]:
[[6, 1, 7, 2],
 [12, 8, 9, 7],
 [4, 12, 1, 11],
 [10, 5, 11, 6],
 [8, 4, 5, 3],
 [2, 9, 3, 10]]

The default behavior of the methods of the KnotInfo class is to convert the string from the original table to a Sage or Python object:

In [28]:
type(L6pd)
Out[28]:
<class 'list'>

To obtain the original string from the table you have to use the keyword original:

In [29]:
L6pdo = L6.pd_notation(original=True); L6pdo
Out[29]:
'{{6, 1, 7, 2}, {12, 8, 9, 7}, {4, 12, 1, 11}, {10, 5, 11, 6}, {8, 4, 5, 3}, {2, 9, 3, 10}}'
In [30]:
type(L6pdo)
Out[30]:
<class 'str'>
In [31]:
L6.is_knot()
Out[31]:
False
In [32]:
L6.num_components()
Out[32]:
3

Knots need a prefix K¶

In [33]:
K4 = KnotInfo.K4_1
K4.is_amphicheiral()
Out[33]:
True

Declaring a link directly by its internal name¶

Using the inject method you may easily declare the interface instance by its name:

In [34]:
KnotInfo.K5_1.inject()
K5_1.dt_notation()
Defining K5_1
Out[34]:
[6, 8, 10, 2, 4]

Obtaining a link by its original name¶

This is possible by using the string of the original name as an argument of the class:

In [35]:
KnotInfo('L6a1{1}').inject()
L6a1_1.is_alternating()
Defining L6a1_1
Out[35]:
True

Obtaining Sage (and SnapPy) instances¶

In [36]:
L6br = L6.braid(); L6br
Out[36]:
(s0*s1^-1)^3
In [37]:
L6br.parent()
Out[37]:
Braid group on 3 strands
In [38]:
l6 = L6.link(); l6
Out[38]:
Link with 3 components represented by 6 crossings
In [39]:
l6.alexander_polynomial()
Out[39]:
t^-2 - 4*t^-1 + 6 - 4*t + t^2
In [40]:
l6s = L6.link(snappy=True); l6s
Out[40]:
<Link: 3 comp; 6 cross>

Conversion methods¶

KnotInfo and LinkInfo list more than 120 properties (in sum). Not all of them have already conversion methods to Sage. At the moment this holds for about a quarter of them including all polynomial invariants:

In [41]:
h6 = L6.homfly_polynomial(); h6
Out[41]:
-v^2*z^2 + z^4 + 2*z^2 + v^2*z^-2 - v^-2*z^2 - 2*z^-2 + v^-2*z^-2
In [42]:
h6.parent()
Out[42]:
Multivariate Laurent Polynomial Ring in v, z over Integer Ring
In [43]:
h6 == L6.link().homfly_polynomial(normalization='vz')
Out[43]:
True

Everyone is invited to extend the amount of conversions! But anyway, as a string all properties can be obtained, right now:

In [44]:
K4[K4.items.arc_index]
Out[44]:
'6'

Launching web pages¶

The following examples will launch the corresponding web-pages in your default browser:

In [45]:
L6.diagram()
L6.knot_atlas_webpage()
L6.items.jones_polynomial.description_webpage()
Out[45]:
True

The following example launches the web-pages of all diagrams of knots with less than 9 crossings and three genus equal to three:

In [46]:
listg3 = [K for K in KnotInfo if K.is_knot() and K.crossing_number() < 9 and K.three_genus() == 3]
len(listg3)
Out[46]:
10
In [47]:
all(K.diagram() for K in listg3)
Out[47]:
True

Now declare all of them:

In [48]:
any(K.inject() for K in listg3)
K7_1.is_alternating() == K8_10.is_alternating()
Defining K7_1
Defining K8_2
Defining K8_5
Defining K8_7
Defining K8_9
Defining K8_10
Defining K8_16
Defining K8_17
Defining K8_18
Defining K8_19
Out[48]:
True

From Sage to KnotInfo¶

If you want to identify a link defined in Sage with an isotopic link from the KnotInfo or LinkInfo databases you can use the get_knotinfo method:

In [49]:
L = Link([[3,1,2,4], [8,9,1,7], [5,6,7,3], [4,18,6,5],
          [17,19,8,18], [9,10,11,14], [10,12,13,11],
          [12,19,15,13], [20,16,14,15], [16,20,17,2]])
L.get_knotinfo()
Out[49]:
(<KnotInfo.K0_1: '0_1'>, None)
In [50]:
K10_165.get_knotinfo()
Out[50]:
(<KnotInfo.K10_165: '10_165'>, True)

KnotInfoSeries¶

To each interface instance there is a series of knots or links where the instance belongs to. Use the series method to declare it:

In [51]:
L6.series().inject()
list(L6a)
Defining L6a
Out[51]:
[Series of links L6a1,
 Series of links L6a2,
 Series of links L6a3,
 Series of links L6a4,
 Series of links L6a5]
In [52]:
L6a[0].inject()
Defining L6a1
In [53]:
list(L6a1)
Out[53]:
[<KnotInfo.L6a1_0: 'L6a1{0}'>, <KnotInfo.L6a1_1: 'L6a1{1}'>]

Another way to define a series is to use the constructor of the corresponding class directly:

In [54]:
KnotInfoSeries(10, True, True).inject()
for i in range(160, 166):
    K = K10(i)
    k = K.link(K.items.name, snappy=True)
    print(k, '--->', k.sage_link().get_knotinfo())
Defining K10
<Link 10_160: 1 comp; 10 cross> ---> (<KnotInfo.K10_160: '10_160'>, False)
<Link 10_161: 1 comp; 10 cross> ---> (<KnotInfo.K10_161: '10_161'>, True)
<Link 10_162: 1 comp; 10 cross> ---> (<KnotInfo.K10_161: '10_161'>, False)
<Link 10_163: 1 comp; 10 cross> ---> (<KnotInfo.K10_162: '10_162'>, False)
<Link 10_164: 1 comp; 10 cross> ---> (<KnotInfo.K10_163: '10_163'>, False)
<Link 10_165: 1 comp; 10 cross> ---> (<KnotInfo.K10_164: '10_164'>, False)

Note the differences to the naming of SnapPy concerning the Perko-Pair.

In [55]:
import snappy
snappy.Link('10_166').sage_link().get_knotinfo()
Out[55]:
(<KnotInfo.K10_165: '10_165'>, True)

What does # optional - snappy and # optional - database_knotinfo mean in the examples of the reference manual?¶

This is needed in the development proccess. After each commit which is pushed to the repository all examples are automatically tested by patchbots. The examples marked as optional prevent the patchbot to do that if the corresponding package is not installed.

Conversely this means that all examples which are not marked like this will be tested permanently on further development of Sage. They are perfomed on a subset of 20 links and about 20 properties hold statically in the Sage library for demonstration purpose. This prevents the interface to run out of compatibility.

How can you use KnotInfo in Sage?¶

The demonstration cases are shipped with the binaries of Sage since release 9.4. The complete Database can be installed typing

sage -i database_knotinfo

in a comand line. If you don't have Sage of a release 9.4 or newer please see the hints in the introduction section.

In [ ]: