some updates

main
parent 2a401bccbb
commit f8699ee02f

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

@ -737,6 +737,8 @@ class SignedGaussCodeCrossing:
def is_left(self) -> bool: ...
def is_right(self) -> bool: ...
def opposite(self) -> SignedGaussCodeCrossing: ...
def switch(self) -> SignedGaussCodeCrossing: ...
def flip_handedness(self) -> SignedGaussCodeCrossing: ...
```
```python
@ -788,14 +790,21 @@ Let's notice that here each crossing appears twice, once as over-crossing and on
==== 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.
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 each component of the link. Then we can walk along the shadow of each component in order 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.
#figure(
image("assets/standard-unlink-construction.png"),
caption: [Standard unknot construction for a link, in blue are highlighted all the crossings in the resulting switching sequence],
)
On the other hand our algorithm directly works with switching sequences $lambda$ that bring a link $L$ to its standard unlink $hat(L)$. 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.
```python
def std_unknot_switching_sequence(self) -> list[int]:
visited_crossings: set[int] = set()
switched_crossings: list[int] = []
visited_crossings: set[int] = set() # list of ids
switched_crossings: list[int] = [] # resulting list of ids to switch
for component in self.components:
for crossing in component:
@ -807,23 +816,61 @@ def std_unknot_switching_sequence(self) -> list[int]:
return switched_crossings
```
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.
Now we need to be careful when applying these switches as they do not preserve the writhe as we can see in the following figure
#figure(
image("assets/standard-unlink-signs-switches.png", width: 80%),
caption: [Switched crossing signs after brining the previous link to its standard unlink],
)
```python
def apply_switching_sequence(self, seq: list[int]) -> SignedGaussCode:
return SignedGaussCode([
First we wrote two functions one called `switch` that creates a new switched crossing from another crossing and then another one for `SGCode` that does the _two_ switches.
#align(
center,
grid(
columns: 2,
gutter: 1em,
align: center + horizon,
[
```py
def switch(self: SGCodeCrossing):
return SGCodeCrossing(
self.id,
-self.over_under,
-self.handedness
)
```
],
[
```py
def switch_crossing(self: SGCode, id: int):
return SGCode([
[
crossing.switch()
if crossing.id == id else crossing
for crossing in component
]
for component in self.components
])
```
],
),
)
With this infrastructure in place now applying a switching sequence is just a matter of applying all switches in a given sequence, this can be done in a single pass with the following function.
```py
def apply_switching_sequence(self, seq: list[int]) -> SGCode:
return SGCode([
[
crossing.opposite(invert_handedness=True)
if crossing.id in seq else crossing
crossing.switch()
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 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.
@ -840,7 +887,7 @@ Then we have the following cases
So we have a total of $2 times 2 times 2 = 8$ cases to analyze. The following diagram shows all the possible cases for horizontal splicing for _signed gauss codes_.
#figure(
image("assets/splice-h-cases.png", width: 125%),
image("assets/operations-splice-h-cases.png", width: 125%),
caption: [Cases for horizontal splicing],
)
@ -894,7 +941,7 @@ TODO: Code snippet
The code for the *vertical splices* is omitted as the cases are the same as for the horizontal splices with a minor change. All the cases for vertical splices are shown below in the following diagram and we can see that the output lists are the same as in the previous splice case just switched based on the crossing sign.
#figure(
image("assets/splice-v-cases.png"),
image("assets/operations-splice-v-cases.png"),
caption: [Cases for vertical splicing],
)
@ -904,6 +951,64 @@ So the final code is just a conversion of all this cases to list slicing and re-
= Appendix
== Enhancing SGCode rejoining code
The initial code to do horizontal and vertical splices looked something like the following
```py
...
if handedness == HANDED_LEFT:
return SGCode([
*(component[:]
for i, component in enumerate(self.components)
if i != component_index),
[
*l1,
*l3,
],
l2,
])
else:
return SGCode([
*(component[:]
for i, component in enumerate(self.components)
if i != component_index),
[
*l1,
*l2[::-1],
*l3,
],
])
```
This is actually wrong as this is not correcting the signs of all crossing from the reversed strand. The corrected code for the second case is not very readable
```py
over_crossing_ids = set(c.id for c in l2 if c.is_over())
under_crossing_ids = set(c.id for c in l2 if c.is_under())
non_self_crossing_ids = over_crossing_ids.symmetric_difference(under_crossing_ids)
update_signs = lambda strand: [
c.flip_handedness() if c.id in non_self_crossing_ids else c
for c in strand
]
SGCode([
*(
update_signs(component)
for i, component in enumerate(self.components)
if i != component_index
),
[
*update_signs(l1),
*update_signs(reversed(l2)),
*update_signs(l3),
],
])
```
== Nice Typst Stuff
All combinations of `skein-generic` typst function
#[

@ -165,6 +165,7 @@
show figure: it => {
// Customize the figure's caption.
show figure.caption: caption => {
set text(size: small-size)
smallcaps(caption.supplement)
if caption.numbering != none {
[ ]

Loading…
Cancel
Save