Skip to content

Commit

Permalink
Trac #33969: Implement missing KnotInfo wrappers for polynomial invar…
Browse files Browse the repository at this point in the history
…iants

A couple of wrappers for link properties listed in the
[https://knotinfo.math.indiana.edu/ KnotInfo] and
[https://linkinfo.sitehost.iu.edu/ LinkInfo] databases have already been
implemented in #30352. But since there are more than 120 of them there
are still a lot missing. Here we add missing polynomial link invariants,
explicitly the Conway and Khovanov polynomials.

Furthermore we let Sage point to the current version of
[https://pypi.org/project/database-knotinfo/ database_knotinfo].

URL: https://trac.sagemath.org/33969
Reported by: soehms
Ticket author(s): Sebastian Oehms
Reviewer(s): Matthias Koeppe
  • Loading branch information
Release Manager committed Oct 11, 2022
2 parents a9abd3b + b9b8743 commit 2356379
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 5 deletions.
6 changes: 3 additions & 3 deletions build/pkgs/database_knotinfo/checksums.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tarball=database_knotinfo-VERSION.tar.gz
sha1=187e6b5ee2a935e3a50bc7648b181dfc7cb7bfa2
md5=90822e09a1a84c8dbb84e20773c367f1
cksum=1855405219
sha1=16039d4e399efc78e4b1278527019f4bcdfdde13
md5=3095993756f6b51d14c35adae5a75930
cksum=2884062991
upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz
2 changes: 1 addition & 1 deletion build/pkgs/database_knotinfo/package-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2021.10.1
2022.7.1
60 changes: 59 additions & 1 deletion src/sage/databases/knotinfo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class KnotInfoColumns(Enum):
'PD Notation (vector)',
'PD Notation (KnotTheory)',
'Braid Notation',
'Quasipositive Braid',
'Multivariable Alexander Polynomial',
'HOMFLYPT Polynomial',
'Unoriented',
Expand Down Expand Up @@ -822,6 +823,8 @@ def _test_database(self, **options):
'homfly_polynomial': ['HOMFLY', KnotInfoColumnTypes.OnlyKnots],
'homflypt_polynomial': ['HOMFLYPT Polynomial', KnotInfoColumnTypes.OnlyLinks],
'kauffman_polynomial': ['Kauffman', KnotInfoColumnTypes.KnotsAndLinks],
'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.KnotsAndLinks],
'khovanov_torsion_polynomial': ['Khovanov Torsion', KnotInfoColumnTypes.OnlyKnots],
'determinant': ['Determinant', KnotInfoColumnTypes.KnotsAndLinks],
'positive': ['Positive', KnotInfoColumnTypes.OnlyKnots],
'fibered': ['Fibered', KnotInfoColumnTypes.OnlyKnots],
Expand Down Expand Up @@ -1089,5 +1092,60 @@ def _test_database(self, **options):
'1-3*t+ 3*t^2-3*t^3+ t^4',
'1-3*t+ 5*t^2-3*t^3+ t^4',
'1-t+ t^2-t^3+ t^4-t^5+ t^6',
'3-5*t+ 3*t^2']
'3-5*t+ 3*t^2'],
dc.conway_polynomial: [
'1',
'1+z^2',
'1-z^2',
'1+3*z^2+z^4',
'1+2*z^2',
'1-2*z^2',
'1-z^2-z^4',
'1+z^2+z^4',
'1+6*z^2+5*z^4+z^6',
'1+3*z^2',
'-z',
'z',
'-2*z',
'2*z + z^3',
'z^3',
'z^3',
'-2*z + z^3',
'2*z + 2*z^3',
'-3*z-2*z^3',
'3*z + 2*z^3',
'-3*z-4*z^3-z^5'],
dc.khovanov_polynomial: [
'',
'q^(-9)t^(-3)+q^(-5)t^(-2)+q^(-3)+q^(-1)',
'q^(-5)t^(-2)+q^(-1)t^(-1)+q+q^(-1)+qt+q^5t^2',
'q^(-15)t^(-5)+q^(-11)t^(-4)+q^(-11)t^(-3)+q^(-7)t^(-2)+q^(-5)+q^(-3)',
'q^(-13)t^(-5)+q^(-9)t^(-4)+q^(-9)t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)',
'q^(-9)t^(-4)+q^(-5)t^(-3)+q^(-5)t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+q^(-1)+qt+q^5t^2',
'q^(-11)t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+(q^(-5)+q^(-3))t^(-1)+q^(-3)+2q^(-1)+tq^(-1)+q^3t^2',
'q^(-7)t^(-3)+(q^(-5)+q^(-3))t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+2q^(-1)+t(q+q^3)+(q^3+q^5)t^2+q^7t^3',
'q^(-21)t^(-7)+q^(-17)t^(-6)+q^(-17)t^(-5)+q^(-13)t^(-4)+q^(-13)t^(-3)+q^(-9)t^(-2)+q^(-7)+q^(-5)',
'q^(-17)t^(-7)+q^(-13)t^(-6)+q^(-13)t^(-5)+(q^(-11)+q^(-9))t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)',
'1 + q^(-2) + 1/(q^6*t^2) + 1/(q^4*t^2)',
'1 + q^2 + q^4*t^2 + q^6*t^2',
'1 + q^(-2) + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^6*t^2) + 1/(q^2*t)',
'q^2 + q^4 + q^6*t^2 + q^10*t^3 + q^10*t^4 + q^12*t^4',
'2 + 2/q^2 + 1/(q^8*t^3) + 1/(q^6*t^2) + 1/(q^4*t^2) + 1/(q^2*t) + t + q^4*t^2',
'2 + 2/q^2 + 1/(q^8*t^3) + 1/(q^6*t^2) + 1/(q^4*t^2) + 1/(q^2*t) + t + q^4*t^2',
'1 + 2/q^2 + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^8*t^3) + 2/(q^6*t^2) + 1/(q^4*t^2) + 2/(q^2*t) + t + q^2*t + q^4*t^2',
'q^2 + q^4 + q^4*t + 2*q^6*t^2 + q^8*t^2 + 2*q^10*t^3 + 2*q^10*t^4 + q^12*t^4 + q^12*t^5 + q^14*t^5 + q^16*t^6',
'q^(-4) + q^(-2) + 1/(q^16*t^6) + 1/(q^14*t^6) + 1/(q^14*t^5) + 1/(q^12*t^4) + 1/(q^10*t^4) + 1/(q^10*t^3) + 1/(q^8*t^3) + 1/(q^8*t^2) + 1/(q^6*t^2) + 1/(q^4*t)',
'q^2 + q^4 + q^4*t + q^6*t^2 + q^8*t^2 + q^8*t^3 + q^10*t^3 + q^10*t^4 + q^12*t^4 + q^14*t^5 + q^14*t^6 + q^16*t^6',
'q^(-6) + q^(-4) + 1/(q^18*t^6) + 1/(q^16*t^6) + 1/(q^16*t^5) + 1/(q^12*t^4) + 1/(q^12*t^3) + 1/(q^8*t^2)'],
dc.khovanov_torsion_polynomial: [
'',
'Q^(-7)t^(-2)',
'Q^(-3)t^(-1)+Q^3t^2',
'Q^(-13)t^(-4)+Q^(-9)t^(-2)',
'Q^(-11)t^(-4)+Q^(-7)t^(-2)+Q^(-5)t^(-1)',
'Q^(-7)t^(-3)+Q^(-3)t^(-1)+Q^(-1)+Q^3t^2',
'Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)+Q^(-3)+Qt^2',
'Q^(-5)t^(-2)+Q^(-3)t^(-1)+Q^(-1)+Qt+Q^3t^2+Q^5t^3',
'Q^(-19)t^(-6)+Q^(-15)t^(-4)+Q^(-11)t^(-2)',
'Q^(-15)t^(-6)+Q^(-11)t^(-4)+Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)']
}
156 changes: 156 additions & 0 deletions src/sage/knots/knotinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
AUTHORS:
- Sebastian Oehms August 2020: initial version
- Sebastian Oehms June 2022: add :meth:`conway_polynomial` and :meth:`khovanov_polynomial` (:trac:`33969`)
Thanks to Chuck Livingston and Allison Moore for their support. For further acknowledgments see the correspondig hompages.
"""
Expand Down Expand Up @@ -1620,6 +1621,161 @@ def alexander_polynomial(self, var='t', original=False, laurent_poly=False):
exp = ap.exponents()
return t ** ((-max(exp) - min(exp)) // 2) * ap

@cached_method
def conway_polynomial(self, var='t', original=False):
r"""
Return the Conway polynomial according to the value of column
``conway_polynomial`` for this knot or link as an instance of
:class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
It is obtained from the Seifert matrix `V` of ``self`` by the following
formula (see the KnotInfo description web-page; to launch it see the
example below):
.. MATH::
\nabla(L) = \det(t^{\frac{1}{2}} V -t^{\frac{-1}{2}} V^t)
Here `V^t` stands for the transpose of `V`.
INPUT:
- ``var`` -- (default: ``'t'``) the variable
- ``original`` -- boolean (optional, default ``False``) if set to
``True`` the original table entry is returned as a string
OUTPUT:
A polynomial over the integers, more precisely an instance of
:class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
If ``original`` is set to ``True`` then a string is returned.
EXAMPLES::
sage: from sage.knots.knotinfo import KnotInfo
sage: K = KnotInfo.K4_1
sage: Kc = K.conway_polynomial(); Kc
-t^2 + 1
sage: L = KnotInfo.L5a1_0
sage: Lc = L.conway_polynomial(); Lc
t^3
Comparision to Sage's results::
sage: Kc == K.link().conway_polynomial()
True
sage: Lc == L.link().conway_polynomial()
True
Launch the KnotInfo description web-page::
sage: K.items.conway_polynomial.description_webpage() # not tested
True
"""
conway_polynomial = self[self.items.conway_polynomial]

if original:
return conway_polynomial

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
R = PolynomialRing(ZZ, var)

if not conway_polynomial and self.crossing_number() == 0:
return R.one()

t, = R.gens()
lc = {'z': t}
return R(eval_knotinfo(conway_polynomial, locals=lc))

@cached_method
def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False):
r"""
Return the Khovanov polynomial according to the value of column
``khovanov_polynomial`` for this knot or link as an instance of
:class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
INPUT:
- ``var1`` -- (default: ``'q'``) the first variable
- ``var2`` -- (default: ``'t'``) the second variable
- ``base_ring`` -- (default: ``ZZ``) the ring of the polynomial's
coefficients
- ``original`` -- boolean (optional, default ``False``) if set to
``True`` the original table entry is returned as a string
OUTPUT:
A Laurent polynomial over the integers, more precisely an instance of
:class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
If ``original`` is set to ``True`` then a string is returned.
.. NOTE ::
The Khovanov polynomial given in KnotInfo corresponds to the mirror
image of the given knot for a `list of 140 exceptions
<https://raw.githubusercontent.com/soehms/database_knotinfo/main/hints/list_of_mirrored_khovanov_polynonmial.txt>`__.
EXAMPLES::
sage: from sage.knots.knotinfo import KnotInfo
sage: K = KnotInfo.K6_3
sage: Kk = K.khovanov_polynomial(); Kk
q^7*t^3 + q^5*t^2 + q^3*t^2 + q^3*t + q*t + 2*q + 2*q^-1 + q^-1*t^-1
+ q^-3*t^-1 + q^-3*t^-2 + q^-5*t^-2 + q^-7*t^-3
sage: Kk2 = K.khovanov_polynomial(var1='p', base_ring=GF(2)); Kk2
p^7*t^3 + p^5*t^3 + p^5*t^2 + p^3*t + p^-1 + p^-1*t^-1 + p^-3*t^-2 + p^-7*t^-3
sage: L = KnotInfo.L5a1_0
sage: Lk = L.khovanov_polynomial(); Lk
q^4*t^2 + t + 2 + 2*q^-2 + q^-2*t^-1 + q^-4*t^-2 + q^-6*t^-2 + q^-8*t^-3
Comparision to Sage's results::
sage: Kk == K.link().khovanov_polynomial()
True
sage: Kk2 == K.link().khovanov_polynomial(var1='p', base_ring=GF(2))
True
sage: Lk == L.link().khovanov_polynomial()
True
"""
khovanov_polynomial = self[self.items.khovanov_polynomial]

if original:
return khovanov_polynomial

from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
var_names = [var1, var2]
R = LaurentPolynomialRing(base_ring, var_names)

if not khovanov_polynomial and self.crossing_number() == 0:
return R({(1, 0): 1, (-1, 0): 1})

ch = base_ring.characteristic()
if ch == 2:
if not self.is_knot():
raise NotImplementedError('Khovanov polynomial available only for knots in characteristic 2')
khovanov_torsion_polynomial = self[self.items.khovanov_torsion_polynomial]
khovanov_torsion_polynomial = khovanov_torsion_polynomial.replace('Q', 'q')
khovanov_polynomial = '%s + %s' % (khovanov_polynomial, khovanov_torsion_polynomial)

if not khovanov_polynomial:
# given just for links with less than 12 crossings
raise NotImplementedError('Khovanov polynomial not available for this link')

from sage.repl.preparse import implicit_mul
# since implicit_mul does not know about the choice of variable names
# we have to insert * between them separately
for i in ['q', 't',')']:
for j in ['q', 't', '(']:
khovanov_polynomial = khovanov_polynomial.replace('%s%s' % (i, j), '%s*%s' % (i, j))
khovanov_polynomial = implicit_mul(khovanov_polynomial)
gens = R.gens_dict()
lc = {}
lc['q'] = gens[var1]
lc['t'] = gens[var2]

return R(eval_knotinfo(khovanov_polynomial, locals=lc))

@cached_method
def link(self, use_item=db.columns().pd_notation, snappy=False):
Expand Down

0 comments on commit 2356379

Please sign in to comment.