Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ def three_genus(self):

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 @@ def signature(self):

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 @@ def kauffman_polynomial(self, var1='a', var2='z', original=False):
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 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False
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 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False
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 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False
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 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False
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 @@ def conway_polynomial(self, var='t', original=False):
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 @@ def conway_polynomial(self, var='t', original=False):

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 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False,
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 @@ def _test_recover(self, **options):

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

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

EXAMPLES::

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