gauss notation and some diagrams

main
parent 79ed111299
commit 8cda43d443

Binary file not shown.

@ -5,15 +5,28 @@
#import "@preview/cetz:0.3.4"
#let kL = $L$
#let draw-strand = (polyline) => {
#let draw-strand(polyline, style: (:)) = {
import cetz.draw: *
set-style(stroke: (paint: white, thickness: 5pt, cap: "butt"))
set-style(
..cetz.styles.resolve(
(stroke: (paint: white, thickness: 5pt, cap: "butt")),
base: style,
),
)
polyline
set-style(stroke: (paint: black, thickness: 0.75pt, cap: "round"))
set-style(
..cetz.styles.resolve(
style,
base: (stroke: (paint: black, thickness: 0.75pt, cap: "round")),
),
)
polyline
set-style(stroke: (paint: black, thickness: 0.75pt, cap: "round"))
}
#let skein-canvas = body => cetz.canvas(
@ -22,87 +35,33 @@
{
import cetz.draw: *
rect((-1, -1), (1, 1), fill: white, stroke: none)
body
}
},
)
#let arrow-size = 0.35
#let draw-arrow((x, y), angle: 0, ..style) = {
import cetz.draw: *
{
let len = arrow-size / calc.sqrt(2)
set-style(..style)
translate(x: x, y: y)
rotate(z: angle)
line((0, 0), (-len, +len), ..style)
line((0, 0), (-len, -len), ..style)
}
}
#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)) })
@ -137,9 +96,43 @@
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)) })
})
}),
)
#let skein-generic(
kind: "over",
direction: (+1, +1),
arrows: (true, true),
styles: ((:), (:)),
) = {
skein-canvas({
import cetz.draw: *
if kind == "over" {
draw-strand({ line((-1, -1), (1, 1)) }, style: styles.at(1))
draw-strand({ line((-1, 1), (1, -1)) }, style: styles.at(0))
}
if kind == "under" {
draw-strand({ line((-1, 1), (1, -1)) }, style: styles.at(0))
draw-strand({ line((-1, -1), (1, 1)) }, style: styles.at(1))
}
if arrows.at(0) {
if direction.at(0) == +1 {
line((1 - arrow-size, -1), (1, -1), (1, -1 + arrow-size), ..styles.at(0))
} else {
line((-1, 1 - arrow-size), (-1, 1), (-1 + arrow-size, 1), ..styles.at(0))
}
}
if arrows.at(1) {
if direction.at(1) == +1 {
line((1 - arrow-size, 1), (1, 1), (1, 1 - arrow-size), ..styles.at(1))
} else {
line((-1, -1 + arrow-size), (-1, -1), (-1 + arrow-size, -1), ..styles.at(1))
}
}
})
}
#show: ams-article.with(
paper-size: "a4",
title: [Implementation of the Kauffman Polynomial in SageMath],
@ -149,12 +142,12 @@
organization: [Dipartimento di Matematica],
location: [Pisa, Italia],
email: "antonio.delucreziis@gmail.com",
url: "https://poisson.phc.dm.unipi.it/~delucreziis/"
url: "https://poisson.phc.dm.unipi.it/~delucreziis/",
),
),
abstract: [
In this project we implement the Kauffman polynomial in SageMath (Python).
]
],
)
@ -191,15 +184,15 @@ The first problem in computational knot theory is to find a good representation
- *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.
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.
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.
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.
@ -207,20 +200,18 @@ There are also other codes like *Braid representations* and *DT (Dowker-Thistlet
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.
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"),
)
#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.
Input: An oriented link diagram with starting points on each component
Output: List<(Nat, Nat, Nat, Nat)>
- Choose an ordering for the components and starting point for each component
@ -228,20 +219,18 @@ Output: List<(Nat, Nat, Nat, Nat)>
- 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
- 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"),
)
#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(
#figure(
image("assets/pd-code-labelling.svg"),
caption: [Oriented link with starting points and edge labels.],
)
@ -249,7 +238,8 @@ First let's choose a starting point for each component and label accordingly the
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,
align(
center,
grid(
columns: 2,
align: horizon,
@ -259,35 +249,38 @@ Now we can start processing each component of the link by walking along it in it
block[
#set align(left)
First link component \
First link component \
First over-crossing \
$=>$ `(6,1,7,2)`
],
image("assets/pd-code-crossing-2.svg"),
block[
#set align(left)
First link component \
First link component \
Second over-crossing \
$=>$ `(8,3,5,4)`
],
image("assets/pd-code-crossing-3.svg"),
block[
#set align(left)
Second link component \
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 link component \
Second over-crossing \
$=>$ `(4,7,1,8)`
],
)
),
)
}
@ -300,10 +293,285 @@ Every directed crossing appears only once as an over-crossing so this algorithm
`[(6,1,7,2), (8,3,5,4), (2,5,3,6), (4,7,1,8)]`
}
=== Link reconstruction from a PD code
=== Signed Gauss Codes
Gauss originally developed a notation called *Gauss codes* based on labelling each crossing of a knot with a number and keeping track of when we walk an over-crossing or an under-crossing using a sign. This produces a list of numbers where each number appears exactly twice with different signs. This has a few problems like the fact that this doesn't distinguish a knot vs its mirror.
This is solved by *Signed Gauss Codes*#footnote[#link("https://en.wikipedia.org/wiki/Gauss_notation")[Gauss Notation on Wikipedia]] where we also store the information about the handedness of a crossing.
More precisely this is constructed by the following: for each component we walk along it, when we pass at a crossing we write a tuple $(plus.minus i, plus.minus 1)$ where the first component is the index of the crossing with a sign indicating if this is an over-crossing or under-crossing, the second sign (that can be added in a second pass over the loop) is given by the handedness of the crossing using the following convention
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
$
epsilon(#skein-generic(direction: (+1, +1))) = +1
wide
epsilon(#skein-generic(direction: (+1, -1))) = -1
$
*Algorithm*:
```
Input: An oriented link diagram with starting points on each component
Output: List<List<(Int, Int)>>
- Label each crossing with a number in order
- For each component:
- Walk along it from the starting point in its orientation
- At each crossing, write a tuple with components
- $+i$ or $-i$ if this is an over-crossing or under-crossing
- $+1$ or $-1$ if this is a left-handed or right-handed
```
Converting one code to the other is not too much work as one just need to first do a labelling step to convert crossing labels and then convert the over/under-strand and left/right-handedness relations between the two notations.
For example the _Knot Theory code for the Kauffman polynomial_ converts the *pd notation* to *signed gauss notation* as this is better suited for doing manipulations directly on the crossings.
#let style-stroke-green = (stroke: (paint: green.darken(20%)))
- Crossing switch is just two sign swaps on each of the two occurrences of the over and under strand
- Splicing is just a matter of splitting and rejoining lists correctly, for example let's see what happens in the case of an _horizontal splice_. There are two cases based on the orientation of the strands:
- If the splice happens on a *self-crossing* (crossing with a part of the same curve) then we apply the following modification to the link, the rest remains the same except for the curve containing the spliced crossing that is changed as follows
$
lr(
[
... thin ell^+_1 thin ...
#skein-generic(styles: (style-stroke-green, (:)))
... thin ell^+_2 thin ...
#skein-generic(styles: ((:), style-stroke-green))
... thin ell^+_3 thin ...
],
size: #1.75em
)
& mapsto &&
lr(
[
... thin ell^+_1 thin ...
#skein-canvas(
{
import cetz.draw: *
draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) }, style: style-stroke-green)
draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
draw-arrow((0.1, 0.61), ..style-stroke-green)
},
)
... thin ell^+_3 thin ...
],
size: #1.75em
),
lr(
[
#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) }, style: style-stroke-green)
draw-arrow((0.1, -0.61), ..style-stroke-green)
},
)
... thin ell^+_2 thin ...
],
size: #1.75em
) \
lr(
[
... thin ell^+_1 thin ...
#skein-generic(styles: (style-stroke-green, (:)), direction: (1, -1))
... thin ell^+_2 thin ...
#skein-generic(styles: ((:), style-stroke-green), direction: (1, -1))
... thin ell^+_3 thin ...
],
size: #1.75em
)
& mapsto &&
lr(
[
... thin ell^+_1 thin ...
#skein-canvas(
{
import cetz.draw: *
draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) }, style: style-stroke-green)
draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
draw-arrow((0.1, +0.61), ..style-stroke-green)
},
)
... thin ell^-_2 thin ...
#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) }, style: style-stroke-green)
draw-arrow((-0.1, -0.61), angle: 180deg, ..style-stroke-green)
},
)
... thin ell^+_3 thin ...
],
size: #1.75em
),
$
here by "$... thin ell_i^+ thin ...$" we mean a part of the crossing list and "$... thin ell_i^- thin ...$" is the same list reversed.
- Otherwise if the splice happens on a crossing *between strands of different curves*
$
lr(
[
...
[
... thin ell^+_1 thin ...
#skein-generic(styles: (style-stroke-green, (:)))
... thin ell^+_2 thin ...
]
...
[
... thin ell^+_3 thin ...
#skein-generic(styles: ((:), style-stroke-green))
... thin ell^+_4 thin ...
]
...
],
size: #1.75em
) \
#rotate(90deg)[$mapsto$] \
lr(
[
...
[
... thin ell^+_1 thin ...
#skein-canvas(
{
import cetz.draw: *
draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) }, style: style-stroke-green)
draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
draw-arrow((0.1, +0.61), ..style-stroke-green)
},
)
... thin ell^+_4 thin ... thin ell^+_3 thin ...
#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) }, style: style-stroke-green)
draw-arrow((0.1, -0.61), ..style-stroke-green)
},
)
... thin ell^+_2 thin ...
]
...
],
size: #1.75em
)
$
or in case of the other orientation
$
lr(
[
...
[
... thin ell^+_1 thin ...
#skein-generic(styles: (style-stroke-green, (:)), direction: (1, -1))
... thin ell^+_2 thin ...
]
...
[
... thin ell^+_3 thin ...
#skein-generic(styles: ((:), style-stroke-green), direction: (1, -1))
... thin ell^+_4 thin ...
]
...
],
size: #1.75em
) \
#rotate(90deg)[$mapsto$] \
lr(
[
...
[
... thin ell^+_1 thin ...
#skein-canvas(
{
import cetz.draw: *
draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) }, style: style-stroke-green)
draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
draw-arrow((0.1, +0.61), ..style-stroke-green)
},
)
... thin ell^-_3 thin ... thin ell^-_4 thin ...
#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) }, style: style-stroke-green)
draw-arrow((0.1, -0.61), ..style-stroke-green)
},
)
... thin ell^+_2 thin ...
]
...
],
size: #1.75em
)
$
// $
// lr(
// [
// ... thin ell^+_1 thin ...
// #skein-generic(styles: (style-stroke-green, (:)), direction: (1, -1))
// ... thin ell^+_2 thin ...
// #skein-generic(styles: ((:), style-stroke-green), direction: (1, -1))
// ... thin ell^+_3 thin ...
// ],
// size: #1.75em
// )
// & mapsto &
// lr(
// [
// ... thin ell^+_1 thin ...
// #skein-canvas(
// {
// import cetz.draw: *
// draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) }, style: style-stroke-green)
// draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
// draw-arrow((0.1, +0.61), ..style-stroke-green)
// },
// )
// ... thin ell^-_2 thin ...
// #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) }, style: style-stroke-green)
// draw-arrow((-0.1, -0.61), angle: 180deg, ..style-stroke-green)
// },
// )
// ... thin ell^+_3 thin ...
// ],
// size: #1.75em
// ),
// $
=== Link reconstruction from code
We briefly mention that reconstructing a link from a PD code is not trivial and there are various approaches that can be used for this task.
==== Linear Integer Programming
For example the #link("https://doc.sagemath.org/html/en/reference/knots/sage/knots/link.html")[KnotTheory package in Sage] has a #link("https://doc.sagemath.org/html/en/reference/knots/sage/knots/link.html#sage.knots.link.Link.plot")[`Link.plot()`] method that thats a link that can be constructed using a PD code and then plots it as follows
#[
#set align(center)
@ -319,20 +587,25 @@ We briefly mention that reconstructing a link from a PD code is not trivial, for
fill: luma(93%),
radius: 0.5em,
inset: 1em,
image("assets/sage-monster-unknot.svg", width: 40%)
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.
Sage 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] instead uses _network flows_. The problem here is to find an orthogonal presentation for the link with the _minimum number of left and right bends_
#footnote[#link("https://www.sciencedirect.com/science/article/pii/S0096300305002778")[A better heuristic for area-compaction of orthogonal representations]].
More references:
==== Planar Graph Embeddings
- #link("https://www.sciencedirect.com/science/article/pii/S0096300305002778")[A better heuristic for area-compaction of orthogonal representations]
Another approach used by #link("https://knotfol.io/")[KnotFolio] is based on #link("https://en.wikipedia.org/wiki/Tutte_embedding")[Tutte embeddings]. A *Tutte embedding or barycentric embedding* of a simple, 3-vertex-connected, planar graph is a crossing-free straight-line embedding with the properties that the outer face is a convex polygon and that each interior vertex is at the average (or barycenter) of its neighbors' positions.
This condition that every point is the average of its neighbors can be easily expressed as a system of linear equations where some points on a chosen outer face have been fixed. When the graph is planar and 3-vertex-connected the linear system is non degenerate and has a unique solution.
== Algorithm for computing the Kauffman Polynomial
Let's now recap the main formal 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.
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 built 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
]
@ -340,26 +613,106 @@ More references:
#definition[
Let's give a name to the following knot modifications for $K$ near a specific crossing $i$
#align(center,
#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$,
)
row-gutter: 1em,
skein.over, skein.under, skein.h, skein.v,
$K$, $S_i K$, $E_i K$, $e_i K$,
[ #set text(size: 9pt); _original_],
[ #set text(size: 9pt); _switch_],
[ #set text(size: 9pt); _splice_],
[ #set text(size: 9pt); _splice_],
),
)
]
Let now $K$ be an oriented link with $n$ components so $K = K_1 union ... union K_n$.
#definition[
Let $K$ abd $lambda = (lambda_n, ..., lambda_0)$ a sequence of indices of crossing of $K$ and let $i$ be an index of one of the crossings, let's define the following actions
- $A_i^lambda colon.eq E_i S_(lambda_i) ... S_(lambda_0)$
- $B_i^lambda colon.eq e_i S_(lambda_i) ... S_(lambda_0)$
- Then let $lambda$ be a sequence of indices that bring $K$ to $hat(K)$ so that $hat(K)(lambda) colon.eq S_(lambda_n) dots.c space S_(lambda_0) K$ and define
$
sum_K (lambda) =
sum_(i=0)^n (-1)^i (kL(A_i^lambda K) + kL(B_i^lambda K)) \
Omega_K (lambda) =
(-1)^(|lambda| + 1) L_(hat(K)(lambda)) + z sum_K (lambda)
$
]
*Algorithm.* Here follows the algorithm that computes $kL_(K)(a,z)$.
1. If $K = hat(K)$ is a _standard unknot_ then $kL_K (a, z) colon.eq a^w(K)$
2. If $K_1$ _overlies_ $K_2$, let $d colon.eq (a + a^(-1)) slash z - 1$ and then
$
kL(K_1 union K_2) colon.eq d kL(K_1) kL(K_2)
$
3. If $K = K_1 union dots.c union K_n$
- If a $K_i$ _overlies_ another another component than apply (ii).
- If no $K_i$ _overlies_ all others let $p_1, ..., p_n$ be _directed starting points_ on $K_1, ..., K_n$ and let $overline(p)_i$ be the same _directed starting point_ with the opposite direction of $p_i$ on $K_i$. Let $lambda(p_i)$ the sequence of under-crossings of $K_i$ with $K - K_i$ so that $hat(K)(lambda(p_i)) = K_i union.sq (K - K_i)$ so that $K_i$ _overlies_ the rest of the components. At this point we can define $kL_K$ as
#[
#set text(size: 11pt)
$
kL_K (a, z) colon.eq
1 / (2n) [
sum_(q = p_i, overline(p)_i) sum_(i=1)^(|lambda(q)|) (-1)^(|lambda(q)|+1) d kL_(K_i) kL_(K - K_i) + z sum_K (lambda(q))
]
$
]
- If $K$ is a single component then let $p$ be a directed starting point on $K$ and $overline(p)$ the one with opposite direction. Let $lambda(p)$ the switching sequence that brings it to the standard unknot $hat(K)$ and define
#[
#set text(size: 11pt)
$
kL_K (a, z) colon.eq
1 / 2 [
sum_(q = p, overline(p)) sum_(i=1)^(|lambda(q)|) (-1)^(|lambda(q)|+1) kL(hat(K)(lambda(q))) + z sum_K (lambda(q))
]
$
]
#pagebreak()
= Appendix
All combinations of `skein-generic` typst function
#[
#set align(center + horizon)
#for kind in ("over", "under") {
[*Kind:* #raw(repr(kind))]
grid(
columns: 4,
gutter: 1.5em,
..(
((+1, +1), (+1, -1), (-1, +1), (-1, -1)).map(direction => {
((true, true), (false, true), (true, false), (false, false)).map(arrows => {
skein-generic(
kind: kind,
direction: direction,
arrows: arrows,
)
[#direction \ #arrows]
})
})
).flatten()
)
}
]

@ -13,21 +13,16 @@
#let ams-article(
// The article's title.
title: [Paper title],
// An array of authors. For each author you can specify a name,
// department, organization, location, and email. Everything but
// but the name is optional.
authors: (),
// Your article's abstract. Can be omitted if you don't have one.
abstract: none,
// The article's paper size. Also affects the margins.
paper-size: "us-letter",
// The result of a call to the `bibliography` function or `none`.
bibliography: none,
// The document's content.
body,
) = {
@ -45,7 +40,7 @@
// Set the body font. AMS uses the LaTeX font.
set text(size: normal-size, font: "New Computer Modern")
// set text(size: normal-size, font: "Fira Sans", weight: 400)
// show math.equation: set text(font: "Fira Math")
// set strong(delta: 100)
@ -79,7 +74,7 @@
footer: context {
let i = counter(page).get().first()
align(center, text(size: script-size, [#i]))
}
},
)
// Configure headings.
@ -97,30 +92,30 @@
set par(first-line-indent: 0em)
if it.level == 1 {
// set align(center)
set text(size: 17pt)
smallcaps[
#v(1.5em, weak: true)
#number
#it.body
#v(normal-size, weak: true)
]
set text(size: 18pt)
v(normal-size * 2, weak: true)
number
strong(smallcaps(it.body))
v(normal-size * 1.5, weak: true)
counter(figure.where(kind: "definition")).update(0)
counter(figure.where(kind: "theorem")).update(0)
} else if it.level == 2 {
set text(size: 13pt, fill: luma(10%))
v(normal-size * 2, weak: true)
number
strong(it.body)
v(normal-size * 1.5, weak: true)
} else if it.level == 3 {
set text(size: 12pt)
v(normal-size * 2, weak: true)
number
smallcaps(it.body)
v(normal-size * 1.5, weak: true)
} else {
if it.level == 2 {
set text(size: 12pt)
v(1.5em, weak: true)
number
smallcaps(it.body)
h(7pt, weak: true)
} else {
set text(size: 12pt)
v(1.5em, weak: true)
number
it.body
h(7pt, weak: true)
}
set text(size: 10pt, fill: luma(20%))
v(normal-size * 2, weak: true)
strong(it.body)
v(normal-size * 1.5, weak: true)
}
}
@ -128,7 +123,7 @@
set par(spacing: 1.5em, leading: 1em, justify: true)
// Configure lists and links.
set list(spacing: 1.5em, indent: 0.5em, body-indent: 0.5em, marker: ([•]))
set list(spacing: 1.5em, indent: 0.5em, body-indent: 0.5em, marker: [•])
set enum(spacing: 1.5em, indent: 0.5em, body-indent: 0.5em, numbering: "i.1.a)")
show link: it => {
@ -149,17 +144,17 @@
show raw: set text(font: "JetBrains Mono")
show raw: it => box(
fill: luma(92%),
radius: 3pt,
fill: luma(92%),
radius: 3pt,
outset: (x: 2pt, y: 3pt),
it
it,
)
show raw.where(block: true): it => box(
fill: luma(92%),
radius: 4pt,
fill: luma(92%),
radius: 4pt,
inset: 4pt,
it
it,
)
// Configure citation and bibliography styles.
@ -188,42 +183,51 @@
// Definitions
show figure.where(kind: "definition"): set align(start)
show figure.where(kind: "definition"): it => block(spacing: 11.5pt, {
strong({
it.supplement
if it.numbering != none {
[ ]
it.counter.display(it.numbering)
}
[.]
})
[ ]
it.body
})
show figure.where(kind: "definition"): it => block(
spacing: 11.5pt,
{
strong({
it.supplement
if it.numbering != none {
[ ]
it.counter.display(it.numbering)
}
[.]
})
[ ]
it.body
},
)
// Definitions
show figure.where(kind: "theorem"): set align(start)
show figure.where(kind: "theorem"): it => block(spacing: 11.5pt, {
strong({
it.supplement
if it.numbering != none {
[ ]
it.counter.display(it.numbering)
}
[.]
})
[ ]
emph(it.body)
})
show figure.where(kind: "theorem"): it => block(
spacing: 11.5pt,
{
strong({
it.supplement
if it.numbering != none {
[ ]
it.counter.display(it.numbering)
}
[.]
})
[ ]
emph(it.body)
},
)
// Display the title and authors.
v(35pt, weak: true)
align(center, smallcaps({
text(size: 18pt, weight: 600, title)
v(25pt, weak: true)
// text(authors.map(author => link(author.url, author.name)).join(", "))
text(author-string)
}))
align(
center,
smallcaps({
text(size: 18pt, weight: 600, title)
v(25pt, weak: true)
// text(authors.map(author => link(author.url, author.name)).join(", "))
text(author-string)
}),
)
// Display the abstract
if abstract != none {
@ -279,21 +283,21 @@
body,
kind: "definition",
supplement: [Definition],
numbering: if numbered { n => [#n] }
numbering: if numbered { n => [#n] },
)
#let theorem(body, numbered: true) = figure(
body,
kind: "theorem",
supplement: [Theorem],
numbering: if numbered { n => [#n] }
numbering: if numbered { n => [#n] },
)
#let proposition(body, numbered: true) = figure(
body,
kind: "theorem",
supplement: [Proposition],
numbering: if numbered { n => [#n] }
numbering: if numbered { n => [#n] },
)
#let todo(msg: "", body) = grid(
@ -310,20 +314,23 @@
)
// And a function for a proof.
#let proof(body) = block(spacing: 11.5pt, {
emph[Proof.]
[ ]
body
h(1fr)
#let proof(body) = block(
spacing: 11.5pt,
{
emph[Proof.]
[ ]
body
h(1fr)
// Add a word-joiner so that the proof square and the last word before the
// 1fr spacing are kept together.
sym.wj
// Add a word-joiner so that the proof square and the last word before the
// 1fr spacing are kept together.
sym.wj
// Add a non-breaking space to ensure a minimum amount of space between the
// text and the proof square.
sym.space.nobreak
// Add a non-breaking space to ensure a minimum amount of space between the
// text and the proof square.
sym.space.nobreak
$square.stroked$
})
$square.stroked$
},
)

Loading…
Cancel
Save