diff --git a/src/bin/sage-fixdoctests b/src/bin/sage-fixdoctests index 3cba3464a3e..0c06d448887 100755 --- a/src/bin/sage-fixdoctests +++ b/src/bin/sage-fixdoctests @@ -14,71 +14,233 @@ AUTHORS:: situations when either the expected output or computed output are empty. Added doctest to sage.tests.cmdline """ + +# **************************************************************************** +# Copyright (C) 2006 William Stein +# 2009 Nicolas M. Thiery +# 2013 Andrew Mathas +# 2014 Volker Braun +# 2020 Jonathan Kliem +# 2021 Frédéric Chapoton +# 2023 Matthias Koeppe +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +import itertools import os +import re +import shlex import subprocess +import sys + from argparse import ArgumentParser, FileType +from pathlib import Path + +from sage.doctest.control import skipfile +from sage.doctest.parsing import parse_file_optional_tags, parse_optional_tags, unparse_optional_tags, update_optional_tags +from sage.env import SAGE_ROOT +from sage.features import PythonModule +from sage.features.all import all_features, module_feature, name_feature from sage.misc.temporary_file import tmp_filename parser = ArgumentParser(description="Given an input file with doctests, this creates a modified file that passes the doctests (modulo any raised exceptions). By default, the input file is modified. You can also name an output file.") -parser.add_argument('-l', '--long', - dest='long', action="store_true", default=False) -parser.add_argument("input", help="input filename", - type=FileType('r')) -parser.add_argument('output', nargs='?', help="output filename", - type=FileType('w')) +parser.add_argument('-l', '--long', dest='long', action="store_true", default=False, + help="include tests tagged '# long time'") +parser.add_argument("--distribution", type=str, default='', + help="distribution package to test, e.g., 'sagemath-graphs', 'sagemath-combinat[modules]'; sets defaults for --venv and --environment") +parser.add_argument("--venv", type=str, default='', + help="directory name of a venv where 'sage -t' is to be run") +parser.add_argument("--environment", type=str, default='', + help="name of a module that provides the global environment for tests, e.g., 'sage.all__sagemath_modules'; implies --keep-both and --full-tracebacks") +parser.add_argument("--no-test", default=False, action="store_true", + help="do not run the doctester, only rewrite '# optional/needs' tags; implies --only-tags") +parser.add_argument("--full-tracebacks", default=False, action="store_true", + help="include full tracebacks rather than '...'") +parser.add_argument("--only-tags", default=False, action="store_true", + help="only add '# optional/needs' tags where needed, ignore other failures") +parser.add_argument("--probe", metavar="FEATURES", type=str, default='', + help="check whether '# optional/needs' tags are still needed, remove these") +parser.add_argument("--keep-both", default=False, action="store_true", + help="do not replace test results; duplicate the test instead, showing both results, and mark both copies '# optional'") +parser.add_argument("--overwrite", default=False, action="store_true", + help="never interpret a second filename as OUTPUT; overwrite the source files") +parser.add_argument("--no-overwrite", default=False, action="store_true", + help="never interpret a second filename as OUTPUT; output goes to files named INPUT.fixed") +parser.add_argument("filename", nargs='*', help="input filenames; or (deprecated) INPUT_FILENAME OUTPUT_FILENAME if exactly two filenames are given and neither --overwrite nor --no-overwrite is present", + type=str) args = parser.parse_args() -# set input and output files -test_file = args.input -src_in = test_file.read() -test_file.close() -# put the output of the test into sage's temporary directory -doc_file = tmp_filename() -os.system('sage -t %s %s > %s' % ('--long' if args.long else '', - test_file.name, doc_file)) -with open(doc_file, 'r') as doc: - doc_out = doc.read() -sep = "**********************************************************************\n" +runtest_default_environment = "sage.repl.ipython_kernel.all_jupyter" + +def default_venv_environment_from_distribution(): + if args.distribution: + # shortcuts / variants + args.distribution = args.distribution.replace('_', '-') + if not (args.distribution.startswith('sagemath-') + or args.distribution.startswith('sage-')): + args.distribution = f'sagemath-{args.distribution}' + # extras + m = re.fullmatch(r'([^[]*)(\[([^]]*)\])?', args.distribution) + plain_distribution, extras = m.group(1), m.group(3) + tox_env_name = 'sagepython-sagewheels-nopypi-norequirements' + if extras: + tox_env_name += '-' + extras.replace(',', '-') + default_venv = os.path.join(SAGE_ROOT, 'pkgs', plain_distribution, '.tox', tox_env_name) + default_environment = 'sage.all__' + plain_distribution.replace('-', '_') + else: + default_venv = '' + default_environment = runtest_default_environment + return default_venv, default_environment -doctests = doc_out.split(sep) -src_in_lines = src_in.splitlines() +default_venv, default_environment = default_venv_environment_from_distribution() -for block in doctests: - if 'Failed example:' not in block: - continue # sanity checking, but shouldn't happen +if not args.venv: + args.venv = default_venv +if not args.environment: + args.environment = default_environment +if args.distribution or args.venv != default_venv or args.environment != default_environment: + args.keep_both = args.full_tracebacks = True + +venv_explainers = [] + +if args.venv: + if m := re.search(f'pkgs/(sage[^/]*)/[.]tox/((sagepython|sagewheels|nopypi|norequirements)-*)*([^/]*)$', + args.venv): + args.distribution, extras = m.group(1), m.group(4) + if extras: + args.distribution += '[' + extras.replace('-', ',') + ']' + default_venv_given_distribution, default_environment_given_distribution = default_venv_environment_from_distribution() + + if (Path(args.venv).resolve() == Path(default_venv_given_distribution).resolve() + or args.environment == default_environment_given_distribution): + venv_explainers.append(f'--distribution {shlex.quote(args.distribution)}') + default_venv, default_environment = default_venv_given_distribution, default_environment_given_distribution + +if Path(args.venv).resolve() != Path(default_venv).resolve(): + venv_explainers.append(f'--venv {shlex.quote(args.venv)}') +if args.environment != default_environment: + venv_explainers.append(f'--environment {args.environment}') + +if venv_explainers: + venv_explainer = ' (with ' + ' '.join(venv_explainers) + ')' +else: + venv_explainer = '' + + +def process_block(block, src_in_lines, file_optional_tags): # Extract the line, what was expected, and was got. - line = block.find('line ') # block should contain: 'line ##, in ...', where ## is an integer - comma = block.find(', in ') # we try to extract the line number which gives the test failure - if line == -1 or comma == -1: - continue # but if something goes wrong we give up - line_num = eval(block[line + 5:comma]) - - # Take care of multiline examples. - if 'Expected' in block: - i1 = block.index('Failed example') - i2 = block.index('Expected') - example_len = block[i1:i2].count('\n') - 1 - line_num += example_len - 1 - - if 'Expected nothing' in block: - exp = block.find('Expected nothing') - block = '\n' + block[exp + 17:] # so that split('\nGot:\n') does not fail below - elif 'Expected:' in block: - exp = block.find('Expected:\n') - block = block[exp + 10:] - elif 'Exception raised' in block: - exp = block.find('Exception raised') - block = '\nGot:\n' + block[exp + 17:] + if not (m := re.match('File "([^"]*)", line ([0-9]+), in ', block)): + return + filename = m.group(1) + first_line_num = line_num = int(m.group(2)) # 1-based line number of the first line of the example + + if m := re.search(r"using.*block-scoped tag.*'(sage: .*)'.*to avoid repeating the tag", block): + indent = (len(src_in_lines[first_line_num - 1]) - len(src_in_lines[first_line_num - 1].lstrip())) + src_in_lines[line_num - 2] += '\n' + ' ' * indent + m.group(1) + + if m := re.search(r"updating.*block-scoped tag.*'sage: (.*)'.*to avoid repeating the tag", block): + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + tags=parse_optional_tags('# ' + m.group(1))) + + if m := re.search(r"referenced here was set only in doctest marked '# (optional|needs)[-: ]*([^;']*)", block): + optional = m.group(2).split() + if src_in_lines[first_line_num - 1].strip() in ['"""', "'''"]: + # This happens due to a virtual doctest in src/sage/repl/user_globals.py + return + optional = set(optional) - set(file_optional_tags) + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=optional) + + if m := re.search(r"tag '# (optional|needs)[-: ]([^;']*)' may no longer be needed", block): + optional = m.group(2).split() + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + remove_tags=optional) + + if m2 := re.search('(Expected:|Expected nothing|Exception raised:)\n', block): + m1 = re.search('Failed example:\n', block) + line_num += block[m1.end() : m2.start()].count('\n') - 1 + # Now line_num is the 1-based line number of the last line of the example + + if m2.group(1) == 'Expected nothing': + expected = '' + block = '\n' + block[m2.end():] # so that split('\nGot:\n') does not fail below + elif m2.group(1) == 'Exception raised:': + # In this case, the doctester does not show the expected output, + # so we do not know how many lines it spans; so we check for the next prompt or + # docstring end. + expected = [] + indentation = ' ' * (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + i = line_num + while ((not src_in_lines[i].rstrip() or src_in_lines[i].startswith(indentation)) + and not re.match(' *(sage:|""")', src_in_lines[i])): + expected.append(src_in_lines[i]) + i += 1 + block = '\n'.join(expected) + '\nGot:\n' + block[m2.end():] + else: + block = block[m2.end():] else: - continue + return + # Error testing. + if m := re.search(r"ModuleNotFoundError: No module named '([^']*)'", block): + module = m.group(1) + asked_why = re.search('#.*(why|explain)', src_in_lines[first_line_num - 1]) + optional = module_feature(module) + if optional and optional.name not in file_optional_tags: + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=[optional.name]) + if not asked_why: + # When no explanation has been demanded, + # we just mark the doctest with the feature + return + # Otherwise, continue and show the backtrace as 'GOT' + if 'Traceback (most recent call last):' in block: + expected, got = block.split('\nGot:\n') - got = got.splitlines() - got = [' Traceback (most recent call last):', ' ...', got[-1]] + if args.full_tracebacks: + if re.fullmatch(' *\n', got): + got = got[re.end(0):] + # don't show doctester internals (anything before first "" frame + if m := re.search('( *Traceback.*\n *)(?s:.*?)(^ *File "]*)>', got, re.MULTILINE): + got = m.group(1) + '...\n' + m.group(2) + '...' + got[m.end(3):] + while m := re.search(' *File "]*)>', got): + got = got[:m.start(1)] + '...' + got[m.end(1):] + # simplify filenames shown in backtrace + while m := re.search('"([-a-zA-Z0-9._/]*/site-packages)/sage/', got): + got = got[:m.start(1)] + '...' + got[m.end(1):] + + last_frame = got.rfind('File "') + if (last_frame >= 0 + and (index_NameError := got.rfind("NameError:")) >= 0 + and got[last_frame:].startswith('File "\n': expected = block[:-22] got = [''] @@ -86,21 +248,37 @@ for block in doctests: expected, got = block.split('\nGot:\n') got = got.splitlines() # got can't be the empty string + if args.only_tags: + return + + expected = expected.splitlines() + + if args.keep_both: + test_lines = ([update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=[f'GOT{venv_explainer}'])] + + src_in_lines[first_line_num : line_num]) + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=['EXPECTED']) + indent = (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + line_num += len(expected) # skip to the last line of the expected output + src_in_lines[line_num - 1] += '\n'.join([''] + test_lines) # 2nd copy of the test + # now line_num is the last line of the 2nd copy of the test + expected = [] + # If we expected nothing, and got something, then we need to insert the line before line_num # and match indentation with line number line_num-1 - if expected == '': - indent = (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + if not expected: + indent = (len(src_in_lines[first_line_num - 1]) - len(src_in_lines[first_line_num - 1].lstrip())) src_in_lines[line_num - 1] += '\n' + '\n'.join('%s%s' % (' ' * indent, line.lstrip()) for line in got) - continue + return - # Guess how much extra indenting got needs to match with the indentation + # Guess how much extra indenting ``got`` needs to match with the indentation # of src_in_lines - we match the indentation with the line in ``got`` which # has the smallest indentation after lstrip(). Note that the amount of indentation # required could be negative if the ``got`` block is indented. In this case # ``indent`` is set to zero. - indent = max(0, (len(src_in_lines[line_num]) - len(src_in_lines[line_num].lstrip()) - min(len(got[j]) - len(got[j].lstrip()) for j in range(len(got))))) - - expected = expected.splitlines() + indent = max(0, (len(src_in_lines[line_num]) - len(src_in_lines[line_num].lstrip()) + - min(len(got[j]) - len(got[j].lstrip()) for j in range(len(got))))) # Double check that what was expected was indeed in the source file and if # it is not then then print a warning for the user which contains the @@ -111,7 +289,7 @@ for block in doctests: txt = "Did not manage to replace\n%s\n%s\n%s\nwith\n%s\n%s\n%s" warnings.warn(txt % ('>' * 40, '\n'.join(expected), '>' * 40, '<' * 40, '\n'.join(got), '<' * 40)) - continue + return # If we got something when we expected nothing then we delete the line from the # output, otherwise, add all of what we `got` onto the end of src_in_lines[line_num] @@ -125,28 +303,109 @@ for block in doctests: for i in range(1, len(expected)): src_in_lines[line_num + i] = None -# Overwrite the source (or output file specified on the command line) -if args.output: - test_output = args.output + +# set input and output files +if len(args.filename) == 2 and not args.overwrite and not args.no_overwrite: + inputs, outputs = [args.filename[0]], [args.filename[1]] + print("sage-fixdoctests: When passing two filenames, the second one is taken as an output filename; " + "this is deprecated. To pass two input filenames, use the option --overwrite.") +elif args.no_overwrite: + inputs, outputs = args.filename, [input + ".fixed" for input in args.filename] else: - test_output = open(test_file.name, 'w') + inputs = outputs = args.filename + +# Test the doctester, putting the output of the test into sage's temporary directory +if not args.no_test: + executable = f'{os.path.relpath(args.venv)}/bin/sage' if args.venv else 'sage' + environment_args = f'--environment {args.environment} ' if args.environment != runtest_default_environment else '' + long_args = f'--long ' if args.long else '' + probe_args = f'--probe {shlex.quote(args.probe)} ' if args.probe else '' + lib_args = f'--if-installed ' if args.venv else '' + doc_file = tmp_filename() + if args.venv or environment_args: + input = os.path.join(os.path.relpath(SAGE_ROOT), 'src', 'sage', 'version.py') + cmdline = f'{shlex.quote(executable)} -t {environment_args}{long_args}{probe_args}{lib_args}{shlex.quote(input)}' + print(f'Running "{cmdline}"') + if status := os.waitstatus_to_exitcode(os.system(f'{cmdline} > {shlex.quote(doc_file)}')): + print(f'Doctester exited with error status {status}') + sys.exit(status) -for line in src_in_lines: - if line is None: +for input, output in zip(inputs, outputs): + if (skipfile_result := skipfile(input, True, log=print)) is True: continue - test_output.write(line) - test_output.write('\n') -test_output.close() + if args.no_test: + doc_out = '' + else: + # Run the doctester, putting the output of the test into sage's temporary directory + cmdline = f'{shlex.quote(executable)} -t {environment_args}{long_args}{probe_args}{lib_args}{shlex.quote(input)}' + print(f'Running "{cmdline}"') + os.system(f'{cmdline} > {shlex.quote(doc_file)}') + + with open(doc_file, 'r') as doc: + doc_out = doc.read() -# Show summary of changes -if args.output: - print('The fixed doctests have been saved as {0}.'.format(test_output.name)) -else: - from sage.env import SAGE_ROOT - relative = os.path.relpath(test_output.name, SAGE_ROOT) - print(relative) - if relative.startswith('..'): - print('Fixed source file is not part of Sage.') + # echo control messages + for m in re.finditer('^Skipping .*', doc_out, re.MULTILINE): + print('sage-runtests: ' + m.group(0)) + break else: - subprocess.call(['git', 'diff', relative], cwd=SAGE_ROOT) + sep = "**********************************************************************\n" + doctests = doc_out.split(sep) + + with open(input, 'r') as test_file: + src_in = test_file.read() + 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))) + + for block in doctests: + process_block(block, src_in_lines, file_optional_tags) + + # Now source line numbers do not matter any more, and lines can be real lines again + src_in_lines = list(itertools.chain.from_iterable( + [] if line is None else [''] if not line else line.splitlines() + for line in src_in_lines)) + + # Remove duplicate optional tags and rewrite all '# optional' that should be '# needs' + persistent_optional_tags = {} + 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} + line = update_optional_tags(line, tags=persistent_optional_tags, force_rewrite='standard') + if not line.rstrip(): + # persistent (block-scoped or file-scoped) tag was removed, so remove the whole line + line = None + else: + tags = {tag: explanation + for tag, explanation in tags.items() + 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') + src_in_lines[i] = line + elif line.strip() in ['', '"""', "'''"]: # Blank line or end of docstring + persistent_optional_tags = {} + + if src_in_lines != shallow_copy_of_src_in_lines: + with open(output, 'w') as test_output: + for line in src_in_lines: + if line is None: + continue + test_output.write(line) + test_output.write('\n') + + # Show summary of changes + if input != output: + print("The fixed doctests have been saved as '{0}'.".format(output)) + else: + relative = os.path.relpath(output, SAGE_ROOT) + print(f"The input file '{output}' has been overwritten.") + if not relative.startswith('..'): + subprocess.call(['git', '--no-pager', 'diff', relative], cwd=SAGE_ROOT) + else: + print(f"No fixes made in '{input}'") diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 4131161647d..4d606c5a3a8 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -56,6 +56,9 @@ if __name__ == "__main__": help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' 'if "optional" is listed, will also hide features corresponding to optional packages.') + parser.add_argument("--probe", metavar="FEATURES", default="", + help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' + 'report the tests that pass nevertheless') parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) @@ -66,6 +69,7 @@ if __name__ == "__main__": parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") + parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") diff --git a/src/doc/de/tutorial/latex.rst b/src/doc/de/tutorial/latex.rst index c664bb970cd..77c0834ab77 100644 --- a/src/doc/de/tutorial/latex.rst +++ b/src/doc/de/tutorial/latex.rst @@ -319,17 +319,18 @@ lässt. Diese Liste wird verwaltet durch die Befehle ``latex.add_to_mathjax_avoid_list`` und ``latex.mathjax_avoid_list``. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Nehmen wir an ein LaTeX-Ausdruck wurde im Notebook durch ``view()`` diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index b323a7eaee4..091cb47283b 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -935,6 +935,15 @@ written. Sage does not know about the function ``AA()`` by default, so it needs to be imported before it is tested. Hence the first line in the example. + All blocks within the same docstring are linked: Variables set + in a doctest keep their values for the remaining doctests within the + same docstring. It is good practice to use different variable names for different + values, as it makes the data flow in the examples easier to understand + for human readers. (It also makes the data flow analysis in the + Sage doctester more precise.) In particular, when unrelated examples + appear in the same docstring, do not use the same variable name + for both examples. + - **Preparsing:** As in Sage's console, `4/3` returns `4/3` and not `1.3333333333333333` as in Python 3.8. Testing occurs with full Sage preparsing of input within the standard Sage shell environment, as @@ -958,6 +967,78 @@ written. 5 7 +- **Wrap long doctest lines:** Note that all doctests in EXAMPLES blocks + get formatted as part of our HTML and PDF reference manuals. Our HTML manuals + are formatted using the responsive design provided by the + :ref:`Furo theme `. Even when the browser window is expanded to + make use of the full width of a wide desktop screen, the style will not + allow code boxes to grow arbitrarily wide. + + It is best to wrap long lines when possible so that readers do not have to + scroll horizontally (back and forth) to follow an example. + + - Try to wrap long lines somewhere around columns 80 to 88 + and try to never exceed column 95 in the source file. + (Columns numbers are from the left margin in the source file; + these rules work no matter how deep the docstring may be nested + because also the formatted output will be nested.) + + - If you have to break an expression at a place that is not already + nested in parentheses, wrap it in parentheses:: + + sage: (len(list(Permutations(['a', 'b', 'c', 'd', 'e', 'f', 'g']))) + ....: == len(list(Permutations(7)))) + True + + - If the output in your only example is very wide and cannot be reasonably + reformatted to fit (for example, large symbolic matrices or numbers with many digits), + consider showing a smaller example first. + + - No need to wrap long ``import`` statements. Typically, the ``import`` statements + are not the interesting parts of the doctests. Users only need to be able to + copy-paste them into a Sage session or source file:: + + sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict, MPolynomialRing_polydict_domain # this is fine + + - Wrap and indent long output to maximize readability in the source code + and in the HTML output. But do not wrap strings:: + + sage: from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_quasi + sage: P. = ProjectiveSpace(2, ZZ) + sage: S = P.subscheme([]) + sage: T = P.subscheme([x - y]) + sage: U = AlgebraicScheme_quasi(S, T); U + Quasi-projective subscheme X - Y of Projective Space of dimension 2 + over Integer Ring, + where X is defined by: (no polynomials) + and Y is defined by: x - y + sage: U._repr_() # this is fine + 'Quasi-projective subscheme X - Y of Projective Space of dimension 2 over Integer Ring, where X is defined by:\n (no polynomials)\nand Y is defined by:\n x - y' + + Also, if there is no whitespace in the doctest output where you could wrap the line, + do not add such whitespace. Just don't wrap the line:: + + sage: B47 = RibbonGraph(4,7, bipartite=True); B47 + Ribbon graph of genus 9 and 1 boundary components + sage: B47.sigma() # this is fine + (1,2,3,4,5,6,7)(8,9,10,11,12,13,14)(15,16,17,18,19,20,21)(22,23,24,25,26,27,28)(29,30,31,32)(33,34,35,36)(37,38,39,40)(41,42,43,44)(45,46,47,48)(49,50,51,52)(53,54,55,56) + + - Doctest tags for modularization purposes such as ``# needs sage.modules`` + (see :ref:`section-further_conventions`) should be aligned at column 88. + Clean lines from consistent alignment help reduce visual clutter. + Moreover, at the maximum window width, only the word ``# needs`` will be + visible in the HTML output without horizontal scrolling, striking a + thoughtfully chosen balance between presenting + the information and reducing visual clutter. (How much can be seen may be + browser-dependent, of course.) In visually dense doctests, you can try to sculpt out visual space to separate + the test commands from the annotation. + + - Doctest tags such as ``# optional - pynormaliz`` that make the doctest + conditional on the presence of optional packages, on the other hand, + should be aligned so that they are visible without having to scroll horizontally. + The :ref:`doctest fixer ` uses + tab stops at columns 48, 56, 64, ... for these tags. + - **Python3 print:** Python3 syntax for print must be used in Sage code and doctests. If you use an old-style print in doctests, it will raise a SyntaxError:: @@ -1131,8 +1212,9 @@ framework. Here is a comprehensive list: Neither of this applies to files or directories which are explicitly given as command line arguments: those are always tested. -- **optional:** A line flagged with ``optional - keyword`` is not tested unless - the ``--optional=keyword`` flag is passed to ``sage -t`` (see +- **optional/needs:** A line tagged with ``optional - FEATURE`` + or ``needs FEATURE`` is not tested unless the ``--optional=KEYWORD`` flag + is passed to ``sage -t`` (see :ref:`section-optional-doctest-flag`). The main applications are: - **optional packages:** When a line requires an optional package to be @@ -1140,26 +1222,6 @@ framework. Here is a comprehensive list: sage: SloaneEncyclopedia[60843] # optional - sloane_database - .. NOTE:: - - If one of the first 10 lines of a file starts with any of - ``r""" sage.doctest: optional - keyword`` - (or ``""" sage.doctest: optional - keyword`` - or ``# sage.doctest: optional - keyword`` - or ``% sage.doctest: optional - keyword`` - or ``.. sage.doctest: optional - keyword``, - or any of these with different spacing), - then that file will be skipped unless - the ``--optional=keyword`` flag is passed to ``sage -t``. - - This does not apply to files which are explicitly given - as command line arguments: those are always tested. - - If you add such a line to a file, you are strongly encouraged - to add a note to the module-level documentation, saying that - the doctests in this file will be skipped unless the - appropriate conditions are met. - - **internet:** For lines that require an internet connection:: sage: oeis(60843) # optional - internet @@ -1167,8 +1229,8 @@ framework. Here is a comprehensive list: n-state Turing machine can make on an initially blank tape before eventually halting. - - **bug:** For lines that describe bugs. Alternatively, use ``# known bug`` - instead: it is an alias for ``optional bug``. + - **known bugs:** For lines that describe known bugs, you can use ``# optional - bug``, + although ``# known bug`` is preferred. .. CODE-BLOCK:: rest @@ -1179,21 +1241,55 @@ framework. Here is a comprehensive list: sage: 2+2 # known bug 5 + - **modularization:** To enable + :ref:`separate testing of the distribution packages ` + of the modularized Sage library, doctests that depend on features provided + by other distribution packages can be tagged ``# needs FEATURE``. + For example: + + .. CODE-BLOCK:: rest + + Consider the following calculation:: + + sage: a = AA(2).sqrt() # needs sage.rings.number_field + sage: b = sqrt(3) # needs sage.symbolic + sage: a + AA(b) # needs sage.rings.number_field sage.symbolic + 3.146264369941973? + .. NOTE:: - - Any words after ``# optional`` are interpreted as a list of + - Any words after ``# optional`` and ``# needs`` are interpreted as a list of package (spkg) names or other feature tags, separated by spaces. - Any punctuation other than underscores (``_``) and periods (``.``), that is, commas, hyphens, semicolons, ..., after the first word ends the list of packages. Hyphens or colons between the word ``optional`` and the first package name are allowed. Therefore, - you should not write ``optional: needs package CHomP`` but simply - ``optional: CHomP``. + you should not write ``# optional - depends on package CHomP`` but simply + ``# optional - CHomP``. - - Optional tags are case-insensitive, so you could also write ``optional: + - Optional tags are case-insensitive, so you could also write ``# optional - chOMP``. + If ``# optional`` or ``# needs`` is placed right after the ``sage:`` prompt, + it is a block-scoped tag, which applies to all doctest lines until + a blank line is encountered. + + These tags can also be applied to an entire file. If one of the first 10 lines + of a file starts with any of ``r""" sage.doctest: optional - FEATURE``, + ``# sage.doctest: needs FEATURE``, or ``.. sage.doctest: optional - FEATURE`` + (in ``.rst`` files), etc., then this applies to all doctests in this file. + + When a file is skipped that was explicitly given as a command line argument, + a warning is displayed. + + .. NOTE:: + + If you add such a line to a file, you are strongly encouraged + to add a note to the module-level documentation, saying that + the doctests in this file will be skipped unless the + appropriate conditions are met. + - **indirect doctest:** in the docstring of a function ``A(...)``, a line calling ``A`` and in which the name ``A`` does not appear should have this flag. This prevents ``sage --coverage `` from reporting the docstring as diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 2bc8f4765bf..db4fc7f9a83 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -13,11 +13,11 @@ its documentation. Testing can be performed using one thread or multiple threads. After compiling a source version of Sage, doctesting can be run on the whole Sage library, on all modules under a given directory, or on a specified module only. For the purposes of this -chapter, suppose we have compiled Sage 6.0 from source and the top -level Sage directory is:: +chapter, suppose we have compiled Sage from source and the top +level directory is:: - [jdemeyer@sage sage-6.0]$ pwd - /scratch/jdemeyer/build/sage-6.0 + [jdemeyer@localhost sage]$ pwd + /home/jdemeyer/sage See the section :ref:`chapter-testing` for information on Sage's automated testing process. The general syntax for doctesting is as @@ -43,7 +43,7 @@ Say we want to run all tests in the sudoku module top level Sage directory of our local Sage installation. Now we can start doctesting as demonstrated in the following terminal session:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-36-49-d82849c6. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -63,7 +63,7 @@ one module so it is not surprising that the total testing time is approximately the same as the time required to test only that one module. Notice that the syntax is:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-39-02-da6accbb. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -77,7 +77,7 @@ module. Notice that the syntax is:: but not:: - [jdemeyer@sage sage-6.0]$ ./sage -t sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t sage/games/sudoku.py Running doctests with ID 2012-07-03-03-40-53-6cc4f29f. No files matching sage/games/sudoku.py No files to doctest @@ -85,11 +85,11 @@ but not:: We can also first ``cd`` to the directory containing the module ``sudoku.py`` and doctest that module as follows:: - [jdemeyer@sage sage-6.0]$ cd src/sage/games/ - [jdemeyer@sage games]$ ls + [jdemeyer@localhost sage]$ cd src/sage/games/ + [jdemeyer@localhost games]$ ls __init__.py hexad.py sudoku.py sudoku_backtrack.pyx all.py quantumino.py sudoku_backtrack.c - [jdemeyer@sage games]$ ../../../../sage -t sudoku.py + [jdemeyer@localhost games]$ ../../../../sage -t sudoku.py Running doctests with ID 2012-07-03-03-41-39-95ebd2ff. Doctesting 1 file. sage -t sudoku.py @@ -108,7 +108,7 @@ installation is a recipe for confusion. You can also run the Sage doctester as follows:: - [jdemeyer@sage sage-6.0]$ ./sage -tox -e doctest -- src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -tox -e doctest -- src/sage/games/sudoku.py See :ref:`chapter-tools` for more information about tox. @@ -126,7 +126,7 @@ our system has multiple Sage installations. For example, the following syntax is acceptable because we explicitly specify the Sage installation in the current ``SAGE_ROOT``:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-43-24-a3449f54. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -137,7 +137,7 @@ installation in the current ``SAGE_ROOT``:: Total time for all tests: 4.9 seconds cpu time: 3.6 seconds cumulative wall time: 3.6 seconds - [jdemeyer@sage sage-6.0]$ ./sage -t "src/sage/games/sudoku.py" + [jdemeyer@localhost sage]$ ./sage -t "src/sage/games/sudoku.py" Running doctests with ID 2012-07-03-03-43-54-ac8ca007. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -156,10 +156,10 @@ Sage installation (if it exists): :: - [jdemeyer@sage sage-6.0]$ sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ sage -t src/sage/games/sudoku.py sage -t "src/sage/games/sudoku.py" ********************************************************************** - File "/home/jdemeyer/sage/sage-6.0/src/sage/games/sudoku.py", line 515: + File "/home/jdemeyer/sage/src/sage/games/sudoku.py", line 515: sage: next(h.solve(algorithm='backtrack')) Exception raised: Traceback (most recent call last): @@ -215,7 +215,7 @@ and then using four threads. For this example, suppose we want to test all the modules under ``sage/crypto/``. We can use a syntax similar to that shown above to achieve this:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/crypto + [jdemeyer@localhost sage]$ ./sage -t src/sage/crypto Running doctests with ID 2012-07-03-03-45-40-7f837dcf. Doctesting 24 files. sage -t src/sage/crypto/__init__.py @@ -276,7 +276,7 @@ that shown above to achieve this:: Now we do the same thing, but this time we also use the optional argument ``--long``:: - [jdemeyer@sage sage-6.0]$ ./sage -t --long src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -t --long src/sage/crypto/ Running doctests with ID 2012-07-03-03-48-11-c16721e6. Doctesting 24 files. sage -t --long src/sage/crypto/__init__.py @@ -372,7 +372,7 @@ as taking a long time: Now we doctest the same directory in parallel using 4 threads:: - [jdemeyer@sage sage-6.0]$ ./sage -tp 4 src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -tp 4 src/sage/crypto/ Running doctests with ID 2012-07-07-00-11-55-9b17765e. Sorting sources by runtime so that slower doctests are run first.... Doctesting 24 files using 4 threads. @@ -430,7 +430,7 @@ Now we doctest the same directory in parallel using 4 threads:: Total time for all tests: 12.9 seconds cpu time: 30.5 seconds cumulative wall time: 31.7 seconds - [jdemeyer@sage sage-6.0]$ ./sage -tp 4 --long src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -tp 4 --long src/sage/crypto/ Running doctests with ID 2012-07-07-00-13-04-d71f3cd4. Sorting sources by runtime so that slower doctests are run first.... Doctesting 24 files using 4 threads. @@ -504,7 +504,7 @@ to doctest the main library using multiple threads. When doing release management or patching the main Sage library, a release manager would parallel test the library using 10 threads with the following command:: - [jdemeyer@sage sage-6.0]$ ./sage -tp 10 --long src/ + [jdemeyer@localhost sage]$ ./sage -tp 10 --long src/ Another way is run ``make ptestlong``, which builds Sage (if necessary), builds the Sage documentation (if necessary), and then runs parallel @@ -516,7 +516,7 @@ the number of CPU cores (as determined by the Python function In any case, this will test the Sage library with multiple threads:: - [jdemeyer@sage sage-6.0]$ make ptestlong + [jdemeyer@localhost sage]$ make ptestlong Any of the following commands would also doctest the Sage library or one of its clones: @@ -577,7 +577,7 @@ Doctesting also works fine for files not in the Sage library. For example, suppose we have a Python script called ``my_python_script.py``:: - [mvngu@sage build]$ cat my_python_script.py + [mvngu@localhost sage]$ cat my_python_script.py from sage.all_cmdline import * # import sage library def square(n): @@ -593,7 +593,7 @@ example, suppose we have a Python script called Then we can doctest it just as with Sage library files:: - [mvngu@sage sage-6.0]$ ./sage -t my_python_script.py + [mvngu@localhost sage]$ ./sage -t my_python_script.py Running doctests with ID 2012-07-07-00-17-56-d056f7c0. Doctesting 1 file. sage -t my_python_script.py @@ -608,7 +608,7 @@ Then we can doctest it just as with Sage library files:: Doctesting can also be performed on Sage scripts. Say we have a Sage script called ``my_sage_script.sage`` with the following content:: - [mvngu@sage sage-6.0]$ cat my_sage_script.sage + [mvngu@localhost sage]$ cat my_sage_script.sage def cube(n): r""" Return the cube of n. @@ -622,7 +622,7 @@ script called ``my_sage_script.sage`` with the following content:: Then we can doctest it just as for Python files:: - [mvngu@sage build]$ sage-6.0/sage -t my_sage_script.sage + [mvngu@localhost sage]$ ./sage -t my_sage_script.sage Running doctests with ID 2012-07-07-00-20-06-82ee728c. Doctesting 1 file. sage -t my_sage_script.sage @@ -637,8 +637,8 @@ Then we can doctest it just as for Python files:: Alternatively, we can preparse it to convert it to a Python script, and then doctest that:: - [mvngu@sage build]$ sage-6.0/sage --preparse my_sage_script.sage - [mvngu@sage build]$ cat my_sage_script.sage.py + [mvngu@localhost sage]$ ./sage --preparse my_sage_script.sage + [mvngu@localhost sage]$ cat my_sage_script.sage.py # This file was *autogenerated* from the file my_sage_script.sage. from sage.all_cmdline import * # import sage library _sage_const_3 = Integer(3) @@ -652,7 +652,7 @@ and then doctest that:: 8 """ return n**_sage_const_3 - [mvngu@sage build]$ sage-6.0/sage -t my_sage_script.sage.py + [mvngu@localhost sage]$ ./sage -t my_sage_script.sage.py Running doctests with ID 2012-07-07-00-26-46-2bb00911. Doctesting 1 file. sage -t my_sage_script.sage.py @@ -716,7 +716,7 @@ Use the ``--long`` flag to run doctests that have been marked with the comment ``# long time``. These tests are normally skipped in order to reduce the time spent running tests:: - [roed@sage sage-6.0]$ sage -t src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t src/sage/rings/tests.py Running doctests with ID 2012-06-21-16-00-13-40835825. Doctesting 1 file. sage -t tests.py @@ -730,7 +730,7 @@ reduce the time spent running tests:: In order to run the long tests as well, do the following:: - [roed@sage sage-6.0]$ sage -t --long src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t --long src/sage/rings/tests.py Running doctests with ID 2012-06-21-16-02-05-d13a9a24. Doctesting 1 file. sage -t tests.py @@ -747,7 +747,7 @@ To find tests that take longer than the allowed time use the print a warning if they take longer than 1.0 second. Note that this is a warning, not an error:: - [roed@sage sage-6.0]$ sage -t --warn-long src/sage/rings/factorint.pyx + [roed@localhost sage]$ ./sage -t --warn-long src/sage/rings/factorint.pyx Running doctests with ID 2012-07-14-03-27-03-2c952ac1. Doctesting 1 file. sage -t --warn-long src/sage/rings/factorint.pyx @@ -781,7 +781,7 @@ a warning, not an error:: You can also pass in an explicit amount of time:: - [roed@sage sage-6.0]$ sage -t --long --warn-long 2.0 src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t --long --warn-long 2.0 src/sage/rings/tests.py Running doctests with ID 2012-07-14-03-30-13-c9164c9d. Doctesting 1 file. sage -t --long --warn-long 2.0 tests.py @@ -808,7 +808,7 @@ Finally, you can disable any warnings about long tests with Doctests start from a random seed:: - [kliem@sage sage-9.2]$ sage -t src/sage/doctest/tests/random_seed.rst + [kliem@localhost sage]$ ./sage -t src/sage/doctest/tests/random_seed.rst Running doctests with ID 2020-06-23-23-22-59-49f37a55. ... Doctesting 1 file. @@ -834,7 +834,9 @@ Doctests start from a random seed:: This seed can be set explicitly to reproduce possible failures:: - [kliem@sage sage-9.2]$ sage -t --warn-long 89.5 --random-seed=112986622569797306072457879734474628454 src/sage/doctest/tests/random_seed.rst + [kliem@localhost sage]$ ./sage -t --warn-long 89.5 \ + --random-seed=112986622569797306072457879734474628454 \ + src/sage/doctest/tests/random_seed.rst Running doctests with ID 2020-06-23-23-24-28-14a52269. ... Doctesting 1 file. @@ -869,13 +871,12 @@ Run optional doctests You can run tests that require optional packages by using the ``--optional`` flag. Obviously, you need to have installed the -necessary optional packages in order for these tests to succeed. See -http://www.sagemath.org/packages/optional/ in order to download -optional packages. +necessary optional packages in order for these tests to succeed. By default, Sage only runs doctests that are not marked with the ``optional`` tag. This is equivalent to running :: - [roed@sage sage-6.0]$ sage -t --optional=sagemath_doc_html,sage src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=sagemath_doc_html,sage \ + src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-30-a368a200. Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -889,7 +890,8 @@ By default, Sage only runs doctests that are not marked with the ``optional`` ta If you want to also run tests that require magma, you can do the following:: - [roed@sage sage-6.0]$ sage -t --optional=sagemath_doc_html,sage,magma src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=sagemath_doc_html,sage,magma \ + src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-30-a00a7319 Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -903,7 +905,7 @@ If you want to also run tests that require magma, you can do the following:: In order to just run the tests that are marked as requiring magma, omit ``sage`` and ``sagemath_doc_html``:: - [roed@sage sage-6.0]$ sage -t --optional=magma src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=magma src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-33-a2bc1fdf Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -919,7 +921,7 @@ If you want Sage to detect external software or other capabilities (such as magma, latex, internet) automatically and run all of the relevant tests, then add ``external``:: - $ sage -t --optional=external src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=external src/sage/rings/real_mpfr.pyx Running doctests with ID 2016-03-16-14-10-21-af2ebb67. Using --optional=external External software to be detected: cplex,gurobi,internet,latex,macaulay2,magma,maple,mathematica,matlab,octave,scilab @@ -936,7 +938,7 @@ relevant tests, then add ``external``:: To run all tests, regardless of whether they are marked optional, pass ``all`` as the ``optional`` tag:: - [roed@sage sage-6.0]$ sage -t --optional=all src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=all src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-31-18-8c097f55 Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -957,7 +959,7 @@ than one thread. To run doctests in parallel use the ``--nthreads`` flag (``-p`` is a shortened version). Pass in the number of threads you would like to use (by default Sage just uses 1):: - [roed@sage sage-6.0]$ sage -tp 2 src/sage/doctest/ + [roed@localhost sage]$ ./sage -tp 2 src/sage/doctest/ Running doctests with ID 2012-06-22-19-09-25-a3afdb8c. Sorting sources by runtime so that slower doctests are run first.... Doctesting 8 files using 2 threads. @@ -993,12 +995,12 @@ short). In addition to testing the code in Sage's Python and Cython files, this command will run the tests defined in Sage's documentation as well as testing the Sage notebook:: - [roed@sage sage-6.0]$ sage -t -a + [roed@localhost sage]$ ./sage -t -a Running doctests with ID 2012-06-22-19-10-27-e26fce6d. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... Doctesting 2020 files. - sage -t /Users/roed/sage/sage-5.3/src/sage/plot/plot.py + sage -t /Users/roed/sage/src/sage/plot/plot.py [304 tests, 69.0 s] ... @@ -1014,7 +1016,8 @@ short) then you will drop into an interactive Python debugger whenever a Python exception occurs. As an example, I modified :mod:`sage.schemes.elliptic_curves.constructor` to produce an error:: - [roed@sage sage-6.0]$ sage -t --debug src/sage/schemes/elliptic_curves/constructor.py + [roed@localhost sage]$ ./sage -t --debug \ + src/sage/schemes/elliptic_curves/constructor.py Running doctests with ID 2012-06-23-12-09-04-b6352629. Doctesting 1 file. ********************************************************************** @@ -1023,22 +1026,22 @@ a Python exception occurs. As an example, I modified EllipticCurve([0,0]) Exception raised: Traceback (most recent call last): - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/doctest/forker.py", line 573, in _run + File ".../site-packages/sage/doctest/forker.py", line 573, in _run self.execute(example, compiled, test.globs) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/doctest/forker.py", line 835, in execute + File ".../site-packages/sage/doctest/forker.py", line 835, in execute exec compiled in globs File "", line 1, in EllipticCurve([Integer(0),Integer(0)]) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/constructor.py", line 346, in EllipticCurve + File ".../site-packages/sage/schemes/elliptic_curves/constructor.py", line 346, in EllipticCurve return ell_rational_field.EllipticCurve_rational_field(x, y) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_rational_field.py", line 216, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_rational_field.py", line 216, in __init__ EllipticCurve_number_field.__init__(self, Q, ainvs) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_number_field.py", line 159, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_number_field.py", line 159, in __init__ EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 156, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 156, in __init__ "Invariants %s define a singular curve."%ainvs ArithmeticError: Invariants [0, 0, 0, 0, 0] define a singular curve. - > /Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_generic.py(156)__init__() + > .../site-packages/sage/schemes/elliptic_curves/ell_generic.py(156)__init__() -> "Invariants %s define a singular curve."%ainvs (Pdb) l 151 if len(ainvs) == 2: @@ -1075,8 +1078,9 @@ you know what test caused the problem (if you want this output to appear in real time use the ``--verbose`` flag). To have doctests run under the control of gdb, use the ``--gdb`` flag:: - [roed@sage sage-6.0]$ sage -t --gdb src/sage/schemes/elliptic_curves/constructor.py - exec gdb --eval-commands="run" --args /home/roed/sage-9.7/local/var/lib/sage/venv-python3.9/bin/python3 sage-runtests --serial --timeout=0 --stats_path=/home/roed/.sage/timings2.json --optional=pip,sage,sage_spkg src/sage/schemes/elliptic_curves/constructor.py + [roed@localhost sage]$ ./sage -t --gdb \ + src/sage/schemes/elliptic_curves/constructor.py + exec gdb --eval-commands="run" --args /home/roed/sage/local/var/lib/sage/venv-python3.9/bin/python3 sage-runtests --serial --timeout=0 --stats_path=/home/roed/.sage/timings2.json --optional=pip,sage,sage_spkg src/sage/schemes/elliptic_curves/constructor.py GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later @@ -1110,7 +1114,7 @@ Once you're done fixing whatever problems where revealed by the doctests, you can rerun just those files that failed their most recent test by using the ``--failed`` flag (``-f`` for short):: - [roed@sage sage-6.0]$ sage -t -fa + [roed@localhost sage]$ ./sage -t -fa Running doctests with ID 2012-07-07-00-45-35-d8b5a408. Doctesting entire Sage library. Only doctesting files that failed last test. @@ -1138,7 +1142,8 @@ Show skipped optional tests To print a summary at the end of each file with the number of optional tests skipped, use the ``--show-skipped`` flag:: - [roed@sage sage-6.0]$ sage -t --show-skipped src/sage/rings/finite_rings/integer_mod.pyx + [roed@localhost sage]$ ./sage -t --show-skipped \ + src/sage/rings/finite_rings/integer_mod.pyx Running doctests with ID 2013-03-14-15-32-05-8136f5e3. Doctesting 1 file. sage -t sage/rings/finite_rings/integer_mod.pyx @@ -1163,7 +1168,7 @@ you to run tests repeatedly in an attempt to search for Heisenbugs. The flag ``--global-iterations`` takes an integer and runs the whole set of tests that many times serially:: - [roed@sage sage-6.0]$ sage -t --global-iterations 2 src/sage/sandpiles + [roed@localhost sage]$ ./sage -t --global-iterations 2 src/sage/sandpiles Running doctests with ID 2012-07-07-00-59-28-e7048ad9. Doctesting 3 files (2 global iterations). sage -t src/sage/sandpiles/__init__.py @@ -1194,7 +1199,7 @@ set of tests that many times serially:: You can also iterate in a different order: the ``--file-iterations`` flag runs the tests in each file ``N`` times before proceeding:: - [roed@sage sage-6.0]$ sage -t --file-iterations 2 src/sage/sandpiles + [roed@localhost sage]$ ./sage -t --file-iterations 2 src/sage/sandpiles Running doctests with ID 2012-07-07-01-01-43-8f954206. Doctesting 3 files (2 file iterations). sage -t src/sage/sandpiles/__init__.py @@ -1222,7 +1227,7 @@ On a slow machine the default timeout of 5 minutes may not be enough for the slowest files. Use the ``--timeout`` flag (``-T`` for short) to set it to something else:: - [roed@sage sage-6.0]$ sage -tp 2 --all --timeout 1 + [roed@localhost sage]$ ./sage -tp 2 --all --timeout 1 Running doctests with ID 2012-07-07-01-09-37-deb1ab83. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... @@ -1237,10 +1242,10 @@ Using absolute paths By default filenames are printed using relative paths. To use absolute paths instead pass in the ``--abspath`` flag:: - [roed@sage sage-6.0]$ sage -t --abspath src/sage/doctest/control.py + [roed@localhost sage]$ ./sage -t --abspath src/sage/doctest/control.py Running doctests with ID 2012-07-07-01-13-03-a023e212. Doctesting 1 file. - sage -t /home/roed/sage-6.0/src/sage/doctest/control.py + sage -t /home/roed/sage/src/sage/doctest/control.py [133 tests, 4.7 s] ------------------------------------------------------------------------ All tests passed! @@ -1258,7 +1263,7 @@ convenient to test only the files that have changed. To do so use the ``--new`` flag, which tests files that have been modified or added since the last commit:: - [roed@sage sage-6.0]$ sage -t --new + [roed@localhost sage]$ ./sage -t --new Running doctests with ID 2012-07-07-01-15-52-645620ee. Doctesting files changed since last git commit. Doctesting 1 file. @@ -1279,7 +1284,7 @@ By default, tests are run in the order in which they appear in the file. To run tests in a random order (which can reveal subtle bugs), use the ``--randorder`` flag and pass in a random seed:: - [roed@sage sage-6.0]$ sage -t --new --randorder 127 + [roed@localhost sage]$ ./sage -t --new --randorder 127 Running doctests with ID 2012-07-07-01-19-06-97c8484e. Doctesting files changed since last git commit. Doctesting 1 file. @@ -1309,7 +1314,7 @@ Auxiliary files To specify a logfile (rather than use the default which is created for ``sage -t --all``), use the ``--logfile`` flag:: - [roed@sage sage-6.0]$ sage -t --logfile test1.log src/sage/doctest/control.py + [roed@localhost sage]$ ./sage -t --logfile test1.log src/sage/doctest/control.py Running doctests with ID 2012-07-07-01-25-49-e7c0e52d. Doctesting 1 file. sage -t src/sage/doctest/control.py @@ -1320,7 +1325,7 @@ To specify a logfile (rather than use the default which is created for Total time for all tests: 6.7 seconds cpu time: 0.1 seconds cumulative wall time: 4.3 seconds - [roed@sage sage-6.0]$ cat test1.log + [roed@localhost sage]$ cat test1.log Running doctests with ID 2012-07-07-01-25-49-e7c0e52d. Doctesting 1 file. sage -t src/sage/doctest/control.py @@ -1338,9 +1343,315 @@ To give a json file storing the timings for each file, use the that slower tests are run first (and thus multiple processes are utilized most efficiently):: - [roed@sage sage-6.0]$ sage -tp 2 --stats-path ~/.sage/timings2.json --all + [roed@localhost sage]$ ./sage -tp 2 --stats-path ~/.sage/timings2.json --all Running doctests with ID 2012-07-07-01-28-34-2df4251d. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... Doctesting 2067 files using 2 threads. ... + +.. _section-doctesting-venv: + +Options for testing in virtual environments +------------------------------------------- + +The distribution packages of the modularized Sage library can be tested in virtual environments. +Sage has infrastructure to create such virtual environments using ``tox``, which is explained +in detail in :ref:`section-modularized-doctesting`. Our examples in this section +refer to this setting, but it applies the same to any user-created virtual environments. + +The virtual environments, set up in directories such as +``pkgs/sagemath-standard/.tox/sagepython-sagewheels-nopypi-norequirements`` +contain installations of built (non-editable) wheels. + +To test all modules of Sage that are installed in a virtual environment, +use the option ``--installed`` (instead of ``--all``):: + + [mkoeppe@localhost sage]$ pkgs/sagemath-standard/.tox/sagepython-.../sage -t \ + -p4 --installed + +This tests against the doctests as they appear in the installed copies of the files +(in ``site-packages/sage/...``). +Note that these installed copies should never be edited, as they can +be overwritten without warning. + +When testing a modularized distribution package other than sagemath-standard, +the top-level module :mod:`sage.all` is not available. Use the option ``--environment`` +to select an appropriate top-level module:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --installed + +To test the installed modules against the doctests as they appear in the source +tree (``src/sage/...``):: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + src/sage/structure + +Note that testing all doctests as they appear in the source tree does not make sense +because many of the source files may not be installed in the virtual environment. +Use the option ``--if-installed`` to skip the source files of all Python/Cython modules +that are not installed in the virtual environment:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --if-installed src/sage/schemes + +This option can also be combined with ``--all``:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --if-installed --all + + +.. _section-fixdoctests: + +The doctest fixer +================= + +Sage provides a development tool that assists with updating doctests. + + +Updating doctest outputs +------------------------ + +By default, ``./sage --fixdoctests`` runs the doctester and replaces the expected outputs +of all examples by the actual outputs from the current version of Sage:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests \ + --overwrite src/sage/arith/weird.py + +For example, when applied to this Python file:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 + | 5 + | sage: factor("91") + | "7" * "13" + | ... + +the doctest fixer edits the file as follows:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 + | 4 + | sage: factor("91") + | Traceback (most recent call last): + | ... + | TypeError: unable to factor '91' + | ... + +As this command edits the source file, it may be a good practice to first use ``git commit`` +to save any changes made in the file. + +After running the doctest fixer, it is a good idea to use ``git diff`` to check +all edits that the automated tool made. + +An alternative to this workflow is to use the option ``--keep-both``. When expected and +actual output of an example differ, it duplicates the example, marking the two copies +``# optional - EXPECTED`` and ``# optional - GOT``. (Thus, when re-running the doctester, +neither of the two copies is run; this makes ``./sage --fixdoctests`` idempotent.) + +When exceptions are expected by an example, it is standard practice to abbreviate +the tracebacks using ``...``. The doctest fixer uses this abbreviation automatically +when formatting the actual output, as shown in the above example. +To disable it so that the details of the exception +can be inspected, use the option ``--full-tracebacks``. This is particularly useful +in combination with ``--keep-both``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --keep-both --full-tracebacks \ + --overwrite src/sage/arith/weird.py + +This will give the following result on the above example:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 # optional - EXPECTED + | 5 + | sage: 2 + 2 # optional - GOT + | 4 + | sage: factor("91") # optional - EXPECTED + | "7" * "13" + | sage: factor("91") # optional - GOT + | Traceback (most recent call last): + | ... + | File "", line 1, in + | factor("91") + | File ".../src/sage/arith/misc.py", line 2680, in factor + | raise TypeError("unable to factor {!r}".format(n)) + | TypeError: unable to factor '91' + | ... + | """ + +To make sure that all doctests are updated, you may have to use the option ``--long``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --long \ + --overwrite src/sage/arith/weird.py + +If you are not comfortable with allowing this tool to edit your source files, you can use +the option ``--no-overwrite``, which will create a new file with the extension ``.fixed`` +instead of overwriting the source file:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests \ + --no-overwrite src/sage/arith/weird.py + + +.. _section-fixdoctests-optional-needs: + +Managing ``# optional`` and ``# needs`` tags +-------------------------------------------- + +When a file uses a ``# sage.doctest: optional/needs FEATURE`` directive, the +doctest fixer automatically removes the redundant ``# optional/needs FEATURE`` +tags from all ``sage:`` lines. Likewise, when a block-scoped tag +``sage: # optional/needs FEATURE`` is used, then the doctest fixer removes +redundant tags from all doctests in this scope. For example:: + + | # sage.doctest: optional - sirocco, needs sage.rings.number_field + | r""" + | ... + | + | EXAMPLES:: + | + | sage: # needs sage.modules sage.rings.number_field + | sage: Q5 = QuadraticField(5) + | sage: V = Q5^42 # needs sage.modules + | sage: T = transmogrify(V) # optional - bliss sirocco + +is automatically transformed to:: + + | # sage.doctest: optional - sirocco, needs sage.rings.number_field + | r""" + | ... + | + | EXAMPLES:: + | + | sage: # needs sage.modules + | sage: Q5 = QuadraticField(5) + | sage: V = Q5^42 + | sage: T = transmogrify(V) # optional - bliss + +The doctest fixer also aligns the ``# optional/needs FEATURE`` tags on +individual doctests at a fixed set of tab stops. + +The doctester may issue style warnings when ``# optional/needs`` tags are +repeated on a whole block of doctests, suggesting to use a block-scoped tag +instead. The doctest fixer makes these changes automatically. + +There are situations in which the doctester and doctest fixer show too +much restraint and a manual intervention would improve the formatting +of the doctests. In the example below, the doctester does not issue a +style warning because the first doctest line does not carry the ``# needs`` +tag:: + + | EXAMPLES:: + | + | sage: set_verbose(-1) + | sage: P. = ProjectiveSpace(QQbar, 2) # needs sage.rings.number_field + | sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 # needs sage.rings.number_field + | ....: + x^3*z + 7*x^2*y*z + | ....: + 14*x*y^2*z + 9*y^3*z], P) + | sage: Q = P([0,0,1]) # needs sage.rings.number_field + | sage: C.tangents(Q) # needs sage.rings.number_field + | [x + 4.147899035704788?*y, + | x + (1.426050482147607? + 0.3689894074818041?*I)*y, + | x + (1.426050482147607? - 0.3689894074818041?*I)*y] + +To change this example, there are two approaches: + +#. Just add the line ``sage: # needs sage.rings.number_field`` at + the beginning and run the doctest fixer, which will remove the tags on the individual + doctests that have now become redundant. + +#. Insert a blank line after the first doctest line, splitting the block into two. + Now the ``# needs`` tag is repeated on the whole second block, so running the doctest + fixer will add a block-scoped tag and remove the individual tags:: + + | EXAMPLES:: + | + | sage: set_verbose(-1) + | + | sage: # needs sage.rings.number_field + | sage: P. = ProjectiveSpace(QQbar, 2) + | sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 + | ....: + x^3*z + 7*x^2*y*z + | ....: + 14*x*y^2*z + 9*y^3*z], P) + | sage: Q = P([0,0,1]) + | sage: C.tangents(Q) + | [x + 4.147899035704788?*y, + | x + (1.426050482147607? + 0.3689894074818041?*I)*y, + | x + (1.426050482147607? - 0.3689894074818041?*I)*y] + +In places where the doctester issues a doctest dataflow warning +(``Variable ... referenced here was set only in doctest marked '# optional - FEATURE'``), +the doctest fixer automatically adds the missing ``# optional/needs`` tags. + +Sometimes code changes can make existing ``# optional/needs FEATURE`` tags unnecessary. +In an installation or virtual environment where ``FEATURE`` is not available, +you can invoke the doctest fixer with the option ``--probe FEATURE``. +Then it will run examples marked ``# optional/needs - FEATURE`` silently, and if the example +turns out to work anyway, the tag is automatically removed. + +.. note:: + + Probing works best when the doctests within a docstring do not reuse the same variable + for different values. + +To have the doctest fixer take care of the ``# optional/needs`` tags, +but not change the expected results of examples, use the option ``--only-tags``. +This mode is suitable for mostly unattended runs on many files. + +.. warning:: + + While the doctest fixer guarantees to preserve any comments that + appear before ``# optional/needs`` and all parenthesized comments + of the form ``# optional - FEATURE (EXPLANATION)``, any free-form comments + that may be mixed with the doctest tags will be lost. + +If you don't want to update any doctests, you can use the +option ``--no-test``. In this mode, the doctest fixer does not run +the doctester and only normalizes the style of the ``# optional`` tags. + + +Use in virtual environments +--------------------------- + +The doctest fixer can also run tests using the Sage doctester installed in +a virtual environment:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --overwrite \ + --distribution sagemath-categories \ + src/sage/geometry/schemes/generic/*.py + +This command, using ``--distribution``, is equivalent to a command +that uses the more specific options ``--venv`` and ``--environment``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --overwrite \ + --venv pkgs/sagemath-categories/.tox/sagepython-... \ + --environment sage.all__sagemath_categories + src/sage/geometry/schemes/generic/*.py + +Either way, the options ``--keep-both``, ``--full-tracebacks``, and +``--if-installed`` are implied. + +In this mode of operation, when the doctester encounters a global name +that is unknown in its virtual environment (:class:`NameError`), +the doctest fixer will look up the name in its own environment (typically +a full installation of the Sage library) and add a ``# needs ...`` tag +to the doctest. + +Likewise, when the doctester runs into a :class:`ModuleNotFoundError`, +the doctest fixer will automatically add a ``# needs ...`` tag. diff --git a/src/doc/en/developer/packaging_sage_library.rst b/src/doc/en/developer/packaging_sage_library.rst index 83f883dd53e..abda71bbed8 100644 --- a/src/doc/en/developer/packaging_sage_library.rst +++ b/src/doc/en/developer/packaging_sage_library.rst @@ -536,21 +536,28 @@ Not shown in the diagram are build dependencies and optional dependencies for te the Sage doctester (:mod:`sage.doctest`), and some related modules from :mod:`sage.misc`. +.. _section-modularized-doctesting: + Testing distribution packages ============================= Of course, we need tools for testing modularized distributions of portions of the Sage library. -- Modularized distributions must be testable separately! +- Distribution packages of the modularized Sage library must be testable separately! - But we want to keep integration testing with other portions of Sage too! -Preparing doctests ------------------- +Preparing doctests for modularized testing +------------------------------------------ + +Section :ref:`section-doctest-writing` explains how to write doctests +for Sage. Here we show how to prepare existing or new doctests so that +they are suitable for modularized testing. -Whenever an optional package is needed for a particular test, we use the -doctest annotation ``# optional``. This mechanism can also be used for making a +Per section :ref:`section-further_conventions`, +whenever an optional package is needed for a particular test, we use the +doctest tag ``# optional``. This mechanism can also be used for making a doctest conditional on the presence of a portion of the Sage library. The available tags take the form of package or module names such as @@ -564,29 +571,32 @@ hints to the user. For example, the package :mod:`sage.tensor` is purely algebraic and has no dependency on symbolics. However, there are a small number of doctests that depend on :class:`sage.symbolic.ring.SymbolicRing` for integration -testing. Hence, these doctests are marked ``# optional - -sage.symbolic``. +testing. Hence, these doctests are marked as depending on the feature +:class:`sage.symbolic <~sage.features.sagemath.sage__symbolic>`. -When defining new features for the purpose of doctest annotations, it may be a good +By convention, because :class:`sage.symbolic <~sage.features.sagemath.sage__symbolic>` +is present in a standard installation of Sage, we use the keyword ``# needs`` +instead of ``# optional``. These two keywords have identical semantics; +the tool :ref:`sage --fixdoctests ` +rewrites the doctest tags according to the convention. + +When defining new features for the purpose of conditionalizing doctests, it may be a good idea to hide implementation details from feature names. For example, all doctests that -use finite fields have to depend on PARI. However, we have defined a feature +use large finite fields have to depend on PARI. However, we have defined a feature :mod:`sage.rings.finite_rings` (which implies the presence of :mod:`sage.libs.pari`). -Annotating the doctests ``# optional - sage.rings.finite_rings`` expresses the -dependency in a clearer way than using ``# optional - sage.libs.pari``, and it +Marking the doctests ``# needs sage.rings.finite_rings`` expresses the +dependency in a clearer way than using ``# needs sage.libs.pari``, and it will be a smaller maintenance burden when implementation details change. Testing the distribution in virtual environments with tox --------------------------------------------------------- -So how to test that this works? - -Sure, we could go into the installation directory -``SAGE_VENV/lib/python3.9/site-packages/`` and do ``rm -rf -sage/symbolic`` and test that things still work. But that's not a good -way of testing. +Chapter :ref:`chapter-doctesting` explains in detail how to run the +Sage doctester with various options. -Instead, we use a virtual environment in which we only install the +To test a distribution package of the modularized Sage library, +we use a virtual environment in which we only install the distribution to be tested (and its Python dependencies). Let's try it out first with the entire Sage library, represented by @@ -641,7 +651,7 @@ command:: This command does not make any changes to the normal installation of Sage. The virtual environment is created in a subdirectory of -``SAGE_ROOT/pkgs/sagemath-standard-no-symbolics/.tox/``. After the command +``SAGE_ROOT/pkgs/sagemath-standard/.tox/``. After the command finishes, we can start the separate installation of the Sage library in its virtual environment:: @@ -655,10 +665,10 @@ The whole ``.tox`` directory can be safely deleted at any time. We can do the same with other distributions, for example the large distribution **sagemath-standard-no-symbolics** -(from :trac:`32601`), which is intended to provide +(from :trac:`35095`), which is intended to provide everything that is currently in the standard Sage library, i.e., without depending on optional packages, but without the packages -:mod:`sage.symbolic`, :mod:`sage.functions`, :mod:`sage.calculus`, etc. +:mod:`sage.symbolic`, :mod:`sage.calculus`, etc. Again we can run the test with ``tox`` in a separate virtual environment:: @@ -682,4 +692,4 @@ Building these small distributions serves as a valuable regression testsuite. However, a current issue with both of these distributions is that they are not separately testable: The doctests for these modules depend on a lot of other functionality from higher-level parts -of the Sage library. +of the Sage library. This is being addressed in :issue:`35095`. diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index ae326102be1..bed8a4fad93 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -306,17 +306,18 @@ d'appeler latex (ou plus généralement le moteur choisi via ``latex.engine()``) au lieu MathJax. Les méthodes ``latex.add_to_mathjax_avoid_list`` et ``latex.mathjax_avoid_list`` permettent de gérer le contenu de cette liste. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Supposons maintenant que, dans le bloc-notes, un appel à ``view()`` ou diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index e02d8507eb6..9f4dc85090b 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -280,17 +280,18 @@ LaTeX処理のカスタマイズ :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] diff --git a/src/doc/pt/tutorial/latex.rst b/src/doc/pt/tutorial/latex.rst index 3ed1a642ba8..56cb8e778c7 100644 --- a/src/doc/pt/tutorial/latex.rst +++ b/src/doc/pt/tutorial/latex.rst @@ -328,17 +328,18 @@ que for definido pelo comando ``latex.engine()``). Essa lista é gerenciada pelos comandos ``latex.add_to_mathjax_avoid_list`` e ``latex.mathjax_avoid_list``. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Suponha que uma expressão em LaTeX é produzida no Notebook com o diff --git a/src/sage/calculus/ode.pyx b/src/sage/calculus/ode.pyx index ea26f16998a..74bac5b0811 100644 --- a/src/sage/calculus/ode.pyx +++ b/src/sage/calculus/ode.pyx @@ -324,14 +324,15 @@ class ode_solver(): following (WARNING: the following is *not* automatically doctested):: - sage: T = ode_solver() # not tested - sage: T.algorithm = "bsimp" # not tested - sage: vander = van_der_pol() # not tested - sage: T.function = vander # not tested - sage: T.ode_solve(y_0=[1, 0], t_span=[0, 2000], # not tested + sage: # not tested + sage: T = ode_solver() + sage: T.algorithm = "bsimp" + sage: vander = van_der_pol() + sage: T.function = vander + sage: T.ode_solve(y_0=[1, 0], t_span=[0, 2000], ....: num_points=1000) - sage: from tempfile import NamedTemporaryFile # not tested - sage: with NamedTemporaryFile(suffix=".png") as f: # not tested + sage: from tempfile import NamedTemporaryFile + sage: with NamedTemporaryFile(suffix=".png") as f: ....: T.plot_solution(i=0, filename=f.name) """ diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index ea8f9265033..543ce0375f7 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -135,13 +135,14 @@ class Category_singleton(Category): One sees that containment tests for the singleton class is a lot faster than for a usual class:: - sage: timeit("R in MyRings()", number=10000) # not tested + sage: # not tested + sage: timeit("R in MyRings()", number=10000) 10000 loops, best of 3: 7.12 µs per loop - sage: timeit("R1 in MyRings()", number=10000) # not tested + sage: timeit("R1 in MyRings()", number=10000) 10000 loops, best of 3: 6.98 µs per loop - sage: timeit("R in MyRingsSingleton()", number=10000) # not tested + sage: timeit("R in MyRingsSingleton()", number=10000) 10000 loops, best of 3: 3.08 µs per loop - sage: timeit("R2 in MyRingsSingleton()", number=10000) # not tested + sage: timeit("R2 in MyRingsSingleton()", number=10000) 10000 loops, best of 3: 2.99 µs per loop So this is an improvement, but not yet competitive with a pure diff --git a/src/sage/categories/lie_algebras.py b/src/sage/categories/lie_algebras.py index 04597de0308..0bd0c3185e7 100644 --- a/src/sage/categories/lie_algebras.py +++ b/src/sage/categories/lie_algebras.py @@ -45,24 +45,24 @@ class LieAlgebras(Category_over_base_ring): We construct a typical parent in this category, and do some computations with it:: - sage: A = C.example(); A # optional - sage.groups sage.modules + sage: A = C.example(); A # needs sage.groups sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) - sage: A.category() # optional - sage.groups sage.modules + sage: A.category() # needs sage.groups sage.modules Category of Lie algebras over Rational Field - sage: A.base_ring() # optional - sage.groups sage.modules + sage: A.base_ring() # needs sage.groups sage.modules Rational Field - sage: a, b = A.lie_algebra_generators() # optional - sage.groups sage.modules - sage: a.bracket(b) # optional - sage.groups sage.modules + sage: a, b = A.lie_algebra_generators() # needs sage.groups sage.modules + sage: a.bracket(b) # needs sage.groups sage.modules -[1, 3, 2] + [3, 2, 1] - sage: b.bracket(2*a + b) # optional - sage.groups sage.modules + sage: b.bracket(2*a + b) # needs sage.groups sage.modules 2*[1, 3, 2] - 2*[3, 2, 1] - sage: A.bracket(a, b) # optional - sage.groups sage.modules + sage: A.bracket(a, b) # needs sage.groups sage.modules -[1, 3, 2] + [3, 2, 1] Please see the source code of `A` (with ``A??``) for how to @@ -70,9 +70,9 @@ class LieAlgebras(Category_over_base_ring): TESTS:: - sage: C = LieAlgebras(QQ) # optional - sage.combinat sage.modules - sage: TestSuite(C).run() # optional - sage.combinat sage.modules - sage: TestSuite(C.example()).run() # optional - sage.combinat sage.modules + sage: C = LieAlgebras(QQ) # needs sage.combinat sage.modules + sage: TestSuite(C).run() # needs sage.combinat sage.modules + sage: TestSuite(C.example()).run() # needs sage.combinat sage.modules .. TODO:: @@ -151,15 +151,15 @@ def example(self, gens=None): EXAMPLES:: - sage: LieAlgebras(QQ).example() # optional - sage.groups sage.modules + sage: LieAlgebras(QQ).example() # needs sage.groups sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) Another set of generators can be specified as an optional argument:: - sage: F. = FreeAlgebra(QQ) # optional - sage.combinat sage.modules - sage: LieAlgebras(QQ).example(F.gens()) # optional - sage.combinat sage.modules + sage: F. = FreeAlgebra(QQ) # needs sage.combinat sage.modules + sage: LieAlgebras(QQ).example(F.gens()) # needs sage.combinat sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Free Algebra on 3 generators (x, y, z) over Rational Field generated by (x, y, z) @@ -189,14 +189,14 @@ def extra_super_categories(self): [Category of finite sets] sage: LieAlgebras(ZZ).FiniteDimensional().extra_super_categories() [] - sage: C = LieAlgebras(GF(5)).FiniteDimensional() # optional - sage.rings.finite_rings - sage: C.is_subcategory(Sets().Finite()) # optional - sage.rings.finite_rings + sage: C = LieAlgebras(GF(5)).FiniteDimensional() # needs sage.rings.finite_rings + sage: C.is_subcategory(Sets().Finite()) # needs sage.rings.finite_rings True sage: C = LieAlgebras(ZZ).FiniteDimensional() sage: C.is_subcategory(Sets().Finite()) False - sage: C = LieAlgebras(GF(5)).WithBasis().FiniteDimensional() # optional - sage.rings.finite_rings - sage: C.is_subcategory(Sets().Finite()) # optional - sage.rings.finite_rings + sage: C = LieAlgebras(GF(5)).WithBasis().FiniteDimensional() # needs sage.rings.finite_rings + sage: C.is_subcategory(Sets().Finite()) # needs sage.rings.finite_rings True """ if self.base_ring() in Sets().Finite(): @@ -209,8 +209,8 @@ class Nilpotent(CategoryWithAxiom_over_base_ring): TESTS:: - sage: C = LieAlgebras(QQ).Nilpotent() # optional - sage.combinat sage.modules - sage: TestSuite(C).run() # optional - sage.combinat sage.modules + sage: C = LieAlgebras(QQ).Nilpotent() # needs sage.combinat sage.modules + sage: TestSuite(C).run() # needs sage.combinat sage.modules """ class ParentMethods: @abstract_method @@ -220,8 +220,8 @@ def step(self): EXAMPLES:: - sage: h = lie_algebras.Heisenberg(ZZ, oo) # optional - sage.combinat sage.modules - sage: h.step() # optional - sage.combinat sage.modules + sage: h = lie_algebras.Heisenberg(ZZ, oo) # needs sage.combinat sage.modules + sage: h.step() # needs sage.combinat sage.modules 2 """ @@ -231,8 +231,8 @@ def is_nilpotent(self): EXAMPLES:: - sage: h = lie_algebras.Heisenberg(ZZ, oo) # optional - sage.combinat sage.modules - sage: h.is_nilpotent() # optional - sage.combinat sage.modules + sage: h = lie_algebras.Heisenberg(ZZ, oo) # needs sage.combinat sage.modules + sage: h.is_nilpotent() # needs sage.combinat sage.modules True """ return True @@ -257,30 +257,31 @@ def bracket(self, lhs, rhs): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x, y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.bracket(x, x + y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x, y = L.lie_algebra_generators() + sage: L.bracket(x, x + y) -[1, 3, 2] + [3, 2, 1] - sage: L.bracket(x, 0) # optional - sage.combinat sage.modules + sage: L.bracket(x, 0) 0 - sage: L.bracket(0, x) # optional - sage.combinat sage.modules + sage: L.bracket(0, x) 0 Constructing the product space:: - sage: L = lie_algebras.Heisenberg(QQ, 1) # optional - sage.combinat sage.modules - sage: Z = L.bracket(L, L); Z # optional - sage.combinat sage.modules + sage: L = lie_algebras.Heisenberg(QQ, 1) # needs sage.combinat sage.modules + sage: Z = L.bracket(L, L); Z # needs sage.combinat sage.modules Ideal (z) of Heisenberg algebra of rank 1 over Rational Field - sage: L.bracket(L, Z) # optional - sage.combinat sage.modules + sage: L.bracket(L, Z) # needs sage.combinat sage.modules Ideal () of Heisenberg algebra of rank 1 over Rational Field Constructing ideals:: - sage: p, q, z = L.basis(); p, q, z # optional - sage.combinat sage.modules + sage: p, q, z = L.basis(); p, q, z # needs sage.combinat sage.modules (p1, q1, z) - sage: L.bracket(3*p, L) # optional - sage.combinat sage.modules + sage: L.bracket(3*p, L) # needs sage.combinat sage.modules Ideal (3*p1) of Heisenberg algebra of rank 1 over Rational Field - sage: L.bracket(L, q + p) # optional - sage.combinat sage.modules + sage: L.bracket(L, q + p) # needs sage.combinat sage.modules Ideal (p1 + q1) of Heisenberg algebra of rank 1 over Rational Field """ if lhs in LieAlgebras: @@ -300,15 +301,15 @@ def universal_enveloping_algebra(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # needs sage.combinat sage.modules Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: - sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # needs sage.combinat sage.modules Multivariate Polynomial Ring in x0, x1, x2 over Rational Field .. SEEALSO:: @@ -333,15 +334,15 @@ def _construct_UEA(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L._construct_UEA() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L._construct_UEA() # needs sage.combinat sage.modules Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: - sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # indirect doctest # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # indirect doctest # needs sage.combinat sage.modules Multivariate Polynomial Ring in x0, x1, x2 over Rational Field """ @@ -397,8 +398,8 @@ def module(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.module() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.module() # needs sage.combinat sage.modules Vector space of dimension 3 over Rational Field """ @@ -413,10 +414,10 @@ def from_vector(self, v, order=None, coerce=False): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u # needs sage.combinat sage.modules (1, 0, 0) - sage: parent(u) is L # optional - sage.combinat sage.modules + sage: parent(u) is L # needs sage.combinat sage.modules True """ @@ -433,11 +434,12 @@ def lift(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: lifted = L.lift(2*a + b - c); lifted # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: lifted = L.lift(2*a + b - c); lifted 2*b0 + b1 - b2 - sage: lifted.parent() is L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: lifted.parent() is L.universal_enveloping_algebra() True """ M = LiftMorphism(self, self._construct_UEA()) @@ -450,9 +452,9 @@ def subalgebra(self, gens, names=None, index_set=None, category=None): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.subalgebra([2*a - c, b + c]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.subalgebra([2*a - c, b + c]) # needs sage.combinat sage.modules An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: @@ -461,9 +463,9 @@ def subalgebra(self, gens, names=None, index_set=None, category=None): :: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.subalgebra([x + y]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.subalgebra([x + y]) # needs sage.combinat sage.modules Traceback (most recent call last): ... NotImplementedError: subalgebras not yet implemented: see #17416 @@ -478,9 +480,9 @@ def ideal(self, *gens, **kwds): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.ideal([2*a - c, b + c]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.ideal([2*a - c, b + c]) # needs sage.combinat sage.modules An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: @@ -489,9 +491,9 @@ def ideal(self, *gens, **kwds): :: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x, y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.ideal([x + y]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x, y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.ideal([x + y]) # needs sage.combinat sage.modules Traceback (most recent call last): ... NotImplementedError: ideals not yet implemented: see #16824 @@ -511,8 +513,8 @@ def is_ideal(self, A): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_ideal(L) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L.is_ideal(L) # needs sage.combinat sage.modules True """ if A == self: @@ -528,9 +530,9 @@ def killing_form(self, x, y): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.killing_form(a, b + c) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.killing_form(a, b + c) # needs sage.combinat sage.modules 0 """ @@ -543,21 +545,23 @@ def is_abelian(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_abelian() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: L.is_abelian() False - sage: R = QQ['x,y'] # optional - sage.combinat sage.modules - sage: L = LieAlgebras(QQ).example(R.gens()) # optional - sage.combinat sage.modules - sage: L.is_abelian() # optional - sage.combinat sage.modules + sage: R = QQ['x,y'] + sage: L = LieAlgebras(QQ).example(R.gens()) + sage: L.is_abelian() True :: - sage: L. = LieAlgebra(QQ, 1) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_abelian() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: # not implemented, needs sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 1) + sage: L.is_abelian() True - sage: L. = LieAlgebra(QQ, 2) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_abelian() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 2) + sage: L.is_abelian() False """ G = self.lie_algebra_generators() @@ -573,14 +577,14 @@ def is_commutative(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_commutative() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L.is_commutative() # needs sage.combinat sage.modules False :: - sage: L. = LieAlgebra(QQ, 1) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_commutative() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 1) # not implemented # needs sage.combinat sage.modules + sage: L.is_commutative() # not implemented # needs sage.combinat sage.modules True """ return self.is_abelian() @@ -592,8 +596,8 @@ def is_solvable(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.is_solvable() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.is_solvable() # needs sage.combinat sage.modules True """ @@ -604,8 +608,8 @@ def is_nilpotent(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.is_nilpotent() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.is_nilpotent() # needs sage.combinat sage.modules True """ @@ -630,36 +634,36 @@ def bch(self, X, Y, prec=None): The BCH formula for the generators of a free nilpotent Lie algebra of step 4:: - sage: L = LieAlgebra(QQ, 2, step=4) # optional - sage.combinat sage.modules - sage: L.inject_variables() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 2, step=4) # needs sage.combinat sage.modules + sage: L.inject_variables() # needs sage.combinat sage.modules Defining X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222 - sage: L.bch(X_1, X_2) # optional - sage.combinat sage.modules + sage: L.bch(X_1, X_2) # needs sage.combinat sage.modules X_1 + X_2 + 1/2*X_12 + 1/12*X_112 + 1/12*X_122 + 1/24*X_1122 An example of the BCH formula in a quotient:: - sage: Q = L.quotient(X_112 + X_122) # optional - sage.combinat sage.modules - sage: x, y = Q.basis().list()[:2] # optional - sage.combinat sage.modules - sage: Q.bch(x, y) # optional - sage.combinat sage.modules + sage: Q = L.quotient(X_112 + X_122) # needs sage.combinat sage.modules + sage: x, y = Q.basis().list()[:2] # needs sage.combinat sage.modules + sage: Q.bch(x, y) # needs sage.combinat sage.modules X_1 + X_2 + 1/2*X_12 - 1/24*X_1112 The BCH formula for a non-nilpotent Lie algebra requires the precision to be explicitly stated:: - sage: L. = LieAlgebra(QQ) # optional - sage.combinat sage.modules - sage: L.bch(X, Y) # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ) # needs sage.combinat sage.modules + sage: L.bch(X, Y) # needs sage.combinat sage.modules Traceback (most recent call last): ... ValueError: the Lie algebra is not known to be nilpotent, so you must specify the precision - sage: L.bch(X, Y, 4) # optional - sage.combinat sage.modules + sage: L.bch(X, Y, 4) # needs sage.combinat sage.modules X + 1/12*[X, [X, Y]] + 1/24*[X, [[X, Y], Y]] + 1/2*[X, Y] + 1/12*[[X, Y], Y] + Y The BCH formula requires a coercion from the rationals:: - sage: L. = LieAlgebra(ZZ, 2, step=2) # optional - sage.combinat sage.modules - sage: L.bch(X, Y) # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(ZZ, 2, step=2) # needs sage.combinat sage.modules + sage: L.bch(X, Y) # needs sage.combinat sage.modules Traceback (most recent call last): ... TypeError: the BCH formula is not well defined @@ -688,8 +692,8 @@ def lie_group(self, name='G', **kwds): EXAMPLES:: - sage: L = lie_algebras.Heisenberg(QQ, 1) # optional - sage.combinat sage.modules - sage: G = L.lie_group('G'); G # optional - sage.combinat sage.modules sage.symbolic + sage: L = lie_algebras.Heisenberg(QQ, 1) # needs sage.combinat sage.modules + sage: G = L.lie_group('G'); G # needs sage.combinat sage.modules sage.symbolic Lie group G of Heisenberg algebra of rank 1 over Rational Field """ @@ -707,15 +711,15 @@ def _test_jacobi_identity(self, **options): By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_jacobi_identity() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_jacobi_identity() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L._test_jacobi_identity(elements=[x + y, x, 2*y, x.bracket(y)]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L._test_jacobi_identity(elements=[x + y, x, 2*y, x.bracket(y)]) # needs sage.combinat sage.modules See the documentation for :class:`TestSuite` for more information. """ @@ -746,15 +750,15 @@ def _test_antisymmetry(self, **options): By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_antisymmetry() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_antisymmetry() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L._test_antisymmetry(elements=[x + y, x, 2*y, x.bracket(y)]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L._test_antisymmetry(elements=[x + y, x, 2*y, x.bracket(y)]) # needs sage.combinat sage.modules See the documentation for :class:`TestSuite` for more information. """ @@ -775,27 +779,28 @@ def _test_distributivity(self, **options): TESTS:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_distributivity() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_distributivity() # needs sage.combinat sage.modules EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") # optional - sage.combinat sage.modules - sage: L.some_elements() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") # needs sage.combinat sage.modules + sage: L.some_elements() # needs sage.combinat sage.modules [x + y + z] - sage: L._test_distributivity() # optional - sage.combinat sage.modules + sage: L._test_distributivity() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: h1 = L.gen(0) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: h2 = L.gen(1) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: e2 = L.gen(3) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: L._test_distributivity(elements=[h1, h2, e2]) # todo: not implemented - #16821 # optional - sage.combinat sage.modules + sage: # not implemented, needs sage.combinat sage.modules + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: h1 = L.gen(0) + sage: h2 = L.gen(1) + sage: e2 = L.gen(3) + sage: L._test_distributivity(elements=[h1, h2, e2]) See the documentation for :class:`TestSuite` for more information. """ @@ -818,11 +823,12 @@ def bracket(self, rhs): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: x.bracket(y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x.bracket(y) -[1, 3, 2] + [3, 2, 1] - sage: x.bracket(0) # optional - sage.combinat sage.modules + sage: x.bracket(0) 0 """ return self._bracket_(rhs) @@ -837,13 +843,14 @@ def _bracket_(self, y): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: x._bracket_(y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x._bracket_(y) -[1, 3, 2] + [3, 2, 1] - sage: y._bracket_(x) # optional - sage.combinat sage.modules + sage: y._bracket_(x) [1, 3, 2] - [3, 2, 1] - sage: x._bracket_(x) # optional - sage.combinat sage.modules + sage: x._bracket_(x) 0 """ @@ -859,10 +866,10 @@ def to_vector(self, order=None): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: u = L((1, 0, 0)).to_vector(); u # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: u = L((1, 0, 0)).to_vector(); u # needs sage.combinat sage.modules (1, 0, 0) - sage: parent(u) # optional - sage.combinat sage.modules + sage: parent(u) # needs sage.combinat sage.modules Vector space of dimension 3 over Rational Field """ @@ -874,16 +881,17 @@ def lift(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: elt = 3*a + b - c # optional - sage.combinat sage.modules - sage: elt.lift() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 3*a + b - c + sage: elt.lift() 3*b0 + b1 - b2 :: - sage: L. = LieAlgebra(QQ, abelian=True) # optional - sage.combinat sage.modules - sage: x.lift() # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, abelian=True) # needs sage.combinat sage.modules + sage: x.lift() # needs sage.combinat sage.modules x """ @@ -893,9 +901,9 @@ def killing_form(self, x): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: a.killing_form(b) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: a.killing_form(b) # needs sage.combinat sage.modules 0 """ return self.parent().killing_form(self, x) @@ -912,26 +920,28 @@ def exp(self, lie_group=None): EXAMPLES:: - sage: L. = LieAlgebra(QQ, 2, step=2) # optional - sage.combinat sage.modules - sage: g = (X + Y + Z).exp(); g # optional - sage.combinat sage.modules sage.symbolic + sage: # needs sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 2, step=2) + sage: g = (X + Y + Z).exp(); g # needs sage.symbolic exp(X + Y + Z) - sage: h = X.exp(); h # optional - sage.combinat sage.modules sage.symbolic + sage: h = X.exp(); h # needs sage.symbolic exp(X) - sage: g.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() # needs sage.symbolic Lie group G of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field - sage: g.parent() is h.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() is h.parent() # needs sage.symbolic True The Lie group can be specified explicitly:: - sage: H = L.lie_group('H') # optional - sage.combinat sage.modules sage.symbolic - sage: k = Z.exp(lie_group=H); k # optional - sage.combinat sage.modules sage.symbolic + sage: # needs sage.combinat sage.modules sage.symbolic + sage: H = L.lie_group('H') + sage: k = Z.exp(lie_group=H); k exp(Z) - sage: k.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: k.parent() Lie group H of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field - sage: g.parent() == k.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() == k.parent() False """ if lie_group is None: @@ -949,13 +959,13 @@ def __init__(self, domain, codomain): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: f = L.lift # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: f = L.lift # needs sage.combinat sage.modules We skip the category test since this is currently not an element of a homspace:: - sage: TestSuite(f).run(skip="_test_category") # optional - sage.combinat sage.modules + sage: TestSuite(f).run(skip="_test_category") # needs sage.combinat sage.modules """ Morphism.__init__(self, Hom(domain, codomain)) @@ -965,9 +975,9 @@ def _call_(self, x): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.lift(3*a + b - c) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.lift(3*a + b - c) # needs sage.combinat sage.modules 3*b0 + b1 - b2 """ return x.lift() diff --git a/src/sage/categories/posets.py b/src/sage/categories/posets.py index 24a83e02dbd..47408110da2 100644 --- a/src/sage/categories/posets.py +++ b/src/sage/categories/posets.py @@ -71,13 +71,14 @@ class Posets(Category): :trac:`10130` will be resolved, this will be automatically provided by this category:: - sage: x < y # todo: not implemented + sage: # not implemented + sage: x < y True - sage: x < x # todo: not implemented + sage: x < x False - sage: x <= x # todo: not implemented + sage: x <= x True - sage: y >= x # todo: not implemented + sage: y >= x True .. SEEALSO:: :func:`Poset`, :class:`FinitePosets`, :class:`LatticePosets` diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index ce92530637d..3021cbbfab6 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -1,4 +1,4 @@ -# sage.doctest: optional - sage.modules sage.rings.finite_rings +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" Guruswami-Sudan decoder for (Generalized) Reed-Solomon codes @@ -754,7 +754,7 @@ def list_size(self): def decode_to_message(self, r): r""" - Decodes ``r`` to the list of polynomials whose encoding by + Decode ``r`` to the list of polynomials whose encoding by :meth:`self.code()` is within Hamming distance :meth:`self.decoding_radius` of ``r``. @@ -795,7 +795,7 @@ def decode_to_message(self, r): Traceback (most recent call last): ... ValueError: The provided root-finding algorithm has a wrong signature. - See the documentation of `GSD.rootfinding_algorithm()` for details + See the documentation of `...rootfinding_algorithm()` for details """ return [self.connected_encoder().unencode(c) for c in self.decode_to_code(r)] diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 01e1b9bb4f8..2b687775102 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -391,7 +391,7 @@ def __init__(self, base_field, sub_field, length, default_encoder_name, sage: from sage.coding.linear_rank_metric import AbstractLinearRankMetricCode sage: class RankRepetitionCode(AbstractLinearRankMetricCode): ....: def __init__(self, base_field, sub_field, length): - ....: super().__init__(self, base_field, sub_field, length, + ....: super().__init__(base_field, sub_field, length, ....: "GeneratorMatrix", "NearestNeighbor") ....: beta = base_field.gen() ....: self._generator_matrix = matrix(base_field, diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index fc47efbd49c..5d0e3c743df 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -498,9 +498,9 @@ def specht_module(self, base_ring=None): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (1,1), (2,2), (2,3)]) - sage: SM = D.specht_module(QQ) - sage: s = SymmetricFunctions(QQ).s() - sage: s(SM.frobenius_image()) + sage: SM = D.specht_module(QQ) # needs sage.modules + sage: s = SymmetricFunctions(QQ).s() # needs sage.modules + sage: s(SM.frobenius_image()) # needs sage.modules s[2, 1, 1] + s[2, 2] + 2*s[3, 1] + s[4] """ from sage.combinat.specht_module import SpechtModule @@ -523,9 +523,9 @@ def specht_module_dimension(self, base_ring=None): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (1,1), (2,2), (2,3)]) - sage: D.specht_module_dimension() + sage: D.specht_module_dimension() # needs sage.modules 12 - sage: D.specht_module(QQ).dimension() + sage: D.specht_module(QQ).dimension() # needs sage.modules 12 """ from sage.combinat.specht_module import specht_module_rank @@ -656,9 +656,9 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): . . O - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) - sage: Dgms(p).pp() + sage: from sage.combinat.tiling import Polyomino # needs sage.modules + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) # needs sage.modules + sage: Dgms(p).pp() # needs sage.modules O . . O O O @@ -671,8 +671,8 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): O O . . O O O O - sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) - sage: Dgms(M).pp() + sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) # needs sage.modules + sage: Dgms(M).pp() # needs sage.modules O O O O O O . . . . . . @@ -716,23 +716,23 @@ def from_polyomino(self, p): EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: from sage.combinat.tiling import Polyomino # needs sage.modules + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) # needs sage.modules sage: from sage.combinat.diagram import Diagrams - sage: Diagrams()(p).pp() + sage: Diagrams()(p).pp() # needs sage.modules O . . O O O We can also call this method directly:: - sage: Diagrams().from_polyomino(p).pp() + sage: Diagrams().from_polyomino(p).pp() # needs sage.modules O . . O O O This only works for a 2d :class:`~sage.combinat.tiling.Polyomino`:: - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - sage: Diagrams().from_polyomino(p) + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') # needs sage.modules + sage: Diagrams().from_polyomino(p) # needs sage.modules Traceback (most recent call last): ... ValueError: the polyomino must be 2 dimensional @@ -777,17 +777,17 @@ def from_zero_one_matrix(self, M, check=True): EXAMPLES:: - sage: M = matrix([[1,0,1,1],[0,1,1,0]]) # optional - sage.modules + sage: M = matrix([[1,0,1,1],[0,1,1,0]]) # needs sage.modules sage: from sage.combinat.diagram import Diagrams - sage: Diagrams()(M).pp() # optional - sage.modules + sage: Diagrams()(M).pp() # needs sage.modules O . O O . O O . - sage: Diagrams().from_zero_one_matrix(M).pp() # optional - sage.modules + sage: Diagrams().from_zero_one_matrix(M).pp() # needs sage.modules O . O O . O O . - sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) # optional - sage.modules - sage: Diagrams()(M).pp() # optional - sage.modules + sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) # needs sage.modules + sage: Diagrams()(M).pp() # needs sage.modules O . . O . . . . . @@ -1435,10 +1435,10 @@ def from_parallelogram_polyomino(self, p): EXAMPLES:: - sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], # optional - sage.modules + sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], # needs sage.modules ....: [1, 1, 0, 1, 0, 0, 0, 0]]) sage: from sage.combinat.diagram import NorthwestDiagrams - sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() + sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() # needs sage.modules O O . O O O . O O @@ -1494,8 +1494,8 @@ def RotheDiagram(w): :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: - sage: w = SymmetricGroup(9).an_element() - sage: RotheDiagram(w) + sage: w = SymmetricGroup(9).an_element() # needs sage.groups + sage: RotheDiagram(w) # needs sage.groups Traceback (most recent call last): ... ValueError: w must be a permutation diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index decef1d4479..35de4581bd0 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -944,7 +944,6 @@ from sage.misc.latex import latex from sage.misc.verbose import verbose from sage.misc.sageinspect import sage_getargspec -from sage.rings.qqbar import QQbar from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.structure.sage_object import SageObject @@ -9885,7 +9884,7 @@ def number_of_words(self, variable=None, from sage.arith.misc import binomial from sage.symbolic.ring import SR if base_ring is None: - base_ring = QQbar + from sage.rings.qqbar import QQbar as base_ring if variable is None: variable = SR.symbol('n') diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 51ade61b8d1..a5edc7146ea 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -275,37 +275,37 @@ introduce two variables, `C` and `z`, and we define the equation:: - sage: C, z = var('C,z') # optional - sage.symbolic - sage: sys = [ C == z + C*C ] # optional - sage.symbolic + sage: C, z = var('C,z') # needs sage.symbolic + sage: sys = [ C == z + C*C ] # needs sage.symbolic There are two solutions, which happen to have closed forms:: - sage: sol = solve(sys, C, solution_dict=True); sol # optional - sage.symbolic + sage: sol = solve(sys, C, solution_dict=True); sol # needs sage.symbolic [{C: -1/2*sqrt(-4*z + 1) + 1/2}, {C: 1/2*sqrt(-4*z + 1) + 1/2}] - sage: s0 = sol[0][C]; s1 = sol[1][C] # optional - sage.symbolic + sage: s0 = sol[0][C]; s1 = sol[1][C] # needs sage.symbolic and whose Taylor series begin as follows:: - sage: s0.series(z, 6) # optional - sage.symbolic + sage: s0.series(z, 6) # needs sage.symbolic 1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + Order(z^6) - sage: s1.series(z, 6) # optional - sage.symbolic + sage: s1.series(z, 6) # needs sage.symbolic 1 + (-1)*z + (-1)*z^2 + (-2)*z^3 + (-5)*z^4 + (-14)*z^5 + Order(z^6) The second solution is clearly aberrant, while the first one gives the expected coefficients. Therefore, we set:: - sage: C = s0 # optional - sage.symbolic + sage: C = s0 # needs sage.symbolic We can now calculate the next terms:: - sage: C.series(z, 11) # optional - sage.symbolic + sage: C.series(z, 11) # needs sage.symbolic 1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + 429*z^8 + 1430*z^9 + 4862*z^10 + Order(z^11) or calculate, more or less instantaneously, the 100-th coefficient:: - sage: C.series(z, 101).coefficient(z,100) # optional - sage.symbolic + sage: C.series(z, 101).coefficient(z,100) # needs sage.symbolic 227508830794229349661819540395688853956041682601541047340 It is unfortunate to have to recalculate everything if at some point we @@ -338,19 +338,19 @@ We now return to the closed form of `C(z)`:: - sage: z = var('z') # optional - sage.symbolic - sage: C = s0; C # optional - sage.symbolic + sage: z = var('z') # needs sage.symbolic + sage: C = s0; C # needs sage.symbolic -1/2*sqrt(-4*z + 1) + 1/2 The `n`-th coefficient in the Taylor series for `C(z)` being given by `\frac{1}{n!} C(z)^{(n)}(0)`, we look at the successive derivatives `C(z)^{(n)}(z)`:: - sage: derivative(C, z, 1) # optional - sage.symbolic + sage: derivative(C, z, 1) # needs sage.symbolic 1/sqrt(-4*z + 1) - sage: derivative(C, z, 2) # optional - sage.symbolic + sage: derivative(C, z, 2) # needs sage.symbolic 2/(-4*z + 1)^(3/2) - sage: derivative(C, z, 3) # optional - sage.symbolic + sage: derivative(C, z, 3) # needs sage.symbolic 12/(-4*z + 1)^(5/2) This suggests the existence of a simple explicit formula, which we will @@ -360,7 +360,7 @@ Taking successive quotients:: - sage: [ (d(n+1) / d(n)) for n in range(1,17) ] # optional - sage.symbolic + sage: [ (d(n+1) / d(n)) for n in range(1,17) ] # needs sage.symbolic [2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62] we observe that `d_n` satisfies the recurrence relation @@ -373,9 +373,9 @@ We check this:: - sage: n = var('n') # optional - sage.symbolic - sage: c = 1/n*binomial(2*(n-1),n-1) # optional - sage.symbolic - sage: [c.subs(n=k) for k in range(1, 11)] # optional - sage.symbolic + sage: n = var('n') # needs sage.symbolic + sage: c = 1/n*binomial(2*(n-1),n-1) # needs sage.symbolic + sage: [c.subs(n=k) for k in range(1, 11)] # needs sage.symbolic [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862] sage: [catalan_number(k-1) for k in range(1, 11)] [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862] @@ -383,14 +383,14 @@ We can now calculate coefficients much further; here we calculate `c_{100000}` which has more than `60000` digits:: - sage: cc = c(n=100000) # optional - sage.symbolic + sage: cc = c(n=100000) # needs sage.symbolic This takes a couple of seconds:: - sage: %time cc = c(100000) # not tested # optional - sage.symbolic + sage: %time cc = c(100000) # not tested # needs sage.symbolic CPU times: user 2.34 s, sys: 0.00 s, total: 2.34 s Wall time: 2.34 s - sage: ZZ(cc).ndigits() # optional - sage.symbolic + sage: ZZ(cc).ndigits() # needs sage.symbolic 60198 The methods which we have used generalize to all recursively defined @@ -417,11 +417,12 @@ In the present case, `P=y^2-y+x`. We formally differentiate this equation with respect to `z`:: - sage: x, y, z = var('x, y, z') # optional - sage.symbolic - sage: P = function('P')(x, y) # optional - sage.symbolic - sage: C = function('C')(z) # optional - sage.symbolic - sage: equation = P(x=z, y=C) == 0 # optional - sage.symbolic - sage: diff(equation, z) # optional - sage.symbolic + sage: # needs sage.symbolic + sage: x, y, z = var('x, y, z') + sage: P = function('P')(x, y) + sage: C = function('C')(z) + sage: equation = P(x=z, y=C) == 0 + sage: diff(equation, z) diff(C(z), z)*D[1](P)(z, C(z)) + D[0](P)(z, C(z)) == 0 or, in a more readable format, @@ -434,9 +435,9 @@ In the case of complete binary trees, this gives:: - sage: P = y^2 - y + x # optional - sage.symbolic - sage: Px = diff(P, x); Py = diff(P, y) # optional - sage.symbolic - sage: - Px / Py # optional - sage.symbolic + sage: P = y^2 - y + x # needs sage.symbolic + sage: Px = diff(P, x); Py = diff(P, y) # needs sage.symbolic + sage: - Px / Py # needs sage.symbolic -1/(2*y - 1) Recall that `P(z, C(z))=0`. Thus, we can calculate this fraction @@ -447,7 +448,7 @@ sage: Qx = QQ['x'].fraction_field() sage: Qxy = Qx['y'] - sage: R = Qxy.quo(P); R # optional - sage.symbolic + sage: R = Qxy.quo(P); R # needs sage.symbolic Univariate Quotient Polynomial Ring in ybar over Fraction Field of Univariate Polynomial Ring in x over Rational Field with modulus y^2 - y + x @@ -458,7 +459,7 @@ We continue the calculation of this fraction in `R`:: - sage: fraction = - R(Px) / R(Py); fraction # optional - sage.symbolic + sage: fraction = - R(Px) / R(Py); fraction # needs sage.symbolic (1/2/(x - 1/4))*ybar - 1/4/(x - 1/4) .. note:: @@ -474,9 +475,9 @@ `z` and `C(z)` to obtain an expression for `\frac{d}{dz}C(z)`:: - sage: fraction = fraction.lift(); fraction # optional - sage.symbolic + sage: fraction = fraction.lift(); fraction # needs sage.symbolic (1/2/(x - 1/4))*y - 1/4/(x - 1/4) - sage: fraction(x=z, y=C) # optional - sage.symbolic + sage: fraction(x=z, y=C) # needs sage.symbolic 2*C(z)/(4*z - 1) - 1/(4*z - 1) or, more legibly, @@ -486,13 +487,14 @@ In this simple case, we can directly deduce from this expression a linear differential equation with coefficients in `\QQ[z]`:: - sage: equadiff = diff(C,z) == fraction(x=z, y=C) # optional - sage.symbolic - sage: equadiff # optional - sage.symbolic + sage: # needs sage.symbolic + sage: equadiff = diff(C,z) == fraction(x=z, y=C) + sage: equadiff diff(C(z), z) == 2*C(z)/(4*z - 1) - 1/(4*z - 1) - sage: equadiff = equadiff.simplify_rational() # optional - sage.symbolic - sage: equadiff = equadiff * equadiff.rhs().denominator() # optional - sage.symbolic - sage: equadiff = equadiff - equadiff.rhs() # optional - sage.symbolic - sage: equadiff # optional - sage.symbolic + sage: equadiff = equadiff.simplify_rational() + sage: equadiff = equadiff * equadiff.rhs().denominator() + sage: equadiff = equadiff - equadiff.rhs() + sage: equadiff (4*z - 1)*diff(C(z), z) - 2*C(z) + 1 == 0 or, more legibly, @@ -501,10 +503,10 @@ It is trivial to verify this equation on the closed form:: - sage: Cf = sage.symbolic.function_factory.function('C') # optional - sage.symbolic - sage: equadiff.substitute_function(Cf, s0.function(z)) # optional - sage.symbolic + sage: Cf = sage.symbolic.function_factory.function('C') # needs sage.symbolic + sage: equadiff.substitute_function(Cf, s0.function(z)) # needs sage.symbolic (4*z - 1)/sqrt(-4*z + 1) + sqrt(-4*z + 1) == 0 - sage: bool(equadiff.substitute_function(Cf, s0.function(z))) # optional - sage.symbolic + sage: bool(equadiff.substitute_function(Cf, s0.function(z))) # needs sage.symbolic True .. On veut non seulement remplacer les occurrences de C(z), mais @@ -780,8 +782,8 @@ Similarly, if we consider the number of compositions of `5` by length, we find a line of Pascal’s triangle:: - sage: x = var('x') # optional - sage.symbolic - sage: sum(x^len(c) for c in C5) # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic + sage: sum(x^len(c) for c in C5) # needs sage.symbolic x^5 + 4*x^4 + 6*x^3 + 4*x^2 + x The above example uses a functionality which we have not seen yet: @@ -872,12 +874,13 @@ ``Sage``; as a result, the following commands are not yet implemented:: - sage: C = Graphs(5) # todo: not implemented - sage: C.cardinality() # todo: not implemented + sage: # not implemented + sage: C = Graphs(5) + sage: C.cardinality() 34 - sage: Graphs(19).cardinality() # todo: not implemented + sage: Graphs(19).cardinality() 24637809253125004524383007491432768 - sage: Graphs(19).random_element() # todo: not implemented + sage: Graphs(19).random_element() Graph on 19 vertices What we have seen so far also applies, in principle, to finite algebraic @@ -893,8 +896,8 @@ or the algebra of `2\times 2` matrices over the finite field `\ZZ/2\ZZ`:: - sage: C = MatrixSpace(GF(2), 2) # optional - sage.modules sage.rings.finite_rings - sage: C.list() # optional - sage.modules sage.rings.finite_rings + sage: C = MatrixSpace(GF(2), 2) # needs sage.modules sage.rings.finite_rings + sage: C.list() # needs sage.modules sage.rings.finite_rings [ [0 0] [1 0] [0 1] [0 0] [0 0] [1 1] [1 0] [1 0] [0 1] [0 1] [0 0], [0 0], [0 0], [1 0], [0 1], [0 0], [1 0], [0 1], [1 0], [0 1], @@ -902,7 +905,7 @@ [0 0] [1 1] [1 1] [1 0] [0 1] [1 1] [1 1], [1 0], [0 1], [1 1], [1 1], [1 1] ] - sage: C.cardinality() # optional - sage.modules sage.rings.finite_rings + sage: C.cardinality() # needs sage.modules sage.rings.finite_rings 16 .. topic:: Exercise @@ -1146,18 +1149,18 @@ :: - sage: x = var('x') # optional - sage.symbolic - sage: sum(x^len(s) for s in Subsets(8)) # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic + sage: sum(x^len(s) for s in Subsets(8)) # needs sage.symbolic x^8 + 8*x^7 + 28*x^6 + 56*x^5 + 70*x^4 + 56*x^3 + 28*x^2 + 8*x + 1 :: - sage: sum(x^p.length() for p in Permutations(3)) # optional - sage.symbolic + sage: sum(x^p.length() for p in Permutations(3)) # needs sage.symbolic x^3 + 2*x^2 + 2*x + 1 :: - sage: factor(sum(x^p.length() for p in Permutations(3))) # optional - sage.symbolic + sage: factor(sum(x^p.length() for p in Permutations(3))) # needs sage.symbolic (x^2 + x + 1)*(x + 1) :: @@ -1375,15 +1378,15 @@ to define a formal variable ``Leaf`` for the leaves and a formal 2-ary function ``Node``:: - sage: var('Leaf') # optional - sage.symbolic + sage: var('Leaf') # needs sage.symbolic Leaf - sage: function('Node', nargs=2) # optional - sage.symbolic + sage: function('Node', nargs=2) # needs sage.symbolic Node The second tree in :ref:`figure-examples-catalan-trees` can be represented by the expression:: - sage: tr = Node(Node(Leaf, Node(Leaf, Leaf)), Leaf) # optional - sage.symbolic + sage: tr = Node(Node(Leaf, Node(Leaf, Leaf)), Leaf) # needs sage.symbolic .. _section-constructions: @@ -1666,7 +1669,7 @@ combinatorial species:: sage: from sage.combinat.species.library import * - sage: o = var('o') # optional - sage.symbolic + sage: o = var('o') # needs sage.symbolic We begin by redefining the complete binary trees; to do so, we stipulate the recurrence relation directly on the sets:: @@ -1678,10 +1681,10 @@ Now we can construct the set of trees with five nodes, list them, count them...:: - sage: BT5 = BT.isotypes([o]*5) # optional - sage.symbolic - sage: BT5.cardinality() # optional - sage.symbolic + sage: BT5 = BT.isotypes([o]*5) # needs sage.symbolic + sage: BT5.cardinality() # needs sage.symbolic 14 - sage: BT5.list() # optional - sage.symbolic + sage: BT5.list() # needs sage.symbolic [o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), @@ -1730,8 +1733,8 @@ :: - sage: FW3 = FW.isotypes([o]*3) # optional - sage.symbolic - sage: FW3.list() # optional - sage.symbolic + sage: FW3 = FW.isotypes([o]*3) # needs sage.symbolic + sage: FW3.list() # needs sage.symbolic [o*(o*(o*{})), o*(o*(({}*o)*{})), o*((({}*o)*o)*{}), (({}*o)*o)*(o*{}), (({}*o)*o)*(({}*o)*{})] diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 5aae3f020c6..4844662073d 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -8,10 +8,20 @@ - David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code. """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2016 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012-2013 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2014 Volker Braun +# 2013-2018 Jeroen Demeyer +# 2013-2021 John H. Palmieri +# 2017 Erik M. Bray +# 2017-2021 Frédéric Chapoton +# 2018 Sébastien Labbé +# 2019 François Bissey +# 2020-2023 Matthias Koeppe +# 2022 Michael Orlitzky +# 2022 Sebastian Oehms # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,6 +30,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +import importlib import random import os import sys @@ -35,16 +46,14 @@ from sage.misc.temporary_file import tmp_dir from cysignals.signals import AlarmInterrupt, init_cysignals -from .sources import FileDocTestSource, DictAsObject +from .sources import FileDocTestSource, DictAsObject, get_basename from .forker import DocTestDispatcher from .reporting import DocTestReporter from .util import Timer, count_noun, dict_difference from .external import available_software -from .parsing import parse_optional_tags +from .parsing import parse_optional_tags, parse_file_optional_tags, unparse_optional_tags, \ + nodoctest_regex, optionaltag_regex, optionalfiledirective_regex -nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') -optionaltag_regex = re.compile(r'^(\w|[.])+$') -optionalfiledirective_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)') # Optional tags which are always automatically added @@ -114,6 +123,7 @@ def __init__(self, **kwds): self.initial = False self.exitfirst = False self.force_lib = False + self.if_installed = False self.abspath = True # sage-runtests default is False self.verbose = False self.debug = False @@ -137,6 +147,7 @@ def __init__(self, **kwds): # the auto_optional_tags there. self.optional = set(['sage']) | auto_optional_tags self.hide = '' + self.probe = '' # > 0: always run GC before every test # < 0: disable GC @@ -213,18 +224,23 @@ def skipdir(dirname): return True return False -def skipfile(filename, tested_optional_tags=False): +def skipfile(filename, tested_optional_tags=False, *, + if_installed=False, log=None): """ - Return True if and only if the file ``filename`` should not be - doctested. + Return ``True`` if and only if the file ``filename`` should not be doctested. INPUT: - - ``filename`` - name of a file + - ``filename`` -- name of a file - - ``tested_optional_tags`` - a list or tuple or set of optional tags to test, + - ``tested_optional_tags`` -- a list or tuple or set of optional tags to test, or ``False`` (no optional test) or ``True`` (all optional tests) + - ``if_installed`` -- (boolean, default ``False``) whether to skip Python/Cython files + that are not installed as modules + + - ``log`` -- function to call with log messages, or ``None`` + If ``filename`` contains a line of the form ``"# sage.doctest: optional - xyz")``, then this will return ``False`` if "xyz" is in ``tested_optional_tags``. Otherwise, it returns the matching tag @@ -262,28 +278,49 @@ def skipfile(filename, tested_optional_tags=False): base, ext = os.path.splitext(filename) # .rst.txt appear in the installed documentation in subdirectories named "_sources" if ext not in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst', '.tex', '.rst.txt'): + if log: + log(f"Skipping '{filename}' because it does not have one of the recognized file name extensions") return True - # These files are created by the jupyter-sphinx extension for internal use and should not be tested if "jupyter_execute" in filename: + if log: + log(f"Skipping '{filename}' because it is created by the jupyter-sphinx extension for internal use and should not be tested") return True - with open(filename) as F: - line_count = 0 - for line in F: - if nodoctest_regex.match(line): + if if_installed and ext in ('.py', '.pyx', '.pxd'): + module_name = get_basename(filename) + try: + if not importlib.util.find_spec(module_name): # tries to import the containing package + if log: + log(f"Skipping '{filename}' because module {module_name} is not present in the venv") return True - if tested_optional_tags is not True: - # Adapted from code in SageDocTestParser.parse - m = optionalfiledirective_regex.match(line) - if m: - if tested_optional_tags is False: - return m.group(2) - optional_tags = parse_optional_tags('#' + m.group(2)) - extra = optional_tags - set(tested_optional_tags) - if extra: - return m.group(2) - line_count += 1 - if line_count >= 10: - break + except ModuleNotFoundError as e: + if log: + log(f"Skipping '{filename}' because module {e.name} cannot be imported") + return True + + with open(filename) as F: + file_optional_tags = parse_file_optional_tags(enumerate(F)) + + if 'not tested' in file_optional_tags: + if log: + log(f"Skipping '{filename}' because it is marked 'nodoctest'") + return True + + if tested_optional_tags is False: + if file_optional_tags: + file_tag_string = unparse_optional_tags(file_optional_tags, prefix='') + if log: + log(f"Skipping '{filename}' because it is marked '# {file_tag_string}'") + return file_tag_string + + elif tested_optional_tags is not True: + extra = set(tag for tag in file_optional_tags + if tag not in tested_optional_tags) + if extra: + file_tag_string = unparse_optional_tags(file_optional_tags, prefix='') + if log: + log(f"Skipping '{filename}' because it is marked '{file_tag_string}'") + return file_tag_string + return False @@ -458,6 +495,23 @@ def __init__(self, options, args): options.optional |= auto_optional_tags options.optional -= options.disabled_optional + if isinstance(options.probe, str): + if options.probe == 'none': + options.probe = '' + s = options.probe.lower() + if not s: + options.probe = set() + else: + options.probe = set(s.split(',')) + if "all" in options.probe: + # Special case to probe all features that are not present + options.probe = True + else: + # Check that all tags are valid + for o in options.probe: + if not optionaltag_regex.search(o): + raise ValueError('invalid optional tag {!r}'.format(o)) + self.options = options self.files = args @@ -893,7 +947,9 @@ def all_doc_sources(): and (filename.endswith(".py") or filename.endswith(".pyx") or filename.endswith(".rst")) - and not skipfile(opj(SAGE_ROOT, filename), self.options.optional)): + and not skipfile(opj(SAGE_ROOT, filename), + True if self.options.optional else False, + if_installed=self.options.if_installed)): self.files.append(os.path.relpath(opj(SAGE_ROOT, filename))) def expand_files_into_sources(self): @@ -953,11 +1009,14 @@ def expand(): if dir[0] == "." or skipdir(os.path.join(root,dir)): dirs.remove(dir) for file in files: - if not skipfile(os.path.join(root, file), self.options.optional): + if not skipfile(os.path.join(root, file), + True if self.options.optional else False, + if_installed=self.options.if_installed): yield os.path.join(root, file) else: - # the user input this file explicitly, so we don't skip it - yield path + if not skipfile(path, True if self.options.optional else False, + if_installed=self.options.if_installed, log=self.log): # log when directly specified filenames are skipped + yield path self.sources = [FileDocTestSource(path, self.options) for path in expand()] def filter_sources(self): @@ -1462,6 +1521,9 @@ def run(self): self.log("Features to be detected: " + ','.join(available_software.detectable())) if self.options.hidden_features: self.log("Hidden features: " + ','.join([f.name for f in self.options.hidden_features])) + if self.options.probe: + self.log("Features to be probed: " + ('all' if self.options.probe is True + else ','.join(self.options.probe))) self.add_files() self.expand_files_into_sources() self.filter_sources() diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 60299862755..8744bd93e72 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -16,7 +16,12 @@ """ #***************************************************************************** -# Copyright (C) 2016 KWANKYU LEE +# Copyright (C) 2016 Kwankyu Lee +# 2018 Thierry Monteil +# 2018-2021 Sébastien Labbé +# 2019 Markus Wageringel +# 2020 John H. Palmieri +# 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index e6f8fc45f8e..a3b5e9edda7 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -33,7 +33,7 @@ """ #***************************************************************************** -# Copyright (C) 2014 Martin von Gagern +# Copyright (C) 2014-2015 Martin von Gagern # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 24ede7eeb9c..b5521868dd5 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -22,10 +22,22 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2013-2015 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2018 Jeroen Demeyer +# 2013-2020 John H. Palmieri +# 2013-2017 Volker Braun +# 2014 André Apitzsch +# 2014 Darij Grinberg +# 2016-2021 Frédéric Chapoton +# 2017-2019 Erik M. Bray +# 2018 Julian Rüth +# 2020 Jonathan Kliem +# 2020-2023 Matthias Koeppe +# 2022 Markus Wageringel +# 2022 Michael Orlitzky # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -57,9 +69,9 @@ from sage.misc.timing import walltime from .util import Timer, RecordingDict, count_noun from .sources import DictAsObject -from .parsing import OriginalSource, reduce_hex, unparse_optional_tags +from .parsing import OriginalSource, reduce_hex from sage.structure.sage_object import SageObject -from .parsing import SageOutputChecker, pre_hash, get_source +from .parsing import SageOutputChecker, pre_hash, get_source, unparse_optional_tags from sage.repl.user_globals import set_globals from sage.cpython.atexit import restore_atexit from sage.cpython.string import bytes_to_str, str_to_bytes @@ -132,10 +144,11 @@ def init_sage(controller=None): Check that SymPy equation pretty printer is limited in doctest mode to default width (80 chars):: - sage: from sympy import sympify # optional - sage.symbolic - sage: from sympy.printing.pretty.pretty import PrettyPrinter # optional - sage.symbolic - sage: s = sympify('+x^'.join(str(i) for i in range(30))) # optional - sage.symbolic - sage: print(PrettyPrinter(settings={'wrap_line': True}).doprint(s)) # optional - sage.symbolic + sage: # needs sympy + sage: from sympy import sympify + sage: from sympy.printing.pretty.pretty import PrettyPrinter + sage: s = sympify('+x^'.join(str(i) for i in range(30))) + sage: print(PrettyPrinter(settings={'wrap_line': True}).doprint(s)) 29 28 27 26 25 24 23 22 21 20 19 18 17 x + x + x + x + x + x + x + x + x + x + x + x + x + @@ -716,10 +729,18 @@ def compiler(example): outcome = FAILURE # guilty until proved innocent or insane + probed_tags = getattr(example, 'probed_tags', False) + # If the example executed without raising any exceptions, # verify its output. if exception is None: if check(example.want, got, self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded.") outcome = SUCCESS # The example raised an exception: check if it was expected. @@ -755,6 +776,12 @@ def compiler(example): # We expected an exception: see whether it matches. elif check(example.exc_msg, exc_msg, self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(example.probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded (raised the expected exception).") outcome = SUCCESS # Another chance if they didn't care about the detail. @@ -763,22 +790,32 @@ def compiler(example): m2 = re.match(r'(?:[^:]*\.)?([^:]*:)', exc_msg) if m1 and m2 and check(m1.group(1), m2.group(1), self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(example.probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded (raised an exception as expected).") outcome = SUCCESS check_duration = walltime(check_starttime) self.total_walltime += example.walltime + check_duration # Report the outcome. + if example.warnings: + for warning in example.warnings: + out(self._failure_header(test, example, f'Warning: {warning}')) if outcome is SUCCESS: if self.options.warn_long > 0 and example.walltime + check_duration > self.options.warn_long: self.report_overtime(out, test, example, got, check_duration=check_duration) elif example.warnings: - for warning in example.warnings: - out(self._failure_header(test, example, f'Warning: {warning}')) + pass elif not quiet: self.report_success(out, test, example, got, check_duration=check_duration) + elif probed_tags: + pass elif outcome is FAILURE: if not quiet: self.report_failure(out, test, example, got, test.globs) @@ -1097,7 +1134,8 @@ def compile_and_execute(self, example, compiler, globs): if isinstance(globs, RecordingDict): globs.start() example.sequence_number = len(self.history) - example.warnings = [] + if not hasattr(example, 'warnings'): + example.warnings = [] self.history.append(example) timer = Timer().start() try: @@ -1111,16 +1149,24 @@ def compile_and_execute(self, example, compiler, globs): for name in globs.got: setters_dict = self.setters.get(name) # setter_optional_tags -> setter if setters_dict: + was_set = False for setter_optional_tags, setter in setters_dict.items(): - if setter_optional_tags.issubset(example.optional_tags): + if setter_optional_tags.issubset(example.optional_tags): # was set in a less constrained doctest + was_set = True example.predecessors.append(setter) - if not example.predecessors: - f_setter_optional_tags = "; ".join("'" - + unparse_optional_tags(setter_optional_tags) - + "'" - for setter_optional_tags in setters_dict) - example.warnings.append(f"Variable '{name}' referenced here " - f"was set only in doctest marked {f_setter_optional_tags}") + if not was_set: + if example.probed_tags: + # Probing confusion. + # Do not issue the "was set only in doctest marked" warning; + # and also do not issue the "may no longer be needed" notice + example.probed_tags = True + else: + f_setter_optional_tags = "; ".join("'" + + unparse_optional_tags(setter_optional_tags) + + "'" + for setter_optional_tags in setters_dict) + example.warnings.append(f"Variable '{name}' referenced here " + f"was set only in doctest marked {f_setter_optional_tags}") for name in globs.set: self.setters[name][example.optional_tags] = example else: @@ -2134,7 +2180,7 @@ def run(self): TESTS:: - sage: run_doctests(sage.symbolic.units) # indirect doctest # optional - sage.symbolic + sage: run_doctests(sage.symbolic.units) # indirect doctest # needs sage.symbolic Running doctests with ID ... Doctesting 1 file. sage -t .../sage/symbolic/units.py diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 579768d6a06..86d7aa71174 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -13,9 +13,19 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# Copyright (C) 2012-2018 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013 Volker Braun +# 2013-2018 Jeroen Demeyer +# 2016-2021 Frédéric Chapoton +# 2017-2018 Erik M. Bray +# 2020 Marc Mezzarobba +# 2020-2023 Matthias Koeppe +# 2022 John H. Palmieri +# 2022 Sébastien Labbé +# 2023 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -23,13 +33,17 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import re +import collections.abc import doctest +import re + from collections import defaultdict -from sage.repl.preparse import preparse, strip_string_literals from functools import reduce -from .external import available_software +from sage.misc.cachefunc import cached_function +from sage.repl.preparse import preparse, strip_string_literals + +from .external import available_software, external_software _RIFtol = None @@ -80,111 +94,455 @@ def fake_RIFtol(*args): ansi_escape_sequence = re.compile(r'(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])') special_optional_regex = 'arb216|arb218|py2|long time|not implemented|not tested|known bug' -optional_regex = re.compile(fr'({special_optional_regex})|([^ a-z]\s*optional\s*[:-]*((\s|\w|[.])*))') -special_optional_regex = re.compile(special_optional_regex) +tag_with_explanation_regex = fr'((?:\w|[.])+)\s*(?:\((.*?)\))?' +optional_regex = re.compile(fr'(?P{special_optional_regex})\s*(?:\((?P.*?)\))?|' + fr'[^ a-z]\s*(optional|needs)(?:\s|[:-])*(?P(?:(?:{tag_with_explanation_regex})\s*)*)', + re.IGNORECASE) +special_optional_regex = re.compile(special_optional_regex, re.IGNORECASE) +tag_with_explanation_regex = re.compile(tag_with_explanation_regex, re.IGNORECASE) +nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') +optionaltag_regex = re.compile(r'^(\w|[.])+$') +optionalfiledirective_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)') -def parse_optional_tags(string): - """ - Return a set consisting of the optional tags from the following + +def parse_optional_tags(string, *, return_string_sans_tags=False): + r""" + Return a dictionary whose keys are optional tags from the following set that occur in a comment on the first line of the input string. - - 'long time' - - 'not implemented' - - 'not tested' - - 'known bug' - - 'py2' - - 'arb216' - - 'arb218' - - 'optional: PKG_NAME' -- the set will just contain 'PKG_NAME' + - ``'long time'`` + - ``'not implemented'`` + - ``'not tested'`` + - ``'known bug'`` + - ``'py2'`` + - ``'arb216'`` + - ``'arb218'`` + - ``'optional - FEATURE...'`` or ``'needs FEATURE...'`` -- + the dictionary will just have the key ``'FEATURE'`` + + The values, if non-``None``, are strings with optional explanations + for a tag, which may appear in parentheses after the tag in ``string``. + + INPUT: + + - ``string`` -- a string + + - ``return_string_sans_tags`` -- (boolean, default ``False``); whether to + additionally return ``string`` with the optional tags removed but other + comments kept and a boolean ``is_persistent`` EXAMPLES:: sage: from sage.doctest.parsing import parse_optional_tags sage: parse_optional_tags("sage: magma('2 + 2')# optional: magma") - {'magma'} + {'magma': None} sage: parse_optional_tags("sage: #optional -- mypkg") - {'mypkg'} + {'mypkg': None} sage: parse_optional_tags("sage: print(1) # parentheses are optional here") - set() + {} sage: parse_optional_tags("sage: print(1) # optional") - {''} + {} sage: sorted(list(parse_optional_tags("sage: #optional -- foo bar, baz"))) ['bar', 'foo'] sage: parse_optional_tags("sage: #optional -- foo.bar, baz") - {'foo.bar'} + {'foo.bar': None} + sage: parse_optional_tags("sage: #needs foo.bar, baz") + {'foo.bar': None} sage: sorted(list(parse_optional_tags(" sage: factor(10^(10^10) + 1) # LoNg TiME, NoT TeSTED; OptioNAL -- P4cka9e"))) ['long time', 'not tested', 'p4cka9e'] sage: parse_optional_tags(" sage: raise RuntimeError # known bug") - {'bug'} + {'bug': None} sage: sorted(list(parse_optional_tags(" sage: determine_meaning_of_life() # long time, not implemented"))) ['long time', 'not implemented'] We don't parse inside strings:: sage: parse_optional_tags(" sage: print(' # long time')") - set() + {} sage: parse_optional_tags(" sage: print(' # long time') # not tested") - {'not tested'} + {'not tested': None} UTF-8 works:: - sage: parse_optional_tags("'ěščřžýáíéďĎ'") - set() + sage: parse_optional_tags("'ěščřžýáíéďĎ'") + {} + + Tags with parenthesized explanations:: + + sage: parse_optional_tags(" sage: 1 + 1 # long time (1 year, 2 months??), optional - bliss (because)") + {'bliss': 'because', 'long time': '1 year, 2 months??'} + + With ``return_string_sans_tags=True``:: + + sage: parse_optional_tags("sage: print(1) # very important 1 # optional - foo", + ....: return_string_sans_tags=True) + ({'foo': None}, 'sage: print(1) # very important 1 ', False) + sage: parse_optional_tags("sage: print( # very important too # optional - foo\n....: 2)", + ....: return_string_sans_tags=True) + ({'foo': None}, 'sage: print( # very important too \n....: 2)', False) + sage: parse_optional_tags("sage: #this is persistent #needs scipy", + ....: return_string_sans_tags=True) + ({'scipy': None}, 'sage: #this is persistent ', True) + sage: parse_optional_tags("sage: #this is not #needs scipy\n....: import scipy", + ....: return_string_sans_tags=True) + ({'scipy': None}, 'sage: #this is not \n....: import scipy', False) + """ safe, literals, state = strip_string_literals(string) - first_line = safe.split('\n', 1)[0] - if '#' not in first_line: - return set() - comment = first_line[first_line.find('#') + 1:] - comment = comment[comment.index('(') + 1: comment.rindex(')')] - # strip_string_literals replaces comments - comment = "#" + (literals[comment]).lower() + split = safe.split('\n', 1) + if len(split) > 1: + first_line, rest = split + else: + first_line, rest = split[0], None + + sharp_index = first_line.find('#') + if sharp_index < 0: # no comment + if return_string_sans_tags: + return {}, string, False + else: + return {} + + first_line_sans_comments, comment = first_line[:sharp_index] % literals, first_line[sharp_index:] % literals + + if return_string_sans_tags: + # skip non-tag comments that precede the first tag comment + if m := optional_regex.search(comment): + sharp_index = comment[:m.start(0) + 1].rfind('#') + if sharp_index >= 0: + first_line = first_line_sans_comments + comment[:sharp_index] + comment = comment[sharp_index:] + else: + # no tag comment + return {}, string, False - tags = [] + tags = {} for m in optional_regex.finditer(comment): - cmd = m.group(1) - if cmd == 'known bug': - tags.append('bug') # so that such tests will be run by sage -t ... -only-optional=bug + cmd = m.group('cmd') + if cmd and cmd.lower() == 'known bug': + tags['bug'] = None # so that such tests will be run by sage -t ... -only-optional=bug elif cmd: - tags.append(cmd) + tags[cmd.lower()] = m.group('cmd_explanation') or None else: - tags.extend(m.group(3).split() or [""]) - return set(tags) + # optional/needs + tags.update({m.group(1).lower(): m.group(2) or None + for m in tag_with_explanation_regex.finditer(m.group('tags'))}) + + if return_string_sans_tags: + is_persistent = tags and first_line_sans_comments.strip() == 'sage:' and not rest # persistent (block-scoped) tag + return tags, (first_line + '\n' + rest%literals if rest is not None + else first_line), is_persistent + else: + return tags + + +def parse_file_optional_tags(lines): + r""" + Scan the first few lines for file-level doctest directives. + + INPUT: + + - ``lines`` -- iterable of pairs ``(lineno, line)``. + + OUTPUT: + + a dictionary whose keys are strings (tags); see :func:`parse_optional_tags` + + EXAMPLES:: + + sage: from sage.doctest.parsing import parse_file_optional_tags + sage: filename = tmp_filename(ext=".pyx") + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {} + sage: with open(filename, "w") as f: + ....: _ = f.write("# nodoctest") + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {'not tested': None} + sage: with open(filename, "w") as f: + ....: _ = f.write("# sage.doctest: " # broken in two source lines to avoid the pattern + ....: "optional - xyz") # of relint (multiline_doctest_comment) + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {'xyz': None} + """ + tags = {} + for line_count, line in lines: + if nodoctest_regex.match(line): + tags['not tested'] = None + if m := optionalfiledirective_regex.match(line): + file_tag_string = m.group(2) + tags.update(parse_optional_tags('#' + file_tag_string)) + if line_count >= 10: + break + return tags + + +@cached_function +def _standard_tags(): + r""" + Return the set of the names of all standard features. + + EXAMPLES:: + + sage: from sage.doctest.parsing import _standard_tags + sage: sorted(_standard_tags()) + [..., 'numpy', ..., 'sage.rings.finite_rings', ...] + """ + from sage.features.all import all_features + return frozenset(feature.name for feature in all_features() + if feature._spkg_type() == 'standard') + + +def _tag_group(tag): + r""" + Classify a doctest tag as belonging to one of 4 groups. + INPUT: + + - ``tag`` -- string + + OUTPUT: + + a string; one of ``'special'``, ``'optional'``, ``'standard'``, ``'sage'`` + + EXAMPLES:: -def unparse_optional_tags(tags): + sage: from sage.doctest.parsing import _tag_group + sage: _tag_group('scipy') + 'standard' + sage: _tag_group('sage.numerical.mip') + 'sage' + sage: _tag_group('bliss') + 'optional' + sage: _tag_group('not tested') + 'special' + """ + if tag.startswith('sage.'): + return 'sage' + elif tag in _standard_tags(): + return 'standard' + elif not special_optional_regex.fullmatch(tag): + return 'optional' + else: + return 'special' + + +def unparse_optional_tags(tags, prefix='# '): r""" Return a comment string that sets ``tags``. INPUT: - - ``tags`` -- iterable of tags, as output by :func:`parse_optional_tags` + - ``tags`` -- dict or iterable of tags, as output by :func:`parse_optional_tags` + + - ``prefix`` -- to be put before a nonempty string EXAMPLES:: sage: from sage.doctest.parsing import unparse_optional_tags - sage: unparse_optional_tags(set()) + sage: unparse_optional_tags({}) '' - sage: unparse_optional_tags({'magma'}) + sage: unparse_optional_tags({'magma': None}) '# optional - magma' - sage: unparse_optional_tags(['zipp', 'sage.rings.number_field', 'foo']) - '# optional - foo zipp sage.rings.number_field' - sage: unparse_optional_tags(['long time', 'not tested', 'p4cka9e']) - '# long time, not tested, optional - p4cka9e' + sage: unparse_optional_tags({'fictional_optional': None, + ....: 'sage.rings.number_field': None, + ....: 'scipy': 'just because', + ....: 'bliss': None}) + '# optional - bliss fictional_optional, needs scipy (just because) sage.rings.number_field' + sage: unparse_optional_tags(['long time', 'not tested', 'p4cka9e'], prefix='') + 'long time, not tested, optional - p4cka9e' """ - tags = set(tags) - special_tags = set(tag for tag in tags if special_optional_regex.fullmatch(tag)) - optional_tags = sorted(tags - special_tags, - key=lambda tag: (tag.startswith('sage.'), tag)) - tags = sorted(special_tags) - if optional_tags: - tags.append('optional - ' + " ".join(optional_tags)) + group = defaultdict(set) + if isinstance(tags, collections.abc.Mapping): + for tag, explanation in tags.items(): + if tag == 'bug': + tag = 'known bug' + group[_tag_group(tag)].add(f'{tag} ({explanation})' if explanation else tag) + else: + for tag in tags: + if tag == 'bug': + tag = 'known bug' + group[_tag_group(tag)].add(tag) + + tags = sorted(group.pop('special', [])) + if 'optional' in group: + tags.append('optional - ' + " ".join(sorted(group.pop('optional')))) + if 'standard' in group or 'sage' in group: + tags.append('needs ' + " ".join(sorted(group.pop('standard', [])) + + sorted(group.pop('sage', [])))) + assert not group if tags: - return '# ' + ', '.join(tags) + return prefix + ', '.join(tags) return '' +optional_tag_columns = [48, 56, 64, 72, 80, 84] +standard_tag_columns = [88, 100, 120, 160] + + +def update_optional_tags(line, tags=None, *, add_tags=None, remove_tags=None, force_rewrite=False): + r""" + Return the doctest ``line`` with tags changed. + + EXAMPLES:: + + sage: from sage.doctest.parsing import update_optional_tags, optional_tag_columns, standard_tag_columns + sage: ruler = '' + sage: for column in optional_tag_columns: + ....: ruler += ' ' * (column - len(ruler)) + 'V' + sage: for column in standard_tag_columns: + ....: ruler += ' ' * (column - len(ruler)) + 'v' + sage: def print_with_ruler(lines): + ....: print('|' + ruler) + ....: for line in lines: + ....: print('|' + line) + sage: print_with_ruler([ # the tags are obscured in the source file to avoid relint warnings + ....: update_optional_tags(' sage: something() # opt' 'ional - latte_int', + ....: remove_tags=['latte_int', 'wasnt_even_there']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here()', + ....: tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here(honestly=True)', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here(honestly=True, very=True)', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious()#opt' 'ional:bliss', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: ntbsh() # abbrv for above#opt' 'ional:bliss', + ....: add_tags={'scipy': None, 'long time': '30s on the highest setting'}), + ....: update_optional_tags(' sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # really, you can trust me here', + ....: add_tags=['scipy']), + ....: ]) + | V V V V V V v v v v + | sage: something() + | sage: nothing_to_be_seen_here() # long time # needs scipy + | sage: nothing_to_be_seen_here(honestly=True) # long time # needs scipy + | sage: nothing_to_be_seen_here(honestly=True, very=True) # long time # needs scipy + | sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # long time, optional - bliss, needs scipy + | sage: ntbsh() # abbrv for above # long time (30s on the highest setting), optional - bliss, needs scipy + | sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # really, you can trust me here # needs scipy + + When no tags are changed, by default, the unchanged input is returned. + We can force a rewrite; unconditionally or whenever standard tags are involved. + But even when forced, if comments are already aligned at one of the standard alignment columns, + this alignment is kept even if we would normally realign farther to the left:: + + sage: print_with_ruler([ + ....: update_optional_tags(' sage: unforced() # opt' 'ional - latte_int'), + ....: update_optional_tags(' sage: unforced() # opt' 'ional - latte_int', + ....: add_tags=['latte_int']), + ....: update_optional_tags(' sage: forced()#opt' 'ional- latte_int', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: forced() # opt' 'ional - scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: aligned_with_below() # opt' 'ional - 4ti2', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: aligned_with_above() # opt' 'ional - 4ti2', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: also_already_aligned() # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: two_columns_first_preserved() # lo' 'ng time # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: two_columns_first_preserved() # lo' 'ng time # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: ]) + | V V V V V V v v v v + | sage: unforced() # optional - latte_int + | sage: unforced() # optional - latte_int + | sage: forced() # optional - latte_int + | sage: forced() # needs scipy + | sage: aligned_with_below() # optional - 4ti2 + | sage: aligned_with_above() # optional - 4ti2 + | sage: also_already_aligned() # needs scipy + | sage: two_columns_first_preserved() # long time # needs scipy + | sage: two_columns_first_preserved() # long time # needs scipy + + Rewriting a persistent (block-scoped) tag:: + + sage: print_with_ruler([ + ....: update_optional_tags(' sage: #opt' 'ional:magma sage.symbolic', + ....: force_rewrite=True), + ....: ]) + | V V V V V V v v v v + | sage: # optional - magma, needs sage.symbolic + """ + if not (m := re.match('( *sage: *)(.*)', line)): + raise ValueError(f'line must start with a sage: prompt, got: {line}') + + current_tags, line_sans_tags, is_persistent = parse_optional_tags(line.rstrip(), return_string_sans_tags=True) + + if isinstance(tags, collections.abc.Mapping): + new_tags = dict(tags) + elif tags is not None: + new_tags = {tag: None for tag in tags} + else: + new_tags = dict(current_tags) + + if add_tags is not None: + if isinstance(add_tags, collections.abc.Mapping): + new_tags.update(add_tags) + else: + new_tags.update({tag: None for tag in add_tags}) + + if remove_tags is not None: + for tag in remove_tags: + new_tags.pop(tag, None) + + if not force_rewrite and new_tags == current_tags: + return line + + if not new_tags: + return line_sans_tags.rstrip() + + if (force_rewrite == 'standard' + and new_tags == current_tags + and not any(_tag_group(tag) in ['standard', 'sage'] + for tag in new_tags)): + return line + + if is_persistent: + line = line_sans_tags.rstrip() + ' ' + else: + group = defaultdict(set) + for tag in new_tags: + group[_tag_group(tag)].add(tag) + tag_columns = (optional_tag_columns if group['optional'] or group['special'] + else standard_tag_columns) + + if len(line_sans_tags) in tag_columns and line_sans_tags[-2:] == ' ': + # keep alignment + line = line_sans_tags + pass + else: + # realign + line = line_sans_tags.rstrip() + for column in tag_columns: + if len(line) <= column - 2: + line += ' ' * (column - 2 - len(line)) + break + line += ' ' + + if (group['optional'] or group['special']) and (group['standard'] or group['sage']): + # Try if two-column mode works better + first_part = unparse_optional_tags({tag: explanation + for tag, explanation in new_tags.items() + if (tag in group['optional'] + or tag in group['special'])}) + column = standard_tag_columns[0] + if len(line + first_part) + 8 <= column: + line += first_part + line += ' ' * (column - len(line)) + line += unparse_optional_tags({tag: explanation + for tag, explanation in new_tags.items() + if not (tag in group['optional'] + or tag in group['special'])}) + return line.rstrip() + + line += unparse_optional_tags(new_tags) + return line + + def parse_tolerance(source, want): r""" Return a version of ``want`` marked up with the tolerance tags @@ -476,13 +834,15 @@ class SageDocTestParser(doctest.DocTestParser): A version of the standard doctest parser which handles Sage's custom options and tolerances in floating point arithmetic. """ - def __init__(self, optional_tags=(), long=False): + def __init__(self, optional_tags=(), long=False, *, probed_tags=(), file_optional_tags=()): r""" INPUT: - ``optional_tags`` -- a list or tuple of strings. - ``long`` -- boolean, whether to run doctests marked as taking a long time. + - ``probed_tags`` -- a list or tuple of strings. + - ``file_optional_tags`` -- an iterable of strings. EXAMPLES:: @@ -511,6 +871,8 @@ def __init__(self, optional_tags=(), long=False): self.optional_tags.remove('sage') else: self.optional_only = True + self.probed_tags = probed_tags + self.file_optional_tags = set(file_optional_tags) def __eq__(self, other): """ @@ -556,7 +918,7 @@ def parse(self, string, *args): - A list consisting of strings and :class:`doctest.Example` instances. There will be at least one string between - successive examples (exactly one unless or long or optional + successive examples (exactly one unless long or optional tests are removed), and it will begin and end with a string. EXAMPLES:: @@ -605,7 +967,7 @@ def parse(self, string, *args): of the current line should be joined to the next line. This feature allows for breaking large integers over multiple lines but is not standard for Python doctesting. It's not - guaranteed to persist, but works in Sage 5.5:: + guaranteed to persist:: sage: n = 1234\ ....: 5678 @@ -621,6 +983,23 @@ def parse(self, string, *args): sage: print(m) 87654321 + Optional tags at the start of an example block persist to the end of + the block (delimited by a blank line):: + + sage: # long time, needs sage.rings.number_field + sage: QQbar(I)^10000 + 1 + sage: QQbar(I)^10000 # not tested + I + + sage: # needs sage.rings.finite_rings + sage: GF(7) + Finite Field of size 7 + sage: GF(10) + Traceback (most recent call last): + ... + ValueError: the order of a finite field must be a prime power + Test that :trac:`26575` is resolved:: sage: example3 = 'sage: Zp(5,4,print_mode="digits")(5)\n...00010' @@ -630,6 +1009,68 @@ def parse(self, string, *args): 'Zp(5,4,print_mode="digits")(5)\n' sage: dte.want '...00010\n' + + Style warnings:: + + sage: def parse(test_string): + ....: return [x if isinstance(x, str) + ....: else (getattr(x, 'warnings', None), x.sage_source, x.source) + ....: for x in DTP.parse(test_string)] + + sage: parse('sage: 1 # optional guava mango\nsage: 2 # optional guava\nsage: 3 # optional guava\nsage: 4 # optional guava\nsage: 5 # optional guava\n\nsage: 11 # optional guava') + ['', + (["Consider using a block-scoped tag by inserting the line 'sage: # optional - guava' just before this line to avoid repeating the tag 5 times"], + '1 # optional guava mango\n', + 'None # virtual doctest'), + '', + (None, '2 # optional guava\n', 'Integer(2) # optional guava\n'), + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + '\n', + (None, '11 # optional guava\n', 'Integer(11) # optional guava\n'), + ''] + + sage: parse('sage: 1 # optional guava\nsage: 2 # optional guava mango\nsage: 3 # optional guava\nsage: 4 # optional guava\nsage: 5 # optional guava\n') + ['', + (["Consider using a block-scoped tag by inserting the line 'sage: # optional - guava' just before this line to avoid repeating the tag 5 times"], + '1 # optional guava\n', + 'Integer(1) # optional guava\n'), + '', + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + ''] + + sage: parse('sage: # optional mango\nsage: 1 # optional guava\nsage: 2 # optional guava mango\nsage: 3 # optional guava\nsage: 4 # optional guava\n sage: 5 # optional guava\n') # optional - guava mango + ['', + (["Consider updating this block-scoped tag to 'sage: # optional - guava mango' to avoid repeating the tag 5 times"], + '# optional mango\n', + 'None # virtual doctest'), + '', + '', + '', + '', + '', + ''] + + sage: parse('::\n\n sage: 1 # optional guava\n sage: 2 # optional guava mango\n sage: 3 # optional guava\n\n::\n\n sage: 4 # optional guava\n sage: 5 # optional guava\n') + ['::\n\n', + (None, '1 # optional guava\n', 'Integer(1) # optional guava\n'), + '', + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '\n::\n\n', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + ''] """ # Regular expressions find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) @@ -664,10 +1105,77 @@ def parse(self, string, *args): string = find_sage_continuation.sub(r"\1...", string) res = doctest.DocTestParser.parse(self, string, *args) filtered = [] + persistent_optional_tags = self.file_optional_tags + persistent_optional_tag_setter = None + persistent_optional_tag_setter_index = None + first_example_in_block = None + first_example_in_block_index = None + tag_count_within_block = defaultdict(lambda: 0) + + def update_tag_counts(optional_tags): + for tag in optional_tags: + if tag not in persistent_optional_tags: + tag_count_within_block[tag] += 1 + tag_count_within_block[''] += 1 + + def check_and_clear_tag_counts(): + if (num_examples := tag_count_within_block['']) >= 4: + if overused_tags := set(tag for tag, count in tag_count_within_block.items() + if tag and count >= num_examples): + overused_tags.update(persistent_optional_tags) + overused_tags.difference_update(self.file_optional_tags) + suggested = unparse_optional_tags(overused_tags, prefix='sage: # ') + + if persistent_optional_tag_setter: + warning_example = persistent_optional_tag_setter + index = persistent_optional_tag_setter_index + warning = (f"Consider updating this block-scoped tag to '{suggested}' " + f"to avoid repeating the tag {num_examples} times") + else: + warning_example = first_example_in_block + index = first_example_in_block_index + warning = (f"Consider using a block-scoped tag by " + f"inserting the line '{suggested}' just before this line " + f"to avoid repeating the tag {num_examples} times") + + if not (index < len(filtered) and filtered[index] == warning_example): + # The example to which we want to attach our warning is + # not in ``filtered``. It is either the persistent tag line, + # or the first example of the block and not run because of unmet tags, + # or just a comment. Either way, we transform this example + # to a virtual example and attach the warning to it. + warning_example.sage_source = warning_example.source + if warning_example.sage_source.startswith("sage: "): + warning_example.sage_source = warning_example.source[6:] + warning_example.source = 'None # virtual doctest' + warning_example.want = '' + filtered.insert(index, warning_example) + + if not hasattr(warning_example, 'warnings'): + warning_example.warnings = [] + warning_example.warnings.append(warning) + tag_count_within_block.clear() + for item in res: if isinstance(item, doctest.Example): - optional_tags = parse_optional_tags(item.source) + optional_tags, source_sans_tags, is_persistent = parse_optional_tags(item.source, return_string_sans_tags=True) + optional_tags = set(optional_tags) + if is_persistent: + check_and_clear_tag_counts() + persistent_optional_tags = optional_tags + persistent_optional_tags.update(self.file_optional_tags) + persistent_optional_tag_setter = first_example_in_block = item + persistent_optional_tag_setter_index = len(filtered) + first_example_in_block_index = None + continue + + if not first_example_in_block: + first_example_in_block = item + first_example_in_block_index = len(filtered) + update_tag_counts(optional_tags) + optional_tags.update(persistent_optional_tags) item.optional_tags = frozenset(optional_tags) + item.probed_tags = set() if optional_tags: for tag in optional_tags: self.optionals[tag] += 1 @@ -682,13 +1190,28 @@ def parse(self, string, *args): continue if self.optional_tags is not True: - extra = optional_tags - self.optional_tags # set difference + extra = set(tag + for tag in optional_tags + if (tag not in self.optional_tags + and tag not in available_software)) if extra: - if not available_software.issuperset(extra): + if any(tag in external_software for tag in extra): + # never probe "external" software + continue + if all(tag in persistent_optional_tags for tag in extra): + # don't probe if test is only conditional + # on file-level or block-level tags + continue + if self.probed_tags is True: + item.probed_tags = extra + elif all(tag in self.probed_tags for tag in extra): + item.probed_tags = extra + else: continue elif self.optional_only: self.optionals['sage'] += 1 continue + if replace_ellipsis: item.want = item.want.replace(ellipsis_tag, "...") if item.exc_msg is not None: @@ -699,7 +1222,16 @@ def parse(self, string, *args): if item.sage_source.lstrip().startswith('#'): continue item.source = preparse(item.sage_source) + else: + if '\n' in item: + check_and_clear_tag_counts() + persistent_optional_tags = self.file_optional_tags + persistent_optional_tag_setter = first_example_in_block = None + persistent_optional_tag_setter_index = first_example_in_block_index = None filtered.append(item) + + check_and_clear_tag_counts() + return filtered @@ -783,11 +1315,11 @@ def add_tolerance(self, wantval, want): sage: want_tol = MarkedOutput().update(tol=0.0001) sage: want_abs = MarkedOutput().update(abs_tol=0.0001) sage: want_rel = MarkedOutput().update(rel_tol=0.0001) - sage: OC.add_tolerance(RIF(pi.n(64)), want_tol).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_tol).endpoints() # needs sage.symbolic (3.14127849432443, 3.14190681285516) - sage: OC.add_tolerance(RIF(pi.n(64)), want_abs).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_abs).endpoints() # needs sage.symbolic (3.14149265358979, 3.14169265358980) - sage: OC.add_tolerance(RIF(pi.n(64)), want_rel).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_rel).endpoints() # needs sage.symbolic (3.14127849432443, 3.14190681285516) sage: OC.add_tolerance(RIF(1e1000), want_tol) 1.000?e1000 @@ -897,7 +1429,7 @@ def check_output(self, want, got, optionflags): More explicit tolerance checks:: - sage: _ = x # rel tol 1e10 + sage: _ = x # rel tol 1e10 # needs sage.symbolic sage: raise RuntimeError # rel tol 1e10 Traceback (most recent call last): ... diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 84ef0cf45cc..c2b4537d596 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -23,10 +23,16 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2013 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013 Jeroen Demeyer +# 2013-2017 Volker Braun +# 2018 Julian Rüth +# 2018-2021 Sébastien Labbé +# 2020 Samuel Lelièvre +# 2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 15115a245b3..466ccf12181 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -10,9 +10,17 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2017 Jeroen Demeyer +# 2013-2019 John H. Palmieri +# 2014 Volker Braun +# 2014-2022 Frédéric Chapoton +# 2017 Erik M. Bray +# 2021 Sébastien Labbé +# 2021-2023 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # @@ -225,9 +233,30 @@ def _process_doc(self, doctests, doc, namespace, start): sigon = doctest.Example(sig_on_count_doc_doctest, "0\n", lineno=docstring.count("\n")) sigon.sage_source = sig_on_count_doc_doctest sigon.optional_tags = frozenset() + sigon.probed_tags = frozenset() dt.examples.append(sigon) doctests.append(dt) + @lazy_attribute + def file_optional_tags(self): + r""" + Return the set of tags that should apply to all doctests in this source. + + This default implementation just returns the empty set. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.sources import StringDocTestSource, PythonSource + sage: from sage.structure.dynamic_class import dynamic_class + sage: s = "'''\n sage: 2 + 2\n 4\n'''" + sage: PythonStringSource = dynamic_class('PythonStringSource', (StringDocTestSource, PythonSource)) + sage: PSS = PythonStringSource('', s, DocTestDefaults(), 'runtime') + sage: PSS.file_optional_tags + set() + """ + return set() + def _create_doctests(self, namespace, tab_okay=None): """ Creates a list of doctests defined in this source. @@ -263,7 +292,7 @@ def _create_doctests(self, namespace, tab_okay=None): sage: FDS.qualified_name = NestedName('sage.doctest.sources') sage: doctests, extras = FDS._create_doctests({}) sage: len(doctests) - 41 + 43 sage: extras['tab'] False sage: extras['line_number'] @@ -274,7 +303,9 @@ def _create_doctests(self, namespace, tab_okay=None): self._init() self.line_shift = 0 self.parser = SageDocTestParser(self.options.optional, - self.options.long) + self.options.long, + probed_tags=self.options.probe, + file_optional_tags=self.file_optional_tags) self.linking = False doctests = [] in_docstring = False @@ -686,6 +717,25 @@ def in_lib(self): return (self.options.force_lib or is_package_or_sage_namespace_package_dir(os.path.dirname(self.path))) + @lazy_attribute + def file_optional_tags(self): + """ + Return the set of tags that should apply to all doctests in this source. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.sources import FileDocTestSource + sage: from sage.env import SAGE_SRC + sage: import os + sage: filename = os.path.join(SAGE_SRC, 'sage', 'repl', 'user_globals.py') + sage: FDS = FileDocTestSource(filename, DocTestDefaults()) + sage: FDS.file_optional_tags + {'sage.modules': None} + """ + from .parsing import parse_file_optional_tags + return parse_file_optional_tags(self) + def create_doctests(self, namespace): r""" Return a list of doctests for this file. @@ -710,16 +760,16 @@ def create_doctests(self, namespace): sage: FDS = FileDocTestSource(filename,DocTestDefaults()) sage: doctests, extras = FDS.create_doctests(globals()) sage: len(doctests) - 41 + 43 sage: extras['tab'] False We give a self referential example:: - sage: doctests[18].name + sage: doctests[20].name 'sage.doctest.sources.FileDocTestSource.create_doctests' - sage: doctests[18].examples[10].source - 'doctests[Integer(18)].examples[Integer(10)].source\n' + sage: doctests[20].examples[10].source + 'doctests[Integer(20)].examples[Integer(10)].source\n' TESTS: @@ -728,11 +778,12 @@ def create_doctests(self, namespace): sage: import sys sage: bitness = '64' if sys.maxsize > (1 << 32) else '32' - sage: gp.get_precision() == 38 + sage: gp.get_precision() == 38 # needs sage.libs.pari False # 32-bit True # 64-bit - sage: ex = doctests[18].examples[13] - sage: (bitness == '64' and ex.want == 'True \n') or (bitness == '32' and ex.want == 'False \n') + sage: ex = doctests[20].examples[13] + sage: ((bitness == '64' and ex.want == 'True \n') # needs sage.libs.pari + ....: or (bitness == '32' and ex.want == 'False \n')) True We check that lines starting with a # aren't doctested:: @@ -758,9 +809,11 @@ def create_doctests(self, namespace): def _test_enough_doctests(self, check_extras=True, verbose=True): r""" This function checks to see that the doctests are not getting - unexpectedly skipped. It uses a different (and simpler) code - path than the doctest creation functions, so there are a few - files in Sage that it counts incorrectly. + unexpectedly skipped. + + It uses a different (and simpler) code path than the doctest + creation functions. In particular, it does not understand + file-level and block-level # optional / needs tags. INPUT: @@ -773,13 +826,14 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): TESTS:: + sage: # not tested (because the output will change when source files are changed) sage: from sage.doctest.control import DocTestDefaults sage: from sage.doctest.sources import FileDocTestSource sage: from sage.env import SAGE_SRC sage: cwd = os.getcwd() sage: os.chdir(SAGE_SRC) sage: import itertools - sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')): # long time + sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')): ....: path = os.path.relpath(path) ....: dirs.sort(); files.sort() ....: for F in files: diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index 60075a53578..aa64974fd72 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -253,11 +253,12 @@ is still alive, but it should be killed automatically after the ``die_timeout`` given above (10 seconds):: - sage: pid = int(open(F).read()) # long time - sage: time.sleep(2) # long time - sage: os.kill(pid, signal.SIGQUIT) # long time; 2 seconds passed => still alive - sage: time.sleep(8) # long time - sage: os.kill(pid, signal.SIGQUIT) # long time; 10 seconds passed => dead # random + sage: # long time + sage: pid = int(open(F).read()) + sage: time.sleep(2) + sage: os.kill(pid, signal.SIGQUIT) # 2 seconds passed => still alive + sage: time.sleep(8) + sage: os.kill(pid, signal.SIGQUIT) # 10 seconds passed => dead # random Traceback (most recent call last): ... ProcessLookupError: ... @@ -464,13 +465,12 @@ Running doctests ... Doctesting 1 file. sage -t --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 gap test not run 1 long test not run 1 not tested test not run 0 tests not run because we ran out of time - [1 test, ... s] + [2 tests, ... s] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -484,11 +484,10 @@ Running doctests ... Doctesting 1 file. sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run 0 tests not run because we ran out of time - [3 tests, ... s] + [4 tests, ... s] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -500,10 +499,9 @@ Running doctests ... Doctesting 1 file. sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run - 1 sage test not run + 2 sage tests not run 0 tests not run because we ran out of time [2 tests, ... s] ---------------------------------------------------------------------- diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index 029611497a3..7446373eae0 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -10,8 +10,11 @@ # **************************************************************************** # Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 Jeroen Demeyer +# 2014 Volker Braun +# 2017 Frédéric Chapoton # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 2fdde406202..97cc1c1df3e 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -6008,7 +6008,7 @@ def reduced_form(self, **kwds): sage: f = DynamicalSystem_projective([x^3 + x*y^2, y^3]) sage: m = matrix(QQ, 2, 2, [-201221, -1, 1, 0]) sage: f = f.conjugate(m) - sage: f.reduced_form(prec=50, smallest_coeffs=False) # needs 2 periodic + sage: f.reduced_form(prec=50, smallest_coeffs=False) # this needs 2 periodic Traceback (most recent call last): ... ValueError: accuracy of Newton's root not within tolerance(0.000066... > 1e-06), @@ -6026,7 +6026,7 @@ def reduced_form(self, **kwds): :: sage: PS. = ProjectiveSpace(ZZ, 1) - sage: f = DynamicalSystem_projective([x^2 + x*y, y^2]) # needs 3 periodic + sage: f = DynamicalSystem_projective([x^2 + x*y, y^2]) # this needs 3 periodic sage: m = matrix(QQ, 2, 2, [-221, -1, 1, 0]) sage: f = f.conjugate(m) sage: f.reduced_form(prec=200, smallest_coeffs=False) diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index 5deb0085a63..89a7b184a4c 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -232,7 +232,7 @@ def require(self): EXAMPLES:: sage: from sage.features.gap import GapPackage - sage: GapPackage("ve1EeThu").require() + sage: GapPackage("ve1EeThu").require() # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_ve1EeThu is not available. @@ -379,7 +379,7 @@ def hide(self): sage: from sage.features.graph_generators import Benzene sage: Benzene().hide() - sage: len(list(graphs.fusenes(2))) + sage: len(list(graphs.fusenes(2))) # needs sage.graphs Traceback (most recent call last): ... FeatureNotPresentError: benzene is not available. @@ -387,7 +387,7 @@ def hide(self): Use method `unhide` to make it available again. sage: Benzene().unhide() - sage: len(list(graphs.fusenes(2))) # optional benzene + sage: len(list(graphs.fusenes(2))) # optional - benzene, needs sage.graphs 1 """ self._hidden = True @@ -405,7 +405,7 @@ def unhide(self): sage: from sage.features.gap import GapPackage sage: Polycyclic = GapPackage("polycyclic", spkg="gap_packages") sage: Polycyclic.hide() - sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_polycyclic is not available. @@ -413,7 +413,7 @@ def unhide(self): Use method `unhide` to make it available again. sage: Polycyclic.unhide() - sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap Pcp-group with orders [ 0, 3, 4 ] """ self._hidden = False @@ -452,7 +452,7 @@ def __str__(self): EXAMPLES:: sage: from sage.features.gap import GapPackage - sage: GapPackage("gapZuHoh8Uu").require() # indirect doctest + sage: GapPackage("gapZuHoh8Uu").require() # indirect doctest # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_gapZuHoh8Uu is not available. @@ -485,7 +485,7 @@ class FeatureTestResult(): Explanatory messages might be available as ``reason`` and ``resolution``:: - sage: presence.reason + sage: presence.reason # needs sage.libs.gap '`TestPackageAvailability("NOT_A_PACKAGE")` evaluated to `fail` in GAP.' sage: bool(presence.resolution) False @@ -870,8 +870,10 @@ class CythonFeature(Feature): ....: ....: assert fabs(-1) == 1 ....: ''' - sage: fabs = CythonFeature("fabs", test_code=fabs_test_code, spkg="gcc", url="https://gnu.org", type="standard") - sage: fabs.is_present() + sage: fabs = CythonFeature("fabs", test_code=fabs_test_code, # needs sage.misc.cython + ....: spkg="gcc", url="https://gnu.org", + ....: type="standard") + sage: fabs.is_present() # needs sage.misc.cython FeatureTestResult('fabs', True) Test various failures:: @@ -922,7 +924,7 @@ def _is_present(self): sage: from sage.features import CythonFeature sage: empty = CythonFeature("empty", test_code="") - sage: empty.is_present() + sage: empty.is_present() # needs sage.misc.cython FeatureTestResult('empty', True) """ from sage.misc.temporary_file import tmp_filename @@ -935,6 +937,9 @@ def _is_present(self): pyx.write(self.test_code) try: from sage.misc.cython import cython_import + except ImportError: + return FeatureTestResult(self, False, reason="sage.misc.cython is not available") + try: cython_import(pyx.name, verbose=-1) except CCompilerError: return FeatureTestResult(self, False, reason="Failed to compile test code.") diff --git a/src/sage/features/all.py b/src/sage/features/all.py index 19eabe60126..599bc575dd7 100644 --- a/src/sage/features/all.py +++ b/src/sage/features/all.py @@ -3,7 +3,7 @@ """ # ***************************************************************************** -# Copyright (C) 2021 Matthias Koeppe +# Copyright (C) 2021-2023 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -11,6 +11,9 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +import itertools + + def all_features(): r""" Return an iterable of all features. @@ -34,3 +37,89 @@ def all_features(): else: if af != all_features: yield from af() + + +def module_feature(module_name): + r""" + Find a top-level :class:`Feature` that provides the Python module of the given ``module_name``. + + Only features known to :func:`all_features` are considered. + + INPUT: + + - ``module_name`` -- string + + OUTPUT: a :class:`Feature` or ``None``. + + EXAMPLES:: + + sage: from sage.features.all import module_feature + sage: module_feature('sage.combinat.tableau') # needs sage.combinat + Feature('sage.combinat') + sage: module_feature('sage.combinat.posets.poset') # needs sage.graphs + Feature('sage.graphs') + sage: module_feature('sage.schemes.toric.variety') # needs sage.geometry.polyhedron + Feature('sage.geometry.polyhedron') + sage: module_feature('scipy') # needs scipy + Feature('scipy') + sage: print(module_feature('sage.structure.element')) + None + sage: print(module_feature('sage.does_not_exist')) + None + """ + longest_prefix = '' + longest_prefix_feature = None + for feature in all_features(): + for joined in itertools.chain([feature], feature.joined_features()): + if joined.name == module_name: + return feature + if (joined.name + '.').startswith(longest_prefix): + if (module_name + '.').startswith(joined.name + '.'): + longest_prefix = feature.name + '.' + longest_prefix_feature = feature + return longest_prefix_feature + + +def name_feature(name, toplevel=None): + r""" + Find a top-level :class:`Feature` that provides the top-level ``name``. + + Only features known to :func:`all_features` are considered. + + INPUT: + + - ``name`` -- string + + - ``toplevel`` -- a module or other namespace + + OUTPUT: a :class:`Feature` or ``None``. + + EXAMPLES:: + + sage: from sage.features.all import name_feature + sage: name_feature('QuadraticField') # needs sage.rings.number_field + Feature('sage.rings.number_field') + sage: name_feature('line') # needs sage.plot + Feature('sage.plot') + sage: print(name_feature('ZZ')) + None + sage: print(name_feature('does_not_exist')) + None + """ + if toplevel is None: + try: + import sage.all as toplevel + except ImportError: + return None + try: + obj = getattr(toplevel, name) + except AttributeError: + return None + + from sage.misc.dev_tools import find_object_modules + + for module, names in find_object_modules(obj).items(): + if name in names and (feature := module_feature(module)): + return feature + + return None diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py index dad0e975804..17aabcdca54 100644 --- a/src/sage/features/gap.py +++ b/src/sage/features/gap.py @@ -13,12 +13,16 @@ from . import Feature, FeatureTestResult, PythonModule from .join_feature import JoinFeature - +from .sagemath import sage__libs__gap class GapPackage(Feature): r""" A :class:`~sage.features.Feature` describing the presence of a GAP package. + .. SEEALSO:: + + :class:`Feature sage.libs.gap <~sage.features.sagemath.sage__libs__gap>` + EXAMPLES:: sage: from sage.features.gap import GapPackage @@ -48,7 +52,11 @@ def _is_present(self): sage: GapPackage("grape", spkg="gap_packages")._is_present() # optional - gap_packages FeatureTestResult('gap_package_grape', True) """ - from sage.libs.gap.libgap import libgap + try: + from sage.libs.gap.libgap import libgap + except ImportError: + return FeatureTestResult(self, False, + reason="sage.libs.gap is not available") command = 'TestPackageAvailability("{package}")'.format(package=self.package) presence = libgap.eval(command) if presence: @@ -59,32 +67,5 @@ def _is_present(self): reason="`{command}` evaluated to `{presence}` in GAP.".format(command=command, presence=presence)) -class sage__libs__gap(JoinFeature): - r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.gap` - (the library interface to :ref:`GAP `) and :mod:`sage.interfaces.gap` (the pexpect - interface to GAP). By design, we do not distinguish between these two, in order - to facilitate the conversion of code from the pexpect interface to the library - interface. - - EXAMPLES:: - - sage: from sage.features.gap import sage__libs__gap - sage: sage__libs__gap().is_present() # optional - sage.libs.gap - FeatureTestResult('sage.libs.gap', True) - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.gap import sage__libs__gap - sage: isinstance(sage__libs__gap(), sage__libs__gap) - True - """ - JoinFeature.__init__(self, 'sage.libs.gap', - [PythonModule('sage.libs.gap.libgap'), - PythonModule('sage.interfaces.gap')]) - - def all_features(): - return [sage__libs__gap()] + return [] diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py index 0060e6deb03..72f703da15f 100644 --- a/src/sage/features/kenzo.py +++ b/src/sage/features/kenzo.py @@ -48,7 +48,10 @@ def _is_present(self): sage: Kenzo()._is_present() # optional - kenzo FeatureTestResult('kenzo', True) """ - from sage.libs.ecl import ecl_eval + try: + from sage.libs.ecl import ecl_eval + except ImportError: + return FeatureTestResult(self, False, reason="sage.libs.ecl is not available") # Redirection of ECL and Maxima stdout to /dev/null # This is also done in the Maxima library, but we # also do it here for redundancy. diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 580be376237..f7c3c0749ee 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -1,10 +1,37 @@ r""" Features for testing the presence of Python modules in the Sage library + +All of these features are present in a monolithic installation of the Sage library, +such as the one made by the SageMath distribution. + +The features are defined for the purpose of separately testing modularized +distributions such as :ref:`sagemath-categories ` +and :ref:`sagemath-repl `. + +Often, doctests in a module of the Sage library illustrate the +interplay with a range of different objects; this is a form of integration testing. +These objects may come from modules shipped in +other distributions. For example, :mod:`sage.structure.element` +(shipped by :ref:`sagemath-objects `, +one of the most fundamental distributions) contains the +doctest:: + + sage: G = SymmetricGroup(4) # needs sage.groups + sage: g = G([2, 3, 4, 1]) # needs sage.groups + sage: g.powers(4) # needs sage.groups + [(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)] + +This test cannot pass when the distribution :ref:`sagemath-objects ` +is tested separately (in a virtual environment): In this situation, +:class:`SymmetricGroup` is not defined anywhere (and thus not present +in the top-level namespace). +Hence, we conditionalize this doctest on the presence of the feature +:class:`sage.groups `. """ # ***************************************************************************** -# Copyright (C) 2021 Matthias Koeppe -# 2021 Kwankyu Lee +# Copyright (C) 2021-2023 Matthias Koeppe +# 2021 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -14,18 +41,26 @@ from . import PythonModule, StaticFile from .join_feature import JoinFeature -from .singular import sage__libs__singular class sagemath_doc_html(StaticFile): r""" - A :class:`Feature` which describes the presence of the documentation + A :class:`~sage.features.Feature` which describes the presence of the documentation of the Sage library in HTML format. - EXAMPLES:: + Developers often use ``make build`` instead of ``make`` to avoid the + long time it takes to compile the documentation. Although commands + such as ``make ptest`` build the documentation before testing, other + test commands such as ``make ptestlong-nodoc`` or ``./sage -t --all`` + do not. + + All doctests that refer to the built documentation need to be marked + ``# needs sagemath_doc_html``. + + TESTS:: sage: from sage.features.sagemath import sagemath_doc_html - sage: sagemath_doc_html().is_present() # optional - sagemath_doc_html + sage: sagemath_doc_html().is_present() # needs sagemath_doc_html FeatureTestResult('sagemath_doc_html', True) """ def __init__(self): @@ -48,10 +83,54 @@ class sage__combinat(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.combinat`. - EXAMPLES:: + EXAMPLES: + + Python modules that provide elementary combinatorial objects such as :mod:`sage.combinat.subset`, + :mod:`sage.combinat.composition`, :mod:`sage.combinat.permutation` are always available; + there is no need for an ``# optional/needs`` tag:: + + sage: Permutation([1,2,3]).is_even() + True + sage: Permutation([6,1,4,5,2,3]).bruhat_inversions() + [[0, 1], [0, 2], [0, 3], [2, 4], [2, 5], [3, 4], [3, 5]] + + Use ``# needs sage.combinat`` for doctests that use any other Python modules + from :mod:`sage.combinat`, for example :mod:`sage.combinat.tableau_tuple`:: + + sage: TableauTuple([[[7,8,9]],[],[[1,2,3],[4,5],[6]]]).shape() # needs sage.combinat + ([3], [], [3, 2, 1]) + + Doctests that use Python modules from :mod:`sage.combinat` that involve trees, + graphs, hypergraphs, posets, quivers, combinatorial designs, + finite state machines etc. should be marked ``# needs sage.combinat sage.graphs``:: + + sage: L = Poset({0: [1], 1: [2], 2:[3], 3:[4]}) # needs sage.combinat sage.graphs + sage: L.is_chain() # needs sage.combinat sage.graphs + True + + Doctests that use combinatorial modules/algebras, or root systems should use the tag + ``# needs sage.combinat sage.modules``:: + + sage: # needs sage.combinat sage.modules + sage: A = SchurAlgebra(QQ, 2, 3) + sage: a = A.an_element(); a + 2*S((1, 1, 1), (1, 1, 1)) + 2*S((1, 1, 1), (1, 1, 2)) + + 3*S((1, 1, 1), (1, 2, 2)) + sage: L = RootSystem(['A',3,1]).root_lattice() + sage: PIR = L.positive_imaginary_roots(); PIR + Positive imaginary roots of type ['A', 3, 1] + + Doctests that use lattices, semilattices, or Dynkin diagrams should use the tag + ``# needs sage.combinat sage.graphs sage.modules``:: + + sage: L = LatticePoset({0: [1,2], 1: [3], 2: [3,4], 3: [5], 4: [5]}) # needs sage.combinat sage.graphs sage.modules + sage: L.meet_irreducibles() # needs sage.combinat sage.graphs sage.modules + [1, 3, 4] + + TESTS:: sage: from sage.features.sagemath import sage__combinat - sage: sage__combinat().is_present() # optional - sage.combinat + sage: sage__combinat().is_present() # needs sage.combinat FeatureTestResult('sage.combinat', True) """ def __init__(self): @@ -67,18 +146,36 @@ def __init__(self): # Some modules providing basic combinatorics are already included in sagemath-categories. # Hence, we test a Python module within the package. JoinFeature.__init__(self, 'sage.combinat', - [PythonModule('sage.combinat.tableau')], + [PythonModule('sage.combinat'), # namespace package + PythonModule('sage.combinat.tableau'), # representative + ], spkg='sagemath_combinat', type="standard") -class sage__geometry__polyhedron(PythonModule): +class sage__geometry__polyhedron(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.geometry.polyhedron`. - EXAMPLES:: + EXAMPLES: + + Doctests that use polyhedra, cones, geometric complexes, triangulations, etc. should use + the tag ``# needs sage.geometry.polyhedron``:: + + sage: co = polytopes.truncated_tetrahedron() # needs sage.geometry.polyhedron + sage: co.volume() # needs sage.geometry.polyhedron + 184/3 + + Some constructions of polyhedra require additional tags:: + + sage: # needs sage.combinat sage.geometry.polyhedron sage.rings.number_field + sage: perm_a3_reg_nf = polytopes.generalized_permutahedron( + ....: ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices + + TESTS:: sage: from sage.features.sagemath import sage__geometry__polyhedron - sage: sage__geometry__polyhedron().is_present() # optional - sage.geometry.polyhedron + sage: sage__geometry__polyhedron().is_present() # needs sage.geometry.polyhedron FeatureTestResult('sage.geometry.polyhedron', True) """ @@ -90,18 +187,69 @@ def __init__(self): sage: isinstance(sage__geometry__polyhedron(), sage__geometry__polyhedron) True """ - PythonModule.__init__(self, 'sage.geometry.polyhedron', - spkg='sagemath_polyhedra', type="standard") + JoinFeature.__init__(self, 'sage.geometry.polyhedron', + [PythonModule('sage.geometry'), # namespace package + PythonModule('sage.geometry.polyhedron'), # representative + PythonModule('sage.schemes.toric'), # namespace package + PythonModule('sage.schemes.toric.variety'), # representative + ], + spkg='sagemath_polyhedra', type="standard") class sage__graphs(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.graphs`. - EXAMPLES:: + EXAMPLES: + + Doctests that use anything from :mod:`sage.graphs` (:class:`Graph`, :class:`DiGraph`, ...) + should be marked ``# needs sage.graphs``. The same applies to any doctest that + uses a :class:`~sage.combinat.posets.posets.Poset`, cluster algebra quiver, finite + state machines, abelian sandpiles, or Dynkin diagrams:: + + sage: g = graphs.PetersenGraph() # needs sage.graphs + sage: r, s = g.is_weakly_chordal(certificate=True); r # needs sage.graphs + False + + Also any use of tree classes defined in :mod:`sage.combinat` (:class:`BinaryTree`, + :class:`RootedTree`, ...) in doctests should be marked the same. + + By way of generalization, any use of :class:`SimplicialComplex` or other abstract complexes from + :mod:`sage.topology`, hypergraphs, and combinatorial designs, should be marked + ``# needs sage.graphs`` as well:: + + sage: X = SimplicialComplex([[0,1,2], [1,2,3]]) # needs sage.graphs + sage: X.link(Simplex([0])) # needs sage.graphs + Simplicial complex with vertex set (1, 2) and facets {(1, 2)} + + sage: IncidenceStructure([[1,2,3],[1,4]]).degrees(2) # needs sage.graphs + {(1, 2): 1, (1, 3): 1, (1, 4): 1, (2, 3): 1, (2, 4): 0, (3, 4): 0} + + On the other hand, matroids are not implemented as posets in Sage but are instead + closely tied to linear algebra over fields; hence use ``# needs sage.modules`` instead:: + + sage: # needs sage.modules + sage: M = Matroid(Matrix(QQ, [[1, 0, 0, 0, 1, 1, 1], + ....: [0, 1, 0, 1, 0, 1, 1], + ....: [0, 0, 1, 1, 1, 0, 1]])) + sage: N = M / [2] \ [3, 4] + sage: sorted(N.groundset()) + [0, 1, 5, 6] + + However, many constructions (and some methods) of matroids do involve graphs:: + + sage: # needs sage.modules + sage: W = matroids.Wheel(3) # despite the name, not created via graphs + sage: W.is_isomorphic(N) # goes through a graph isomorphism test # needs sage.graphs + False + sage: K4 = matroids.CompleteGraphic(4) # this one is created via graphs # needs sage.graphs + sage: K4.is_isomorphic(W) # needs sage.graphs + True + + TESTS:: sage: from sage.features.sagemath import sage__graphs - sage: sage__graphs().is_present() # optional - sage.graphs + sage: sage__graphs().is_present() # needs sage.graphs FeatureTestResult('sage.graphs', True) """ def __init__(self): @@ -113,54 +261,76 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.graphs', - [PythonModule('sage.graphs.graph')], + # These lists of modules are an (incomplete) duplication + # of information in the distribution's MANIFEST. + # But at least as long as the monolithic Sage library is + # around, we need this information here for use by + # sage-fixdoctests. + [PythonModule('sage.graphs'), # namespace package + PythonModule('sage.graphs.graph'), # representative + PythonModule('sage.combinat.designs'), # namespace package + PythonModule('sage.combinat.designs.block_design'), # representative + PythonModule('sage.combinat.posets'), # namespace package + PythonModule('sage.combinat.posets.posets'), # representative + PythonModule('sage.topology'), # namespace package + PythonModule('sage.topology.simplicial_complex'), # representative + ], spkg='sagemath_graphs', type="standard") -class sage__modular(JoinFeature): +class sage__groups(JoinFeature): r""" - A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modular`. + A :class:`~sage.features.Feature` describing the presence of ``sage.groups``. - EXAMPLES:: + EXAMPLES: - sage: from sage.features.sagemath import sage__modular - sage: sage__modular().is_present() # optional - sage.modular - FeatureTestResult('sage.modular', True) + Permutations and sets of permutations are always available, but permutation groups are + implemented in Sage using the :ref:`GAP ` system and require the tag + ``# needs sage.groups``:: + + sage: p = Permutation([2,1,4,3]) + sage: p.to_permutation_group_element() # needs sage.groups + (1,2)(3,4) + + TESTS:: + + sage: from sage.features.sagemath import sage__groups + sage: sage__groups().is_present() # needs sage.groups + FeatureTestResult('sage.groups', True) """ def __init__(self): r""" TESTS:: - sage: from sage.features.sagemath import sage__modular - sage: isinstance(sage__modular(), sage__modular) + sage: from sage.features.sagemath import sage__groups + sage: isinstance(sage__groups(), sage__groups) True """ - JoinFeature.__init__(self, 'sage.modular', - [PythonModule('sage.modular.modform.eisenstein_submodule')], - spkg='sagemath_schemes', type='standard') + JoinFeature.__init__(self, 'sage.groups', + [PythonModule('sage.groups.perm_gps.permgroup')], + spkg='sagemath_groups', type='standard') -class sage__groups(JoinFeature): +class sage__libs__ecl(PythonModule): r""" - A :class:`sage.features.Feature` describing the presence of ``sage.groups``. + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.ecl`. EXAMPLES:: - sage: from sage.features.sagemath import sage__groups - sage: sage__groups().is_present() # optional - sage.groups - FeatureTestResult('sage.groups', True) + sage: from sage.features.sagemath import sage__libs__ecl + sage: sage__libs__ecl().is_present() # optional - sage.libs.ecl + FeatureTestResult('sage.libs.ecl', True) """ + def __init__(self): r""" TESTS:: - sage: from sage.features.sagemath import sage__groups - sage: isinstance(sage__groups(), sage__groups) + sage: from sage.features.sagemath import sage__libs__ecl + sage: isinstance(sage__libs__ecl(), sage__libs__ecl) True """ - JoinFeature.__init__(self, 'sage.groups', - [PythonModule('sage.groups.perm_gps.permgroup')], - spkg='sagemath_groups', type='standard') + PythonModule.__init__(self, 'sage.libs.ecl') class sage__libs__flint(JoinFeature): @@ -168,10 +338,13 @@ class sage__libs__flint(JoinFeature): A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.flint` and other modules depending on FLINT and arb. - EXAMPLES:: + 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__flint - sage: sage__libs__flint().is_present() # optional - sage.libs.flint + sage: sage__libs__flint().is_present() # needs sage.libs.flint FeatureTestResult('sage.libs.flint', True) """ def __init__(self): @@ -188,15 +361,49 @@ def __init__(self): spkg='sagemath_flint', type='standard') +class sage__libs__gap(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.gap` + (the library interface to :ref:`GAP `) and :mod:`sage.interfaces.gap` (the pexpect + interface to GAP). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + .. SEEALSO:: + + :class:`Features for GAP packages <~sage.features.gap.GapPackage>` + + TESTS:: + + sage: from sage.features.gap import sage__libs__gap + sage: sage__libs__gap().is_present() # needs sage.libs.gap + FeatureTestResult('sage.libs.gap', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.gap import sage__libs__gap + sage: isinstance(sage__libs__gap(), sage__libs__gap) + True + """ + JoinFeature.__init__(self, 'sage.libs.gap', + [PythonModule('sage.libs.gap.libgap'), + PythonModule('sage.interfaces.gap')]) + + class sage__libs__ntl(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.ntl` and other modules depending on NTL and arb. - EXAMPLES:: + 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__ntl - sage: sage__libs__ntl().is_present() # optional - sage.libs.ntl + sage: sage__libs__ntl().is_present() # needs sage.libs.ntl FeatureTestResult('sage.libs.ntl', True) """ def __init__(self): @@ -214,12 +421,26 @@ def __init__(self): class sage__libs__pari(JoinFeature): r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.pari`. + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.pari`. + + SageMath uses the :ref:`PARI ` library (via :ref:`cypari2 `) for numerous purposes. + Doctests that involves such features should be marked ``# needs sage.libs.pari``. + + In addition to the modularization purposes that this tag serves, it also provides attribution + to the upstream project. EXAMPLES:: + sage: R. = QQ[] + sage: S. = R[] + sage: f = x^2 + a; g = x^3 + a + sage: r = f.resultant(g); r # needs sage.libs.pari + a^3 + a^2 + + TESTS:: + sage: from sage.features.sagemath import sage__libs__pari - sage: sage__libs__pari().is_present() # optional - sage.libs.pari + sage: sage__libs__pari().is_present() # needs sage.libs.pari FeatureTestResult('sage.libs.pari', True) """ def __init__(self): @@ -235,14 +456,83 @@ def __init__(self): spkg='sagemath_pari', type='standard') +class sage__libs__singular(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.singular` + (the library interface to Singular) and :mod:`sage.interfaces.singular` (the pexpect + interface to Singular). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + .. SEEALSO:: + + :class:`Feature singular <~sage.features.singular.Singular>` + + TESTS:: + + sage: from sage.features.singular import sage__libs__singular + sage: sage__libs__singular().is_present() # needs sage.libs.singular + FeatureTestResult('sage.libs.singular', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.singular import sage__libs__singular + sage: isinstance(sage__libs__singular(), sage__libs__singular) + True + """ + JoinFeature.__init__(self, 'sage.libs.singular', + [PythonModule('sage.libs.singular.singular'), + PythonModule('sage.interfaces.singular')]) + + +class sage__modular(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modular`. + + TESTS:: + + sage: from sage.features.sagemath import sage__modular + sage: sage__modular().is_present() # needs sage.modular + FeatureTestResult('sage.modular', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__modular + sage: isinstance(sage__modular(), sage__modular) + True + """ + JoinFeature.__init__(self, 'sage.modular', + [PythonModule('sage.modular.modform.eisenstein_submodule')], + spkg='sagemath_schemes', type='standard') + + class sage__modules(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modules`. - EXAMPLES:: + EXAMPLES: + + All uses of implementations of vector spaces / free modules in SageMath, whether + :class:`sage.modules.free_module.FreeModule`, + :class:`sage.combinat.free_module.CombinatorialFreeModule`, + :class:`sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`, or + additive abelian groups, should be marked ``# needs sage.modules``. + + The same holds for matrices, tensors, algebras, quadratic forms, + point lattices, root systems, matrix/affine/Weyl/Coxeter groups, matroids, + and ring derivations. + + Likewise, all uses of :mod:`sage.coding`, :mod:`sage.crypto`, and :mod:`sage.homology` + in doctests should be marked ``# needs sage.modules``. + + TESTS:: sage: from sage.features.sagemath import sage__modules - sage: sage__modules().is_present() # optional - sage.modules + sage: sage__modules().is_present() # needs sage.modules FeatureTestResult('sage.modules', True) """ def __init__(self): @@ -254,18 +544,57 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.modules', - [PythonModule('sage.modules.free_module')], + [PythonModule('sage.modules'), # namespace package + PythonModule('sage.modules.free_module'), # representative + PythonModule('sage.matrix'), # namespace package + PythonModule('sage.matrix.matrix2'), # representative + PythonModule('sage.combinat.free_module'), + PythonModule('sage.quadratic_forms'), # namespace package + PythonModule('sage.quadratic_forms.quadratic_form'), # representative + PythonModule('sage.groups.additive_abelian'), # namespace package + PythonModule('sage.groups.additive_abelian.qmodnz'), # representative + PythonModule('sage.groups.affine_gps'), # namespace package + PythonModule('sage.groups.affine_gps.affine_group'), # representative + PythonModule('sage.groups.matrix_gps'), # namespace package + PythonModule('sage.groups.matrix_gps.named_group'), # representative + PythonModule('sage.homology'), # namespace package + PythonModule('sage.homology.chain_complex'), # representative + PythonModule('sage.matroids'), # namespace package + PythonModule('sage.matroids.matroid'), # representative + ], spkg='sagemath_modules', type='standard') +class sage__numerical__mip(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.numerical.mip`. + + TESTS:: + + sage: from sage.features.sagemath import sage__numerical__mip + sage: sage__numerical__mip().is_present() # needs sage.numerical.mip + FeatureTestResult('sage.numerical.mip', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__numerical__mip + sage: isinstance(sage__numerical__mip(), sage__numerical__mip) + True + """ + PythonModule.__init__(self, 'sage.numerical.mip', + spkg='sagemath_polyhedra') + + class sage__plot(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.plot`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__plot - sage: sage__plot().is_present() # optional - sage.plot + sage: sage__plot().is_present() # needs sage.plot FeatureTestResult('sage.plot', True) """ def __init__(self): @@ -278,18 +607,40 @@ def __init__(self): """ JoinFeature.__init__(self, 'sage.plot', [PythonModule('sage.plot.plot')], - spkg='sagemath_symbolics', type='standard') + spkg='sagemath_plot', type='standard') + + +class sage__rings__complex_double(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.complex_double`. + + TESTS:: + + sage: from sage.features.sagemath import sage__rings__complex_double + sage: sage__rings__complex_double().is_present() # needs sage.rings.complex_double + FeatureTestResult('sage.rings.complex_double', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__rings__complex_double + sage: isinstance(sage__rings__complex_double(), sage__rings__complex_double) + True + """ + PythonModule.__init__(self, 'sage.rings.complex_double', + spkg='sagemath_modules', type='standard') class sage__rings__finite_rings(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.finite_rings`; - specifically, the element implementations using PARI. + specifically, the element implementations using the :ref:`PARI ` library. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__finite_rings - sage: sage__rings__finite_rings().is_present() # optional - sage.rings.finite_rings + sage: sage__rings__finite_rings().is_present() # needs sage.rings.finite_rings FeatureTestResult('sage.rings.finite_rings', True) """ def __init__(self): @@ -301,7 +652,8 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.rings.finite_rings', - [PythonModule('sage.rings.finite_rings.element_pari_ffelt')], + [PythonModule('sage.rings.finite_rings.element_pari_ffelt'), + PythonModule('sage.rings.algebraic_closure_finite_field')], type='standard') @@ -309,10 +661,30 @@ class sage__rings__function_field(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.function_field`. - EXAMPLES:: + EXAMPLES: + + Rational function fields are always available:: + + sage: K. = FunctionField(QQ) + sage: K.maximal_order() + Maximal order of Rational function field in x over Rational Field + + Use the tag ``# needs sage.rings.function_field`` whenever extensions + of function fields (by adjoining a root of a univariate polynomial) come into play:: + + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L # needs sage.rings.function_field + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + + Such extensions of function fields are implemented using Gröbner bases of polynomial rings; + Sage makes essential use of the :ref:`Singular ` system for this. + (It is not necessary to use the tag ``# needs sage.libs.singular``; it is + implied by ``# needs sage.rings.function_field``.) + + TESTS:: sage: from sage.features.sagemath import sage__rings__function_field - sage: sage__rings__function_field().is_present() # optional - sage.rings.function_field + sage: sage__rings__function_field().is_present() # needs sage.rings.function_field FeatureTestResult('sage.rings.function_field', True) """ def __init__(self): @@ -333,10 +705,52 @@ class sage__rings__number_field(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.number_field`. - EXAMPLES:: + Number fields are implemented in Sage using a complicated mixture of various libraries, + including :ref:`arb `, :ref:`FLINT `, :ref:`GAP `, + :ref:`MPFI `, :ref:`NTL `, and :ref:`PARI `. + + EXAMPLES: + + Rational numbers are, of course, always available:: + + sage: QQ in NumberFields() + True + + Doctests that construct algebraic number fields should be marked ``# needs sage.rings.number_field``:: + + sage: # needs sage.rings.number_field + sage: K. = NumberField(x^3 - 2) + sage: L. = K.extension(x^3 - 3) + sage: S. = L.extension(x^2 - 2); S + Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field + + sage: # needs sage.rings.number_field + sage: K. = CyclotomicField(15) + sage: CC(zeta) + 0.913545457642601 + 0.406736643075800*I + + Doctests that make use of the algebraic field ``QQbar``, the algebraic real field ``AA``, + or the universal cyclotomic field should be marked likewise:: + + sage: # needs sage.rings.number_field + sage: AA(-1)^(1/3) + -1 + sage: QQbar(-1)^(1/3) + 0.500000000000000? + 0.866025403784439?*I + + sage: # needs sage.rings.number_field + sage: UCF = UniversalCyclotomicField(); UCF + Universal Cyclotomic Field + sage: E = UCF.gen + sage: f = E(2) + E(3); f + 2*E(3) + E(3)^2 + sage: f.galois_conjugates() + [2*E(3) + E(3)^2, E(3) + 2*E(3)^2] + + TESTS:: sage: from sage.features.sagemath import sage__rings__number_field - sage: sage__rings__number_field().is_present() # optional - sage.rings.number_field + sage: sage__rings__number_field().is_present() # needs sage.rings.number_field FeatureTestResult('sage.rings.number_field', True) """ def __init__(self): @@ -348,18 +762,20 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.rings.number_field', - [PythonModule('sage.rings.number_field.number_field_element')], + [PythonModule('sage.rings.number_field.number_field_element'), + PythonModule('sage.rings.universal_cyclotomic_field'), + PythonModule('sage.rings.qqbar')], type='standard') class sage__rings__padics(JoinFeature): r""" - A :class:`sage.features.Feature` describing the presence of ``sage.rings.padics``. + A :class:`~sage.features.Feature` describing the presence of ``sage.rings.padics``. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__padics - sage: sage__rings__padics().is_present() # optional - sage.rings.padics + sage: sage__rings__padics().is_present() # needs sage.rings.padics FeatureTestResult('sage.rings.padics', True) """ def __init__(self): @@ -379,10 +795,10 @@ class sage__rings__polynomial__pbori(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.rings.polynomial.pbori`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__polynomial__pbori - sage: sage__rings__polynomial__pbori().is_present() # optional - sage.rings.polynomial.pbori + sage: sage__rings__polynomial__pbori().is_present() # needs sage.rings.polynomial.pbori FeatureTestResult('sage.rings.polynomial.pbori', True) """ def __init__(self): @@ -402,10 +818,20 @@ class sage__rings__real_double(PythonModule): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.real_double`. - EXAMPLES:: + EXAMPLES: + + The Real Double Field is basically always available, and no ``# optional/needs`` tag is needed:: + + sage: RDF.characteristic() + 0 + + The feature exists for use in doctests of Python modules that are shipped by the + most fundamental distributions. + + TESTS:: sage: from sage.features.sagemath import sage__rings__real_double - sage: sage__rings__real_double().is_present() # optional - sage.rings.real_double + sage: sage__rings__real_double().is_present() # needs sage.rings.real_double FeatureTestResult('sage.rings.real_double', True) """ def __init__(self): @@ -416,17 +842,17 @@ def __init__(self): sage: isinstance(sage__rings__real_double(), sage__rings__real_double) True """ - PythonModule.__init__(self, 'sage.rings.real_double') + PythonModule.__init__(self, 'sage.rings.real_double', type='standard') -class sage__rings__real_mpfr(PythonModule): +class sage__rings__real_mpfr(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.real_mpfr`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__real_mpfr - sage: sage__rings__real_mpfr().is_present() # optional - sage.rings.real_mpfr + sage: sage__rings__real_mpfr().is_present() # needs sage.rings.real_mpfr FeatureTestResult('sage.rings.real_mpfr', True) """ def __init__(self): @@ -437,18 +863,44 @@ def __init__(self): sage: isinstance(sage__rings__real_mpfr(), sage__rings__real_mpfr) True """ - PythonModule.__init__(self, 'sage.rings.real_mpfr', - spkg='sagemath_modules') + JoinFeature.__init__(self, 'sage.rings.real_mpfr', + [PythonModule('sage.rings.real_mpfr'), + PythonModule('sage.rings.complex_mpfr'), + ], + spkg='sagemath_modules', type='standard') + + +class sage__sat(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.sat`. + + TESTS:: + + sage: from sage.features.sagemath import sage__sat + sage: sage__sat().is_present() # needs sage.sat + FeatureTestResult('sage.sat', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__sat + sage: isinstance(sage__sat(), sage__sat) + True + """ + JoinFeature.__init__(self, 'sage.sat', + [PythonModule('sage.sat.expression')], + spkg='sagemath_combinat', type='standard') class sage__schemes(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.schemes`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__schemes - sage: sage__schemes().is_present() # optional - sage.schemes + sage: sage__schemes().is_present() # needs sage.schemes FeatureTestResult('sage.schemes', True) """ def __init__(self): @@ -461,17 +913,29 @@ def __init__(self): """ JoinFeature.__init__(self, 'sage.schemes', [PythonModule('sage.schemes.elliptic_curves.ell_generic')], - spkg="sagemath_schemes") + spkg="sagemath_schemes", type='standard') class sage__symbolic(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.symbolic`. - EXAMPLES:: + EXAMPLES: + + The symbolics subsystem of Sage will be provided by the distribution + sagemath-symbolics, in preparation at :issue:`35095`. If it is not installed, + Sage will be able to provide installation advice:: + + sage: from sage.features.sagemath import sage__symbolic + sage: print(sage__symbolic().resolution()) # optional - sage_spkg, not tested + ...To install sagemath_symbolics...you can try to run... + pip install sagemath-symbolics + ... + + TESTS:: sage: from sage.features.sagemath import sage__symbolic - sage: sage__symbolic().is_present() # optional - sage.symbolic + sage: sage__symbolic().is_present() # needs sage.symbolic FeatureTestResult('sage.symbolic', True) """ def __init__(self): @@ -483,8 +947,32 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.symbolic', - [PythonModule('sage.symbolic.expression')], - spkg="sagemath_symbolics") + [PythonModule('sage.symbolic.expression'), + PythonModule('sage.manifolds'), + PythonModule('sage.calculus.calculus'), + PythonModule('sage.calculus.desolvers'), + PythonModule('sage.calculus.predefined'), + PythonModule('sage.calculus.tests'), + PythonModule('sage.calculus.var'), + PythonModule('sage.geometry.riemannian_manifolds'), + PythonModule('sage.geometry.hyperbolic_space'), + PythonModule('sage.dynamics.complex_dynamics'), + PythonModule('sage.libs.pynac'), + PythonModule('sage.libs.ecl'), + PythonModule('sage.interfaces.fricas'), + PythonModule('sage.interfaces.giac'), + PythonModule('sage.interfaces.magma'), + PythonModule('sage.interfaces.magma_free'), + PythonModule('sage.interfaces.maple'), + PythonModule('sage.interfaces.mathematica'), + PythonModule('sage.interfaces.mathics'), + PythonModule('sage.interfaces.maxima'), + PythonModule('sage.interfaces.maxima_abstract'), + PythonModule('sage.interfaces.maxima_lib'), + PythonModule('sage.interfaces.qepcad'), + PythonModule('sage.interfaces.sympy'), + PythonModule('sage.interfaces.sympy_wrapper'), + ], spkg='sagemath_symbolics', type='standard') def all_features(): @@ -514,12 +1002,17 @@ def all_features(): sage__geometry__polyhedron(), sage__graphs(), sage__groups(), + sage__libs__ecl(), sage__libs__flint(), + sage__libs__gap(), sage__libs__ntl(), sage__libs__pari(), + sage__libs__singular(), sage__modular(), sage__modules(), + sage__numerical__mip(), sage__plot(), + sage__rings__complex_double(), sage__rings__finite_rings(), sage__rings__function_field(), sage__rings__number_field(), @@ -527,5 +1020,6 @@ def all_features(): sage__rings__polynomial__pbori(), sage__rings__real_double(), sage__rings__real_mpfr(), + sage__sat(), sage__schemes(), sage__symbolic()] diff --git a/src/sage/features/singular.py b/src/sage/features/singular.py index 64a24320044..fce89a8e91c 100644 --- a/src/sage/features/singular.py +++ b/src/sage/features/singular.py @@ -13,6 +13,7 @@ from . import Executable, PythonModule from .join_feature import JoinFeature +from .sagemath import sage__libs__singular from sage.env import SINGULAR_BIN @@ -20,10 +21,14 @@ class Singular(Executable): r""" A :class:`~sage.features.Feature` describing the presence of the :ref:`singular ` executable. + .. SEEALSO:: + + :class:`Feature sage.libs.singular <~sage.features.sagemath.sage__libs__singular>` + EXAMPLES:: sage: from sage.features.singular import Singular - sage: Singular().is_present() + sage: Singular().is_present() # needs singular FeatureTestResult('singular', True) """ def __init__(self): @@ -38,33 +43,5 @@ def __init__(self): spkg='singular', type='standard') -class sage__libs__singular(JoinFeature): - r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.singular` - (the library interface to Singular) and :mod:`sage.interfaces.singular` (the pexpect - interface to Singular). By design, we do not distinguish between these two, in order - to facilitate the conversion of code from the pexpect interface to the library - interface. - - EXAMPLES:: - - sage: from sage.features.singular import sage__libs__singular - sage: sage__libs__singular().is_present() # optional - sage.libs.singular - FeatureTestResult('sage.libs.singular', True) - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.singular import sage__libs__singular - sage: isinstance(sage__libs__singular(), sage__libs__singular) - True - """ - JoinFeature.__init__(self, 'sage.libs.singular', - [PythonModule('sage.libs.singular.singular'), - PythonModule('sage.interfaces.singular')]) - - def all_features(): - return [Singular(), - sage__libs__singular()] + return [Singular()] diff --git a/src/sage/geometry/polyhedron/base4.py b/src/sage/geometry/polyhedron/base4.py index 6b309553123..ae092615ac5 100644 --- a/src/sage/geometry/polyhedron/base4.py +++ b/src/sage/geometry/polyhedron/base4.py @@ -1,4 +1,4 @@ -# sage.doctest: optional - sage.graphs +# sage.doctest: needs sage.graphs r""" Base class for polyhedra: Graph-theoretic methods @@ -353,7 +353,7 @@ def face_lattice(self): sage: c5_20_fl = c5_20.face_lattice() # long time sage: [len(x) for x in c5_20_fl.level_sets()] # long time [1, 20, 190, 580, 680, 272, 1] - sage: polytopes.hypercube(2).face_lattice().plot() # optional - sage.plot + sage: polytopes.hypercube(2).face_lattice().plot() # needs sage.plot Graphics object consisting of 27 graphics primitives sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets() sage: level_sets[0][0].ambient_V_indices(), level_sets[-1][0].ambient_V_indices() @@ -399,31 +399,33 @@ def hasse_diagram(self): EXAMPLES:: - sage: P = polytopes.regular_polygon(4).pyramid() # optional - sage.rings.number_field - sage: D = P.hasse_diagram(); D # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P = polytopes.regular_polygon(4).pyramid() + sage: D = P.hasse_diagram(); D Digraph on 20 vertices - sage: D.degree_polynomial() # optional - sage.rings.number_field + sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 Faces of an mutable polyhedron are not hashable. Hence those are not suitable as vertices of the hasse diagram. Use the combinatorial polyhedron instead:: - sage: P = polytopes.regular_polygon(4).pyramid() # optional - sage.rings.number_field - sage: parent = P.parent() # optional - sage.rings.number_field - sage: parent = parent.change_ring(QQ, backend='ppl') # optional - sage.rings.number_field - sage: Q = parent._element_constructor_(P, mutable=True) # optional - sage.rings.number_field - sage: Q.hasse_diagram() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P = polytopes.regular_polygon(4).pyramid() + sage: parent = P.parent() + sage: parent = parent.change_ring(QQ, backend='ppl') + sage: Q = parent._element_constructor_(P, mutable=True) + sage: Q.hasse_diagram() Traceback (most recent call last): ... TypeError: mutable polyhedra are unhashable - sage: C = Q.combinatorial_polyhedron() # optional - sage.rings.number_field - sage: D = C.hasse_diagram() # optional - sage.rings.number_field - sage: set(D.vertices()) == set(range(20)) # optional - sage.rings.number_field + sage: C = Q.combinatorial_polyhedron() + sage: D = C.hasse_diagram() + sage: set(D.vertices(sort=False)) == set(range(20)) True sage: def index_to_combinatorial_face(n): ....: return C.face_by_face_lattice_index(n) - sage: D.relabel(index_to_combinatorial_face, inplace=True) # optional - sage.rings.number_field - sage: D.vertices() # optional - sage.rings.number_field + sage: D.relabel(index_to_combinatorial_face, inplace=True) + sage: D.vertices(sort=True) [A -1-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, @@ -444,7 +446,7 @@ def hasse_diagram(self): A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 3-dimensional face of a 3-dimensional combinatorial polyhedron] - sage: D.degree_polynomial() # optional - sage.rings.number_field + sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 """ @@ -643,7 +645,8 @@ def combinatorial_automorphism_group(self, vertex_graph_only=False): EXAMPLES:: sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)]) - sage: quadrangle.combinatorial_automorphism_group().is_isomorphic(groups.permutation.Dihedral(4)) + sage: quadrangle.combinatorial_automorphism_group().is_isomorphic( + ....: groups.permutation.Dihedral(4)) True sage: quadrangle.restricted_automorphism_group() Permutation Group with generators [()] @@ -655,19 +658,24 @@ def combinatorial_automorphism_group(self, vertex_graph_only=False): Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))] This shows an example of two polytopes whose vertex-edge graphs are isomorphic, - but their face_lattices are not isomorphic:: - - sage: Q=Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], - ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], - ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], - ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877], [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], - ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) + but their face lattices are not isomorphic:: + + sage: Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], + ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], + ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], + ....: [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], + ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877], + ....: [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], + ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], + ....: [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) sage: C = polytopes.cyclic_polytope(4,8) sage: C.is_combinatorially_isomorphic(Q) False - sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic(Q.combinatorial_automorphism_group(vertex_graph_only=True)) + sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic( + ....: Q.combinatorial_automorphism_group(vertex_graph_only=True)) True - sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic(Q.combinatorial_automorphism_group(vertex_graph_only=False)) + sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic( + ....: Q.combinatorial_automorphism_group(vertex_graph_only=False)) False The automorphism group of the face lattice is isomorphic to the combinatorial automorphism group:: @@ -1080,10 +1088,10 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): All the faces of the 3-dimensional permutahedron are either combinatorially isomorphic to a square or a hexagon:: - sage: H = polytopes.regular_polygon(6) # optional - sage.rings.number_field + sage: H = polytopes.regular_polygon(6) # needs sage.rings.number_field sage: S = polytopes.hypercube(2) sage: P = polytopes.permutahedron(4) - sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # optional - sage.rings.number_field + sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field ....: or F.as_polyhedron().is_combinatorially_isomorphic(H) ....: for F in P.faces(2)) True @@ -1102,7 +1110,7 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): ....: return C.intersection(H) sage: [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)] [True, True, True] - sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # optional - sage.rings.number_field + sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field True sage: simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron()) True @@ -1225,7 +1233,7 @@ def is_self_dual(self): True sage: polytopes.cube().is_self_dual() False - sage: polytopes.hypersimplex(5,2).is_self_dual() # optional - sage.combinat + sage: polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat False sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual() Traceback (most recent call last): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 1f8db82faed..851b341643c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -909,7 +909,7 @@ def _latex_(self): sage: from sage.graphs.graph_latex import check_tkz_graph sage: check_tkz_graph() # random - depends on TeX installation sage: g = graphs.CompleteGraph(2) - sage: print(g._latex_()) # optional - sage.plot + sage: print(g._latex_()) # needs sage.plot \begin{tikzpicture} \definecolor{cv0}{rgb}{0.0,0.0,0.0} \definecolor{cfv0}{rgb}{1.0,1.0,1.0} @@ -946,21 +946,21 @@ def _matrix_(self, R=None, vertices=None): EXAMPLES:: sage: G = graphs.CompleteBipartiteGraph(2, 3) - sage: m = matrix(G); m.parent() # optional - sage.modules + sage: m = matrix(G); m.parent() # needs sage.modules Full MatrixSpace of 5 by 5 dense matrices over Integer Ring - sage: m # optional - sage.modules + sage: m # needs sage.modules [0 0 1 1 1] [0 0 1 1 1] [1 1 0 0 0] [1 1 0 0 0] [1 1 0 0 0] - sage: G._matrix_() # optional - sage.modules + sage: G._matrix_() # needs sage.modules [0 0 1 1 1] [0 0 1 1 1] [1 1 0 0 0] [1 1 0 0 0] [1 1 0 0 0] - sage: factor(m.charpoly()) # optional - sage.modules + sage: factor(m.charpoly()) # needs sage.modules x^3 * (x^2 - 6) """ return self.am(vertices=vertices, base_ring=R) @@ -1369,24 +1369,24 @@ def export_to_file(self, filename, format=None, **kwds): sage: g = graphs.PetersenGraph() sage: filename = tmp_filename(ext=".pajek") - sage: g.export_to_file(filename) # optional - networkx - sage: import networkx # optional - networkx - sage: G_networkx = networkx.read_pajek(filename) # optional - networkx - sage: Graph(G_networkx).is_isomorphic(g) # optional - networkx + sage: g.export_to_file(filename) # needs networkx + sage: import networkx # needs networkx + sage: G_networkx = networkx.read_pajek(filename) # needs networkx + sage: Graph(G_networkx).is_isomorphic(g) # needs networkx True sage: filename = tmp_filename(ext=".edgelist") - sage: g.export_to_file(filename, data=False) # optional - networkx - sage: h = Graph(networkx.read_edgelist(filename)) # optional - networkx - sage: g.is_isomorphic(h) # optional - networkx + sage: g.export_to_file(filename, data=False) # needs networkx + sage: h = Graph(networkx.read_edgelist(filename)) # needs networkx + sage: g.is_isomorphic(h) # needs networkx True TESTS:: - sage: g.export_to_file("hey", format="When I feel heavy metaaaaaallll...") # optional - networkx + sage: g.export_to_file("hey", format="When I feel heavy metaaaaaallll...") # needs networkx Traceback (most recent call last): ... ValueError: format 'When I feel heavy metaaaaaallll...' unknown - sage: g.export_to_file("my_file.Yeeeeppeeeeee") # optional - networkx + sage: g.export_to_file("my_file.Yeeeeppeeeeee") # needs networkx Traceback (most recent call last): ... RuntimeError: the file format could not be guessed from 'my_file.Yeeeeppeeeeee' @@ -1508,21 +1508,21 @@ def networkx_graph(self, weight_function=None): EXAMPLES:: sage: G = graphs.TetrahedralGraph() - sage: N = G.networkx_graph() # optional - networkx - sage: type(N) # optional - networkx + sage: N = G.networkx_graph() # needs networkx + sage: type(N) # needs networkx sage: def weight_fn(e): ....: return e[2] sage: G1 = Graph([(1,2,1), (1,3,4), (2,3,3), (3,4,4)]) - sage: H = G1.networkx_graph(weight_function=weight_fn) # optional - networkx - sage: H.edges(data=True) # optional - networkx + sage: H = G1.networkx_graph(weight_function=weight_fn) # needs networkx + sage: H.edges(data=True) # needs networkx EdgeDataView([(1, 2, {'weight': 1}), (1, 3, {'weight': 4}), (2, 3, {'weight': 3}), (3, 4, {'weight': 4})]) sage: G2 = DiGraph([(1,2,1), (1,3,4), (2,3,3), (3,4,4), (3,4,5)], ....: multiedges=True) - sage: H = G2.networkx_graph(weight_function=weight_fn) # optional - networkx - sage: H.edges(data=True) # optional - networkx + sage: H = G2.networkx_graph(weight_function=weight_fn) # needs networkx + sage: H.edges(data=True) # needs networkx OutMultiEdgeDataView([(1, 2, {'weight': 1}), (1, 3, {'weight': 4}), (2, 3, {'weight': 3}), (3, 4, {'weight': 5}), (3, 4, {'weight': 4})]) @@ -1912,7 +1912,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds EXAMPLES:: sage: G = graphs.CubeGraph(4) - sage: G.adjacency_matrix() # optional - sage.modules + sage: G.adjacency_matrix() # needs sage.modules [0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0] [1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0] [1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0] @@ -1932,7 +1932,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds :: - sage: matrix(GF(2), G) # matrix over GF(2) # optional - sage.modules sage.rings.finite_rings + sage: matrix(GF(2), G) # matrix over GF(2) # needs sage.modules sage.rings.finite_rings [0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0] [1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0] [1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0] @@ -1954,7 +1954,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds sage: D = DiGraph({0: [1, 2, 3], 1: [0, 2], 2: [3], ....: 3: [4], 4: [0, 5], 5: [1]}) - sage: D.adjacency_matrix() # optional - sage.modules + sage: D.adjacency_matrix() # needs sage.modules [0 1 1 1 0 0] [1 0 1 0 0 0] [0 0 0 1 0 0] @@ -1964,7 +1964,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds A different ordering of the vertices:: - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[2, 4, 1, 3, 0]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[2, 4, 1, 3, 0]) # needs sage.modules [0 0 1 1 0] [0 0 0 1 0] [1 0 0 0 1] @@ -1973,18 +1973,18 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds A different base ring:: - sage: graphs.PathGraph(5).adjacency_matrix(base_ring=RDF) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(base_ring=RDF) # needs sage.modules [0.0 1.0 0.0 0.0 0.0] [1.0 0.0 1.0 0.0 0.0] [0.0 1.0 0.0 1.0 0.0] [0.0 0.0 1.0 0.0 1.0] [0.0 0.0 0.0 1.0 0.0] - sage: type(_) # optional - sage.modules + sage: type(_) # needs sage.modules A different matrix implementation:: - sage: graphs.PathGraph(5).adjacency_matrix(sparse=False, # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(sparse=False, # needs sage.modules ....: implementation='numpy') [0 1 0 0 0] [1 0 1 0 0] @@ -1996,14 +1996,14 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds As an immutable matrix:: - sage: M = graphs.PathGraph(5).adjacency_matrix(sparse=False, # optional - sage.modules + sage: M = graphs.PathGraph(5).adjacency_matrix(sparse=False, # needs sage.modules ....: immutable=True); M [0 1 0 0 0] [1 0 1 0 0] [0 1 0 1 0] [0 0 1 0 1] [0 0 0 1 0] - sage: M[2, 2] = 1 + sage: M[2, 2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2011,18 +2011,18 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds TESTS:: - sage: graphs.CubeGraph(8).adjacency_matrix().parent() # optional - sage.modules + sage: graphs.CubeGraph(8).adjacency_matrix().parent() # needs sage.modules Full MatrixSpace of 256 by 256 dense matrices over Integer Ring - sage: graphs.CubeGraph(9).adjacency_matrix().parent() # optional - sage.modules + sage: graphs.CubeGraph(9).adjacency_matrix().parent() # needs sage.modules Full MatrixSpace of 512 by 512 sparse matrices over Integer Ring - sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # optional - sage.modules + sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # needs sage.modules ....: multiedges=True).adjacency_matrix().parent() Full MatrixSpace of 501 by 501 dense matrices over Integer Ring - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices @@ -2129,7 +2129,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None EXAMPLES:: sage: G = graphs.PetersenGraph() - sage: G.incidence_matrix() # optional - sage.modules + sage: G.incidence_matrix() # needs sage.modules [1 1 1 0 0 0 0 0 0 0 0 0 0 0 0] [1 0 0 1 1 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 1 1 0 0 0 0 0 0 0 0] @@ -2140,7 +2140,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None [0 0 0 0 0 0 1 0 0 0 1 0 0 0 1] [0 0 0 0 0 0 0 0 1 0 0 1 1 0 0] [0 0 0 0 0 0 0 0 0 1 0 0 0 1 1] - sage: G.incidence_matrix(oriented=True) # optional - sage.modules + sage: G.incidence_matrix(oriented=True) # needs sage.modules [-1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0] [ 1 0 0 -1 -1 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 1 0 -1 -1 0 0 0 0 0 0 0 0] @@ -2153,18 +2153,18 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None [ 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1] sage: G = digraphs.Circulant(4, [1, 3]) - sage: G.incidence_matrix() # optional - sage.modules + sage: G.incidence_matrix() # needs sage.modules [-1 -1 1 0 0 0 1 0] [ 1 0 -1 -1 1 0 0 0] [ 0 0 0 1 -1 -1 0 1] [ 0 1 0 0 0 1 -1 -1] - sage: graphs.CompleteGraph(3).incidence_matrix() # optional - sage.modules + sage: graphs.CompleteGraph(3).incidence_matrix() # needs sage.modules [1 1 0] [1 0 1] [0 1 1] sage: G = Graph([(0, 0), (0, 1), (0, 1)], loops=True, multiedges=True) - sage: G.incidence_matrix(oriented=False) # optional - sage.modules + sage: G.incidence_matrix(oriented=False) # needs sage.modules [2 1 1] [0 1 1] @@ -2173,30 +2173,30 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None Kirchhoff matrix:: sage: G = graphs.PetersenGraph() - sage: m = G.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == G.kirchhoff_matrix() # optional - sage.modules + sage: m = G.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == G.kirchhoff_matrix() # needs sage.modules True sage: K = graphs.CompleteGraph(3) - sage: m = K.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == K.kirchhoff_matrix() # optional - sage.modules + sage: m = K.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == K.kirchhoff_matrix() # needs sage.modules True sage: H = Graph([(0, 0), (0, 1), (0, 1)], loops=True, multiedges=True) - sage: m = H.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == H.kirchhoff_matrix() # optional - sage.modules + sage: m = H.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == H.kirchhoff_matrix() # needs sage.modules True A different ordering of the vertices:: sage: P5 = graphs.PathGraph(5) - sage: P5.incidence_matrix() # optional - sage.modules + sage: P5.incidence_matrix() # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] [0 0 1 1] [0 0 0 1] - sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0]) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0]) # needs sage.modules [0 1 1 0] [0 0 0 1] [1 1 0 0] @@ -2206,13 +2206,13 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None A different ordering of the edges:: sage: E = list(P5.edge_iterator(labels=False)) - sage: P5.incidence_matrix(edges=E[::-1]) # optional - sage.modules + sage: P5.incidence_matrix(edges=E[::-1]) # needs sage.modules [0 0 0 1] [0 0 1 1] [0 1 1 0] [1 1 0 0] [1 0 0 0] - sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0], edges=E[::-1]) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0], edges=E[::-1]) # needs sage.modules [0 1 1 0] [1 0 0 0] [0 0 1 1] @@ -2221,7 +2221,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None A different base ring:: - sage: P5.incidence_matrix(base_ring=RDF) # optional - sage.modules + sage: P5.incidence_matrix(base_ring=RDF) # needs sage.modules [1.0 0.0 0.0 0.0] [1.0 1.0 0.0 0.0] [0.0 1.0 1.0 0.0] @@ -2230,13 +2230,13 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None Creating an immutable matrix:: - sage: m = P5.incidence_matrix(immutable=True); m # optional - sage.modules + sage: m = P5.incidence_matrix(immutable=True); m # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] [0 0 1 1] [0 0 0 1] - sage: m[1,2] = 1 # optional - sage.modules + sage: m[1,2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2245,15 +2245,15 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None TESTS:: sage: P5 = graphs.PathGraph(5) - sage: P5.incidence_matrix(vertices=[1] * P5.order()) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[1] * P5.order()) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices - sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # optional - sage.modules + sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # needs sage.modules Traceback (most recent call last): ... ValueError: ``edges`` must be a permutation of the edges - sage: P5.incidence_matrix(edges=P5.edges(sort=False, labels=True)) # optional - sage.modules + sage: P5.incidence_matrix(edges=P5.edges(sort=False, labels=True)) # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] @@ -2342,19 +2342,19 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): EXAMPLES:: sage: d = DiGraph({1: [2, 3], 2: [3], 3: [4], 4: [1]}) - sage: d.distance_matrix() # optional - sage.modules + sage: d.distance_matrix() # needs sage.modules [0 1 1 2] [3 0 1 2] [2 3 0 1] [1 2 2 0] - sage: d.distance_matrix(vertices=[4, 3, 2, 1]) # optional - sage.modules + sage: d.distance_matrix(vertices=[4, 3, 2, 1]) # needs sage.modules [0 2 2 1] [1 0 3 2] [2 1 0 3] [2 1 1 0] sage: G = graphs.CubeGraph(3) - sage: G.distance_matrix() # optional - sage.modules + sage: G.distance_matrix() # needs sage.modules [0 1 1 2 1 2 2 3] [1 0 2 1 2 1 3 2] [1 2 0 1 2 3 1 2] @@ -2368,7 +2368,7 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): of the distance matrix of any tree of order `n` is `(-1)^{n-1}(n-1)2^{n-2}`:: - sage: all(T.distance_matrix().det() == (-1)^9*(9)*2^8 # optional - sage.modules + sage: all(T.distance_matrix().det() == (-1)^9*(9)*2^8 # needs sage.modules ....: for T in graphs.trees(10)) True @@ -2382,18 +2382,18 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): Asking for an immutable matrix:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix().is_immutable() # optional - sage.modules + sage: G.distance_matrix().is_immutable() # needs sage.modules False - sage: G.distance_matrix(immutable=True).is_immutable() # optional - sage.modules + sage: G.distance_matrix(immutable=True).is_immutable() # needs sage.modules True Specifying a base ring:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix(vertices=[0, 1], base_ring=ZZ) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], base_ring=ZZ) # needs sage.modules [0 1] [1 0] - sage: G.distance_matrix(vertices=[0, 1], base_ring=RDF) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], base_ring=RDF) # needs sage.modules [0.0 1.0] [1.0 0.0] @@ -2401,7 +2401,7 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): constructor:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix(vertices=[0, 1], weight_function=lambda e:2) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], weight_function=lambda e:2) # needs sage.modules [0 2] [2 0] """ @@ -2479,15 +2479,15 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, sage: G = Graph(sparse=True, weighted=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.weighted_adjacency_matrix(); M # optional - sage.modules + sage: M = G.weighted_adjacency_matrix(); M # needs sage.modules [0 1 3 4] [1 0 2 0] [3 2 0 0] [4 0 0 0] - sage: H = Graph(data=M, format='weighted_adjacency_matrix', sparse=True) # optional - sage.modules - sage: H == G # optional - sage.modules + sage: H = Graph(data=M, format='weighted_adjacency_matrix', sparse=True) # needs sage.modules + sage: H == G # needs sage.modules True - sage: G.weighted_adjacency_matrix(vertices=[3, 2, 1, 0]) # optional - sage.modules + sage: G.weighted_adjacency_matrix(vertices=[3, 2, 1, 0]) # needs sage.modules [0 0 0 4] [0 0 2 3] [0 2 0 1] @@ -2495,7 +2495,7 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, Using a different matrix implementation:: - sage: M = G.weighted_adjacency_matrix(sparse=False, base_ring=ZZ, # optional - numpy sage.modules + sage: M = G.weighted_adjacency_matrix(sparse=False, base_ring=ZZ, # needs numpy sage.modules ....: implementation='numpy'); M [0 1 3 4] [1 0 2 0] @@ -2504,12 +2504,12 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, As an immutable matrix:: - sage: M = G.weighted_adjacency_matrix(immutable=True); M # optional - sage.modules + sage: M = G.weighted_adjacency_matrix(immutable=True); M # needs sage.modules [0 1 3 4] [1 0 2 0] [3 2 0 0] [4 0 0 0] - sage: M[2, 2] = 1 # optional - sage.modules + sage: M[2, 2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2520,7 +2520,7 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, The following doctest verifies that :trac:`4888` is fixed:: sage: G = DiGraph({0:{}, 1:{0:1}, 2:{0:1}}, weighted=True, sparse=True) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules [0 0 0] [1 0 0] [1 0 0] @@ -2528,16 +2528,16 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, Check error message for non numerical edge weights (:trac:`33562`):: sage: G = Graph([(0, 1)]) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules Traceback (most recent call last): ... ValueError: cannot find the weight of (0, 1, None). Consider setting parameter 'default_weight' - sage: G.weighted_adjacency_matrix(default_weight=3) # optional - sage.modules + sage: G.weighted_adjacency_matrix(default_weight=3) # needs sage.modules [0 3] [3 0] sage: G = Graph([(0, 1, 'a')]) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules Traceback (most recent call last): ... TypeError: Cannot convert NoneType to sage.structure.parent.Parent @@ -2656,33 +2656,33 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: G = Graph(sparse=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.kirchhoff_matrix(weighted=True); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True); M # needs sage.modules [ 8 -1 -3 -4] [-1 3 -2 0] [-3 -2 5 0] [-4 0 0 4] - sage: M = G.kirchhoff_matrix(); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(); M # needs sage.modules [ 3 -1 -1 -1] [-1 2 -1 0] [-1 -1 2 0] [-1 0 0 1] - sage: M = G.laplacian_matrix(normalized=True); M # optional - sage.modules sage.symbolic + sage: M = G.laplacian_matrix(normalized=True); M # needs sage.modules sage.symbolic [ 1 -1/6*sqrt(3)*sqrt(2) -1/6*sqrt(3)*sqrt(2) -1/3*sqrt(3)] [-1/6*sqrt(3)*sqrt(2) 1 -1/2 0] [-1/6*sqrt(3)*sqrt(2) -1/2 1 0] [ -1/3*sqrt(3) 0 0 1] - sage: M = G.kirchhoff_matrix(weighted=True, signless=True); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True, signless=True); M # needs sage.modules [8 1 3 4] [1 3 2 0] [3 2 5 0] [4 0 0 4] sage: G = Graph({0: [], 1: [2]}) - sage: G.laplacian_matrix(normalized=True) # optional - sage.modules + sage: G.laplacian_matrix(normalized=True) # needs sage.modules [ 0 0 0] [ 0 1 -1] [ 0 -1 1] - sage: G.laplacian_matrix(normalized=True, signless=True) # optional - sage.modules + sage: G.laplacian_matrix(normalized=True, signless=True) # needs sage.modules [0 0 0] [0 1 1] [0 1 1] @@ -2690,14 +2690,14 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl A weighted directed graph with loops, changing the variable ``indegree`` :: sage: G = DiGraph({1: {1: 2, 2: 3}, 2: {1: 4}}, weighted=True, sparse=True) - sage: G.laplacian_matrix() # optional - sage.modules + sage: G.laplacian_matrix() # needs sage.modules [ 4 -3] [-4 3] :: sage: G = DiGraph({1: {1: 2, 2: 3}, 2: {1: 4}}, weighted=True, sparse=True) - sage: G.laplacian_matrix(indegree=False) # optional - sage.modules + sage: G.laplacian_matrix(indegree=False) # needs sage.modules [ 3 -3] [-4 4] @@ -2706,12 +2706,12 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: G = Graph(sparse=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.kirchhoff_matrix(vertices=[3, 2, 1, 0]); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(vertices=[3, 2, 1, 0]); M # needs sage.modules [ 1 0 0 -1] [ 0 2 -1 -1] [ 0 -1 2 -1] [-1 -1 -1 3] - sage: M = G.kirchhoff_matrix(weighted=True, vertices=[3, 2, 1, 0]); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True, vertices=[3, 2, 1, 0]); M # needs sage.modules [ 4 0 0 -4] [ 0 5 -2 -3] [ 0 -2 3 -1] @@ -2721,8 +2721,8 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl immutable:: sage: G = Graph([(0, 1)]) - sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # optional - sage.modules - sage: M.is_immutable() # optional - sage.modules + sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # needs sage.modules + sage: M.is_immutable() # needs sage.modules True """ from sage.matrix.constructor import diagonal_matrix @@ -3717,8 +3717,8 @@ def get_pos(self, dim=2): sage: G.get_pos() sage: G.get_pos() is None True - sage: P = G.plot(save_pos=True) # optional - sage.plot - sage: G.get_pos() # optional - sage.plot + sage: P = G.plot(save_pos=True) # needs sage.plot + sage: G.get_pos() # needs sage.plot {} Some of the named graphs come with a pre-specified positioning:: @@ -3843,8 +3843,8 @@ def set_pos(self, pos, dim=2): invalid positioning are ignored:: sage: G.set_pos(dict(enumerate('abcdefghi'))) - sage: P = G.plot() # positions are ignored # optional - sage.plot - sage: G.get_pos() is None # optional - sage.plot + sage: P = G.plot() # positions are ignored # needs sage.plot + sage: G.get_pos() is None # needs sage.plot True """ if pos is None: @@ -3974,21 +3974,21 @@ def antisymmetric(self): A directed acyclic graph is antisymmetric:: - sage: G = digraphs.RandomDirectedGNR(20, 0.5) # optional - networkx - sage: G.antisymmetric() # optional - networkx + sage: G = digraphs.RandomDirectedGNR(20, 0.5) # needs networkx + sage: G.antisymmetric() # needs networkx True Loops are allowed:: - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.antisymmetric() # optional - networkx + sage: G.allow_loops(True) # needs networkx + sage: G.add_edge(0, 0) # needs networkx + sage: G.antisymmetric() # needs networkx True An undirected graph is never antisymmetric unless it is just a union of isolated vertices (with possible loops):: - sage: graphs.RandomGNP(20, 0.5).antisymmetric() # optional - networkx + sage: graphs.RandomGNP(20, 0.5).antisymmetric() # needs networkx False sage: Graph(3).antisymmetric() True @@ -4070,7 +4070,7 @@ def is_bipartite(self, certificate=False): True sage: graphs.CycleGraph(5).is_bipartite() False - sage: graphs.RandomBipartite(10, 10, 0.7).is_bipartite() # optional - numpy + sage: graphs.RandomBipartite(10, 10, 0.7).is_bipartite() # needs numpy True A random graph is very rarely bipartite:: @@ -4705,7 +4705,7 @@ def min_spanning_tree(self, NetworkX algorithm:: - sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # needs networkx [(0, 1, None), (0, 2, None), (0, 3, None), (0, 4, None)] More complicated weights:: @@ -4756,7 +4756,7 @@ def min_spanning_tree(self, [(0, 1, 1), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Prim_Boost')) [(0, 1, 1), (1, 2, 1)] - sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # needs networkx [(0, 1, 1), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Boruvka')) [(0, 1, 1), (1, 2, 1)] @@ -4778,7 +4778,7 @@ def min_spanning_tree(self, [(0, 2, 10), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Prim_Boost', weight_function=weight)) [(0, 2, 10), (1, 2, 1)] - sage: sorted(g.min_spanning_tree(algorithm='NetworkX', weight_function=weight)) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX', weight_function=weight)) # needs networkx [(0, 2, 10), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Boruvka', weight_function=weight)) [(0, 2, 10), (1, 2, 1)] @@ -4977,13 +4977,14 @@ def spanning_trees_count(self, root_vertex=None): :: - sage: M = matrix(3, 3, [0, 1, 0, 0, 0, 1, 1, 1, 0]) # optional - sage.modules - sage: D = DiGraph(M) # optional - sage.modules - sage: D.spanning_trees_count() # optional - sage.modules + sage: # needs sage.modules + sage: M = matrix(3, 3, [0, 1, 0, 0, 0, 1, 1, 1, 0]) + sage: D = DiGraph(M) + sage: D.spanning_trees_count() 1 - sage: D.spanning_trees_count(0) # optional - sage.modules + sage: D.spanning_trees_count(0) 1 - sage: D.spanning_trees_count(2) # optional - sage.modules + sage: D.spanning_trees_count(2) 2 """ if not self.order(): @@ -5041,13 +5042,13 @@ def cycle_basis(self, output='vertex'): A cycle basis in Petersen's Graph :: sage: g = graphs.PetersenGraph() - sage: g.cycle_basis() # optional - networkx + sage: g.cycle_basis() # needs networkx [[1, 6, 8, 5, 0], [4, 9, 6, 8, 5, 0], [7, 9, 6, 8, 5], [4, 3, 8, 5, 0], [1, 2, 3, 8, 5, 0], [7, 2, 3, 8, 5]] One can also get the result as a list of lists of edges:: - sage: g.cycle_basis(output='edge') # optional - networkx + sage: g.cycle_basis(output='edge') # needs networkx [[(1, 6, None), (6, 8, None), (8, 5, None), (5, 0, None), (0, 1, None)], [(4, 9, None), (9, 6, None), (6, 8, None), (8, 5, None), (5, 0, None), (0, 4, None)], [(7, 9, None), @@ -5059,17 +5060,17 @@ def cycle_basis(self, output='vertex'): Checking the given cycles are algebraically free:: - sage: g = graphs.RandomGNP(30, .4) # optional - networkx - sage: basis = g.cycle_basis() # optional - networkx + sage: g = graphs.RandomGNP(30, .4) # needs networkx + sage: basis = g.cycle_basis() # needs networkx Building the space of (directed) edges over `Z/2Z`. On the way, building a dictionary associating a unique vector to each undirected edge:: sage: m = g.size() - sage: edge_space = VectorSpace(FiniteField(2), m) # optional - sage.modules sage.rings.finite_rings - sage: edge_vector = dict(zip(g.edges(labels=False, sort=False), # optional - sage.modules sage.rings.finite_rings + sage: edge_space = VectorSpace(FiniteField(2), m) # needs sage.modules sage.rings.finite_rings + sage: edge_vector = dict(zip(g.edges(labels=False, sort=False), # needs sage.modules sage.rings.finite_rings ....: edge_space.basis())) - sage: for (u, v), vec in list(edge_vector.items()): # optional - sage.modules sage.rings.finite_rings + sage: for (u, v), vec in list(edge_vector.items()): # needs sage.modules sage.rings.finite_rings ....: edge_vector[(v, u)] = vec Defining a lambda function associating a vector to the vertices of a @@ -5080,29 +5081,29 @@ def cycle_basis(self, output='vertex'): Finally checking the cycles are a free set:: - sage: basis_as_vectors = [cycle_to_vector(_) for _ in basis] # optional - sage.modules sage.rings.finite_rings - sage: edge_space.span(basis_as_vectors).rank() == len(basis) # optional - sage.modules sage.rings.finite_rings + sage: basis_as_vectors = [cycle_to_vector(_) for _ in basis] # needs networkx sage.modules sage.rings.finite_rings + sage: edge_space.span(basis_as_vectors).rank() == len(basis) # needs networkx sage.modules sage.rings.finite_rings True For undirected graphs with multiple edges:: sage: G = Graph([(0, 2, 'a'), (0, 2, 'b'), (0, 1, 'c'), (1, 2, 'd')], ....: multiedges=True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[0, 2], [2, 1, 0]] - sage: G.cycle_basis(output='edge') # optional - networkx + sage: G.cycle_basis(output='edge') # needs networkx [[(0, 2, 'a'), (2, 0, 'b')], [(2, 1, 'd'), (1, 0, 'c'), (0, 2, 'a')]] sage: H = Graph([(1, 2), (2, 3), (2, 3), (3, 4), (1, 4), ....: (1, 4), (4, 5), (5, 6), (4, 6), (6, 7)], multiedges=True) - sage: H.cycle_basis() # optional - networkx + sage: H.cycle_basis() # needs networkx [[1, 4], [2, 3], [4, 3, 2, 1], [6, 5, 4]] Disconnected graph:: sage: G.add_cycle(["Hey", "Wuuhuu", "Really ?"]) - sage: [sorted(c) for c in G.cycle_basis()] # optional - networkx + sage: [sorted(c) for c in G.cycle_basis()] # needs networkx [['Hey', 'Really ?', 'Wuuhuu'], [0, 2], [0, 1, 2]] - sage: [sorted(c) for c in G.cycle_basis(output='edge')] # optional - networkx + sage: [sorted(c) for c in G.cycle_basis(output='edge')] # needs networkx [[('Hey', 'Wuuhuu', None), ('Really ?', 'Hey', None), ('Wuuhuu', 'Really ?', None)], @@ -5113,13 +5114,13 @@ def cycle_basis(self, output='vertex'): sage: G = graphs.CycleGraph(3) sage: G.allow_multiple_edges(True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[2, 1, 0]] Not yet implemented for directed graphs:: sage: G = DiGraph([(0, 2, 'a'), (0, 1, 'c'), (1, 2, 'd')]) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx Traceback (most recent call last): ... NotImplementedError: not implemented for directed graphs @@ -5130,9 +5131,9 @@ def cycle_basis(self, output='vertex'): sage: G = Graph([(1, 2, 'a'), (2, 3, 'b'), (2, 3, 'c'), ....: (3, 4, 'd'), (3, 4, 'e'), (4, 1, 'f')], multiedges=True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[2, 3], [4, 3, 2, 1], [4, 3, 2, 1]] - sage: G.cycle_basis(output='edge') # optional - networkx + sage: G.cycle_basis(output='edge') # needs networkx [[(2, 3, 'b'), (3, 2, 'c')], [(4, 3, 'd'), (3, 2, 'b'), (2, 1, 'a'), (1, 4, 'f')], [(4, 3, 'e'), (3, 2, 'b'), (2, 1, 'a'), (1, 4, 'f')]] @@ -5213,9 +5214,9 @@ def minimum_cycle_basis(self, algorithm=None, weight_function=None, by_weight=Fa [[1, 2, 3], [1, 2, 3, 4], [5, 6, 7]] sage: sorted(g.minimum_cycle_basis(by_weight=False)) [[1, 2, 3], [1, 3, 4], [5, 6, 7]] - sage: sorted(g.minimum_cycle_basis(by_weight=True, algorithm='NetworkX')) # optional - networkx + sage: sorted(g.minimum_cycle_basis(by_weight=True, algorithm='NetworkX')) # needs networkx [[1, 2, 3], [1, 2, 3, 4], [5, 6, 7]] - sage: g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX') # optional - networkx + sage: g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX') # needs networkx [[1, 2, 3], [1, 3, 4], [5, 6, 7]] :: @@ -5223,7 +5224,7 @@ def minimum_cycle_basis(self, algorithm=None, weight_function=None, by_weight=Fa sage: g = Graph([(1, 2), (2, 3), (3, 4), (4, 5), (5, 1), (5, 3)]) sage: sorted(g.minimum_cycle_basis(by_weight=False)) [[1, 2, 3, 5], [3, 4, 5]] - sage: sorted(g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX')) # optional - networkx + sage: sorted(g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX')) # needs networkx [[1, 2, 3, 5], [3, 4, 5]] TESTS:: @@ -5352,7 +5353,7 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se :: sage: g = graphs.PetersenGraph() - sage: (g.is_planar(kuratowski=True))[1].adjacency_matrix() # optional - sage.modules + sage: (g.is_planar(kuratowski=True))[1].adjacency_matrix() # needs sage.modules [0 1 0 0 0 1 0 0 0] [1 0 1 0 0 0 1 0 0] [0 1 0 1 0 0 0 1 0] @@ -5555,7 +5556,7 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, EXAMPLES:: sage: g439 = Graph({1: [5, 7], 2: [5, 6], 3: [6, 7], 4: [5, 6, 7]}) - sage: g439.show() # optional - sage.plot + sage: g439.show() # needs sage.plot sage: g439.is_circular_planar(boundary=[1, 2, 3, 4]) False sage: g439.is_circular_planar(kuratowski=True, boundary=[1, 2, 3, 4]) @@ -5731,11 +5732,11 @@ def layout_planar(self, set_embedding=False, on_embedding=None, 7: [2, 4], 8: [1, 6], 9: [2, 5]} - sage: g = graphs.BalancedTree(3, 4) # optional - networkx - sage: pos = g.layout(layout='planar', save_pos=True, test=True) # optional - networkx - sage: pos[0] # optional - networkx + sage: g = graphs.BalancedTree(3, 4) # needs networkx + sage: pos = g.layout(layout='planar', save_pos=True, test=True) # needs networkx + sage: pos[0] # needs networkx [0, 119] - sage: pos[120] # optional - networkx + sage: pos[120] # needs networkx [21, 37] sage: g = graphs.CycleGraph(7) sage: g.layout(layout='planar', save_pos=True, test=True) @@ -10161,7 +10162,7 @@ def _build_flow_graph(self, flow, integer): The method removes zero-cost flow cycles and updates the values accordingly:: - sage: g = digraphs.DeBruijn(2,3) # optional - sage.combinat + sage: g = digraphs.DeBruijn(2,3) # needs sage.combinat sage: flow = {('001', '010'): 1, ('010', '100'): 1, ....: ('010', '101'): 1, ('101', '010'): 1} sage: flow_graph = g._build_flow_graph(flow, True) @@ -10480,34 +10481,34 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, EXAMPLES:: sage: G = graphs.CycleGraph(4) - sage: G.pagerank(algorithm="Networkx") # optional - networkx + sage: G.pagerank(algorithm="Networkx") # needs networkx {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25} sage: G.pagerank(alpha=0.50, algorithm="igraph") # abs tol 1e-9, optional - python_igraph {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25} sage: G = Graph([(1, 2, 40), (2, 3, 50), (3, 4, 60), ....: (1, 4, 70), (4, 5, 80), (5, 6, 20)]) - sage: G.pagerank(algorithm="NetworkX") # abs tol 1e-9 # optional - networkx + sage: G.pagerank(algorithm="NetworkX") # abs tol 1e-9 # needs networkx {1: 0.16112205885619563, 2: 0.1619531043247219, 3: 0.16112205885619563, 4: 0.2374999999999999, 5: 0.17775588228760858, 6: 0.100546895675278} - sage: G.pagerank(algorithm="NetworkX", by_weight=True) # abs tol 1e-9 # optional - networkx + sage: G.pagerank(algorithm="NetworkX", by_weight=True) # abs tol 1e-9 # needs networkx {1: 0.16459583718588994, 2: 0.13977928595154515, 3: 0.16539840184339605, 4: 0.3063198690713853, 5: 0.1700057609707141, 6: 0.05390084497706962} - sage: G.pagerank(algorithm="Scipy") # abs tol 1e-9 # optional - scipy + sage: G.pagerank(algorithm="Scipy") # abs tol 1e-9 # needs scipy {1: 0.16112205885619563, 2: 0.1619531043247219, 3: 0.16112205885619563, 4: 0.2374999999999999, 5: 0.17775588228760858, 6: 0.100546895675278} - sage: G.pagerank(algorithm="Scipy", by_weight=True) # abs tol 1e-9 # optional - scipy + sage: G.pagerank(algorithm="Scipy", by_weight=True) # abs tol 1e-9 # needs scipy {1: 0.16459583718588994, 2: 0.13977928595154515, 3: 0.16539840184339605, @@ -10539,7 +10540,7 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, TESTS:: sage: G = Graph([(1, 2), (2, 3), (3, 4), (1, 3)]) - sage: G.pagerank(algorithm="NetworkX", # optional - networkx + sage: G.pagerank(algorithm="NetworkX", # needs networkx ....: personalization={1:0, 2:3, 3:-2, 4:-1}) Traceback (most recent call last): ... @@ -10678,7 +10679,7 @@ def delete_vertex(self, vertex, in_order=False): sage: G = Graph(graphs.WheelGraph(9)) sage: G.delete_vertex(0) - sage: G.show() # optional - sage.plot + sage: G.show() # needs sage.plot :: @@ -11354,13 +11355,14 @@ def vertices(self, sort=None, key=None, degree=None, vertex_property=None): If you do not care about sorted output and you are concerned about the time taken to sort, consider the following alternative:: - sage: timeit V = P.vertices(sort=True) # not tested + sage: # not tested + sage: timeit V = P.vertices(sort=True) 625 loops, best of 3: 3.86 [micro]s per loop - sage: timeit V = P.vertices(sort=False) # not tested + sage: timeit V = P.vertices(sort=False) 625 loops, best of 3: 2.06 [micro]s per loop - sage: timeit V = list(P.vertex_iterator()) # not tested + sage: timeit V = list(P.vertex_iterator()) 625 loops, best of 3: 2.05 [micro]s per loop - sage: timeit('V = list(P)') # not tested + sage: timeit('V = list(P)') 625 loops, best of 3: 1.98 [micro]s per loop We illustrate various ways to use a ``key`` to sort the list:: @@ -12979,13 +12981,14 @@ def degree(self, vertices=None, labels=False): returned list is the degree of the `i`-th vertex in the list ``list(self)``:: - sage: D = digraphs.DeBruijn(4, 2) # optional - sage.combinat - sage: D.delete_vertex('20') # optional - sage.combinat - sage: print(D.degree()) # optional - sage.combinat + sage: # needs sage.combinat + sage: D = digraphs.DeBruijn(4, 2) + sage: D.delete_vertex('20') + sage: print(D.degree()) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=list(D))) # optional - sage.combinat + sage: print(D.degree(vertices=list(D))) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=D.vertices(sort=False))) # optional - sage.combinat + sage: print(D.degree(vertices=D.vertices(sort=False))) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if labels: @@ -13123,11 +13126,11 @@ def degree_iterator(self, vertices=None, labels=False): When ``vertices=None`` yields values in the order of ``list(D)``:: sage: V = list(D) - sage: D = digraphs.DeBruijn(4, 2) # optional - sage.combinat - sage: D.delete_vertex('20') # optional - sage.combinat - sage: print(list(D.degree_iterator())) # optional - sage.combinat + sage: D = digraphs.DeBruijn(4, 2) # needs sage.combinat + sage: D.delete_vertex('20') # needs sage.combinat + sage: print(list(D.degree_iterator())) # needs sage.combinat [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print([D.degree(v) for v in D]) # optional - sage.combinat + sage: print([D.degree(v) for v in D]) # needs sage.combinat [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if vertices is None: @@ -13758,99 +13761,100 @@ def subgraph_search(self, G, induced=False): The Petersen graph contains the path graph `P_5`:: sage: g = graphs.PetersenGraph() - sage: h1 = g.subgraph_search(graphs.PathGraph(5)); h1 # optional - sage.modules + sage: h1 = g.subgraph_search(graphs.PathGraph(5)); h1 # needs sage.modules Subgraph of (Petersen graph): Graph on 5 vertices - sage: h1.vertices(sort=True); h1.edges(sort=True, labels=False) # optional - sage.modules + sage: h1.vertices(sort=True); h1.edges(sort=True, labels=False) # needs sage.modules [0, 1, 2, 3, 4] [(0, 1), (1, 2), (2, 3), (3, 4)] - sage: I1 = g.subgraph_search(graphs.PathGraph(5), induced=True); I1 # optional - sage.modules + sage: I1 = g.subgraph_search(graphs.PathGraph(5), induced=True); I1 # needs sage.modules Subgraph of (Petersen graph): Graph on 5 vertices - sage: I1.vertices(sort=True); I1.edges(sort=True, labels=False) # optional - sage.modules + sage: I1.vertices(sort=True); I1.edges(sort=True, labels=False) # needs sage.modules [0, 1, 2, 3, 8] [(0, 1), (1, 2), (2, 3), (3, 8)] It also contains the claw `K_{1,3}`:: - sage: h2 = g.subgraph_search(graphs.ClawGraph()); h2 # optional - sage.modules + sage: # needs sage.modules + sage: h2 = g.subgraph_search(graphs.ClawGraph()); h2 Subgraph of (Petersen graph): Graph on 4 vertices - sage: h2.vertices(sort=True); h2.edges(sort=True, labels=False) # optional - sage.modules + sage: h2.vertices(sort=True); h2.edges(sort=True, labels=False) [0, 1, 4, 5] [(0, 1), (0, 4), (0, 5)] - sage: I2 = g.subgraph_search(graphs.ClawGraph(), induced=True); I2 # optional - sage.modules + sage: I2 = g.subgraph_search(graphs.ClawGraph(), induced=True); I2 Subgraph of (Petersen graph): Graph on 4 vertices - sage: I2.vertices(sort=True); I2.edges(sort=True, labels=False) # optional - sage.modules + sage: I2.vertices(sort=True); I2.edges(sort=True, labels=False) [0, 1, 4, 5] [(0, 1), (0, 4), (0, 5)] Of course the induced copies are isomorphic to the graphs we were looking for:: - sage: I1.is_isomorphic(graphs.PathGraph(5)) # optional - sage.modules + sage: I1.is_isomorphic(graphs.PathGraph(5)) # needs sage.modules True - sage: I2.is_isomorphic(graphs.ClawGraph()) # optional - sage.modules + sage: I2.is_isomorphic(graphs.ClawGraph()) # needs sage.modules True However, the Petersen graph does not contain a subgraph isomorphic to `K_3`:: - sage: g.subgraph_search(graphs.CompleteGraph(3)) is None # optional - sage.modules + sage: g.subgraph_search(graphs.CompleteGraph(3)) is None # needs sage.modules True Nor does it contain a nonempty induced subgraph isomorphic to `P_6`:: - sage: g.subgraph_search(graphs.PathGraph(6), induced=True) is None # optional - sage.modules + sage: g.subgraph_search(graphs.PathGraph(6), induced=True) is None # needs sage.modules True The empty graph is a subgraph of every graph:: - sage: g.subgraph_search(graphs.EmptyGraph()) # optional - sage.modules + sage: g.subgraph_search(graphs.EmptyGraph()) # needs sage.modules Graph on 0 vertices - sage: g.subgraph_search(graphs.EmptyGraph(), induced=True) # optional - sage.modules + sage: g.subgraph_search(graphs.EmptyGraph(), induced=True) # needs sage.modules Graph on 0 vertices The subgraph may just have edges missing:: sage: k3 = graphs.CompleteGraph(3); p3 = graphs.PathGraph(3) sage: k3.relabel(list('abc')) - sage: s = k3.subgraph_search(p3) # optional - sage.modules - sage: s.edges(sort=True, labels=False) # optional - sage.modules + sage: s = k3.subgraph_search(p3) # needs sage.modules + sage: s.edges(sort=True, labels=False) # needs sage.modules [('a', 'b'), ('b', 'c')] Of course, `P_3` is not an induced subgraph of `K_3`, though:: sage: k3 = graphs.CompleteGraph(3); p3 = graphs.PathGraph(3) sage: k3.relabel(list('abc')) - sage: k3.subgraph_search(p3, induced=True) is None # optional - sage.modules + sage: k3.subgraph_search(p3, induced=True) is None # needs sage.modules True If the graph has labels, the labels are just ignored:: sage: g.set_vertex(0, 'foo') - sage: c = g.subgraph_search(graphs.PathGraph(5)) # optional - sage.modules - sage: c.get_vertices() # optional - sage.modules + sage: c = g.subgraph_search(graphs.PathGraph(5)) # needs sage.modules + sage: c.get_vertices() # needs sage.modules {0: 'foo', 1: None, 2: None, 3: None, 4: None} TESTS: Inside of a small graph (:trac:`13906`):: - sage: Graph(5).subgraph_search(Graph(1)) # optional - sage.modules + sage: Graph(5).subgraph_search(Graph(1)) # needs sage.modules Graph on 1 vertex For labelled edges (:trac:`14999`):: sage: G = graphs.CompleteGraph(10) - sage: C = G.subgraph_search(graphs.CycleGraph(4)) # optional - sage.modules - sage: C.size() # optional - sage.modules + sage: C = G.subgraph_search(graphs.CycleGraph(4)) # needs sage.modules + sage: C.size() # needs sage.modules 4 - sage: C.edges(sort=True) # optional - sage.modules + sage: C.edges(sort=True) # needs sage.modules [(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)] sage: for (u,v) in G.edges(sort=True, labels=False): ....: G.set_edge_label(u, v, u) - sage: C = G.subgraph_search(graphs.CycleGraph(4)) # optional - sage.modules - sage: C.edges(sort=True) # optional - sage.modules + sage: C = G.subgraph_search(graphs.CycleGraph(4)) # needs sage.modules + sage: C.edges(sort=True) # needs sage.modules [(0, 1, 0), (0, 3, 0), (1, 2, 1), (2, 3, 2)] """ @@ -13891,12 +13895,12 @@ def subgraph_search_count(self, G, induced=False): Counting the number of paths `P_5` in a PetersenGraph:: sage: g = graphs.PetersenGraph() - sage: g.subgraph_search_count(graphs.PathGraph(5)) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5)) # needs sage.modules 240 Requiring these subgraphs be induced:: - sage: g.subgraph_search_count(graphs.PathGraph(5), induced=True) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5), induced=True) # needs sage.modules 120 If we define the graph `T_k` (the transitive tournament on `k` vertices) @@ -13905,36 +13909,36 @@ def subgraph_search_count(self, G, induced=False): `0`:: sage: T5 = digraphs.TransitiveTournament(5) - sage: T5.subgraph_search_count(digraphs.Circuit(3)) # optional - sage.modules + sage: T5.subgraph_search_count(digraphs.Circuit(3)) # needs sage.modules 0 If we count instead the number of `T_3` in `T_5`, we expect the answer to be `\binom{5}{3}`:: sage: T3 = digraphs.TransitiveTournament(3) - sage: T5.subgraph_search_count(T3) # optional - sage.modules + sage: T5.subgraph_search_count(T3) # needs sage.modules 10 sage: binomial(5,3) 10 - sage: T3.is_isomorphic(T5.subgraph(vertices=[0, 1, 2])) # optional - sage.modules + sage: T3.is_isomorphic(T5.subgraph(vertices=[0, 1, 2])) # needs sage.modules True The empty graph is a subgraph of every graph:: - sage: g.subgraph_search_count(graphs.EmptyGraph()) # optional - sage.modules + sage: g.subgraph_search_count(graphs.EmptyGraph()) # needs sage.modules 1 If the graph has vertex labels or edge labels, the label is just ignored:: sage: g.set_vertex(0, 'foo') - sage: g.subgraph_search_count(graphs.PathGraph(5)) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5)) # needs sage.modules 240 TESTS: Inside of a small graph (:trac:`13906`):: - sage: Graph(5).subgraph_search_count(Graph(1)) # optional - sage.modules + sage: Graph(5).subgraph_search_count(Graph(1)) # needs sage.modules 5 """ from sage.graphs.generic_graph_pyx import SubgraphSearch @@ -14001,7 +14005,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): sage: g = graphs.PathGraph(5) sage: P3 = graphs.PathGraph(3) - sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # needs sage.modules ....: print(p) [0, 1, 2] [1, 2, 3] @@ -14009,7 +14013,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): [2, 3, 4] [3, 2, 1] [4, 3, 2] - sage: for p in g.subgraph_search_iterator(P3, return_graphs=True): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=True): # needs sage.modules ....: print(p) Subgraph of (Path graph) Subgraph of (Path graph) @@ -14017,13 +14021,13 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): Subgraph of (Path graph) Subgraph of (Path graph) Subgraph of (Path graph) - sage: all(h.is_isomorphic(P3) for h in g.subgraph_search_iterator(P3)) # optional - sage.modules + sage: all(h.is_isomorphic(P3) for h in g.subgraph_search_iterator(P3)) # needs sage.modules True If the graph has vertex labels or edge labels, the label is just ignored:: sage: g.set_vertex(0, 'foo') - sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # needs sage.modules ....: print(p) [0, 1, 2] [1, 2, 3] @@ -14036,47 +14040,47 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): sage: H = graphs.HouseGraph() sage: P4 = graphs.PathGraph(4) - sage: all(h.is_isomorphic(P4) # optional - sage.modules + sage: all(h.is_isomorphic(P4) # needs sage.modules ....: for h in H.subgraph_search_iterator(P4, induced=True)) True - sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=True)) # optional - sage.modules + sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=True)) # needs sage.modules 4 - sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=False)) # optional - sage.modules + sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=False)) # needs sage.modules 20 Search for subdigraphs:: sage: H = digraphs.Complete(5) sage: P4 = digraphs.Path(4) - sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=True)) # optional - sage.modules + sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=True)) # needs sage.modules 0 - sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=False)) # optional - sage.modules + sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=False)) # needs sage.modules 120 This method also works for bipartite graphs:: sage: K33 = BipartiteGraph(graphs.CompleteBipartiteGraph(3, 3)) sage: K22 = BipartiteGraph(graphs.CompleteBipartiteGraph(2, 2)) - sage: sum(1 for _ in K33.subgraph_search_iterator(K22)) # optional - sage.modules + sage: sum(1 for _ in K33.subgraph_search_iterator(K22)) # needs sage.modules 72 TESTS: Inside of a small graph (:trac:`13906`):: - sage: list(Graph(5).subgraph_search_iterator(Graph(1))) # optional - sage.modules + sage: list(Graph(5).subgraph_search_iterator(Graph(1))) # needs sage.modules [Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex] Check that the behavior of the method is consistent (:trac:`34004`):: sage: g = graphs.CycleGraph(3) - sage: for i in range(3): # optional - sage.modules + sage: for i in range(3): # needs sage.modules ....: g.subgraph_search_iterator(graphs.PathGraph(i)) sage: K4 = digraphs.Complete(4) sage: K3 = digraphs.Complete(3) - sage: for g in K4.subgraph_search_iterator(K3, return_graphs=True): # optional - sage.modules + sage: for g in K4.subgraph_search_iterator(K3, return_graphs=True): # needs sage.modules ....: print(type(g)) ....: break sage: K33 = BipartiteGraph(graphs.CompleteBipartiteGraph(3, 3)) sage: K22 = BipartiteGraph(graphs.CompleteBipartiteGraph(2, 2)) - sage: for b in K33.subgraph_search_iterator(K22, return_graphs=True): # optional - sage.modules + sage: for b in K33.subgraph_search_iterator(K22, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break sage: P5 = graphs.PathGraph(5) - sage: for b in K33.subgraph_search_iterator(P5, return_graphs=True): # optional - sage.modules + sage: for b in K33.subgraph_search_iterator(P5, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break - sage: for b in Graph(K33).subgraph_search_iterator(K22, return_graphs=True): # optional - sage.modules + sage: for b in Graph(K33).subgraph_search_iterator(K22, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break @@ -14244,21 +14248,21 @@ def is_chordal(self, certificate=False, algorithm="B"): The same goes with the product of a random lobster (which is a tree) and a Complete Graph :: - sage: grl = graphs.RandomLobster(10, .5, .5) # optional - networkx - sage: g = grl.lexicographic_product(graphs.CompleteGraph(3)) # optional - networkx - sage: g.is_chordal() # optional - networkx + sage: grl = graphs.RandomLobster(10, .5, .5) # needs networkx + sage: g = grl.lexicographic_product(graphs.CompleteGraph(3)) # needs networkx + sage: g.is_chordal() # needs networkx True The disjoint union of chordal graphs is still chordal:: - sage: (2 * g).is_chordal() # optional - networkx + sage: (2 * g).is_chordal() # needs networkx True Let us check the certificate given by Sage is indeed a perfect elimination order:: - sage: _, peo = g.is_chordal(certificate=True) # optional - networkx - sage: for v in peo: # optional - networkx + sage: _, peo = g.is_chordal(certificate=True) # needs networkx + sage: for v in peo: # needs networkx ....: if not g.subgraph(g.neighbors(v)).is_clique(): ....: raise ValueError("this should never happen") ....: g.delete_vertex(v) @@ -14478,27 +14482,27 @@ def is_circulant(self, certificate=False): The Petersen graph is not a circulant graph:: sage: g = graphs.PetersenGraph() - sage: g.is_circulant() # optional - sage.groups + sage: g.is_circulant() # needs sage.groups False A cycle is obviously a circulant graph, but several sets of parameters can be used to define it:: sage: g = graphs.CycleGraph(5) - sage: g.is_circulant(certificate=True) # optional - sage.groups + sage: g.is_circulant(certificate=True) # needs sage.groups (True, [(5, [1, 4]), (5, [2, 3])]) The same goes for directed graphs:: sage: g = digraphs.Circuit(5) - sage: g.is_circulant(certificate=True) # optional - sage.groups + sage: g.is_circulant(certificate=True) # needs sage.groups (True, [(5, [1]), (5, [3]), (5, [2]), (5, [4])]) With this information, it is very easy to create (and plot) all possible drawings of a circulant graph:: sage: g = graphs.CirculantGraph(13, [2, 3, 10, 11]) - sage: for param in g.is_circulant(certificate=True)[1]: # optional - sage.groups + sage: for param in g.is_circulant(certificate=True)[1]: # needs sage.groups ....: graphs.CirculantGraph(*param) Circulant graph ([2, 3, 10, 11]): Graph on 13 vertices Circulant graph ([1, 5, 8, 12]): Graph on 13 vertices @@ -14506,13 +14510,14 @@ def is_circulant(self, certificate=False): TESTS:: - sage: digraphs.DeBruijn(3,1).is_circulant(certificate=True) # optional - sage.combinat sage.groups + sage: # needs sage.groups + sage: digraphs.DeBruijn(3,1).is_circulant(certificate=True) # needs sage.combinat (True, [(3, [0, 1, 2])]) - sage: Graph(1).is_circulant(certificate=True) # optional - sage.groups + sage: Graph(1).is_circulant(certificate=True) (True, (1, [])) - sage: Graph(0).is_circulant(certificate=True) # optional - sage.groups + sage: Graph(0).is_circulant(certificate=True) (True, (0, [])) - sage: Graph({0: [0]}).is_circulant(certificate=True) # optional - sage.groups + sage: Graph({0: [0]}).is_circulant(certificate=True) (True, (1, [0])) """ self._scream_if_not_simple(allow_loops=True) @@ -15102,11 +15107,11 @@ def is_subgraph(self, other, induced=True, up_to_isomorphism=False): sage: p11 = graphs.PathGraph(11) sage: p15 = graphs.PathGraph(15) sage: g = graphs.Grid2dGraph(4, 4) - sage: p15.is_subgraph(g, induced=False, up_to_isomorphism=True) # optional - sage.modules + sage: p15.is_subgraph(g, induced=False, up_to_isomorphism=True) # needs sage.modules True - sage: p15.is_subgraph(g, induced=True, up_to_isomorphism=True) # optional - sage.modules + sage: p15.is_subgraph(g, induced=True, up_to_isomorphism=True) # needs sage.modules False - sage: p11.is_subgraph(g, induced=True, up_to_isomorphism=True) # optional - sage.modules + sage: p11.is_subgraph(g, induced=True, up_to_isomorphism=True) # needs sage.modules True TESTS: @@ -15184,15 +15189,15 @@ def cluster_triangles(self, nbunch=None, implementation=None): :: sage: G = graphs.RandomGNP(20, .3) - sage: d1 = G.cluster_triangles(implementation="networkx") # optional - networkx + sage: d1 = G.cluster_triangles(implementation="networkx") # needs networkx sage: d2 = G.cluster_triangles(implementation="dense_copy") sage: d3 = G.cluster_triangles(implementation="sparse_copy") - sage: d1 == d2 and d1 == d3 + sage: d1 == d2 and d1 == d3 # needs networkx True TESTS:: - sage: DiGraph().cluster_triangles(implementation="networkx") # optional - networkx + sage: DiGraph().cluster_triangles(implementation="networkx") # needs networkx Traceback (most recent call last): ... ValueError: the 'networkx' implementation does not support directed graphs @@ -15252,7 +15257,7 @@ def clustering_average(self, implementation=None): sage: (graphs.FruchtGraph()).clustering_average() 1/4 - sage: (graphs.FruchtGraph()).clustering_average(implementation='networkx') # optional - networkx + sage: (graphs.FruchtGraph()).clustering_average(implementation='networkx') # needs networkx 0.25 TESTS: @@ -15268,7 +15273,7 @@ def clustering_average(self, implementation=None): sage: G = graphs.RandomGNM(10,20) sage: impls = ['boost','sparse_copy','dense_copy'] - sage: impls += ['networkx'] # optional - networkx + sage: impls += ['networkx'] # needs networkx sage: coeffs = [G.clustering_average(implementation=impl) ....: for impl in impls] sage: max(coeffs) - min(coeffs) # tol abs 1e-12 @@ -15454,7 +15459,7 @@ def cluster_transitivity(self): EXAMPLES:: - sage: graphs.FruchtGraph().cluster_transitivity() # optional - networkx + sage: graphs.FruchtGraph().cluster_transitivity() # needs networkx 0.25 """ import networkx @@ -15689,13 +15694,13 @@ def girth(self, certificate=False): sage: g = digraphs.Circuit(6) sage: g.girth() 6 - sage: g = digraphs.RandomDirectedGNC(10) # optional - networkx - sage: g.girth() # optional - networkx + sage: g = digraphs.RandomDirectedGNC(10) # needs networkx + sage: g.girth() # needs networkx +Infinity - sage: g = DiGraph([(0, 1), (1, 2), (1, 3), (2, 3), (3, 4), (4, 0)]) # optional - networkx - sage: g.girth() # optional - networkx + sage: g = DiGraph([(0, 1), (1, 2), (1, 3), (2, 3), (3, 4), (4, 0)]) # needs networkx + sage: g.girth() # needs networkx 4 - sage: Graph(g).girth() # optional - networkx + sage: Graph(g).girth() # needs networkx 3 """ # Cases where girth <= 2 @@ -15746,10 +15751,10 @@ def odd_girth(self, algorithm="bfs", certificate=False): The McGee graph has girth 7 and therefore its odd girth is 7 as well:: - sage: G = graphs.McGeeGraph() # optional - networkx - sage: G.girth() # optional - networkx + sage: G = graphs.McGeeGraph() # needs networkx + sage: G.girth() # needs networkx 7 - sage: G.odd_girth() # optional - networkx + sage: G.odd_girth() # needs networkx 7 Any complete (directed) graph on more than 2 vertices contains @@ -15774,15 +15779,16 @@ def odd_girth(self, algorithm="bfs", certificate=False): The odd girth of a (directed) graph with loops is 1:: - sage: G = graphs.RandomGNP(10, .5) # optional - networkx - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.odd_girth() # optional - networkx + sage: # needs networkx + sage: G = graphs.RandomGNP(10, .5) + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.odd_girth() 1 - sage: G = digraphs.RandomDirectedGNP(10, .5) # optional - networkx - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.odd_girth() # optional - networkx + sage: G = digraphs.RandomDirectedGNP(10, .5) + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.odd_girth() 1 .. SEEALSO:: @@ -15987,17 +15993,17 @@ def centrality_betweenness(self, k=None, normalized=True, weight=None, 9: 3.333333333333333, 10: 3.333333333333333, 11: 3.333333333333333} sage: D = DiGraph({0:[1,2,3], 1:[2], 3:[0,1]}) - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D = D.to_undirected() - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_betweenness() # abs tol abs 1e-10 {0: 0.16666666666666666, 1: 0.16666666666666666, 2: 0.0, 3: 0.0} TESTS:: - sage: tests = ([graphs.RandomGNP(30,.1) for i in range(10)]+ # optional - networkx + sage: tests = ([graphs.RandomGNP(30,.1) for i in range(10)]+ # needs networkx ....: [digraphs.RandomDirectedGNP(30,.1) for i in range(10)]) - sage: for g in tests: # optional - networkx + sage: for g in tests: # needs networkx ....: r1 = g.centrality_betweenness(algorithm="Sage",exact=0) ....: r2 = g.centrality_betweenness(algorithm="Sage",exact=1) ....: r3 = g.centrality_betweenness(algorithm="NetworkX") @@ -16127,11 +16133,11 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, 8: 0.61111111111111..., 9: 0.61111111111111..., 10: 0.61111111111111..., 11: 0.61111111111111...} sage: D = DiGraph({0:[1,2,3], 1:[2], 3:[0,1]}) - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_closeness(vert=[0,1]) {0: 1.0, 1: 0.3333333333333333} sage: D = D.to_undirected() - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_closeness() {0: 1.0, 1: 1.0, 2: 0.75, 3: 0.75} @@ -16503,9 +16509,9 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [4, 17, 16, 12, 13, 9] sage: D.shortest_path(4, 9, algorithm='BFS') [4, 3, 2, 1, 8, 9] - sage: D.shortest_path(4, 8, algorithm='Dijkstra_NetworkX') # optional - networkx + sage: D.shortest_path(4, 8, algorithm='Dijkstra_NetworkX') # needs networkx [4, 3, 2, 1, 8] - sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') # optional - networkx + sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') # needs networkx [4, 3, 2, 1, 8] sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid') [4, 3, 19, 0, 10, 9] @@ -16521,10 +16527,10 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [0, 4, 3] sage: G.shortest_path(0, 3, by_weight=True) [0, 1, 2, 3] - sage: G.shortest_path(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_NetworkX') [0, 1, 2, 3] - sage: G.shortest_path(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_Bid_NetworkX') [0, 1, 2, 3] @@ -16564,7 +16570,7 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, sage: G = Graph() sage: G.add_vertices([1, 2]) sage: algs = ['BFS', 'BFS_Bid', 'Dijkstra_Bid', 'Bellman-Ford_Boost'] - sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # optional - networkx + sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # needs networkx sage: all(G.shortest_path(1, 2, algorithm=alg) == [] ....: for alg in algs) True @@ -16688,9 +16694,9 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, 5 sage: D.shortest_path_length(4, 9, algorithm='BFS') 5 - sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_NetworkX') # optional - networkx + sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_NetworkX') # needs networkx 5 - sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid_NetworkX') # optional - networkx + sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid_NetworkX') # needs networkx 5 sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid') 5 @@ -16708,10 +16714,10 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, 2 sage: G.shortest_path_length(0, 3, by_weight=True) 3 - sage: G.shortest_path_length(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path_length(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_NetworkX') 3 - sage: G.shortest_path_length(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path_length(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_Bid_NetworkX') 3 @@ -16754,7 +16760,7 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, sage: G = Graph() sage: G.add_vertices([1, 2]) sage: algs = ['BFS', 'BFS_Bid', 'Dijkstra_Bid', 'Bellman-Ford_Boost'] - sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # optional - networkx + sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # needs networkx sage: all(G.shortest_path_length(1, 2, algorithm=alg) == Infinity ....: for alg in algs) True @@ -17265,7 +17271,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: G = Graph({0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2}}, ....: sparse=True) - sage: G.plot(edge_labels=True).show() # long time # optional - sage.plot + sage: G.plot(edge_labels=True).show() # long time # needs sage.plot sage: G.shortest_path_lengths(0, by_weight=True) {0: 0, 1: 1, 2: 2, 3: 3, 4: 2} @@ -17273,7 +17279,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: D = DiGraph([(0,1,{'weight':1}), (1,2,{'weight':3}), (0,2,{'weight':5})]) sage: weight_function = lambda e: e[2]['weight'] - sage: D.shortest_path_lengths(1, algorithm='Dijkstra_NetworkX', # optional - networkx + sage: D.shortest_path_lengths(1, algorithm='Dijkstra_NetworkX', # needs networkx ....: by_weight=False) {1: 0, 2: 1} sage: D.shortest_path_lengths(0, weight_function=weight_function) @@ -17299,10 +17305,10 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: g = graphs.Grid2dGraph(5,5) sage: d1 = g.shortest_path_lengths((0,0), algorithm="BFS") - sage: d2 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_NetworkX") # optional - networkx + sage: d2 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_NetworkX") # needs networkx sage: d3 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_Boost") sage: d4 = g.shortest_path_lengths((0,0), algorithm="Bellman-Ford_Boost") - sage: d1 == d2 == d3 == d4 + sage: d1 == d2 == d3 == d4 # needs networkx True """ by_weight, weight_function = self._get_weight_function(by_weight=by_weight, @@ -18131,7 +18137,7 @@ def breadth_first_search(self, start, ignore_direction=False, [0, 1, 4, 5, 2, 6, 3, 9, 7, 8] sage: D = DiGraph({0: [1, 3], 1: [0, 2], 2: [0, 3], 3: [4]}) - sage: D.show() # optional - sage.plot + sage: D.show() # needs sage.plot sage: list(D.breadth_first_search(4, neighbors=D.neighbor_in_iterator, ....: report_distance=True)) [(4, 0), (3, 1), (0, 2), (2, 2), (1, 3)] @@ -18476,11 +18482,11 @@ def add_cycle(self, vertices): sage: G = Graph() sage: G.add_vertices(range(10)); G Graph on 10 vertices - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_cycle(list(range(10, 20))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_cycle(list(range(10))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot :: @@ -18532,11 +18538,11 @@ def add_path(self, vertices): sage: G = Graph() sage: G.add_vertices(range(10)); G Graph on 10 vertices - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_path(list(range(10, 20))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_path(list(range(10))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot :: @@ -18561,10 +18567,10 @@ def complement(self): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.plot() # long time # optional - sage.plot + sage: P.plot() # long time # needs sage.plot Graphics object consisting of 26 graphics primitives sage: PC = P.complement() - sage: PC.plot() # long time # optional - sage.plot + sage: PC.plot() # long time # needs sage.plot Graphics object consisting of 41 graphics primitives :: @@ -18893,9 +18899,9 @@ def cartesian_product(self, other): Cartesian product of digraphs:: sage: P = DiGraph([(0, 1)]) - sage: B = digraphs.DeBruijn(['a', 'b'], 2) # optional - sage.combinat - sage: Q = P.cartesian_product(B) # optional - sage.combinat - sage: Q.edges(sort=True, labels=None) # optional - sage.combinat + sage: B = digraphs.DeBruijn(['a', 'b'], 2) # needs sage.combinat + sage: Q = P.cartesian_product(B) # needs sage.combinat + sage: Q.edges(sort=True, labels=None) # needs sage.combinat [((0, 'aa'), (0, 'aa')), ((0, 'aa'), (0, 'ab')), ((0, 'aa'), (1, 'aa')), ((0, 'ab'), (0, 'ba')), ((0, 'ab'), (0, 'bb')), ((0, 'ab'), (1, 'ab')), @@ -18906,10 +18912,10 @@ def cartesian_product(self, other): ((1, 'ab'), (1, 'ba')), ((1, 'ab'), (1, 'bb')), ((1, 'ba'), (1, 'aa')), ((1, 'ba'), (1, 'ab')), ((1, 'bb'), (1, 'ba')), ((1, 'bb'), (1, 'bb'))] - sage: Q.strongly_connected_components_digraph().num_verts() # optional - sage.combinat + sage: Q.strongly_connected_components_digraph().num_verts() # needs sage.combinat 2 - sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # optional - sage.combinat - sage: B.is_isomorphic(Q.subgraph(V)) # optional - sage.combinat + sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # needs sage.combinat + sage: B.is_isomorphic(Q.subgraph(V)) # needs sage.combinat True """ self._scream_if_not_simple(allow_loops=True) @@ -19144,13 +19150,14 @@ def strong_product(self, other): Counting the edges (see :trac:`13699`):: - sage: g = graphs.RandomGNP(5, .5) # optional - networkx - sage: gn,gm = g.order(), g.size() # optional - networkx - sage: h = graphs.RandomGNP(5, .5) # optional - networkx - sage: hn,hm = h.order(), h.size() # optional - networkx - sage: product_size = g.strong_product(h).size() # optional - networkx - sage: expected = gm * hn + hm * gn + 2 * gm * hm # optional - networkx - sage: product_size == expected # optional - networkx + sage: # needs networkx + sage: g = graphs.RandomGNP(5, .5) + sage: gn,gm = g.order(), g.size() + sage: h = graphs.RandomGNP(5, .5) + sage: hn,hm = h.order(), h.size() + sage: product_size = g.strong_product(h).size() + sage: expected = gm * hn + hm * gn + 2 * gm * hm + sage: product_size == expected True """ self._scream_if_not_simple(allow_loops=True) @@ -19444,35 +19451,36 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" We consider the Cayley graph of the symmetric group, whose edges are labelled by the numbers 1,2, and 3:: - sage: G = SymmetricGroup(4).cayley_graph() # optional - sage.groups - sage: set(G.edge_labels()) # optional - sage.groups + sage: G = SymmetricGroup(4).cayley_graph() # needs sage.groups + sage: set(G.edge_labels()) # needs sage.groups {1, 2, 3} We first request the coloring as a function:: - sage: f = G._color_by_label(as_function=True) # optional - sage.groups - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: # needs sage.groups + sage: f = G._color_by_label(as_function=True) + sage: [f(1), f(2), f(3)] ['#0000ff', '#ff0000', '#00ff00'] - sage: f = G._color_by_label({1: "blue", 2: "red", 3: "green"}, # optional - sage.groups + sage: f = G._color_by_label({1: "blue", 2: "red", 3: "green"}, ....: as_function=True) - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: [f(1), f(2), f(3)] ['blue', 'red', 'green'] - sage: f = G._color_by_label({1: "red"}, as_function=True) # optional - sage.groups - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: f = G._color_by_label({1: "red"}, as_function=True) + sage: [f(1), f(2), f(3)] ['red', 'black', 'black'] - sage: f = G._color_by_label({1: "red"}, as_function=True, # optional - sage.groups + sage: f = G._color_by_label({1: "red"}, as_function=True, ....: default_color='blue') - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: [f(1), f(2), f(3)] ['red', 'blue', 'blue'] The default output is a dictionary assigning edges to colors:: - sage: G._color_by_label() # optional - sage.groups + sage: G._color_by_label() # needs sage.groups {'#0000ff': [((), (1,2), 1), ...], '#00ff00': [((), (3,4), 3), ...], '#ff0000': [((), (2,3), 2), ...]} - sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # optional - sage.groups + sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # needs sage.groups {'blue': [((), (1,2), 1), ...], 'green': [((), (3,4), 3), ...], 'red': [((), (2,3), 2), ...]} @@ -19481,12 +19489,13 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" We check what happens when several labels have the same color:: - sage: result = G._color_by_label({1: "blue", 2: "blue", 3: "green"}) # optional - sage.groups - sage: sorted(result) # optional - sage.groups + sage: # needs sage.groups + sage: result = G._color_by_label({1: "blue", 2: "blue", 3: "green"}) + sage: sorted(result) ['blue', 'green'] - sage: len(result['blue']) # optional - sage.groups + sage: len(result['blue']) 48 - sage: len(result['green']) # optional - sage.groups + sage: len(result['green']) 24 """ if format is True: @@ -19540,8 +19549,8 @@ def latex_options(self): sage: opts = g.latex_options() sage: opts LaTeX options for Petersen graph: {} - sage: opts.set_option('tkz_style', 'Classic') # optional - sage.plot - sage: opts # optional - sage.plot + sage: opts.set_option('tkz_style', 'Classic') # needs sage.plot + sage: opts # needs sage.plot LaTeX options for Petersen graph: {'tkz_style': 'Classic'} """ if self._latex_opts is None: @@ -19569,9 +19578,9 @@ def set_latex_options(self, **kwds): EXAMPLES:: sage: g = graphs.PetersenGraph() - sage: g.set_latex_options(tkz_style='Welsh') # optional - sage.plot - sage: opts = g.latex_options() # optional - sage.plot - sage: opts.get_option('tkz_style') # optional - sage.plot + sage: g.set_latex_options(tkz_style='Welsh') # needs sage.plot + sage: opts = g.latex_options() # needs sage.plot + sage: opts.get_option('tkz_style') # needs sage.plot 'Welsh' """ opts = self.latex_options() @@ -19735,7 +19744,7 @@ def layout_spring(self, by_component=True, **options): 4: [2.14..., -0.30...], 5: [2.80..., 0.22...]} sage: g = graphs.LadderGraph(7) - sage: g.plot(layout="spring") # optional - sage.plot + sage: g.plot(layout="spring") # needs sage.plot Graphics object consisting of 34 graphics primitives """ return spring_layout_fast(self, by_component=by_component, **options) @@ -19775,7 +19784,7 @@ def layout_ranked(self, heights=None, dim=2, spring=False, **options): 4: [1.33..., 1], 5: [1.33..., 2]} sage: g = graphs.LadderGraph(7) - sage: g.plot(layout="ranked", heights={i: (i, i+7) for i in range(7)}) # optional - sage.plot + sage: g.plot(layout="ranked", heights={i: (i, i+7) for i in range(7)}) # needs sage.plot Graphics object consisting of 34 graphics primitives """ assert heights is not None @@ -19903,7 +19912,7 @@ def layout_circular(self, dim=2, center=(0, 0), radius=1, shift=0, angle=0, **op 4: (0.43..., -0.90...), 5: (0.97..., -0.22...), 6: (0.78..., 0.62...)} - sage: G.plot(layout="circular") # optional - sage.plot + sage: G.plot(layout="circular") # needs sage.plot Graphics object consisting of 22 graphics primitives """ assert dim == 2, "3D circular layout not implemented" @@ -19937,19 +19946,19 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, sage: G = graphs.RandomTree(4) + graphs.RandomTree(5) + graphs.RandomTree(6) sage: p = G.layout_forest() - sage: G.plot(pos=p) # random # optional - sage.plot + sage: G.plot(pos=p) # random # needs sage.plot Graphics object consisting of 28 graphics primitives sage: P5 = graphs.PathGraph(5) - sage: H = P5 + P5 + graphs.BalancedTree(2,2) # optional - networkx - sage: p = H.layout_forest(forest_roots=[14,3]) # optional - networkx - sage: H.plot(pos=p) # optional - networkx sage.plot + sage: H = P5 + P5 + graphs.BalancedTree(2,2) # needs networkx + sage: p = H.layout_forest(forest_roots=[14,3]) # needs networkx + sage: H.plot(pos=p) # needs networkx sage.plot Graphics object consisting of 32 graphics primitives TESTS:: sage: G = Graph(0) - sage: G.plot(layout='forest') # optional - sage.plot + sage: G.plot(layout='forest') # needs sage.plot Graphics object consisting of 0 graphics primitives Works for forests that are trees:: @@ -20010,19 +20019,19 @@ def layout_tree(self, tree_orientation="down", tree_root=None, EXAMPLES:: sage: G = graphs.RandomTree(80) - sage: G.plot(layout="tree", tree_orientation="right") # optional - sage.plot + sage: G.plot(layout="tree", tree_orientation="right") # needs sage.plot Graphics object consisting of 160 graphics primitives - sage: T = graphs.RandomLobster(25, 0.3, 0.3) # optional - networkx - sage: T.show(layout='tree', tree_orientation='up') # optional - networkx sage.plot + sage: T = graphs.RandomLobster(25, 0.3, 0.3) # needs networkx + sage: T.show(layout='tree', tree_orientation='up') # needs networkx sage.plot sage: G = graphs.HoffmanSingletonGraph() sage: T = Graph() sage: T.add_edges(G.min_spanning_tree(starting_vertex=0)) - sage: T.show(layout='tree', tree_root=0) # optional - sage.plot + sage: T.show(layout='tree', tree_root=0) # needs sage.plot - sage: G = graphs.BalancedTree(2, 2) # optional - networkx - sage: G.layout_tree(tree_root=0) # optional - networkx + sage: G = graphs.BalancedTree(2, 2) # needs networkx + sage: G.layout_tree(tree_root=0) # needs networkx {0: [1.5, 0], 1: [2.5, -1], 2: [0.5, -1], @@ -20031,8 +20040,8 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 5: [1.0, -2], 6: [0.0, -2]} - sage: G = graphs.BalancedTree(2, 4) # optional - networkx - sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") # optional - networkx sage.plot + sage: G = graphs.BalancedTree(2, 4) # needs networkx + sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") # needs networkx sage.plot Graphics object consisting of 62 graphics primitives Using the embedding when it exists:: @@ -20050,13 +20059,13 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 6: [2.0, -1], 7: [1.0, -2], 8: [0.0, -2]} - sage: T.plot(layout="tree", tree_root=3) # optional - sage.plot + sage: T.plot(layout="tree", tree_root=3) # needs sage.plot Graphics object consisting of 18 graphics primitives TESTS:: - sage: G = graphs.BalancedTree(2, 2) # optional - networkx - sage: G.layout_tree(tree_root=0, tree_orientation='left') # optional - networkx + sage: G = graphs.BalancedTree(2, 2) # needs networkx + sage: G.layout_tree(tree_root=0, tree_orientation='left') # needs networkx {0: [0, 1.5], 1: [-1, 2.5], 2: [-1, 0.5], @@ -20066,12 +20075,12 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 6: [-2, 0.0]} sage: G = graphs.CycleGraph(3) - sage: G.plot(layout='tree') # optional - sage.plot + sage: G.plot(layout='tree') # needs sage.plot Traceback (most recent call last): ... RuntimeError: cannot use tree layout on this graph: self.is_tree() returns False sage: G = Graph(0) - sage: G.plot(layout='tree') # optional - sage.plot + sage: G.plot(layout='tree') # needs sage.plot Graphics object consisting of 0 graphics primitives """ if dim != 2: @@ -20238,8 +20247,8 @@ def layout_graphviz(self, dim=2, prog='dot', **options): Graphics object consisting of 29 graphics primitives sage: g.plot(layout="graphviz", prog="fdp") # optional - dot2tex graphviz Graphics object consisting of 29 graphics primitives - sage: g = graphs.BalancedTree(5,2) # optional - networkx - sage: g.plot(layout="graphviz", prog="circo") # optional - dot2tex graphviz networkx + sage: g = graphs.BalancedTree(5,2) # needs networkx + sage: g.plot(layout="graphviz", prog="circo") # optional - dot2tex graphviz, needs networkx Graphics object consisting of 62 graphics primitives .. TODO:: @@ -20364,7 +20373,7 @@ def _circle_embedding(self, vertices, center=(0, 0), radius=1, shift=0, angle=0, sage: g = graphs.CycleGraph(5) sage: g._circle_embedding([0, 2, 4, 1, 3], radius=2, shift=.5) - sage: g.show() # optional - sage.plot + sage: g.show() # needs sage.plot sage: g._circle_embedding(g.vertices(sort=True), angle=0) sage: g._pos[0] @@ -20444,7 +20453,7 @@ def _line_embedding(self, vertices, first=(0, 0), last=(0, 1), return_dict=False sage: g = graphs.PathGraph(5) sage: g._line_embedding([0, 2, 4, 1, 3], first=(-1, -1), last=(1, 1)) - sage: g.show() # optional - sage.plot + sage: g.show() # needs sage.plot sage: pos = g._line_embedding([4, 2, 0, 1, 3], first=(-1, -1), last=(1, 1), ....: return_dict=True) @@ -20514,19 +20523,20 @@ def graphplot(self, **options): sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(edge_labels=True, color_by_label=True, # optional - sage.plot + sage: GP = g.graphplot(edge_labels=True, color_by_label=True, # needs sage.plot ....: edge_style='dashed') - sage: GP.plot() # optional - sage.plot + sage: GP.plot() # needs sage.plot Graphics object consisting of 22 graphics primitives We can modify the :class:`~sage.graphs.graph_plot.GraphPlot` object. Notice that the changes are cumulative:: - sage: GP.set_edges(edge_style='solid') # optional - sage.plot - sage: GP.plot() # optional - sage.plot + sage: # needs sage.plot + sage: GP.set_edges(edge_style='solid') + sage: GP.plot() Graphics object consisting of 22 graphics primitives - sage: GP.set_vertices(talk=True) # optional - sage.plot - sage: GP.plot() # optional - sage.plot + sage: GP.set_vertices(talk=True) + sage: GP.plot() Graphics object consisting of 22 graphics primitives """ from sage.graphs.graph_plot import GraphPlot @@ -20551,7 +20561,7 @@ def _rich_repr_(self, display_manager, **kwds): sage: dm.preferences.supplemental_plot 'never' sage: del dm.preferences.supplemental_plot - sage: graphs.RandomGNP(20,0.0) # optional - networkx + sage: graphs.RandomGNP(20,0.0) # needs networkx RandomGNP(20,0.000000000000000): Graph on 20 vertices (use the .plot() method to plot) sage: dm.preferences.supplemental_plot = 'never' """ @@ -20707,21 +20717,21 @@ def plot(self, **options): ....: x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] - sage: pl = P.plot(pos=pos_dict, vertex_colors=d) # optional - sage.plot - sage: pl.show() # optional - sage.plot + sage: pl = P.plot(pos=pos_dict, vertex_colors=d) # needs sage.plot + sage: pl.show() # needs sage.plot :: sage: C = graphs.CubeGraph(8) - sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # optional - sage.plot - sage: P.show() # optional - sage.plot + sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # needs sage.plot + sage: P.show() # needs sage.plot :: sage: G = graphs.HeawoodGraph() sage: for u, v, l in G.edges(sort=False): ....: G.set_edge_label(u, v, '(' + str(u) + ',' + str(v) + ')') - sage: G.plot(edge_labels=True).show() # optional - sage.plot + sage: G.plot(edge_labels=True).show() # needs sage.plot :: @@ -20732,7 +20742,7 @@ def plot(self, **options): ....: 16: [17], 17: [18], 18: [19]}, sparse=True) sage: for u,v,l in D.edges(sort=False): ....: D.set_edge_label(u, v, '(' + str(u) + ',' + str(v) + ')') - sage: D.plot(edge_labels=True, layout='circular').show() # optional - sage.plot + sage: D.plot(edge_labels=True, layout='circular').show() # needs sage.plot :: @@ -20744,45 +20754,45 @@ def plot(self, **options): ....: for i in range(5): ....: if u[i] != v[i]: ....: edge_colors[R[i]].append((u, v, l)) - sage: C.plot(vertex_labels=False, vertex_size=0, # optional - sage.plot + sage: C.plot(vertex_labels=False, vertex_size=0, # needs sage.plot ....: edge_colors=edge_colors).show() :: sage: D = graphs.DodecahedralGraph() sage: Pi = [[6,5,15,14,7], [16,13,8,2,4], [12,17,9,3,1], [0,19,18,10,11]] - sage: D.show(partition=Pi) # optional - sage.plot + sage: D.show(partition=Pi) # needs sage.plot :: sage: G = graphs.PetersenGraph() sage: G.allow_loops(True) sage: G.add_edge(0, 0) - sage: G.show() # optional - sage.plot + sage: G.show() # needs sage.plot :: sage: D = DiGraph({0: [0, 1], 1: [2], 2: [3]}, loops=True) - sage: D.show() # optional - sage.plot - sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], # optional - sage.plot + sage: D.show() # needs sage.plot + sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], # needs sage.plot ....: (0, 0, 0): [(2, 3, None)]}) :: sage: pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], 3: [0.6, -0.8], 4: [0.8, 0.3]} sage: g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) - sage: g.plot(pos=pos, layout='spring', iterations=0) # optional - sage.plot + sage: g.plot(pos=pos, layout='spring', iterations=0) # needs sage.plot Graphics object consisting of 11 graphics primitives :: sage: G = Graph() - sage: P = G.plot() # optional - sage.plot - sage: P.axes() # optional - sage.plot + sage: P = G.plot() # needs sage.plot + sage: P.axes() # needs sage.plot False sage: G = DiGraph() - sage: P = G.plot() # optional - sage.plot - sage: P.axes() # optional - sage.plot + sage: P = G.plot() # needs sage.plot + sage: P.axes() # needs sage.plot False :: @@ -20799,11 +20809,11 @@ def plot(self, **options): 7: (-0.29..., -0.40...), 8: (0.29..., -0.40...), 9: (0.47..., 0.15...)} - sage: P = G.plot(save_pos=True, layout='spring') # optional - sage.plot + sage: P = G.plot(save_pos=True, layout='spring') # needs sage.plot The following illustrates the format of a position dictionary:: - sage: G.get_pos() # currently random across platforms, see #9593 # optional - sage.plot + sage: G.get_pos() # currently random across platforms, see #9593 # needs sage.plot {0: [1.17..., -0.855...], 1: [1.81..., -0.0990...], 2: [1.35..., 0.184...], @@ -20819,14 +20829,14 @@ def plot(self, **options): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # needs sage.plot Graphics object consisting of 14 graphics primitives :: sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # needs sage.plot Graphics object consisting of 14 graphics primitives sage: t.set_edge_label(0, 1, -7) sage: t.set_edge_label(0, 5, 3) @@ -20835,7 +20845,7 @@ def plot(self, **options): sage: t.set_edge_label(3, 2, 'spam') sage: t.set_edge_label(2, 6, 3/2) sage: t.set_edge_label(0, 4, 66) - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}, # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}, # needs sage.plot ....: edge_labels=True) Graphics object consisting of 20 graphics primitives @@ -20843,32 +20853,32 @@ def plot(self, **options): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(layout='tree') # optional - sage.plot + sage: t.plot(layout='tree') # needs sage.plot Graphics object consisting of 14 graphics primitives :: sage: t = DiGraph('JCC???@A??GO??CO??GO??') - sage: t.plot(layout='tree', tree_root=0, tree_orientation="up") # optional - sage.plot + sage: t.plot(layout='tree', tree_root=0, tree_orientation="up") # needs sage.plot Graphics object consisting of 22 graphics primitives sage: D = DiGraph({0: [1, 2, 3], 2: [1, 4], 3: [0]}) - sage: D.plot() # optional - sage.plot + sage: D.plot() # needs sage.plot Graphics object consisting of 16 graphics primitives sage: D = DiGraph(multiedges=True,sparse=True) sage: for i in range(5): ....: D.add_edge((i, i + 1, 'a')) ....: D.add_edge((i, i - 1, 'b')) - sage: D.plot(edge_labels=True, edge_colors=D._color_by_label()) # optional - sage.plot + sage: D.plot(edge_labels=True, edge_colors=D._color_by_label()) # needs sage.plot Graphics object consisting of 34 graphics primitives - sage: D.plot(edge_labels=True, color_by_label={'a': 'blue', 'b': 'red'}, # optional - sage.plot + sage: D.plot(edge_labels=True, color_by_label={'a': 'blue', 'b': 'red'}, # needs sage.plot ....: edge_style='dashed') Graphics object consisting of 34 graphics primitives sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), (0, 1, 'd'), ....: (0, 1, 'e'), (0, 1, 'f'), (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) - sage: g.plot(edge_labels=True, color_by_label=True, edge_style='dashed') # optional - sage.plot + sage: g.plot(edge_labels=True, color_by_label=True, edge_style='dashed') # needs sage.plot Graphics object consisting of 22 graphics primitives :: @@ -20876,21 +20886,21 @@ def plot(self, **options): sage: S = SupersingularModule(389) sage: H = S.hecke_matrix(2) sage: D = DiGraph(H, sparse=True) - sage: P = D.plot() # optional - sage.plot + sage: P = D.plot() # needs sage.plot :: sage: G = Graph({'a': ['a','b','b','b','e'], 'b': ['c','d','e'], ....: 'c':['c','d','d','d'],'d':['e']}, sparse=True) - sage: G.show(pos={'a':[0,1],'b':[1,1],'c':[2,0],'d':[1,0],'e':[0,0]}) # optional - sage.plot + sage: G.show(pos={'a':[0,1],'b':[1,1],'c':[2,0],'d':[1,0],'e':[0,0]}) # needs sage.plot TESTS:: sage: G = DiGraph({0: {1: 'a', 2: 'a'}, 1: {0: 'b'}, 2: {0: 'c'}}) - sage: p = G.plot(edge_labels=True, # optional - sage.plot + sage: p = G.plot(edge_labels=True, # needs sage.plot ....: color_by_label={'a': 'yellow', 'b': 'purple'}); p Graphics object consisting of 14 graphics primitives - sage: sorted(x.options()['rgbcolor'] for x in p # optional - sage.plot + sage: sorted(x.options()['rgbcolor'] for x in p # needs sage.plot ....: if isinstance(x, sage.plot.arrow.CurveArrow)) ['black', 'purple', 'yellow', 'yellow'] """ @@ -20917,8 +20927,8 @@ def show(self, method="matplotlib", **kwds): EXAMPLES:: sage: C = graphs.CubeGraph(8) - sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # optional - sage.plot - sage: P.show() # long time (3s on sage.math, 2011) # optional - sage.plot + sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # needs sage.plot + sage: P.show() # long time (3s on sage.math, 2011), needs sage.plot """ if method == "js": @@ -21006,15 +21016,15 @@ def plot3d(self, bgcolor=(1, 1, 1), EXAMPLES:: sage: G = graphs.CubeGraph(5) - sage: G.plot3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, optional - sage.plot + sage: G.plot3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, needs sage.plot Graphics3d Object We plot a fairly complicated Cayley graph:: - sage: A5 = AlternatingGroup(5); A5 # optional - sage.groups + sage: A5 = AlternatingGroup(5); A5 # needs sage.groups Alternating group of order 5!/2 as a permutation group - sage: G = A5.cayley_graph() # optional - sage.groups - sage: G.plot3d(vertex_size=0.03, edge_size=0.01, # long time, optional - sage.groups sage.plot + sage: G = A5.cayley_graph() # needs sage.groups + sage: G.plot3d(vertex_size=0.03, edge_size=0.01, # long time # needs sage.groups sage.plot ....: vertex_colors={(1,1,1): list(G)}, bgcolor=(0,0,0), ....: color_by_label=True, iterations=200) Graphics3d Object @@ -21022,26 +21032,26 @@ def plot3d(self, bgcolor=(1, 1, 1), Some :class:`~sage.plot.plot3d.tachyon.Tachyon` examples:: sage: D = graphs.DodecahedralGraph() - sage: P3D = D.plot3d(engine='tachyon') # optional - sage.plot - sage: P3D.show() # long time, optional - sage.plot + sage: P3D = D.plot3d(engine='tachyon') # needs sage.plot + sage: P3D.show() # long time # needs sage.plot :: sage: G = graphs.PetersenGraph() - sage: G.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: G.plot3d(engine='tachyon', # long time # needs sage.plot ....: vertex_colors={(0,0,1): list(G)}).show() :: sage: C = graphs.CubeGraph(4) - sage: C.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: C.plot3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(0,1,0): C.edges(sort=False)}, ....: vertex_colors={(1,1,1): list(C)}, bgcolor=(0,0,0)).show() :: sage: K = graphs.CompleteGraph(3) - sage: K.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: K.plot3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(1,0,0): [(0,1,None)], ....: (0,1,0): [(0,2,None)], ....: (0,0,1): [(1,2,None)]}).show() @@ -21055,7 +21065,7 @@ def plot3d(self, bgcolor=(1, 1, 1), ....: 8: [9], 9: [10, 13], 10: [11], 11: [12, 18], ....: 12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], ....: 17: [18], 18: [19], 19: []}) - sage: D.plot3d().show() # long time, optional - sage.plot + sage: D.plot3d().show() # long time # needs sage.plot :: @@ -21063,7 +21073,7 @@ def plot3d(self, bgcolor=(1, 1, 1), sage: from sage.plot.colors import rainbow sage: R = rainbow(P.size(), 'rgbtuple') sage: edge_colors = {R[i]: [e] for i, e in enumerate(P.edge_iterator())} - sage: P.plot3d(engine='tachyon', edge_colors=edge_colors).show() # long time, optional - sage.plot + sage: P.plot3d(engine='tachyon', edge_colors=edge_colors).show() # long time, needs sage.plot :: @@ -21078,24 +21088,24 @@ def plot3d(self, bgcolor=(1, 1, 1), Using the ``partition`` keyword:: sage: G = graphs.WheelGraph(7) - sage: G.plot3d(partition=[[0], [1, 2, 3, 4, 5, 6]]) # optional - sage.plot + sage: G.plot3d(partition=[[0], [1, 2, 3, 4, 5, 6]]) # needs sage.plot Graphics3d Object TESTS:: sage: G = DiGraph({0: {1: 'a', 2: 'a'}, 1: {0: 'b'}, 2: {0: 'c'}}) - sage: p = G.plot3d(edge_labels=True, # optional - sage.plot + sage: p = G.plot3d(edge_labels=True, # needs sage.plot ....: color_by_label={'a': 'yellow', 'b': 'cyan'}) - sage: s = p.x3d_str() # optional - sage.plot + sage: s = p.x3d_str() # needs sage.plot This 3D plot contains four yellow objects (two cylinders and two cones), two black objects and 2 cyan objects:: - sage: s.count("Material diffuseColor='1.0 1.0 0.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='1.0 1.0 0.0'") # needs sage.plot 4 - sage: s.count("Material diffuseColor='0.0 0.0 0.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='0.0 0.0 0.0'") # needs sage.plot 2 - sage: s.count("Material diffuseColor='0.0 1.0 1.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='0.0 1.0 1.0'") # needs sage.plot 2 .. SEEALSO:: @@ -21264,14 +21274,14 @@ def show3d(self, bgcolor=(1, 1, 1), vertex_colors=None, vertex_size=0.06, EXAMPLES:: sage: G = graphs.CubeGraph(5) - sage: G.show3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, optional - sage.plot + sage: G.show3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, needs sage.plot We plot a fairly complicated Cayley graph:: - sage: A5 = AlternatingGroup(5); A5 # optional - sage.groups + sage: A5 = AlternatingGroup(5); A5 # needs sage.groups Alternating group of order 5!/2 as a permutation group - sage: G = A5.cayley_graph() # optional - sage.groups - sage: G.show3d(vertex_size=0.03, # long time, optional - sage.groups sage.plot + sage: G = A5.cayley_graph() # needs sage.groups + sage: G.show3d(vertex_size=0.03, # long time # needs sage.groups sage.plot ....: edge_size=0.01, edge_size2=0.02, ....: vertex_colors={(1,1,1): list(G)}, bgcolor=(0,0,0), ....: color_by_label=True, iterations=200) @@ -21279,25 +21289,25 @@ def show3d(self, bgcolor=(1, 1, 1), vertex_colors=None, vertex_size=0.06, Some :class:`~sage.plot.plot3d.tachyon.Tachyon` examples:: sage: D = graphs.DodecahedralGraph() - sage: D.show3d(engine='tachyon') # long time, optional - sage.plot + sage: D.show3d(engine='tachyon') # long time # needs sage.plot :: sage: G = graphs.PetersenGraph() - sage: G.show3d(engine='tachyon', # long time, optional - sage.plot + sage: G.show3d(engine='tachyon', # long time # needs sage.plot ....: vertex_colors={(0,0,1): list(G)}) :: sage: C = graphs.CubeGraph(4) - sage: C.show3d(engine='tachyon', # long time, optional - sage.plot + sage: C.show3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(0,1,0): C.edges(sort=False)}, ....: vertex_colors={(1,1,1): list(C)}, bgcolor=(0,0,0)) :: sage: K = graphs.CompleteGraph(3) - sage: K.show3d(engine='tachyon', # long time, optional - sage.plot + sage: K.show3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(1,0,0): [(0, 1, None)], ....: (0, 1, 0): [(0, 2, None)], ....: (0, 0, 1): [(1, 2, None)]}) @@ -21460,12 +21470,13 @@ def graphviz_string(self, **options): A digraph using latex labels for vertices and edges:: - sage: f(x) = -1 / x # optional - sage.symbolic - sage: g(x) = 1 / (x + 1) # optional - sage.symbolic - sage: G = DiGraph() # optional - sage.symbolic - sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) # optional - sage.symbolic - sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) # optional - sage.symbolic - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: # needs sage.symbolic + sage: f(x) = -1 / x + sage: g(x) = 1 / (x + 1) + sage: G = DiGraph() + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) + sage: print(G.graphviz_string(labels="latex", # random ....: edge_labels=True)) digraph { node [shape="plaintext"]; @@ -21492,7 +21503,7 @@ def graphviz_string(self, **options): node_4 -> node_9 [label=" ", texlbl="$x \ {\mapsto}\ \frac{1}{x + 1}$"]; } - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: print(G.graphviz_string(labels="latex", # random # needs sage.symbolic ....: color_by_label=True)) digraph { node [shape="plaintext"]; @@ -21519,7 +21530,7 @@ def graphviz_string(self, **options): node_4 -> node_9 [color = "#00ffff"]; } - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: print(G.graphviz_string(labels="latex", # random # needs sage.symbolic ....: color_by_label={f: "red", g: "blue"})) digraph { node [shape="plaintext"]; @@ -21607,7 +21618,7 @@ def graphviz_string(self, **options): sage: def edge_options(data): ....: u, v, label = data ....: return {"dir":"back"} if u == 1 else {} - sage: print(G.graphviz_string(edge_options=edge_options)) # random # optional - sage.symbolic + sage: print(G.graphviz_string(edge_options=edge_options)) # random # needs sage.symbolic digraph { node_0 [label="-1"]; node_1 [label="-1/2"]; @@ -21641,7 +21652,7 @@ def graphviz_string(self, **options): ....: if (u,v) == (1, -1): options["label_style"] = "latex" ....: if (u,v) == (1, 1/2): options["dir"] = "back" ....: return options - sage: print(G.graphviz_string(edge_options=edge_options)) # random # optional - sage.symbolic + sage: print(G.graphviz_string(edge_options=edge_options)) # random # needs sage.symbolic digraph { node_0 [label="-1"]; node_1 [label="-1/2"]; @@ -21737,12 +21748,13 @@ def graphviz_string(self, **options): The following digraph has vertices with newlines in their string representations:: - sage: m1 = matrix(3, 3) # optional - sage.modules - sage: m2 = matrix(3, 3, 1) # optional - sage.modules - sage: m1.set_immutable() # optional - sage.modules - sage: m2.set_immutable() # optional - sage.modules - sage: g = DiGraph({m1: [m2]}) # optional - sage.modules - sage: print(g.graphviz_string()) # optional - sage.modules + sage: # needs sage.modules + sage: m1 = matrix(3, 3) + sage: m2 = matrix(3, 3, 1) + sage: m1.set_immutable() + sage: m2.set_immutable() + sage: g = DiGraph({m1: [m2]}) + sage: print(g.graphviz_string()) digraph { node_0 [label="[0 0 0]\n\ [0 0 0]\n\ @@ -22053,26 +22065,26 @@ def spectrum(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.spectrum() # optional - sage.modules + sage: P.spectrum() # needs sage.modules [3, 1, 1, 1, 1, 1, -2, -2, -2, -2] - sage: P.spectrum(laplacian=True) # optional - sage.modules + sage: P.spectrum(laplacian=True) # needs sage.modules [5, 5, 5, 5, 2, 2, 2, 2, 2, 0] sage: D = P.to_directed() sage: D.delete_edge(7, 9) - sage: D.spectrum() # optional - sage.modules + sage: D.spectrum() # needs sage.modules [2.9032119259..., 1, 1, 1, 1, 0.8060634335..., -1.7092753594..., -2, -2, -2] :: sage: C = graphs.CycleGraph(8) - sage: C.spectrum() # optional - sage.modules + sage: C.spectrum() # needs sage.modules [2, 1.4142135623..., 1.4142135623..., 0, 0, -1.4142135623..., -1.4142135623..., -2] A digraph may have complex eigenvalues. Previously, the complex parts of graph eigenvalues were being dropped. For a 3-cycle, we have:: sage: T = DiGraph({0: [1], 1: [2], 2: [0]}) - sage: T.spectrum() # optional - sage.modules + sage: T.spectrum() # needs sage.modules [1, -0.5000000000... + 0.8660254037...*I, -0.5000000000... - 0.8660254037...*I] TESTS: @@ -22086,10 +22098,10 @@ def spectrum(self, laplacian=False): eigenvalues. :: sage: H = graphs.HoffmanSingletonGraph() - sage: evals = H.spectrum() # optional - sage.modules - sage: lap = [7 - x for x in evals] # optional - sage.modules - sage: lap.sort(reverse=True) # optional - sage.modules - sage: lap == H.spectrum(laplacian=True) # optional - sage.modules + sage: evals = H.spectrum() # needs sage.modules + sage: lap = [7 - x for x in evals] # needs sage.modules + sage: lap.sort(reverse=True) # needs sage.modules + sage: lap == H.spectrum(laplacian=True) # needs sage.modules True """ # Ideally the spectrum should return something like a Factorization object @@ -22135,11 +22147,11 @@ def characteristic_polynomial(self, var='x', laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.characteristic_polynomial() # optional - sage.modules + sage: P.characteristic_polynomial() # needs sage.modules x^10 - 15*x^8 + 75*x^6 - 24*x^5 - 165*x^4 + 120*x^3 + 120*x^2 - 160*x + 48 - sage: P.charpoly() # optional - sage.modules + sage: P.charpoly() # needs sage.modules x^10 - 15*x^8 + 75*x^6 - 24*x^5 - 165*x^4 + 120*x^3 + 120*x^2 - 160*x + 48 - sage: P.characteristic_polynomial(laplacian=True) # optional - sage.modules + sage: P.characteristic_polynomial(laplacian=True) # needs sage.modules x^10 - 30*x^9 + 390*x^8 - 2880*x^7 + 13305*x^6 - 39882*x^5 + 77640*x^4 - 94800*x^3 + 66000*x^2 - 20000*x """ @@ -22175,7 +22187,7 @@ def eigenvectors(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.eigenvectors() # optional - sage.modules + sage: P.eigenvectors() # needs sage.modules [(3, [ (1, 1, 1, 1, 1, 1, 1, 1, 1, 1) ], 1), (-2, [ @@ -22195,7 +22207,7 @@ def eigenvectors(self, laplacian=False): graph is regular. However, since the output also contains the eigenvalues, the two outputs are slightly different:: - sage: P.eigenvectors(laplacian=True) # optional - sage.modules + sage: P.eigenvectors(laplacian=True) # needs sage.modules [(0, [ (1, 1, 1, 1, 1, 1, 1, 1, 1, 1) ], 1), (5, [ @@ -22214,7 +22226,7 @@ def eigenvectors(self, laplacian=False): :: sage: C = graphs.CycleGraph(8) - sage: C.eigenvectors() # optional - sage.modules + sage: C.eigenvectors() # needs sage.modules [(2, [ (1, 1, 1, 1, 1, 1, 1, 1) @@ -22244,7 +22256,7 @@ def eigenvectors(self, laplacian=False): graph eigenvalues were being dropped. For a 3-cycle, we have:: sage: T = DiGraph({0:[1], 1:[2], 2:[0]}) - sage: T.eigenvectors() # optional - sage.modules + sage: T.eigenvectors() # needs sage.modules [(1, [ (1, 1, 1) @@ -22285,7 +22297,7 @@ def eigenspaces(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.eigenspaces() # optional - sage.modules + sage: P.eigenspaces() # needs sage.modules [ (3, Vector space of degree 10 and dimension 1 over Rational Field User basis matrix: @@ -22309,7 +22321,7 @@ def eigenspaces(self, laplacian=False): graph is regular. However, since the output also contains the eigenvalues, the two outputs are slightly different:: - sage: P.eigenspaces(laplacian=True) # optional - sage.modules + sage: P.eigenspaces(laplacian=True) # needs sage.modules [ (0, Vector space of degree 10 and dimension 1 over Rational Field User basis matrix: @@ -22334,7 +22346,7 @@ def eigenspaces(self, laplacian=False): corresponding eigenspace:: sage: C = graphs.CycleGraph(8) - sage: C.eigenspaces() # optional - sage.modules + sage: C.eigenspaces() # needs sage.modules [ (2, Vector space of degree 8 and dimension 1 over Rational Field User basis matrix: @@ -22357,7 +22369,7 @@ def eigenspaces(self, laplacian=False): we have:: sage: T = DiGraph({0: [1], 1: [2], 2: [0]}) - sage: T.eigenspaces() # optional - sage.modules + sage: T.eigenspaces() # needs sage.modules [ (1, Vector space of degree 3 and dimension 1 over Rational Field User basis matrix: @@ -22435,7 +22447,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c EXAMPLES:: sage: G = graphs.PathGraph(3) - sage: G.am() # optional - sage.modules + sage: G.am() # needs sage.modules [0 1 0] [1 0 1] [0 1 0] @@ -22443,7 +22455,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a dictionary. Note that the dictionary does not define the new label of vertex `0`:: - sage: G.relabel({1:2,2:1}, inplace=False).am() # optional - sage.modules + sage: G.relabel({1:2,2:1}, inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22453,7 +22465,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c vertices have an image can require some time, and this feature can be disabled (at your own risk):: - sage: G.relabel({1:2,2:1}, inplace=False, # optional - sage.modules + sage: G.relabel({1:2,2:1}, inplace=False, # needs sage.modules ....: complete_partial_function=False).am() Traceback (most recent call last): ... @@ -22461,14 +22473,14 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a list:: - sage: G.relabel([0,2,1], inplace=False).am() # optional - sage.modules + sage: G.relabel([0,2,1], inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] Relabeling using an iterable:: - sage: G.relabel(iter((0,2,1)), inplace=False).am() # optional - sage.modules + sage: G.relabel(iter((0,2,1)), inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22476,10 +22488,10 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a Sage permutation:: sage: G = graphs.PathGraph(3) - sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # optional - sage.groups - sage: S = SymmetricGroup(3) # optional - sage.groups - sage: gamma = S('(1,2)') # optional - sage.groups - sage: G.relabel(gamma, inplace=False).am() # optional - sage.groups sage.modules + sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # needs sage.groups + sage: S = SymmetricGroup(3) # needs sage.groups + sage: gamma = S('(1,2)') # needs sage.groups + sage: G.relabel(gamma, inplace=False).am() # needs sage.groups sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22762,7 +22774,7 @@ def is_equitable(self, partition, quotient_matrix=False): False sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]]) True - sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]], quotient_matrix=True) # optional - sage.modules + sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]], quotient_matrix=True) # needs sage.modules [1 2 0] [1 0 2] [0 2 1] @@ -22948,7 +22960,7 @@ def automorphism_group(self, partition=None, verbosity=0, sage: graphs_query = GraphQuery(display_cols=['graph6'],num_vertices=4) sage: L = graphs_query.get_graphs_list() - sage: graphs_list.show_graphs(L) # optional - sage.plot + sage: graphs_list.show_graphs(L) # needs sage.plot sage: for g in L: ....: G = g.automorphism_group() ....: G.order(), G.gens() @@ -23051,10 +23063,11 @@ def automorphism_group(self, partition=None, verbosity=0, One can also use the faster algorithm for computing the automorphism group of the graph - bliss:: - sage: G = graphs.HallJankoGraph() # optional - bliss - sage: A1 = G.automorphism_group() # optional - bliss - sage: A2 = G.automorphism_group(algorithm='bliss') # optional - bliss - sage: A1.is_isomorphic(A2) # optional - bliss + sage: # optional - bliss + sage: G = graphs.HallJankoGraph() + sage: A1 = G.automorphism_group() + sage: A2 = G.automorphism_group(algorithm='bliss') + sage: A1.is_isomorphic(A2) True TESTS: @@ -23286,13 +23299,13 @@ def is_vertex_transitive(self, partition=None, verbosity=0, sage: G.is_vertex_transitive() False sage: P = graphs.PetersenGraph() - sage: P.is_vertex_transitive() # optional - sage.groups + sage: P.is_vertex_transitive() # needs sage.groups True sage: D = graphs.DodecahedralGraph() - sage: D.is_vertex_transitive() # optional - sage.groups + sage: D.is_vertex_transitive() # needs sage.groups True - sage: R = graphs.RandomGNP(2000, .01) # optional - networkx - sage: R.is_vertex_transitive() # optional - networkx + sage: R = graphs.RandomGNP(2000, .01) # needs networkx + sage: R.is_vertex_transitive() # needs networkx False """ if partition is None: @@ -23424,21 +23437,21 @@ def is_isomorphic(self, other, certificate=False, verbosity=0, edge_labels=False Graphs:: - sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # optional - sage.groups + sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # needs sage.groups sage: D = graphs.DodecahedralGraph() sage: E = copy(D) - sage: gamma = SymmetricGroup(20).random_element() # optional - sage.groups - sage: E.relabel(gamma) # optional - sage.groups + sage: gamma = SymmetricGroup(20).random_element() # needs sage.groups + sage: E.relabel(gamma) # needs sage.groups sage: D.is_isomorphic(E) True :: sage: D = graphs.DodecahedralGraph() - sage: S = SymmetricGroup(20) # optional - sage.groups - sage: gamma = S.random_element() # optional - sage.groups - sage: E = copy(D) # optional - sage.groups - sage: E.relabel(gamma) # optional - sage.groups + sage: S = SymmetricGroup(20) # needs sage.groups + sage: gamma = S.random_element() # needs sage.groups + sage: E = copy(D) # needs sage.groups + sage: E.relabel(gamma) # needs sage.groups sage: a,b = D.is_isomorphic(E, certificate=True); a True sage: from sage.graphs.generic_graph_pyx import spring_layout_fast @@ -23803,7 +23816,7 @@ class by some canonization function `c`. If `G` and `H` are graphs, sage: P = graphs.PetersenGraph() sage: DP = P.to_directed() - sage: DP.canonical_label(algorithm='sage').adjacency_matrix() # optional - sage.modules + sage: DP.canonical_label(algorithm='sage').adjacency_matrix() # needs sage.modules [0 0 0 0 0 0 0 1 1 1] [0 0 0 0 1 0 1 0 0 1] [0 0 0 1 0 0 1 0 1 0] @@ -24027,15 +24040,15 @@ def is_cayley(self, return_group=False, mapping=False, A Petersen Graph is not a Cayley graph:: sage: g = graphs.PetersenGraph() - sage: g.is_cayley() # optional - sage.groups + sage: g.is_cayley() # needs sage.groups False A Cayley digraph is a Cayley graph:: - sage: C7 = groups.permutation.Cyclic(7) # optional - sage.groups + sage: C7 = groups.permutation.Cyclic(7) # needs sage.groups sage: S = [(1,2,3,4,5,6,7), (1,3,5,7,2,4,6), (1,5,2,6,3,7,4)] - sage: d = C7.cayley_graph(generators=S) # optional - sage.groups - sage: d.is_cayley() # optional - sage.groups + sage: d = C7.cayley_graph(generators=S) # needs sage.groups + sage: d.is_cayley() # needs sage.groups True Graphs with loops and multiedges will have identity and repeated @@ -24309,7 +24322,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix of an undirected 4-cycle. :: sage: G = graphs.CycleGraph(4) - sage: G.katz_matrix(1/20) # optional - sage.modules + sage: G.katz_matrix(1/20) # needs sage.modules [1/198 5/99 1/198 5/99] [ 5/99 1/198 5/99 1/198] [1/198 5/99 1/198 5/99] @@ -24318,7 +24331,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix of an undirected 4-cycle with all entries other than those which correspond to non-edges zeroed out. :: - sage: G.katz_matrix(1/20, True) # optional - sage.modules + sage: G.katz_matrix(1/20, True) # needs sage.modules [ 0 0 1/198 0] [ 0 0 0 1/198] [1/198 0 0 0] @@ -24330,7 +24343,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix in a fan on 6 vertices. :: sage: H = Graph([(0,1),(0,2),(0,3),(0,4),(0,5),(0,6),(1,2),(2,3),(3,4),(4,5)]) - sage: H.katz_matrix(1/10) # optional - sage.modules + sage: H.katz_matrix(1/10) # needs sage.modules [ 169/2256 545/4512 25/188 605/4512 25/188 545/4512 485/4512] [ 545/4512 7081/297792 4355/37224 229/9024 595/37224 4073/297792 109/9024] [ 25/188 4355/37224 172/4653 45/376 125/4653 595/37224 5/376] @@ -24346,22 +24359,23 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): TESTS:: - sage: (graphs.CompleteGraph(4)).katz_matrix(1/4) # optional - sage.modules + sage: # needs sage.modules + sage: (graphs.CompleteGraph(4)).katz_matrix(1/4) [3/5 4/5 4/5 4/5] [4/5 3/5 4/5 4/5] [4/5 4/5 3/5 4/5] [4/5 4/5 4/5 3/5] - sage: (graphs.CompleteGraph(4)).katz_matrix(1/4, nonedgesonly=True) # optional - sage.modules + sage: (graphs.CompleteGraph(4)).katz_matrix(1/4, nonedgesonly=True) [0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0] - sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=False) # optional - sage.modules + sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=False) [15/209 60/209 16/209 4/209] [60/209 31/209 64/209 16/209] [16/209 64/209 31/209 60/209] [ 4/209 16/209 60/209 15/209] - sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=True) # optional - sage.modules + sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=True) [ 0 0 16/209 4/209] [ 0 0 0 16/209] [16/209 0 0 0] @@ -24426,7 +24440,7 @@ def katz_centrality(self, alpha, u=None): all 4 vertices have the same centrality) :: sage: G = graphs.CycleGraph(4) - sage: G.katz_centrality(1/20) # optional - sage.modules + sage: G.katz_centrality(1/20) # needs sage.modules {0: 1/9, 1: 1/9, 2: 1/9, 3: 1/9} Note that in the below example the nodes having indegree `0` also have @@ -24435,7 +24449,7 @@ def katz_centrality(self, alpha, u=None): sage: G = DiGraph({1: [10], 2:[10,11], 3:[10,11], 4:[], 5:[11, 4], 6:[11], ....: 7:[10,11], 8:[10,11], 9:[10], 10:[11, 5, 8], 11:[6]}) - sage: G.katz_centrality(.85) # rel tol 1e-14 # optional - sage.modules + sage: G.katz_centrality(.85) # rel tol 1e-14 # needs sage.modules {1: 0.000000000000000, 2: 0.000000000000000, 3: 0.000000000000000, @@ -24456,15 +24470,16 @@ def katz_centrality(self, alpha, u=None): TESTS:: - sage: graphs.PathGraph(3).katz_centrality(1/20) # optional - sage.modules + sage: # needs sage.modules + sage: graphs.PathGraph(3).katz_centrality(1/20) {0: 11/199, 1: 21/199, 2: 11/199} - sage: graphs.PathGraph(4).katz_centrality(1/20) # optional - sage.modules + sage: graphs.PathGraph(4).katz_centrality(1/20) {0: 21/379, 1: 41/379, 2: 41/379, 3: 21/379} - sage: graphs.PathGraph(3).katz_centrality(1/20,2) # optional - sage.modules + sage: graphs.PathGraph(3).katz_centrality(1/20,2) 11/199 - sage: graphs.PathGraph(4).katz_centrality(1/20,3) # optional - sage.modules + sage: graphs.PathGraph(4).katz_centrality(1/20,3) 21/379 - sage: (graphs.PathGraph(3) + graphs.PathGraph(4)).katz_centrality(1/20) # optional - sage.modules + sage: (graphs.PathGraph(3) + graphs.PathGraph(4)).katz_centrality(1/20) {0: 11/199, 1: 21/199, 2: 11/199, 3: 21/379, 4: 41/379, 5: 41/379, 6: 21/379} """ @@ -24515,29 +24530,29 @@ def edge_polytope(self, backend=None): The EP of a `4`-cycle is a square:: sage: G = graphs.CycleGraph(4) - sage: P = G.edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.edge_polytope(); P # needs sage.geometry.polyhedron A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices The EP of a complete graph on `4` vertices is cross polytope:: sage: G = graphs.CompleteGraph(4) - sage: P = G.edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cross_polytope(3)) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cross_polytope(3)) # needs sage.geometry.polyhedron True The EP of a graph is isomorphic to the subdirect sum of its connected components EPs:: sage: n = randint(3, 6) - sage: G1 = graphs.RandomGNP(n, 0.2) # optional - networkx + sage: G1 = graphs.RandomGNP(n, 0.2) # needs networkx sage: n = randint(3, 6) - sage: G2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = G1.disjoint_union(G2) # optional - networkx - sage: P = G.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P1 = G1.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P2 = G2.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # optional - networkx sage.geometry.polyhedron + sage: G2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = G1.disjoint_union(G2) # needs networkx + sage: P = G.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P1 = G1.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P2 = G2.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # needs networkx sage.geometry.polyhedron True All trees on `n` vertices have isomorphic EPs:: @@ -24545,9 +24560,9 @@ def edge_polytope(self, backend=None): sage: n = randint(4, 10) sage: G1 = graphs.RandomTree(n) sage: G2 = graphs.RandomTree(n) - sage: P1 = G1.edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True However, there are still many different EPs:: @@ -24555,14 +24570,14 @@ def edge_polytope(self, backend=None): sage: len(list(graphs(5))) 34 sage: polys = [] - sage: for G in graphs(5): # optional - sage.geometry.polyhedron + sage: for G in graphs(5): # needs sage.geometry.polyhedron ....: P = G.edge_polytope() ....: for P1 in polys: ....: if P.is_combinatorially_isomorphic(P1): ....: break ....: else: ....: polys.append(P) - sage: len(polys) # optional - sage.geometry.polyhedron + sage: len(polys) # needs sage.geometry.polyhedron 19 TESTS: @@ -24570,7 +24585,7 @@ def edge_polytope(self, backend=None): Obtain the EP with unsortable vertices:: sage: G = Graph([[1, (1, 2)]]) - sage: G.edge_polytope() # optional - sage.geometry.polyhedron + sage: G.edge_polytope() # needs sage.geometry.polyhedron A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex """ from sage.matrix.special import identity_matrix @@ -24601,17 +24616,17 @@ def symmetric_edge_polytope(self, backend=None): The SEP of a `4`-cycle is a cube:: sage: G = graphs.CycleGraph(4) - sage: P = G.symmetric_edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 8 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cube()) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cube()) # needs sage.geometry.polyhedron True The SEP of a complete graph on `4` vertices is a cuboctahedron:: sage: G = graphs.CompleteGraph(4) - sage: P = G.symmetric_edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 12 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cuboctahedron()) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cuboctahedron()) # needs sage.geometry.polyhedron True The SEP of a graph with edges on `n` vertices has dimension `n` @@ -24619,26 +24634,26 @@ def symmetric_edge_polytope(self, backend=None): sage: n = randint(5, 12) sage: G = Graph() - sage: while not G.num_edges(): # optional - networkx + sage: while not G.num_edges(): # needs networkx ....: G = graphs.RandomGNP(n, 0.2) - sage: P = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.ambient_dim() == n # optional - networkx sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.ambient_dim() == n # needs networkx sage.geometry.polyhedron True - sage: P.dim() == n - G.connected_components_number() # optional - networkx sage.geometry.polyhedron + sage: P.dim() == n - G.connected_components_number() # needs networkx sage.geometry.polyhedron True The SEP of a graph is isomorphic to the subdirect sum of its connected components SEP's:: sage: n = randint(3, 6) - sage: G1 = graphs.RandomGNP(n, 0.2) # optional - networkx + sage: G1 = graphs.RandomGNP(n, 0.2) # needs networkx sage: n = randint(3, 6) - sage: G2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = G1.disjoint_union(G2) # optional - networkx - sage: P = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P1 = G1.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # optional - networkx sage.geometry.polyhedron + sage: G2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = G1.disjoint_union(G2) # needs networkx + sage: P = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # needs networkx sage.geometry.polyhedron True All trees on `n` vertices have isomorphic SEPs:: @@ -24646,9 +24661,9 @@ def symmetric_edge_polytope(self, backend=None): sage: n = randint(4, 10) sage: G1 = graphs.RandomTree(n) sage: G2 = graphs.RandomTree(n) - sage: P1 = G1.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True However, there are still many different SEPs:: @@ -24656,7 +24671,7 @@ def symmetric_edge_polytope(self, backend=None): sage: len(list(graphs(5))) 34 sage: polys = [] - sage: for G in graphs(5): # optional - sage.geometry.polyhedron + sage: for G in graphs(5): # needs sage.geometry.polyhedron ....: P = G.symmetric_edge_polytope() ....: for P1 in polys: ....: if P.is_combinatorially_isomorphic(P1): @@ -24675,24 +24690,24 @@ def symmetric_edge_polytope(self, backend=None): sage: G2.add_edges([[0, 7], [7, 3]]) sage: G1.is_isomorphic(G2) False - sage: P1 = G1.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True Apparently, glueing two graphs together on a vertex gives isomorphic SEPs:: sage: n = randint(3, 7) - sage: g1 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: g2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = g1.disjoint_union(g2) # optional - networkx - sage: H = copy(G) # optional - networkx - sage: G.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # optional - networkx - sage: H.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # optional - networkx - sage: PG = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: PH = H.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: PG.is_combinatorially_isomorphic(PH) # optional - networkx sage.geometry.polyhedron + sage: g1 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: g2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = g1.disjoint_union(g2) # needs networkx + sage: H = copy(G) # needs networkx + sage: G.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # needs networkx + sage: H.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # needs networkx + sage: PG = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: PH = H.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: PG.is_combinatorially_isomorphic(PH) # needs networkx sage.geometry.polyhedron True TESTS: @@ -24700,7 +24715,7 @@ def symmetric_edge_polytope(self, backend=None): Obtain the SEP with unsortable vertices:: sage: G = Graph([[1, (1, 2)]]) - sage: G.symmetric_edge_polytope() # optional - sage.geometry.polyhedron + sage: G.symmetric_edge_polytope() # needs sage.geometry.polyhedron A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices """ from itertools import chain @@ -24736,10 +24751,10 @@ def tachyon_vertex_plot(g, bgcolor=(1, 1, 1), sage: G = graphs.TetrahedralGraph() sage: from sage.graphs.generic_graph import tachyon_vertex_plot - sage: T,p = tachyon_vertex_plot(G, pos3d=G.layout(dim=3)) # optional - sage.plot - sage: type(T) # optional - sage.plot + sage: T,p = tachyon_vertex_plot(G, pos3d=G.layout(dim=3)) # needs sage.plot + sage: type(T) # needs sage.plot - sage: type(p) # optional - sage.plot + sage: type(p) # needs sage.plot <... 'dict'> """ assert pos3d is not None diff --git a/src/sage/interfaces/gnuplot.py b/src/sage/interfaces/gnuplot.py index 8ab14c75e1c..b423866fd15 100644 --- a/src/sage/interfaces/gnuplot.py +++ b/src/sage/interfaces/gnuplot.py @@ -159,7 +159,7 @@ def plot3d_parametric(self, f='cos(u)*(3 + v*cos(u/2)), sin(u)*(3 + v*cos(u/2)), EXAMPLES:: - sage: gnuplot.plot3d_parametric('v^2*sin(u), v*cos(u), v*(1-v)') # optional - gnuplot (not tested, since something pops up). + sage: gnuplot.plot3d_parametric('v^2*sin(u), v*cos(u), v*(1-v)') # optional - gnuplot, not tested (since something pops up) """ if title is None: title = str(f) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 144d581b18e..7d18f1e467e 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -66,13 +66,14 @@ sage: x.var() # optional - rpy2 [1] 53.853 - sage: x.sort() # optional - rpy2 + sage: # optional - rpy2 + sage: x.sort() [1] 3.1 5.6 6.4 10.4 21.7 - sage: x.min() # optional - rpy2 + sage: x.min() [1] 3.1 - sage: x.max() # optional - rpy2 + sage: x.max() [1] 21.7 - sage: x # optional - rpy2 + sage: x [1] 10.4 5.6 3.1 6.4 21.7 sage: r(-17).sqrt() # optional - rpy2 @@ -92,42 +93,44 @@ sage: r.seq(length=10, from_=-1, by=.2) # optional - rpy2 [1] -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: x.rep(2) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: x.rep(2) [1] 10.4 5.6 3.1 6.4 21.7 10.4 5.6 3.1 6.4 21.7 - sage: x.rep(times=2) # optional - rpy2 + sage: x.rep(times=2) [1] 10.4 5.6 3.1 6.4 21.7 10.4 5.6 3.1 6.4 21.7 - sage: x.rep(each=2) # optional - rpy2 + sage: x.rep(each=2) [1] 10.4 10.4 5.6 5.6 3.1 3.1 6.4 6.4 21.7 21.7 Missing Values:: - sage: na = r('NA') # optional - rpy2 - sage: z = r([1,2,3,na]) # optional - rpy2 - sage: z # optional - rpy2 + sage: # optional - rpy2 + sage: na = r('NA') + sage: z = r([1,2,3,na]) + sage: z [1] 1 2 3 NA - sage: ind = r.is_na(z) # optional - rpy2 - sage: ind # optional - rpy2 + sage: ind = r.is_na(z) + sage: ind [1] FALSE FALSE FALSE TRUE - sage: zero = r(0) # optional - rpy2 - sage: zero / zero # optional - rpy2 + sage: zero = r(0) + sage: zero / zero [1] NaN - sage: inf = r('Inf') # optional - rpy2 - sage: inf-inf # optional - rpy2 + sage: inf = r('Inf') + sage: inf-inf [1] NaN - sage: r.is_na(inf) # optional - rpy2 + sage: r.is_na(inf) [1] FALSE - sage: r.is_na(inf-inf) # optional - rpy2 + sage: r.is_na(inf-inf) [1] TRUE - sage: r.is_na(zero/zero) # optional - rpy2 + sage: r.is_na(zero/zero) [1] TRUE - sage: r.is_na(na) # optional - rpy2 + sage: r.is_na(na) [1] TRUE - sage: r.is_nan(inf-inf) # optional - rpy2 + sage: r.is_nan(inf-inf) [1] TRUE - sage: r.is_nan(zero/zero) # optional - rpy2 + sage: r.is_nan(zero/zero) [1] TRUE - sage: r.is_nan(na) # optional - rpy2 + sage: r.is_nan(na) [1] FALSE @@ -145,13 +148,14 @@ sage: x['!is.na(self)'] # optional - rpy2 [1] 10.4 5.6 3.1 6.4 21.7 - sage: x = r([10.4,5.6,3.1,6.4,21.7,na]); x # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7,na]); x [1] 10.4 5.6 3.1 6.4 21.7 NA - sage: (x+1)['(!is.na(self)) & self>0'] # optional - rpy2 + sage: (x+1)['(!is.na(self)) & self>0'] [1] 11.4 6.6 4.1 7.4 22.7 - sage: x = r([10.4,-2,3.1,-0.5,21.7,na]); x # optional - rpy2 + sage: x = r([10.4,-2,3.1,-0.5,21.7,na]); x [1] 10.4 -2.0 3.1 -0.5 21.7 NA - sage: (x+1)['(!is.na(self)) & self>0'] # optional - rpy2 + sage: (x+1)['(!is.na(self)) & self>0'] [1] 11.4 4.1 0.5 22.7 Distributions:: @@ -187,16 +191,17 @@ Or you get a dictionary to be able to access all the information:: - sage: rs = r.summary(r.c(1,4,3,4,3,2,5,1)) # optional - rpy2 - sage: rs # optional - rpy2 + sage: # optional - rpy2 + sage: rs = r.summary(r.c(1,4,3,4,3,2,5,1)) + sage: rs Min. 1st Qu. Median Mean 3rd Qu. Max. 1.000 1.750 3.000 2.875 4.000 5.000 - sage: d = rs._sage_() # optional - rpy2 - sage: d['DATA'] # optional - rpy2 + sage: d = rs._sage_() + sage: d['DATA'] [1, 1.75, 3, 2.875, 4, 5] - sage: d['_Names'] # optional - rpy2 + sage: d['_Names'] ['Min.', '1st Qu.', 'Median', 'Mean', '3rd Qu.', 'Max.'] - sage: d['_r_class'] # optional - rpy2 + sage: d['_r_class'] ['summaryDefault', 'table'] It is also possible to access the plotting capabilities of R @@ -346,13 +351,14 @@ def _setup_r_to_sage_converter(): The conversion can handle "not a number", infinity, imaginary values and missing values:: - sage: r(-17).sqrt().sage() # optional - rpy2 + sage: # optional - rpy2 + sage: r(-17).sqrt().sage() nan - sage: r('-17+0i').sqrt().sage() # optional - rpy2 + sage: r('-17+0i').sqrt().sage() 4.123105625617661j - sage: r('NA').sage() # optional - rpy2 + sage: r('NA').sage() NA - sage: inf = r('Inf'); inf.sage() # optional - rpy2 + sage: inf = r('Inf'); inf.sage() inf Character Vectors are represented by regular python arrays:: @@ -504,30 +510,33 @@ def _lazy_init(self): Initialization happens on eval:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: my_r(42) # indirect doctest # optional - rpy2 + sage: my_r(42) # indirect doctest [1] 42 - sage: my_r._initialized # optional - rpy2 + sage: my_r._initialized True And on package import:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: my_r.library('grid') # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: my_r.library('grid') + sage: my_r._initialized True And when fetching help pages:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: _ = my_r.help('c') # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: _ = my_r.help('c') + sage: my_r._initialized True """ if not self._initialized: @@ -611,15 +620,16 @@ def png(self, *args, **kwds): EXAMPLES:: - sage: filename = tmp_filename() + '.png' # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: x = r([1,2,3]) # optional - rpy2 - sage: y = r([4,5,6]) # optional - rpy2 - sage: r.plot(x,y) # This saves to filename, but is not viewable from command line; optional -- rgraphics # optional - rpy2 + sage: x = r([1,2,3]) + sage: y = r([4,5,6]) + sage: r.plot(x,y) # optional - rgraphics null device 1 - sage: import os; os.unlink(filename) # We remove the file for doctesting; optional -- rgraphics # optional - rpy2 + sage: import os; os.unlink(filename) # optional - rgraphics We want to make sure that we actually can view R graphics, which happens differently on different platforms:: @@ -743,12 +753,13 @@ def read(self, filename): EXAMPLES:: - sage: filename = tmp_filename() # optional - rpy2 - sage: f = open(filename, 'w') # optional - rpy2 - sage: _ = f.write('a <- 2+2\n') # optional - rpy2 - sage: f.close() # optional - rpy2 - sage: r.read(filename) # optional - rpy2 - sage: r.get('a') # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + sage: f = open(filename, 'w') + sage: _ = f.write('a <- 2+2\n') + sage: f.close() + sage: r.read(filename) + sage: r.get('a') '[1] 4' """ self.eval( self._read_in_file_command(filename) ) @@ -804,12 +815,13 @@ def version(self): EXAMPLES:: - sage: r.version() # not tested # optional - rpy2 + sage: # optional - rpy2 + sage: r.version() # not tested ((3, 0, 1), 'R version 3.0.1 (2013-05-16)') - sage: rint, rstr = r.version() # optional - rpy2 - sage: rint[0] >= 3 # optional - rpy2 + sage: rint, rstr = r.version() + sage: rint[0] >= 3 True - sage: rstr.startswith('R version') # optional - rpy2 + sage: rstr.startswith('R version') True """ major_re = re.compile(r'^major\s*(\d.*?)$', re.M) @@ -1271,15 +1283,16 @@ def plot(self, *args, **kwds): the output device to that file. If this is done in the notebook, it must be done in the same cell as the plot itself:: - sage: filename = tmp_filename() + '.png' # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # Note the double quotes in single quotes!; optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: x = r([1,2,3]) # optional - rpy2 - sage: y = r([4,5,6]) # optional - rpy2 - sage: r.plot(x,y) # optional -- rgraphics # optional - rpy2 + sage: x = r([1,2,3]) + sage: y = r([4,5,6]) + sage: r.plot(x,y) # optional - rgraphics null device 1 - sage: import os; os.unlink(filename) # For doctesting, we remove the file; optional -- rgraphics # optional - rpy2 + sage: import os; os.unlink(filename) # optional - rgraphics Please note that for more extensive use of R's plotting capabilities (such as the lattices package), it is advisable @@ -1287,23 +1300,25 @@ def plot(self, *args, **kwds): notebook. The following examples are not tested, because they differ depending on operating system:: - sage: r.X11() # not tested - opens interactive device on systems with X11 support # optional - rpy2 - sage: r.quartz() # not tested - opens interactive device on OSX # optional - rpy2 - sage: r.hist("rnorm(100)") # not tested - makes a plot # optional - rpy2 - sage: r.library("lattice") # not tested - loads R lattice plotting package # optional - rpy2 - sage: r.histogram(x = "~ wt | cyl", data="mtcars") # not tested - makes a lattice plot # optional - rpy2 - sage: r.dev_off() # not tested, turns off the interactive viewer # optional - rpy2 + sage: # not tested, optional - rpy2 + sage: r.X11() + sage: r.quartz() + sage: r.hist("rnorm(100)") + sage: r.library("lattice") + sage: r.histogram(x = "~ wt | cyl", data="mtcars") + sage: r.dev_off() In the notebook, one can use r.png() to open the device, but would need to use the following since R lattice graphics do not automatically print away from the command line:: - sage: filename = tmp_filename() + '.png' # Not needed in notebook, used for doctesting # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # filename not needed in notebook, used for doctesting; optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' # Not needed in notebook, used for doctesting + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: r.library("lattice") # optional - rpy2 - sage: r("print(histogram(~wt | cyl, data=mtcars))") # plot should appear; optional -- rgraphics # optional - rpy2 - sage: import os; os.unlink(filename) # We remove the file for doctesting, not needed in notebook; optional -- rgraphics # optional - rpy2 + sage: r.library("lattice") + sage: r("print(histogram(~wt | cyl, data=mtcars))") # optional - rgraphics + sage: import os; os.unlink(filename) # optional - rgraphics """ # We have to define this to override the plot function defined in the # superclass. @@ -1337,14 +1352,15 @@ def _r_to_sage_name(self, s): EXAMPLES:: - sage: f = r._r_to_sage_name # optional - rpy2 - sage: f('t.test') # optional - rpy2 + sage: # optional - rpy2 + sage: f = r._r_to_sage_name + sage: f('t.test') 't_test' - sage: f('attr<-') # optional - rpy2 + sage: f('attr<-') 'attr__' - sage: f('parent.env<-') # optional - rpy2 + sage: f('parent.env<-') 'parent_env__' - sage: f('class') # optional - rpy2 + sage: f('class') 'class_' """ from keyword import iskeyword @@ -1360,16 +1376,17 @@ def _sage_to_r_name(self, s): EXAMPLES:: - sage: f = r._sage_to_r_name # optional - rpy2 - sage: f('t_test') # optional - rpy2 + sage: # optional - rpy2 + sage: f = r._sage_to_r_name + sage: f('t_test') 't.test' - sage: f('attr__') # optional - rpy2 + sage: f('attr__') 'attr<-' - sage: f('parent_env__') # optional - rpy2 + sage: f('parent_env__') 'parent.env<-' - sage: r._r_to_sage_name(f('parent_env__')) # optional - rpy2 + sage: r._r_to_sage_name(f('parent_env__')) 'parent_env__' - sage: f('class_') # optional - rpy2 + sage: f('class_') 'class' """ if len(s) > 1 and s[-2:] == "__": @@ -1451,11 +1468,12 @@ def tilde(self, x): EXAMPLES:: - sage: x = r([1,2,3,4,5]) # optional - rpy2 - sage: y = r([3,5,7,9,11]) # optional - rpy2 - sage: a = r.lm( y.tilde(x) ) # lm( y ~ x ) # optional - rpy2 - sage: d = a._sage_() # optional - rpy2 - sage: d['DATA']['coefficients']['DATA'][1] # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([1,2,3,4,5]) + sage: y = r([3,5,7,9,11]) + sage: a = r.lm( y.tilde(x) ) # lm( y ~ x ) + sage: d = a._sage_() + sage: d['DATA']['coefficients']['DATA'][1] 2 """ par = self.parent() @@ -1505,11 +1523,12 @@ def __getattr__(self, attrname): EXAMPLES:: - sage: x = r([1,2,3]) # optional - rpy2 - sage: length = x.length # optional - rpy2 - sage: type(length) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([1,2,3]) + sage: length = x.length + sage: type(length) - sage: length() # optional - rpy2 + sage: length() [1] 3 """ try: @@ -1535,24 +1554,25 @@ def __getitem__(self, n): EXAMPLES:: - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: x[0] # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: x[0] numeric(0) - sage: x[1] # optional - rpy2 + sage: x[1] [1] 10.4 - sage: x[-1] # optional - rpy2 + sage: x[-1] [1] 5.6 3.1 6.4 21.7 - sage: x[-2] # optional - rpy2 + sage: x[-2] [1] 10.4 3.1 6.4 21.7 - sage: x[-3] # optional - rpy2 + sage: x[-3] [1] 10.4 5.6 6.4 21.7 - sage: x['c(2,3)'] # optional - rpy2 + sage: x['c(2,3)'] [1] 5.6 3.1 - sage: key = r.c(2,3) # optional - rpy2 - sage: x[key] # optional - rpy2 + sage: key = r.c(2,3) + sage: x[key] [1] 5.6 3.1 - sage: m = r.array('1:3',r.c(2,4,2)) # optional - rpy2 - sage: m # optional - rpy2 + sage: m = r.array('1:3',r.c(2,4,2)) + sage: m , , 1 [,1] [,2] [,3] [,4] [1,] 1 3 2 1 @@ -1561,9 +1581,9 @@ def __getitem__(self, n): [,1] [,2] [,3] [,4] [1,] 3 2 1 3 [2,] 1 3 2 1 - sage: m[1,2,2] # optional - rpy2 + sage: m[1,2,2] [1] 2 - sage: m[1,r.c(1,2),1] # optional - rpy2 + sage: m[1,r.c(1,2),1] [1] 1 3 """ P = self._check_valid() @@ -1593,15 +1613,16 @@ def __bool__(self): EXAMPLES:: - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: bool(x) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: bool(x) True - sage: y = r([0,0,0,0]) # optional - rpy2 - sage: bool(y) # optional - rpy2 + sage: y = r([0,0,0,0]) + sage: bool(y) False - sage: bool(r(0)) # optional - rpy2 + sage: bool(r(0)) False - sage: bool(r(1)) # optional - rpy2 + sage: bool(r(1)) True """ return "FALSE" in repr(self == 0) @@ -2049,12 +2070,13 @@ def r_version(): EXAMPLES:: - sage: r_version() # not tested # optional - rpy2 + sage: # optional - rpy2 + sage: r_version() # not tested ((3, 0, 1), 'R version 3.0.1 (2013-05-16)') - sage: rint, rstr = r_version() # optional - rpy2 - sage: rint[0] >= 3 # optional - rpy2 + sage: rint, rstr = r_version() + sage: rint[0] >= 3 True - sage: rstr.startswith('R version') # optional - rpy2 + sage: rstr.startswith('R version') True """ return r.version() diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index fc2f53081e3..411e7cb6bca 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -175,13 +175,14 @@ You can launch web-pages attached to the links:: - sage: K.diagram() # not tested + sage: # not tested + sage: K.diagram() True - sage: L.diagram(single=True) # not tested + sage: L.diagram(single=True) True - sage: L.knot_atlas_webpage() # not tested + sage: L.knot_atlas_webpage() True - sage: K.knotilus_webpage() # not tested + sage: K.knotilus_webpage() True and the description web-pages of the properties:: diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index cd42519c5ec..3ed2476fe9a 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -156,35 +156,36 @@ tikzpicture code generated by Sage from some polyhedron:: sage: from sage.misc.latex_standalone import TikzPicture - sage: V = [[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]] - sage: P = Polyhedron(vertices=V).polar() - sage: s = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') - sage: t = TikzPicture(s) + sage: V = [[1,0,1], [1,0,0], [1,1,0], [0,0,-1], + ....: [0,1,0], [-1,0,0], [0,1,1], [0,0,1], [0,-1,0]] + sage: P = Polyhedron(vertices=V).polar() # needs sage.geometry.polyhedron + sage: s1 = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') # needs sage.geometry.polyhedron sage.plot + sage: t1 = TikzPicture(s1) # needs sage.geometry.polyhedron sage.plot Open the image in a viewer (the returned value is a string giving the absolute path to the file in some temporary directory):: - sage: path_to_file = t.pdf() # not tested + sage: path_to_file = t1.pdf() # not tested # needs sage.geometry.polyhedron sage.plot Instead, you may save a pdf of the tikzpicture into a file of your choice (but this does not open the viewer):: - sage: _ = t.pdf('tikz_polytope.pdf') # not tested + sage: _ = t1.pdf('tikz_polytope.pdf') # not tested # needs sage.geometry.polyhedron sage.plot Opening the image in a viewer can be turned off:: - sage: _ = t.pdf(view=False) # long time (2s) # optional latex + sage: _ = t1.pdf(view=False) # long time (2s), optional - latex # needs sage.geometry.polyhedron sage.plot The same can be done with png format (translated from pdf with convert command which needs the installation of imagemagick):: - sage: _ = t.png(view=False) # long time (2s) # optional latex imagemagick + sage: _ = t1.png(view=False) # long time (2s), optional - imagemagick latex, needs sage.geometry.polyhedron sage.plot The string representation gives the header (5 lines) and tail (5 lines) of the tikzpicture. In Jupyter, it will instead use rich representation and show the image directly below the cell in png or svg format:: - sage: t + sage: t1 # needs sage.geometry.polyhedron sage.plot \documentclass[tikz]{standalone} \begin{document} \begin{tikzpicture}% @@ -201,21 +202,24 @@ Use ``print(t)`` to see the complete content of the file:: - sage: print(t) # not tested + sage: print(t1) # not tested # needs sage.geometry.polyhedron sage.plot Adding a border in the options avoids cropping the vertices of a graph:: - sage: g = graphs.PetersenGraph() # optional - sage.graphs - sage: s = latex(g) # takes 3s but the result is cached # optional latex sage.graphs - sage: t = TikzPicture(s, standalone_config=["border=4mm"], usepackage=['tkz-graph']) # optional latex sage.graphs - sage: _ = t.pdf() # not tested + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: s2 = latex(g) # takes 3s but the result is cached # optional - latex + sage: t2 = TikzPicture(s2, standalone_config=["border=4mm"], # optional - latex + ....: usepackage=['tkz-graph']) + sage: _ = t2.pdf() # not tested The current latex representation of a transducer is a tikzpicture using the tikz library automata. The string can be used as input:: - sage: s = latex(transducers.GrayCode()) # optional sage.combinat - sage: t = TikzPicture(s, usetikzlibrary=['automata']) # optional sage.combinat - sage: _ = t.pdf(view=False) # long time (2s) # optional sage.combinat latex + sage: # needs sage.graphs sage.modules + sage: s3 = latex(transducers.GrayCode()) + sage: t3 = TikzPicture(s3, usetikzlibrary=['automata']) + sage: _ = t3.pdf(view=False) # long time (2s) # optional - latex AUTHORS: @@ -274,7 +278,8 @@ class Standalone(SageObject): :: - sage: t = Standalone(content, standalone_config=["border=4mm"], usepackage=['amsmath']) + sage: t = Standalone(content, standalone_config=["border=4mm"], + ....: usepackage=['amsmath']) sage: t \documentclass{standalone} \standaloneconfig{border=4mm} @@ -645,28 +650,28 @@ def pdf(self, filename=None, view=True, program=None): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.pdf(view=False) # long time (1s) # optional latex + sage: _ = t.pdf(view=False) # long time (1s) # optional - latex Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.pdf(view=False) # not tested + sage: _ = t.pdf(view=False) # not tested A filename may be provided where to save the file, in which case the viewer does not open the file:: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.pdf') - sage: path_to_file = t.pdf(filename) # long time (1s) # optional latex - sage: path_to_file[-4:] # long time (fast) # optional latex + sage: path_to_file = t.pdf(filename) # long time (1s) # optional - latex + sage: path_to_file[-4:] # long time (fast) # optional - latex '.pdf' The filename may contain spaces:: sage: filename = tmp_filename('filename with spaces','.pdf') - sage: path_to_file = t.pdf(filename) # long time (1s) # optional latex + sage: path_to_file = t.pdf(filename) # long time (1s) # optional - latex TESTS: @@ -675,7 +680,7 @@ def pdf(self, filename=None, view=True, program=None): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: s_missing_last_character = s[:-1] sage: t = TikzPicture(s_missing_last_character) - sage: _ = t.pdf() # optional latex + sage: _ = t.pdf() # optional - latex Traceback (most recent call last): ... CalledProcessError: Command '['...latex', '-interaction=nonstopmode', @@ -769,28 +774,28 @@ def dvi(self, filename=None, view=True, program='latex'): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.dvi(view=False) # long time (1s) # optional latex + sage: _ = t.dvi(view=False) # long time (1s) # optional - latex Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.dvi(view=False) # not tested + sage: _ = t.dvi(view=False) # not tested A filename may be provided where to save the file, in which case the viewer does not open the file:: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.dvi') - sage: path_to_file = t.dvi(filename) # long time (1s) # optional latex - sage: path_to_file[-4:] # long time (fast) # optional latex + sage: path_to_file = t.dvi(filename) # long time (1s) # optional - latex + sage: path_to_file[-4:] # long time (fast) # optional - latex '.dvi' The filename may contain spaces:: sage: filename = tmp_filename('filename with spaces','.dvi') - sage: path_to_file = t.dvi(filename) # long time (1s) # optional latex + sage: path_to_file = t.dvi(filename) # long time (1s) # optional - latex TESTS: @@ -799,7 +804,7 @@ def dvi(self, filename=None, view=True, program='latex'): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: s_missing_last_character = s[:-1] sage: t = TikzPicture(s_missing_last_character) - sage: _ = t.dvi() # optional latex + sage: _ = t.dvi() # optional - latex Traceback (most recent call last): ... CalledProcessError: Command '['latex', '-interaction=nonstopmode', @@ -897,21 +902,21 @@ def png(self, filename=None, density=150, view=True): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.png(view=False) # long time (1s) # optional latex imagemagick + sage: _ = t.png(view=False) # long time (1s) # optional - latex imagemagick Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.png(view=False) # not tested + sage: _ = t.png(view=False) # not tested :: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.png') - sage: path_to_file = t.png(filename) # long time (1s) # optional latex imagemagick - sage: path_to_file[-4:] # long time (fast) # optional latex imagemagick + sage: path_to_file = t.png(filename) # long time (1s) # optional - latex imagemagick + sage: path_to_file[-4:] # long time (fast) # optional - latex imagemagick '.png' """ @@ -999,11 +1004,13 @@ def svg(self, filename=None, view=True, program='pdftocairo'): sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp', '.svg') - sage: path_to_file = t.svg(filename, program='pdf2svg') # long time (1s) # optional latex pdf2svg - sage: path_to_file[-4:] # long time (fast) # optional latex pdf2svg + sage: path_to_file = t.svg(filename, # long time (1s) # optional - latex pdf2svg + ....: program='pdf2svg') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdf2svg '.svg' - sage: path_to_file = t.svg(filename, program='pdftocairo') # long time (1s) # optional latex pdftocairo - sage: path_to_file[-4:] # long time (fast) # optional latex pdftocairo + sage: path_to_file = t.svg(filename, # long time (1s) # optional - latex pdftocairo + ....: program='pdftocairo') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdftocairo '.svg' """ @@ -1099,11 +1106,13 @@ def eps(self, filename=None, view=True, program='dvips'): sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp', '.eps') - sage: path_to_file = t.eps(filename, program='dvips') # long time (1s) # optional latex dvips - sage: path_to_file[-4:] # long time (fast) # optional latex dvips + sage: path_to_file = t.eps(filename, # long time (1s) # optional - latex dvips + ....: program='dvips') + sage: path_to_file[-4:] # long time (fast) # optional - latex dvips '.eps' - sage: path_to_file = t.eps(filename, program='pdftocairo') # long time (1s) # optional latex pdftocairo - sage: path_to_file[-4:] # long time (fast) # optional latex pdftocairo + sage: path_to_file = t.eps(filename, # long time (1s) # optional - latex pdftocairo + ....: program='pdftocairo') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdftocairo '.eps' TESTS: @@ -1278,9 +1287,9 @@ def save(self, filename, **kwds): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') sage: filename = tmp_filename('temp','.pdf') - sage: t.save(filename) # long time (1s) # optional latex + sage: t.save(filename) # long time (1s) # optional - latex sage: filename = tmp_filename('temp','.eps') - sage: t.save(filename) # long time (1s) # optional latex dvips + sage: t.save(filename) # long time (1s) # optional - latex dvips """ ext = os.path.splitext(filename)[1].lower() @@ -1344,19 +1353,22 @@ class TikzPicture(Standalone): below methods return a string providing the path to the filename, which is by default in a temporary folder:: - sage: _ = t.pdf() # not tested - sage: _ = t.png() # not tested - sage: _ = t.svg() # not tested - sage: _ = t.tex() # not tested - sage: _ = t.pdf(filename='abc.pdf') # not tested + sage: # not tested + sage: _ = t.pdf() + sage: _ = t.png() + sage: _ = t.svg() + sage: _ = t.tex() + sage: _ = t.pdf(filename='abc.pdf') Here we create a tikzpicture for the latex representation of a graph. This is using tkz-graph tex library:: - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: s = latex(g) # optional sage.graphs latex - sage: t = TikzPicture(s, standalone_config=["border=4mm"], usepackage=['tkz-graph']) # optional sage.graphs latex - sage: _ = t.pdf(view=False) # long time (2s) # optional - sage.graphs latex latex_package_tkz_graph + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: s = latex(g) # optional - latex + sage: t = TikzPicture(s, standalone_config=["border=4mm"], # optional - latex + ....: usepackage=['tkz-graph']) + sage: _ = t.pdf(view=False) # long time (2s), optional - latex latex_package_tkz_graph Here are standalone configurations, packages, tikz libraries and macros that can be set:: @@ -1371,7 +1383,7 @@ class TikzPicture(Standalone): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s, standalone_config=options, usepackage=usepackage, ....: usetikzlibrary=tikzlib, macros=macros) - sage: _ = t.pdf(view=False) # long time (2s) # optional latex + sage: _ = t.pdf(view=False) # long time (2s), optional - latex """ def __init__(self, content, standalone_config=None, usepackage=None, usetikzlibrary=None, macros=None, use_sage_preamble=False): @@ -1453,30 +1465,34 @@ def from_dot_string(cls, dotdata, prog='dot'): EXAMPLES:: + sage: # needs sage.graphs sage: from sage.misc.latex_standalone import TikzPicture - sage: G = graphs.PetersenGraph() # optional sage.graphs - sage: dotdata = G.graphviz_string() # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: G = graphs.PetersenGraph() + sage: dotdata = G.graphviz_string() + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: - sage: dotdata = G.graphviz_string(labels='latex') # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.graphs + sage: dotdata = G.graphviz_string(labels='latex') + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: + sage: # needs sage.combinat sage.graphs sage.groups sage: W = CoxeterGroup(["A",2]) - sage: G = W.cayley_graph() # optional sage.graphs - sage: dotdata = G.graphviz_string() # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: G = W.cayley_graph() + sage: dotdata = G.graphviz_string() + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: - sage: dotdata = G.graphviz_string(labels='latex') # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.combinat sage.graphs sage.groups + sage: dotdata = G.graphviz_string(labels='latex') + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested """ @@ -1538,9 +1554,10 @@ def from_graph(cls, graph, merge_multiedges=True, EXAMPLES:: + sage: # needs sage.graphs sage: from sage.misc.latex_standalone import TikzPicture - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: tikz = TikzPicture.from_graph(g) # optional sage.graphs dot2tex graphviz + sage: g = graphs.PetersenGraph() + sage: tikz = TikzPicture.from_graph(g) # optional - dot2tex graphviz doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. @@ -1548,28 +1565,33 @@ def from_graph(cls, graph, merge_multiedges=True, Using ``prog``:: - sage: tikz = TikzPicture.from_graph(g, prog='neato', color_by_label=True) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.graphs + sage: tikz = TikzPicture.from_graph(g, prog='neato', # long time (3s), optional - dot2tex graphviz + ....: color_by_label=True) sage: _ = tikz.pdf() # not tested Using ``rankdir``:: - sage: tikz = TikzPicture.from_graph(g, rankdir='right') # optional sage.graphs dot2tex graphviz # long time (3s) + sage: tikz = TikzPicture.from_graph(g, rankdir='right') # long time (3s), optional - dot2tex graphviz, needs sage.graphs sage: _ = tikz.pdf() # not tested Using ``merge_multiedges``:: + sage: # needs sage.graphs sage.modules sage.symbolic sage: alpha = var('alpha') - sage: m = matrix(2,range(4)); m.set_immutable() - sage: G = DiGraph([(0,1,alpha), (0,1,0), (0,2,9), (0,2,m)], multiedges=True) # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, merge_multiedges=True) # optional sage.graphs dot2tex graphviz + sage: m = matrix(2, range(4)); m.set_immutable() + sage: G = DiGraph([(0,1,alpha), (0,1,0), (0,2,9), (0,2,m)], + ....: multiedges=True) + sage: tikz = TikzPicture.from_graph(G, merge_multiedges=True) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested Using ``merge_multiedges`` with ``merge_label_function``:: + sage: # needs sage.graphs sage: fn = lambda L: LatexExpr(','.join(map(str, L))) sage: edges = [(0,1,'a'), (0,1,'b'), (0,2,'c'), (0,2,'d')] - sage: G = DiGraph(edges, multiedges=True) # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, # optional sage.graphs dot2tex graphviz + sage: G = DiGraph(edges, multiedges=True) + sage: tikz = TikzPicture.from_graph(G, # optional - dot2tex graphviz ....: merge_multiedges=True, merge_label_function=fn) sage: _ = tikz.pdf() # not tested @@ -1581,24 +1603,25 @@ def from_graph(cls, graph, merge_multiedges=True, sage: a = S((0,1,3,0,0)) sage: b = S((0,2,4,1,0)) sage: roots = [I] - sage: succ = lambda v:[v*a,v*b,a*v,b*v] + sage: succ = lambda v: [v*a,v*b,a*v,b*v] sage: R = RecursivelyEnumeratedSet(roots, succ) - sage: G = R.to_digraph() # optional sage.graphs - sage: G # optional sage.graphs + sage: G = R.to_digraph() # needs sage.graphs + sage: G # needs sage.graphs Looped multi-digraph on 27 vertices - sage: C = G.strongly_connected_components() # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, # optional sage.graphs dot2tex graphviz + sage: C = G.strongly_connected_components() # needs sage.graphs + sage: tikz = TikzPicture.from_graph(G, # optional - dot2tex graphviz, needs sage.graphs ....: merge_multiedges=False, subgraph_clusters=C) sage: _ = tikz.pdf() # not tested An example coming from ``graphviz_string`` documentation in SageMath:: - sage: f(x) = -1 / x # optional sage.symbolic - sage: g(x) = 1 / (x + 1) # optional sage.symbolic - sage: G = DiGraph() # optional sage.symbolic sage.graphs - sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) # optional sage.symbolic sage.graphs - sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) # optional sage.symbolic sage.graphs - sage: tikz = TikzPicture.from_graph(G) # optional sage.symbolic sage.graphs dot2tex graphviz + sage: # needs sage.graphs sage.symbolic + sage: f(x) = -1 / x + sage: g(x) = 1 / (x + 1) + sage: G = DiGraph() + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) + sage: tikz = TikzPicture.from_graph(G) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested sage: def edge_options(data): ....: u, v, label = data @@ -1608,7 +1631,7 @@ def from_graph(cls, graph, merge_multiedges=True, ....: if (u,v) == (1, -1): options["label_style"] = "latex" ....: if (u,v) == (1, 1/2): options["dir"] = "back" ....: return options - sage: tikz = TikzPicture.from_graph(G, edge_options=edge_options) # optional sage.symbolic sage.graphs dot2tex graphviz + sage: tikz = TikzPicture.from_graph(G, edge_options=edge_options) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested """ @@ -1668,20 +1691,23 @@ def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, EXAMPLES:: sage: from sage.misc.latex_standalone import TikzPicture - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: tikz = TikzPicture.from_graph_with_pos(g) # optional sage.graphs + + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: tikz = TikzPicture.from_graph_with_pos(g) doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. :: + sage: # needs sage.graphs sage: edges = [(0,0,'a'),(0,1,'b'),(0,1,'c')] sage: kwds = dict(format='list_of_edges', loops=True, multiedges=True) - sage: G = DiGraph(edges, **kwds) # optional sage.graphs - sage: G.set_pos({0:(0,0), 1:(1,0)}) # optional sage.graphs - sage: f = lambda label:','.join(label) # optional sage.graphs - sage: TikzPicture.from_graph_with_pos(G, merge_label_function=f) # optional sage.graphs + sage: G = DiGraph(edges, **kwds) + sage: G.set_pos({0:(0,0), 1:(1,0)}) + sage: f = lambda label:','.join(label) + sage: TikzPicture.from_graph_with_pos(G, merge_label_function=f) \documentclass[tikz]{standalone} \standaloneconfig{border=4mm} \begin{document} @@ -1699,10 +1725,11 @@ def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, TESTS:: + sage: # needs sage.graphs sage: edges = [(0,0,'a'),(0,1,'b'),(0,1,'c')] sage: kwds = dict(format='list_of_edges', loops=True, multiedges=True) - sage: G = DiGraph(edges, **kwds) # optional sage.graphs - sage: TikzPicture.from_graph_with_pos(G) # optional sage.graphs + sage: G = DiGraph(edges, **kwds) + sage: TikzPicture.from_graph_with_pos(G) Traceback (most recent call last): ... ValueError: vertex positions need to be set first @@ -1796,21 +1823,25 @@ def from_poset(cls, poset, **kwds): EXAMPLES:: sage: from sage.misc.latex_standalone import TikzPicture - sage: P = posets.PentagonPoset() # optional sage.combinat - sage: tikz = TikzPicture.from_poset(P) # optional sage.combinat dot2tex graphviz + + sage: # needs sage.graphs sage.modules + sage: P = posets.PentagonPoset() + sage: tikz = TikzPicture.from_poset(P) # optional - dot2tex graphviz doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. :: - sage: tikz = TikzPicture.from_poset(P, prog='neato', color_by_label=True) # optional sage.combinat dot2tex # long time (3s) + sage: tikz = TikzPicture.from_poset(P, prog='neato', # long time (3s), optional - dot2tex, needs sage.graphs sage.modules + ....: color_by_label=True) :: - sage: P = posets.SymmetricGroupWeakOrderPoset(4) # optional sage.combinat - sage: tikz = TikzPicture.from_poset(P) # optional sage.combinat dot2tex graphviz # long time (4s) - sage: tikz = TikzPicture.from_poset(P, prog='neato') # optional sage.combinat dot2tex graphviz # long time (4s) + sage: # needs sage.graphs + sage: P = posets.SymmetricGroupWeakOrderPoset(4) + sage: tikz = TikzPicture.from_poset(P) # long time (4s), optional - dot2tex graphviz + sage: tikz = TikzPicture.from_poset(P, prog='neato') # long time (4s), optional - dot2tex graphviz """ graph = poset.hasse_diagram() return cls.from_graph(graph, **kwds) diff --git a/src/sage/misc/profiler.py b/src/sage/misc/profiler.py index 48c0d0cbbea..7db5d9d6eec 100644 --- a/src/sage/misc/profiler.py +++ b/src/sage/misc/profiler.py @@ -42,11 +42,12 @@ class Profiler: You can create a checkpoints without a string; ``Profiler`` will use the source code instead:: - sage: p() # not tested - sage: y = factor(25) # not tested - sage: p("last step") # not tested - sage: z = factor(35) # not tested - sage: p() # not tested + sage: # not tested + sage: p() + sage: y = factor(25) + sage: p("last step") + sage: z = factor(35) + sage: p() This will give a nice list of timings between checkpoints:: diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 5a69dbcb0cc..78a6e1fe3d3 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -41,6 +41,7 @@ # **************************************************************************** import os import re +import shutil import sys import pydoc from sage.misc.temporary_file import tmp_dir @@ -589,6 +590,35 @@ def process_mathtt(s): return s +def process_optional_doctest_tags(s): + r""" + Remove ``# optional/needs`` doctest tags for present features from docstring ``s``. + + EXAMPLES: + + sage: from sage.misc.sagedoc import process_optional_doctest_tags + sage: process_optional_doctest_tags("sage: # needs sage.rings.finite_rings\nsage: K. = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2") # needs sage.rings.finite_rings + "sage: K. = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2" + """ + import io + from sage.doctest.external import available_software + from sage.doctest.parsing import parse_optional_tags, update_optional_tags + + start = 0 + with io.StringIO() as output: + for m in re.finditer('( *sage: *.*#.*)\n', s): + output.write(s[start:m.start(0)]) + line = m.group(1) + tags = [tag for tag in parse_optional_tags(line) + if tag not in available_software] + line = update_optional_tags(line, tags=tags) + if not re.fullmatch(' *sage: *', line): + print(line, file=output) + start = m.end(0) + output.write(s[start:]) + return output.getvalue() + + def format(s, embedded=False): r"""noreplace Format Sage documentation ``s`` for viewing with IPython. @@ -768,6 +798,10 @@ def format(s, embedded=False): s = process_mathtt(s) s = process_extlinks(s, embedded=embedded) s = detex(s, embedded=embedded) + + if not embedded: + s = process_optional_doctest_tags(s) + return s diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index cc1cdab8da6..3d05ba7e270 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -89,15 +89,16 @@ cdef class PathAlgebraElement(RingElement): faster, but the timing for path algebra elements has improved by about 20%:: - sage: timeit('pF^5+3*pF^3') # not tested + sage: # not tested + sage: timeit('pF^5+3*pF^3') 1 loops, best of 3: 338 ms per loop - sage: timeit('pP^5+3*pP^3') # not tested + sage: timeit('pP^5+3*pP^3') 100 loops, best of 3: 2.55 ms per loop - sage: timeit('pF2^7') # not tested + sage: timeit('pF2^7') 10000 loops, best of 3: 513 ms per loop - sage: timeit('pL2^7') # not tested + sage: timeit('pL2^7') 125 loops, best of 3: 1.99 ms per loop - sage: timeit('pP2^7') # not tested + sage: timeit('pP2^7') 10000 loops, best of 3: 1.54 ms per loop So, if one is merely interested in basic arithmetic operations for diff --git a/src/sage/repl/user_globals.py b/src/sage/repl/user_globals.py index 92de7d87e0c..73c13c44690 100644 --- a/src/sage/repl/user_globals.py +++ b/src/sage/repl/user_globals.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules r""" User-interface globals diff --git a/src/sage/rings/big_oh.py b/src/sage/rings/big_oh.py index ad43b8fbc84..21938a2256b 100644 --- a/src/sage/rings/big_oh.py +++ b/src/sage/rings/big_oh.py @@ -57,44 +57,45 @@ def O(*x, **kwds): This is also useful to create `p`-adic numbers:: - sage: O(7^6) # optional - sage.rings.padics + sage: O(7^6) # needs sage.rings.padics O(7^6) - sage: 1/3 + O(7^6) # optional - sage.rings.padics + sage: 1/3 + O(7^6) # needs sage.rings.padics 5 + 4*7 + 4*7^2 + 4*7^3 + 4*7^4 + 4*7^5 + O(7^6) It behaves well with respect to adding negative powers of `p`:: - sage: a = O(11^-32); a # optional - sage.rings.padics + sage: a = O(11^-32); a # needs sage.rings.padics O(11^-32) - sage: a.parent() # optional - sage.rings.padics + sage: a.parent() # needs sage.rings.padics 11-adic Field with capped relative precision 20 There are problems if you add a rational with very negative valuation to an `O`-Term:: - sage: 11^-12 + O(11^15) # optional - sage.rings.padics + sage: 11^-12 + O(11^15) # needs sage.rings.padics 11^-12 + O(11^8) The reason that this fails is that the constructor doesn't know the right precision cap to use. If you cast explicitly or use other means of element creation, you can get around this issue:: - sage: K = Qp(11, 30) # optional - sage.rings.padics - sage: K(11^-12) + O(11^15) # optional - sage.rings.padics + sage: # needs sage.rings.padics + sage: K = Qp(11, 30) + sage: K(11^-12) + O(11^15) 11^-12 + O(11^15) - sage: 11^-12 + K(O(11^15)) # optional - sage.rings.padics + sage: 11^-12 + K(O(11^15)) 11^-12 + O(11^15) - sage: K(11^-12, absprec=15) # optional - sage.rings.padics + sage: K(11^-12, absprec=15) 11^-12 + O(11^15) - sage: K(11^-12, 15) # optional - sage.rings.padics + sage: K(11^-12, 15) 11^-12 + O(11^15) We can also work with `asymptotic expansions`_:: - sage: A. = AsymptoticRing(growth_group='QQ^n * n^QQ * log(n)^QQ', # optional - sage.symbolic + sage: A. = AsymptoticRing(growth_group='QQ^n * n^QQ * log(n)^QQ', # needs sage.symbolic ....: coefficient_ring=QQ); A Asymptotic Ring over Rational Field - sage: O(n) # optional - sage.symbolic + sage: O(n) # needs sage.symbolic O(n) Application with Puiseux series:: @@ -108,17 +109,17 @@ def O(*x, **kwds): TESTS:: - sage: var('x, y') # optional - sage.symbolic + sage: var('x, y') # needs sage.symbolic (x, y) - sage: O(x) # optional - sage.symbolic + sage: O(x) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(x) not defined - sage: O(y) # optional - sage.symbolic + sage: O(y) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(y) not defined - sage: O(x, y) # optional - sage.symbolic + sage: O(x, y) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(x, y) not defined diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 14ae02423e4..733fbcbd6a7 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -1265,10 +1265,11 @@ def guess(self, sequence, algorithm='sage'): r""" .. TODO:: - sage: CFiniteSequence(x+x^2+x^3+x^4+x^5+O(x^6)) # not implemented - sage: latex(r) # not implemented + sage: # not implemented + sage: CFiniteSequence(x+x^2+x^3+x^4+x^5+O(x^6)) + sage: latex(r) \big\{a_{n\ge0}\big|a_{n+2}=\sum_{i=0}^{1}c_ia_{n+i}, c=\{1,1\}, a_{n<2}=\{0,0,0,1\}\big\} - sage: r.egf() # not implemented + sage: r.egf() exp(2*x) - sage: r = CFiniteSequence(1/(1-y-x*y), x) # not implemented + sage: r = CFiniteSequence(1/(1-y-x*y), x) """ diff --git a/src/sage/rings/function_field/ideal_polymod.py b/src/sage/rings/function_field/ideal_polymod.py index 936b63b48fe..6735dd6a8fa 100644 --- a/src/sage/rings/function_field/ideal_polymod.py +++ b/src/sage/rings/function_field/ideal_polymod.py @@ -44,10 +44,11 @@ class FunctionFieldIdeal_polymod(FunctionFieldIdeal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.ideal(y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: O.ideal(y) Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x """ def __init__(self, ring, hnf, denominator=1): @@ -56,11 +57,12 @@ def __init__(self, ring, hnf, denominator=1): TESTS:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdeal.__init__(self, ring) @@ -96,29 +98,31 @@ def __bool__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: I.is_zero() # optional - sage.rings.finite_rings + sage: I.is_zero() False - sage: J = 0*I; J # optional - sage.rings.finite_rings + sage: J = 0*I; J Zero ideal of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: J.is_zero() # optional - sage.rings.finite_rings + sage: J.is_zero() True - sage: K. = FunctionField(GF(2)); _.=K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _.=K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I.is_zero() # optional - sage.rings.finite_rings + sage: I.is_zero() False - sage: J = 0*I; J # optional - sage.rings.finite_rings + sage: J = 0*I; J Zero ideal of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: J.is_zero() # optional - sage.rings.finite_rings + sage: J.is_zero() True """ return self._hnf.nrows() != 0 @@ -129,17 +133,19 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: { I: 2 }[I] == 2 True - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: { I: 2 }[I] == 2 True """ return hash((self._ring, self._hnf, self._denominator)) @@ -150,30 +156,32 @@ def __contains__(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I Ideal (y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: x * y in I # optional - sage.rings.finite_rings + sage: x * y in I True - sage: y / x in I # optional - sage.rings.finite_rings + sage: y / x in I False - sage: y^2 - 2 in I # optional - sage.rings.finite_rings + sage: y^2 - 2 in I False - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: x * y in I # optional - sage.rings.finite_rings + sage: x * y in I True - sage: y / x in I # optional - sage.rings.finite_rings + sage: y / x in I False - sage: y^2 - 2 in I # optional - sage.rings.finite_rings + sage: y^2 - 2 in I False sage: K. = FunctionField(QQ); _. = K[] @@ -219,30 +227,32 @@ def __invert__(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: ~I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I^(-1) # optional - sage.rings.finite_rings + sage: I^(-1) Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: ~I * I # optional - sage.rings.finite_rings + sage: ~I * I Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: ~I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I^(-1) # optional - sage.rings.finite_rings + sage: I^(-1) Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I * I # optional - sage.rings.finite_rings + sage: ~I * I Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x :: @@ -291,28 +301,30 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I == I + I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I == I + I True - sage: I == I * I # optional - sage.rings.finite_rings + sage: I == I * I False :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I == I + I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I == I + I True - sage: I == I * I # optional - sage.rings.finite_rings + sage: I == I * I False - sage: I < I * I # optional - sage.rings.finite_rings + sage: I < I * I True - sage: I > I * I # optional - sage.rings.finite_rings + sage: I > I * I False """ return richcmp((self._denominator, self._hnf), (other._denominator, other._hnf), op) @@ -323,19 +335,21 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I + J Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I + J Ideal (1, y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ ds = self._denominator @@ -350,20 +364,22 @@ def _mul_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I * J Ideal (x^4 + x^2 + x, x*y + x^2) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I * J Ideal ((x + 1)*y + (x^2 + 1)/x) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -401,19 +417,21 @@ def _acted_upon_(self, other, on_left): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: x * I == I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: x * I == I * J True - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: x * I == I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: x * I == I * J True """ from sage.modules.free_module_element import vector @@ -441,12 +459,13 @@ def intersect(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: I.intersect(J) == I * J * (I + J)^-1 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: I.intersect(J) == I * J * (I + J)^-1 True """ @@ -486,10 +505,11 @@ def hnf(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y*(y+1)); I.hnf() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y*(y+1)); I.hnf() [x^6 + x^3 0] [ x^3 + 1 1] @@ -510,13 +530,14 @@ def denominator(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y/(y+1)) # optional - sage.rings.finite_rings - sage: d = I.denominator(); d # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y/(y+1)) + sage: d = I.denominator(); d x^3 - sage: d in O # optional - sage.rings.finite_rings + sage: d in O True :: @@ -540,11 +561,12 @@ def module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.module() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: F. = K.extension(y^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.module() Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: @@ -564,17 +586,19 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens_over_base() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens_over_base() (x^3 + 1, y + x) """ gens, d = self._gens_over_base @@ -588,11 +612,12 @@ def _gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I._gens_over_base # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I._gens_over_base ([x, y], x) """ gens = [] @@ -609,17 +634,19 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^3 + 1, y + x) """ return self.gens_over_base() @@ -634,11 +661,12 @@ def basis_matrix(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.denominator() * I.basis_matrix() == I.hnf() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.denominator() * I.basis_matrix() == I.hnf() True """ d = self.denominator() @@ -654,24 +682,26 @@ def is_integral(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.is_integral() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.is_integral() False - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.is_integral() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.is_integral() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.is_integral() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.is_integral() False - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.is_integral() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.is_integral() True sage: K. = FunctionField(QQ); _. = PolynomialRing(K) @@ -694,29 +724,31 @@ def ideal_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.ideal_below() Traceback (most recent call last): ... TypeError: not an integral ideal - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.ideal_below() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.ideal_below() Ideal (x^3 + x^2 + x) of Maximal order of Rational function field in x over Finite Field of size 2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.ideal_below() Traceback (most recent call last): ... TypeError: not an integral ideal - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.ideal_below() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.ideal_below() Ideal (x^3 + x) of Maximal order of Rational function field in x over Finite Field of size 2 @@ -760,38 +792,40 @@ def norm(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings - sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings - sage: i3 = i1 * i2 # optional - sage.rings.finite_rings - sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: i1 = O.ideal(x) + sage: i2 = O.ideal(y) + sage: i3 = i1 * i2 + sage: i3.norm() == i1.norm() * i2.norm() True - sage: i1.norm() # optional - sage.rings.finite_rings + sage: i1.norm() x^3 - sage: i1.norm() == x ** F.degree() # optional - sage.rings.finite_rings + sage: i1.norm() == x ** F.degree() True - sage: i2.norm() # optional - sage.rings.finite_rings + sage: i2.norm() x^6 + x^4 + x^2 - sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + sage: i2.norm() == y.norm() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings - sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings - sage: i3 = i1 * i2 # optional - sage.rings.finite_rings - sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: i1 = O.ideal(x) + sage: i2 = O.ideal(y) + sage: i3 = i1 * i2 + sage: i3.norm() == i1.norm() * i2.norm() True - sage: i1.norm() # optional - sage.rings.finite_rings + sage: i1.norm() x^2 - sage: i1.norm() == x ** L.degree() # optional - sage.rings.finite_rings + sage: i1.norm() == x ** L.degree() True - sage: i2.norm() # optional - sage.rings.finite_rings + sage: i2.norm() (x^2 + 1)/x - sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + sage: i2.norm() == y.norm() True """ n = 1 @@ -806,18 +840,20 @@ def is_prime(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.is_prime() for f,_ in I.factor()] [True, True] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.is_prime() for f,_ in I.factor()] [True, True] sage: K. = FunctionField(QQ); _. = PolynomialRing(K) @@ -854,21 +890,23 @@ def valuation(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, (1/(x^3 + x^2 + x))*y^2) # optional - sage.rings.finite_rings - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, (1/(x^3 + x^2 + x))*y^2) + sage: I.is_prime() True - sage: J = O.ideal(y) # optional - sage.rings.finite_rings - sage: I.valuation(J) # optional - sage.rings.finite_rings + sage: J = O.ideal(y) + sage: I.valuation(J) 2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.valuation(I) for f,_ in I.factor()] [-1, 2] The method closely follows Algorithm 4.8.17 of [Coh1993]_. @@ -923,20 +961,22 @@ def prime_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.prime_below() for f,_ in I.factor()] [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, Ideal (x^2 + x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.prime_below() for f,_ in I.factor()] [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, Ideal (x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] @@ -956,11 +996,12 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I == I.factor().prod() # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I == I.factor().prod() # indirect doctest True """ O = self.ring() @@ -999,10 +1040,11 @@ class FunctionFieldIdeal_global(FunctionFieldIdeal_polymod): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.ideal(y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: O.ideal(y) Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x """ def __init__(self, ring, hnf, denominator=1): @@ -1011,11 +1053,12 @@ def __init__(self, ring, hnf, denominator=1): TESTS:: - sage: K. = FunctionField(GF(5)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdeal_polymod.__init__(self, ring, hnf, denominator) @@ -1028,18 +1071,19 @@ def __pow__(self, mod): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^7 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: S = I / J # optional - sage.rings.finite_rings - sage: a = S^100 # optional - sage.rings.finite_rings - sage: _ = S.gens_two() # optional - sage.rings.finite_rings - sage: b = S^100 # faster # optional - sage.rings.finite_rings - sage: b == I^100 / J^100 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^7 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: S = I / J + sage: a = S^100 + sage: _ = S.gens_two() + sage: b = S^100 # faster + sage: b == I^100 / J^100 True - sage: b == a # optional - sage.rings.finite_rings + sage: b == a True """ if mod > 2 and self._gens_two_vecs is not None: @@ -1074,17 +1118,19 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^3 + 1, y + x) """ if self._gens_two.is_in_cache(): @@ -1100,25 +1146,27 @@ def gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I # indirect doctest Ideal (y) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: ~I # indirect doctest # optional - sage.rings.finite_rings + sage: ~I # indirect doctest Ideal ((1/(x^6 + x^4 + x^2))*y^2) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I # indirect doctest Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I # indirect doctest # optional - sage.rings.finite_rings + sage: ~I # indirect doctest Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1153,17 +1201,19 @@ def _gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2, x*y, x + y) # optional - sage.rings.finite_rings - sage: I._gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: F. = K.extension(Y^3 + x^3*Y + x) + sage: O = F.maximal_order() + sage: I = O.ideal(x^2, x*y, x + y) + sage: I._gens_two() (x, y) - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y - x) # optional - sage.rings.finite_rings - sage: y.zeros()[0].prime_ideal()._gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: _. = K[] + sage: L. = K.extension(Y - x) + sage: y.zeros()[0].prime_ideal()._gens_two() (x,) """ O = self.ring() @@ -1264,10 +1314,11 @@ class FunctionFieldIdealInfinite_polymod(FunctionFieldIdealInfinite): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.ideal(1/y) Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 """ @@ -1277,11 +1328,12 @@ def __init__(self, ring, ideal): TESTS:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: TestSuite(I).run() """ FunctionFieldIdealInfinite.__init__(self, ring) self._ideal = ideal @@ -1292,17 +1344,19 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: d = { I: 1 } # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: d = { I: 1 } - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: d = { I: 1 } # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: d = { I: 1 } """ return hash((self.ring(), self._ideal)) @@ -1316,15 +1370,16 @@ def __contains__(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: 1/y in I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: 1/y in I True - sage: 1/x in I # optional - sage.rings.finite_rings + sage: 1/x in I False - sage: 1/x^2 in I # optional - sage.rings.finite_rings + sage: 1/x^2 in I True """ F = self.ring().fraction_field() @@ -1341,21 +1396,23 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I + J Ideal (1/x) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I + J Ideal (1/x) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1371,21 +1428,23 @@ def _mul_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J Ideal (1/x^7*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J Ideal (1/x^4*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1397,11 +1456,12 @@ def __pow__(self, n): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: J^3 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: J = Oinf.ideal(1/x) + sage: J^3 Ideal (1/x^3) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 """ @@ -1413,25 +1473,27 @@ def __invert__(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: ~J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: J = Oinf.ideal(y) + sage: ~J Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: J * ~J # optional - sage.rings.finite_rings + sage: J * ~J Ideal (1) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: ~J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: J = Oinf.ideal(y) + sage: ~J Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: J * ~J # optional - sage.rings.finite_rings + sage: J * ~J Ideal (1) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1443,32 +1505,34 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J == J * I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J == J * I True - sage: I + J == J # optional - sage.rings.finite_rings + sage: I + J == J True - sage: I + J == I # optional - sage.rings.finite_rings + sage: I + J == I False - sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + sage: (I < J) == (not J < I) True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J == J * I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J == J * I True - sage: I + J == J # optional - sage.rings.finite_rings + sage: I + J == J True - sage: I + J == I # optional - sage.rings.finite_rings + sage: I + J == I False - sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + sage: (I < J) == (not J < I) True """ return richcmp(self._ideal, other._ideal, op) @@ -1480,11 +1544,12 @@ def _relative_degree(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: [J._relative_degree for J,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: [J._relative_degree for J,_ in I.factor()] [1] """ if not self.is_prime(): @@ -1498,18 +1563,20 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens() (x, y, 1/x^2*y^2) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens() (x, y) """ F = self.ring().fraction_field() @@ -1522,18 +1589,20 @@ def gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_two() (x, y) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_two() (x,) """ F = self.ring().fraction_field() @@ -1546,11 +1615,12 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_over_base() (x, y, 1/x^2*y^2) """ F = self.ring().fraction_field() @@ -1563,11 +1633,12 @@ def ideal_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y^2) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y^2) + sage: I.ideal_below() Ideal (x^3) of Maximal order of Rational function field in x over Finite Field in z2 of size 3^2 """ @@ -1579,30 +1650,32 @@ def is_prime(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^3 - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: I.is_prime() False - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x)^2 - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: I.is_prime() False - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True """ return self._ideal.is_prime() @@ -1614,31 +1687,33 @@ def prime_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^3 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.prime_below() # optional - sage.rings.finite_rings + sage: J.prime_below() Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field in z2 of size 3^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x)^2 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.prime_below() # optional - sage.rings.finite_rings + sage: J.prime_below() Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -1659,11 +1734,12 @@ def valuation(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: [f.valuation(I) for f,_ in I.factor()] [-1] """ if not self.is_prime(): @@ -1677,16 +1753,17 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: f = 1/x # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(f) # optional - sage.rings.finite_rings - sage: I._factor() # optional - sage.rings.finite_rings - [(Ideal (1/x, 1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y - defined by y^3 + x^6 + x^4 + x^2, 1), - (Ideal (1/x, 1/x^2*y + 1) of Maximal infinite order of Function field in y - defined by y^3 + x^6 + x^4 + x^2, 1)] + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: f = 1/x + sage: I = Oinf.ideal(f) + sage: I._factor() + [(Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2, + 1), + (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2, + 1)] """ if self._ideal.is_prime.is_in_cache() and self._ideal.is_prime(): return [(self, 1)] diff --git a/src/sage/rings/homset.py b/src/sage/rings/homset.py index b89ba5a1ed7..6013a6ca28b 100644 --- a/src/sage/rings/homset.py +++ b/src/sage/rings/homset.py @@ -28,9 +28,9 @@ def is_RingHomset(H): True sage: is_RH(ZZ) False - sage: is_RH(Hom(RR, CC)) + sage: is_RH(Hom(RR, CC)) # needs sage.rings.real_mpfr True - sage: is_RH(Hom(FreeModule(ZZ,1), FreeModule(QQ,1))) + sage: is_RH(Hom(FreeModule(ZZ,1), FreeModule(QQ,1))) # needs sage.modules False """ return isinstance(H, RingHomset_generic) @@ -133,26 +133,27 @@ def _element_constructor_(self, x, check=True, base_map=None): You can provide a morphism on the base:: - sage: k = GF(9) # optional - sage.rings.finite_rings - sage: z2 = k.gen() # optional - sage.rings.finite_rings - sage: cc = k.frobenius_endomorphism() # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: H = Hom(R, R) # optional - sage.rings.finite_rings - sage: phi = H([x^2], base_map=cc); phi # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(9) + sage: z2 = k.gen() + sage: cc = k.frobenius_endomorphism() + sage: R. = k[] + sage: H = Hom(R, R) + sage: phi = H([x^2], base_map=cc); phi Ring endomorphism of Univariate Polynomial Ring in x over Finite Field in z2 of size 3^2 Defn: x |--> x^2 with map of base ring - sage: phi(z2 * x) == z2^3 * x^2 # optional - sage.rings.finite_rings + sage: phi(z2 * x) == z2^3 * x^2 True sage: R. = ZZ[] - sage: K. = GF(7^2) # optional - sage.rings.finite_rings - sage: L. = K.extension(x^3 - 3) # optional - sage.rings.finite_rings - sage: phi = L.hom([u^7], base_map=K.frobenius_endomorphism()) # optional - sage.rings.finite_rings - sage: phi(u) == u^7 # optional - sage.rings.finite_rings + sage: K. = GF(7^2) # needs sage.rings.finite_rings + sage: L. = K.extension(x^3 - 3) # needs sage.rings.finite_rings + sage: phi = L.hom([u^7], base_map=K.frobenius_endomorphism()) # needs sage.rings.finite_rings + sage: phi(u) == u^7 # needs sage.rings.finite_rings True - sage: phi(a) == a^7 # optional - sage.rings.finite_rings + sage: phi(a) == a^7 # needs sage.rings.finite_rings True TESTS:: @@ -248,15 +249,15 @@ class RingHomset_quo_ring(RingHomset_generic): EXAMPLES:: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: phi = S.hom([b,a]); phi # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: phi = S.hom([b,a]); phi # needs sage.libs.singular Ring endomorphism of Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) Defn: a |--> b b |--> a - sage: phi(a) # optional - sage.libs.singular + sage: phi(a) # needs sage.libs.singular b - sage: phi(b) # optional - sage.libs.singular + sage: phi(b) # needs sage.libs.singular a TESTS: @@ -266,15 +267,15 @@ class RingHomset_quo_ring(RingHomset_generic): :: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: H = S.Hom(R) # optional - sage.libs.singular - sage: H == loads(dumps(H)) # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: H = S.Hom(R) # needs sage.libs.singular + sage: H == loads(dumps(H)) # needs sage.libs.singular True We test pickling of actual homomorphisms in a quotient:: - sage: phi = S.hom([b,a]) # optional - sage.libs.singular - sage: phi == loads(dumps(phi)) # optional - sage.libs.singular + sage: phi = S.hom([b,a]) # needs sage.libs.singular + sage: phi == loads(dumps(phi)) # needs sage.libs.singular True """ @@ -287,17 +288,17 @@ def _element_constructor_(self, x, base_map=None, check=True): EXAMPLES:: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: H = S.Hom(R) # optional - sage.libs.singular - sage: phi = H([b, a]); phi # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: H = S.Hom(R) # needs sage.libs.singular + sage: phi = H([b, a]); phi # needs sage.libs.singular Ring morphism: From: Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) To: Multivariate Polynomial Ring in x, y over Rational Field Defn: a |--> b b |--> a sage: R2. = PolynomialRing(ZZ, 2) - sage: H2 = Hom(R2, S) # optional - sage.libs.singular - sage: H2(phi) # optional - sage.libs.singular + sage: H2 = Hom(R2, S) # needs sage.libs.singular + sage: H2(phi) # needs sage.libs.singular Composite map: From: Multivariate Polynomial Ring in x, y over Integer Ring To: Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 7bf29415157..099e72c2c58 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -166,11 +166,11 @@ sage: factor(164878) 2 * 7 * 11777 - sage: I.change_ring(P.change_ring(GF(2))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(2))).groebner_basis() # needs sage.rings.finite_rings [x + y + z, y^2 + y, y*z + y, z^2 + 1] - sage: I.change_ring(P.change_ring(GF(7))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(7))).groebner_basis() # needs sage.rings.finite_rings [x - 1, y + 3, z - 2] - sage: I.change_ring(P.change_ring(GF(11777))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(11777))).groebner_basis() # needs sage.rings.finite_rings [x + 5633, y - 3007, z - 2626] The Groebner basis modulo any product of the prime factors is also non-trivial:: @@ -181,7 +181,7 @@ Modulo any other prime the Groebner basis is trivial so there are no other solutions. For example:: - sage: I.change_ring(P.change_ring(GF(3))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(3))).groebner_basis() # needs sage.rings.finite_rings [1] TESTS:: @@ -343,9 +343,9 @@ def _magma_init_(self, magma): EXAMPLES:: - sage: R. = PolynomialRing(GF(127),10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R,4) # indirect doctest # optional - sage.rings.finite_rings - sage: magma(I) # optional - magma # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127),10) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Cyclic(R,4) # indirect doctest # needs sage.rings.finite_rings + sage: magma(I) # optional - magma # needs sage.rings.finite_rings Ideal of Polynomial ring of rank 10 over GF(127) Order: Graded Reverse Lexicographical Variables: a, b, c, d, e, f, g, h, i, j @@ -380,18 +380,20 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) EXAMPLES:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 6) # optional - sage.rings.finite_rings - sage: gb = I.groebner_basis('magma:GroebnerBasis') # indirect doctest; optional - magma sage.rings.finite_rings - sage: len(gb) # optional - magma # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 6) + sage: gb = I.groebner_basis('magma:GroebnerBasis') # optional - magma + sage: len(gb) # optional - magma 45 We may also pass a degree bound to Magma:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 6) # optional - sage.rings.finite_rings - sage: gb = I.groebner_basis('magma:GroebnerBasis', deg_bound=4) # indirect doctest; optional - magma # optional - sage.rings.finite_rings - sage: len(gb) # optional - magma # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 6) + sage: gb = I.groebner_basis('magma:GroebnerBasis', deg_bound=4) # optional - magma + sage: len(gb) # optional - magma 5 """ R = self.ring() @@ -1046,38 +1048,38 @@ def triangular_decomposition(self, algorithm=None, singular=singular_default): EXAMPLES:: - sage: P. = PolynomialRing(QQ, 5, order='lex'); P.rename("P") + sage: P. = PolynomialRing(QQ, 5, order='lex') sage: I = sage.rings.ideal.Cyclic(P) sage: GB = Ideal(I.groebner_basis('libsingular:stdfglm')) sage: GB.triangular_decomposition('singular:triangLfak') - [Ideal (a - 1, b - 1, c - 1, d^2 + 3*d + 1, e + d + 3) of P, - Ideal (a - 1, b - 1, c^2 + 3*c + 1, d + c + 3, e - 1) of P, - Ideal (a - 1, b^2 + 3*b + 1, c + b + 3, d - 1, e - 1) of P, + [Ideal (a - 1, b - 1, c - 1, d^2 + 3*d + 1, e + d + 3) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a - 1, b - 1, c^2 + 3*c + 1, d + c + 3, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a - 1, b^2 + 3*b + 1, c + b + 3, d - 1, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a - 1, b^4 + b^3 + b^2 + b + 1, -c + b^2, -d + b^3, - e + b^3 + b^2 + b + 1) of P, - Ideal (a^2 + 3*a + 1, b - 1, c - 1, d - 1, e + a + 3) of P, - Ideal (a^2 + 3*a + 1, b + a + 3, c - 1, d - 1, e - 1) of P, + e + b^3 + b^2 + b + 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a^2 + 3*a + 1, b - 1, c - 1, d - 1, e + a + 3) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a^2 + 3*a + 1, b + a + 3, c - 1, d - 1, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 - 4*a^3 + 6*a^2 + a + 1, -11*b^2 + 6*b*a^3 - 26*b*a^2 + 41*b*a - 4*b - 8*a^3 + 31*a^2 - 40*a - 24, 11*c + 3*a^3 - 13*a^2 + 26*a - 2, 11*d + 3*a^3 - 13*a^2 + 26*a - 2, - -11*e - 11*b + 6*a^3 - 26*a^2 + 41*a - 4) of P, + -11*e - 11*b + 6*a^3 - 26*a^2 + 41*a - 4) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - 1, c + a^3 + a^2 + a + 1, -d + a^3, -e + a^2) of P, + b - 1, c + a^3 + a^2 + a + 1, -d + a^3, -e + a^2) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - a, c - a, d^2 + 3*d*a + a^2, e + d + 3*a) of P, + b - a, c - a, d^2 + 3*d*a + a^2, e + d + 3*a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - a, c^2 + 3*c*a + a^2, d + c + 3*a, e - a) of P, + b - a, c^2 + 3*c*a + a^2, d + c + 3*a, e - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b^2 + 3*b*a + a^2, c + b + 3*a, d - a, e - a) of P, + b^2 + 3*b*a + a^2, c + b + 3*a, d - a, e - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, b^3 + b^2*a + b^2 + b*a^2 + b*a + b + a^3 + a^2 + a + 1, c + b^2*a^3 + b^2*a^2 + b^2*a + b^2, -d + b^2*a^2 + b^2*a + b^2 + b*a^2 + b*a + a^2, - -e + b^2*a^3 - b*a^2 - b*a - b - a^2 - a) of P, + -e + b^2*a^3 - b*a^2 - b*a - b - a^2 - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + 6*a^2 - 4*a + 1, -11*b^2 + 6*b*a^3 + 10*b*a^2 + 39*b*a + 2*b + 16*a^3 + 23*a^2 + 104*a - 24, 11*c + 3*a^3 + 5*a^2 + 25*a + 1, 11*d + 3*a^3 + 5*a^2 + 25*a + 1, - -11*e - 11*b + 6*a^3 + 10*a^2 + 39*a + 2) of P] + -11*e - 11*b + 6*a^3 + 10*a^2 + 39*a + 2) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field] sage: R. = PolynomialRing(QQ, 2, order='lex') sage: f1 = 1/2*((x1^2 + 2*x1 - 4)*x2^2 + 2*(x1^2 + x1)*x2 + x1^2) @@ -1099,9 +1101,9 @@ def triangular_decomposition(self, algorithm=None, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: J = Ideal(x^2 + y^2 - 2, y^2 - 1) # optional - sage.rings.number_field - sage: J.triangular_decomposition() # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: J = Ideal(x^2 + y^2 - 2, y^2 - 1) # needs sage.rings.number_field + sage: J.triangular_decomposition() # needs sage.rings.number_field [Ideal (y^2 - 1, x^2 - 1) of Multivariate Polynomial Ring in x, y over Algebraic Field] """ P = self.ring() @@ -1157,9 +1159,9 @@ def dimension(self, singular=singular_default): EXAMPLES:: - sage: P. = PolynomialRing(GF(32003), order='degrevlex') # optional - sage.rings.finite_rings - sage: I = ideal(x^2 - y, x^3) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(32003), order='degrevlex') # needs sage.rings.finite_rings + sage: I = ideal(x^2 - y, x^3) # needs sage.rings.finite_rings + sage: I.dimension() # needs sage.rings.finite_rings 1 If the ideal is the total ring, the dimension is `-1` by convention. @@ -1171,22 +1173,23 @@ def dimension(self, singular=singular_default): EXAMPLES:: - sage: R. = PolynomialRing(GF(2147483659^2), order='lex') # optional - sage.rings.finite_rings - sage: I = R.ideal([x*y, x*y + 1]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^2), order='lex') + sage: I = R.ideal([x*y, x*y + 1]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. -1 - sage: I=ideal([x*(x*y+1), y*(x*y+1)]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: I=ideal([x*(x*y+1), y*(x*y+1)]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 1 - sage: I = R.ideal([x^3*y, x*y^2]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: I = R.ideal([x^3*y, x*y^2]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 1 - sage: R. = PolynomialRing(GF(2147483659^2), order='lex') # optional - sage.rings.finite_rings - sage: I = R.ideal(0) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^2), order='lex') + sage: I = R.ideal(0) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 2 @@ -1198,9 +1201,9 @@ def dimension(self, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x^2-y, x^3-QQbar(-1)) # optional - sage.rings.number_field - sage: I.dimension() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = ideal(x^2-y, x^3-QQbar(-1)) # needs sage.rings.number_field + sage: I.dimension() # needs sage.rings.number_field 1 .. NOTE:: @@ -1299,19 +1302,20 @@ def vector_space_dimension(self): Due to integer overflow, the result is correct only modulo ``2^32``, see :trac:`8586`:: - sage: P. = PolynomialRing(GF(32003), 3) # optional - sage.rings.finite_rings - sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(32003), 3) # needs sage.rings.finite_rings + sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug, needs sage.rings.finite_rings 32777216864027 TESTS: Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x^2-y,x^3-QQbar(-1),z-y) # optional - sage.rings.number_field - sage: I.dimension() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P. = QQbar[] + sage: I = ideal(x^2-y,x^3-QQbar(-1),z-y) + sage: I.dimension() 0 - sage: I.vector_space_dimension() # optional - sage.rings.number_field + sage: I.vector_space_dimension() 3 """ @@ -1351,9 +1355,9 @@ def _groebner_basis_ginv(self, algorithm="TQ", criteria='CritPartially', divisio sage: I.groebner_basis(algorithm='ginv') # optional - ginv [z^3 - 79/210*z^2 + 1/30*y + 1/70*z, y^2 - 3/5*z^2 - 1/5*y + 1/5*z, y*z + 6/5*z^2 - 1/10*y - 2/5*z, x + 2*y + 2*z - 1] - sage: P. = PolynomialRing(GF(127), order='degrevlex') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: I.groebner_basis(algorithm='ginv') # optional - ginv # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(127), order='degrevlex') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(P) # needs sage.rings.finite_rings + sage: I.groebner_basis(algorithm='ginv') # optional - ginv # needs sage.rings.finite_rings ... [z^3 + 22*z^2 - 55*y + 49*z, y^2 - 26*z^2 - 51*y + 51*z, y*z + 52*z^2 + 38*y + 25*z, x + 2*y + 2*z - 1] @@ -1596,9 +1600,9 @@ def genus(self): Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(y^3*z + x^3*y + x*z^3) # optional - sage.rings.number_field - sage: I.genus() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = ideal(y^3*z + x^3*y + x*z^3) # needs sage.rings.number_field + sage: I.genus() # needs sage.rings.number_field 3 """ from sage.libs.singular.function_factory import ff @@ -1657,10 +1661,11 @@ def intersection(self, *others): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = x*R # optional - sage.rings.number_field - sage: J = y*R # optional - sage.rings.number_field - sage: I.intersection(J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = x*R + sage: J = y*R + sage: I.intersection(J) Ideal (x*y) of Multivariate Polynomial Ring in x, y over Algebraic Field """ R = self.ring() @@ -1741,10 +1746,11 @@ def radical(self): :: - sage: R. = PolynomialRing(GF(37), 3) # optional - sage.rings.finite_rings - sage: p = z^2 + 1; q = z^3 + 2 # optional - sage.rings.finite_rings - sage: I = (p*q^2, y - z^2) * R # optional - sage.rings.finite_rings - sage: I.radical() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(37), 3) + sage: p = z^2 + 1; q = z^3 + 2 + sage: I = (p*q^2, y - z^2) * R + sage: I.radical() Ideal (z^2 - y, y^2*z + y*z + 2*y + 2) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 37 """ @@ -1829,16 +1835,17 @@ def syzygy_module(self): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: f = 2*x^2 + y # optional - sage.rings.number_field - sage: g = y # optional - sage.rings.number_field - sage: h = 2*f + g # optional - sage.rings.number_field - sage: I = Ideal([f,g,h]) # optional - sage.rings.number_field - sage: M = I.syzygy_module(); M # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: f = 2*x^2 + y + sage: g = y + sage: h = 2*f + g + sage: I = Ideal([f,g,h]) + sage: M = I.syzygy_module(); M [ -2 -1 1] [ -y 2*x^2 + y 0] - sage: G = vector(I.gens()) # optional - sage.rings.number_field - sage: M*G # optional - sage.rings.number_field + sage: G = vector(I.gens()) + sage: M*G (0, 0) """ from sage.libs.singular.function_factory import ff @@ -1967,8 +1974,8 @@ def interreduced_basis(self): The interreduced basis of 0 is 0:: - sage: P. = GF(2)[] # optional - sage.rings.finite_rings - sage: Ideal(P(0)).interreduced_basis() # optional - sage.rings.finite_rings + sage: P. = GF(2)[] # needs sage.rings.finite_rings + sage: Ideal(P(0)).interreduced_basis() # needs sage.rings.finite_rings [0] ALGORITHM: @@ -1981,9 +1988,9 @@ def interreduced_basis(self): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([z*x + y^3, z + y^3, z + x*y]) # optional - sage.rings.number_field - sage: I.interreduced_basis() # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([z*x + y^3, z + y^3, z + x*y]) # needs sage.rings.number_field + sage: I.interreduced_basis() # needs sage.rings.number_field [y^3 + z, x*y + z, x*z - z] """ return self.basis.reduced() @@ -2011,18 +2018,19 @@ def basis_is_groebner(self, singular=singular_default): EXAMPLES:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 4) # optional - sage.rings.finite_rings - sage: I.basis_is_groebner() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 4) + sage: I.basis_is_groebner() False - sage: I2 = Ideal(I.groebner_basis()) # optional - sage.rings.finite_rings - sage: I2.basis_is_groebner() # optional - sage.rings.finite_rings + sage: I2 = Ideal(I.groebner_basis()) + sage: I2.basis_is_groebner() True A more complicated example:: - sage: R. = PolynomialRing(GF(7583)) # optional - sage.rings.finite_rings - sage: l = [u6 + u5 + u4 + u3 + u2 - 3791*h, # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(7583)) # needs sage.rings.finite_rings + sage: l = [u6 + u5 + u4 + u3 + u2 - 3791*h, # needs sage.rings.finite_rings ....: U6 + U5 + U4 + U3 + U2 - 3791*h, ....: U2*u2 - h^2, U3*u3 - h^2, U4*u4 - h^2, ....: U5*u4 + U5*u3 + U4*u3 + U5*u2 + U4*u2 + U3*u2 - 3791*U5*h @@ -2064,10 +2072,10 @@ def basis_is_groebner(self, singular=singular_default): ....: - 2*U5*U4*U2^2*h^2 - 2*U5*U3*U2^2*h^2 - 2*U4*U3*U2^2*h^2 ....: - U5*U4*U3*h^3 - U5*U4*U2*h^3 - U5*U3*U2*h^3 - U4*U3*U2*h^3] - sage: Ideal(l).basis_is_groebner() # optional - sage.rings.finite_rings + sage: Ideal(l).basis_is_groebner() # needs sage.rings.finite_rings False - sage: gb = Ideal(l).groebner_basis() # optional - sage.rings.finite_rings - sage: Ideal(gb).basis_is_groebner() # optional - sage.rings.finite_rings + sage: gb = Ideal(l).groebner_basis() # needs sage.rings.finite_rings + sage: Ideal(gb).basis_is_groebner() # needs sage.rings.finite_rings True .. NOTE:: @@ -2085,12 +2093,13 @@ def basis_is_groebner(self, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = sage.rings.ideal.Cyclic(R,4) # optional - sage.rings.number_field - sage: I.basis_is_groebner() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = sage.rings.ideal.Cyclic(R,4) + sage: I.basis_is_groebner() False - sage: I2 = Ideal(I.groebner_basis()) # optional - sage.rings.number_field - sage: I2.basis_is_groebner() # optional - sage.rings.number_field + sage: I2 = Ideal(I.groebner_basis()) + sage: I2.basis_is_groebner() True """ from sage.matrix.constructor import matrix @@ -2173,9 +2182,9 @@ def transformed_basis(self, algorithm="gwalk", other_ring=None, singular=singula :: - sage: R. = PolynomialRing(GF(32003), 3, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([y^3 + x*y*z + y^2*z + x*z^3, 3 + x*y + x^2*y + y^2*z]) # optional - sage.rings.finite_rings - sage: I.transformed_basis('gwalk') # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(32003), 3, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([y^3 + x*y*z + y^2*z + x*z^3, 3 + x*y + x^2*y + y^2*z]) # needs sage.rings.finite_rings + sage: I.transformed_basis('gwalk') # needs sage.rings.finite_rings [z*y^2 + y*x^2 + y*x + 3, z*x + 8297*y^8*x^2 + 8297*y^8*x + 3556*y^7 - 8297*y^6*x^4 + 15409*y^6*x^3 - 8297*y^6*x^2 - 8297*y^5*x^5 + 15409*y^5*x^4 - 8297*y^5*x^3 + 3556*y^5*x^2 @@ -2195,12 +2204,13 @@ def transformed_basis(self, algorithm="gwalk", other_ring=None, singular=singula Check that this method works over QQbar (:trac:`25351`). We are not currently able to specify other_ring, due to the limitations of @handle_AA_and_QQbar:: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([y^3 + x^2, x^2*y + x^2, x^3 - x^2, z^4 - x^2 - y]) # optional - sage.rings.number_field - sage: I = Ideal(I.groebner_basis()) # optional - sage.rings.number_field - sage: S. = PolynomialRing(QQbar, 3, order='lex') # optional - sage.rings.number_field - sage: J = Ideal(I.transformed_basis('fglm', other_ring=S)) # known bug # optional - sage.rings.number_field - sage: J # known bug # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = Ideal([y^3 + x^2, x^2*y + x^2, x^3 - x^2, z^4 - x^2 - y]) + sage: I = Ideal(I.groebner_basis()) + sage: S. = PolynomialRing(QQbar, 3, order='lex') + sage: J = Ideal(I.transformed_basis('fglm', other_ring=S)) # known bug + sage: J # known bug """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence R = self.ring() @@ -2287,12 +2297,13 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R * [x - t, y - t^2, z - t^3, s - x + y^3] # optional - sage.rings.number_field - sage: J = I.elimination_ideal([t, s]); J # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R * [x - t, y - t^2, z - t^3, s - x + y^3] + sage: J = I.elimination_ideal([t, s]); J Ideal (y^2 - x*z, x*y - z, x^2 - y) of Multivariate Polynomial Ring in x, y, t, s, z over Algebraic Field - sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J # optional - sage.rings.number_field + sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J possible output... True @@ -2362,15 +2373,16 @@ def quotient(self, J): EXAMPLES:: - sage: R. = PolynomialRing(GF(181), 3) # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 + x*y*z, y^2 - z^3*y, z^3 + y^5*x*z]) # optional - sage.rings.finite_rings - sage: J = Ideal([x]) # optional - sage.rings.finite_rings - sage: Q = I.quotient(J) # optional - sage.rings.finite_rings - sage: y*z + x in I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(181), 3) + sage: I = Ideal([x^2 + x*y*z, y^2 - z^3*y, z^3 + y^5*x*z]) + sage: J = Ideal([x]) + sage: Q = I.quotient(J) + sage: y*z + x in I False - sage: x in J # optional - sage.rings.finite_rings + sage: x in J True - sage: x * (y*z + x) in I # optional - sage.rings.finite_rings + sage: x * (y*z + x) in I True TESTS: @@ -2385,10 +2397,11 @@ def quotient(self, J): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x, z) # optional - sage.rings.number_field - sage: J = ideal(R(1)) # optional - sage.rings.number_field - sage: I.quotient(J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = ideal(x, z) + sage: J = ideal(R(1)) + sage: I.quotient(J) Ideal (z, x) of Multivariate Polynomial Ring in x, y, z over Algebraic Field Check that :trac:`12803` is fixed:: @@ -2439,10 +2452,11 @@ def saturation(self, other): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R.ideal(x^5*z^3, x*y*z, y*z^4) # optional - sage.rings.number_field - sage: J = R.ideal(z) # optional - sage.rings.number_field - sage: I.saturation(other = J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R.ideal(x^5*z^3, x*y*z, y*z^4) + sage: J = R.ideal(z) + sage: I.saturation(other = J) (Ideal (y, x^5) of Multivariate Polynomial Ring in x, y, z over Algebraic Field, 4) """ from sage.libs.singular.function_factory import ff @@ -2492,10 +2506,11 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True EXAMPLES:: - sage: K. = GF(27) # this example is from the MAGMA handbook # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(K, 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^8 + y + 2, y^6 + x*y^5 + x^2]) # optional - sage.rings.finite_rings - sage: I = Ideal(I.groebner_basis()); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = GF(27) # this example is from the MAGMA handbook + sage: P. = PolynomialRing(K, 2, order='lex') + sage: I = Ideal([x^8 + y + 2, y^6 + x*y^5 + x^2]) + sage: I = Ideal(I.groebner_basis()); I Ideal (x - y^47 - y^45 + y^44 - y^43 + y^41 - y^39 - y^38 - y^37 - y^36 + y^35 - y^34 - y^33 + y^32 - y^31 + y^30 + y^28 + y^27 + y^26 + y^25 - y^23 + y^22 + y^21 - y^19 - y^18 - y^16 + y^15 + y^13 @@ -2504,19 +2519,20 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True - y^25 + y^24 + y^2 + y + 1) of Multivariate Polynomial Ring in x, y over Finite Field in w of size 3^3 - sage: V = I.variety(); # optional - sage.rings.finite_rings - sage: sorted(V, key=str) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: V = I.variety(); + sage: sorted(V, key=str) [{y: w^2 + 2*w, x: 2*w + 2}, {y: w^2 + 2, x: 2*w}, {y: w^2 + w, x: 2*w + 1}] - sage: [f.subs(v) # check that all polynomials vanish # optional - sage.rings.finite_rings + sage: [f.subs(v) # check that all polynomials vanish ....: for f in I.gens() for v in V] [0, 0, 0, 0, 0, 0] - sage: [I.subs(v).is_zero() for v in V] # same test, but nicer syntax # optional - sage.rings.finite_rings + sage: [I.subs(v).is_zero() for v in V] # same test, but nicer syntax [True, True, True] However, we only account for solutions in the ground field and not in the algebraic closure:: - sage: I.vector_space_dimension() # optional - sage.rings.finite_rings + sage: I.vector_space_dimension() # needs sage.rings.finite_rings 48 Here we compute the points of intersection of a hyperbola and a @@ -2538,7 +2554,7 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: sorted(I.variety(ring=RR), key=str) [{y: 0.361103080528647, x: 2.76929235423863}, {y: 1.00000000000000, x: 1.00000000000000}] - sage: I.variety(ring=AA) # optional - sage.rings.number_field + sage: I.variety(ring=AA) # needs sage.rings.number_field [{y: 1, x: 1}, {y: 0.3611030805286474?, x: 2.769292354238632?}] @@ -2551,7 +2567,7 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True x: 0.11535382288068... + 0.58974280502220...*I}, {y: 0.36110308052864..., x: 2.7692923542386...}, {y: 1.00000000000000, x: 1.00000000000000}] - sage: sorted(I.variety(ring=QQbar), key=str) # optional - sage.rings.number_field + sage: sorted(I.variety(ring=QQbar), key=str) # needs sage.rings.number_field [{y: 0.3194484597356763? + 1.633170240915238?*I, x: 0.11535382288068429? - 0.5897428050222055?*I}, {y: 0.3194484597356763? - 1.633170240915238?*I, @@ -2586,9 +2602,9 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True If the ground field's characteristic is too large for Singular, we resort to a toy implementation:: - sage: R. = PolynomialRing(GF(2147483659^3), order='lex') # optional - sage.rings.finite_rings - sage: I = ideal([x^3 - 2*y^2, 3*x + y^4]) # optional - sage.rings.finite_rings - sage: I.variety() # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^3), order='lex') # needs sage.rings.finite_rings + sage: I = ideal([x^3 - 2*y^2, 3*x + y^4]) # needs sage.rings.finite_rings + sage: I.variety() # needs sage.rings.finite_rings verbose 0 (...: multi_polynomial_ideal.py, groebner_basis) Warning: falling back to very slow toy implementation. verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: falling back to very slow toy implementation. @@ -2601,20 +2617,20 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: K. = QQ[] sage: I = ideal([x^2 + 2*y - 5, x + y + 3]) - sage: v = I.variety(AA)[0]; v[x], v[y] # optional - sage.rings.number_field + sage: v = I.variety(AA)[0]; v[x], v[y] # needs sage.rings.number_field (4.464101615137755?, -7.464101615137755?) - sage: list(v)[0].parent() # optional - sage.rings.number_field + sage: list(v)[0].parent() # needs sage.rings.number_field Multivariate Polynomial Ring in x, y over Algebraic Real Field - sage: v[x] # optional - sage.rings.number_field + sage: v[x] # needs sage.rings.number_field 4.464101615137755? - sage: v["y"] # optional - sage.rings.number_field + sage: v["y"] # needs sage.rings.number_field -7.464101615137755? ``msolve`` also works over finite fields:: - sage: R. = PolynomialRing(GF(536870909), 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 - 1, y^2 - 1]) # optional - sage.rings.finite_rings - sage: sorted(I.variety(algorithm='msolve', # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(536870909), 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([x^2 - 1, y^2 - 1]) # needs sage.rings.finite_rings + sage: sorted(I.variety(algorithm='msolve', # optional - msolve # needs sage.rings.finite_rings ....: proof=False), ....: key=str) [{x: 1, y: 1}, @@ -2625,9 +2641,9 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True but may fail in small characteristic, especially with ideals of high degree with respect to the characteristic:: - sage: R. = PolynomialRing(GF(3), 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 - 1, y^2 - 1]) # optional - sage.rings.finite_rings - sage: I.variety(algorithm='msolve', proof=False) # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(3), 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([x^2 - 1, y^2 - 1]) # needs sage.rings.finite_rings + sage: I.variety(algorithm='msolve', proof=False) # optional - msolve, needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: characteristic 3 too small @@ -2662,20 +2678,20 @@ def _variety_triangular_decomposition(self, ring): TESTS:: - sage: K. = GF(27) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(K, 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([ x^8 + y + 2, y^6 + x*y^5 + x^2 ]) # optional - sage.rings.finite_rings + sage: K. = GF(27) # needs sage.rings.finite_rings + sage: P. = PolynomialRing(K, 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([ x^8 + y + 2, y^6 + x*y^5 + x^2 ]) # needs sage.rings.finite_rings Testing the robustness of the Singular interface:: - sage: T = I.triangular_decomposition('singular:triangLfak') # optional - sage.rings.finite_rings - sage: sorted(I.variety(), key=str) # optional - sage.rings.finite_rings + sage: T = I.triangular_decomposition('singular:triangLfak') # needs sage.rings.finite_rings + sage: sorted(I.variety(), key=str) # needs sage.rings.finite_rings [{y: w^2 + 2*w, x: 2*w + 2}, {y: w^2 + 2, x: 2*w}, {y: w^2 + w, x: 2*w + 1}] Testing that a bug is indeed fixed :: - sage: R = PolynomialRing(GF(2), 30, ['x%d'%(i+1) for i in range(30)], order='lex') # optional - sage.rings.finite_rings - sage: R.inject_variables() # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(2), 30, ['x%d'%(i+1) for i in range(30)], order='lex') # needs sage.rings.finite_rings + sage: R.inject_variables() # needs sage.rings.finite_rings Defining... sage: I = Ideal([x1 + 1, x2, x3 + 1, x5*x10 + x10 + x18, x5*x11 + x11, \ x5*x18, x6, x7 + 1, x9, x10*x11 + x10 + x18, x10*x18 + x18, \ @@ -2687,9 +2703,9 @@ def _variety_triangular_decomposition(self, ring): x16^2 + x16, x17^2 + x17, x18^2 + x18, x19^2 + x19, x20^2 + x20, \ x21^2 + x21, x22^2 + x22, x23^2 + x23, x24^2 + x24, x25^2 + x25, \ x26^2 + x26, x27^2 + x27, x28^2 + x28, x29^2 + x29, x30^2 + x30]) # optional - sage.rings.finite_rings - sage: I.basis_is_groebner() + sage: I.basis_is_groebner() # needs sage.rings.finite_rings True - sage: sorted("".join(str(V[g]) for g in R.gens()) for V in I.variety()) # long time (6s on sage.math, 2011) # optional - sage.rings.finite_rings + sage: sorted("".join(str(V[g]) for g in R.gens()) for V in I.variety()) # long time (6s on sage.math, 2011), needs sage.rings.finite_rings ['101000100000000110001000100110', '101000100000000110001000101110', '101000100100000101001000100110', @@ -2742,7 +2758,7 @@ def _variety_triangular_decomposition(self, ring): sage: R. = PolynomialRing(QQ, order='lex') sage: I = R.ideal(c^2-2, b-c, a) - sage: I.variety(QQbar) # optional - sage.rings.number_field + sage: I.variety(QQbar) # needs sage.rings.number_field [...a: 0...] An early version of :trac:`25351` broke this method by adding the @@ -2750,13 +2766,14 @@ def _variety_triangular_decomposition(self, ring): that this circle and this hyperbola have two real intersections and two more complex ones:: - sage: K. = PolynomialRing(AA) # optional - sage.rings.number_field - sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) # optional - sage.rings.number_field - sage: len(I.variety()) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = PolynomialRing(AA) + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: len(I.variety()) 2 - sage: K. = PolynomialRing(QQbar) # optional - sage.rings.number_field - sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) # optional - sage.rings.number_field - sage: len(I.variety()) # optional - sage.rings.number_field + sage: K. = PolynomialRing(QQbar) + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: len(I.variety()) 4 """ @@ -2842,7 +2859,7 @@ def hilbert_polynomial(self, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint 5*t - 5 Of course, the Hilbert polynomial of a zero-dimensional ideal @@ -2853,14 +2870,14 @@ def hilbert_polynomial(self, algorithm='sage'): sage: J = P*[m.lm() for m in J0.groebner_basis()] sage: J.dimension() 0 - sage: J.hilbert_polynomial() # optional - sage.libs.flint + sage: J.hilbert_polynomial() # needs sage.libs.flint 0 It is possible to request a computation using the Singular library:: - sage: I.hilbert_polynomial(algorithm='singular') == I.hilbert_polynomial() # optional - sage.libs.flint sage.libs.singular + sage: I.hilbert_polynomial(algorithm='singular') == I.hilbert_polynomial() # needs sage.libs.flint True - sage: J.hilbert_polynomial(algorithm='singular') == J.hilbert_polynomial() # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_polynomial(algorithm='singular') == J.hilbert_polynomial() # needs sage.libs.flint True Here is a bigger examples:: @@ -2868,7 +2885,7 @@ def hilbert_polynomial(self, algorithm='sage'): sage: n = 4; m = 11; P = PolynomialRing(QQ, n * m, "x"); x = P.gens() sage: M = Matrix(n, x) sage: Minors = P.ideal(M.minors(2)) - sage: hp = Minors.hilbert_polynomial(); hp # optional - sage.libs.flint + sage: hp = Minors.hilbert_polynomial(); hp # needs sage.libs.flint 1/21772800*t^13 + 61/21772800*t^12 + 1661/21772800*t^11 + 26681/21772800*t^10 + 93841/7257600*t^9 + 685421/7257600*t^8 + 1524809/3110400*t^7 + 39780323/21772800*t^6 + 6638071/1360800*t^5 @@ -2879,7 +2896,7 @@ def hilbert_polynomial(self, algorithm='sage'): with Singular. We don't test it here, as it has a side-effect on other tests that is not understood yet (see :trac:`26300`):: - sage: Minors.hilbert_polynomial(algorithm='singular') # not tested # optional - sage.libs.singular + sage: Minors.hilbert_polynomial(algorithm='singular') # not tested Traceback (most recent call last): ... RuntimeError: error in Singular function call 'hilbPoly': @@ -2891,8 +2908,8 @@ def hilbert_polynomial(self, algorithm='sage'): coefficients of the Hilbert-Poincaré series in all degrees:: sage: P = PowerSeriesRing(QQ, 't', default_prec=50) - sage: hs = Minors.hilbert_series() # optional - sage.libs.flint - sage: list(P(hs.numerator()) / P(hs.denominator())) == [hp(t=k) # optional - sage.libs.flint + sage: hs = Minors.hilbert_series() # needs sage.libs.flint + sage: list(P(hs.numerator()) / P(hs.denominator())) == [hp(t=k) # needs sage.libs.flint ....: for k in range(50)] True @@ -2902,23 +2919,23 @@ def hilbert_polynomial(self, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3, x*y^2, y^4, x^2*y*z, y^3*z, x^2*z^2, x*y*z^2, x*z^3]) - sage: I.hilbert_polynomial(algorithm='singular') # optional - sage.libs.singular + sage: I.hilbert_polynomial(algorithm='singular') 3 - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint 3 Check that this method works over ``QQbar`` (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_polynomial() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_polynomial() # needs sage.rings.number_field 5*t - 5 Check for :trac:`33597`:: sage: R. = QQ[] sage: I = R.ideal([X^2*Y^3, X*Z]) - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint t + 5 """ if not self.is_homogeneous(): @@ -2981,48 +2998,49 @@ def hilbert_series(self, grading=None, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_series() # optional - sage.libs.flint + sage: I.hilbert_series() # needs sage.libs.flint (t^4 + t^3 + t^2 + t + 1)/(t^2 - 2*t + 1) sage: R. = PolynomialRing(QQ) sage: J = R.ideal([a^2*b, a*b^2]) - sage: J.hilbert_series() # optional - sage.libs.flint + sage: J.hilbert_series() # needs sage.libs.flint (t^3 - t^2 - t - 1)/(t - 1) - sage: J.hilbert_series(grading=(10,3)) # optional - sage.libs.flint + sage: J.hilbert_series(grading=(10,3)) # needs sage.libs.flint (t^25 + t^24 + t^23 - t^15 - t^14 - t^13 - t^12 - t^11 - t^10 - t^9 - t^8 - t^7 - t^6 - t^5 - t^4 - t^3 - t^2 - t - 1)/(t^12 + t^11 + t^10 - t^2 - t - 1) sage: K = R.ideal([a^2*b^3, a*b^4 + a^3*b^2]) - sage: K.hilbert_series(grading=[1,2]) # optional - sage.libs.flint + sage: K.hilbert_series(grading=[1,2]) # needs sage.libs.flint (t^11 + t^8 - t^6 - t^5 - t^4 - t^3 - t^2 - t - 1)/(t^2 - 1) - sage: K.hilbert_series(grading=[2,1]) # optional - sage.libs.flint + sage: K.hilbert_series(grading=[2,1]) # needs sage.libs.flint (2*t^7 - t^6 - t^4 - t^2 - 1)/(t - 1) TESTS:: - sage: I.hilbert_series() == I.hilbert_series(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: # needs sage.libs.flint + sage: I.hilbert_series() == I.hilbert_series(algorithm='singular') True - sage: J.hilbert_series() == J.hilbert_series(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_series() == J.hilbert_series(algorithm='singular') True - sage: J.hilbert_series(grading=(10,3)) == J.hilbert_series(grading=(10,3), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_series(grading=(10,3)) == J.hilbert_series(grading=(10,3), algorithm='singular') True - sage: K.hilbert_series(grading=(1,2)) == K.hilbert_series(grading=(1,2), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: K.hilbert_series(grading=(1,2)) == K.hilbert_series(grading=(1,2), algorithm='singular') True - sage: K.hilbert_series(grading=(2,1)) == K.hilbert_series(grading=(2,1), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: K.hilbert_series(grading=(2,1)) == K.hilbert_series(grading=(2,1), algorithm='singular') True sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_series(grading=5) # optional - sage.libs.flint + sage: I.hilbert_series(grading=5) # needs sage.libs.flint Traceback (most recent call last): ... TypeError: grading must be a list or a tuple of integers Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_series() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_series() # needs sage.rings.number_field (t^4 + t^3 + t^2 + t + 1)/(t^2 - 2*t + 1) """ if not self.is_homogeneous(): @@ -3080,42 +3098,43 @@ def hilbert_numerator(self, grading=None, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_numerator() # optional - sage.libs.flint + sage: I.hilbert_numerator() # needs sage.libs.flint -t^5 + 1 sage: R. = PolynomialRing(QQ) sage: J = R.ideal([a^2*b, a*b^2]) - sage: J.hilbert_numerator() # optional - sage.libs.flint + sage: J.hilbert_numerator() # needs sage.libs.flint t^4 - 2*t^3 + 1 - sage: J.hilbert_numerator(grading=(10,3)) # optional - sage.libs.flint + sage: J.hilbert_numerator(grading=(10,3)) # needs sage.libs.flint t^26 - t^23 - t^16 + 1 TESTS:: - sage: I.hilbert_numerator() == I.hilbert_numerator(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: I.hilbert_numerator() == I.hilbert_numerator(algorithm='singular') # needs sage.libs.flint True - sage: J.hilbert_numerator() == J.hilbert_numerator(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_numerator() == J.hilbert_numerator(algorithm='singular') # needs sage.libs.flint True - sage: J.hilbert_numerator(grading=(10,3)) == J.hilbert_numerator(grading=(10,3), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_numerator(grading=(10,3)) == J.hilbert_numerator(grading=(10,3), algorithm='singular') # needs sage.libs.flint True Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_numerator() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_numerator() # needs sage.rings.number_field -t^5 + 1 Our two algorithms should always agree; not tested until :trac:`33178` is fixed:: - sage: m = ZZ.random_element(2,8) # not tested - sage: nvars = m^2 # not tested - sage: R = PolynomialRing(QQ, "x", nvars) # not tested - sage: M = matrix(R, m, R.gens()) # not tested - sage: I = R.ideal(M.minors(2)) # not tested - sage: n1 = I.hilbert_numerator() # not tested - sage: n2 = I.hilbert_numerator(algorithm='singular') # not tested - sage: n1 == n2 # not tested + sage: # not tested + sage: m = ZZ.random_element(2,8) + sage: nvars = m^2 + sage: R = PolynomialRing(QQ, "x", nvars) + sage: M = matrix(R, m, R.gens()) + sage: I = R.ideal(M.minors(2)) + sage: n1 = I.hilbert_numerator() + sage: n2 = I.hilbert_numerator(algorithm='singular') + sage: n1 == n2 True """ @@ -3272,19 +3291,20 @@ def normal_basis(self, degree=None, algorithm='libsingular', Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5, x*z-1) # optional - sage.rings.number_field - sage: I.normal_basis() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5, x*z-1) + sage: I.normal_basis() [y*z^2, z^2, y*z, z, x*y, y, x, 1] - sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) # optional - sage.rings.number_field - sage: [J.normal_basis(d) for d in (0..3)] # optional - sage.rings.number_field + sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) + sage: [J.normal_basis(d) for d in (0..3)] [[1], [z, y, x], [z^2, y*z, x*z, x*y], [z^3, y*z^2, x*z^2, x*y*z]] Check the option ``algorithm="singular"`` with a weighted term order:: sage: T = TermOrder('wdegrevlex', (1, 2, 3)) - sage: S. = PolynomialRing(GF(2), order=T) # optional - sage.rings.finite_rings - sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') # optional - sage.rings.finite_rings + sage: S. = PolynomialRing(GF(2), order=T) # needs sage.rings.finite_rings + sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') # needs sage.rings.finite_rings [x^4*y, x^2*y^2, y^3, x^3*z, x*y*z, z^2] """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence @@ -3364,14 +3384,15 @@ def _groebner_basis_macaulay2(self, strategy=None): Over finite fields, Macaulay2 supports different algorithms to compute Gröbner bases:: - sage: R = PolynomialRing(GF(101), 'x', 4) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb1 = I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb2 = I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb3 = I.groebner_basis('macaulay2:f4') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: gb1 == gb2 == gb3 # optional - macaulay2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(101), 'x', 4) + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb1 = I.groebner_basis('macaulay2:gb') # optional - macaulay2 + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb2 = I.groebner_basis('macaulay2:mgb') # optional - macaulay2 + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb3 = I.groebner_basis('macaulay2:f4') # optional - macaulay2 + sage: gb1 == gb2 == gb3 # optional - macaulay2 True TESTS:: @@ -3435,26 +3456,27 @@ def __init__(self, ring, gens, coerce=True, side="left"): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # indirect doctest # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # indirect doctest ....: coerce=False) - sage: I # random # optional - sage.combinat sage.modules + sage: I # random Left Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(I.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.gens(), key=str) [x^2, y^2, z^2 - 1] - sage: H.ideal([y^2, x^2, z^2 - H.one()], side="twosided") # random # optional - sage.combinat sage.modules + sage: H.ideal([y^2, x^2, z^2 - H.one()], side="twosided") # random Twosided Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(H.ideal([y^2, x^2, z^2 - H.one()], side="twosided").gens(), # optional - sage.combinat sage.modules + sage: sorted(H.ideal([y^2, x^2, z^2 - H.one()], side="twosided").gens(), ....: key=str) [x^2, y^2, z^2 - 1] - sage: H.ideal([y^2, x^2, z^2 - H.one()], side="right") # optional - sage.combinat sage.modules + sage: H.ideal([y^2, x^2, z^2 - H.one()], side="right") Traceback (most recent call last): ... ValueError: Only left and two-sided ideals are allowed. @@ -3479,15 +3501,16 @@ def __call_singular(self, cmd, arg=None): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: id = H.ideal(x + y, y + z) # optional - sage.combinat sage.modules - sage: id.std() # indirect doctest # random # optional - sage.combinat sage.modules + sage: id = H.ideal(x + y, y + z) + sage: id.std() # indirect doctest # random Left Ideal (z, y, x) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(id.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(id.std().gens(), key=str) [x, y, z] """ from sage.libs.singular.function import singular_function @@ -3504,16 +3527,17 @@ def std(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.std() #random # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.std() #random Left Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(I.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.std().gens(), key=str) [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] @@ -3521,37 +3545,38 @@ def std(self): Groebner basis. But if it is a two-sided ideal, then the output of :meth:`std` and :meth:`twostd` coincide:: - sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) # optional - sage.combinat sage.modules - sage: JL #random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) + sage: JL #random Left Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JL.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JL.gens(), key=str) [x^3, y^3, z^3 - 4*z] - sage: JL.std() # random # optional - sage.combinat sage.modules + sage: JL.std() # random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JL.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JL.std().gens(), key=str) [2*x*y*z - z^2 - 2*z, x*z^2 + 2*x*z, x^3, y*z^2 - 2*y*z, y^3, z^3 - 4*z] - sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') # optional - sage.combinat sage.modules - sage: JT #random # optional - sage.combinat sage.modules + sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') + sage: JT #random Twosided Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JT.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JT.gens(), key=str) [x^3, y^3, z^3 - 4*z] - sage: JT.std() #random # optional - sage.combinat sage.modules + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JT.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JT.std().gens(), key=str) [2*x*y*z - z^2 - 2*z, x*y^2 - y*z, x*z^2 + 2*x*z, x^2*y - x*z - 2*x, x^2*z + 2*x^2, x^3, y*z^2 - 2*y*z, y^2*z - 2*y^2, y^3, z^3 - 4*z] - sage: JT.std() == JL.twostd() # optional - sage.combinat sage.modules + sage: JT.std() == JL.twostd() True ALGORITHM: Uses Singular's ``std`` command @@ -3568,20 +3593,21 @@ def elimination_ideal(self, variables): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.elimination_ideal([x, z]) # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.elimination_ideal([x, z]) Left Ideal (y^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} - sage: J = I.twostd(); J # optional - sage.combinat sage.modules + sage: J = I.twostd(); J Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} - sage: J.elimination_ideal([x, z]) # optional - sage.combinat sage.modules + sage: J.elimination_ideal([x, z]) Twosided Ideal (y^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} @@ -3604,15 +3630,16 @@ def twostd(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.twostd() #random # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.twostd() #random Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field... - sage: sorted(I.twostd().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.twostd().gens(), key=str) [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] ALGORITHM: Uses Singular's ``twostd`` command @@ -3631,10 +3658,11 @@ def _groebner_strategy(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I._groebner_strategy() # random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: I._groebner_strategy() # random Groebner Strategy for ideal generated by 6 elements over Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} @@ -3655,27 +3683,28 @@ def reduce(self,p): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], ....: coerce=False, side='twosided') - sage: Q = H.quotient(I); Q #random # optional - sage.combinat sage.modules + sage: Q = H.quotient(I); Q #random Quotient of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} by the ideal (y^2, x^2, z^2 - 1) - sage: Q.2^2 == Q.one() # indirect doctest # optional - sage.combinat sage.modules + sage: Q.2^2 == Q.one() # indirect doctest True Here, we see that the relation that we just found in the quotient is actually a consequence of the given relations:: - sage: H.2^2 - H.one() in I.std().gens() # optional - sage.combinat sage.modules + sage: H.2^2 - H.one() in I.std().gens() # needs sage.combinat sage.modules True Here is the corresponding direct test:: - sage: I.reduce(z^2) # optional - sage.combinat sage.modules + sage: I.reduce(z^2) # needs sage.combinat sage.modules 1 """ @@ -3687,15 +3716,16 @@ def _contains_(self,p): We define a left and a two-sided ideal:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) # optional - sage.combinat sage.modules - sage: JL.std() #random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) + sage: JL.std() #random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') # optional - sage.combinat sage.modules - sage: JT.std() #random # optional - sage.combinat sage.modules + sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, @@ -3704,9 +3734,9 @@ def _contains_(self,p): Apparently, ``x*y^2-y*z`` should be in the two-sided, but not in the left ideal:: - sage: x*y^2-y*z in JL #indirect doctest # optional - sage.combinat sage.modules + sage: x*y^2-y*z in JL #indirect doctest # needs sage.combinat sage.modules False - sage: x*y^2-y*z in JT # optional - sage.combinat sage.modules + sage: x*y^2-y*z in JT # needs sage.combinat sage.modules True """ @@ -3726,12 +3756,13 @@ def syzygy_module(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: G = vector(I.gens()); G # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: G = vector(I.gens()); G d...: UserWarning: You are constructing a free module over a noncommutative ring. Sage does not have a concept of left/right and both sided modules, so be careful. @@ -3743,7 +3774,7 @@ def syzygy_module(self): It's also not guaranteed that all multiplications are done from the right side. (y^2, x^2, z^2 - 1) - sage: M = I.syzygy_module(); M # optional - sage.combinat sage.modules + sage: M = I.syzygy_module(); M [ -z^2 - 8*z - 15 0 y^2] [ 0 -z^2 + 8*z - 15 x^2] [ x^2*z^2 + 8*x^2*z + 15*x^2 -y^2*z^2 + 8*y^2*z - 15*y^2 -4*x*y*z + 2*z^2 + 2*z] @@ -3754,7 +3785,7 @@ def syzygy_module(self): [ x^4*z + 4*x^4 -x^2*y^2*z + 4*x^2*y^2 - 4*x*y*z^2 + 32*x*y*z - 6*z^3 - 64*x*y + 66*z^2 - 240*z + 288 0] [x^3*y^2*z + 4*x^3*y^2 + 18*x^2*y*z - 36*x*z^3 + 66*x^2*y - 432*x*z^2 - 1656*x*z - 2052*x -x*y^4*z + 4*x*y^4 - 8*y^3*z^2 + 62*y^3*z - 114*y^3 48*y*z^2 - 36*y*z] - sage: M*G # optional - sage.combinat sage.modules + sage: M*G # needs sage.combinat sage.modules (0, 0, 0, 0, 0, 0, 0, 0, 0) ALGORITHM: Uses Singular's ``syz`` command @@ -3781,12 +3812,13 @@ def res(self, length): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.res(3) # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: I.res(3) """ if self.side() == 'twosided': @@ -3816,8 +3848,8 @@ def __init__(self, ring, gens, coerce=True): sage: R. = PolynomialRing(IntegerRing(), 2, order='lex') sage: R.ideal([x, y]) Ideal (x, y) of Multivariate Polynomial Ring in x, y over Integer Ring - sage: R. = GF(3)[] # optional - sage.rings.finite_rings - sage: R.ideal([x0^2, x1^3]) # optional - sage.rings.finite_rings + sage: R. = GF(3)[] # needs sage.rings.finite_rings + sage: R.ideal([x0^2, x1^3]) # needs sage.rings.finite_rings Ideal (x0^2, x1^3) of Multivariate Polynomial Ring in x0, x1 over Finite Field of size 3 """ Ideal_generic.__init__(self, ring, gens, coerce=coerce) @@ -3903,22 +3935,24 @@ def __richcmp__(self, other, op): :: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J < I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J < I False - sage: I < J # optional - sage.rings.finite_rings + sage: I < J True :: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J > I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J > I True - sage: I > J # optional - sage.rings.finite_rings + sage: I > J False :: @@ -3956,15 +3990,16 @@ def __richcmp__(self, other, op): We test to make sure that pickling works with the cached Groebner basis:: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J >= I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J >= I True - sage: I >= J # optional - sage.rings.finite_rings + sage: I >= J False - sage: loads(dumps(I)).__getstate__() # optional - sage.rings.finite_rings + sage: loads(dumps(I)).__getstate__() # needs sage.rings.finite_rings (Monoid of ideals of Multivariate Polynomial Ring in x, y over Finite Field of size 32003, {'_Ideal_generic__gens': (x^2 + x, y), '_Ideal_generic__ring': Multivariate Polynomial Ring in x, y over Finite Field of size 32003, @@ -4330,31 +4365,31 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Here we use Macaulay2 with three different strategies over a finite field. :: - sage: R. = PolynomialRing(GF(101), 3) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:f4') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:f4') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] Over prime fields of small characteristic, we can also use the `optional package msolve <../spkg/msolve.html>`_:: - sage: R. = PolynomialRing(GF(101), 3) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('msolve') # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('msolve') # optional - msolve # needs sage.rings.finite_rings [a + 2*b + 2*c - 1, b*c - 19*c^2 + 10*b + 40*c, b^2 - 41*c^2 + 20*b - 20*c, c^3 + 28*c^2 - 37*b + 13*c] :: - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma, needs sage.rings.finite_rings [a - 60*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 - 79/7*c^2 + 3/7*c, c^4 - 10/21*c^3 + 1/84*c^2 + 1/84*c] Singular and libSingular can compute Groebner basis with degree @@ -4527,54 +4562,55 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Check that this method works over QQbar (:trac:`25351`):: - sage: P. = PolynomialRing(QQbar, 3, order='lex') # optional - sage.rings.number_field - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis() # optional - sage.rings.number_field + sage: P. = PolynomialRing(QQbar, 3, order='lex') # needs sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis() # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:groebner') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:groebner') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:std') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:std') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:stdhilb') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:stdhilb') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:stdfglm') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:stdfglm') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:slimgb') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:slimgb') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: J = I.change_ring(P.change_ring(order='degrevlex')) # optional - sage.rings.number_field - sage: gb = J.groebner_basis('giac') # random # optional - sage.rings.number_field - sage: gb # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching + sage: J = I.change_ring(P.change_ring(order='degrevlex')) + sage: gb = J.groebner_basis('giac') # random + sage: gb [c^3 + (-79/210)*c^2 + 1/30*b + 1/70*c, b^2 + (-3/5)*c^2 + (-1/5)*b + 1/5*c, b*c + 6/5*c^2 + (-1/10)*b + (-2/5)*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('toy:buchberger2') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('toy:buchberger2') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma, needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] msolve currently supports the degrevlex order only:: - sage: R. = PolynomialRing(GF(101), 3, order='lex') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('msolve') # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3, order='lex') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('msolve') # optional - msolve # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: msolve only supports the degrevlex order (use transformed_basis()) @@ -4927,28 +4963,28 @@ def homogenize(self, var='h'): EXAMPLES:: - sage: P. = PolynomialRing(GF(2)) # optional - sage.rings.finite_rings - sage: I = Ideal([x^2*y + z + 1, x + y^2 + 1]); I # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(2)) # needs sage.rings.finite_rings + sage: I = Ideal([x^2*y + z + 1, x + y^2 + 1]); I # needs sage.rings.finite_rings Ideal (x^2*y + z + 1, y^2 + x + 1) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 2 :: - sage: I.homogenize() # optional - sage.rings.finite_rings + sage: I.homogenize() # needs sage.rings.finite_rings Ideal (x^2*y + z*h^2 + h^3, y^2 + x*h + h^2) of Multivariate Polynomial Ring in x, y, z, h over Finite Field of size 2 :: - sage: I.homogenize(y) # optional - sage.rings.finite_rings + sage: I.homogenize(y) # needs sage.rings.finite_rings Ideal (x^2*y + y^3 + y^2*z, x*y) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 2 :: - sage: I = Ideal([x^2*y + z^3 + y^2*x, x + y^2 + 1]) # optional - sage.rings.finite_rings - sage: I.homogenize() # optional - sage.rings.finite_rings + sage: I = Ideal([x^2*y + z^3 + y^2*x, x + y^2 + 1]) # needs sage.rings.finite_rings + sage: I.homogenize() # needs sage.rings.finite_rings Ideal (x^2*y + x*y^2 + z^3, y^2 + x*h + h^2) of Multivariate Polynomial Ring in x, y, z, h over Finite Field of size 2 @@ -5024,51 +5060,51 @@ def degree_of_semi_regularity(self): We consider a homogeneous example:: sage: n = 8 - sage: K = GF(127) # optional - sage.rings.finite_rings - sage: P = PolynomialRing(K, n, 'x') # optional - sage.rings.finite_rings - sage: s = [K.random_element() for _ in range(n)] # optional - sage.rings.finite_rings - sage: L = [] # optional - sage.rings.finite_rings - sage: for i in range(2 * n): # optional - sage.rings.finite_rings + sage: K = GF(127) # needs sage.rings.finite_rings + sage: P = PolynomialRing(K, n, 'x') # needs sage.rings.finite_rings + sage: s = [K.random_element() for _ in range(n)] # needs sage.rings.finite_rings + sage: L = [] # needs sage.rings.finite_rings + sage: for i in range(2 * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 4 From this, we expect a Groebner basis computation to reach at most degree 4. For homogeneous systems this is equivalent to the largest degree in the Groebner basis:: - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 4 We increase the number of polynomials and observe a decrease the degree of regularity:: - sage: for i in range(2 * n): # optional - sage.rings.finite_rings + sage: for i in range(2 * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 3 - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 3 The degree of regularity approaches 2 for quadratic systems as the number of polynomials approaches `n^2`:: - sage: for i in range((n-4) * n): # optional - sage.rings.finite_rings + sage: for i in range((n-4) * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 2 - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 2 .. NOTE:: @@ -5117,45 +5153,45 @@ def plot(self, *args, **kwds): sage: R. = PolynomialRing(QQ, 2) sage: I = R.ideal([y^3 - x^2]) - sage: I.plot() # cusp # optional - sage.plot + sage: I.plot() # cusp # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2 - x^2 - 1]) - sage: I.plot((x,-3, 3), (y, -2, 2)) # hyperbola # optional - sage.plot + sage: I.plot((x,-3, 3), (y, -2, 2)) # hyperbola # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2 + x^2*(1/4) - 1]) - sage: I.plot() # ellipse # optional - sage.plot + sage: I.plot() # ellipse # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2-(x^2-1)*(x-2)]) - sage: I.plot() # elliptic curve # optional - sage.plot + sage: I.plot() # elliptic curve # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: f = ((x+3)^3 + 2*(x+3)^2 - y^2)*(x^3 - y^2)*((x-3)^3-2*(x-3)^2-y^2) sage: I = R.ideal(f) - sage: I.plot() # the Singular logo # optional - sage.plot + sage: I.plot() # the Singular logo # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: R. = PolynomialRing(QQ, 2) sage: I = R.ideal([x - 1]) - sage: I.plot((y, -2, 2)) # vertical line # optional - sage.plot + sage: I.plot((y, -2, 2)) # vertical line # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([-x^2*y + 1]) - sage: I.plot() # blow up # optional - sage.plot + sage: I.plot() # blow up # needs sage.plot Graphics object consisting of 1 graphics primitive """ @@ -5234,12 +5270,13 @@ def random_element(self, degree, compute_gb=False, *args, **kwds): We compute a uniformly random element up to the provided degree. :: - sage: P. = GF(127)[] # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: f = I.random_element(degree=4, compute_gb=True, terms=infinity) # optional - sage.rings.finite_rings - sage: f.degree() <= 4 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = GF(127)[] + sage: I = sage.rings.ideal.Katsura(P) + sage: f = I.random_element(degree=4, compute_gb=True, terms=infinity) + sage: f.degree() <= 4 True - sage: len(list(f)) <= 35 # optional - sage.rings.finite_rings + sage: len(list(f)) <= 35 True Note that sampling uniformly at random from the ideal at some large enough degree is @@ -5247,45 +5284,47 @@ def random_element(self, degree, compute_gb=False, *args, **kwds): basis if we can sample uniformly at random from an ideal:: sage: n = 3; d = 4 - sage: P = PolynomialRing(GF(127), n, 'x') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(P) # optional - sage.rings.finite_rings + sage: P = PolynomialRing(GF(127), n, 'x') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Cyclic(P) # needs sage.rings.finite_rings 1. We sample `n^d` uniformly random elements in the ideal:: - sage: F = Sequence(I.random_element(degree=d, compute_gb=True, # optional - sage.rings.finite_rings + sage: F = Sequence(I.random_element(degree=d, compute_gb=True, # needs sage.rings.finite_rings ....: terms=infinity) ....: for _ in range(n^d)) 2. We linearize and compute the echelon form:: - sage: A, v = F.coefficient_matrix() # optional - sage.rings.finite_rings - sage: A.echelonize() # optional - sage.rings.finite_rings + sage: A, v = F.coefficient_matrix() # needs sage.rings.finite_rings + sage: A.echelonize() # needs sage.rings.finite_rings 3. The result is the desired Gröbner basis:: - sage: G = Sequence((A * v).list()) # optional - sage.rings.finite_rings - sage: G.is_groebner() # optional - sage.rings.finite_rings + sage: G = Sequence((A * v).list()) # needs sage.rings.finite_rings + sage: G.is_groebner() # needs sage.rings.finite_rings True - sage: Ideal(G) == I # optional - sage.rings.finite_rings + sage: Ideal(G) == I # needs sage.rings.finite_rings True We return some element in the ideal with no guarantee on the distribution:: - sage: P = PolynomialRing(GF(127), 10, 'x') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: f = I.random_element(degree=3) # optional - sage.rings.finite_rings - sage: f # random # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(127), 10, 'x') + sage: I = sage.rings.ideal.Katsura(P) + sage: f = I.random_element(degree=3) + sage: f # random -25*x0^2*x1 + 14*x1^3 + 57*x0*x1*x2 + ... + 19*x7*x9 + 40*x8*x9 + 49*x1 - sage: f.degree() # optional - sage.rings.finite_rings + sage: f.degree() 3 We show that the default method does not sample uniformly at random from the ideal:: - sage: P. = GF(127)[] # optional - sage.rings.finite_rings - sage: G = Sequence([x + 7, y - 2, z + 110]) # optional - sage.rings.finite_rings - sage: I = Ideal([sum(P.random_element() * g for g in G) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = GF(127)[] + sage: G = Sequence([x + 7, y - 2, z + 110]) + sage: I = Ideal([sum(P.random_element() * g for g in G) ....: for _ in range(4)]) - sage: all(I.random_element(degree=1) == 0 for _ in range(100)) # optional - sage.rings.finite_rings + sage: all(I.random_element(degree=1) == 0 for _ in range(100)) True If ``degree`` equals the degree of the generators, a random linear @@ -5350,33 +5389,35 @@ def weil_restriction(self): EXAMPLES:: - sage: k. = GF(2^2) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(k, 2) # optional - sage.rings.finite_rings - sage: I = Ideal([x*y + 1, a*x + 1]) # optional - sage.rings.finite_rings - sage: I.variety() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(2^2) + sage: P. = PolynomialRing(k, 2) + sage: I = Ideal([x*y + 1, a*x + 1]) + sage: I.variety() [{y: a, x: a + 1}] - sage: J = I.weil_restriction() # optional - sage.rings.finite_rings - sage: J # optional - sage.rings.finite_rings + sage: J = I.weil_restriction() + sage: J Ideal (x0*y0 + x1*y1 + 1, x1*y0 + x0*y1 + x1*y1, x1 + 1, x0 + x1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Finite Field of size 2 - sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # optional - sage.rings.finite_rings - sage: J.variety() # optional - sage.rings.finite_rings + sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal + sage: J.variety() [{y1: 1, y0: 0, x1: 1, x0: 1}] - sage: J.weil_restriction() # returns J # optional - sage.rings.finite_rings + sage: J.weil_restriction() # returns J # needs sage.rings.finite_rings Ideal (x0*y0 + x1*y1 + 1, x1*y0 + x0*y1 + x1*y1, x1 + 1, x0 + x1, x0^2 + x0, x1^2 + x1, y0^2 + y0, y1^2 + y1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Finite Field of size 2 - sage: k. = GF(3^5) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(k) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(3^5) + sage: P. = PolynomialRing(k) + sage: I = sage.rings.ideal.Katsura(P) + sage: I.dimension() 0 - sage: I.variety() # optional - sage.rings.finite_rings + sage: I.variety() [{z: 0, y: 0, x: 1}] - sage: J = I.weil_restriction(); J # optional - sage.rings.finite_rings + sage: J = I.weil_restriction(); J # needs sage.rings.finite_rings Ideal (x0 - y0 - z0 - 1, x1 - y1 - z1, x2 - y2 - z2, x3 - y3 - z3, x4 - y4 - z4, x0^2 + x2*x3 + x1*x4 - y0^2 - y2*y3 - y1*y4 - z0^2 - z2*z3 - z1*z4 - x0, @@ -5401,9 +5442,9 @@ def weil_restriction(self): - y4*z0 - y3*z1 - y2*z2 - y1*z3 - y0*z4 - y4*z4 - y4) of Multivariate Polynomial Ring in x0, x1, x2, x3, x4, y0, y1, y2, y3, y4, z0, z1, z2, z3, z4 over Finite Field of size 3 - sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # optional - sage.rings.finite_rings + sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # needs sage.rings.finite_rings sage: from sage.doctest.fixtures import reproducible_repr - sage: print(reproducible_repr(J.variety())) # optional - sage.rings.finite_rings + sage: print(reproducible_repr(J.variety())) # needs sage.rings.finite_rings [{x0: 1, x1: 0, x2: 0, x3: 0, x4: 0, y0: 0, y1: 0, y2: 0, y3: 0, y4: 0, z0: 0, z1: 0, z2: 0, z3: 0, z4: 0}] @@ -5412,15 +5453,15 @@ def weil_restriction(self): Weil restrictions are often used to study elliptic curves over extension fields so we give a simple example involving those:: - sage: K. = QuadraticField(1/3) # optional - sage.rings.number_field - sage: E = EllipticCurve(K, [1,2,3,4,5]) # optional - sage.rings.number_field + sage: K. = QuadraticField(1/3) # needs sage.rings.number_field + sage: E = EllipticCurve(K, [1,2,3,4,5]) # needs sage.rings.number_field We pick a point on ``E``:: - sage: p = E.lift_x(1); p # optional - sage.rings.number_field + sage: p = E.lift_x(1); p # needs sage.rings.number_field (1 : -6 : 1) - sage: I = E.defining_ideal(); I # optional - sage.rings.number_field + sage: I = E.defining_ideal(); I # needs sage.rings.number_field Ideal (-x^3 - 2*x^2*z + x*y*z + y^2*z - 4*x*z^2 + 3*y*z^2 - 5*z^3) of Multivariate Polynomial Ring in x, y, z over Number Field in a with defining polynomial x^2 - 1/3 @@ -5428,20 +5469,20 @@ def weil_restriction(self): Of course, the point ``p`` is a root of all generators of ``I``:: - sage: I.subs(x=1, y=2, z=1) # optional - sage.rings.number_field + sage: I.subs(x=1, y=2, z=1) # needs sage.rings.number_field Ideal (0) of Multivariate Polynomial Ring in x, y, z over Number Field in a with defining polynomial x^2 - 1/3 with a = 0.5773502691896258? ``I`` is also radical:: - sage: I.radical() == I # optional - sage.rings.number_field + sage: I.radical() == I # needs sage.rings.number_field True So we compute its Weil restriction:: - sage: J = I.weil_restriction() # optional - sage.rings.number_field - sage: J # optional - sage.rings.number_field + sage: J = I.weil_restriction() # needs sage.rings.number_field + sage: J # needs sage.rings.number_field Ideal (-x0^3 - x0*x1^2 - 2*x0^2*z0 - 2/3*x1^2*z0 + x0*y0*z0 + y0^2*z0 + 1/3*x1*y1*z0 + 1/3*y1^2*z0 - 4*x0*z0^2 + 3*y0*z0^2 - 5*z0^3 - 4/3*x0*x1*z1 + 1/3*x1*y0*z1 + 1/3*x0*y1*z1 + 2/3*y0*y1*z1 @@ -5454,19 +5495,19 @@ def weil_restriction(self): We can check that the point ``p`` is still a root of all generators of ``J``:: - sage: J.subs(x0=1, y0=2, z0=1, x1=0, y1=0, z1=0) # optional - sage.rings.number_field + sage: J.subs(x0=1, y0=2, z0=1, x1=0, y1=0, z1=0) # needs sage.rings.number_field Ideal (0, 0) of Multivariate Polynomial Ring in x0, x1, y0, y1, z0, z1 over Rational Field Example for relative number fields:: sage: R. = QQ[] - sage: K. = NumberField(x^5 - 2) # optional - sage.rings.number_field - sage: R. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(x^2 + 1) # optional - sage.rings.number_field - sage: S. = L[] # optional - sage.rings.number_field - sage: I = S.ideal([y^2 - x^3 - 1]) # optional - sage.rings.number_field - sage: I.weil_restriction() # optional - sage.rings.number_field + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: R. = K[] # needs sage.rings.number_field + sage: L. = K.extension(x^2 + 1) # needs sage.rings.number_field + sage: S. = L[] # needs sage.rings.number_field + sage: I = S.ideal([y^2 - x^3 - 1]) # needs sage.rings.number_field + sage: I.weil_restriction() # needs sage.rings.number_field Ideal (-x0^3 + 3*x0*x1^2 + y0^2 - y1^2 - 1, -3*x0^2*x1 + x1^3 + 2*y0*y1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Number Field in w with defining polynomial x^5 - 2 diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 5e6203a05c3..5e4a52943fc 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -47,17 +47,18 @@ We create a polynomial ring over a quaternion algebra:: - sage: A. = QuaternionAlgebra(QQ, -1,-1) # optional - sage.combinat sage.modules - sage: R. = PolynomialRing(A, sparse=True) # optional - sage.combinat sage.modules - sage: f = w^3 + (i+j)*w + 1 # optional - sage.combinat sage.modules - sage: f # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = QuaternionAlgebra(QQ, -1,-1) + sage: R. = PolynomialRing(A, sparse=True) + sage: f = w^3 + (i+j)*w + 1 + sage: f w^3 + (i + j)*w + 1 - sage: f^2 # optional - sage.combinat sage.modules + sage: f^2 w^6 + (2*i + 2*j)*w^4 + 2*w^3 - 2*w^2 + (2*i + 2*j)*w + 1 - sage: f = w + i ; g = w + j # optional - sage.combinat sage.modules - sage: f * g # optional - sage.combinat sage.modules + sage: f = w + i ; g = w + j + sage: f * g w^2 + (i + j)*w + k - sage: g * f # optional - sage.combinat sage.modules + sage: g * f w^2 + (i + j)*w - k :trac:`9944` introduced some changes related with @@ -88,28 +89,29 @@ different base rings. In that situation, coercion works by means of the :func:`~sage.categories.pushout.pushout` formalism:: - sage: R. = PolynomialRing(GF(5), sparse=True) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(5), sparse=True) sage: S. = PolynomialRing(ZZ) - sage: R.has_coerce_map_from(S) # optional - sage.rings.finite_rings + sage: R.has_coerce_map_from(S) False - sage: S.has_coerce_map_from(R) # optional - sage.rings.finite_rings + sage: S.has_coerce_map_from(R) False - sage: S.0 + R.0 # optional - sage.rings.finite_rings + sage: S.0 + R.0 2*x - sage: (S.0 + R.0).parent() # optional - sage.rings.finite_rings + sage: (S.0 + R.0).parent() Univariate Polynomial Ring in x over Finite Field of size 5 - sage: (S.0 + R.0).parent().is_sparse() # optional - sage.rings.finite_rings + sage: (S.0 + R.0).parent().is_sparse() False Similarly, there is a coercion from the (non-default) NTL implementation for univariate polynomials over the integers to the default FLINT implementation, but not vice versa:: - sage: R. = PolynomialRing(ZZ, implementation='NTL') # optional - sage.libs.ntl - sage: S. = PolynomialRing(ZZ, implementation='FLINT') # optional - sage.libs.flint - sage: (S.0 + R.0).parent() is S # optional - sage.libs.flint sage.libs.ntl + sage: R. = PolynomialRing(ZZ, implementation='NTL') # needs sage.libs.ntl + sage: S. = PolynomialRing(ZZ, implementation='FLINT') # needs sage.libs.flint + sage: (S.0 + R.0).parent() is S # needs sage.libs.flint sage.libs.ntl True - sage: (R.0 + S.0).parent() is S # optional - sage.libs.flint sage.libs.ntl + sage: (R.0 + S.0).parent() is S # needs sage.libs.flint sage.libs.ntl True TESTS:: @@ -122,9 +124,9 @@ Check that :trac:`5562` has been fixed:: sage: R. = PolynomialRing(RDF, 1) - sage: v1 = vector([u]) # optional - sage.modules - sage: v2 = vector([CDF(2)]) # optional - sage.modules - sage: v1 * v2 # optional - sage.modules + sage: v1 = vector([u]) # needs sage.modules + sage: v2 = vector([CDF(2)]) # needs sage.modules + sage: v1 * v2 # needs sage.modules 2.0*u """ @@ -210,11 +212,11 @@ def is_PolynomialRing(x): :: - sage: R. = PolynomialRing(ZZ, implementation="singular"); R # optional - sage.libs.singular + sage: R. = PolynomialRing(ZZ, implementation="singular"); R # needs sage.libs.singular Multivariate Polynomial Ring in w over Integer Ring - sage: is_PolynomialRing(R) # optional - sage.libs.singular + sage: is_PolynomialRing(R) # needs sage.libs.singular False - sage: type(R) # optional - sage.libs.singular + sage: type(R) # needs sage.libs.singular """ return isinstance(x, PolynomialRing_general) @@ -243,7 +245,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, and Category of commutative algebras over (euclidean domains and infinite enumerated sets and metric spaces) and Category of infinite sets - sage: category(GF(7)['x']) # optional - sage.rings.finite_rings + sage: category(GF(7)['x']) # needs sage.rings.finite_rings Join of Category of euclidean domains and Category of commutative algebras over (finite enumerated fields and subquotients of monoids and quotients of semigroups) and Category of infinite sets @@ -268,13 +270,13 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: Zmod(1)['x'].is_finite() True - sage: GF(7)['x'].is_finite() # optional - sage.rings.finite_rings + sage: GF(7)['x'].is_finite() # needs sage.rings.finite_rings False sage: Zmod(1)['x']['y'].is_finite() True - sage: GF(7)['x']['y'].is_finite() # optional - sage.rings.finite_rings + sage: GF(7)['x']['y'].is_finite() # needs sage.rings.finite_rings False """ @@ -357,9 +359,9 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, Coercing in pari elements:: - sage: QQ['x'](pari('[1,2,3/5]')) # optional - sage.libs.pari + sage: QQ['x'](pari('[1,2,3/5]')) # needs sage.libs.pari 3/5*x^2 + 2*x + 1 - sage: QQ['x'](pari('(-1/3)*x^10 + (2/3)*x - 1/5')) # optional - sage.libs.pari + sage: QQ['x'](pari('(-1/3)*x^10 + (2/3)*x - 1/5')) # needs sage.libs.pari -1/3*x^10 + 2/3*x - 1/5 Coercing strings:: @@ -377,10 +379,10 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, This shows that the issue at :trac:`4106` is fixed:: - sage: x = var('x') # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic sage: R = IntegerModRing(4) - sage: S = R['x'] # optional - sage.symbolic - sage: S(x) # optional - sage.symbolic + sage: S = R['x'] # needs sage.symbolic + sage: S(x) # needs sage.symbolic x Throw a TypeError if any of the coefficients cannot be coerced @@ -393,16 +395,17 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, Check that the bug in :trac:`11239` is fixed:: - sage: K. = GF(5^2, prefix='z') # optional - sage.rings.finite_rings - sage: L. = GF(5^4, prefix='z') # optional - sage.rings.finite_rings - sage: f = K['x'].gen() + a # optional - sage.rings.finite_rings - sage: L['x'](f) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = GF(5^2, prefix='z') + sage: L. = GF(5^4, prefix='z') + sage: f = K['x'].gen() + a + sage: L['x'](f) x + b^3 + b^2 + b + 3 A test from :trac:`14485` :: - sage: x = SR.var('x') # optional - sage.symbolic - sage: QQbar[x](x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5) # optional - sage.rings.number_field sage.symbolic + sage: x = SR.var('x') # needs sage.symbolic + sage: QQbar[x](x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5) # needs sage.rings.number_field sage.symbolic x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5 Check support for unicode characters (:trac:`29280`):: @@ -694,7 +697,7 @@ def _coerce_map_from_base_ring(self): Polynomial base injection morphism: From: Rational Field To: Univariate Polynomial Ring in x over Rational Field - sage: R.coerce_map_from(GF(7)) # optional - sage.rings.finite_rings + sage: R.coerce_map_from(GF(7)) # needs sage.rings.finite_rings """ from .polynomial_element import PolynomialBaseringInjection @@ -762,20 +765,20 @@ def _coerce_map_from_(self, P): Over the integers, there is a coercion from the NTL and generic implementation to the default FLINT implementation:: - sage: R = PolynomialRing(ZZ, 't', implementation="NTL") # optional - sage.libs.ntl - sage: S = PolynomialRing(ZZ, 't', implementation="FLINT") # optional - sage.libs.flint + sage: R = PolynomialRing(ZZ, 't', implementation="NTL") # needs sage.libs.ntl + sage: S = PolynomialRing(ZZ, 't', implementation="FLINT") # needs sage.libs.flint sage: T = PolynomialRing(ZZ, 't', implementation="generic") - sage: R.has_coerce_map_from(S) # optional - sage.libs.flint sage.libs.ntl + sage: R.has_coerce_map_from(S) # needs sage.libs.flint sage.libs.ntl False - sage: R.has_coerce_map_from(T) # optional - sage.libs.ntl + sage: R.has_coerce_map_from(T) # needs sage.libs.ntl False - sage: S.has_coerce_map_from(T) # optional - sage.libs.flint + sage: S.has_coerce_map_from(T) # needs sage.libs.flint True - sage: S.has_coerce_map_from(R) # optional - sage.libs.flint sage.libs.ntl + sage: S.has_coerce_map_from(R) # needs sage.libs.flint sage.libs.ntl True - sage: T.has_coerce_map_from(R) # optional - sage.libs.ntl + sage: T.has_coerce_map_from(R) # needs sage.libs.ntl False - sage: T.has_coerce_map_from(S) # optional - sage.libs.flint + sage: T.has_coerce_map_from(S) # needs sage.libs.flint False """ base_ring = self.base_ring() @@ -836,19 +839,20 @@ def _magma_init_(self, magma): EXAMPLES:: + sage: # optional - magma sage: R = QQ['y'] - sage: R._magma_init_(magma) # optional - magma + sage: R._magma_init_(magma) 'SageCreateWithNames(PolynomialRing(_sage_ref...),["y"])' - sage: S = magma(R) # optional - magma - sage: S # optional - magma + sage: S = magma(R) + sage: S Univariate Polynomial Ring in y over Rational Field - sage: S.1 # optional - magma + sage: S.1 y - sage: magma(PolynomialRing(GF(7), 'x')) # optional - magma # optional - sage.rings.finite_rings + sage: magma(PolynomialRing(GF(7), 'x')) # needs sage.rings.finite_rings Univariate Polynomial Ring in x over GF(7) - sage: magma(PolynomialRing(GF(49,'a'), 'x')) # optional - magma # optional - sage.rings.finite_rings + sage: magma(PolynomialRing(GF(49,'a'), 'x')) # needs sage.rings.finite_rings Univariate Polynomial Ring in x over GF(7^2) - sage: magma(PolynomialRing(PolynomialRing(ZZ,'w'), 'x')) # optional - magma + sage: magma(PolynomialRing(PolynomialRing(ZZ,'w'), 'x')) Univariate Polynomial Ring in x over Univariate Polynomial Ring in w over Integer Ring Watch out, Magma has different semantics from Sage, i.e., in Magma @@ -858,19 +862,20 @@ def _magma_init_(self, magma): :: - sage: m = Magma() # new magma session; optional - magma - sage: m(QQ['w']) # optional - magma + sage: # optional - magma + sage: m = Magma() + sage: m(QQ['w']) Univariate Polynomial Ring in w over Rational Field - sage: m(QQ['x']) # optional - magma + sage: m(QQ['x']) Univariate Polynomial Ring in x over Rational Field - sage: m(QQ['w']) # same magma object, now prints as x; optional - magma + sage: m(QQ['w']) Univariate Polynomial Ring in x over Rational Field A nested example over a Givaro finite field:: - sage: k. = GF(9) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: magma(a^2*x^3 + (a+1)*x + a) # optional - magma # optional - sage.rings.finite_rings + sage: k. = GF(9) # needs sage.rings.finite_rings + sage: R. = k[] # needs sage.rings.finite_rings + sage: magma(a^2*x^3 + (a+1)*x + a) # optional - magma, needs sage.rings.finite_rings a^2*x^3 + a^2*x + a """ B = magma(self.base_ring()) @@ -889,23 +894,24 @@ def _gap_init_(self, gap=None): EXAMPLES:: sage: R. = ZZ[] - sage: gap(R) # optional - sage.libs.gap + sage: gap(R) # needs sage.libs.gap PolynomialRing( Integers, ["z"] ) - sage: gap(R) is gap(R) # optional - sage.libs.gap + sage: gap(R) is gap(R) # needs sage.libs.gap True - sage: gap(z^2 + z) # optional - sage.libs.gap + sage: gap(z^2 + z) # needs sage.libs.gap z^2+z A univariate polynomial ring over a multivariate polynomial ring over a number field:: + sage: # needs sage.rings.number_field sage: Q. = QQ[] - sage: K. = NumberField(t^2 + t + 1) # optional - sage.rings.number_field - sage: P. = K[] # optional - sage.rings.number_field - sage: S. = P[] # optional - sage.rings.number_field - sage: gap(S) # optional - sage.libs.gap sage.rings.number_field + sage: K. = NumberField(t^2 + t + 1) + sage: P. = K[] + sage: S. = P[] + sage: gap(S) # needs sage.libs.gap PolynomialRing( PolynomialRing( , ["x", "y"] ), ["z"] ) - sage: gap(S) is gap(S) # optional - sage.libs.gap sage.rings.number_field + sage: gap(S) is gap(S) # needs sage.libs.gap True """ if gap is not None: @@ -921,11 +927,11 @@ def _sage_input_(self, sib, coerced): EXAMPLES:: - sage: sage_input(GF(5)['x']['y'], verify=True) # optional - sage.rings.finite_rings + sage: sage_input(GF(5)['x']['y'], verify=True) # needs sage.rings.finite_rings # Verified GF(5)['x']['y'] - sage: from sage.misc.sage_input import SageInputBuilder # optional - sage.rings.finite_rings - sage: ZZ['z']._sage_input_(SageInputBuilder(), False) # optional - sage.rings.finite_rings + sage: from sage.misc.sage_input import SageInputBuilder # needs sage.rings.finite_rings + sage: ZZ['z']._sage_input_(SageInputBuilder(), False) # needs sage.rings.finite_rings {constr_parent: {subscr: {atomic:ZZ}[{atomic:'z'}]} with gens: ('z',)} """ base = sib(self.base_ring()) @@ -964,9 +970,9 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): EXAMPLES:: sage: R. = QQ[] - sage: R._is_valid_homomorphism_(GF(7), [5]) # optional - sage.rings.finite_rings + sage: R._is_valid_homomorphism_(GF(7), [5]) # needs sage.rings.finite_rings False - sage: R._is_valid_homomorphism_(Qp(7), [5]) # optional - sage.rings.padics + sage: R._is_valid_homomorphism_(Qp(7), [5]) # needs sage.rings.padics True """ # Since poly rings are free, any image of the gen @@ -1044,7 +1050,7 @@ def change_ring(self, R): sage: R. = RealIntervalField()[]; R Univariate Polynomial Ring in ZZZ over Real Interval Field with 53 bits of precision - sage: R.change_ring(GF(19^2, 'b')) # optional - sage.rings.finite_rings + sage: R.change_ring(GF(19^2, 'b')) # needs sage.rings.finite_rings Univariate Polynomial Ring in ZZZ over Finite Field in b of size 19^2 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1148,9 +1154,9 @@ def characteristic(self): Univariate Polynomial Ring in ZZZ over Real Interval Field with 53 bits of precision sage: R.characteristic() 0 - sage: S = R.change_ring(GF(19^2, 'b')); S # optional - sage.rings.finite_rings + sage: S = R.change_ring(GF(19^2, 'b')); S # needs sage.rings.finite_rings Univariate Polynomial Ring in ZZZ over Finite Field in b of size 19^2 - sage: S.characteristic() + sage: S.characteristic() # needs sage.rings.finite_rings 19 """ return self.base_ring().characteristic() @@ -1165,23 +1171,24 @@ def cyclotomic_polynomial(self, n): EXAMPLES:: sage: R = ZZ['x'] - sage: R.cyclotomic_polynomial(8) # optional - sage.libs.pari + sage: R.cyclotomic_polynomial(8) # needs sage.libs.pari x^4 + 1 - sage: R.cyclotomic_polynomial(12) # optional - sage.libs.pari + sage: R.cyclotomic_polynomial(12) # needs sage.libs.pari x^4 - x^2 + 1 - sage: S = PolynomialRing(FiniteField(7), 'x') # optional - sage.rings.finite_rings - sage: S.cyclotomic_polynomial(12) # optional - sage.rings.finite_rings + + sage: S = PolynomialRing(FiniteField(7), 'x') # needs sage.rings.finite_rings + sage: S.cyclotomic_polynomial(12) # needs sage.rings.finite_rings x^4 + 6*x^2 + 1 - sage: S.cyclotomic_polynomial(1) # optional - sage.rings.finite_rings + sage: S.cyclotomic_polynomial(1) # needs sage.rings.finite_rings x + 6 TESTS: Make sure it agrees with other systems for the trivial case:: - sage: ZZ['x'].cyclotomic_polynomial(1) # optional - sage.libs.pari + sage: ZZ['x'].cyclotomic_polynomial(1) # needs sage.libs.pari x - 1 - sage: gp('polcyclo(1)') # optional - sage.libs.pari + sage: gp('polcyclo(1)') # needs sage.libs.pari x - 1 """ if n <= 0: @@ -1309,16 +1316,18 @@ def krull_dimension(self): sage: R. = QQ[] sage: R.krull_dimension() 1 - sage: R. = GF(9, 'a')[]; R # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R. = GF(9, 'a')[]; R Univariate Polynomial Ring in z over Finite Field in a of size 3^2 - sage: R.krull_dimension() # optional - sage.rings.finite_rings + sage: R.krull_dimension() 1 - sage: S. = R[] # optional - sage.rings.finite_rings - sage: S.krull_dimension() # optional - sage.rings.finite_rings + sage: S. = R[] + sage: S.krull_dimension() 2 - sage: for n in range(10): # optional - sage.rings.finite_rings + sage: for n in range(10): ....: S = PolynomialRing(S, 'w') - sage: S.krull_dimension() # optional - sage.rings.finite_rings + sage: S.krull_dimension() 12 """ return self.base_ring().krull_dimension() + 1 @@ -1392,13 +1401,13 @@ def random_element(self, degree=(-1,2), *args, **kwds): Check that :trac:`16682` is fixed:: - sage: R = PolynomialRing(GF(2), 'z') # optional - sage.rings.finite_rings - sage: for _ in range(100): # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(2), 'z') # needs sage.rings.finite_rings + sage: for _ in range(100): # needs sage.rings.finite_rings ....: d = randint(-1,20) ....: P = R.random_element(degree=d) ....: assert P.degree() == d, "problem with {} which has not degree {}".format(P,d) - sage: R.random_element(degree=-2) # optional - sage.rings.finite_rings + sage: R.random_element(degree=-2) # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: degree should be an integer greater or equal than -1 @@ -1495,12 +1504,12 @@ def _Karatsuba_threshold(self): EXAMPLES:: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: R._Karatsuba_threshold # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: R._Karatsuba_threshold # needs sage.rings.number_field 8 - sage: MS = MatrixSpace(ZZ, 2, 2) # optional - sage.modules - sage: R. = MS[] # optional - sage.modules - sage: R._Karatsuba_threshold # optional - sage.modules + sage: MS = MatrixSpace(ZZ, 2, 2) # needs sage.modules + sage: R. = MS[] # needs sage.modules + sage: R._Karatsuba_threshold # needs sage.modules 0 """ base_ring = self.base_ring() @@ -1569,8 +1578,9 @@ def polynomials( self, of_degree=None, max_degree=None ): EXAMPLES:: - sage: P = PolynomialRing(GF(3), 'y') # optional - sage.rings.finite_rings - sage: for p in P.polynomials(of_degree=2): print(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(3), 'y') + sage: for p in P.polynomials(of_degree=2): print(p) y^2 y^2 + 1 y^2 + 2 @@ -1589,7 +1599,7 @@ def polynomials( self, of_degree=None, max_degree=None ): 2*y^2 + 2*y 2*y^2 + 2*y + 1 2*y^2 + 2*y + 2 - sage: for p in P.polynomials(max_degree=1): print(p) # optional - sage.rings.finite_rings + sage: for p in P.polynomials(max_degree=1): print(p) 0 1 2 @@ -1599,7 +1609,7 @@ def polynomials( self, of_degree=None, max_degree=None ): 2*y 2*y + 1 2*y + 2 - sage: for p in P.polynomials(max_degree=1, of_degree=3): print(p) # optional - sage.rings.finite_rings + sage: for p in P.polynomials(max_degree=1, of_degree=3): print(p) Traceback (most recent call last): ... ValueError: you should pass exactly one of of_degree and max_degree @@ -1636,8 +1646,9 @@ def monics( self, of_degree=None, max_degree=None ): EXAMPLES:: - sage: P = PolynomialRing(GF(4, 'a'), 'y') # optional - sage.rings.finite_rings - sage: for p in P.monics(of_degree=2): print(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(4, 'a'), 'y') + sage: for p in P.monics(of_degree=2): print(p) y^2 y^2 + a y^2 + a + 1 @@ -1654,13 +1665,13 @@ def monics( self, of_degree=None, max_degree=None ): y^2 + y + a y^2 + y + a + 1 y^2 + y + 1 - sage: for p in P.monics(max_degree=1): print(p) # optional - sage.rings.finite_rings + sage: for p in P.monics(max_degree=1): print(p) 1 y y + a y + a + 1 y + 1 - sage: for p in P.monics(max_degree=1, of_degree=3): print(p) # optional - sage.rings.finite_rings + sage: for p in P.monics(max_degree=1, of_degree=3): print(p) Traceback (most recent call last): ... ValueError: you should pass exactly one of of_degree and max_degree @@ -1712,7 +1723,7 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): sage: R. = QQ[] sage: I = (x^2 - 1) * R - sage: R.quotient_by_principal_ideal(I) # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(I) # needs sage.libs.pari Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 - 1 @@ -1720,7 +1731,7 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): and customizing the variable name:: sage: R. = QQ[] - sage: R.quotient_by_principal_ideal(x^2 - 1, names=('foo',)) # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(x^2 - 1, names=('foo',)) # needs sage.libs.pari Univariate Quotient Polynomial Ring in foo over Rational Field with modulus x^2 - 1 @@ -1729,9 +1740,9 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): Quotienting by the zero ideal returns ``self`` (:trac:`5978`):: sage: R = QQ['x'] - sage: R.quotient_by_principal_ideal(R.zero_ideal()) is R # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(R.zero_ideal()) is R # needs sage.libs.pari True - sage: R.quotient_by_principal_ideal(0) is R # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(0) is R # needs sage.libs.pari True """ from sage.rings.ideal import Ideal @@ -1749,9 +1760,9 @@ def weyl_algebra(self): EXAMPLES:: sage: R = QQ['x'] - sage: W = R.weyl_algebra(); W # optional - sage.combinat sage.modules + sage: W = R.weyl_algebra(); W # needs sage.combinat sage.modules Differential Weyl algebra of polynomials in x over Rational Field - sage: W.polynomial_ring() == R # optional - sage.combinat sage.modules + sage: W.polynomial_ring() == R # needs sage.combinat sage.modules True """ from sage.algebras.weyl_algebra import DifferentialWeylAlgebra @@ -1814,12 +1825,12 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing sage: R = PRing(ZZ, 'x'); R Univariate Polynomial Ring in x over Integer Ring - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl """ self._implementation_repr = '' @@ -1887,20 +1898,20 @@ def weil_polynomials(self, d, q, sign=1, lead=1): EXAMPLES:: + sage: # needs sage.libs.flint sage: R. = ZZ[] - sage: L = R.weil_polynomials(4, 2) # optional - sage.libs.flint - sage: len(L) # optional - sage.libs.flint + sage: L = R.weil_polynomials(4, 2) + sage: len(L) 35 - sage: L[9] # optional - sage.libs.flint + sage: L[9] T^4 + T^3 + 2*T^2 + 2*T + 4 - sage: all(p.is_weil_polynomial() for p in L) # optional - sage.libs.flint + sage: all(p.is_weil_polynomial() for p in L) True Setting multiple leading coefficients:: sage: R. = QQ[] - sage: l = R.weil_polynomials(4, 2, lead=((1,0), (2,4), (1,2))) # optional - sage.libs.flint - sage: l # optional - sage.libs.flint + sage: l = R.weil_polynomials(4, 2, lead=((1,0), (2,4), (1,2))); l # needs sage.libs.flint [T^4 + 2*T^3 + 5*T^2 + 4*T + 4, T^4 + 2*T^3 + 3*T^2 + 4*T + 4, T^4 - 2*T^3 + 5*T^2 - 4*T + 4, @@ -1910,43 +1921,43 @@ def weil_polynomials(self, d, q, sign=1, lead=1): polynomials associated to K3 surfaces over `\GF{2}` of Picard number at least 12:: sage: R. = QQ[] - sage: l = R.weil_polynomials(10, 1, lead=2) # optional - sage.libs.flint - sage: len(l) # optional - sage.libs.flint + sage: l = R.weil_polynomials(10, 1, lead=2) # needs sage.libs.flint + sage: len(l) # needs sage.libs.flint 4865 - sage: l[len(l)//2] # optional - sage.libs.flint + sage: l[len(l)//2] # needs sage.libs.flint 2*T^10 + T^8 + T^6 + T^4 + T^2 + 2 TESTS: We check that products of Weil polynomials are also listed as Weil polynomials:: - sage: all((f * g) in R.weil_polynomials(6, q) for q in [3, 4] # optional - sage.libs.flint + sage: all((f * g) in R.weil_polynomials(6, q) for q in [3, 4] # needs sage.libs.flint ....: for f in R.weil_polynomials(2, q) for g in R.weil_polynomials(4, q)) True We check that irreducible Weil polynomials of degree 6 are CM:: - sage: simples = [f for f in R.weil_polynomials(6, 3) if f.is_irreducible()] # optional - sage.libs.flint - sage: len(simples) # optional - sage.libs.flint + sage: simples = [f for f in R.weil_polynomials(6, 3) if f.is_irreducible()] # needs sage.libs.flint + sage: len(simples) # needs sage.libs.flint 348 - sage: reals = [R([f[3+i] + sum((-3)^j * (i+2*j)/(i+j) * binomial(i+j,j) * f[3+i+2*j] # optional - sage.libs.flint + sage: reals = [R([f[3+i] + sum((-3)^j * (i+2*j)/(i+j) * binomial(i+j,j) * f[3+i+2*j] # needs sage.libs.flint ....: for j in range(1, (3+i)//2 + 1)) ....: for i in range(4)]) for f in simples] Check that every polynomial in this list has 3 real roots between `-2 \sqrt{3}` and `2 \sqrt{3}`:: - sage: roots = [f.roots(RR, multiplicities=False) for f in reals] # optional - sage.libs.flint - sage: all(len(L) == 3 and all(x^2 <= 12 for x in L) for L in roots) # optional - sage.libs.flint + sage: roots = [f.roots(RR, multiplicities=False) for f in reals] # needs sage.libs.flint + sage: all(len(L) == 3 and all(x^2 <= 12 for x in L) for L in roots) # needs sage.libs.flint True Finally, check that the original polynomials are reconstructed as CM polynomials:: - sage: all(f == T^3*r(T + 3/T) for (f, r) in zip(simples, reals)) # optional - sage.libs.flint + sage: all(f == T^3*r(T + 3/T) for (f, r) in zip(simples, reals)) # needs sage.libs.flint True A simple check (not sufficient):: - sage: all(f.number_of_real_roots() == 0 for f in simples) # optional - sage.libs.flint + sage: all(f.number_of_real_roots() == 0 for f in simples) # needs sage.libs.flint True """ R = self.base_ring() @@ -1985,7 +1996,7 @@ def _repr_(self): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) """ s = PolynomialRing_commutative._repr_(self) @@ -2005,11 +2016,11 @@ def construction(self): sage: functor.implementation is None True - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) - sage: functor, arg = R.construction(); functor, arg # optional - sage.libs.ntl + sage: functor, arg = R.construction(); functor, arg # needs sage.libs.ntl (Poly[x], Integer Ring) - sage: functor.implementation # optional - sage.libs.ntl + sage: functor.implementation # needs sage.libs.ntl 'NTL' """ implementation = None @@ -2034,7 +2045,7 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_field as PRing sage: R = PRing(QQ, 'x'); R Univariate Polynomial Ring in x over Rational Field - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint sage: R = PRing(QQ, 'x', sparse=True); R Sparse Univariate Polynomial Ring in x over Rational Field @@ -2047,8 +2058,8 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, Demonstrate that :trac:`8762` is fixed:: - sage: R. = PolynomialRing(GF(next_prime(10^20)), sparse=True) # optional - sage.rings.finite_rings - sage: x^(10^20) # this should be fast + sage: R. = PolynomialRing(GF(next_prime(10^20)), sparse=True) # needs sage.rings.finite_rings + sage: x^(10^20) # this should be fast # needs sage.rings.finite_rings x^100000000000000000000 """ def _element_class(): @@ -2094,8 +2105,8 @@ def _ideal_class_(self, n=0): EXAMPLES:: - sage: R. = GF(5)[] # optional - sage.rings.finite_rings - sage: R._ideal_class_() # optional - sage.rings.finite_rings + sage: R. = GF(5)[] # needs sage.rings.finite_rings + sage: R._ideal_class_() # needs sage.rings.finite_rings """ from sage.rings.polynomial.ideal import Ideal_1poly_field @@ -2247,15 +2258,17 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r -2 sage: f(-4) 9 - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: f = R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)]); f # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') + sage: a = R.base_ring().gen() + sage: f = R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)]); f a^2*x^2 + a^2*x + a^2 - sage: f(a^2 + a) # optional - sage.rings.finite_rings + sage: f(a^2 + a) a - sage: f(a) # optional - sage.rings.finite_rings + sage: f(a) 1 - sage: f(a^2) # optional - sage.rings.finite_rings + sage: f(a^2) a^2 + a + 1 Now use a memory efficient version of Neville's method:: @@ -2267,9 +2280,10 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r -11/7*x + 19/7, -17/42*x^2 - 83/42*x + 53/7, -23/84*x^3 - 11/84*x^2 + 13/7*x + 1] - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # optional - sage.rings.finite_rings + + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # needs sage.rings.finite_rings + sage: a = R.base_ring().gen() # needs sage.rings.finite_rings + sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # needs sage.rings.finite_rings ....: algorithm="neville") [a^2 + a + 1, x + a + 1, a^2*x^2 + a^2*x + a^2] @@ -2281,10 +2295,12 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r sage: R.lagrange_polynomial([(0,1), (2,2), (3,-2), (-4,9)], ....: algorithm="neville", previous_row=p)[-1] -23/84*x^3 - 11/84*x^2 + 13/7*x + 1 - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: p = R.lagrange_polynomial([(a^2+a, a), (a, 1)], algorithm="neville") # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') + sage: a = R.base_ring().gen() + sage: p = R.lagrange_polynomial([(a^2+a, a), (a, 1)], algorithm="neville") + sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], ....: algorithm="neville", previous_row=p)[-1] a^2*x^2 + a^2*x + a^2 @@ -2327,10 +2343,10 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r Check that base fields of positive characteristic are treated correctly (see :trac:`9787`):: - sage: R. = GF(101)[] # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([[1, 0], [2, 0]]) # optional - sage.rings.finite_rings + sage: R. = GF(101)[] # needs sage.rings.finite_rings + sage: R.lagrange_polynomial([[1, 0], [2, 0]]) # needs sage.rings.finite_rings 0 - sage: R.lagrange_polynomial([[1, 0], [2, 0], [3, 0]]) # optional - sage.rings.finite_rings + sage: R.lagrange_polynomial([[1, 0], [2, 0], [3, 0]]) # needs sage.rings.finite_rings 0 """ # Perhaps we should be slightly stricter on the input and use @@ -2416,8 +2432,8 @@ def fraction_field(self): EXAMPLES:: - sage: R. = GF(5)[] # optional - sage.rings.finite_rings - sage: R.fraction_field() # optional - sage.rings.finite_rings + sage: R. = GF(5)[] # needs sage.rings.finite_rings + sage: R.fraction_field() # needs sage.rings.finite_rings Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 5 @@ -2425,16 +2441,18 @@ def fraction_field(self): Check that :trac:`25449` has been resolved:: - sage: k = GF(25453) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: t(x) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(25453) + sage: F. = FunctionField(k) + sage: R. = k[] + sage: t(x) x - sage: k = GF(55667) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: t(x) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(55667) + sage: F. = FunctionField(k) + sage: R. = k[] + sage: t(x) x """ @@ -2454,24 +2472,24 @@ class PolynomialRing_dense_finite_field(PolynomialRing_field): EXAMPLES:: - sage: R = PolynomialRing(GF(27, 'a'), 'x') # optional - sage.rings.finite_rings - sage: type(R) # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(27, 'a'), 'x') # needs sage.rings.finite_rings + sage: type(R) # needs sage.rings.finite_rings """ def __init__(self, base_ring, name="x", element_class=None, implementation=None): """ TESTS:: - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field # optional - sage.rings.finite_rings - sage: R = PolynomialRing_dense_finite_field(GF(5), implementation='generic') # optional - sage.rings.finite_rings - sage: type(R(0)) # optional - sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field + sage: R = PolynomialRing_dense_finite_field(GF(5), implementation='generic') + sage: type(R(0)) - sage: S = PolynomialRing_dense_finite_field(GF(25, 'a'), implementation='NTL') # optional - sage.rings.finite_rings - sage: type(S(0)) # optional - sage.rings.finite_rings + sage: S = PolynomialRing_dense_finite_field(GF(25, 'a'), implementation='NTL') # needs sage.rings.finite_rings + sage: type(S(0)) # needs sage.rings.finite_rings - sage: S = PolynomialRing_dense_finite_field(GF(64), implementation='superfast') # optional - sage.rings.finite_rings + sage: S = PolynomialRing_dense_finite_field(GF(64), implementation='superfast') # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: unknown implementation 'superfast' for dense polynomial rings over Finite Field in z6 of size 2^6 @@ -2500,16 +2518,17 @@ def _implementation_names_impl(implementation, base_ring, sparse): """ TESTS:: - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field # optional - sage.rings.finite_rings - sage: PolynomialRing_dense_finite_field._implementation_names_impl("NTL", GF(4), False) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field + sage: PolynomialRing_dense_finite_field._implementation_names_impl("NTL", GF(4), False) ['NTL', None] - sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), False) ['NTL', None] - sage: PolynomialRing_dense_finite_field._implementation_names_impl("generic", GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl("generic", GF(4), False) ['generic'] - sage: PolynomialRing_dense_finite_field._implementation_names_impl("FLINT", GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl("FLINT", GF(4), False) NotImplemented - sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), True) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), True) NotImplemented """ if sparse: @@ -2544,16 +2563,17 @@ def irreducible_element(self, n, algorithm=None): EXAMPLES:: - sage: f = GF(5^3, 'a')['x'].irreducible_element(2) # optional - sage.rings.finite_rings - sage: f.degree() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: f = GF(5^3, 'a')['x'].irreducible_element(2) + sage: f.degree() 2 - sage: f.is_irreducible() # optional - sage.rings.finite_rings + sage: f.is_irreducible() True - sage: R = GF(19)['x'] # optional - sage.rings.finite_rings - sage: R.irreducible_element(21, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: R = GF(19)['x'] + sage: R.irreducible_element(21, algorithm="first_lexicographic") x^21 + x + 5 - sage: R = GF(5**2, 'a')['x'] # optional - sage.rings.finite_rings - sage: R.irreducible_element(17, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: R = GF(5**2, 'a')['x'] + sage: R.irreducible_element(17, algorithm="first_lexicographic") x^17 + a*x + 4*a + 3 AUTHORS: @@ -2614,15 +2634,16 @@ def _roth_ruckenstein(self, p, degree_bound, precision): EXAMPLES:: - sage: F = GF(17) # optional - sage.rings.finite_rings - sage: Px. = F[] # optional - sage.rings.finite_rings - sage: Pxy. = Px[] # optional - sage.rings.finite_rings - sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16)) # optional - sage.rings.finite_rings - sage: Px._roth_ruckenstein(p, 3, None) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(17) + sage: Px. = F[] + sage: Pxy. = Px[] + sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16)) + sage: Px._roth_ruckenstein(p, 3, None) [x^3 + 4*x + 16, x^2 + x + 1] - sage: Px._roth_ruckenstein(p, 2, None) # optional - sage.rings.finite_rings + sage: Px._roth_ruckenstein(p, 2, None) [x^2 + x + 1] - sage: Px._roth_ruckenstein(p, 1, 2) # optional - sage.rings.finite_rings + sage: Px._roth_ruckenstein(p, 1, 2) [(4*x + 16, 2), (2*x + 13, 2), (15*x + 4, 2), (x + 1, 2)] """ def roth_rec(p, lam, k, g): @@ -2713,29 +2734,31 @@ def _alekhnovich(self, p, degree_bound, precision=None, dc_threshold=None): EXAMPLES:: - sage: R. = GF(17)[] # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: p = (y - 2*x^2 - 3*x - 14) * (y - 3*x + 2) * (y - 1) # optional - sage.rings.finite_rings - sage: R._alekhnovich(p, 2) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(17)[] + sage: S. = R[] + sage: p = (y - 2*x^2 - 3*x - 14) * (y - 3*x + 2) * (y - 1) + sage: R._alekhnovich(p, 2) [3*x + 15, 2*x^2 + 3*x + 14, 1] - sage: R._alekhnovich(p, 1) # optional - sage.rings.finite_rings + sage: R._alekhnovich(p, 1) [3*x + 15, 1] - sage: R._alekhnovich(p, 1, precision=2) # optional - sage.rings.finite_rings + sage: R._alekhnovich(p, 1, precision=2) [(3*x + 15, 2), (3*x + 14, 2), (1, 2)] Example of benchmark to check that `dc_threshold = None` is better:: - sage: p = prod(y - R.random_element(20) # not tested # optional - sage.rings.finite_rings + sage: # not tested, needs sage.rings.finite_rings + sage: p = prod(y - R.random_element(20) ....: for _ in range(10)) * S.random_element(10,10) - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = None) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = None) 1 loop, best of 3: 418 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 1) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 1) 1 loop, best of 3: 416 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 2) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 2) 1 loop, best of 3: 418 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 3) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 3) 1 loop, best of 3: 454 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 4) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 4) 1 loop, best of 3: 519 ms per loop AUTHORS: @@ -2825,24 +2848,25 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=False, algor EXAMPLES:: - sage: R. = GF(13)[] # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1 # optional - sage.rings.finite_rings - sage: p.roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(13)[] + sage: S. = R[] + sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1 + sage: p.roots(multiplicities=False) [x^2 + 11*x + 1, x + 1] - sage: p.roots(multiplicities=False, degree_bound=1) # optional - sage.rings.finite_rings + sage: p.roots(multiplicities=False, degree_bound=1) [x + 1] - sage: p.roots(multiplicities=False, algorithm="Roth-Ruckenstein") # optional - sage.rings.finite_rings + sage: p.roots(multiplicities=False, algorithm="Roth-Ruckenstein") [x^2 + 11*x + 1, x + 1] TESTS: Check that :trac:`23639` is fixed:: - sage: R = GF(3)['x']['y'] # optional - sage.rings.finite_rings - sage: R.one().roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: R = GF(3)['x']['y'] # needs sage.rings.finite_rings + sage: R.one().roots(multiplicities=False) # needs sage.rings.finite_rings [] - sage: R.zero().roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: R.zero().roots(multiplicities=False) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: roots of 0 are not defined @@ -2887,7 +2911,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: isinstance(S, PolynomialRing_cdvr) False - sage: S. = Zp(5)[] # optional - sage.rings.padics + sage: S. = Zp(5)[] # needs sage.rings.padics sage: isinstance(S, PolynomialRing_cdvr) True """ @@ -2918,8 +2942,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: isinstance(S, PolynomialRing_cdvf) False - sage: S. = Qp(5)[] # optional - sage.rings.padics - sage: isinstance(S, PolynomialRing_cdvf) # optional - sage.rings.padics + sage: S. = Qp(5)[] # needs sage.rings.padics + sage: isinstance(S, PolynomialRing_cdvf) # needs sage.rings.padics True """ if element_class is None: @@ -2951,11 +2975,11 @@ def _implementation_names_impl(implementation, base_ring, sparse): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_generic - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), False) # needs sage.rings.padics [None] - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), True) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), True) # needs sage.rings.padics NotImplemented - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl("generic", Zp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl("generic", Zp(2), False) # needs sage.rings.padics NotImplemented """ if implementation is None and not sparse: @@ -2980,11 +3004,11 @@ def _implementation_names_impl(implementation, base_ring, sparse): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_field_generic - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), False) # needs sage.rings.padics [None] - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), True) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), True) # needs sage.rings.padics NotImplemented - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl("generic", Qp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl("generic", Qp(2), False) # needs sage.rings.padics NotImplemented """ if implementation is None and not sparse: @@ -2998,9 +3022,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_capped_relative as PRing - sage: R = PRing(Zp(13), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring with capped relative precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3019,9 +3043,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_capped_absolute as PRing - sage: R = PRing(Zp(13, type='capped-abs'), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13, type='capped-abs'), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring with capped absolute precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3039,10 +3063,10 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_fixed_mod as PRing - sage: R = PRing(Zp(13, type='fixed-mod'), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13, type='fixed-mod'), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring of fixed modulus 13^20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3060,9 +3084,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_field_capped_relative as PRing - sage: R = PRing(Qp(13), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Qp(13), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Field with capped relative precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3084,27 +3108,27 @@ def __init__(self, base_ring, name=None, element_class=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_n as PRing sage: R = PRing(Zmod(15), 'x'); R Univariate Polynomial Ring in x over Ring of integers modulo 15 - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint - sage: R = PRing(Zmod(15), 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(15), 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 15 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl - sage: R = PRing(Zmod(2**63*3), 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(2**63*3), 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 27670116110564327424 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl - sage: R = PRing(Zmod(2**63*3), 'x', implementation='FLINT') # optional - sage.libs.flint + sage: R = PRing(Zmod(2**63*3), 'x', implementation='FLINT') # needs sage.libs.flint Traceback (most recent call last): ... ValueError: FLINT does not support modulus 27670116110564327424 - sage: R = PRing(Zmod(2**63*3), 'x'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(2**63*3), 'x'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 27670116110564327424 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl """ if element_class is None: @@ -3195,7 +3219,7 @@ def _repr_(self): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) """ s = PolynomialRing_commutative._repr_(self) @@ -3207,38 +3231,39 @@ def residue_field(self, ideal, names=None): EXAMPLES:: - sage: R. = GF(2)[] # optional - sage.rings.finite_rings - sage: k. = R.residue_field(t^3 + t + 1); k # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(2)[] + sage: k. = R.residue_field(t^3 + t + 1); k Residue field in a of Principal ideal (t^3 + t + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k.list() # optional - sage.rings.finite_rings + sage: k.list() [0, a, a^2, a + 1, a^2 + a, a^2 + a + 1, a^2 + 1, 1] - sage: R.residue_field(t) # optional - sage.rings.finite_rings + sage: R.residue_field(t) Residue field of Principal ideal (t) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: P = R.irreducible_element(8) * R # optional - sage.rings.finite_rings - sage: P # optional - sage.rings.finite_rings + sage: P = R.irreducible_element(8) * R + sage: P Principal ideal (t^8 + t^4 + t^3 + t^2 + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k. = R.residue_field(P); k # optional - sage.rings.finite_rings + sage: k. = R.residue_field(P); k Residue field in a of Principal ideal (t^8 + t^4 + t^3 + t^2 + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k.cardinality() # optional - sage.rings.finite_rings + sage: k.cardinality() 256 Non-maximal ideals are not accepted:: - sage: R.residue_field(t^2 + 1) # optional - sage.rings.finite_rings + sage: R.residue_field(t^2 + 1) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal - sage: R.residue_field(0) # optional - sage.rings.finite_rings + sage: R.residue_field(0) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal - sage: R.residue_field(1) # optional - sage.rings.finite_rings + sage: R.residue_field(1) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal @@ -3256,36 +3281,35 @@ def __init__(self, base_ring, name="x", implementation=None, element_class=None, """ TESTS:: - sage: P = GF(2)['x']; P # optional - sage.rings.finite_rings + sage: P = GF(2)['x']; P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_p # optional - sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(5), 'x'); P # optional - sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_p # needs sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(5), 'x'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 5 - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(5), 'x', implementation='NTL'); P # optional - sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(5), 'x', implementation='NTL'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 5 (using NTL) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(9223372036854775837), 'x') # optional - sage.rings.finite_rings - sage: P # optional - sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(9223372036854775837), 'x'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 9223372036854775837 (using NTL) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings This caching bug was fixed in :trac:`24264`:: sage: p = 2^64 + 13 - sage: A = GF(p^2) # optional - sage.rings.finite_rings - sage: B = GF(p^3) # optional - sage.rings.finite_rings - sage: R = A.modulus().parent() # optional - sage.rings.finite_rings - sage: S = B.modulus().parent() # optional - sage.rings.finite_rings - sage: R is S # optional - sage.rings.finite_rings + sage: A = GF(p^2) # needs sage.rings.finite_rings + sage: B = GF(p^3) # needs sage.rings.finite_rings + sage: R = A.modulus().parent() # needs sage.rings.finite_rings + sage: S = B.modulus().parent() # needs sage.rings.finite_rings + sage: R is S # needs sage.rings.finite_rings True """ if element_class is None: @@ -3327,15 +3351,16 @@ def _implementation_names_impl(implementation, base_ring, sparse): """ TESTS:: - sage: PolynomialRing(GF(2), 'x', implementation="GF2X") # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="GF2X") Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation="NTL") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="NTL") Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation=None) # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation=None) Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation="FLINT") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="FLINT") Univariate Polynomial Ring in x over Finite Field of size 2 - sage: PolynomialRing(GF(3), 'x', implementation="GF2X") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(3), 'x', implementation="GF2X") Traceback (most recent call last): ... ValueError: GF2X only supports modulus 2 @@ -3407,35 +3432,36 @@ def irreducible_element(self, n, algorithm=None): EXAMPLES:: - sage: GF(5)['x'].irreducible_element(2) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2) x^2 + 4*x + 2 - sage: GF(5)['x'].irreducible_element(2, algorithm="adleman-lenstra") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2, algorithm="adleman-lenstra") x^2 + x + 1 - sage: GF(5)['x'].irreducible_element(2, algorithm="primitive") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2, algorithm="primitive") x^2 + 4*x + 2 - sage: GF(5)['x'].irreducible_element(32, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="first_lexicographic") x^32 + 2 - sage: GF(5)['x'].irreducible_element(32, algorithm="conway") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="conway") Traceback (most recent call last): ... RuntimeError: requested Conway polynomial not in database. - sage: GF(5)['x'].irreducible_element(32, algorithm="primitive") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="primitive") x^32 + ... In characteristic 2:: - sage: GF(2)['x'].irreducible_element(33) # optional - sage.rings.finite_rings + sage: GF(2)['x'].irreducible_element(33) # needs sage.rings.finite_rings x^33 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^3 + 1 - sage: GF(2)['x'].irreducible_element(33, algorithm="minimal_weight") # optional - sage.rings.finite_rings + sage: GF(2)['x'].irreducible_element(33, algorithm="minimal_weight") # needs sage.rings.finite_rings x^33 + x^10 + 1 In degree 1:: - sage: GF(97)['x'].irreducible_element(1) # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1) # needs sage.rings.finite_rings x + 96 - sage: GF(97)['x'].irreducible_element(1, algorithm="conway") # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1, algorithm="conway") # needs sage.rings.finite_rings x + 92 - sage: GF(97)['x'].irreducible_element(1, algorithm="adleman-lenstra") # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1, algorithm="adleman-lenstra") # needs sage.rings.finite_rings x AUTHORS: diff --git a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py index d2bdefa5e3a..b1121543de6 100644 --- a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py +++ b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py @@ -1147,7 +1147,7 @@ def frobenius_polynomial(self): + 24687045654725446027864774006541463602997309796*x^10 + 11320844849639649951608809973589776933203136765026963553258401 - sage; h = PolynomialRing(GF(1009^2), 'x')([-1] + [0]*(5-1) + [1]) + sage: h = PolynomialRing(GF(1009^2), 'x')([-1] + [0]*(5-1) + [1]) sage: CyclicCover(3, h).frobenius_polynomial() # long time x^8 + 532*x^7 - 2877542*x^6 - 242628176*x^5 + 4390163797795*x^4 - 247015136050256*x^3 - 2982540407204025062*x^2 + 561382189105547134612*x + 1074309286591662654798721 diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 71e620c643a..71f4b86d29b 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -47,8 +47,8 @@ sage: E = EllipticCurve('43a'); P = E.heegner_point(-7) sage: P.x_poly_exact() x - sage: P.point_exact() - (0 : 0 : 1) + sage: z = P.point_exact(); z == E(0,0,1) or -z == E(0,0,1) + True sage: E = EllipticCurve('997a') sage: E.rank() @@ -58,16 +58,17 @@ sage: P = E.heegner_point(-19) sage: P.x_poly_exact() x - 141/49 - sage: P.point_exact() - (141/49 : -162/343 : 1) + sage: z = P.point_exact(); z == E(141/49, -162/343, 1) or -z == E(141/49, -162/343, 1) + True Here we find that the Heegner point generates a subgroup of index 3:: sage: E = EllipticCurve('92b1') sage: E.heegner_discriminants_list(1) [-7] - sage: P = E.heegner_point(-7); z = P.point_exact(); z - (0 : 1 : 1) + sage: P = E.heegner_point(-7) + sage: z = P.point_exact(); z == E(0, 1, 1) or -z == E(0, 1, 1) + True sage: E.regulator() 0.0498083972980648 sage: z.height() @@ -6421,8 +6422,8 @@ def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True): [-7, -11, -40, -47, -67, -71, -83, -84, -95, -104] sage: P = E.heegner_point(-7); P # indirect doctest Heegner point of discriminant -7 on elliptic curve of conductor 37 - sage: P.point_exact() - (0 : 0 : 1) + sage: z = P.point_exact(); z == E(0, 0, 1) or -z == E(0, 0, 1) + True sage: P.curve() Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: P = E.heegner_point(-40).point_exact(); P @@ -7139,14 +7140,15 @@ def heegner_sha_an(self, D, prec=53): 2.3 in [GZ1986]_ page 311, then that conjecture is false, as the following example shows:: - sage: E = EllipticCurve('65a') # long time - sage: E.heegner_sha_an(-56) # long time + sage: # long time + sage: E = EllipticCurve('65a') + sage: E.heegner_sha_an(-56) 1.00000000000000 - sage: E.torsion_order() # long time + sage: E.torsion_order() 2 - sage: E.tamagawa_product() # long time + sage: E.tamagawa_product() 1 - sage: E.quadratic_twist(-56).rank() # long time + sage: E.quadratic_twist(-56).rank() 2 """ # check conditions, then return from cache if possible. diff --git a/src/sage/schemes/elliptic_curves/hom_frobenius.py b/src/sage/schemes/elliptic_curves/hom_frobenius.py index 38f22f1fcdf..b5f2b51d470 100644 --- a/src/sage/schemes/elliptic_curves/hom_frobenius.py +++ b/src/sage/schemes/elliptic_curves/hom_frobenius.py @@ -269,10 +269,14 @@ def _eval(self, P): sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius sage: E = EllipticCurve(GF(11), [1,1]) sage: pi = EllipticCurveHom_frobenius(E) - sage: P = E.change_ring(GF(11^6)).lift_x(GF(11^3).gen()); P - (6*z6^5 + 8*z6^4 + 8*z6^3 + 6*z6^2 + 10*z6 + 5 : 2*z6^5 + 2*z6^4 + 2*z6^3 + 4*z6 + 6 : 1) - sage: pi._eval(P) - (z6^5 + 3*z6^4 + 3*z6^3 + 6*z6^2 + 9 : z6^5 + 10*z6^4 + 10*z6^3 + 5*z6^2 + 4*z6 + 8 : 1) + sage: Ebar = E.change_ring(GF(11^6)) + sage: z6 = GF(11^6).gen() + sage: P = Ebar.lift_x(GF(11^3).gen()) + sage: p = Ebar(6*z6^5 + 8*z6^4 + 8*z6^3 + 6*z6^2 + 10*z6 + 5, 2*z6^5 + 2*z6^4 + 2*z6^3 + 4*z6 + 6, 1) + sage: Q = pi._eval(P) + sage: q = Ebar(z6^5 + 3*z6^4 + 3*z6^3 + 6*z6^2 + 9, z6^5 + 10*z6^4 + 10*z6^3 + 5*z6^2 + 4*z6 + 8, 1) + sage: (P == p and Q == q) or (P == -p and Q == -q) + True """ if self._domain.defining_polynomial()(*P): raise ValueError(f'{P} not on {self._domain}')