You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
12 KiB
Plaintext
366 lines
12 KiB
Plaintext
#import "theme.typ": ams-article, definition, theorem, proposition, proof, todo
|
|
|
|
#import "@preview/algorithmic:0.1.0"
|
|
#import algorithmic: algorithm
|
|
|
|
#import "@preview/cetz:0.3.4"
|
|
|
|
#let kL = $L$
|
|
|
|
#let draw-strand = (polyline) => {
|
|
import cetz.draw: *
|
|
|
|
set-style(stroke: (paint: white, thickness: 5pt, cap: "butt"))
|
|
polyline
|
|
set-style(stroke: (paint: black, thickness: 0.75pt, cap: "round"))
|
|
polyline
|
|
}
|
|
|
|
#let skein-canvas = body => cetz.canvas(
|
|
length: 0.25cm,
|
|
padding: 0.25,
|
|
{
|
|
import cetz.draw: *
|
|
rect((-1, -1), (1, 1), fill: white, stroke: none)
|
|
|
|
body
|
|
}
|
|
)
|
|
|
|
#let skein = (
|
|
unit: skein-canvas({
|
|
import cetz.draw: *
|
|
|
|
circle((0, 0), radius: 1, stroke: (paint: black, thickness: 0.75pt))
|
|
}),
|
|
// over: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
// draw-strand({ line((-1, -1), (1, 1)) })
|
|
// draw-strand({ line((-1, 1), (1, -1)) })
|
|
// }
|
|
// ),
|
|
// under: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// draw-strand({ line((-1, 1), (1, -1)) })
|
|
// draw-strand({ line((-1, -1), (1, 1)) })
|
|
// }
|
|
// ),
|
|
// h: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
|
|
// draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) })
|
|
// }
|
|
// ),
|
|
// v: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// draw-strand({ hobby((-1, -1), (-0.61, 0), (-1, 1), omega: 1) })
|
|
// draw-strand({ hobby((1, -1), (+0.61, 0), (1, 1), omega: 1) })
|
|
// }
|
|
// ),
|
|
// strand: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// rect((-1, -1), (1, 1), fill: white, stroke: none)
|
|
|
|
// draw-strand({ hobby((-1, 0), (0, 0.25), (1, 0), omega: 1) })
|
|
// }
|
|
// ),
|
|
// over-twist: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
|
|
// draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
|
|
// }
|
|
// ),
|
|
// under-twist: cetz.canvas(
|
|
// length: 0.25cm,
|
|
// padding: 0.25,
|
|
// {
|
|
// import cetz.draw: *
|
|
|
|
// draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
|
|
// draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
|
|
// }
|
|
// )
|
|
over: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ line((-1, -1), (1, 1)) })
|
|
draw-strand({ line((-1, 1), (1, -1)) })
|
|
}),
|
|
under: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ line((-1, 1), (1, -1)) })
|
|
draw-strand({ line((-1, -1), (1, 1)) })
|
|
}),
|
|
h: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
|
|
draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) })
|
|
}),
|
|
v: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ hobby((-1, -1), (-0.61, 0), (-1, 1), omega: 1) })
|
|
draw-strand({ hobby((1, -1), (+0.61, 0), (1, 1), omega: 1) })
|
|
}),
|
|
strand: skein-canvas({
|
|
import cetz.draw: *
|
|
rect((-1, -1), (1, 1), fill: white, stroke: none)
|
|
draw-strand({ hobby((-1, 0), (0, 0.25), (1, 0), omega: 1) })
|
|
}),
|
|
over-twist: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
|
|
draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
|
|
}),
|
|
under-twist: skein-canvas({
|
|
import cetz.draw: *
|
|
draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
|
|
draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
|
|
})
|
|
)
|
|
|
|
#show: ams-article.with(
|
|
paper-size: "a4",
|
|
title: [Implementation of the Kauffman Polynomial in SageMath],
|
|
authors: (
|
|
(
|
|
name: "Antonio De Lucreziis",
|
|
organization: [Dipartimento di Matematica],
|
|
location: [Pisa, Italia],
|
|
email: "antonio.delucreziis@gmail.com",
|
|
url: "https://poisson.phc.dm.unipi.it/~delucreziis/"
|
|
),
|
|
),
|
|
abstract: [
|
|
In this project we implement the Kauffman polynomial in SageMath (Python).
|
|
]
|
|
)
|
|
|
|
|
|
|
|
#outline()
|
|
|
|
= Introduction
|
|
|
|
Actually we don't like Python so we will be using Rust and then write bindings for Python that can be used in SageMath.
|
|
|
|
== The Kauffman Polynomial
|
|
|
|
The Kauffman polynomial $kL$ is a two-variable polynomial invariant of unoriented knots and links in 3-dimensional space. It is defined using _Skein relations_, more precisely an implicit functional equation.
|
|
|
|
The defining axioms of the Kauffman polynomial are the following, given a link diagram $K$ we have $kL_K(a,z) in ZZ[a, a^(-1), z, z^(-1)]$ and:
|
|
|
|
1. If $K$ and $K'$ are two equivalent up to regular isotopy, then $kL_(K)(a,z) = kL_(K')(a,z)$.
|
|
|
|
2. We have the following identities:
|
|
|
|
- $kL(#skein.over) + kL(#skein.under) = z (kL(#skein.h) + kL(#skein.v))$
|
|
|
|
- $kL(#skein.unit) = 1$
|
|
|
|
- $kL(#skein.over-twist) = a kL(#skein.strand)$
|
|
|
|
- $kL(#skein.under-twist) = a^(-1) kL(#skein.strand)$
|
|
|
|
We will later be seeing that the Kauffman polynomial can be defined in a more explicit way, using a recursive definition that is the one we will be using to derive our algorithm.
|
|
|
|
== Computational Knot Theory
|
|
|
|
The first problem in computational knot theory is to find a good representation for knots and links. There are various common representations, such as:
|
|
|
|
- *Gauss codes*:
|
|
|
|
This is very simple to generate, we just need to label each crossing with a number and then write down the sequence of numbers in the order they appear by walking along the knot. This has the problem that it is not unique, for example it does not distinguish between the trefoil knot and its mirror image.
|
|
|
|
- *Signed Gauss codes*:
|
|
|
|
This is an improvement over the previous representation, on the second occurrence of a number we use a $+$ or $-$ sign to indicate the handedness of the crossing.
|
|
|
|
- *Planar diagram codes (PD codes)*, this is the one we will be using in this project:
|
|
|
|
The PD code for a link is generated by labelling each arc of the link with a number. Then we choose a starting point and walk along the link, when we pass at an over-crossing for the current strand we write down a $4$-uple of counter-clockwise numbers for the arc incident to the crossing.
|
|
|
|
There are also other codes like *Braid representations* and *DT (Dowker-Thistlethwaite) Codes* we will not be using in this project.
|
|
|
|
=== PD codes
|
|
|
|
Reference: #link("https://knotinfo.math.indiana.edu/descriptions/pd_notation.html")[PD notation article from KnotInfo]
|
|
|
|
The PD code for a link is generated by labelling each arc of the link with a number. Then we choose a starting point for each component and process each component in order.
|
|
|
|
For each component we walk along it from the starting point in the component direction.
|
|
|
|
#figure(
|
|
image("assets/pd-code-crossing-ordering.svg"),
|
|
)
|
|
|
|
When we pass at a crossing that is an over-crossing for the current strand we write down a $4$-uple of counter-clockwise numbers for the arc incident to the crossing starting from the _entering under-crossing_ arc.
|
|
|
|
*Algorithm*:
|
|
|
|
```
|
|
Input: An oriented link diagram.
|
|
Output: List<(Nat, Nat, Nat, Nat)>
|
|
|
|
- Choose an ordering for the components and starting point for each component
|
|
- Label each arc with a number
|
|
- For each component:
|
|
- Walk along it from the starting point in its orientation
|
|
- At each crossing, when at an over-crossing for the current strand
|
|
- Write down a 4-uple of counter-clockwise numbers for the arc incident to the crossing starting from the entering under-crossing arc
|
|
|
|
```
|
|
|
|
|
|
Let's see an example of how to construct the PD code for the following link diagram
|
|
|
|
#figure(
|
|
image("assets/pd-code-0.svg"),
|
|
)
|
|
|
|
First let's choose a starting point for each component and label accordingly the arcs of the link. We will use the following convention:
|
|
|
|
#figure(
|
|
image("assets/pd-code-labelling.svg"),
|
|
caption: [Oriented link with starting points and edge labels.],
|
|
)
|
|
|
|
Now we can start processing each component of the link by walking along it in its orientation and writing down the over-crossings we encounter.
|
|
|
|
#{
|
|
align(center,
|
|
grid(
|
|
columns: 2,
|
|
align: horizon,
|
|
column-gutter: 2em,
|
|
row-gutter: 1em,
|
|
image("assets/pd-code-crossing-1.svg"),
|
|
block[
|
|
#set align(left)
|
|
|
|
First link component \
|
|
First over-crossing \
|
|
$=>$ `(6,1,7,2)`
|
|
],
|
|
image("assets/pd-code-crossing-2.svg"),
|
|
block[
|
|
#set align(left)
|
|
|
|
First link component \
|
|
Second over-crossing \
|
|
$=>$ `(8,3,5,4)`
|
|
],
|
|
image("assets/pd-code-crossing-3.svg"),
|
|
block[
|
|
#set align(left)
|
|
|
|
Second link component \
|
|
First over-crossing \
|
|
$=>$ `(2,5,3,6)`
|
|
],
|
|
image("assets/pd-code-crossing-4.svg"),
|
|
block[
|
|
#set align(left)
|
|
|
|
Second link component \
|
|
Second over-crossing \
|
|
$=>$ `(4,7,1,8)`
|
|
],
|
|
)
|
|
)
|
|
}
|
|
|
|
Every directed crossing appears only once as an over-crossing so this algorithm terminates when all crossings have been visited. So the final PD code for this link is
|
|
|
|
#{
|
|
set align(center)
|
|
set text(size: 1.125em)
|
|
|
|
`[(6,1,7,2), (8,3,5,4), (2,5,3,6), (4,7,1,8)]`
|
|
}
|
|
|
|
=== Link reconstruction from a PD code
|
|
|
|
We briefly mention that reconstructing a link from a PD code is not trivial, for example the KnotTheory package in SageMath has a #link("https://doc.sagemath.org/html/en/reference/knots/sage/knots/link.html#sage.knots.link.Link.plot")[`Link.plot()`] that can be used as follows
|
|
|
|
#[
|
|
#set align(center)
|
|
|
|
```python
|
|
# The "monster" unknot
|
|
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.plot()
|
|
```
|
|
|
|
#box(
|
|
fill: luma(93%),
|
|
radius: 0.5em,
|
|
inset: 1em,
|
|
image("assets/sage-monster-unknot.svg", width: 40%)
|
|
)
|
|
]
|
|
|
|
This internally uses a #link("https://github.com/sagemath/sage/blob/e0cf1e41d419feb9ddbc0e3c54823928a01587dc/src/sage/knots/link.py#L3639")[_mixed integer linear programming_ (MILP)] solver to generate a knot diagram from a PD code. Another library called #link("https://github.com/3-manifolds/Spherogram/blob/725086a1d8c5d1381ff6a70315047efd8e0dac3f/spherogram_src/links/orthogonal.py")[Spherogram uses _network flows_] to generate this visualization by computing the minimum number of left and right bends for an "orthogonal" presentation of the link.
|
|
|
|
More references:
|
|
|
|
- #link("https://www.sciencedirect.com/science/article/pii/S0096300305002778")[A better heuristic for area-compaction of orthogonal representations]
|
|
|
|
== Algorithm for computing the Kauffman Polynomial
|
|
|
|
#definition[
|
|
Let $K$ be an un-oriented link. We denote by $hat(K)(p)$ the *standard unknot* for $K$ where $p$ is a _directed starting point_. This is build by considering the planar shadow $U$ of $K$ and walking along $U$ starting from $p$ and making each crossing an over-crossing when passing on it the first time.
|
|
|
|
TODO: Disegnini
|
|
]
|
|
|
|
#definition[
|
|
Let's give a name to the following knot modifications for $K$ near a specific crossing $i$
|
|
|
|
#align(center,
|
|
grid(
|
|
columns: 4,
|
|
column-gutter: 2em,
|
|
row-gutter: 0.5em,
|
|
skein.over,
|
|
skein.under,
|
|
skein.h,
|
|
skein.v,
|
|
$K$,
|
|
$S_i K$,
|
|
$E_i K$,
|
|
$e_i K$,
|
|
)
|
|
)
|
|
]
|
|
|
|
Let now $K$ be an oriented link with $n$ components so $K = K_1 union ... union K_n$.
|
|
|
|
|
|
*Algorithm.* Here follows the algorithm that computes $kL_(K)(a,z)$.
|
|
|
|
|