@ -623,8 +623,8 @@ Let's now recap the main formal algorithm for computing the Kauffman polynomial.
$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_],
[ #set text(size: 9pt); _h-splice_],
[ #set text(size: 9pt); _v-splice_],
),
)
]
@ -634,9 +634,9 @@ Let now $K$ be an oriented link with $n$ components so $K = K_1 union ... union
#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
- $B_i^lambda K colon.eq e_i S_(lambda_i) dots.c space S_(lambda_0) K$
- 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
@ -687,6 +687,176 @@ Let now $K$ be an oriented link with $n$ components so $K = K_1 union ... union
$
]
== Python Implementation
The approach has been a mix of bottom-up and top-down. First we defined a couple of classed `SignedGaussCode` and `PDCode` to work with these codes and easily convert between each other.
This initial implementation uses `SignedGaussCodes` as they are easier to work with when working with crossing switches and splices but with some modifications the code could be adapted to work directly on `PDCode` provided of some efficient implementations of `splice_h` and `splice_v` methods.
#pagebreak()
=== Signed Gauss Codes
We are now going to walk thorough the class that lets use work nicely with *Signed Gauss Codes*. The the classes we are going to use are all _frozen data-classes_ to ensure immutability and enforce a more functional programming style.
One of the first important things we need is to compute the *writhe* $w(K)$ of a link, this can easily be done with signed gauss codes as its a list of tuples where the second entry is the crossing sign. Let $L$ be an oriented link with components $C_1, ..., C_k$ each with crossings $c_(i, j)$ with $i = 1, ..., k$ and $j = 1, ..., |C_i|$.
Let's notice that here each crossing appears twice, once as over-crossing and once as an under-crossing this is the reason for the $1 slash 2$ in the following formula. By $epsilon(c)$ we refer to the sign (or handedness) of the crossing at $c$.
#[
#set align(center)
#grid(
columns: 3,
gutter: 1.5em,
align: horizon,
[
$
w(L) = 1 / 2 sum_(c "crossing") epsilon(c)
$
],
[$ arrow.squiggly $],
[
```python
def writhe(self):
return sum(
c.handedness # => +1 or -1
for component in self.components
for c in component
) // 2
```
],
)
]
==== Standard Unknot
The next building block for computing the Kauffman polynomial is detecting and computing the *standard unknot or unlink*. Formally this is done by taking the _planar shadow_ and a directed starting point on it. Then we can walk along the shadow and make each crossing an over-crossing when passing on it on the first time.
On the other hand our algorithm directly works with switching sequences $lambda$ that bring a knot $K$ to its standard unknot $hat(K)$. We wrote methods to directly compute and apply these switching sequences.
The `std_unknot_switching_sequence` method just walks along each component in its orientation marking what switches have to be made to bring that link to its standard unknot.
crossing.opposite() if crossing.id in switching_sequence else crossing
for crossing in component
]
for component in self.components
]
)
```
Applying a switching sequence is just a matter of walking along the crossings and flipping the crossings that are in the sequence. This is also how the `switch_crossing(id: int)` method works.
==== Crossing Splices
The splicing code is more involved due to the number of cases to analyze, let's first see formally what we need to do.
We have all the following cases, first we can assume the _entering over strand_ is in the top left corner of a diagram (this can be done by applying locally a small isotopy). So we have $2$ cases for the crossing sign
#{
set align(center)
grid(
columns: 4,
grid(
//
columns: 5,
gutter: 1.5em,
align: horizon,
skein-generic(kind: "over", direction: (+1, +1)),
skein-generic(kind: "over", direction: (+1, -1)),
[$arrow.squiggly$],
skein.h,
[$arrow.squiggly$],
skein-generic(kind: "over", direction: (+1, +1)),
skein-generic(kind: "over", direction: (+1, -1)),
[$arrow.squiggly$],
skein.v,
[$arrow.squiggly$],
),
)
}
So the final code is just a conversion of all this cases to list slicing and re-joining with the appropriate crossings removed.
```python
def splice_h(self, id: int):
raise NotImplementedError("Splicing not implemented yet")
def splice_v(self, id: int):
raise NotImplementedError("Splicing not implemented yet")