passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
sage/knots/knotinfo.py ADDED
@@ -0,0 +1,2900 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs sage.groups
3
+ r"""
4
+ Access to the KnotInfo database
5
+
6
+ This module contains the class :class:`KnotInfoBase` which is derived from
7
+ :class:`Enum` and provides knots and links listed in the databases at the
8
+ web-pages `KnotInfo <https://knotinfo.math.indiana.edu/>`__
9
+ and `LinkInfo <https://linkinfo.sitehost.iu.edu/>`__ as its items.
10
+
11
+ This interface contains a set of about twenty knots and links statically as
12
+ demonstration cases. The complete database can be installed as an optional Sage
13
+ package using
14
+
15
+ - ``sage -i database_knotinfo`` (does not install if the current version is present)
16
+ - ``sage -f database_knotinfo`` (installs even if the current version is present)
17
+
18
+ This will install a `Python wrapper <https://github.com/soehms/database_knotinfo#readme>`__
19
+ for the original databases in Sage. This wrapper performs an automatic progress
20
+ of version numbers. For more details and further install instructions please see
21
+ the corresponding web-page.
22
+
23
+ To perform all the doctests concerning the usage of the database on the installation
24
+ add the option ``-c``. In this case (for instance ``sage -f -c database_knotinfo``)
25
+ the installation breaks on failing tests.
26
+
27
+ The installation of the complete database will be necessary in order to have
28
+ access to all the properties recorded in the databases, as well.
29
+
30
+ If the entire database is installed as explained above, the import instructions
31
+ for :class:`KnotInfo` and :class:`KnotInfoSeries`, which can be seen in the opening
32
+ lines of the examples, are unnecessary.
33
+
34
+ Be aware that there are a couple of conventions used differently on KnotInfo as
35
+ in Sage.
36
+
37
+ For different conventions regarding normalization of the polynomial invariants see
38
+ the according documentation of :meth:`KnotInfoBase.homfly_polynomial`,
39
+ :meth:`KnotInfoBase.jones_polynomial` and :meth:`KnotInfoBase.alexander_polynomial`.
40
+
41
+ Also, note that the braid notation is used according to Sage, even thought in
42
+ the source where it is taken from, the braid generators are assumed to have a
43
+ negative crossing which would be opposite to the convention in Sage (see definition
44
+ 3 of
45
+ :arxiv:`Gittings, T., "Minimum Braids: A Complete Invariant of Knots and Links" <math/0401051>`).
46
+
47
+ Furthermore, note that not all columns available in the database are visible on the web
48
+ pages. It is planned to remove non-visible columns from the database in the future (see
49
+ the `Python Wrapper <https://github.com/soehms/database_knotinfo#readme>`__ for
50
+ updated information).
51
+
52
+ EXAMPLES::
53
+
54
+ sage: L = KnotInfo.L4a1_0
55
+ sage: L.pd_notation()
56
+ [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]]
57
+ sage: L.pd_notation(original=True)
58
+ '{{6, 1, 7, 2}, {8, 3, 5, 4}, {2, 5, 3, 6}, {4, 7, 1, 8}}'
59
+ sage: L.is_knot()
60
+ False
61
+ sage: L.num_components()
62
+ 2
63
+
64
+ Items for knots need a leading ``K`` for technical reason::
65
+
66
+ sage: K = KnotInfo.K4_1
67
+ sage: K.is_knot()
68
+ True
69
+
70
+ Injecting the variable name into the namespace::
71
+
72
+ sage: KnotInfo.K5_1.inject()
73
+ Defining K5_1
74
+ sage: K5_1.dt_notation()
75
+ [6, 8, 10, 2, 4]
76
+
77
+ Defining a link from the original name string::
78
+
79
+ sage: KnotInfo('L6a1{1}').inject()
80
+ Defining L6a1_1
81
+ sage: L6a1_1.is_alternating()
82
+ True
83
+
84
+ Obtaining an instance of :class:`~sage.groups.braid.Braid`::
85
+
86
+ sage: L.braid()
87
+ s1^-2*s0^-1*s1*s0^-1
88
+ sage: type(_)
89
+ <class 'sage.groups.braid.BraidGroup_class_with_category.element_class'>
90
+
91
+ Obtaining an instance of :class:`Link`::
92
+
93
+ sage: l = L.link(); l
94
+ Link with 2 components represented by 4 crossings
95
+ sage: type(l)
96
+ <class 'sage.knots.link.Link'>
97
+
98
+ If you have `SnapPy <https://snappy.math.uic.edu/index.html>`__ installed inside
99
+ Sage, you can obtain an instance of :class:`~spherogram.links.links_base.Link`,
100
+ too::
101
+
102
+ sage: # optional - snappy
103
+ sage: L6 = KnotInfo.L6a1_0
104
+ sage: l6s = L6.link(snappy=True); l6s
105
+ ...
106
+ <Link: 2 comp; 6 cross>
107
+ sage: type(l6s)
108
+ <class 'spherogram.links.invariants.Link'>
109
+ sage: l6 = L6.link()
110
+ sage: l6 == l6s.sage_link()
111
+ True
112
+ sage: L6.link(L6.items.name, snappy=True)
113
+ <Link L6a1: 2 comp; 6 cross>
114
+ sage: l6sn = _
115
+ sage: l6s == l6sn
116
+ False
117
+ sage: l6m = l6.mirror_image()
118
+ sage: l6sn.sage_link().is_isotopic(l6m)
119
+ True
120
+
121
+ But observe that the name conversion to SnapPy does not distinguish orientation
122
+ types::
123
+
124
+ sage: L6b = KnotInfo.L6a1_1
125
+ sage: L6b.link(L6b.items.name, snappy=True) # optional - snappy
126
+ <Link L6a1: 2 comp; 6 cross>
127
+ sage: _.PD_code() == l6sn.PD_code() # optional - snappy
128
+ True
129
+
130
+ Obtaining the HOMFLY-PT polynomial::
131
+
132
+ sage: L.homfly_polynomial()
133
+ -v^-1*z - v^-3*z - v^-3*z^-1 + v^-5*z^-1
134
+ sage: _ == l.homfly_polynomial(normalization='vz')
135
+ True
136
+
137
+
138
+ Obtaining the original string from the database for an arbitrary property::
139
+
140
+ sage: K[K.items.classical_conway_name] # optional - database_knotinfo
141
+ '4_1'
142
+
143
+ Further methods::
144
+
145
+ sage: K.crossing_number()
146
+ 4
147
+ sage: K.gauss_notation()
148
+ [-1, 2, -3, 1, -4, 3, -2, 4]
149
+ sage: K.dt_notation()
150
+ [4, 6, 8, 2]
151
+ sage: K.determinant()
152
+ 5
153
+ sage: K.symmetry_type()
154
+ 'fully amphicheiral'
155
+ sage: _ == K[K.items.symmetry_type]
156
+ True
157
+ sage: K.is_reversible()
158
+ True
159
+ sage: K.is_amphicheiral()
160
+ True
161
+ sage: K.jones_polynomial() # needs sage.symbolic
162
+ t^2 - t - 1/t + 1/t^2 + 1
163
+ sage: K.kauffman_polynomial()
164
+ a^2*z^2 + a*z^3 - a^2 - a*z + 2*z^2 + a^-1*z^3 - 1 - a^-1*z + a^-2*z^2 - a^-2
165
+ sage: K.alexander_polynomial()
166
+ t^2 - 3*t + 1
167
+
168
+ Using the ``column_type`` of a property::
169
+
170
+ sage: def select_column(i):
171
+ ....: return i.column_type() != i.types.OnlyLinks and K[i] == 'Y'
172
+ sage: [i.column_name() for i in K.items if select_column(i)] # optional - database_knotinfo
173
+ ['Alternating', 'Fibered', 'Quasialternating', 'Adequate']
174
+
175
+ You can launch web-pages attached to the links::
176
+
177
+ sage: # not tested
178
+ sage: K.diagram()
179
+ True
180
+ sage: L.diagram(single=True)
181
+ True
182
+ sage: L.knot_atlas_webpage()
183
+ True
184
+ sage: K.knotilus_webpage()
185
+ True
186
+
187
+ and the description web-pages of the properties::
188
+
189
+ sage: K.items.positive.description_webpage() # not tested
190
+ True
191
+
192
+ To see all the properties available in this interface you can use "tab-completion".
193
+ For example type ``K.items.`` and than hit the :kbd:`Tab` key. You can select the item
194
+ you want from the list. If you know some first letters type them first to obtain a
195
+ reduced selection list.
196
+
197
+ In a similar way you may select the knots and links. Here you have to type ``KnotInfo.``
198
+ or ``KnotInfo.L7`` before stroking the :kbd:`Tab` key. In the latter case the selection list
199
+ will be reduced to proper links with 7 crossings.
200
+
201
+ Finally there is a method :meth:`Link.get_knotinfo` of class :class:`Link` to find an instance
202
+ in the KnotInfo database::
203
+
204
+ sage: L = Link([[3,1,2,4], [8,9,1,7], [5,6,7,3], [4,18,6,5],
205
+ ....: [17,19,8,18], [9,10,11,14], [10,12,13,11],
206
+ ....: [12,19,15,13], [20,16,14,15], [16,20,17,2]])
207
+ sage: L.get_knotinfo()
208
+ KnotInfo['K0_1']
209
+
210
+
211
+ REFERENCES:
212
+
213
+ - `KnotInfo <https://knotinfo.math.indiana.edu/>`__
214
+ - `LinkInfo <https://linkinfo.sitehost.iu.edu/>`__
215
+
216
+
217
+ AUTHORS:
218
+
219
+ - Sebastian Oehms August 2020: initial version
220
+ - Sebastian Oehms June 2022: add :meth:`conway_polynomial` and :meth:`khovanov_polynomial` (:issue:`33969`)
221
+
222
+ Thanks to Chuck Livingston and Allison Moore for their support. For further acknowledgments see the corresponding hompages.
223
+ """
224
+
225
+
226
+ ##############################################################################
227
+ # Copyright (C) 2020 Sebastian Oehms <seb.oehms@gmail.com>
228
+ #
229
+ # This program is free software: you can redistribute it and/or modify
230
+ # it under the terms of the GNU General Public License as published by
231
+ # the Free Software Foundation, either version 2 of the License, or
232
+ # (at your option) any later version.
233
+ # https://www.gnu.org/licenses/
234
+ ##############################################################################
235
+
236
+
237
+ from enum import Enum
238
+ from sage.misc.cachefunc import cached_method
239
+ from sage.misc.lazy_import import lazy_import
240
+ from sage.misc.sage_eval import sage_eval
241
+ from sage.structure.sage_object import SageObject
242
+ from sage.structure.unique_representation import UniqueRepresentation
243
+ from sage.rings.integer_ring import ZZ
244
+ from sage.knots.knot import Knots
245
+ from sage.databases.knotinfo_db import KnotInfoColumns, db
246
+
247
+ lazy_import('sage.groups.braid', 'BraidGroup')
248
+
249
+
250
+ def eval_knotinfo(string, locals={}, to_tuple=True):
251
+ r"""
252
+ Preparse a string from the KnotInfo database and evaluate it by ``sage_eval``.
253
+
254
+ INPUT:
255
+
256
+ - ``string`` -- string that gives a value of some database entry
257
+ - ``locals`` -- dictionary of locals passed to ``sage_eval``
258
+
259
+ EXAMPLES::
260
+
261
+ sage: from sage.knots.knotinfo import eval_knotinfo
262
+ sage: L = KnotInfo.L4a1_0
263
+ sage: L.braid_notation(original=True)
264
+ '{3, {-2, -2, -1, 2, -1}}'
265
+ sage: eval_knotinfo(_)
266
+ (3, (-2, -2, -1, 2, -1))
267
+ sage: KnotInfo.K13a_1.kauffman_polynomial() # optional - database_knotinfo # indirect doctest
268
+ Traceback (most recent call last):
269
+ ...
270
+ NotImplementedError: this value is not provided by the database
271
+ """
272
+ if not string:
273
+ # An empty string in the database Excel spreadsheet indicates that
274
+ # the property is not provided for that particular knot or link.
275
+ raise NotImplementedError('this value is not provided by the database')
276
+ if to_tuple:
277
+ new_string = string.replace('{', '(')
278
+ new_string = new_string.replace('}', ')')
279
+ else:
280
+ new_string = string.replace('{', '[')
281
+ new_string = new_string.replace('}', ']')
282
+ new_string = new_string.replace(';', ',')
283
+ return sage_eval(new_string, locals=locals)
284
+
285
+
286
+ def knotinfo_int(string):
287
+ r"""
288
+ Preparse a string from the KnotInfo database representing an integer.
289
+
290
+ INPUT:
291
+
292
+ - ``string`` -- string that gives a value of some database entry
293
+
294
+ EXAMPLES::
295
+
296
+ sage: from sage.knots.knotinfo import knotinfo_int
297
+ sage: knotinfo_int('7')
298
+ 7
299
+ sage: KnotInfo.K13a_1.braid_index() # optional - database_knotinfo # indirect doctest
300
+ Traceback (most recent call last):
301
+ ...
302
+ NotImplementedError: this integer is not provided by the database
303
+ """
304
+ if not string:
305
+ # an empty string in the Excel sheet of the database indicates that
306
+ # the property is not provided for this special knot or link.
307
+ raise NotImplementedError('this integer is not provided by the database')
308
+ else:
309
+ return int(string)
310
+
311
+
312
+ def knotinfo_bool(string):
313
+ r"""
314
+ Preparse a string from the KnotInfo database representing a boolean.
315
+
316
+ INPUT:
317
+
318
+ - ``string`` -- string that gives a value of some database entry
319
+
320
+ EXAMPLES::
321
+
322
+ sage: from sage.knots.knotinfo import knotinfo_bool
323
+ sage: knotinfo_bool('Y')
324
+ True
325
+ sage: KnotInfo.K13a_1.is_almost_alternating() # optional - database_knotinfo # indirect doctest
326
+ Traceback (most recent call last):
327
+ ...
328
+ NotImplementedError: this boolean is not provided by the database
329
+ """
330
+ if not string:
331
+ # an empty string in the Excel sheet of the database indicates that
332
+ # the property is not provided for this special knot or link.
333
+ raise NotImplementedError('this boolean is not provided by the database')
334
+ if string == 'Y':
335
+ return True
336
+ elif string == 'N':
337
+ return False
338
+ raise ValueError('%s is not a KnotInfo boolean')
339
+
340
+
341
+ class SymmetryMutant(Enum):
342
+ r"""
343
+ Enum to specify the symmetry mutant link of the prime link listed in the
344
+ KnotInfo and LinkInfo databases. From the KnotInfo description page:
345
+
346
+ If a knot is viewed as the oriented diffeomorphism
347
+ class of an oriented pair, `K = (S_3, S_1)`, with `S_i`
348
+ diffeomorphic to `S^i`, there are four oriented knots
349
+ associated to any particular knot `K`. In addition to
350
+ `K` itself, there is the reverse, `K^r = (S_3, -S_1)`,
351
+ the concordance inverse, `-K = (-S_3, -S_1)`, and the
352
+ mirror image, `K^m = (-S_3, S_1)`.
353
+ """
354
+ itself = 's'
355
+ reverse = 'r'
356
+ concordance_inverse = 'c'
357
+ mirror_image = 'm'
358
+ mixed = 'x' # to be used in connection with KnotInfoSeries
359
+ unknown = '?'
360
+
361
+ def __gt__(self, other):
362
+ r"""
363
+ Implement comparison of different items in order to have ``sorted`` work.
364
+
365
+ EXAMPLES::
366
+
367
+ sage: from sage.knots.knotinfo import SymmetryMutant
368
+ sage: sorted(SymmetryMutant) # indirect doctest
369
+ [<SymmetryMutant.mixed: 'x'>,
370
+ <SymmetryMutant.itself: 's'>,
371
+ <SymmetryMutant.reverse: 'r'>,
372
+ <SymmetryMutant.mirror_image: 'm'>,
373
+ <SymmetryMutant.concordance_inverse: 'c'>,
374
+ <SymmetryMutant.unknown: '?'>]
375
+ """
376
+ # We use the reversal of the alphabetical order of the values so that
377
+ # `itself` occurs before the mirrored cases
378
+ return self.value < other.value
379
+
380
+ def rev(self):
381
+ r"""
382
+ Return the reverse of ``self``.
383
+
384
+ EXAMPLES::
385
+
386
+ sage: from sage.knots.knotinfo import SymmetryMutant
387
+ sage: all( sym.rev().rev() == sym for sym in SymmetryMutant)
388
+ True
389
+ """
390
+ if self is SymmetryMutant.itself:
391
+ return SymmetryMutant.reverse
392
+ elif self is SymmetryMutant.reverse:
393
+ return SymmetryMutant.itself
394
+ elif self is SymmetryMutant.mirror_image:
395
+ return SymmetryMutant.concordance_inverse
396
+ elif self is SymmetryMutant.concordance_inverse:
397
+ return SymmetryMutant.mirror_image
398
+ return self
399
+
400
+ def mir(self):
401
+ r"""
402
+ Return the mirror image of ``self``.
403
+
404
+ EXAMPLES::
405
+
406
+ sage: from sage.knots.knotinfo import SymmetryMutant
407
+ sage: all( sym.mir().mir() == sym for sym in SymmetryMutant)
408
+ True
409
+ """
410
+ if self is SymmetryMutant.itself:
411
+ return SymmetryMutant.mirror_image
412
+ elif self is SymmetryMutant.reverse:
413
+ return SymmetryMutant.concordance_inverse
414
+ elif self is SymmetryMutant.mirror_image:
415
+ return SymmetryMutant.itself
416
+ elif self is SymmetryMutant.concordance_inverse:
417
+ return SymmetryMutant.reverse
418
+ return self
419
+
420
+ def matches(self, link):
421
+ r"""
422
+ Return the list of other symmetry mutants that give isotopic links
423
+ with respect to ``link`` and ``self``. For ``self`` is
424
+ ``SymmetryMutant.unknown`` a boolean is returned which is ``True``
425
+ if the chirality of ``link`` is unknown.
426
+
427
+ EXAMPLES::
428
+
429
+ sage: from sage.knots.knotinfo import SymmetryMutant
430
+ sage: SymmetryMutant.itself.matches(KnotInfo.K6_1)
431
+ [<SymmetryMutant.reverse: 'r'>]
432
+ sage: SymmetryMutant.mirror_image.matches(KnotInfo.K6_1)
433
+ [<SymmetryMutant.concordance_inverse: 'c'>]
434
+ """
435
+ rev = link.is_reversible()
436
+ achp = link.is_amphicheiral(positive=True)
437
+ ach = link.is_amphicheiral()
438
+ if self is SymmetryMutant.unknown:
439
+ if rev is None or ach is None or achp is None:
440
+ return True
441
+ else:
442
+ return False
443
+ res = []
444
+ if rev:
445
+ res.append(self.rev())
446
+ if achp:
447
+ res.append(self.mir())
448
+ if ach:
449
+ res.append(self.rev().mir())
450
+ return res
451
+
452
+ def is_minimal(self, link) -> bool:
453
+ r"""
454
+ Return whether ``self`` is minimal among its matching mutants.
455
+
456
+ EXAMPLES::
457
+
458
+ sage: from sage.knots.knotinfo import SymmetryMutant
459
+ sage: SymmetryMutant.itself.is_minimal(KnotInfo.K6_1)
460
+ True
461
+ sage: SymmetryMutant.concordance_inverse.is_minimal(KnotInfo.K6_1)
462
+ False
463
+ """
464
+ if self in [SymmetryMutant.unknown, SymmetryMutant.mixed]:
465
+ return False
466
+ matches = self.matches(link)
467
+ return all(self < other for other in matches)
468
+
469
+
470
+ # ---------------------------------------------------------------------------------
471
+ # KnotInfoBase
472
+ # ---------------------------------------------------------------------------------
473
+ class KnotInfoBase(Enum):
474
+ r"""
475
+ Enum class to select the knots and links listed in the databases at the web-pages
476
+ `KnotInfo <https://knotinfo.math.indiana.edu/>`__ and `LinkInfo <https://linkinfo.sitehost.iu.edu/>`__.
477
+
478
+ EXAMPLES::
479
+
480
+ sage: [knot.name for knot in KnotInfo if knot.crossing_number() < 5]
481
+ ['K0_1', 'K3_1', 'K4_1', 'L2a1_0', 'L2a1_1', 'L4a1_0', 'L4a1_1']
482
+
483
+ More examples and information can be seen in the module header
484
+ :mod:`~sage.knots.knotinfo` (by typing)::
485
+
486
+ sage: import sage.knots.knotinfo # not tested
487
+ sage: sage.knots.knotinfo? # not tested
488
+
489
+ TESTS:
490
+
491
+ sage: KnotInfo.K7_1.inject()
492
+ Defining K7_1
493
+ sage: TestSuite(K7_1).run()
494
+ """
495
+
496
+ def __gt__(self, other):
497
+ r"""
498
+ Implement comparison of different items in order to have ``sorted`` work.
499
+
500
+ EXAMPLES::
501
+
502
+ sage: KnotInfo.L4a1_0 < KnotInfo.L4a1_1 # indirect doctest
503
+ True
504
+ sage: KnotInfo.L2a1_0 < KnotInfo.K3_1 # indirect doctest
505
+ False
506
+ sage: KnotInfo.K10_3 > KnotInfo.K3_1 # optional - database_knotinfo
507
+ True
508
+ """
509
+ if self.__class__ is other.__class__:
510
+ tups = (not self.is_knot(), self.crossing_number(), self.value)
511
+ tupo = (not other.is_knot(), other.crossing_number(), other.value)
512
+ return tups > tupo
513
+ return NotImplemented
514
+
515
+ @property
516
+ def items(self):
517
+ r"""
518
+ Return an Enum class to select a column item of the KnotInfo database.
519
+
520
+ EXAMPLES::
521
+
522
+ sage: L = KnotInfo.L4a1_0
523
+ sage: it = L.items
524
+ sage: [i.name for i in it if i.name.startswith('braid')]
525
+ ['braid_index', 'braid_length', 'braid_notation', 'braid_notation_old']
526
+ sage: L.items.dt_notation.column_name()
527
+ 'DT Notation'
528
+
529
+ To check if the item is available for proper links or only knots type::
530
+
531
+ sage: it.gauss_notation.column_type()
532
+ <KnotInfoColumnTypes.KnotsAndLinks: 'B'>
533
+ sage: it.dt_notation.column_type()
534
+ <KnotInfoColumnTypes.OnlyKnots: 'K'>
535
+
536
+ To see the description of the item in your web browser type::
537
+
538
+ sage: it.gauss_notation.description_webpage() # not tested
539
+ True
540
+ """
541
+ return db.columns()
542
+
543
+ @cached_method
544
+ def __getitem__(self, item):
545
+ r"""
546
+ EXAMPLES::
547
+
548
+ sage: L = KnotInfo.L4a1_0
549
+ sage: L[L.items.alternating]
550
+ 'Y'
551
+ sage: L[L.items.arc_notation]
552
+ '{{6, 4}, {3, 5}, {4, 2}, {1, 3}, {2, 6}, {5, 1}}'
553
+ sage: L[L.items.braid_notation]
554
+ '{3, {-2, -2, -1, 2, -1}}'
555
+ sage: L[0]
556
+ Traceback (most recent call last):
557
+ ...
558
+ KeyError: "item must be an instance of <enum 'KnotInfoColumns'>"
559
+ """
560
+ if not isinstance(item, KnotInfoColumns):
561
+ raise KeyError('item must be an instance of %s' % (KnotInfoColumns))
562
+ if item.column_type() == item.types.OnlyLinks and self.is_knot():
563
+ raise KeyError('item not available for knots' % (KnotInfoColumns))
564
+ if item.column_type() == item.types.OnlyKnots and not self.is_knot():
565
+ raise KeyError('item not available for links' % (KnotInfoColumns))
566
+
567
+ l = db.read(item)
568
+ ind = db.read_row_dict()[self.name][0]
569
+ offset = 0
570
+ if item.column_type() == item.types.OnlyLinks:
571
+ offset = self._offset_knots()
572
+
573
+ return l[ind - offset]
574
+
575
+ def _offset_knots(self):
576
+ r"""
577
+ Return the list index of the first proper link in a combined
578
+ list containing knots and proper links together which is the
579
+ case for columns used for KnotInfo and LinkInfo in common.
580
+ This index is exactly the total number of knots recorded
581
+ in KnotInfo.
582
+
583
+ EXAMPLES::
584
+
585
+ sage: L = KnotInfo.L4a1_0
586
+ sage: L._offset_knots() # optional - database_knotinfo
587
+ 12966
588
+ """
589
+ return db.read_num_knots()
590
+
591
+ @cached_method
592
+ def _braid_group(self):
593
+ r"""
594
+ Return the braid group corresponding to the braid index
595
+ of ``self``.
596
+
597
+ EXAMPLES::
598
+
599
+ sage: L = KnotInfo.L4a1_0
600
+ sage: L._braid_group()
601
+ Braid group on 3 strands
602
+ """
603
+ try:
604
+ n = self.braid_index()
605
+ except NotImplementedError:
606
+ bn = self.braid_notation()
607
+ n = max(abs(i) for i in bn) + 1
608
+
609
+ if n == 1:
610
+ return BraidGroup(2)
611
+ else:
612
+ return BraidGroup(n)
613
+
614
+ @cached_method
615
+ def _homfly_pol_ring(self, var1, var2):
616
+ r"""
617
+ Return the parent Laurent polynomial ring for the HOMFLY-PT
618
+ polynomial according to Sage's internal one.
619
+
620
+ EXAMPLES::
621
+
622
+ sage: L = KnotInfo.L4a1_1
623
+ sage: L._homfly_pol_ring('u', 'v')
624
+ Multivariate Laurent Polynomial Ring in u, v over Integer Ring
625
+ """
626
+ K3_1 = Knots().from_table(3, 1)
627
+ return K3_1.homfly_polynomial(var1=var1, var2=var2).parent()
628
+
629
+ @cached_method
630
+ def pd_notation(self, original=False):
631
+ r"""
632
+ Return the value of column ``pd_notation`` for this
633
+ link as a Python list of Python lists. For more information
634
+ type ``KnotInfo.K0_1.items.pd_notation.description_webpage()``.
635
+
636
+ INPUT:
637
+
638
+ - ``original`` -- boolean (default: ``False``); if set to
639
+ ``True`` the original table entry is returned as a string
640
+
641
+ OUTPUT:
642
+
643
+ Python list of python lists each entry of the outer list
644
+ representing a crossing.
645
+
646
+ EXAMPLES::
647
+
648
+ sage: L = KnotInfo.L4a1_0
649
+ sage: L.pd_notation()
650
+ [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]]
651
+ sage: L.pd_notation(original=True)
652
+ '{{6, 1, 7, 2}, {8, 3, 5, 4}, {2, 5, 3, 6}, {4, 7, 1, 8}}'
653
+ sage: K = KnotInfo.K4_1
654
+ sage: K.pd_notation()
655
+ [[4, 2, 5, 1], [8, 6, 1, 5], [6, 3, 7, 4], [2, 7, 3, 8]]
656
+ """
657
+ if self.is_knot():
658
+ pd_notation = self[self.items.pd_notation]
659
+ else:
660
+ pd_notation = self[self.items.pd_notation_vector]
661
+
662
+ if original:
663
+ return pd_notation
664
+
665
+ if not pd_notation:
666
+ # don't forget the unknot
667
+ return []
668
+
669
+ return eval_knotinfo(pd_notation, to_tuple=False)
670
+
671
+ @cached_method
672
+ def dt_notation(self, original=False):
673
+ r"""
674
+ Return the value of column ``dt_notation`` for this
675
+ link as a Python list of Python lists. For more information
676
+ type ``KnotInfo.K0_1.items.dt_notation.description_webpage()``.
677
+
678
+ INPUT:
679
+
680
+ - ``original`` -- boolean (default: ``False``); if set to
681
+ ``True`` the original table entry is returned as a string
682
+
683
+ OUTPUT:
684
+
685
+ Python list of python lists each entry of the outer list
686
+ representing a crossing.
687
+
688
+ EXAMPLES::
689
+
690
+ sage: L = KnotInfo.L4a1_0
691
+ sage: L.dt_notation()
692
+ [[6, 8], [2, 4]]
693
+ sage: L.dt_notation(original=True)
694
+ '[{6, 8}, {2, 4}]'
695
+ sage: L = KnotInfo.L4a1_0
696
+ sage: K = KnotInfo.K4_1
697
+ sage: K.dt_notation()
698
+ [4, 6, 8, 2]
699
+ """
700
+ if self.is_knot():
701
+ dt_notation = self[self.items.dt_notation]
702
+ else:
703
+ dt_notation = self[self.items.dt_code]
704
+
705
+ if original:
706
+ return dt_notation
707
+
708
+ if not dt_notation:
709
+ # don't forget the unknot
710
+ return []
711
+
712
+ return eval_knotinfo(dt_notation, to_tuple=False)
713
+
714
+ @cached_method
715
+ def gauss_notation(self, original=False):
716
+ r"""
717
+ Return the value of column ``gauss_notation`` for this
718
+ link as a Python list of Python lists. For more information
719
+ type ``KnotInfo.K0_1.items.gauss_notation.description_webpage()``.
720
+
721
+ INPUT:
722
+
723
+ - ``original`` -- boolean (default: ``False``); if set to
724
+ ``True`` the original table entry is returned as a string
725
+
726
+ EXAMPLES::
727
+
728
+ sage: L = KnotInfo.L4a1_0
729
+ sage: L.gauss_notation()
730
+ [[1, -3, 2, -4], [3, -1, 4, -2]]
731
+ sage: L.gauss_notation(original=True)
732
+ '{{1, -3, 2, -4}, {3, -1, 4, -2}}'
733
+ """
734
+ gauss_notation = self[self.items.gauss_notation]
735
+ if original:
736
+ return gauss_notation
737
+
738
+ if not gauss_notation:
739
+ # don't forget the unknot
740
+ return []
741
+
742
+ return eval_knotinfo(gauss_notation, to_tuple=False)
743
+
744
+ @cached_method
745
+ def braid_notation(self, original=False):
746
+ r"""
747
+ Return the value of column ``braid_notation`` for this
748
+ link as a Python tuple (Tietze form). For more information
749
+ type ``KnotInfo.K0_1.items.braid_notation.description_webpage()``.
750
+
751
+ INPUT:
752
+
753
+ - ``original`` -- boolean (default: ``False``); if set to
754
+ ``True`` the original table entry is returned as a string
755
+
756
+ OUTPUT:
757
+
758
+ Python tuple representing the braid whose closure is ``self``
759
+ in Tietze form.
760
+
761
+ ..NOTE::
762
+
763
+ There has been a major change to braid representatives for
764
+ proper links since version 2021.10.1. The former braid
765
+ representatives can be obtained by the column
766
+ ``braid_notation_old`` (see the final example below).
767
+
768
+ EXAMPLES::
769
+
770
+ sage: L = KnotInfo.L4a1_0
771
+ sage: L.braid_notation()
772
+ (-2, -2, -1, 2, -1)
773
+ sage: L.braid_notation(original=True)
774
+ '{3, {-2, -2, -1, 2, -1}}'
775
+ sage: L[L.items.braid_notation_old]
776
+ '{4, {1, -2, 3, -2, -1, -2, -3, -2}}'
777
+
778
+ TESTS:
779
+
780
+ Check that :issue:`33966` is fixed::
781
+
782
+ sage: KnotInfo.K0_1.braid_notation()
783
+ (1,)
784
+ """
785
+ braid_notation = self[self.items.braid_notation]
786
+ if original:
787
+ return braid_notation
788
+
789
+ if not braid_notation:
790
+ # don't forget the unknot
791
+ return (1, )
792
+
793
+ braid_notation = eval_knotinfo(braid_notation)
794
+ if type(braid_notation) in (list, tuple):
795
+ # in some cases there are a pair of braid representations
796
+ # in the database. If this is the case we select the
797
+ # corresponding to the braid index.
798
+ if type(braid_notation[0]) is tuple:
799
+ i = self.braid_index()
800
+ for b in braid_notation:
801
+ if -i < min(b) and max(b) < i:
802
+ braid_notation = b
803
+ break
804
+
805
+ if not self.is_knot():
806
+ # in linkinfo the braid_notation includes the braid_index as
807
+ # first item of a pair
808
+ braid_notation = braid_notation[1]
809
+ return braid_notation
810
+
811
+ @cached_method
812
+ def braid_index(self):
813
+ r"""
814
+ Return the value of column ``braid_index`` for this
815
+ link as a Python int.
816
+
817
+ OUTPUT:
818
+
819
+ Python int giving the minimum of strands needed to
820
+ represent ``self`` as closure of a braid.
821
+
822
+ EXAMPLES::
823
+
824
+ sage: L = KnotInfo.L4a1_0
825
+ sage: L.braid_index()
826
+ 3
827
+ sage: KnotInfo.K13a_1.inject() # optional - database_knotinfo
828
+ Defining K13a_1
829
+ sage: K13a_1.braid_index() # optional - database_knotinfo
830
+ Traceback (most recent call last):
831
+ ...
832
+ NotImplementedError: this integer is not provided by the database
833
+ """
834
+ if self.is_knot():
835
+ return knotinfo_int(self[self.items.braid_index])
836
+ else:
837
+ braid_notation = self[self.items.braid_notation]
838
+ braid_notation = eval_knotinfo(braid_notation)
839
+ return knotinfo_int(braid_notation[0])
840
+
841
+ @cached_method
842
+ def braid_length(self):
843
+ r"""
844
+ Return the value of column ``braid_length`` for this
845
+ link as a Python int.
846
+
847
+ OUTPUT:
848
+
849
+ Python int giving the minimum length of a braid word
850
+ needed to represent ``self`` as closure of a braid.
851
+
852
+ EXAMPLES::
853
+
854
+ sage: K = KnotInfo.K3_1
855
+ sage: K.braid_length()
856
+ 3
857
+ """
858
+ return knotinfo_int(self[self.items.braid_length])
859
+
860
+ @cached_method
861
+ def braid(self):
862
+ r"""
863
+ Return the braid notation of ``self`` as an instance of :class:`~sage.groups.braid.Braid`.
864
+
865
+ EXAMPLES::
866
+
867
+ sage: K = KnotInfo.K3_1
868
+ sage: K.braid()
869
+ s^3
870
+ sage: K.braid_notation()
871
+ (1, 1, 1)
872
+ sage: KnotInfo.K13n_1448.braid() # optional - database_knotinfo
873
+ s0^-1*s1*s2*s3*s4*s3^2*s2^-1*s1^-1*s0*s2^-1*s1*(s3*s2)^2*s4^-1*s3*s2*s1^-1*s3*s2^-1*s3
874
+ """
875
+ return self._braid_group()(self.braid_notation())
876
+
877
+ @cached_method
878
+ def num_components(self):
879
+ r"""
880
+ Return the number of components of ``self``.
881
+
882
+ EXAMPLES::
883
+
884
+ sage: KnotInfo.L6a1_0.num_components()
885
+ 2
886
+ """
887
+ return db.read_row_dict()[self.name][1]
888
+
889
+ @cached_method
890
+ def crossing_number(self):
891
+ r"""
892
+ Return the minimal number of crossings of ``self``.
893
+
894
+ .. NOTE::
895
+
896
+ In contrast to the number of crossings displayed for instances
897
+ of :class:`Link` this number is the minimum over all possible
898
+ diagrams of the link. The number of crossings displayed in
899
+ the representation string of :class:`Link` refers to the
900
+ special diagram which could be larger.
901
+
902
+ EXAMPLES::
903
+
904
+ sage: KnotInfo.L4a1_0.crossing_number()
905
+ 4
906
+ sage: KnotInfo.K3_1.crossing_number()
907
+ 3
908
+ sage: Link(KnotInfo.L4a1_0.braid())
909
+ Link with 2 components represented by 5 crossings
910
+ """
911
+ return knotinfo_int(self[self.items.crossing_number])
912
+
913
+ @cached_method
914
+ def determinant(self):
915
+ r"""
916
+ Return the determinant of ``self``.
917
+
918
+ From the KnotInfo description page:
919
+
920
+ The determinant of a knot is `\det(V + V^t)`, where `V` is a Seifert
921
+ matrix for the knot.
922
+
923
+ To read the complete description type
924
+ ``KnotInfo.K0_1.items.determinant.description_webpage()``.
925
+
926
+ .. NOTE::
927
+
928
+ KnotInfo's value for the unknot ``0_1`` is zero. This is not
929
+ compatible with Sage's result (the value of the Alexander
930
+ polynomial at -1). Since this method is needed to identify
931
+ Sage links we take the according value in that case.
932
+
933
+ EXAMPLES::
934
+
935
+ sage: KnotInfo.L4a1_0.determinant()
936
+ 4
937
+ sage: KnotInfo.K3_1.determinant()
938
+ 3
939
+ sage: KnotInfo.K0_1.determinant()
940
+ 1
941
+ """
942
+ if self.crossing_number() == 0:
943
+ # see note above
944
+ return 1
945
+ return knotinfo_int(self[self.items.determinant])
946
+
947
+ @cached_method
948
+ def three_genus(self):
949
+ r"""
950
+ Return the three genus of ``self``.
951
+
952
+ From the KnotInfo description page:
953
+
954
+ The three-genus of a knot is defined to be the minimal genus of
955
+ a Seifert surface for a knot.
956
+
957
+ To read the complete description type
958
+ ``KnotInfo.K0_1.items.three_genus.description_webpage()``.
959
+
960
+ EXAMPLES::
961
+
962
+ sage: KnotInfo.K5_2.three_genus() # optional - database_knotinfo
963
+ 1
964
+
965
+ Note that this differs from the corresponding result in Sage
966
+ since the latter is obtained for a Seifert surface that does not
967
+ have the minimal genus::
968
+
969
+ sage: KnotInfo.K5_2.link().genus()
970
+ 3
971
+ """
972
+ return knotinfo_int(self[self.items.three_genus])
973
+
974
+ @cached_method
975
+ def signature(self):
976
+ r"""
977
+ Return the signature of ``self``.
978
+
979
+ From the KnotInfo description page:
980
+
981
+ The signature of a knot, `\sigma (K)`, is equal to `\sigma (V + V^t)`,
982
+ the signature of `V + V^t` where `V` is a Seifert matrix for the knot
983
+ and `V^t` is its transpose.
984
+
985
+ To read the complete description type
986
+ ``KnotInfo.K0_1.items.signatur.description_webpage()``.
987
+
988
+ EXAMPLES::
989
+
990
+ sage: KnotInfo.K5_2.signature() # optional - database_knotinfo
991
+ -2
992
+ """
993
+ return knotinfo_int(self[self.items.signature])
994
+
995
+ @cached_method
996
+ def is_knot(self) -> bool:
997
+ r"""
998
+ Return whether ``self`` is a knot or a proper link.
999
+
1000
+ EXAMPLES::
1001
+
1002
+ sage: KnotInfo.L7a1_0.is_knot() # optional - database_knotinfo
1003
+ False
1004
+ sage: KnotInfo.K6_3.is_knot()
1005
+ True
1006
+ """
1007
+ return self.num_components() == 1
1008
+
1009
+ @cached_method
1010
+ def name_unoriented(self):
1011
+ r"""
1012
+ Return the part of the name of ``self`` which is independent on the
1013
+ orientation.
1014
+
1015
+ EXAMPLES::
1016
+
1017
+ sage: KnotInfo.L10a122_1_0.name_unoriented() # optional - database_knotinfo
1018
+ 'L10a122'
1019
+ """
1020
+ return self[self.items.name_unoriented]
1021
+
1022
+ @cached_method
1023
+ def symmetry_type(self):
1024
+ r"""
1025
+ Return the symmetry type of ``self``.
1026
+
1027
+ From the KnotInfo description page:
1028
+
1029
+ If a knot is viewed as the oriented diffeomorphism
1030
+ class of an oriented pair, `K = (S_3, S_1)`, with `S_i`
1031
+ diffeomorphic to `S^i`, there are four oriented knots
1032
+ associated to any particular knot `K`. In addition to
1033
+ `K` itself, there is the reverse, `K^r = (S_3, -S_1)`,
1034
+ the concordance inverse, `-K = (-S_3, -S_1)`, and the
1035
+ mirror image, `K^m = (-S_3, S_1)`. A knot is called
1036
+ reversible if `K = K^r`, negative amphicheiral if
1037
+ `K = -K`, and positive amphicheiral if `K = K^m`.
1038
+
1039
+ A knot possessing any two of these types of symmetry
1040
+ has all three. Thus, in the table, a knot is called
1041
+ reversible if that is the only type of symmetry it has,
1042
+ and likewise for negative amphicheiral. If it has none
1043
+ of these types of symmetry it is called chiral, and if
1044
+ it has all three it is called fully amphicheiral.
1045
+
1046
+ For prime knots with fewer than 12 crossings, all
1047
+ amphicheiral knots are negative amphicheiral.
1048
+
1049
+ EXAMPLES::
1050
+
1051
+ sage: KnotInfo.K6_1.series().inject()
1052
+ Defining K6
1053
+ sage: [(K.name, K.symmetry_type()) for K in K6]
1054
+ [('K6_1', 'reversible'),
1055
+ ('K6_2', 'reversible'),
1056
+ ('K6_3', 'fully amphicheiral')]
1057
+ """
1058
+ if not self.is_knot():
1059
+ raise NotImplementedError('this is only available for knots')
1060
+
1061
+ symmetry_type = self[self.items.symmetry_type].strip() # for example K10_88 is a case with trailing whitespaces
1062
+ if not symmetry_type and self.crossing_number() == 0:
1063
+ return 'fully amphicheiral'
1064
+ return symmetry_type
1065
+
1066
+ @cached_method
1067
+ def is_reversible(self) -> bool:
1068
+ r"""
1069
+ Return whether ``self`` is reversible.
1070
+
1071
+ EXAMPLES::
1072
+
1073
+ sage: KnotInfo.K6_3.is_reversible()
1074
+ True
1075
+
1076
+ TESTS::
1077
+
1078
+ sage: KnotInfo.K10_67.is_reversible() # optional - database_knotinfo
1079
+ False
1080
+ sage: KnotInfo.L7a4_0.is_reversible() # optional - database_knotinfo
1081
+ """
1082
+ if self.is_knot():
1083
+ symmetry_type = self.symmetry_type()
1084
+ if symmetry_type == 'reversible':
1085
+ return True
1086
+ if symmetry_type == 'fully amphicheiral':
1087
+ return True
1088
+ return False
1089
+
1090
+ # revert orientation
1091
+ b = self.braid()
1092
+ bt = list(b.Tietze())
1093
+ bt.reverse()
1094
+ br = b.parent()(tuple(bt))
1095
+ if b.is_conjugated(br):
1096
+ return True
1097
+ return None
1098
+
1099
+ @cached_method
1100
+ def is_amphicheiral(self, positive=False) -> bool:
1101
+ r"""
1102
+ Return whether ``self`` is amphicheiral.
1103
+
1104
+ INPUT:
1105
+
1106
+ - ``positive`` -- boolean (default: ``False``); whether to check
1107
+ if ``self`` is positive or negative amphicheiral (see documentation
1108
+ of :meth:`symmetry_type`)
1109
+
1110
+ OUTPUT: boolean or ``None`` if this cannot be determined
1111
+
1112
+ ``True`` if ``self`` is fully or negative amphicheiral per default. If
1113
+ ``positive`` is set to ``True`` than fully and positive amphicheiral
1114
+ links give ``True``.
1115
+
1116
+ .. NOTE::
1117
+
1118
+ For proper links this property is not provided in the database.
1119
+ Anyway, we support it here in this case, as well, except for a few
1120
+ items where it cannot be determined easily and where ``None``
1121
+ is returned as answer.
1122
+
1123
+ EXAMPLES::
1124
+
1125
+ sage: # optional - database_knotinfo
1126
+ sage: Kp = KnotInfo.K12a_427
1127
+ sage: Kp.is_amphicheiral()
1128
+ False
1129
+ sage: Kp.is_amphicheiral(positive=True)
1130
+ True
1131
+ sage: Kn = KnotInfo.K10_88
1132
+ sage: Kn.is_amphicheiral()
1133
+ True
1134
+ sage: Kn.is_amphicheiral(positive=True)
1135
+ False
1136
+ sage: KnotInfo.L4a1_0.is_amphicheiral()
1137
+ False
1138
+ sage: KnotInfo.L10n59_1.is_amphicheiral()
1139
+ True
1140
+ sage: KnotInfo.L10n36_0.inject()
1141
+ Defining L10n36_0
1142
+ sage: L10n36_0.is_amphicheiral() is None
1143
+ True
1144
+ """
1145
+ if self.is_knot():
1146
+ symmetry_type = self.symmetry_type()
1147
+ if positive:
1148
+ if symmetry_type == 'positive amphicheiral':
1149
+ return True
1150
+ else:
1151
+ if symmetry_type == 'negative amphicheiral':
1152
+ return True
1153
+
1154
+ if symmetry_type == 'fully amphicheiral':
1155
+ return True
1156
+ return False
1157
+
1158
+ h = self.homfly_polynomial()
1159
+ v, z = h.parent().gens()
1160
+ hm = h.subs(v=~v, z=-z)
1161
+ if h != hm:
1162
+ return False
1163
+
1164
+ k = self.kauffman_polynomial()
1165
+ a, z = k.parent().gens()
1166
+ km = k.subs(a=~a)
1167
+ if k != km:
1168
+ return False
1169
+
1170
+ b = self.braid()
1171
+ bi = ~b
1172
+ if b.is_conjugated(bi):
1173
+ # at least negative amphicheiral
1174
+ if not positive:
1175
+ return True
1176
+
1177
+ # revert orientation (back)
1178
+ bit = list(bi.Tietze())
1179
+ bit.reverse()
1180
+ bm = b.parent()(tuple(bit))
1181
+ if b.is_conjugated(bm):
1182
+ if positive:
1183
+ return True
1184
+
1185
+ return None
1186
+
1187
+ @cached_method
1188
+ def is_hyperbolic(self) -> bool:
1189
+ r"""
1190
+ Return whether ``self`` is hyperbolic.
1191
+
1192
+ EXAMPLES::
1193
+
1194
+ sage: KnotInfo.K3_1.is_hyperbolic()
1195
+ False
1196
+ sage: KnotInfo.K5_2.is_hyperbolic()
1197
+ True
1198
+ """
1199
+ geometric_type = self[self.items.geometric_type]
1200
+ if geometric_type == 'hyperbolic':
1201
+ return True
1202
+ return False
1203
+
1204
+ @cached_method
1205
+ def is_alternating(self) -> bool:
1206
+ r"""
1207
+ Return whether ``self`` is alternating.
1208
+
1209
+ EXAMPLES::
1210
+
1211
+ sage: KnotInfo.K5_2.is_alternating()
1212
+ True
1213
+ """
1214
+ return knotinfo_bool(self[self.items.alternating])
1215
+
1216
+ @cached_method
1217
+ def is_almost_alternating(self) -> bool:
1218
+ r"""
1219
+ Return whether ``self`` is almost alternating.
1220
+
1221
+ EXAMPLES::
1222
+
1223
+ sage: KnotInfo.K5_2.is_almost_alternating() # optional - database_knotinfo
1224
+ False
1225
+ """
1226
+ db._feature.require() # column not available in demo-version
1227
+ return knotinfo_bool(self[self.items.almost_alternating])
1228
+
1229
+ @cached_method
1230
+ def is_quasi_alternating(self) -> bool:
1231
+ r"""
1232
+ Return whether ``self`` is quasi alternating.
1233
+
1234
+ EXAMPLES::
1235
+
1236
+ sage: KnotInfo.K5_2.is_quasi_alternating() # optional - database_knotinfo
1237
+ True
1238
+ """
1239
+ db._feature.require() # column not available in demo-version
1240
+ return knotinfo_bool(self[self.items.quasi_alternating])
1241
+
1242
+ @cached_method
1243
+ def is_adequate(self) -> bool:
1244
+ r"""
1245
+ Return whether ``self`` is adequate.
1246
+
1247
+ EXAMPLES::
1248
+
1249
+ sage: KnotInfo.K5_2.is_adequate() # optional - database_knotinfo
1250
+ True
1251
+ """
1252
+ db._feature.require() # column not available in demo-version
1253
+ return knotinfo_bool(self[self.items.adequate])
1254
+
1255
+ @cached_method
1256
+ def is_positive(self) -> bool:
1257
+ r"""
1258
+ Return whether ``self`` is positive.
1259
+
1260
+ EXAMPLES::
1261
+
1262
+ sage: KnotInfo.K5_2.is_positive()
1263
+ True
1264
+ """
1265
+ return knotinfo_bool(self[self.items.positive])
1266
+
1267
+ @cached_method
1268
+ def is_quasipositive(self) -> bool:
1269
+ r"""
1270
+ Return whether ``self`` is quasi-positive.
1271
+
1272
+ EXAMPLES::
1273
+
1274
+ sage: KnotInfo.K5_2.is_quasipositive() # optional - database_knotinfo
1275
+ True
1276
+ """
1277
+ db._feature.require() # column not available in demo-version
1278
+ return knotinfo_bool(self[self.items.quasipositive])
1279
+
1280
+ @cached_method
1281
+ def is_strongly_quasipositive(self) -> bool:
1282
+ r"""
1283
+ Return whether ``self`` is strongly quasi-positive.
1284
+
1285
+ EXAMPLES::
1286
+
1287
+ sage: KnotInfo.K5_2.is_strongly_quasipositive() # optional - database_knotinfo
1288
+ True
1289
+ """
1290
+ db._feature.require() # column not available in demo-version
1291
+ return knotinfo_bool(self[self.items.strongly_quasipositive])
1292
+
1293
+ @cached_method
1294
+ def is_positive_braid(self) -> bool:
1295
+ r"""
1296
+ Return whether ``self`` is a positive braid.
1297
+
1298
+ EXAMPLES::
1299
+
1300
+ sage: KnotInfo.K5_2.is_positive_braid() # optional - database_knotinfo
1301
+ False
1302
+ """
1303
+ db._feature.require() # column not available in demo-version
1304
+ return knotinfo_bool(self[self.items.positive_braid])
1305
+
1306
+ @cached_method
1307
+ def is_fibered(self) -> bool:
1308
+ r"""
1309
+ Return whether ``self`` is fibered.
1310
+
1311
+ EXAMPLES::
1312
+
1313
+ sage: KnotInfo.K6_3.is_fibered()
1314
+ True
1315
+ """
1316
+ return knotinfo_bool(self[self.items.fibered])
1317
+
1318
+ @cached_method
1319
+ def is_oriented(self) -> bool:
1320
+ r"""
1321
+ Return whether ``self`` is oriented.
1322
+
1323
+ EXAMPLES::
1324
+
1325
+ sage: KnotInfo.L6a2_1.is_oriented()
1326
+ True
1327
+ """
1328
+ return not knotinfo_bool(self[self.items.unoriented])
1329
+
1330
+ @cached_method
1331
+ def cosmetic_crossing_conjecture_verified(self):
1332
+ r"""
1333
+ Return whether the Cosmetic Crossing Conjecture has been verified
1334
+ for ``self``.
1335
+
1336
+ From the KnotInfo `description page <https://knotinfo.math.indiana.edu/descriptions/cosmetic_crossing.html>`__:
1337
+
1338
+ A crossing change in a diagram of a knot ``K`` is called cosmetic if
1339
+ the resulting diagram also represents ``K``. The cosmetic crossing
1340
+ conjecture posits that for any knot ``K``, the only cosmetic crossing
1341
+ changes are nugatory, i.e. there exists an embedded 2-sphere in
1342
+ ``S3`` which intersects K only at the two points of the relevant
1343
+ crossing. Conversely, it is not hard to see that any nugatory
1344
+ crossing change is cosmetic.
1345
+
1346
+ EXAMPLES::
1347
+
1348
+ sage: knots = [K for K in KnotInfo if K.is_knot() and K.crossing_number() < 10]
1349
+ sage: all(K.cosmetic_crossing_conjecture_verified() for K in knots)
1350
+ True
1351
+ """
1352
+ cosmetic_crossing = self[self.items.cosmetic_crossing]
1353
+ if self.crossing_number() == 0:
1354
+ return True
1355
+ if not cosmetic_crossing or cosmetic_crossing == 'Unknown':
1356
+ return False
1357
+ if not knotinfo_bool(cosmetic_crossing):
1358
+ return True
1359
+ raise AssertionError(f'{self} is a counterexample to the cosmetic crossing conjecture')
1360
+
1361
+ @cached_method
1362
+ def homfly_polynomial(self, var1='v', var2='z', original=False):
1363
+ r"""
1364
+ Return the HOMFLY-PT polynomial according to the value of column
1365
+ ``homfly_polynomial`` for this knot or link (in the latter case the
1366
+ column ``homflypt_polynomial`` is used) as an instance of the
1367
+ element class according to the output of :meth:`Link.homfly_polynomial`
1368
+ of :class:`Link`.
1369
+
1370
+ The HOMFLY-PT polynomial `P(L)` of a link `L` satisfies the following skein
1371
+ relation (see the corresponding `KnotInfo description page
1372
+ <https://knotinfo.math.indiana.edu/descriptions/jones_homfly_kauffman_description/polynomial_defn.html)>`__):
1373
+
1374
+ .. MATH::
1375
+
1376
+ P(O) = 1,\,\,\, v^{-1} P(L_+) - v P(L_-) = z P(L_0)
1377
+
1378
+ INPUT:
1379
+
1380
+ - ``var1`` -- string (default: ``'v'``); for the name of the first variable
1381
+ - ``var2`` -- string (default: ``'z'``); for the name of the second variable
1382
+ - ``original`` -- boolean (default: ``False``); if set to
1383
+ ``True`` the original table entry is returned as a string
1384
+
1385
+ OUTPUT:
1386
+
1387
+ A Laurent polynomial over the integers, more precisely an instance of
1388
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
1389
+ If ``original`` is set to ``True`` then a string is returned.
1390
+
1391
+ .. NOTE::
1392
+
1393
+ The skein-relation for the HOMFLY-PT polynomial given on KnotInfo
1394
+ does not match the default used in Sage. For comparison you have
1395
+ to use the keyword argument ``normalization='vz'`` on the side
1396
+ of Sage.
1397
+
1398
+ EXAMPLES::
1399
+
1400
+ sage: K3_1 = KnotInfo.K3_1
1401
+ sage: PK3_1 = K3_1.homfly_polynomial(); PK3_1
1402
+ -v^4 + v^2*z^2 + 2*v^2
1403
+ sage: K3_1.homfly_polynomial(original=True)
1404
+ '(2*v^2-v^4)+v^2*z^2'
1405
+ sage: PK3_1 == K3_1.link().homfly_polynomial(normalization='vz')
1406
+ True
1407
+
1408
+ for proper links::
1409
+
1410
+ sage: L4a1_1 = KnotInfo.L4a1_1
1411
+ sage: PL4a1_1 = L4a1_1.homfly_polynomial(var1='x', var2='y'); PL4a1_1
1412
+ -x^5*y + x^3*y^3 - x^5*y^-1 + 3*x^3*y + x^3*y^-1
1413
+ sage: _ == L4a1_1.link().homfly_polynomial('x', 'y', 'vz')
1414
+ True
1415
+
1416
+ check the skein-relation from the KnotInfo description page (applied to one
1417
+ of the positive crossings of the right-handed trefoil)::
1418
+
1419
+ sage: R = PK3_1.parent()
1420
+ sage: PO = R.one()
1421
+ sage: L2a1_1 = KnotInfo.L2a1_1
1422
+ sage: PL2a1_1 = L2a1_1.homfly_polynomial()
1423
+ sage: v, z = R.gens()
1424
+ sage: ~v*PK3_1 -v*PO == z*PL2a1_1
1425
+ True
1426
+
1427
+ TESTS::
1428
+
1429
+ sage: H = KnotInfo.L11n459_1_1_1.homfly_polynomial() # optional - database_knotinfo
1430
+ sage: all(L.homfly_polynomial() == L.link().homfly_polynomial(normalization='vz')\
1431
+ ....: for L in KnotInfo if L.crossing_number() < 7)
1432
+ True
1433
+
1434
+ REFERENCES:
1435
+
1436
+ - :wikipedia:`HOMFLY_polynomial`
1437
+ """
1438
+ if self.is_knot():
1439
+ homfly_polynomial = self[self.items.homfly_polynomial]
1440
+ else:
1441
+ homfly_polynomial = self[self.items.homflypt_polynomial]
1442
+
1443
+ if original:
1444
+ return homfly_polynomial
1445
+
1446
+ R = self._homfly_pol_ring(var1, var2)
1447
+ if not homfly_polynomial and self.crossing_number() == 0:
1448
+ return R.one()
1449
+
1450
+ # As of February 2021 there is a wrong character for the link in the
1451
+ # last row of the database. This is removed here (see SPKG.rst and
1452
+ # the test above). Once this is fixed upstream, the following three
1453
+ # lines of code can be removed again:
1454
+ if self.value == 'L11n459{1,1,1}':
1455
+ if homfly_polynomial.endswith('}'):
1456
+ homfly_polynomial = homfly_polynomial.strip('}')
1457
+
1458
+ L, M = R.gens()
1459
+ lc = {'v': L, 'z': M}
1460
+ return eval_knotinfo(homfly_polynomial, locals=lc)
1461
+
1462
+ @cached_method
1463
+ def kauffman_polynomial(self, var1='a', var2='z', original=False):
1464
+ r"""
1465
+ Return the Kauffman polynomial according to the value of column
1466
+ ``kauffman_polynomial`` for this knot or link as an instance of
1467
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
1468
+
1469
+ The Kauffman polynomial `F(L)` respectivlely its corresponding invariant
1470
+ under regular isotopy `\Delta (L) = a^{w(L)} F(L)` where `w(L)` is the
1471
+ writhe of the link `L` satisfies the following skein relation
1472
+ (see the corresponding `KnotInfo description page
1473
+ <https://knotinfo.math.indiana.edu/descriptions/jones_homfly_kauffman_description/polynomial_defn.html)>`__):
1474
+
1475
+ .. MATH::
1476
+
1477
+ \Delta(O) = 1,\,\,\, \Delta(L_+) - \Delta(L_-) = z (\Delta(L_0 + \Delta(L_{\infty}))
1478
+
1479
+ Furthermore, removing a curl of sign `\epsilon` leads to a multiplication
1480
+ of `\Delta(L)` with `a^{\epsilon}`.
1481
+
1482
+ INPUT:
1483
+
1484
+ - ``var1`` -- (default: ``'a'``) the first variable
1485
+ - ``var2`` -- (default: ``'z'``) the second variable
1486
+ - ``original`` -- boolean (default: ``False``); if set to
1487
+ ``True`` the original table entry is returned as a string
1488
+
1489
+ OUTPUT:
1490
+
1491
+ A Laurent polynomial over the integers, more precisely an instance of
1492
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
1493
+ If ``original`` is set to ``False`` then a string is returned.
1494
+
1495
+ EXAMPLES::
1496
+
1497
+ sage: L = KnotInfo.L2a1_1
1498
+ sage: K = KnotInfo.K4_1
1499
+
1500
+ sage: L.kauffman_polynomial()
1501
+ a^-1*z - a^-1*z^-1 + a^-2 + a^-3*z - a^-3*z^-1
1502
+ sage: K.kauffman_polynomial()
1503
+ a^2*z^2 + a*z^3 - a^2 - a*z + 2*z^2 + a^-1*z^3 - 1 - a^-1*z + a^-2*z^2 - a^-2
1504
+
1505
+ Comparison with Jones polynomial::
1506
+
1507
+ sage: # needs sage.symbolic
1508
+ sage: k = _
1509
+ sage: a, z = k.variables()
1510
+ sage: j = K.jones_polynomial(skein_normalization=True)
1511
+ sage: t, = j.variables()
1512
+ sage: k.subs(a=-t^3, z=~t+t) == j.subs(t=t^4)
1513
+ True
1514
+
1515
+ Check the skein relation::
1516
+
1517
+ sage: K3_1 = KnotInfo.K3_1
1518
+ sage: FK3_1 = K3_1.kauffman_polynomial()
1519
+ sage: FL2a1_1 = L.kauffman_polynomial()
1520
+ sage: z, a = FK3_1.variables()
1521
+ sage: ΔK3_1 = FK3_1 * a**K3_1.link().writhe()
1522
+ sage: ΔL2a1_1 = FL2a1_1 * a**L.link().writhe()
1523
+ sage: ΔO1p = a # unknot with one positive curl
1524
+ sage: ΔO2n = a**-2 # unknot with two negative curls
1525
+ sage: ΔK3_1 + ΔO1p == z*(ΔL2a1_1 + ΔO2n)
1526
+ True
1527
+
1528
+ REFERENCES:
1529
+
1530
+ - :wikipedia:`Kauffman_polynomial`
1531
+ """
1532
+ kauffman_polynomial = self[self.items.kauffman_polynomial]
1533
+
1534
+ if original:
1535
+ return kauffman_polynomial
1536
+
1537
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
1538
+ R = LaurentPolynomialRing(ZZ, (var1, var2))
1539
+ if not kauffman_polynomial and self.crossing_number() == 0:
1540
+ return R.one()
1541
+
1542
+ a, z = R.gens()
1543
+ lc = {'a': a, 'z': z}
1544
+ return R(eval_knotinfo(kauffman_polynomial, locals=lc))
1545
+
1546
+ @cached_method
1547
+ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False, original=False, use_sqrt=False):
1548
+ r"""
1549
+ Return the Jones polynomial according to the value of column
1550
+ ``jones_polynomial`` for this knot or link as an element of the symbolic
1551
+ ring :class:`~sage.symbolic.ring.SR` or an instance of
1552
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial`
1553
+ depending on the keyword ``skein_normalization``. Using the keyword
1554
+ ``puiseux`` instead of an element of the symbolic ring an instance of
1555
+ :class:`~sage.rings.puiseux_series_ring_element.PuiseuxSeries` can be
1556
+ returned.
1557
+
1558
+ The Jones polynomial `V(L)` of a link `L` satisfies the following skein
1559
+ relation (see the corresponding `KnotInfo description page
1560
+ <https://knotinfo.math.indiana.edu/descriptions/jones_homfly_kauffman_description/polynomial_defn.html)>`__):
1561
+
1562
+ .. MATH::
1563
+
1564
+ V(O) = 1,\,\,\, t^{-1} V(L_+) - t V(L_-) = (t^{\frac{1}{2}} - t^{-\frac{1}{2}}) V(L_0)
1565
+
1566
+ INPUT:
1567
+
1568
+ - ``variab`` -- variable (default: ``None``); used according to
1569
+ :meth:`Link.jones_polynomial`
1570
+ - ``skein_normalization`` -- boolean (default: ``False``); used
1571
+ according to :meth:`Link.jones_polynomial`
1572
+ - ``puiseux`` -- boolean (default: ``True``); only used in case
1573
+ ``skein_normalization=False``. If set to ``True`` instead of an element
1574
+ of the symbolic ring an instance of :class:`~sage.rings.puiseux_series_ring_element.PuiseuxSeries`
1575
+ is returned
1576
+ - ``original`` -- boolean (default: ``False``); if set to
1577
+ ``True`` the original table entry is returned as a string
1578
+ - ``use_sqrt`` -- boolean (default: ``False``); see the note below
1579
+
1580
+ OUTPUT:
1581
+
1582
+ Depends on the keywords (in excluding order):
1583
+
1584
+ - ``original=True`` a string according to the original value from the
1585
+ database
1586
+ - ``skein_normalization=True`` a Laurent polynomial over the integers,
1587
+ more precisely an instance of :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial`
1588
+ - ``puiseux=True`` a puiseux series over the integers, more precisely an
1589
+ instance of :class:`~sage.rings.puiseux_series_ring_element.PuiseuxSeries`
1590
+
1591
+ In all other cases an element of the symbolic ring :class:`~sage.symbolic.ring.SR`.
1592
+
1593
+ .. NOTE::
1594
+
1595
+ There is a difference to Sage's conventions concerning the Jones
1596
+ polynomial in the case of proper links. KnotInfo does not display
1597
+ these polynomials in the indeterminate `t` used in the skein relation.
1598
+ Instead a variable `x` is used defined by `x^2 = t`. Sage uses `t` in
1599
+ both cases, knots and proper links. Thus, to obtain the Jones polynomial
1600
+ for a proper link in `t` you have to set the keyword ``use_sqrt``
1601
+ to ``True``.
1602
+
1603
+ EXAMPLES::
1604
+
1605
+ sage: K = KnotInfo.K4_1
1606
+ sage: Kj = K.jones_polynomial(); Kj # needs sage.symbolic
1607
+ t^2 - t - 1/t + 1/t^2 + 1
1608
+ sage: Kjs = K.jones_polynomial(skein_normalization=True); Kjs
1609
+ A^-8 - A^-4 + 1 - A^4 + A^8
1610
+ sage: Kjp = K.jones_polynomial(puiseux=True); Kjp
1611
+ t^-2 - t^-1 + 1 - t + t^2
1612
+
1613
+ for proper links::
1614
+
1615
+ sage: L = KnotInfo.L2a1_1
1616
+ sage: Lj = L.jones_polynomial(); Lj # needs sage.symbolic
1617
+ -x^5 - x
1618
+ sage: Ljt = L.jones_polynomial(use_sqrt=True); Ljt # needs sage.symbolic
1619
+ -t^(5/2) - sqrt(t)
1620
+ sage: Ljp = L.jones_polynomial(puiseux=True); Ljp
1621
+ -t^(1/2) - t^(5/2)
1622
+ sage: Ljs = L.jones_polynomial(skein_normalization=True); Ljs
1623
+ -A^2 - A^10
1624
+ sage: Lj.parent() # needs sage.symbolic
1625
+ Symbolic Ring
1626
+ sage: Ljt.parent() # needs sage.symbolic
1627
+ Symbolic Ring
1628
+ sage: Ljp.parent()
1629
+ Puiseux Series Ring in t over Integer Ring
1630
+ sage: Ljs.parent()
1631
+ Univariate Laurent Polynomial Ring in A over Integer Ring
1632
+
1633
+ Comparison with Sage's results::
1634
+
1635
+ sage: k = K.link()
1636
+ sage: kj = k.jones_polynomial() # needs sage.symbolic
1637
+ sage: bool(Kj == kj) # needs sage.symbolic
1638
+ True
1639
+ sage: kjs = k.jones_polynomial(skein_normalization=True)
1640
+ sage: Kjs == kjs
1641
+ True
1642
+ sage: l = L.link()
1643
+ sage: lj = l.jones_polynomial() # needs sage.symbolic
1644
+ sage: bool(Lj == lj) # needs sage.symbolic
1645
+ False
1646
+ sage: bool(Ljt == lj) # see note above # needs sage.symbolic
1647
+ True
1648
+ sage: ljs = l.jones_polynomial(skein_normalization=True)
1649
+ sage: Ljs == ljs
1650
+ True
1651
+
1652
+ Check the skein-relation from the KnotInfo description page (applied to one
1653
+ of the positive crossings of the right-handed trefoil)::
1654
+
1655
+ sage: K3_1 = KnotInfo.K3_1
1656
+
1657
+ sage: # needs sage.symbolic
1658
+ sage: K3_1j = K3_1.jones_polynomial()
1659
+ sage: L2a1_1j = Ljt # see note above
1660
+ sage: R = L2a1_1j.parent()
1661
+ sage: Oj = R(1)
1662
+ sage: t = R('t')
1663
+ sage: lhs = expand(~t*K3_1j - t*Oj)
1664
+ sage: rhs = expand((sqrt(t) - ~sqrt(t))*L2a1_1j)
1665
+ sage: bool(lhs == rhs)
1666
+ True
1667
+
1668
+ The same with the Puiseux series version::
1669
+
1670
+ sage: K3_1jp = K3_1.jones_polynomial(puiseux=True)
1671
+ sage: L2a1_1jp = Ljp
1672
+ sage: R = L2a1_1jp.parent()
1673
+ sage: Ojp = R(1)
1674
+ sage: t = R('t')
1675
+ sage: ~t*K3_1jp - t*Ojp == (t^(1/2)-~t^(1/2))*L2a1_1jp
1676
+ True
1677
+
1678
+ The same in the case of skein normalization (using `t = A^4`)::
1679
+
1680
+ sage: K3_1js = K3_1.jones_polynomial(skein_normalization=True)
1681
+ sage: L2a1_1js = L.jones_polynomial(skein_normalization=True)
1682
+ sage: Rs = K3_1js.parent()
1683
+ sage: Ojs = Rs.one()
1684
+ sage: A, = Rs.gens()
1685
+ sage: ~A^4*K3_1js - A^4*Ojs == (A^2-~A^2)*L2a1_1js
1686
+ True
1687
+
1688
+ REFERENCES:
1689
+
1690
+ - :wikipedia:`Jones_polynomial`
1691
+ """
1692
+ jones_polynomial = self[self.items.jones_polynomial]
1693
+
1694
+ if original:
1695
+ return jones_polynomial
1696
+
1697
+ if skein_normalization:
1698
+ if not variab:
1699
+ variab = 'A'
1700
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
1701
+ R = LaurentPolynomialRing(ZZ, variab)
1702
+ else:
1703
+ if not variab:
1704
+ if use_sqrt or self.is_knot() or puiseux:
1705
+ variab = 't'
1706
+ else:
1707
+ variab = 'x'
1708
+ if puiseux:
1709
+ from sage.rings.puiseux_series_ring import PuiseuxSeriesRing # since PuiseuxPolynomial is not available, so far
1710
+ R = PuiseuxSeriesRing(ZZ, variab)
1711
+ else:
1712
+ from sage.symbolic.ring import SR
1713
+ R = SR
1714
+
1715
+ if not jones_polynomial and self.crossing_number() == 0:
1716
+ return R.one()
1717
+
1718
+ t = R(variab)
1719
+ if skein_normalization:
1720
+ if self.is_knot():
1721
+ lc = {'t': t**4}
1722
+ else:
1723
+ lc = {'x': t**2}
1724
+ else:
1725
+ if self.is_knot():
1726
+ lc = {'t': t}
1727
+ elif puiseux:
1728
+ lc = {'x': t**(1/2)}
1729
+ elif use_sqrt:
1730
+ from sage.misc.functional import sqrt
1731
+ lc = {'x': sqrt(t)}
1732
+ else:
1733
+ lc = {'x': t}
1734
+
1735
+ return R(eval_knotinfo(jones_polynomial, locals=lc))
1736
+
1737
+ @cached_method
1738
+ def alexander_polynomial(self, var='t', original=False, laurent_poly=False):
1739
+ r"""
1740
+ Return the Alexander polynomial according to the value of column
1741
+ ``alexander_polynomial`` for this knot as an instance of
1742
+ :class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
1743
+
1744
+ It is obtained from the Seifert matrix `V` of ``self`` by the following
1745
+ formula (see the KnotInfo description web-page; to launch it see the
1746
+ example below):
1747
+
1748
+ .. MATH::
1749
+
1750
+ A(L) = \det(V -t V^t)
1751
+
1752
+ Here `V^t` stands for the transpose of `V`.
1753
+
1754
+
1755
+ INPUT:
1756
+
1757
+ - ``var`` -- (default: ``'t'``) the variable
1758
+ - ``original`` -- boolean (default: ``False``); if set to
1759
+ ``True`` the original table entry is returned as a string
1760
+ - ``laurent_poly`` -- boolean (default: ``False``); see the note below
1761
+
1762
+ OUTPUT:
1763
+
1764
+ A polynomial over the integers, more precisely an instance of
1765
+ :class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
1766
+ If ``laurent_poly`` is set to ``True`` a Laurent polynomial
1767
+ over the integers, more precisely an instance of
1768
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial`
1769
+ is returned. If ``original`` is set to ``True`` then a string
1770
+ is returned.
1771
+
1772
+ .. NOTE::
1773
+
1774
+ As an invariant the Alexander polynomial is only unique up to
1775
+ a unit factor in the Laurent polynomial ring over the integers
1776
+ in the indeterminate `t`. While the normalization of the exponents
1777
+ in KnotInfo guarantees it to be a proper polynomial, this is
1778
+ not the case for the implementation in Sage. Use the keyword
1779
+ ``laurent_poly`` to achiev a normalization according to Sage.
1780
+ But, still there may be a difference in sign (see the example below).
1781
+
1782
+ EXAMPLES::
1783
+
1784
+ sage: K = KnotInfo.K4_1
1785
+ sage: Ka = K.alexander_polynomial(); Ka
1786
+ t^2 - 3*t + 1
1787
+
1788
+ Comparison with Sage's results::
1789
+
1790
+ sage: k = K.link()
1791
+ sage: ka = k.alexander_polynomial(); ka
1792
+ -t^-1 + 3 - t
1793
+ sage: K.alexander_polynomial(laurent_poly=True)
1794
+ t^-1 - 3 + t
1795
+ sage: _ == -ka
1796
+ True
1797
+
1798
+ Launch the KnotInfo description web-page::
1799
+
1800
+ sage: K.items.alexander_polynomial.description_webpage() # not tested
1801
+ True
1802
+ """
1803
+ alexander_polynomial = self[self.items.alexander_polynomial]
1804
+
1805
+ if original:
1806
+ return alexander_polynomial
1807
+
1808
+ if laurent_poly:
1809
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
1810
+ R = LaurentPolynomialRing(ZZ, var)
1811
+ else:
1812
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1813
+ R = PolynomialRing(ZZ, var)
1814
+
1815
+ if not alexander_polynomial and self.crossing_number() == 0:
1816
+ return R.one()
1817
+
1818
+ t, = R.gens()
1819
+ lc = {'t': t}
1820
+ ap = R(eval_knotinfo(alexander_polynomial, locals=lc))
1821
+ if not laurent_poly or ap.is_constant():
1822
+ return ap
1823
+
1824
+ exp = ap.exponents()
1825
+ return t ** ((-max(exp) - min(exp)) // 2) * ap
1826
+
1827
+ @cached_method
1828
+ def conway_polynomial(self, var='t', original=False):
1829
+ r"""
1830
+ Return the Conway polynomial according to the value of column
1831
+ ``conway_polynomial`` for this knot or link as an instance of
1832
+ :class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
1833
+
1834
+ It is obtained from the Seifert matrix `V` of ``self`` by the following
1835
+ formula (see the KnotInfo description web-page; to launch it see the
1836
+ example below):
1837
+
1838
+ .. MATH::
1839
+
1840
+ \nabla(L) = \det(t^{\frac{1}{2}} V -t^{\frac{-1}{2}} V^t)
1841
+
1842
+ Here `V^t` stands for the transpose of `V`.
1843
+
1844
+
1845
+ INPUT:
1846
+
1847
+ - ``var`` -- (default: ``'t'``) the variable
1848
+ - ``original`` -- boolean (default: ``False``); if set to
1849
+ ``True`` the original table entry is returned as a string
1850
+
1851
+ OUTPUT:
1852
+
1853
+ A polynomial over the integers, more precisely an instance of
1854
+ :class:`~sage.rings.polynomial.polynomial_element.Polynomial`.
1855
+ If ``original`` is set to ``True`` then a string is returned.
1856
+
1857
+ EXAMPLES::
1858
+
1859
+ sage: K = KnotInfo.K4_1
1860
+ sage: Kc = K.conway_polynomial(); Kc
1861
+ -t^2 + 1
1862
+ sage: L = KnotInfo.L5a1_0
1863
+ sage: Lc = L.conway_polynomial(); Lc
1864
+ t^3
1865
+
1866
+ Comparison to Sage's results::
1867
+
1868
+ sage: Kc == K.link().conway_polynomial()
1869
+ True
1870
+ sage: Lc == L.link().conway_polynomial()
1871
+ True
1872
+
1873
+ Launch the KnotInfo description web-page::
1874
+
1875
+ sage: K.items.conway_polynomial.description_webpage() # not tested
1876
+ True
1877
+ """
1878
+ conway_polynomial = self[self.items.conway_polynomial]
1879
+
1880
+ if original:
1881
+ return conway_polynomial
1882
+
1883
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1884
+ R = PolynomialRing(ZZ, var)
1885
+
1886
+ if not conway_polynomial and self.crossing_number() == 0:
1887
+ return R.one()
1888
+
1889
+ t, = R.gens()
1890
+ lc = {'z': t}
1891
+ return R(eval_knotinfo(conway_polynomial, locals=lc))
1892
+
1893
+ @cached_method
1894
+ def khovanov_polynomial(self, var1='q', var2='t', torsion='T', ring=None, original=False,
1895
+ reduced=False, odd=False, base_ring=None):
1896
+ r"""
1897
+ Return the Khovanov polynomial according to the value of columns
1898
+ ``khovanov_*`` for this knot or link as an instance of
1899
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
1900
+
1901
+ INPUT:
1902
+
1903
+ - ``var1`` -- (default: ``'q'``) the first variable; its exponents
1904
+ correspond to the height of Khovanov homology
1905
+ - ``var2`` -- (default: ``'t'``) the second variable; its exponents
1906
+ correspond to the degree of Khovanov homology
1907
+ - ``torsion`` -- (default: ``'T'``) additional variable to indicate
1908
+ the torsion of the integral homology group corresponding to the
1909
+ monomial; monomials without it correspond to torsion free ``ring``
1910
+ modules; if it appears, its exponents stands for the modulus of
1911
+ the torsion
1912
+ - ``ring`` -- (default: ``ZZ`` for knots and ``QQ`` for proper links)
1913
+ the ring for the homology
1914
+ - ``original`` -- boolean (default: ``False``); if set to
1915
+ ``True`` the original table entry is returned as a string
1916
+ - ``reduced`` -- boolean (default: ``False``); if set to ``True``
1917
+ the reduced version of the homology is used
1918
+ - ``odd`` -- boolean (default: ``False``); if set to ``True``
1919
+ the odd version of the homology is used
1920
+
1921
+ OUTPUT:
1922
+
1923
+ A two or three (for integral homology) variate Laurent polynomial over
1924
+ ``ZZ``, more precisely an instance of
1925
+ :class:`~sage.rings.polynomial.laurent_polynomial.LaurentPolynomial_mpair`.
1926
+ If ``original`` is set to ``True`` then a string is returned.
1927
+
1928
+ .. NOTE ::
1929
+
1930
+ The data used for multi-component links were calculated with the program
1931
+ `KhoHo <https://github.com/AShumakovitch/KhoHo>`__.which uses the ``DT``
1932
+ notation. For knots data calculated with
1933
+ `KnotJob <https://www.maths.dur.ac.uk/users/dirk.schuetz/knotjob.html>`__
1934
+ are used. The latter program is more accurate in terms of orientation
1935
+ and reflection as it is based on ``PD`` code.
1936
+
1937
+ EXAMPLES::
1938
+
1939
+ sage: K = KnotInfo.K6_3
1940
+ sage: Kk = K.khovanov_polynomial(); Kk
1941
+ q^7*t^3 + q^5*t^3*T^2 + q^5*t^2 + q^3*t^2*T^2 + q^3*t^2 + q^3*t + q*t*T^2 + q*t
1942
+ + 2*q + q^-1*T^2 + 2*q^-1 + q^-1*t^-1 + q^-3*t^-1*T^2 + q^-3*t^-1 + q^-3*t^-2
1943
+ + q^-5*t^-2*T^2 + q^-5*t^-2 + q^-7*t^-3
1944
+ sage: L = KnotInfo.L5a1_0
1945
+ sage: Lk = L.khovanov_polynomial(); Lk
1946
+ 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
1947
+ sage: L.khovanov_polynomial(original=True)
1948
+ '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'
1949
+
1950
+ Obtaining the reduced homology (for knots only)::
1951
+
1952
+ sage: Kkr = K.khovanov_polynomial(reduced=True); Kkr
1953
+ q^6*t^3 + 2*q^4*t^2 + 2*q^2*t + 3 + 2*q^-2*t^-1 + 2*q^-4*t^-2 + q^-6*t^-3
1954
+ sage: K.khovanov_polynomial(ring=QQ, reduced=True) == Kkr
1955
+ True
1956
+ sage: Kkr2 = K.khovanov_polynomial(var1='p', ring=GF(2), reduced=True); Kkr2
1957
+ p^6*t^3 + 2*p^4*t^2 + 2*p^2*t + 3 + 2*p^-2*t^-1 + 2*p^-4*t^-2 + p^-6*t^-3
1958
+ sage: KnotInfo.K8_19.inject() # optional database_knotinfo
1959
+ Defining K8_19
1960
+ sage: K8kr = K8_19.khovanov_polynomial(reduced=True); K8kr # optional database_knotinfo
1961
+ q^16*t^5 + q^12*t^4 + q^12*t^3 + q^10*t^2 + q^6
1962
+
1963
+ Obtaining the odd Khovanov homology (for knots only)::
1964
+
1965
+ sage: K.khovanov_polynomial(odd=True) == Kkr
1966
+ True
1967
+ sage: K.khovanov_polynomial(ring=QQ, odd=True) == Kkr
1968
+ True
1969
+ sage: K.khovanov_polynomial(var1='p', ring=GF(2), odd=True) == Kkr2
1970
+ True
1971
+ sage: K8ko = K8_19.khovanov_polynomial(odd=True); K8ko # optional database_knotinfo
1972
+ q^14*t^5*T^3 + q^16*t^5 + q^12*t^4*T^2 + q^10*t^2 + q^6
1973
+ sage: K8kr == K8ko # optional database_knotinfo
1974
+ False
1975
+
1976
+ Caution::
1977
+
1978
+ sage: Kk2 = K.khovanov_polynomial(ring=GF(2))
1979
+ Traceback (most recent call last):
1980
+ ...
1981
+ NotImplementedError: unreduced Khovanov polynomial available only for integral homology
1982
+
1983
+ Comparison to Sage's results::
1984
+
1985
+ sage: Kk == K.link().khovanov_polynomial()
1986
+ True
1987
+ sage: Lk == L.link().khovanov_polynomial(ring=QQ)
1988
+ True
1989
+
1990
+ TESTS::
1991
+
1992
+ sage: KnotInfo.K0_1.inject()
1993
+ Defining K0_1
1994
+ sage: K0_1.khovanov_polynomial()
1995
+ q + q^-1
1996
+ sage: K0_1.khovanov_polynomial(reduced=True)
1997
+ 1
1998
+ sage: K0_1.khovanov_polynomial(odd=True)
1999
+ 1
2000
+ sage: K0_1.khovanov_polynomial(ring=GF(3), reduced=True)
2001
+ Traceback (most recent call last):
2002
+ ...
2003
+ ValueError: characteristic 3 of ring is not valid
2004
+ sage: K0_1.khovanov_polynomial(ring=GF(3), odd=True)
2005
+ Traceback (most recent call last):
2006
+ ...
2007
+ ValueError: characteristic 3 of ring is not valid
2008
+ sage: L.khovanov_polynomial(ring=GF(2))
2009
+ Traceback (most recent call last):
2010
+ ...
2011
+ NotImplementedError: Khovanov polynomial not available for multi-component links for this ring
2012
+
2013
+ REFERENCES:
2014
+
2015
+ - :wikipedia:`Khovanov_homology`
2016
+ - :wikipedia:`Reduced_homology`
2017
+ - [ORS2013]_
2018
+ - `KnotInfo <https://knotinfo.math.indiana.edu/descriptions/khovanov_unreduced_integral_polynomial.html>`__
2019
+ """
2020
+ if not ring:
2021
+ if self.is_knot():
2022
+ ring = ZZ
2023
+ else:
2024
+ from sage.rings.rational_field import QQ
2025
+ ring = QQ
2026
+
2027
+ if base_ring:
2028
+ ring = base_ring
2029
+ from sage.misc.superseded import deprecation
2030
+ deprecation(40149, "base_ring is deprecated, use argument ring instead.")
2031
+
2032
+ ch = ring.characteristic()
2033
+ integral = (ch == 0) and (not ring.is_field())
2034
+ if not self.is_knot():
2035
+ # KnotJob calculated results only available for knots
2036
+ if ch == 0 and ring.is_field():
2037
+ khovanov_polynomial = self[self.items.khovanov_polynomial]
2038
+ else:
2039
+ raise NotImplementedError('Khovanov polynomial not available for multi-component links for this ring')
2040
+ else:
2041
+ if reduced:
2042
+ if integral:
2043
+ khovanov_polynomial = self[self.items.khovanov_reduced_integral_polynomial]
2044
+ elif ch == 0:
2045
+ khovanov_polynomial = self[self.items.khovanov_reduced_rational_polynomial]
2046
+ elif ch == 2:
2047
+ khovanov_polynomial = self[self.items.khovanov_reduced_mod2_polynomial]
2048
+ else:
2049
+ raise ValueError('characteristic %s of ring is not valid' % ch)
2050
+ elif odd:
2051
+ if integral:
2052
+ khovanov_polynomial = self[self.items.khovanov_odd_integral_polynomial]
2053
+ elif ch == 0:
2054
+ khovanov_polynomial = self[self.items.khovanov_odd_rational_polynomial]
2055
+ elif ch == 2:
2056
+ khovanov_polynomial = self[self.items.khovanov_odd_mod2_polynomial]
2057
+ else:
2058
+ raise ValueError('characteristic %s of ring is not valid' % ch)
2059
+ else:
2060
+ if integral:
2061
+ khovanov_polynomial = self[self.items.khovanov_unreduced_integral_polynomial]
2062
+ else:
2063
+ raise NotImplementedError('unreduced Khovanov polynomial available only for integral homology')
2064
+
2065
+ if original:
2066
+ return khovanov_polynomial
2067
+
2068
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
2069
+ if integral:
2070
+ var_names = [var1, var2, torsion]
2071
+ else:
2072
+ var_names = [var1, var2]
2073
+
2074
+ R = LaurentPolynomialRing(ZZ, var_names)
2075
+
2076
+ if not khovanov_polynomial and self.crossing_number() == 0:
2077
+ if reduced or odd:
2078
+ return R.one()
2079
+ else:
2080
+ if integral:
2081
+ return R({(1, 0, 0): 1, (-1, 0, 0): 1})
2082
+ else:
2083
+ return R({(1, 0): 1, (-1, 0): 1})
2084
+
2085
+ if not khovanov_polynomial:
2086
+ # given just for links with less than 12 crossings
2087
+ raise NotImplementedError('Khovanov polynomial not available for this link')
2088
+
2089
+ from sage.repl.preparse import implicit_mul
2090
+ # since implicit_mul does not know about the choice of variable names
2091
+ # we have to insert * between them separately
2092
+ for i in ['q', 't', 'T', ')']:
2093
+ for j in ['q', 't', 'T', '(']:
2094
+ khovanov_polynomial = khovanov_polynomial.replace('%s%s' % (i, j), '%s*%s' % (i, j))
2095
+ khovanov_polynomial = implicit_mul(khovanov_polynomial)
2096
+ gens = R.gens_dict()
2097
+ lc = {}
2098
+ lc['q'] = gens[var1]
2099
+ lc['t'] = gens[var2]
2100
+ if integral:
2101
+ lc['T'] = gens[torsion]
2102
+
2103
+ return R(eval_knotinfo(khovanov_polynomial, locals=lc))
2104
+
2105
+ @cached_method
2106
+ def link(self, use_item=db.columns().pd_notation, snappy=False):
2107
+ r"""
2108
+ Return ``self`` as an instance of :class:`Link` or optional
2109
+ ``spherogram.links.invariants.Link`` (SnapPy).
2110
+
2111
+ INPUT:
2112
+
2113
+ - ``use_item`` -- (default: ``self.items.pd_notation``)
2114
+ instance of :class:`KnotInfoColumns` to choose the column
2115
+ that should be used to construct the link. Allowed values
2116
+ are:
2117
+
2118
+ - ``self.items.pd_notation``
2119
+ - ``self.items.braid_notation``
2120
+ - ``self.items.name`` (only for ``snappy=True``)
2121
+ - ``self.items.dt_notation`` (only for knots and ``snappy=False``)
2122
+ - ``self.items.gauss_notation`` (only for knots and ``snappy=False``)
2123
+
2124
+ - ``snappy`` -- boolean (default: ``False``); if set to ``True``
2125
+ the target of the conversion is the ``pip`` installable
2126
+ package `SnapPy <https://snappy.math.uic.edu/index.html>`__
2127
+ (explicitely, ``spherogram.links.invariants.Link``).
2128
+ If SnapPy is not installed an :exc:`ImportError` is raised. To
2129
+ install SnapPy use ``sage -pip install snappy``.
2130
+
2131
+ .. NOTE::
2132
+
2133
+ We use the PD-notation to construct ``self`` as
2134
+ default. This ensures that the number of crossings
2135
+ displayed in the representation string of the link
2136
+ coincides with the crossing number as a topological
2137
+ invariant.
2138
+
2139
+ Furthermore, note that the mirror version may depend
2140
+ on the used KnotInfo-notation. For instance, regarding to
2141
+ the knot ``5_1`` the Gauss- and the DT-notation refer to
2142
+ the mirror image (see example below).
2143
+
2144
+ EXAMPLES::
2145
+
2146
+ sage: K = KnotInfo.K3_1
2147
+ sage: K.link()
2148
+ Knot represented by 3 crossings
2149
+ sage: _.braid()
2150
+ s^3
2151
+ sage: _ == K.braid()
2152
+ True
2153
+
2154
+ using ``dt_notation``::
2155
+
2156
+ sage: K.link(use_item=K.items.dt_notation)
2157
+ Knot represented by 3 crossings
2158
+ sage: _.braid()
2159
+ s^3
2160
+
2161
+ sage: L = KnotInfo.L4a1_0
2162
+ sage: L.link()
2163
+ Link with 2 components represented by 4 crossings
2164
+
2165
+ sage: L.link(use_item=L.items.dt_notation)
2166
+ Traceback (most recent call last):
2167
+ ...
2168
+ ValueError: link construction using Columns.dt_notation not possible
2169
+
2170
+ using ``snappy``::
2171
+
2172
+ sage: L2 = KnotInfo.L2a1_1
2173
+ sage: l2 = L2.link()
2174
+ sage: l2s = L2.link(snappy=True).sage_link() # optional - snappy
2175
+ sage: l2 == l2s # optional - snappy
2176
+ True
2177
+
2178
+ but observe::
2179
+
2180
+ sage: K7 = KnotInfo.K7_2
2181
+ sage: k7s = K7.link(snappy=True); k7s # optional - snappy
2182
+ <Link: 1 comp; 7 cross>
2183
+ sage: k7sn = K7.link(K7.items.name, snappy=True); k7sn # optional - snappy
2184
+ <Link 7_2: 1 comp; 7 cross>
2185
+ sage: k7s.sage_link().is_isotopic(k7sn) # optional - snappy
2186
+ False
2187
+ sage: k7snm = k7sn.sage_link().mirror_image() # optional - snappy
2188
+ sage: k7s.sage_link().is_isotopic(k7snm) # optional - snappy
2189
+ True
2190
+
2191
+ using ``braid_notation``::
2192
+
2193
+ sage: L2.link(use_item=L.items.braid_notation) == l2
2194
+ True
2195
+
2196
+ observe::
2197
+
2198
+ sage: L.link(use_item=L.items.braid_notation)
2199
+ Link with 2 components represented by 5 crossings
2200
+
2201
+ sage: K6_1 = KnotInfo.K6_1
2202
+ sage: K6_1.link().braid() == K6_1.braid()
2203
+ False
2204
+
2205
+ also observe::
2206
+
2207
+ sage: K4_1 = KnotInfo.K4_1
2208
+ sage: K4_1.link().pd_code()
2209
+ [[4, 2, 5, 1], [8, 6, 1, 5], [6, 3, 7, 4], [2, 7, 3, 8]]
2210
+ sage: K4_1.pd_notation()
2211
+ [[4, 2, 5, 1], [8, 6, 1, 5], [6, 3, 7, 4], [2, 7, 3, 8]]
2212
+
2213
+ sage: K5_1 = KnotInfo.K5_1
2214
+ sage: K5_1.link().braid()
2215
+ s^5
2216
+ sage: K5_1.link(K5_1.items.dt_notation).braid()
2217
+ s^-5
2218
+ sage: K5_1.link(K5_1.items.gauss_notation).braid()
2219
+ s^-5
2220
+ """
2221
+ if not isinstance(use_item, KnotInfoColumns):
2222
+ raise TypeError('%s must be an instance of %s' % (use_item, KnotInfoColumns))
2223
+
2224
+ if snappy:
2225
+ try:
2226
+ from snappy import Link
2227
+ except ImportError:
2228
+ raise ImportError('this option demands snappy to be installed')
2229
+ elif self.is_knot():
2230
+ from sage.knots.knot import Knot as Link
2231
+ else:
2232
+ from sage.knots.link import Link
2233
+
2234
+ if use_item == self.items.pd_notation:
2235
+ return Link(self.pd_notation())
2236
+ elif use_item == self.items.braid_notation:
2237
+ return Link(self.braid())
2238
+ elif use_item == self.items.name and snappy:
2239
+ if not self.is_knot():
2240
+ use_item = self.items.name_unoriented
2241
+ return Link(self[use_item])
2242
+ elif self.is_knot() and not snappy:
2243
+ # Construction via Gauss and DT-Code only possible for knots
2244
+ from sage.knots.knot import Knots
2245
+ if use_item == self.items.dt_notation:
2246
+ return Knots().from_dowker_code(self.dt_notation())
2247
+ elif use_item == self.items.gauss_notation:
2248
+ return Knots().from_gauss_code(self.gauss_notation())
2249
+
2250
+ raise ValueError('link construction using %s not possible' % use_item)
2251
+
2252
+ @cached_method
2253
+ def is_unique(self) -> bool:
2254
+ r"""
2255
+ Return whether there is no other isotopic link in the database or not.
2256
+
2257
+ OUTPUT: boolean or ``None`` if this cannot be determined
2258
+
2259
+ EXAMPLES::
2260
+
2261
+ sage: KnotInfo.L4a1_0.is_unique()
2262
+ True
2263
+ sage: KnotInfo.L5a1_0.is_unique()
2264
+ False
2265
+ sage: L = KnotInfo.L9a43_0_1 # optional - database_knotinfo
2266
+ sage: L.series(oriented=True).inject() # optional - database_knotinfo
2267
+ Defining L9a43
2268
+ sage: [(L,L.is_unique()) for L in L9a43] # optional - database_knotinfo
2269
+ [(<KnotInfo.L9a43_0_0: 'L9a43{0,0}'>, True),
2270
+ (<KnotInfo.L9a43_1_0: 'L9a43{1,0}'>, False),
2271
+ (<KnotInfo.L9a43_0_1: 'L9a43{0,1}'>, None),
2272
+ (<KnotInfo.L9a43_1_1: 'L9a43{1,1}'>, False)]
2273
+ """
2274
+ # an isotopic pair must have the same unoriented name. So, we can focus
2275
+ # on such series
2276
+ if self.is_knot():
2277
+ return True
2278
+ S = self.series(oriented=True)
2279
+ hp = self.homfly_polynomial()
2280
+ Sl = S.list(homfly=hp)
2281
+ if len(Sl) == 1:
2282
+ return True
2283
+ kp = self.kauffman_polynomial()
2284
+ Sl = [L for L in Sl if L != self and L.kauffman_polynomial() == kp]
2285
+ if not Sl:
2286
+ return True
2287
+
2288
+ b = self.braid()
2289
+ for L in Sl:
2290
+ Lb = L.braid()
2291
+ if L.braid() == b:
2292
+ return False
2293
+ if Lb.is_conjugated(b):
2294
+ return False
2295
+
2296
+ return None
2297
+
2298
+ @cached_method
2299
+ def is_recoverable(self, unique=True) -> bool:
2300
+ r"""
2301
+ Return if ``self`` can be recovered from its conversion to Sage links
2302
+ using the ``pd_notation`` and the ``braid_notation`` and their
2303
+ mirror images.
2304
+
2305
+ The method is indirectly used by the ``TestSuite`` of the series of ``self``.
2306
+
2307
+ INPUT:
2308
+
2309
+ - ``unique`` -- boolean (default: ``True``); if set to ``False``
2310
+ it is only checked if ``self`` is among the recovered items
2311
+
2312
+ EXAMPLES::
2313
+
2314
+ sage: KnotInfo.L4a1_0.inject()
2315
+ Defining L4a1_0
2316
+ sage: L4a1_0.is_recoverable()
2317
+ True
2318
+ sage: L4a1_0.is_recoverable(unique=False)
2319
+ True
2320
+ sage: KnotInfo.L5a1_0.inject()
2321
+ Defining L5a1_0
2322
+ sage: L5a1_0.is_recoverable()
2323
+ False
2324
+ sage: L5a1_0.is_recoverable(unique=False)
2325
+ True
2326
+
2327
+ TESTS:
2328
+
2329
+ sage: KnotInfo.K12a_165.is_recoverable(unique=False) # optional - database_knotinfo, long time
2330
+ True
2331
+ """
2332
+ def recover(sym_mut, braid):
2333
+ r"""
2334
+ Check if ``self`` can be recovered form its associated
2335
+ Sage link.
2336
+ """
2337
+ if braid:
2338
+ l = self.link(self.items.braid_notation)
2339
+ else:
2340
+ l = self.link()
2341
+ if sym_mut is SymmetryMutant.mirror_image:
2342
+ l = l.mirror_image()
2343
+ elif sym_mut is SymmetryMutant.reverse:
2344
+ l = l.reverse()
2345
+ elif sym_mut is SymmetryMutant.concordance_inverse:
2346
+ l = l.mirror_image().reservse()
2347
+
2348
+ def check_result(res):
2349
+ r"""
2350
+ Check a single result from ``get_knotinfo``.
2351
+ """
2352
+ if type(res) is tuple:
2353
+ L, s = res
2354
+ else:
2355
+ L, s = res.to_knotinfo()[0]
2356
+ if not isinstance(L, KnotInfoBase):
2357
+ return False
2358
+ if L != self:
2359
+ return False
2360
+ return s == sym_mut
2361
+
2362
+ try:
2363
+ res = l.get_knotinfo(unique=unique)
2364
+ except NotImplementedError:
2365
+ return False
2366
+ if unique:
2367
+ return check_result(res)
2368
+ else:
2369
+ return any(check_result(r) for r in res)
2370
+
2371
+ from sage.misc.misc import some_tuples
2372
+ if SymmetryMutant.unknown.matches(self):
2373
+ sym_muts = [SymmetryMutant.unknown]
2374
+ else:
2375
+ sym_muts = [s for s in SymmetryMutant if s.is_minimal(self)]
2376
+ return all(recover(sym, braid) for sym, braid in some_tuples(sym_muts, 2, 8))
2377
+
2378
+ def inject(self, verbose=True):
2379
+ """
2380
+ Inject ``self`` with its name into the namespace of the
2381
+ Python code from which this function is called.
2382
+
2383
+ INPUT:
2384
+
2385
+ - ``verbose`` -- boolean (default: ``True``); whether to suppress
2386
+ the message printed on the invocation
2387
+
2388
+ EXAMPLES::
2389
+
2390
+ sage: KnotInfo.K5_2.inject()
2391
+ Defining K5_2
2392
+ sage: K5_2.is_alternating()
2393
+ True
2394
+ """
2395
+ name = self.name
2396
+ if verbose:
2397
+ print("Defining %s" % (name))
2398
+ from sage.repl.user_globals import set_global
2399
+ set_global(name, self)
2400
+
2401
+ @cached_method
2402
+ def series(self, oriented=False):
2403
+ r"""
2404
+ Return the series of links ``self`` belongs to.
2405
+
2406
+ INPUT:
2407
+
2408
+ - ``oriented`` -- boolean (default: ``False``); it only affects proper
2409
+ links. By default the items of the series will be again series of
2410
+ links collecting all orientation mutants of an unoriented name. To
2411
+ obtain the series of the individual links this keyword has to be set
2412
+ to ``True``.
2413
+
2414
+ EXAMPLES::
2415
+
2416
+ sage: K5 = KnotInfo.K5_2.series()
2417
+ sage: K5(1)
2418
+ <KnotInfo.K5_1: '5_1'>
2419
+ sage: KnotInfo.L4a1_1.series().inject()
2420
+ Defining L4a
2421
+ sage: L4a(1)
2422
+ Series of links L4a1
2423
+ sage: KnotInfo.L4a1_1.series(oriented=True).inject()
2424
+ Defining L4a1
2425
+ sage: L4a(1) == L4a1
2426
+ True
2427
+ sage: L4a1(1)
2428
+ <KnotInfo.L4a1_1: 'L4a1{1}'>
2429
+ """
2430
+ if oriented:
2431
+ S = KnotInfoSeries(self.crossing_number(), self.is_knot(), self.is_alternating(), self.name_unoriented())
2432
+ else:
2433
+ S = KnotInfoSeries(self.crossing_number(), self.is_knot(), self.is_alternating())
2434
+ return S
2435
+
2436
+ def diagram(self, single=False, new=0, autoraise=True):
2437
+ r"""
2438
+ Launch the diagram of ``self`` given on the KnotInfo web-page.
2439
+
2440
+ INPUT:
2441
+
2442
+ - ``single`` -- boolean (default: ``False``); if set to ``True`` only one
2443
+ diagram is shown
2444
+ - ``new`` -- integer according to :func:`open` of :mod:`webbrowser`
2445
+ (``0`` default, ``1`` new window, ``2`` new tab)
2446
+ - ``autoraise`` -- boolean (default: ``True``)
2447
+
2448
+ EXAMPLES::
2449
+
2450
+ sage: K = KnotInfo.K3_1
2451
+ sage: K.diagram() # not tested
2452
+ True
2453
+ sage: K.diagram(single=True) # not tested
2454
+ True
2455
+ """
2456
+ import webbrowser
2457
+ if self.is_knot():
2458
+ filename = db.filename.knots
2459
+ else:
2460
+ filename = db.filename.links
2461
+
2462
+ if single:
2463
+ return webbrowser.open(filename.diagram_url(self[self.items.diagram], single=single), new=new, autoraise=autoraise)
2464
+ else:
2465
+ return webbrowser.open(filename.diagram_url(self[self.items.name]), new=new, autoraise=autoraise)
2466
+
2467
+ def knot_atlas_webpage(self, new=0, autoraise=True):
2468
+ r"""
2469
+ Launch the Knot Atlas web-page for ``self``.
2470
+
2471
+ INPUT:
2472
+
2473
+ - ``new`` -- integer according to :func:`open` of :mod:`webbrowser`
2474
+ (``0`` default, ``1`` new window, ``2`` new tab)
2475
+ - ``autoraise`` -- boolean (default: ``True``)
2476
+
2477
+ EXAMPLES::
2478
+
2479
+ sage: K = KnotInfo.K3_1
2480
+ sage: K.knot_atlas_webpage() # not tested
2481
+ True
2482
+ """
2483
+ import webbrowser
2484
+ return webbrowser.open(self[self.items.knot_atlas_anon], new=new, autoraise=autoraise)
2485
+
2486
+ def knotilus_webpage(self, new=0, autoraise=True):
2487
+ r"""
2488
+ Launch the Knotilus web-page for ``self``.
2489
+
2490
+ INPUT:
2491
+
2492
+ - ``new`` -- integer according to :func:`open` of :mod:`webbrowser`
2493
+ (``0`` default, ``1`` new window, ``2`` new tab)
2494
+ - ``autoraise`` -- boolean (default: ``True``)
2495
+
2496
+ EXAMPLES::
2497
+
2498
+ sage: K = KnotInfo.K3_1
2499
+ sage: K.knotilus_webpage(new=1) # not tested
2500
+ True
2501
+ """
2502
+ import webbrowser
2503
+ return webbrowser.open(self[self.items.knotilus_page_anon], new=new, autoraise=autoraise)
2504
+
2505
+
2506
+ # --------------------------------------------------------------------------------------------
2507
+ # KnotInfoSeries
2508
+ # --------------------------------------------------------------------------------------------
2509
+ class KnotInfoSeries(UniqueRepresentation, SageObject):
2510
+ r"""
2511
+ This class can be used to access knots and links via their index
2512
+ according to the series they belong to.
2513
+
2514
+ INPUT:
2515
+
2516
+ - ``crossing_number`` -- integer giving the crossing number of this series
2517
+ of links
2518
+ - ``is_knot`` -- boolean; whether this series is a series of knots
2519
+ or proper links
2520
+ - ``is_alternating`` -- boolean; whether this series is restricted to
2521
+ alternating links or not
2522
+ This is not relevant for knots with less than 11 crossings
2523
+ - ``name_unoriented`` -- string restricting the series to all links with
2524
+ that ``name_unoriented``
2525
+
2526
+ EXAMPLES::
2527
+
2528
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2529
+ sage: K6 = KnotInfoSeries(6, True, True); K6
2530
+ Series of knots K6
2531
+ sage: K6(3)
2532
+ <KnotInfo.K6_3: '6_3'>
2533
+ sage: list(K6)
2534
+ [<KnotInfo.K6_1: '6_1'>, <KnotInfo.K6_2: '6_2'>, <KnotInfo.K6_3: '6_3'>]
2535
+ sage: L6a = KnotInfoSeries(6, False, True); L6a
2536
+ Series of links L6a
2537
+ sage: L6a(2)
2538
+ Series of links L6a2
2539
+ sage: _.inject()
2540
+ Defining L6a2
2541
+ sage: list(L6a2)
2542
+ [<KnotInfo.L6a2_0: 'L6a2{0}'>, <KnotInfo.L6a2_1: 'L6a2{1}'>]
2543
+ sage: L6a2(0).series() == L6a
2544
+ True
2545
+ sage: L6a2(0) == L6a2('0')
2546
+ True
2547
+ """
2548
+
2549
+ def __init__(self, crossing_number, is_knot, is_alternating, name_unoriented=None):
2550
+ r"""
2551
+ Python constructor.
2552
+
2553
+ EXAMPLES::
2554
+
2555
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2556
+ sage: L6a = KnotInfoSeries(6, False, True); L6a
2557
+ Series of links L6a
2558
+ """
2559
+ self._crossing_number = crossing_number
2560
+ self._is_knot = is_knot
2561
+ self._is_alternating = is_alternating
2562
+ self._name_unoriented = name_unoriented
2563
+
2564
+ @cached_method
2565
+ def list(self, oriented=False, comp=None, det=None, homfly=None):
2566
+ r"""
2567
+ Return this series as a Python list.
2568
+
2569
+ INPUT:
2570
+
2571
+ - ``oriented`` -- boolean (default: ``False``); it only affects
2572
+ series of proper links. By default the list items of a series of proper
2573
+ links are again series of links collecting all orientation types of an
2574
+ unoriented name. To obtain the list of the individual links this
2575
+ keyword has to be set to ``True``.
2576
+
2577
+ - ``comp`` -- (default: ``None``) if given an integer for this
2578
+ keyword the list is restricted to links having the according number
2579
+ of components. This keyword implies ``oriented=True``.
2580
+
2581
+ - ``det`` -- (default: ``None``) if given an integer for this
2582
+ keyword the list is restricted to links having the according value
2583
+ for its determinant. This keyword implies ``oriented=True``.
2584
+
2585
+ - ``homfly`` -- (default: ``None``) if given a HOMFLY-PT polynomial
2586
+ having ``normalization='vz'`` for this keyword the list is restricted
2587
+ to links having the according value for its HOMFLY-PT
2588
+ polynomial. This keyword implies ``oriented=True``.
2589
+
2590
+ EXAMPLES::
2591
+
2592
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2593
+ sage: K6 = KnotInfoSeries(6, True, True); K6
2594
+ Series of knots K6
2595
+ sage: K6.list()
2596
+ [<KnotInfo.K6_1: '6_1'>, <KnotInfo.K6_2: '6_2'>, <KnotInfo.K6_3: '6_3'>]
2597
+ sage: KnotInfoSeries(2, False, True).inject()
2598
+ Defining L2a
2599
+ sage: L2a.list()
2600
+ [Series of links L2a1]
2601
+ sage: L2a.list(oriented=True)
2602
+ [<KnotInfo.L2a1_0: 'L2a1{0}'>, <KnotInfo.L2a1_1: 'L2a1{1}'>]
2603
+ """
2604
+ if homfly is not None:
2605
+ # additional restriction to number of components, determinant and
2606
+ # HOMFLY-PT polynomial
2607
+ l = self.list(oriented=True, comp=comp, det=det)
2608
+ return [L for L in l if L.homfly_polynomial() == homfly]
2609
+
2610
+ if det is not None:
2611
+ # additional restriction to number of components and determinant
2612
+ l = self.list(oriented=True, comp=comp)
2613
+ return [L for L in l if L.determinant() == det]
2614
+
2615
+ if comp is not None:
2616
+ # additional restriction to number of components
2617
+ l = self.list(oriented=True)
2618
+ return [L for L in l if L.num_components() == comp]
2619
+
2620
+ # default case
2621
+ is_knot = self._is_knot
2622
+ cross_nr = self._crossing_number
2623
+ is_alt = self._is_alternating
2624
+ n_unori = self._name_unoriented
2625
+
2626
+ res = []
2627
+ curr_n_unori = None
2628
+ for K in KnotInfo:
2629
+ if K.is_knot() != is_knot:
2630
+ continue
2631
+ if K.crossing_number() != cross_nr:
2632
+ continue
2633
+ if not is_knot or cross_nr > 10:
2634
+ if K.is_alternating() != is_alt:
2635
+ continue
2636
+ if is_knot or oriented:
2637
+ res.append(K)
2638
+ else:
2639
+ this_n_unori = K.name_unoriented()
2640
+ if n_unori:
2641
+ if this_n_unori != n_unori:
2642
+ continue
2643
+ res.append(K)
2644
+ elif this_n_unori != curr_n_unori:
2645
+ if curr_n_unori:
2646
+ res.append(KnotInfoSeries(cross_nr, is_knot, is_alt, curr_n_unori))
2647
+ curr_n_unori = this_n_unori
2648
+ else:
2649
+ continue
2650
+
2651
+ if curr_n_unori:
2652
+ res.append(KnotInfoSeries(cross_nr, is_knot, is_alt, curr_n_unori))
2653
+ return res
2654
+
2655
+ @cached_method
2656
+ def lower_list(self, oriented=False, comp=None, det=None, homfly=None):
2657
+ r"""
2658
+ Return this series together with all series with smaller crossing number
2659
+ as a Python list.
2660
+
2661
+ INPUT:
2662
+
2663
+ - ``oriented`` -- boolean (default: ``False``); see the
2664
+ description for :meth:`list`
2665
+
2666
+ - ``comp`` -- (default: ``None``) see the description for
2667
+ :meth:`list`
2668
+
2669
+ - ``det`` -- (default: ``None``) see the description for
2670
+ :meth:`list`
2671
+
2672
+ - ``homfly`` -- (default: ``None``) see the description for
2673
+ :meth:`list`
2674
+
2675
+ EXAMPLES::
2676
+
2677
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2678
+ sage: KnotInfoSeries(5, True, True).lower_list()
2679
+ [<KnotInfo.K0_1: '0_1'>,
2680
+ <KnotInfo.K3_1: '3_1'>,
2681
+ <KnotInfo.K4_1: '4_1'>,
2682
+ <KnotInfo.K5_1: '5_1'>,
2683
+ <KnotInfo.K5_2: '5_2'>]
2684
+ sage: KnotInfoSeries(4, False, True).lower_list()
2685
+ [Series of links L2a1, Series of links L4a1]
2686
+ sage: KnotInfoSeries(4, False, True).lower_list(oriented=True)
2687
+ [<KnotInfo.L2a1_0: 'L2a1{0}'>,
2688
+ <KnotInfo.L2a1_1: 'L2a1{1}'>,
2689
+ <KnotInfo.L4a1_0: 'L4a1{0}'>,
2690
+ <KnotInfo.L4a1_1: 'L4a1{1}'>]
2691
+ """
2692
+ l = []
2693
+ cr = self._crossing_number
2694
+ if cr > 0:
2695
+ LS = type(self)(cr - 1, self._is_knot, self._is_alternating, self._name_unoriented)
2696
+ l = LS.lower_list(oriented=oriented, comp=comp, det=det, homfly=homfly)
2697
+ return l + self.list(oriented=oriented, comp=comp, det=det, homfly=homfly)
2698
+
2699
+ def __repr__(self):
2700
+ r"""
2701
+ Return the representation string of ``self``.
2702
+
2703
+ EXAMPLES::
2704
+
2705
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2706
+ sage: KnotInfoSeries(6, True, True)
2707
+ Series of knots K6
2708
+ sage: _.__repr__()
2709
+ 'Series of knots K6'
2710
+ """
2711
+ if self._is_knot:
2712
+ return 'Series of knots %s' % (self._name())
2713
+ else:
2714
+ return 'Series of links %s' % (self._name())
2715
+
2716
+ def __getitem__(self, item):
2717
+ r"""
2718
+ Return the given ``item`` from the list of ``self``
2719
+ (making the Python build-in ``list`` work).
2720
+
2721
+ EXAMPLES::
2722
+
2723
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2724
+ sage: KnotInfoSeries(6, True, True).inject()
2725
+ Defining K6
2726
+ sage: list(K6) # indirect doctest
2727
+ [<KnotInfo.K6_1: '6_1'>, <KnotInfo.K6_2: '6_2'>, <KnotInfo.K6_3: '6_3'>]
2728
+ """
2729
+ from sage.rings.integer import Integer
2730
+ if type(item) not in (int, Integer):
2731
+ raise ValueError('item must be an integer')
2732
+ l = self.list()
2733
+ max_item = len(l)
2734
+ if item < 0 or item > max_item:
2735
+ raise ValueError('item must be nonnegative and smaller than %s' % (max_item))
2736
+
2737
+ return l[item]
2738
+
2739
+ def __call__(self, item):
2740
+ r"""
2741
+ Return the given ``item`` from the list of ``self``
2742
+ (making the function call for ``self`` work).
2743
+ In contrast to ``__getitem__`` the first ``item``
2744
+ has to be ``1`` (not ``0``).
2745
+
2746
+ EXAMPLES::
2747
+
2748
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2749
+ sage: KnotInfoSeries(6, True, True).inject()
2750
+ Defining K6
2751
+ sage: K6(2) # indirect doctest
2752
+ <KnotInfo.K6_2: '6_2'>
2753
+
2754
+ sage: # optional - database_knotinfo
2755
+ sage: KnotInfo.L8a21_0_1_0.inject()
2756
+ Defining L8a21_0_1_0
2757
+ sage: L8a21_0_1_0.series().inject()
2758
+ Defining L8a
2759
+ sage: L8a(1)
2760
+ Series of links L8a1
2761
+ sage: L8a(21)(2) == L8a21_0_1_0
2762
+ True
2763
+ sage: L8a(21)('010') == L8a21_0_1_0
2764
+ True
2765
+ """
2766
+ if self._name_unoriented:
2767
+ if isinstance(item, str):
2768
+ # allow input as dual number according to naming
2769
+ item = int(item, 2)
2770
+ return self[item]
2771
+
2772
+ from sage.rings.integer import Integer
2773
+ if type(item) not in (int, Integer):
2774
+ raise ValueError('item must be an integer')
2775
+ l = self.list()
2776
+ max_item = len(l) + 1
2777
+ if item < 1 or item > max_item:
2778
+ raise ValueError('item must be positive and smaller than %s' % (max_item))
2779
+
2780
+ return l[item-1]
2781
+
2782
+ def _name(self):
2783
+ r"""
2784
+ Return the name of the series.
2785
+
2786
+ EXAMPLES::
2787
+
2788
+ sage: from sage.knots.knotinfo import KnotInfoSeries
2789
+ sage: KnotInfoSeries(6, True, True)._name()
2790
+ 'K6'
2791
+ """
2792
+ is_knot = self._is_knot
2793
+ cross_nr = self._crossing_number
2794
+ is_alt = self._is_alternating
2795
+ n_unori = self._name_unoriented
2796
+
2797
+ alt = 'a'
2798
+ if not is_alt:
2799
+ alt = 'n'
2800
+
2801
+ if is_knot:
2802
+ if cross_nr > 10:
2803
+ res = 'K%s%s' % (cross_nr, alt)
2804
+ else:
2805
+ res = 'K%s' % (cross_nr)
2806
+ elif n_unori:
2807
+ res = '%s' % (n_unori)
2808
+ else:
2809
+ res = 'L%s%s' % (cross_nr, alt)
2810
+ return res
2811
+
2812
+ def is_recoverable(self, unique=True, max_samples=8) -> bool:
2813
+ r"""
2814
+ Return if all items of ``self`` can be recovered from its conversion to
2815
+ Sage links using the ``pd_notation`` and the ``braid_notation`` and their
2816
+ mirror images.
2817
+
2818
+ The method is indirectly used by the ``TestSuite``.
2819
+
2820
+ INPUT:
2821
+
2822
+ - ``unique`` -- boolean (default: ``True``); see
2823
+ :meth:`KnotInfoBase.is_recoverable`
2824
+ - ``max_samples`` -- nonnegative integer or ``infinity``
2825
+ (default: `8`); limits the number of items to check (random sample).
2826
+ If set to ``infinity`` then no limit is set.
2827
+
2828
+ EXAMPLES::
2829
+
2830
+ sage: KnotInfo.L4a1_0.series().inject()
2831
+ Defining L4a
2832
+ sage: L4a.is_recoverable()
2833
+ True
2834
+ sage: L4a.is_recoverable(unique=False)
2835
+ True
2836
+ sage: KnotInfo.L5a1_0.series().inject()
2837
+ Defining L5a
2838
+ sage: L5a.is_recoverable()
2839
+ False
2840
+ sage: L5a.is_recoverable(unique=False)
2841
+ True
2842
+ """
2843
+ from sage.misc.misc import some_tuples
2844
+ l = self.list(oriented=True)
2845
+ bound = len(l)
2846
+ return all(L.is_recoverable(unique=unique) for L, in some_tuples(l, 1, bound, max_samples=max_samples))
2847
+
2848
+ def _test_recover(self, **options):
2849
+ r"""
2850
+ Method used by ``TestSuite``. Tests if all links of the series can be
2851
+ recovered from their conversion to Sage links. It uses :meth:`is_recoverable`.
2852
+ Thus, per default maximal `8` items (random sample) are tested. Use the
2853
+ option ``max_samples`` to choose another limit or test all
2854
+ (``max_samples=infinity``)
2855
+
2856
+ EXAMPLES::
2857
+
2858
+ sage: TestSuite(KnotInfo.L5a1_0.series()).run(verbose=True) # indirect doctest
2859
+ running ._test_category() . . . pass
2860
+ running ._test_new() . . . pass
2861
+ running ._test_not_implemented_methods() . . . pass
2862
+ running ._test_pickling() . . . pass
2863
+ running ._test_recover() . . . pass
2864
+ sage: TestSuite(KnotInfo.K6_1.series()).run(max_samples=infinity) # indirect doctest
2865
+ """
2866
+ tester = options['tester']
2867
+ max_samples = tester._max_samples
2868
+ try:
2869
+ if max_samples:
2870
+ tester.assertTrue(self.is_recoverable(unique=False, max_samples=max_samples))
2871
+ else:
2872
+ tester.assertTrue(self.is_recoverable(unique=False))
2873
+ except ImportError:
2874
+ pass
2875
+
2876
+ def inject(self, verbose=True):
2877
+ r"""
2878
+ Inject ``self`` with its name into the namespace of the
2879
+ Python code from which this function is called.
2880
+
2881
+ INPUT:
2882
+
2883
+ - ``verbose`` -- boolean (default: ``True``); to suppress
2884
+ the message printed on the invocation
2885
+
2886
+ EXAMPLES::
2887
+
2888
+ sage: KnotInfoSeries(6, True, True).inject()
2889
+ Defining K6
2890
+ sage: K6(2)
2891
+ <KnotInfo.K6_2: '6_2'>
2892
+ """
2893
+ name = self._name()
2894
+ if verbose:
2895
+ print("Defining %s" % (name))
2896
+ from sage.repl.user_globals import set_global
2897
+ set_global(name, self)
2898
+
2899
+
2900
+ KnotInfo = KnotInfoBase('KnotInfo', db.row_names())