Release notes for lch.sage

I’ve posted code for computing Legendrian knot invariants in Sage on my website:

https://web.math.princeton.edu/~ssivek/code/lch.sage

This is an update of code I had previously written and posted in the same location, with a bunch of new material for computing the augmentation categories of NRSSZ. It includes the table used by Melvin-Shrestha (also available in the “Legendrian invariants.nb” Mathematica notebook available on Josh Sabloff’s site) with one tb-maximizing Legendrian knot of each type through 9 crossings, and you can input any other knots you’d like in plat form.

I’ve given a bunch of examples of the code in action below, including a proof that a particular $\overline{9_{45}}$ knot is not Legendrian isotopic to its Legendrian mirror. I’ve also tried to document everything in case you want to see what else it can do, but if you have any questions or feature requests feel free to ask.

First, let’s take the right-handed trefoil and compute the size of $Aug(K;\mathbb{F}_3)$. We’ll describe it as the plat [2,2,2], meaning that it has three consecutive positive crossings between strands 2 and 3; we number the strands from 1 at the top to n at the bottom, and here n=4 since it is the smallest even number greater than or equal to 3 (the largest number of a strand involved in a crossing).

sage: K = plat([2,2,2])
sage: f = K.ruling_polynomial(); f
2 + z^2
sage: f(sqrt(3)-1/sqrt(3)) * 3**((K.tb()+1)/2) / 2
5
sage: K.augcat(GF(3)).cardinality()
5

What about the cardinalities of $Aug(K)$ for all of the 6-crossing knots in the Legendrian knot table? We can work over any finite field, with $\mathbb{F}_2$ (or “GF(2)” in Sage) being the default.

sage: for K in LegendrianKnotTable['6_1':'7_1']:
    c = K.augcat().cardinality()
    if c != 0:
        print '|' + str(K.augcat()) + '| =', c
....:         
|Aug(6_1, GF(2))| = 1/4
|Aug(m6_1, GF(2))| = 1/2
|Aug(m6_2, GF(2))| = 5/2

Let’s look at the augmentation category over $\mathbb{F}_2$ of $\overline{7_2}$, which is described in plat form by the list of crossings below. This particular representative has 24 augmentations, numbered 0 through 23; each is represented by a tuple of values $\epsilon(t), \epsilon(t^{-1}), \epsilon(a_0), \dots$ where the $a_i$ are the crossings and cusps of the plat from left to right. We can get a list of representatives of each isomorphism class of object in $Aug(\overline{7_2}; \mathbb{F}_2)$; in this case there are three classes, represented by the augmentations $\epsilon_0,\epsilon_2,\epsilon_4$.

sage: K = LegendrianKnotTable['m7_2']
sage: K.crossings()
(2, 1, 4, 6, 8, 10, 11, 3, 2, 9, 10, 8, 7, 6, 5, 4, 2, 10, 4, 9, 8, 7, 6)
sage: K.tb(), K.rot()
(1, 0)
sage: A = K.augcat(); len(A.objects())
24
sage: print A[4]
(1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
sage: A.isomorphism_representatives()
(0, 2, 4)

Continuing with this example, we can compute the chain complex $\hom(\epsilon_0,\epsilon_0)$. We determine its homology in one of two ways: either as a Python dictionary d where d[i] is the rank of $H^i\hom(\epsilon_0,\epsilon_0)$, or as the Poincaré polynomial of this chain complex. We can also compute the homology of $\hom(\epsilon_0,\epsilon_2)$ and see that it differs from that of $\hom(\epsilon_0,\epsilon_0)$, hence $\epsilon_0$ and $\epsilon_2$ are not isomorphic, a fact which we can also check directly.

sage: A.hom(0,0)
Chain complex with at most 5 nonzero terms over Finite Field of size 2
sage: A.hom(0,0).betti()
{-1: 0, 0: 1, 1: 2, 2: 0, 3: 0}
sage: A.poincare_polynomial(0,0)
1 + 2*t
sage: A.poincare_polynomial(0,2)
t
sage: A.is_isomorphic(0,2)
False

What about the ring structure on $H^*\hom(\epsilon_0,\epsilon_0)$? We can compute it as a FiniteDimensionalAlgebra; I don’t think Sage has a class for graded noncommutative algebras, so we store the grading info in the generator names. This ring is unital (always true) and commutative (often but not always true), with three generators; in this example the generator $e_0$ has grading zero and is the multiplicative unit, while $e_1$ and $e_2$ live in grading 1.

sage: R = A.homology_ring(0); R
Finite-dimensional algebra of degree 3 over Finite Field of size 2
sage: R.is_unitary(), R.is_commutative()
(True, True)
sage: R.gens()
(e0_0, e1_1, e2_1)
sage: R.one()
e0_0
sage: e0, e1, e2 = R.gens()
sage: e1 * e0 == e1 and e0 * e1 == e1
True

We can produce a function which computes the composition $m_2: \hom(\epsilon_2,\epsilon_4) \otimes \hom(\epsilon_0,\epsilon_2) \to \hom(\epsilon_0,\epsilon_4)$ at the chain level, and likewise for higher $A_\infty$ operations such as $m_4: \hom(\epsilon_0,\epsilon_0)^{\otimes 4} \to \hom(\epsilon_0,\epsilon_0)$.

sage: m2 = A.composition(0,2,4)
sage: print A.hom_gens()
(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, x, y)
sage: a2, x, y = A.hom_gens()[2], A.hom_gens()[29], A.hom_gens()[30]
sage: m2(a2,y)
a2
sage: m2(x,a2+y)
x
sage: m4 = A.composition(0,0,0,0,0)
sage: m4('a18','a14','a1-3*y','a16')
a24

We can also do any of these computations in other $\rho$-graded augmentation categories, although I do not claim that the output of the cardinality function makes sense if $\rho \neq 0$. For $9_{13}$, the usual augmentation category is empty, but the 2-graded category has five objects up to isomorphism:

sage: A = LegendrianKnotTable['9_13'].augcat(); A
Aug(9_13, GF(2))
sage: A.cardinality()
0
sage: A2 = LegendrianKnotTable['9_13'].augcat(rho=2); A2
Aug^2(9_13, GF(2))
sage: len(A2.objects())
80
sage: A2.isomorphism_representatives()
(0, 2, 32, 34, 40)
sage: [A2.poincare_polynomial(e) for e in A2.isomorphism_representatives()]
[1 + 4*t, 1 + 4*t, 1 + 4*t, 1 + 4*t, 1 + 4*t]

Finally, let’s prove that the $\overline{9_{45}}$ knot $\Lambda$ in the table is not Legendrian isotopic to its Legendrian mirror $\mu(\Lambda)$. (This is the image of $\Lambda$ under the map $(x,y,z) \mapsto (x,-y,-z)$.) This knot, which is the one in the Legendrian knot atlas with ruling polynomial $2+z^2$, has five augmentations up to isomorphism. For two of these, the corresponding rings $H^*\hom(\epsilon,\epsilon)$ are noncommutative:

sage: K = LegendrianKnotTable['m9_45']
sage: K.ruling_polynomial()
2 + z^2
sage: A = K.augcat(); A.isomorphism_representatives()
(0, 1, 4, 5, 8)
sage: filter(lambda e: not A.homology_ring(e).is_commutative(), [0,1,4,5,8])
[0, 8]

Let’s look at $H^*\hom(\epsilon_0,\epsilon_0)$ and see what the nontrivial products are.

sage: R,b = A.homology_ring_with_basis(0)
sage: for x in R.basis():
....:     for y in R.basis():
....:         if x*y != 0 and x != R.one() and y != R.one():
....:             print '%s * %s = %s'%(x,y,x*y)
....:             
e0_n1 * e3_1 = e1_0
e3_1 * e5_2 = e6_3
e5_2 * e0_n1 = e4_1
sage: b[3],b[5],b[6]
(a3, a5 + a10, a8)

So among the basis elements of this ring other than the unit, we have exactly three nontrivial products: $e_0 \cdot e_3 = e_1$, $e_3 \cdot e_5 = e_6$, and $e_5 \cdot e_0 = e_4$. These belong to the product maps $H^{-1} \otimes H^1 \to H^0$, $H^1 \otimes H^2 \to H^3$, and $H^2 \otimes H^{-1} \to H^{1}$, respectively.

By using homology_ring_with_basis rather than homology_ring above, we can even figure out which generators of the linearized contact homology are involved in the second product ($e_3\cdot e_5 = e_6$) above: numbering the crossings and right cusps from left to right starting with $a_0$, we have $$[a_3]\cdot[a_5+a_{10}] = [a_8].$$ We can double-check that these representatives are cocycles and that they multiply as claimed:

sage: m1, m2 = A.composition(0,0), A.composition(0,0,0)
sage: m1('a3'), m1('a5+a10'), m1('a8')
(0, 0, 0)
sage: m2('a3', 'a5+a10')
a8

The other noncommutative ring, $H^*\hom(\epsilon_8,\epsilon_8)$, is isomorphic to this one.

sage: R = A.homology_ring(8)
sage: for x in R.basis():
    for y in R.basis():
        if x*y != 0 and x != R.one() and y != R.one():
            print '%s * %s = %s'%(x,y,x*y)
....:             
e0_n1 * e3_1 = e1_0
e3_1 * e5_2 = e6_3
e5_2 * e0_n1 = e4_1

So $Aug(\Lambda)$ produces one noncommutative graded ring $R$ up to isomorphism, with nontrivial products $H^i \otimes H^j \to H^{i+j}$ (not including the unit) in bidegrees $(i,j) = (-1,1), (1,2), (2,-1)$. At this point we are done, because the Legendrian mirror $\mu(\Lambda)$ should produce the opposite ring $R^{op}$; since $R^{op}$ has a nontrivial product $H^2 \otimes H^1 \to H^3$ and $R$ does not, they can’t be isomorphic, so $Aug(\Lambda) \not\cong Aug(\mu(\Lambda))$ and $\Lambda$ is not Legendrian isotopic to $\mu(\Lambda)$. Of course, we can also check this directly:

sage: Km = A.knot().legendrian_mirror()
sage: Am = Km.augcat(); Am.isomorphism_representatives()
(0, 1, 4, 5, 8)
sage: filter(lambda e: not Am.homology_ring(e).is_commutative(), [0,1,4,5,8])
[0, 8]
sage: R = Am.homology_ring(0)
sage: for x in R.basis():
    for y in R.basis():
        if x*y != 0 and x != R.one() and y != R.one():
            print '%s * %s = %s'%(x,y,x*y)
....:             
e0_n1 * e5_2 = e4_1
e3_1 * e0_n1 = e1_0
e5_2 * e3_1 = e6_3

So for the Legendrian mirror $\mu(\Lambda)$, $H^*\hom_{Aug(\mu(\Lambda))}(\epsilon_0,\epsilon_0)$ has nontrivial products $H^{-1} \otimes H^2 \to H^1$, $H^1 \otimes H^{-1} \to H^0$, and $H^2 \otimes H^1 \to H^3$, just as expected.

3 comments

  1. ericzaslow says:

    Wow. It looks to me like the “cardinality” procedure does not include the recent adjustments due to higher isomorphisms. Is that correct?

  2. Steven Sivek says:

    It does include them, at least when the grading $\rho$ is zero: see the AugmentationCategory_class.naut method (ctrl-f “def naut”), which is supposed to count the number of automorphisms of an object. The loop “for d in self.hom(e,e).betti().keys(): …” at the end is where we account for the extra factors. You can run something like

    sage: for K in LegendrianKnotTable:
    ….: f = K.ruling_polynomial()
    ….: if f != 0:
    ….: print K, K.augcat().cardinality(), f(sqrt(2)-1/sqrt(2)) * 2**((K.tb()+1)/2)

    to compare the cardinality for each knot over $\mathbb{F}_2$ to the expected specialization of the ruling polynomial — this runs through the whole Legendrian knot table in about 15-20 minutes on my laptop.

    If $\rho$ is positive, I didn’t do anything further to adjust the answer because we don’t know how to do so. However, based on some experiments I think the following is true in the 2-graded case. We observe that if the 2-graded category is nonempty then $\mathcal{A}(\Lambda)$ is $\mathbb{Z}$-graded, because a 2-graded augmentation implies a 2-graded (i.e. oriented) ruling and Sabloff observed that the latter implies $r(\Lambda)=0$. So even though the category only sees a $\mathbb{Z}/2\mathbb{Z}$-grading, there’s actually an honest $\mathbb{Z}$-grading lurking in the background which the $A_\infty$ structure (the differentials, etc.) might not respect.

    Having said this, we can count isomorphism classes of objects in the 2-graded augmentation category, weighted by the usual alternating product. In this product, the automorphisms we count are only those corresponding to elements with $\mathbb{Z}$-grading 0; we divide this by the count of “homotopies” with $\mathbb{Z}$-grading -1; then we multiply by the ones with $\mathbb{Z}$-grading -2; and so on. Experimentally this sort of count does seem to produce the right number involving the 2-graded ruling polynomial, which is a piece of the HOMFLY polynomial, weighted by $q^{(tb+1)/2}/(q-1)$ as expected.

    Maybe this is not such a strange thing to do after all, since the alternating sum of terms with non-positive $\mathbb{Z}$-grading is closely related to $tb(\Lambda)$ as explained in Vivek’s earlier post. This reduces the 2-graded cardinality (in the same way as for the graded cardinality) to counting augmentations isomorphic to a fixed one.

Leave a Reply

Your email address will not be published. Required fields are marked *