Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sage.knots: Modularization fixes (imports), # needs #38118

Merged
merged 11 commits into from
Jul 24, 2024
30 changes: 30 additions & 0 deletions src/bin/sage-fixdoctests
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import subprocess
import sys

from argparse import ArgumentParser
from collections import defaultdict
from pathlib import Path

from sage.doctest.control import DocTestDefaults, DocTestController
Expand Down Expand Up @@ -459,6 +460,8 @@ def process_grouped_blocks(grouped_iterator, distribution=None, venv=None, envir
src_in_lines = src_in.splitlines()
shallow_copy_of_src_in_lines = list(src_in_lines)
file_optional_tags = set(parse_file_optional_tags(enumerate(src_in_lines)))
persistent_tags_counts = defaultdict(int)
tags_counts = defaultdict(int)

for block in blocks:
try:
Expand All @@ -474,13 +477,15 @@ def process_grouped_blocks(grouped_iterator, distribution=None, venv=None, envir

# Remove duplicate optional tags and rewrite all '# optional' that should be '# needs'
persistent_optional_tags = {}
persistent_optional_tags_counted = False
for i, line in enumerate(src_in_lines):
if m := re.match(' *sage: *(.*)#', line):
tags, line_sans_tags, is_persistent = parse_optional_tags(line, return_string_sans_tags=True)
if is_persistent:
persistent_optional_tags = {tag: explanation
for tag, explanation in tags.items()
if explanation or tag not in file_optional_tags}
persistent_optional_tags_counted = False
line = update_optional_tags(line, tags=persistent_optional_tags, force_rewrite='standard')
if re.fullmatch(' *sage: *', line):
# persistent (block-scoped or file-scoped) tag was removed, so remove the whole line
Expand All @@ -491,9 +496,19 @@ def process_grouped_blocks(grouped_iterator, distribution=None, venv=None, envir
if explanation or (tag not in file_optional_tags
and tag not in persistent_optional_tags)}
line = update_optional_tags(line, tags=tags, force_rewrite='standard')
if not persistent_optional_tags_counted:
persistent_tags_counts[frozenset(persistent_optional_tags)] += 1
persistent_optional_tags_counted = True
tags_counts[frozenset(tags)] += 1
src_in_lines[i] = line
elif line.strip() in ['', '"""', "'''"]: # Blank line or end of docstring
persistent_optional_tags = {}
persistent_optional_tags_counted = False
elif re.match(' *sage: ', line):
if not persistent_optional_tags_counted:
persistent_tags_counts[frozenset(persistent_optional_tags)] += 1
persistent_optional_tags_counted = True
tags_counts[frozenset()] += 1

if src_in_lines != shallow_copy_of_src_in_lines:
if (output := output_filename(input)) is None:
Expand Down Expand Up @@ -523,6 +538,21 @@ def process_grouped_blocks(grouped_iterator, distribution=None, venv=None, envir

unprocessed_files.discard(input)

if args.verbose:
if file_optional_tags:
print(f"File tags: ")
print(f" {' '.join(sorted(file_optional_tags))}")
if persistent_tags_counts:
print(f"Doctest blocks by persistent tags: ")
for tags, count in sorted(persistent_tags_counts.items(),
key=lambda i: i[1], reverse=True):
print(f"{count:6} {' '.join(sorted(tags)) or '(untagged)'}")
if tags_counts:
print(f"Doctest examples by tags: ")
for tags, count in sorted(tags_counts.items(),
key=lambda i: i[1], reverse=True):
print(f"{count:6} {' '.join(sorted(tags)) or '(untagged)'}")


def fix_with_distribution(file_set, distribution=None, verbose=False):
if verbose:
Expand Down
27 changes: 27 additions & 0 deletions src/sage/features/sagemath.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,32 @@ def __init__(self):
spkg='sagemath_ntl', type='standard')


class sage__libs__homfly(JoinFeature):
r"""
A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.homfly`.

In addition to the modularization purposes that this tag serves,
it also provides attribution to the upstream project.

TESTS::

sage: from sage.features.sagemath import sage__libs__homfly
sage: sage__libs__homfly().is_present() # needs sage.libs.homfly
FeatureTestResult('sage.libs.homfly', True)
"""
def __init__(self):
r"""
TESTS::

sage: from sage.features.sagemath import sage__libs__homfly
sage: isinstance(sage__libs__homfly(), sage__libs__homfly)
True
"""
JoinFeature.__init__(self, 'sage.libs.homfly',
[PythonModule('sage.libs.homfly')],
spkg='sagemath_homfly', type='standard')


class sage__libs__pari(JoinFeature):
r"""
A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.pari`.
Expand Down Expand Up @@ -1130,6 +1156,7 @@ def all_features():
sage__libs__ecl(),
sage__libs__flint(),
sage__libs__gap(),
sage__libs__homfly(),
sage__libs__linbox(),
sage__libs__m4ri(),
sage__libs__ntl(),
Expand Down
1 change: 1 addition & 0 deletions src/sage/knots/gauss_code.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# sage.doctest: needs sage.graphs
"""
Helper functions related to Gauss codes of knots

Expand Down
7 changes: 6 additions & 1 deletion src/sage/knots/knot.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# sage.doctest: needs sage.graphs sage.groups
r"""
Knots

Expand Down Expand Up @@ -271,10 +272,12 @@ def dt_code(self):
sage: K = Knot([[1,5,2,4],[5,3,6,2],[3,1,4,6]])
sage: K.dt_code()
[4, 6, 2]

sage: B = BraidGroup(4)
sage: K = Knot(B([1, 2, 1, 2]))
sage: K.dt_code()
[4, -6, 8, -2]

sage: K = Knot([[[1, -2, 3, -4, 5, -1, 2, -3, 4, -5]],
....: [1, 1, 1, 1, 1]])
sage: K.dt_code()
Expand Down Expand Up @@ -678,9 +681,11 @@ def from_table(self, n, k):
"""
if n > 10:
raise ValueError('more than 10 crossings, not in the knot table')
from sage.groups.braid import BraidGroup
if (n, k) in small_knots_table:
m, word = small_knots_table[(n, k)]

from sage.groups.braid import BraidGroup

G = BraidGroup(m)
return Knot(G(word))
else:
Expand Down
55 changes: 31 additions & 24 deletions src/sage/knots/knotinfo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# sage.doctest: needs sage.graphs sage.groups
r"""
Access to the KnotInfo database

Expand Down Expand Up @@ -156,7 +157,7 @@
True
sage: K.is_amphicheiral()
True
sage: K.jones_polynomial()
sage: K.jones_polynomial() # needs sage.symbolic
t^2 - t - 1/t + 1/t^2 + 1
sage: K.kauffman_polynomial()
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
Expand Down Expand Up @@ -871,7 +872,7 @@

EXAMPLES::

sage: KnotInfo.K5_2.three_genus() # optional - databsase_knotinfo
sage: KnotInfo.K5_2.three_genus() # optional - database_knotinfo
1

Note that this differs from the corresponding result in Sage
Expand Down Expand Up @@ -899,7 +900,7 @@

EXAMPLES::

sage: KnotInfo.K5_2.signature() # optional - databsase_knotinfo
sage: KnotInfo.K5_2.signature() # optional - database_knotinfo
1
"""
return knotinfo_int(self[self.items.signature])
Expand Down Expand Up @@ -1361,14 +1362,16 @@
EXAMPLES::

sage: L = KnotInfo.L2a1_1
sage: K = KnotInfo.K4_1

sage: L.kauffman_polynomial()
a^-1*z - a^-1*z^-1 + a^-2 + a^-3*z - a^-3*z^-1
sage: K = KnotInfo.K4_1
sage: K.kauffman_polynomial()
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

Comparison with Jones polynomial::

sage: # needs sage.symbolic
sage: k = _
sage: a, z = k.variables()
sage: j = K.jones_polynomial(skein_normalization=True)
Expand Down Expand Up @@ -1467,7 +1470,7 @@
EXAMPLES::

sage: K = KnotInfo.K4_1
sage: Kj = K.jones_polynomial(); Kj
sage: Kj = K.jones_polynomial(); Kj # needs sage.symbolic
t^2 - t - 1/t + 1/t^2 + 1
sage: Kjs = K.jones_polynomial(skein_normalization=True); Kjs
A^-8 - A^-4 + 1 - A^4 + A^8
Expand All @@ -1477,17 +1480,17 @@
for proper links::

sage: L = KnotInfo.L2a1_1
sage: Lj = L.jones_polynomial(); Lj
sage: Lj = L.jones_polynomial(); Lj # needs sage.symbolic
-x^5 - x
sage: Ljt = L.jones_polynomial(use_sqrt=True); Ljt
sage: Ljt = L.jones_polynomial(use_sqrt=True); Ljt # needs sage.symbolic
-t^(5/2) - sqrt(t)
sage: Ljp = L.jones_polynomial(puiseux=True); Ljp
-t^(1/2) - t^(5/2)
sage: Ljs = L.jones_polynomial(skein_normalization=True); Ljs
-A^2 - A^10
sage: Lj.parent()
sage: Lj.parent() # needs sage.symbolic
Symbolic Ring
sage: Ljt.parent()
sage: Ljt.parent() # needs sage.symbolic
Symbolic Ring
sage: Ljp.parent()
Puiseux Series Ring in t over Integer Ring
Expand All @@ -1497,17 +1500,17 @@
Comparison with Sage's results::

sage: k = K.link()
sage: kj = k.jones_polynomial()
sage: bool(Kj == kj)
sage: kj = k.jones_polynomial() # needs sage.symbolic
sage: bool(Kj == kj) # needs sage.symbolic
True
sage: kjs = k.jones_polynomial(skein_normalization=True)
sage: Kjs == kjs
True
sage: l = L.link()
sage: lj = l.jones_polynomial()
sage: bool(Lj == lj)
sage: lj = l.jones_polynomial() # needs sage.symbolic
sage: bool(Lj == lj) # needs sage.symbolic
False
sage: bool(Ljt == lj) # see note above
sage: bool(Ljt == lj) # see note above # needs sage.symbolic
True
sage: ljs = l.jones_polynomial(skein_normalization=True)
sage: Ljs == ljs
Expand All @@ -1517,6 +1520,8 @@
of the positive crossings of the right-handed trefoil)::

sage: K3_1 = KnotInfo.K3_1

sage: # needs sage.symbolic
sage: K3_1j = K3_1.jones_polynomial()
sage: L2a1_1j = Ljt # see note above
sage: R = L2a1_1j.parent()
Expand Down Expand Up @@ -1725,7 +1730,7 @@
sage: Lc = L.conway_polynomial(); Lc
t^3

Comparision to Sage's results::
Comparison to Sage's results::

sage: Kc == K.link().conway_polynomial()
True
Expand All @@ -1734,7 +1739,7 @@

Launch the KnotInfo description web-page::

sage: K.items.conway_polynomial.description_webpage() # not tested
sage: K.items.conway_polynomial.description_webpage() # not tested
True
"""
conway_polynomial = self[self.items.conway_polynomial]
Expand Down Expand Up @@ -1831,7 +1836,7 @@
False


Comparision to Sage's results::
Comparison to Sage's results::

sage: Kk == K.link().khovanov_polynomial()
True
Expand Down Expand Up @@ -2689,20 +2694,23 @@

EXAMPLES::

sage: TestSuite(KnotInfo.L5a1_0.series()).run(verbose=True) # indirec doctest
sage: TestSuite(KnotInfo.L5a1_0.series()).run(verbose=True) # indirect doctest
running ._test_category() . . . pass
running ._test_new() . . . pass
running ._test_not_implemented_methods() . . . pass
running ._test_pickling() . . . pass
running ._test_recover() . . . pass
sage: TestSuite(KnotInfo.K6_1.series()).run(max_samples=infinity) # indirec doctest
sage: TestSuite(KnotInfo.K6_1.series()).run(max_samples=infinity) # indirect doctest
"""
tester = options['tester']
max_samples = tester._max_samples
if max_samples:
tester.assertTrue(self.is_recoverable(unique=False, max_samples=max_samples))
else:
tester.assertTrue(self.is_recoverable(unique=False))
try:
if max_samples:
tester.assertTrue(self.is_recoverable(unique=False, max_samples=max_samples))
else:
tester.assertTrue(self.is_recoverable(unique=False))
except ImportError:
pass

Check warning on line 2713 in src/sage/knots/knotinfo.py

View check run for this annotation

Codecov / codecov/patch

src/sage/knots/knotinfo.py#L2712-L2713

Added lines #L2712 - L2713 were not covered by tests

def inject(self, verbose=True):
r"""
Expand All @@ -2716,7 +2724,6 @@

EXAMPLES::

sage: from sage.knots.knotinfo import KnotInfoSeries
sage: KnotInfoSeries(6, True, True).inject()
Defining K6
sage: K6(2)
Expand Down
Loading
Loading