CableLink.m

From Knot Atlas
Jump to navigationJump to search

(* The program CableLink is documented on the page Threading a link by a polynomial. It is not part of the package KnotTheory` but is designed to work with it. Copy-paste the text below ignoring the *) on the first line and the (* on the last.

*)

(*Kauffman bracket*)
KB1[pd_PD] := KB1[pd, {}, 1];
KB1[pd_PD, inside_, web_] := 
  Module[{pos = 
     First[Ordering[Length[Complement[List @@ #, inside]] & /@ pd]]}, 
   pd[[pos]] /.  
    X[a_, b_, c_, d_] :> 
     KB1[Delete[pd, pos], Union[inside, {a, b, c, d}], 
      Expand[web*(A P[a, d] P[b, c] + 1/A P[a, b] P[c, d])] //. {P[e_,
            f_] P[f_, g_] :> P[e, g], P[e_, _]^2 :> P[e, e], 
        P[e_, e_] -> -A^2 - 1/A^2}]];
KB1[PD[], _, web_] := Expand[web]
(*Position of a strand at a crossing*)
strandAtCrossing[crossing_, s_] := Module[{pos},
   pos = Position[crossing, s];
   If[Length[pos] > 0, pos[[1, 1]], False]
   ];
(*Orientation of overstrand. Complicated by components with only two strand labels*)
orientationOfOverStrand[crossing_, link_] := Module[{otherCrossings},
   (*find the other crossings containing the strand labels of the \
overstrand crossing*)
   otherCrossings = 
    DeleteElements[
     Select[link, 
      ContainsAny[
        List[Delete[#, 0]], {crossing[[2]], 
         crossing[[4]]}] &], {crossing}];
   (*if the length of othercrossings is 1, 
   then the overstrand labels are part of a component with only two \
strand labels*)
   If[Length[otherCrossings] == 1,
    (*first check if a strand label is in the first position of the \
other crossing.*)
    Which[otherCrossings[[1, 1]] == crossing[[2]],
     True,
     otherCrossings[[1, 1]] == crossing[[4]],
     False,
     (*Otherwise, the strands are always overcrossings, 
     in which case we just choose to orient from small to large*)
     True,
     crossing[[2]] > crossing[[4]]
     ],
    (*otherwise, 
    the overcrossing is oriented from 4 to 2 if pos 2 is 1 more than \
pos 4, or if pos 4 is more than 1 greater than pos 2.*)
     crossing[[2]] - crossing[[4]] == 1 || 
     crossing[[4]] - crossing[[2]] > 1
    ]
   ];
(*crossing that a strand enters*)
findEnteringVertex[link_, s_] := 
  Module[{crossingsContainingS, firstCrossing, pos},
   (*find crossings containing s*)
   crossingsContainingS = 
    Select[link, IntegerQ[strandAtCrossing[#, s]] &];
   firstCrossing = crossingsContainingS[[1]];
   pos = strandAtCrossing[firstCrossing, s];
   Which[
    (*if pos is 1, then it is definitely an incoming strand*)
    pos == 1,
    firstCrossing,
    (pos == 2 && ! 
        orientationOfOverStrand[firstCrossing, link]) || (pos == 4 && 
       orientationOfOverStrand[firstCrossing, link]),
    firstCrossing,
    (*otherwise it must be the entering strand at the other vertex \
containing that label*)
    True,
    crossingsContainingS[[2]]
    ]
   ];
duplicateVertex[link_, crossing_, strands_] := 
  Module[{pos = strandAtCrossing[crossing, strands[[1]]], 
    newLabel = Length[link]*2 + 2, 
    newLink = DeleteElements[link, {crossing}], p1 = crossing[[1]], 
    p2 = crossing[[2]], p3 = crossing[[3]], p4 = crossing[[4]]},
   Which[
    (*pos is 1 and the upper strand is oriented from 4 to 2*)
    pos == 1  && orientationOfOverStrand[crossing, link],
    {Union[
      increaseStrandLabels[newLink, p4],
      PD[
       X[If[p1 < p2, p1, p1 + 1], p4 + 1, If[p3 < p2, p3, p3 + 1], 
        p4]],
      PD[X[newLabel, If[p2 > p4, p2 + 1, p2], newLabel + 1, p4 + 1]]
      ],
     Map[If[# <= p2, #, # + 1] &, Prepend[strands[[2 ;;]], p3]]
     },
    (*pos is 1 and the upper strand is oriented from 2 to 4*)
    pos == 1,
    {Union[
      increaseStrandLabels[newLink, p2],
      PD[
       X[If[p1 < p2, p1, p1 + 1], p2 + 1, If[p3 < p2, p3, p3 + 1], 
        If[p4 < p2, p4, p4 + 1]]],
      PD[X[newLabel, p2, newLabel + 1, p2 + 1]]
      ],
     Map[If[# <= p2, #, # + 1] &, Prepend[strands[[2 ;;]], p3]]
     },
    (*pos is 2*)
    pos == 2,
    {Union[
      increaseStrandLabels[newLink, p1],
      PD[
       X[p1, If[p2 < p1, p2, p2 + 1], p1 + 1, 
        If[p4 < p1, p4, p4 + 1]]],
      PD[X[p1 + 1, newLabel, If[p3 < p1, p3, p3 + 1], newLabel + 1]]
      ],
     Map[If[# <= p1, #, # + 1] &, Prepend[strands[[2 ;;]], p4]]
     },
    (*pos is 4*)
    True,
    {Union[
      increaseStrandLabels[newLink, p1],
      PD[
       X[p1 + 1, If[p2 < p1, p2, p2 + 1], If[p3 < p1, p3, p3 + 1], 
        If[p4 < p1, p4, p4 + 1]]],
      PD[X[p1, newLabel + 1, p1 + 1, newLabel]]
      ],
     Map[If[# <= p1, #, # + 1] &, Prepend[strands[[2 ;;]], p2]]
     }
    ]
   ];
deleteVertex[link_, crossing_, s_, strandList_] := 
 Module[{newCrossing = crossing, 
   newLink = DeleteElements[link, {crossing}], min, max, 
   pos = strandAtCrossing[crossing, s], outgoingStrandPos, 
   newStrandList = strandList},
  (*we create an unknot if the two labels of the other strand at the \
crossing are equal*)
  Which[(pos == 1 && crossing[[2]] == crossing[[4]]) , 
   newStrandList = 
    Replace[newStrandList, 
     crossing[[2]] -> 0, {1}], (pos == 2 || pos == 4 ) && 
    crossing[[1]] == crossing[[3]], 
   newStrandList = Replace[newStrandList, crossing[[1]] -> 0, {1}]];
  (*"
	we need to keep track of the outgoing strand label. There's actually \
two ways to know we're done:
	either the outgoing strand is the same as the incoming strand, or \
the whole vertex only involves 2 different labels.
"*)
  outgoingStrandPos = Which[pos == 1, 3, pos == 2, 4, True, 2];
  If[crossing[[outgoingStrandPos]] == s || 
    CountDistinct[{Delete[crossing, 0]}] < 3, 
   outgoingStrandPos = -1];
  (*adjust link after combining pos 1 and pos 3*)
  If[crossing[[1]] > crossing[[3]],
   newLink = 
    decreaseStrandLabels[
     Replace[newLink, crossing[[1]] -> crossing[[3]], {2}], 
     newCrossing[[1]]];
   newCrossing = 
    decreaseStrandLabels[{Replace[newCrossing, 
        crossing[[1]] -> crossing[[3]], {1}]}, newCrossing[[1]]][[
     1]];
   newStrandList = 
    Map[If[# > newCrossing[[1]], # - 1, #] &, 
     Replace[newStrandList, crossing[[1]] -> crossing[[3]], {1}]]
   ,
   newLink = decreaseStrandLabels[newLink, newCrossing[[1]]];
   newCrossing = 
    decreaseStrandLabels[{newCrossing}, newCrossing[[1]]][[1]];
   newStrandList = 
    Map[If[# > newCrossing[[1]], # - 1, #] &, newStrandList]
   ];
  (*adjust link after combining pos 2 and pos 4*)
  min = Min[newCrossing[[2]], newCrossing[[4]]];
  max = Max[newCrossing[[2]], newCrossing[[4]]];
  If[max - min > 1,
   newLink = 
    decreaseStrandLabels[Replace[newLink, max -> min, {2}], max];
   newCrossing = 
    decreaseStrandLabels[{Replace[newCrossing, max -> min, {1}]}, 
      max][[1]];
   newStrandList = 
    Map[If[# > max, # - 1, #] &, 
     Replace[newStrandList, max -> min, {1}]],
   newLink = decreaseStrandLabels[newLink, min];
   newCrossing = decreaseStrandLabels[{newCrossing}, min][[1]];
   newStrandList = Map[If[# > max, # - 1, #] &, newStrandList]
   ];
  {newLink, 
   If[outgoingStrandPos > 0, newCrossing[[outgoingStrandPos]], -1], 
   newStrandList}
  ]
increaseStrandLabels[link_, s_] := 
  Replace[link, n_?(# > s &) -> n + 1, {2}];
decreaseStrandLabels[link_, s_] := 
  Replace[link, n_?(# > s &) -> n - 1, {2}];
cableComponent[link_, s_, strandList_] := 
  Module[{initialStrands, newComponentLabel = 2 Length[link] + 1},
   initialStrands = Join[{s, s, newComponentLabel}, strandList];
   recurse[newLink_, strands_] := 
    Module[{currentStrand = strands[[1]], firstStrand = strands[[2]], 
      currentCrossing, updatedLink, 
      updatedStrands},(*copy the current crossing*)
     currentCrossing = findEnteringVertex[newLink, currentStrand];
     {updatedLink, updatedStrands} = 
      duplicateVertex[newLink, currentCrossing, strands];
     (*check if we've returned to the first strand*)
     If[updatedStrands[[1]] == updatedStrands[[2]],
      (*replace the largest strand label with the first strand label \
of the new component*)
      {Replace[updatedLink, 
        n_?(# == 2 Length[updatedLink] + 1 &) -> 
         updatedStrands[[3]], {2}], 
       updatedStrands[[4 ;;]]},(*keep adding crossings*)
      recurse[updatedLink, updatedStrands]]
     ];
   recurse[link, initialStrands]
   ];
deleteComponent[link_, s_, strandList_] := 
  Module[{newLink, currentStrand, newStrandList, currentCrossing},
   If[s <= 0,
    (*we're done deleting stuff*)
    {link, strandList},
    (*keep deleting*)
    currentCrossing = findEnteringVertex[link, s];
    {newLink, currentStrand, newStrandList} = 
     deleteVertex[link, currentCrossing, s, strandList];
    deleteComponent[newLink, currentStrand, newStrandList]
    ]
   ];
cableLinkMonomial[link_, strandList_, degreeList_] := 
  Module[{newLink = link, newStrandList, newDegreeList, coeff = 1},
   (*reorder so that we're deleting components first*)
   {newDegreeList, newStrandList} = {degreeList, strandList}[[All, 
     Ordering@degreeList]];
   (*start cabling*)
   Do[
    Which[
     newStrandList[[i]] == 0,
     (*it's an unknot*)
     coeff = coeff*(-A^2 - A^(-2))^newDegreeList[[i]],
     newDegreeList[[i]] == 0,
     (*we need to delete it*)
     {newLink, newStrandList} = 
      deleteComponent[newLink, newStrandList[[i]], newStrandList],
     True,
     (*cable the component*)
     Do[
      {newLink, newStrandList} = 
       cableComponent[newLink, newStrandList[[i]], newStrandList]
      , {j, 1, newDegreeList[[i]] - 1}]
     ]
    , {i, 1, Length[newStrandList]}
    ];
   coeff*KB1[newLink]
   ];
CableLink[link_, poly_, strandList_, vars_] := 
 Module[{monomials = CoefficientRules[poly, vars], temp},
  Total[
   Map[
    #[[2]]*cableLinkMonomial[link, strandList, #[[1]]] &
    , monomials]]
  ]

(* 

*)