diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index 1a6fbeef27b..cdae3a49df8 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -62,21 +62,27 @@ for block in diff_blocks: match = re.search(r'^diff --git a/(.*) b/\1', block, flags=re.MULTILINE) if match: doc = match.group(1) - path = 'html/' + doc file_path = os.path.join('$DOC_REPOSITORY', doc) - with open(file_path, 'r') as file: - content = file.readlines() + try: + with open(file_path, 'r') as file: + content = file.readlines() + except FileNotFoundError: + content = [] count = 0 for line in block.splitlines(): if line.startswith('@@ -'): - line_number = int(re.search(r'@@ -(\d+)', line).group(1)) - for i in range(line_number, -1, -1): - if content[i].startswith('<'): - count += 1 - content[i] = f'' + content[i] - break - with open(file_path, 'w') as file: - file.writelines(content) + search_result = re.search(r'@@ -(\d+),(\d+) \+(\d+),(\d+)', line) + if search_result: + line_number = int(search_result.group(3)) + for i in range(line_number - 1, -1, -1): + if content[i].startswith('<'): + count += 1 + content[i] = f'' + content[i] + break + if content: + with open(file_path, 'w') as file: + file.writelines(content) + path = 'html/' + doc hunks = ' '.join(f'#{i + 1}' for i in range(count)) out_blocks.append(f'

{doc} ' + hunks + ' 

' + '\n
'
diff --git a/.github/sync_labels.py b/.github/sync_labels.py
index 97ddf039a16..dbd8566c517 100755
--- a/.github/sync_labels.py
+++ b/.github/sync_labels.py
@@ -141,6 +141,7 @@ def __init__(self, url, actor):
         self._commits = None
         self._commit_date = None
         self._bot_login = None
+        self._gh_version = None
 
         s = url.split('/')
         self._owner = s[3]
@@ -235,13 +236,30 @@ def bot_login(self):
         """
         if self._bot_login:
             return self._bot_login
-        cmd = 'gh auth status'
         from subprocess import run
+        cmd = 'gh version'
+        capt = run(cmd, shell=True, capture_output=True)
+        self._gh_version = str(capt.stdout).split('\\n')[0]
+        info('version: %s' % self._gh_version)
+        cmd = 'gh auth status'
         capt = run(cmd, shell=True, capture_output=True)
-        l = str(capt.stderr).split()
-        if not 'as' in l:
-            l = str(capt.stdout).split()
-        self._bot_login = l[l.index('as')+1]
+        errtxt = str(capt.stderr)
+        outtxt = str(capt.stdout)
+        debug('auth status err: %s' % errtxt)
+        debug('auth status out: %s' % outtxt)
+        def read_login(txt, position_mark):
+            for t in txt:
+                for p in position_mark:
+                    # the output text has changed from as to account
+                    # around version 2.40.0
+                    l = t.split()
+                    if p in l:
+                        return l[l.index(p)+1]
+        self._bot_login = read_login([errtxt, outtxt], ['account', 'as'])
+        if not self._bot_login:
+            self._bot_login = default_bot
+            warning('Bot is unknown')
+            return self._bot_login
         if self._bot_login.endswith('[bot]'):
             self._bot_login = self._bot_login.split('[bot]')[0]
         info('Bot is %s' % self._bot_login)
diff --git a/.github/workflows/ci-conda-known-test-failures.json b/.github/workflows/ci-conda-known-test-failures.json
new file mode 100644
index 00000000000..2d828ac98fb
--- /dev/null
+++ b/.github/workflows/ci-conda-known-test-failures.json
@@ -0,0 +1,53 @@
+{
+    "sage_setup.clean": {
+        "failed": true
+    },
+    "sage.combinat.cluster_algebra_quiver.quiver": {
+        "failed": true
+    },
+    "sage.geometry.cone": {
+        "failed": true
+    },
+    "sage.groups.matrix_gps.finitely_generated_gap": {
+        "failed": true
+    },
+    "sage.interfaces.expect": {
+        "failed": true
+    },
+    "sage.libs.gap.element": {
+        "failed": true
+    },
+    "sage.libs.singular.singular": {
+        "failed": true
+    },
+    "sage.matrix.matrix2": {
+        "failed": true
+    },
+    "sage.matrix.matrix_integer_sparse": {
+        "failed": true
+    },
+    "sage.misc.lazy_import": {
+        "failed": true
+    },
+    "sage.misc.weak_dict": {
+        "failed": true
+    },
+    "sage.modular.modform.l_series_gross_zagier": {
+        "failed": true
+    },
+    "sage.rings.function_field.drinfeld_modules.morphism": {
+        "failed": true
+    },
+    "sage.rings.polynomial.multi_polynomial_ideal": {
+        "failed": true
+    },
+    "sage.rings.polynomial.multi_polynomial_libsingular": {
+        "failed": true
+    },
+    "sage.rings.polynomial.skew_polynomial_finite_field": {
+        "failed": true
+    },
+    "sage.tests.gap_packages": {
+        "failed": true
+    }
+}
diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml
index 3b928fef58d..6432b7a0a6a 100644
--- a/.github/workflows/ci-conda.yml
+++ b/.github/workflows/ci-conda.yml
@@ -92,7 +92,7 @@ jobs:
       - name: Test
         if: success() || failure()
         shell: bash -l {0}
-        run: ./sage -t --all -p0
+        run: ./sage -t --all --baseline-stats-path=.github/workflows/ci-conda-known-test-failures.json -p0
 
       - name: Print logs
         if: always()
diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml
index bfa32760934..7ae675d9e64 100644
--- a/.github/workflows/doc-build-pdf.yml
+++ b/.github/workflows/doc-build-pdf.yml
@@ -57,7 +57,7 @@ jobs:
           export PATH="build/bin:$PATH"
           eval $(sage-print-system-package-command auto update)
           eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip)
-          eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive)
+          eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy)
 
       - name: Add prebuilt tree as a worktree
         id: worktree
diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml
index 0c5200dbe81..9d82909ef5f 100644
--- a/.github/workflows/doc-build.yml
+++ b/.github/workflows/doc-build.yml
@@ -161,7 +161,7 @@ jobs:
           export PATH="build/bin:$PATH"
           eval $(sage-print-system-package-command auto update)
           eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip)
-          eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive)
+          eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy)
           export SAGE_USE_CDNS=yes
           export SAGE_LIVE_DOC=yes
           export SAGE_JUPYTER_SERVER=binder:sagemath/sage-binder-env/dev
diff --git a/CITATION.cff b/CITATION.cff
index d05315fa584..a34996764e6 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -4,8 +4,8 @@ title: SageMath
 abstract: SageMath is a free open-source mathematics software system.
 authors:
 - name: "The SageMath Developers"
-version: 10.3.beta2
+version: 10.3.beta4
 doi: 10.5281/zenodo.593563
-date-released: 2023-12-13
+date-released: 2023-12-26
 repository-code: "https://github.com/sagemath/sage"
 url: "https://www.sagemath.org/"
diff --git a/README.md b/README.md
index 4720c2b87ab..422582292af 100644
--- a/README.md
+++ b/README.md
@@ -424,8 +424,8 @@ For installation of `sage` in python using `pip` you need to install `sagemath-s
 
             $ python3 -m pip install sage_conf
             $ ls $(sage-config SAGE_SPKG_WHEELS)
-            $ python3 -m pip install $(sage-config SAGE_SPKG_WHEELS)/*.whl
-            $ python3 -m pip install sagemath-standard
+            $ python3 -m pip install $(sage-config SAGE_SPKG_WHEELS)/*.whl sage_setup
+            $ python3 -m pip install --no-build-isolation sagemath-standard
 
 You need to install `sage_conf`, a wheelhouse of various python packages. You can list the wheels using `ls $(sage-config SAGE_SPKG_WHEELS)`. After manual installation of these wheels, you can install the sage library, `sagemath-standard`.
 
diff --git a/VERSION.txt b/VERSION.txt
index a6d9f480c1e..f34f97b89ee 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-SageMath version 10.3.beta2, Release Date: 2023-12-13
+SageMath version 10.3.beta4, Release Date: 2023-12-26
diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers
index 2dc56b62466..ac588fc297c 100644
--- a/build/bin/sage-dist-helpers
+++ b/build/bin/sage-dist-helpers
@@ -244,7 +244,7 @@ sdh_pip_install() {
                 # Our default after #33789 (Sage 9.7): We allow the package to provision
                 # its build environment using the stored wheels.
                 # We pass --find-links.
-                # The SPKG needs to declare "setuptools_wheel" as a dependency.
+                # The SPKG needs to declare "setuptools" as a dependency.
                 build_isolation_option="--find-links=$SAGE_SPKG_WHEELS"
                 ;;
             --no-build-isolation)
@@ -289,8 +289,6 @@ sdh_pip_install() {
 
 sdh_pip_editable_install() {
     echo "Installing $PKG_NAME (editable mode)"
-    # Until https://github.com/sagemath/sage/issues/34209 switches us to PEP 660 editable wheels
-    export SETUPTOOLS_ENABLE_FEATURES=legacy-editable
     python3 -m pip install --verbose --no-deps --no-index --no-build-isolation --isolated --editable "$@" || \
         sdh_die "Error installing $PKG_NAME"
 }
diff --git a/build/bin/sage-get-system-packages b/build/bin/sage-get-system-packages
index c8b9314c3fe..1b8aae79c16 100755
--- a/build/bin/sage-get-system-packages
+++ b/build/bin/sage-get-system-packages
@@ -41,7 +41,7 @@ case "$SYSTEM" in
             fi
         fi
         SYSTEM_PACKAGES_FILE_NAMES="distros/$SYSTEM.txt"
-        STRIP_COMMENTS="sed s/#.*//;"
+        STRIP_COMMENTS="sed s/#.*//;s/\${PYTHON_MINOR}/${PYTHON_MINOR}/g"
         COLLECT=echo
         ;;
 esac
diff --git a/build/bin/sage-guess-package-system b/build/bin/sage-guess-package-system
index 09eafd510c9..8e679c2eac9 100755
--- a/build/bin/sage-guess-package-system
+++ b/build/bin/sage-guess-package-system
@@ -11,6 +11,8 @@ if conda --version > /dev/null 2>&1; then
 fi
 if brew --version > /dev/null 2>&1; then
     echo homebrew
+elif port version > /dev/null 2>&1; then
+    echo macports
 elif emerge --version > /dev/null 2>&1; then
     echo gentoo
 elif apt-get --version > /dev/null 2>&1; then
diff --git a/build/bin/sage-print-system-package-command b/build/bin/sage-print-system-package-command
index c36f64e19bf..722a7f75161 100755
--- a/build/bin/sage-print-system-package-command
+++ b/build/bin/sage-print-system-package-command
@@ -34,6 +34,16 @@ do
             ;;
         --prompt)
             PROMPT='  $ '
+            CONTINUATION='        '
+            ;;
+        --continuation=*)
+            CONTINUATION="${1#--continuation=}"
+            ;;
+        --wrap)
+            WRAP=78
+            ;;
+        --wrap=*)
+            WRAP="${1#--wrap=}"
             ;;
         --no-install-recommends)
             NO_INSTALL_RECOMMENDS=yes
@@ -79,7 +89,13 @@ function print_shell_command()
         echo ".. CODE-BLOCK:: bash"
         echo
     fi
-    echo "${PROMPT}$1"
+    # shell-quote arguments if necessary
+    quoted=$(for a in "$@"; do printf "%q " $a; done)
+    if [ -z "$WRAP" -o $# -lt 6 ]; then
+        echo "${PROMPT}${quoted}"
+    else
+        sage-bootstrap-python -c "import textwrap; print(' \\\\\n'.join(textwrap.wrap(r'''${quoted}''', $WRAP, initial_indent=r'''${PROMPT}''', subsequent_indent=r'''${CONTINUATION}''', break_long_words=False, break_on_hyphens=False)))"
+    fi
     if [ -n "$OUTPUT_RST" ]; then
         echo
     fi
@@ -102,6 +118,15 @@ case $system:$command in
         [ -n "$SAGE_ROOT" ] || SAGE_ROOT=.
         echo "${PROMPT}source $SAGE_ROOT/.homebrew-build-env"
         ;;
+    macports*:setup-build-env)
+        $IF_VERBOSE echo "${COMMENT}"
+        $IF_VERBOSE echo "${COMMENT}WARNING: Use of MacPorts is experimental"
+        $IF_VERBOSE echo "${COMMENT}"
+        $IF_VERBOSE echo "${COMMENT}MacPorts does not provide unversioned gfortran executables by default"
+        $IF_VERBOSE echo "${COMMENT}To make gfortran available (and build with gcc from XCode), use:"
+        $IF_VERBOSE echo "${COMMENT}"
+        $IF_VERBOSE print_shell_command ./configure FC=gfortran-mp-11
+        ;;
     *:setup-build-env)
         # Nothing needed
         ;;
@@ -109,70 +134,74 @@ case $system:$command in
     # Verbs handled above are our own inventions. Verbs handled below are apt-get verbs.
     #
     @(debian*|ubuntu*):update)
-        print_shell_command "${SUDO}apt-get $command $system_packages"
+        print_shell_command ${SUDO}apt-get $command $system_packages
         ;;
     @(debian*|ubuntu*):*)
         [ "$NO_INSTALL_RECOMMENDS" = yes ] && options="$options --no-install-recommends"
         [ "$YES" = yes ] && options="$options --yes" env="DEBIAN_FRONTEND=noninteractive "
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}${env}apt-get $command $options $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}${env}apt-get $command $options $system_packages
         ;;
     @(fedora*|redhat*|centos*):install)
         [ "$YES" = yes ] && options="$options -y"
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}yum install $options $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}yum install $options $system_packages
         ;;
     gentoo*:install)
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}emerge $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}emerge $system_packages
         ;;
     arch*:update)
         print_shell_command "${SUDO}pacman -Sy"
         ;;
     arch*:install)
         [ "$YES" = yes ] && options="$options --noconfirm"
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}pacman -S $options $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}pacman -S $options $system_packages
         ;;
     void*:update)
         print_shell_command "${SUDO}xbps-install -Su"
         ;;
     void*:install)
         [ "$YES" = yes ] && options="$options --yes"
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}xbps-install $options $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}xbps-install $options $system_packages
         ;;
     opensuse*:install)
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}zypper install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}zypper install $system_packages
         ;;
     *conda*:install)
         [ "$YES" = yes ] && options="$options --yes"
-        [ -n "$system_packages" ] && print_shell_command "conda install $options $system_packages"
+        [ -n "$system_packages" ] && print_shell_command conda install $options $system_packages
         ;;
     homebrew*:install)
-        [ -n "$system_packages" ] && print_shell_command "brew install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command brew install $system_packages
+        ;;
+    macports*:install)
+        [ "$YES" = yes ] && options="$options -N"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}port $options install $system_packages
         ;;
     slackware*:install)
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}slackpkg install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}slackpkg install $system_packages
         ;;
     cygwin*:update)
         print_comment "first install apt-cyg from https://github.com/transcode-open/apt-cyg"
         ;;
     cygwin*:install)
-        [ -n "$system_packages" ] && print_shell_command "apt-cyg install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command apt-cyg install $system_packages
         ;;
     freebsd*:install)
-        [ -n "$system_packages" ] && print_shell_command "${SUDO}pkg install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command ${SUDO}pkg install $system_packages
         ;;
     nix*:install)
-        [ -n "$system_packages" ] && print_shell_command "nix-env --install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command nix-env --install $system_packages
         ;;
     alpine:update)
         print_shell_command "apk update"
         ;;
     alpine:install)
-        [ -n "$system_packages" ] && print_shell_command "apk add $system_packages"
+        [ -n "$system_packages" ] && print_shell_command apk add $system_packages
         ;;
     pip:install)
-        [ -n "$system_packages" ] && print_shell_command "sage -pip install $system_packages"
+        [ -n "$system_packages" ] && print_shell_command sage -pip install $system_packages
         ;;
     cpan:install)
-        [ -n "$system_packages" ] && print_shell_command "cpan -i $system_packages"
+        [ -n "$system_packages" ] && print_shell_command cpan -i $system_packages
         ;;
     repology:install)
         if [ -n "$system_packages" ]; then
diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg
index ed2e9090cf8..e2a151b4adc 100755
--- a/build/bin/sage-spkg
+++ b/build/bin/sage-spkg
@@ -341,7 +341,7 @@ fi
 PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'`
 if [ -f "$PKG_SCRIPTS/checksums.ini" ]; then
     # Normal/wheel package
-    PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"`
+    PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini"`
 fi
 
 # Set the $SAGE_DESTDIR variable to be passed to the spkg-install
@@ -396,11 +396,11 @@ ensure_pkg_src() { ###############################################
 if [ ! -f "$PKG_SRC" ]; then
     if [ -n "$PKG_NAME_UPSTREAM" ]; then
         # Normal or wheel package
-        if ! sage-download-file $SAGE_DOWNLOAD_FILE_OPTIONS "$PKG_NAME_UPSTREAM"; then
-            error_msg "Error downloading $PKG_NAME_UPSTREAM"
+        PKG_SRC=$(sage-package download $SAGE_DOWNLOAD_FILE_OPTIONS $PKG_BASE)
+        if [ $? != 0 ]; then
+            error_msg "Error downloading tarball of $PKG_BASE"
             exit 1
         fi
-        PKG_SRC="$SAGE_DISTFILES/$PKG_NAME_UPSTREAM"
         # Do a final check that PKG_SRC is a file with an absolute path
         cd /
         if [ ! -f "$PKG_SRC" ]; then
@@ -509,7 +509,9 @@ else
         # just copy to dist/ and create a simple install script.
         mkdir -p dist
         cp "$PKG_SRC" dist/
-        echo "sdh_store_and_pip_install_wheel ." > spkg-install.in
+        if [ ! -f spkg-install.in ]; then
+            echo "sdh_store_and_pip_install_wheel ." > spkg-install.in
+        fi
         ;;
     *)
         # Source tarball
diff --git a/build/bin/sage-spkg-info b/build/bin/sage-spkg-info
index e43e516dc5b..6b66e584331 100755
--- a/build/bin/sage-spkg-info
+++ b/build/bin/sage-spkg-info
@@ -5,11 +5,26 @@
 #
 #  Assumes SAGE_ROOT is set
 PKG_BASE=$1
+if [ -n "$OUTPUT_DIR" ]; then
+    exec > "$OUTPUT_DIR"/$PKG_BASE.rst
+fi
+if [ -n "$OUTPUT_RST" ]; then
+    echo ".. _spkg_$PKG_BASE:"
+    echo
+    ref ()   { echo ":ref:\`$1\`"; }
+    issue () { echo ":issue:\`$1\`"; }
+    code ()  { echo "\`\`$*\`\`"; }
+else
+    ref ()   { echo "$1"; }
+    issue () { echo "https://github.com/sagemath/sage/issues/$1"; }
+    code ()  { echo "$1"; }
+fi
 PKG_SCRIPTS="$SAGE_ROOT/build/pkgs/$PKG_BASE"
 for ext in rst txt; do
     SPKG_FILE="$PKG_SCRIPTS/SPKG.$ext"
     if [ -f "$SPKG_FILE" ]; then
-        sed "1,3s/^ *Sage: Open Source Mathematics Software:/$PKG_BASE:/" "$SPKG_FILE"
+        # for sphinx 4.4 we need to replace all direct links by some "extlink" (issue 33272)
+        sed -e "1,3s/^ *Sage: Open Source Mathematics Software:/$PKG_BASE:/" -e "s|https://github.com/sagemath/sage/issues/\([0-9]*\)|:issue:\`\1\`|g" -e "s|https://arxiv.org/abs/cs/\([0-9]*\)|:arxiv:\`cs/\1\`|g" "$SPKG_FILE"
         break
     fi
 done
@@ -39,9 +54,9 @@ for dep_file in dependencies dependencies_order_only; do
                 # Dependencies like $(BLAS)
                 \$\(*)      echo "- $dep";;
                 # Looks like a package
-                *)          if [ -n "$OUTPUT_RST" -a -r "$SAGE_ROOT/build/pkgs/$dep/SPKG.rst" ]; then
+                *)          if [ -r "$SAGE_ROOT/build/pkgs/$dep/SPKG.rst" ]; then
                                 # This RST label is set in src/doc/bootstrap
-                                echo "- :ref:\`spkg_$dep\`"
+                                echo "- $(ref spkg_$dep)"
                             else
                                 echo "- $dep"
                             fi;;
@@ -103,7 +118,7 @@ for system in $systems; do
                 echo "$system:"
                 ;;
         esac
-        sage-print-system-package-command $system --prompt='    $ ' --sudo install $system_packages
+        sage-print-system-package-command $system --wrap --prompt='    $ ' --continuation='          ' --sudo install $system_packages
         echo
 done
 if [ -z "$system" ]; then
@@ -114,15 +129,15 @@ else
     if [ -f "${SPKG_CONFIGURE}" ]; then
         if grep -q SAGE_PYTHON_PACKAGE_CHECK "${SPKG_CONFIGURE}"; then
             echo "If the system package is installed and if the (experimental) option"
-            echo "--enable-system-site-packages is passed to ./configure, then ./configure"
+            echo "$(code --enable-system-site-packages) is passed to $(code ./configure), then $(code ./configure)"
             echo "will check if the system package can be used."
         else
-            echo "If the system package is installed, ./configure will check if it can be used."
+            echo "If the system package is installed, $(code ./configure) will check if it can be used."
         fi
     else
         echo "However, these system packages will not be used for building Sage"
-        echo "because spkg-configure.m4 has not been written for this package;"
-        echo "see https://github.com/sagemath/sage/issues/27330"
+        echo "because $(code spkg-configure.m4) has not been written for this package;"
+        echo "see $(issue 27330)"
     fi
 fi
 echo
diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh
index bfc4dd751a4..104f7c19b31 100755
--- a/build/bin/write-dockerfile.sh
+++ b/build/bin/write-dockerfile.sh
@@ -16,12 +16,15 @@ export PATH="$SAGE_ROOT"/build/bin:$PATH
 SYSTEM_PACKAGES=$EXTRA_SYSTEM_PACKAGES
 CONFIGURE_ARGS="--enable-option-checking "
 for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do
-    SYSTEM_PACKAGE=$(sage-get-system-packages $SYSTEM $SPKG | sed 's/${PYTHON_MINOR}/'${PYTHON_MINOR}'/g')
+    SYSTEM_PACKAGE=$(sage-get-system-packages $SYSTEM $SPKG)
     if [ -n "${SYSTEM_PACKAGE}" ]; then
-	# SYSTEM_PACKAGE can be empty if, for example, the environment
-	# variable ENABLE_SYSTEM_SITE_PACKAGES is empty.
-	SYSTEM_PACKAGES+=" ${SYSTEM_PACKAGE}"
-	CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} "
+        # SYSTEM_PACKAGE can be empty if, for example, the environment
+        # variable ENABLE_SYSTEM_SITE_PACKAGES is empty.
+        for a in $SYSTEM_PACKAGE; do
+            # shell-quote package if necessary
+            SYSTEM_PACKAGES+=$(printf " %q" "$a")
+        done
+        CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} "
     fi
 done
 echo "# Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh"
diff --git a/build/make/Makefile.in b/build/make/Makefile.in
index 0cbe86a90de..aac7a9164a2 100644
--- a/build/make/Makefile.in
+++ b/build/make/Makefile.in
@@ -324,7 +324,7 @@ all-toolchain: base-toolchain
 
 # Shorthand for a list of packages sufficient for building and installing
 # typical Python packages from source. Wheel packages only need pip.
-PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel setuptools_wheel
+PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel flit_core hatchling
 
 # Trac #32056: Avoid installed setuptools leaking into the build of python3 by uninstalling it.
 # It will have to be reinstalled anyway because of its dependency on $(PYTHON).
diff --git a/build/pkgs/4ti2/checksums.ini b/build/pkgs/4ti2/checksums.ini
index 275cf157a6a..047f39ece1d 100644
--- a/build/pkgs/4ti2/checksums.ini
+++ b/build/pkgs/4ti2/checksums.ini
@@ -1,5 +1,5 @@
-tarball=4ti2-VERSION.tar.gz
+tarball=4ti2-${VERSION}.tar.gz
 sha1=3d41f30ea3ef94c293eae30c087494269fc1a6b9
 md5=1215872325ddfc561865ecb22b2bccb2
 cksum=2439180289
-upstream_url=https://github.com/4ti2/4ti2/releases/download/Release_1_6_10/4ti2-1.6.10.tar.gz
+upstream_url=https://github.com/4ti2/4ti2/releases/download/Release_${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}/4ti2-${VERSION}.tar.gz
diff --git a/build/pkgs/_bootstrap/distros/arch.txt b/build/pkgs/_bootstrap/distros/arch.txt
index 33054ef918c..050de00ca70 100644
--- a/build/pkgs/_bootstrap/distros/arch.txt
+++ b/build/pkgs/_bootstrap/distros/arch.txt
@@ -1,3 +1,5 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool
+autoconf
+automake
+libtool
 pkgconf
diff --git a/build/pkgs/_bootstrap/distros/cygwin.txt b/build/pkgs/_bootstrap/distros/cygwin.txt
index b5d2db8cfb2..bde788ee5d9 100644
--- a/build/pkgs/_bootstrap/distros/cygwin.txt
+++ b/build/pkgs/_bootstrap/distros/cygwin.txt
@@ -1,2 +1,4 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool
+autoconf
+automake
+libtool
diff --git a/build/pkgs/_bootstrap/distros/fedora.txt b/build/pkgs/_bootstrap/distros/fedora.txt
index e8e9330eca1..5fe960ac3a0 100644
--- a/build/pkgs/_bootstrap/distros/fedora.txt
+++ b/build/pkgs/_bootstrap/distros/fedora.txt
@@ -1,4 +1,5 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool
-# Fedora 26 needs:
+autoconf
+automake
+libtool
 pkg-config
diff --git a/build/pkgs/_bootstrap/distros/freebsd.txt b/build/pkgs/_bootstrap/distros/freebsd.txt
index 93d91f5c84b..5fe960ac3a0 100644
--- a/build/pkgs/_bootstrap/distros/freebsd.txt
+++ b/build/pkgs/_bootstrap/distros/freebsd.txt
@@ -1,2 +1,5 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool pkg-config
+autoconf
+automake
+libtool
+pkg-config
diff --git a/build/pkgs/_bootstrap/distros/homebrew.txt b/build/pkgs/_bootstrap/distros/homebrew.txt
index 93d91f5c84b..5fe960ac3a0 100644
--- a/build/pkgs/_bootstrap/distros/homebrew.txt
+++ b/build/pkgs/_bootstrap/distros/homebrew.txt
@@ -1,2 +1,5 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool pkg-config
+autoconf
+automake
+libtool
+pkg-config
diff --git a/build/pkgs/_bootstrap/distros/macports.txt b/build/pkgs/_bootstrap/distros/macports.txt
new file mode 100644
index 00000000000..5f6a8a4c191
--- /dev/null
+++ b/build/pkgs/_bootstrap/distros/macports.txt
@@ -0,0 +1,6 @@
+# Packages needed for ./bootstrap
+gettext
+autoconf
+automake
+libtool
+pkgconfig
diff --git a/build/pkgs/_bootstrap/distros/nix.txt b/build/pkgs/_bootstrap/distros/nix.txt
index 93d91f5c84b..5fe960ac3a0 100644
--- a/build/pkgs/_bootstrap/distros/nix.txt
+++ b/build/pkgs/_bootstrap/distros/nix.txt
@@ -1,2 +1,5 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool pkg-config
+autoconf
+automake
+libtool
+pkg-config
diff --git a/build/pkgs/_bootstrap/distros/void.txt b/build/pkgs/_bootstrap/distros/void.txt
index 6490cf773e7..904bd61a232 100644
--- a/build/pkgs/_bootstrap/distros/void.txt
+++ b/build/pkgs/_bootstrap/distros/void.txt
@@ -1,4 +1,7 @@
 # Packages needed for ./bootstrap
-autoconf automake libtool
-xtools mk-configure
+autoconf
+automake
+libtool
+xtools
+mk-configure
 pkg-config
diff --git a/build/pkgs/_prereq/distros/alpine.txt b/build/pkgs/_prereq/distros/alpine.txt
index 159c452cf30..3986f8ac849 100644
--- a/build/pkgs/_prereq/distros/alpine.txt
+++ b/build/pkgs/_prereq/distros/alpine.txt
@@ -1,3 +1,12 @@
+# This file, build/pkgs/_prereq/distros/alpine.txt, contains names of
+# Alpine Linux packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/alpine.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
 binutils
 make
 m4
diff --git a/build/pkgs/_prereq/distros/arch.txt b/build/pkgs/_prereq/distros/arch.txt
index 9b9bf09e7e2..aac6d8f5976 100644
--- a/build/pkgs/_prereq/distros/arch.txt
+++ b/build/pkgs/_prereq/distros/arch.txt
@@ -1,3 +1,12 @@
+# This file, build/pkgs/_prereq/distros/arch.txt, contains names of
+# Arch Linux packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/arch.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
 binutils
 make
 m4
diff --git a/build/pkgs/_prereq/distros/conda.txt b/build/pkgs/_prereq/distros/conda.txt
index d76388ce7bb..e8fcb099093 100644
--- a/build/pkgs/_prereq/distros/conda.txt
+++ b/build/pkgs/_prereq/distros/conda.txt
@@ -1,4 +1,13 @@
-compilers
+# This file, build/pkgs/_prereq/distros/conda.txt, contains names of
+# conda packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/conda.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
+compilers<=1.6.0 # 1.7 pulls in c-compiler v.16
 make
 m4
 perl
diff --git a/build/pkgs/_prereq/distros/cygwin.txt b/build/pkgs/_prereq/distros/cygwin.txt
index 2bf3164005b..7a05e1aaa54 100644
--- a/build/pkgs/_prereq/distros/cygwin.txt
+++ b/build/pkgs/_prereq/distros/cygwin.txt
@@ -1,19 +1,18 @@
-# This file, build/pkgs/cygwin.txt, contains name Cygwin packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/cygwin.txt, contains names of
+# Cygwin packages needed for installation of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/cygwin.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/cygwin.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
 #
 binutils
 make
 m4
 # a system python is needed for downloading the sage packages, https://github.com/sagemath/sage/issues/29090
-python39-urllib3 python39
+python39-urllib3
+python39
 perl
 perl-ExtUtils-MakeMaker
 tar
diff --git a/build/pkgs/_prereq/distros/debian.txt b/build/pkgs/_prereq/distros/debian.txt
index 50be6ac7e0b..785739528b4 100644
--- a/build/pkgs/_prereq/distros/debian.txt
+++ b/build/pkgs/_prereq/distros/debian.txt
@@ -1,16 +1,12 @@
-# This file, build/pkgs/debian.txt, contains names of Debian/Ubuntu packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/debian.txt, contains names of
+# Debian/Ubuntu/... packages needed for installation of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/debian.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# If distinctions between different distributions need to be made,
-# files named debian....txt or ubuntu.....txt can be used.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/debian.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
+#
 binutils
 make
 m4
diff --git a/build/pkgs/_prereq/distros/fedora.txt b/build/pkgs/_prereq/distros/fedora.txt
index b35d7f64faf..2716e7eb0cd 100644
--- a/build/pkgs/_prereq/distros/fedora.txt
+++ b/build/pkgs/_prereq/distros/fedora.txt
@@ -1,15 +1,11 @@
-# This file, build/pkgs/fedora.txt, contains names of Fedora/Redhat/CentOS packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/fedora.txt, contains names of
+# Fedora/Redhat/CentOS/AlmaLinux packages needed for installation
+# of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/fedora.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# If distinctions between different distributions need to be made,
-# files named fedora....txt, redhat...txt, or centos....txt can be used.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/fedora.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
 #
 binutils
diff --git a/build/pkgs/_prereq/distros/freebsd.txt b/build/pkgs/_prereq/distros/freebsd.txt
index cc708ccfb2b..f9a4937520a 100644
--- a/build/pkgs/_prereq/distros/freebsd.txt
+++ b/build/pkgs/_prereq/distros/freebsd.txt
@@ -1,5 +1,5 @@
-# This file, build/pkgs/freebsd.txt, contains names of FreeBSD packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/freebsd.txt, contains names of
+# FreeBSD packages needed for installation of Sage from source.
 #
 # In addition, the files build/pkgs/SPKG/distros/freebsd.txt contain the
 # names of packages that provide the equivalent of SPKG.
@@ -8,10 +8,9 @@
 # $ cd /usr/ports/math/sage
 # $ sudo make install-missing-packages
 #
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
-#
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
+#
 gmake
 automake
 bash
diff --git a/build/pkgs/_prereq/distros/gentoo.txt b/build/pkgs/_prereq/distros/gentoo.txt
index 1e26c46cacc..455991e8a99 100644
--- a/build/pkgs/_prereq/distros/gentoo.txt
+++ b/build/pkgs/_prereq/distros/gentoo.txt
@@ -1,3 +1,12 @@
+# This file, build/pkgs/_prereq/distros/gentoo.txt, contains names of
+# Gentoo packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/gentoo.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
 sys-devel/binutils
 sys-libs/binutils-libs
 sys-devel/make
diff --git a/build/pkgs/_prereq/distros/homebrew.txt b/build/pkgs/_prereq/distros/homebrew.txt
index 863c22a6bee..2a85c39abfc 100644
--- a/build/pkgs/_prereq/distros/homebrew.txt
+++ b/build/pkgs/_prereq/distros/homebrew.txt
@@ -1,12 +1,11 @@
-# This file, build/pkgs/homebrew.txt, contains names of homebrew packages
-# needed for installation of Sage from source (in addition to XCode).
+# This file, build/pkgs/_prereq/distros/homebrew.txt, contains names of
+# Homebrew packages needed for installation of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/homebrew.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/homebrew.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
+#
 
 # No packages needed
diff --git a/build/pkgs/_prereq/distros/macports.txt b/build/pkgs/_prereq/distros/macports.txt
new file mode 100644
index 00000000000..e887cd91e80
--- /dev/null
+++ b/build/pkgs/_prereq/distros/macports.txt
@@ -0,0 +1,12 @@
+# This file, build/pkgs/_prereq/distros/macports.txt, contains names of macports packages
+# needed for installation of Sage from source (in addition to XCode).
+#
+# In addition, the files build/pkgs/SPKG/distros/macports.txt contain the names
+# of packages that provide the equivalent of SPKG.
+#
+# See build/bin/sage-spkg, where this information is processed
+# for use in "sage -info SPKG".
+#
+# Everything on a line after a # character is ignored.
+
+# No packages needed
diff --git a/build/pkgs/_prereq/distros/nix.txt b/build/pkgs/_prereq/distros/nix.txt
index 01e5ed4e959..523fe272f7c 100644
--- a/build/pkgs/_prereq/distros/nix.txt
+++ b/build/pkgs/_prereq/distros/nix.txt
@@ -1,16 +1,12 @@
-# This file, build/pkgs/debian.txt, contains names of Debian/Ubuntu packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/nix.txt, contains names of
+# nix packages needed for installation of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/debian.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# If distinctions between different distributions need to be made,
-# files named debian....txt or ubuntu.....txt can be used.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/nix.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
+#
 binutils
 gnumake
 gnum4
diff --git a/build/pkgs/_prereq/distros/opensuse.txt b/build/pkgs/_prereq/distros/opensuse.txt
index 6f7a11fea47..56f77da9bcf 100644
--- a/build/pkgs/_prereq/distros/opensuse.txt
+++ b/build/pkgs/_prereq/distros/opensuse.txt
@@ -1,13 +1,12 @@
-# This file, build/pkgs/opensuse.txt, contains names of OpenSuse packages
-# needed for installation of Sage from source.
+# This file, build/pkgs/_prereq/distros/opensuse.txt, contains names of
+# OpenSuse packages needed for installation of Sage from source.
 #
-# In addition, the files build/pkgs/SPKG/opensuse.txt contain the names
-# of packages that provide the equivalent of SPKG.
-#
-# See build/bin/sage-spkg, where this information is processed
-# for use in "sage -info SPKG".
+# In addition, the files build/pkgs/SPKG/distros/opensuse.txt contain
+# the names of packages that provide the equivalent of SPKG.
 #
+# One package per line. No need to escape special characters.
 # Everything on a line after a # character is ignored.
+#
 binutils
 make
 m4
diff --git a/build/pkgs/_prereq/distros/slackware.txt b/build/pkgs/_prereq/distros/slackware.txt
index 4c957e45264..117762dcd00 100644
--- a/build/pkgs/_prereq/distros/slackware.txt
+++ b/build/pkgs/_prereq/distros/slackware.txt
@@ -1,11 +1,26 @@
+# This file, build/pkgs/_prereq/distros/slackware.txt, contains names of
+# Slackware packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/slackware.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
 binutils
 make
-guile gc libffi  # dependencies of make
-"gcc-[0-9]"      # So that slackpkg pattern matching does not pull in all gcc-* packages
+# dependencies of make
+guile
+gc
+libffi
+gcc-[0-9]      # So that slackpkg pattern matching does not pull in all gcc-* packages
 gcc-11           # on slackware-current
 gcc-g++
 gcc-g++-11       # on slackware-current
-libmpc glibc kernel-headers # dependencies of gcc
+# dependencies of gcc
+libmpc
+glibc
+kernel-headers
 perl
 m4
 bc
diff --git a/build/pkgs/_prereq/distros/void.txt b/build/pkgs/_prereq/distros/void.txt
index 552b5a415f2..d5c2143c56c 100644
--- a/build/pkgs/_prereq/distros/void.txt
+++ b/build/pkgs/_prereq/distros/void.txt
@@ -1,3 +1,12 @@
+# This file, build/pkgs/_prereq/distros/void.txt, contains names of
+# Void Linux packages needed for installation of Sage from source.
+#
+# In addition, the files build/pkgs/SPKG/distros/void.txt contain
+# the names of packages that provide the equivalent of SPKG.
+#
+# One package per line. No need to escape special characters.
+# Everything on a line after a # character is ignored.
+#
 bc
 binutils
 gcc
diff --git a/build/pkgs/_recommended/dependencies b/build/pkgs/_recommended/dependencies
index f4baa9b1fb2..6e36fc6d390 100644
--- a/build/pkgs/_recommended/dependencies
+++ b/build/pkgs/_recommended/dependencies
@@ -1 +1 @@
-pandoc ffmpeg imagemagick texlive git libjpeg
+pandoc ffmpeg imagemagick git libjpeg texlive texlive_luatex free_fonts xindy
diff --git a/build/pkgs/_sagemath/distros/arch.txt b/build/pkgs/_sagemath/distros/arch.txt
index 825a91411b0..b58e3f244a7 100644
--- a/build/pkgs/_sagemath/distros/arch.txt
+++ b/build/pkgs/_sagemath/distros/arch.txt
@@ -1 +1,2 @@
-sagemath sagemath-doc
+sagemath
+sagemath-doc
diff --git a/build/pkgs/barvinok/distros/opensuse.txt b/build/pkgs/barvinok/distros/opensuse.txt
index b3369e09db7..65092b1978a 100644
--- a/build/pkgs/barvinok/distros/opensuse.txt
+++ b/build/pkgs/barvinok/distros/opensuse.txt
@@ -1,2 +1,2 @@
 barvinok
-"pkgconfig(barvinok)"
+pkgconfig(barvinok)
diff --git a/build/pkgs/boost_cropped/spkg-legacy-uninstall b/build/pkgs/boost_cropped/spkg-legacy-uninstall
deleted file mode 100755
index ef170323b6a..00000000000
--- a/build/pkgs/boost_cropped/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "${SAGE_LOCAL}"/include/boost
diff --git a/build/pkgs/brial/spkg-legacy-uninstall b/build/pkgs/brial/spkg-legacy-uninstall
deleted file mode 100755
index 59e01958685..00000000000
--- a/build/pkgs/brial/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-echo "Cleaning out old PolyBoRi and BRiAl installations"
-rm -f "$SAGE_LOCAL"/lib/lib{polybori,brial}*
-rm -rf "$SAGE_LOCAL"/include/polybori*
-rm -rf "$SAGE_LOCAL"/share/polybori
diff --git a/build/pkgs/bzip2/distros/cygwin.txt b/build/pkgs/bzip2/distros/cygwin.txt
index 8e127a3d331..8ef7f4d0734 100644
--- a/build/pkgs/bzip2/distros/cygwin.txt
+++ b/build/pkgs/bzip2/distros/cygwin.txt
@@ -1 +1,2 @@
-bzip2 libbz2-devel
+bzip2
+libbz2-devel
diff --git a/build/pkgs/bzip2/distros/fedora.txt b/build/pkgs/bzip2/distros/fedora.txt
index f62dee917bb..13c12f2a5f6 100644
--- a/build/pkgs/bzip2/distros/fedora.txt
+++ b/build/pkgs/bzip2/distros/fedora.txt
@@ -1 +1,2 @@
-bzip2 bzip2-devel
+bzip2
+bzip2-devel
diff --git a/build/pkgs/bzip2/distros/opensuse.txt b/build/pkgs/bzip2/distros/opensuse.txt
index ad77a92d4bb..ff839c0de00 100644
--- a/build/pkgs/bzip2/distros/opensuse.txt
+++ b/build/pkgs/bzip2/distros/opensuse.txt
@@ -1,3 +1,3 @@
 # spkg-configure checks for both the program and the library
 bzip2
-"pkgconfig(bzip2)"
+pkgconfig(bzip2)
diff --git a/build/pkgs/calver/dependencies b/build/pkgs/calver/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/calver/dependencies
+++ b/build/pkgs/calver/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/cbc/distros/fedora.txt b/build/pkgs/cbc/distros/fedora.txt
index 3e1af7ad66b..d63f4439b2a 100644
--- a/build/pkgs/cbc/distros/fedora.txt
+++ b/build/pkgs/cbc/distros/fedora.txt
@@ -1 +1,2 @@
-coin-or-Cbc coin-or-Cbc-devel
+coin-or-Cbc
+coin-or-Cbc-devel
diff --git a/build/pkgs/cddlib/distros/cygwin.txt b/build/pkgs/cddlib/distros/cygwin.txt
index f9eaae4c7ad..d406d903210 100644
--- a/build/pkgs/cddlib/distros/cygwin.txt
+++ b/build/pkgs/cddlib/distros/cygwin.txt
@@ -1 +1,2 @@
-cddlib-devel cddlib-tools
+cddlib-devel
+cddlib-tools
diff --git a/build/pkgs/cddlib/distros/opensuse.txt b/build/pkgs/cddlib/distros/opensuse.txt
index ffb93647443..0d299e7ad0e 100644
--- a/build/pkgs/cddlib/distros/opensuse.txt
+++ b/build/pkgs/cddlib/distros/opensuse.txt
@@ -1,2 +1,2 @@
 cddlib-tools
-"pkgconfig(cddlib)"
+pkgconfig(cddlib)
diff --git a/build/pkgs/cliquer/distros/fedora.txt b/build/pkgs/cliquer/distros/fedora.txt
index 4dadef53faa..718f9f6b2cf 100644
--- a/build/pkgs/cliquer/distros/fedora.txt
+++ b/build/pkgs/cliquer/distros/fedora.txt
@@ -1 +1,2 @@
-cliquer cliquer-devel
+cliquer
+cliquer-devel
diff --git a/build/pkgs/cmake/distros/macports.txt b/build/pkgs/cmake/distros/macports.txt
index a3ea3e4380f..de0e65cf914 100644
--- a/build/pkgs/cmake/distros/macports.txt
+++ b/build/pkgs/cmake/distros/macports.txt
@@ -1 +1,2 @@
-cmake
+# Broken as of 2022-05-01
+#cmake
diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini
index deadbf67dc3..a10d1db6236 100644
--- a/build/pkgs/configure/checksums.ini
+++ b/build/pkgs/configure/checksums.ini
@@ -1,4 +1,4 @@
 tarball=configure-VERSION.tar.gz
-sha1=7f2fe8137f5998559f8d3a0a7f965adf6d5ebc78
-md5=586738771174a2d26d29ce7ba571a95d
-cksum=1156937644
+sha1=05115739db242cb5a48ae17f97d06b179fc70334
+md5=adc8208885e6be527b089b682079c464
+cksum=3321256447
diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt
index 1596e721179..fbcbbd235f8 100644
--- a/build/pkgs/configure/package-version.txt
+++ b/build/pkgs/configure/package-version.txt
@@ -1 +1 @@
-2395f7bb45fd7da537a953055df5e0a70dd96c6a
+e95536ccbad199de456e804968a775f2744e4bff
diff --git a/build/pkgs/coxeter3/distros/fedora.txt b/build/pkgs/coxeter3/distros/fedora.txt
index 3cfeb2f9fa0..0c6fb8a3e95 100644
--- a/build/pkgs/coxeter3/distros/fedora.txt
+++ b/build/pkgs/coxeter3/distros/fedora.txt
@@ -1 +1,3 @@
-coxeter coxeter-devel coxeter-tools
+coxeter
+coxeter-devel
+coxeter-tools
diff --git a/build/pkgs/cunningham_tables/checksums.ini b/build/pkgs/cunningham_tables/checksums.ini
index d6de22c0850..360d9b647b7 100644
--- a/build/pkgs/cunningham_tables/checksums.ini
+++ b/build/pkgs/cunningham_tables/checksums.ini
@@ -2,4 +2,4 @@ tarball=cunningham_tables-VERSION.tar.gz
 sha1=8bea1a113d85bb9c37d8f213dd19525d9d026f22
 md5=e71b32f12e9a46c1c86e275e8441a06b
 cksum=1990403877
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/cunningham_tables-1.0.tar.gz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/cunningham_tables-VERSION.tar.gz
diff --git a/build/pkgs/curl/distros/cygwin.txt b/build/pkgs/curl/distros/cygwin.txt
index 7bcb9889a4d..26bfd4d1186 100644
--- a/build/pkgs/curl/distros/cygwin.txt
+++ b/build/pkgs/curl/distros/cygwin.txt
@@ -1 +1,2 @@
-libcurl-devel curl
+libcurl-devel
+curl
diff --git a/build/pkgs/curl/distros/fedora.txt b/build/pkgs/curl/distros/fedora.txt
index 7bcb9889a4d..26bfd4d1186 100644
--- a/build/pkgs/curl/distros/fedora.txt
+++ b/build/pkgs/curl/distros/fedora.txt
@@ -1 +1,2 @@
-libcurl-devel curl
+libcurl-devel
+curl
diff --git a/build/pkgs/curl/distros/macports.txt b/build/pkgs/curl/distros/macports.txt
index 13368f82902..284d14f8d01 100644
--- a/build/pkgs/curl/distros/macports.txt
+++ b/build/pkgs/curl/distros/macports.txt
@@ -1 +1,2 @@
-curl
+# Broken as of 2022-05-01
+#curl
diff --git a/build/pkgs/curl/distros/opensuse.txt b/build/pkgs/curl/distros/opensuse.txt
index ffd4f36dc86..a0ac40946fa 100644
--- a/build/pkgs/curl/distros/opensuse.txt
+++ b/build/pkgs/curl/distros/opensuse.txt
@@ -1,2 +1,2 @@
 curl
-"pkgconfig(libcurl)"
+pkgconfig(libcurl)
diff --git a/build/pkgs/cvxopt/spkg-install.in b/build/pkgs/cvxopt/spkg-install.in
index 0081c40f819..8179125fc9a 100644
--- a/build/pkgs/cvxopt/spkg-install.in
+++ b/build/pkgs/cvxopt/spkg-install.in
@@ -82,10 +82,6 @@ sdh_pip_install .
 
 if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then
    cd doc
-   # checking to see if there is previously installed documentation.
-   if [ -d $SAGE_LOCAL/share/doc/cvxopt/html ] ; then
-      rm -rf $SAGE_LOCAL/share/doc/cvxopt/html
-   fi
    mkdir -p "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html"
    cp -r html/* "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html/"
 fi
diff --git a/build/pkgs/deprecation/SPKG.rst b/build/pkgs/deprecation/SPKG.rst
deleted file mode 100644
index 8aa6712f42b..00000000000
--- a/build/pkgs/deprecation/SPKG.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-deprecation: A library to handle automated deprecations
-=======================================================
-
-Description
------------
-
-A library to handle automated deprecations
-
-License
--------
-
-Apache 2
-
-Upstream Contact
-----------------
-
-https://pypi.org/project/deprecation/
-
diff --git a/build/pkgs/deprecation/checksums.ini b/build/pkgs/deprecation/checksums.ini
deleted file mode 100644
index b2e6c9d2964..00000000000
--- a/build/pkgs/deprecation/checksums.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-tarball=deprecation-VERSION.tar.gz
-sha1=34847e937ac203cfd0d8ea0a0f2579204cdb8fe4
-md5=6b79c6572fb241e3cecbbd7d539bb66b
-cksum=1034714094
-upstream_url=https://pypi.io/packages/source/d/deprecation/deprecation-VERSION.tar.gz
diff --git a/build/pkgs/deprecation/dependencies b/build/pkgs/deprecation/dependencies
deleted file mode 100644
index 47296a7bace..00000000000
--- a/build/pkgs/deprecation/dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
-
-----------
-All lines of this file are ignored except the first.
diff --git a/build/pkgs/deprecation/distros/conda.txt b/build/pkgs/deprecation/distros/conda.txt
deleted file mode 100644
index 4ba9b7530ed..00000000000
--- a/build/pkgs/deprecation/distros/conda.txt
+++ /dev/null
@@ -1 +0,0 @@
-deprecation
diff --git a/build/pkgs/deprecation/install-requires.txt b/build/pkgs/deprecation/install-requires.txt
deleted file mode 100644
index 4ba9b7530ed..00000000000
--- a/build/pkgs/deprecation/install-requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-deprecation
diff --git a/build/pkgs/deprecation/package-version.txt b/build/pkgs/deprecation/package-version.txt
deleted file mode 100644
index 7ec1d6db408..00000000000
--- a/build/pkgs/deprecation/package-version.txt
+++ /dev/null
@@ -1 +0,0 @@
-2.1.0
diff --git a/build/pkgs/deprecation/spkg-install.in b/build/pkgs/deprecation/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/deprecation/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/deprecation/type b/build/pkgs/deprecation/type
deleted file mode 100644
index a6a7b9cd726..00000000000
--- a/build/pkgs/deprecation/type
+++ /dev/null
@@ -1 +0,0 @@
-standard
diff --git a/build/pkgs/distlib/SPKG.rst b/build/pkgs/distlib/SPKG.rst
index a4496c0427a..e59b0b7292c 100644
--- a/build/pkgs/distlib/SPKG.rst
+++ b/build/pkgs/distlib/SPKG.rst
@@ -9,7 +9,7 @@ Distribution utilities
 License
 -------
 
-Python license
+PSF-2.0
 
 Upstream Contact
 ----------------
diff --git a/build/pkgs/distlib/checksums.ini b/build/pkgs/distlib/checksums.ini
index be255ceca11..b1a05ffc295 100644
--- a/build/pkgs/distlib/checksums.ini
+++ b/build/pkgs/distlib/checksums.ini
@@ -1,5 +1,5 @@
-tarball=distlib-VERSION.tar.gz
-sha1=5c99f8bd1cc58c387a8d22afa632f81c6fec9993
-md5=44e4357e35bbd77fdf1b81e174e34f20
-cksum=3690000669
-upstream_url=https://pypi.io/packages/source/d/distlib/distlib-VERSION.tar.gz
+tarball=distlib-VERSION-py2.py3-none-any.whl
+sha1=97ea3bb71040f0348eaea272ec17fefea5806e87
+md5=cf336842ba81996e554ae4eb2cd76d5d
+cksum=3779817199
+upstream_url=https://pypi.io/packages/py2.py3/d/distlib/distlib-VERSION-py2.py3-none-any.whl
diff --git a/build/pkgs/distlib/dependencies b/build/pkgs/distlib/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/distlib/dependencies
+++ b/build/pkgs/distlib/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/distlib/package-version.txt b/build/pkgs/distlib/package-version.txt
index 0f82685331e..66784322096 100644
--- a/build/pkgs/distlib/package-version.txt
+++ b/build/pkgs/distlib/package-version.txt
@@ -1 +1 @@
-0.3.7
+0.3.8
diff --git a/build/pkgs/distlib/spkg-install.in b/build/pkgs/distlib/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/distlib/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/e_antic/checksums.ini b/build/pkgs/e_antic/checksums.ini
index d24ae72e6f4..fe7fa852b76 100644
--- a/build/pkgs/e_antic/checksums.ini
+++ b/build/pkgs/e_antic/checksums.ini
@@ -1,5 +1,5 @@
 tarball=e-antic-VERSION.tar.gz
-sha1=4f551cf2ab58201fb2137ae994cb670c6fa8b154
-md5=6c6f38408994f7d79f814bbad8183fcb
-cksum=2487959662
+sha1=587052e189f9a7a145ac3144e6b7f11fca54b1ff
+md5=0b54042461b28c4b45239480af062546
+cksum=589498373
 upstream_url=https://github.com/flatsurf/e-antic/releases/download/VERSION/e-antic-VERSION.tar.gz
diff --git a/build/pkgs/e_antic/package-version.txt b/build/pkgs/e_antic/package-version.txt
index f0bb29e7638..227cea21564 100644
--- a/build/pkgs/e_antic/package-version.txt
+++ b/build/pkgs/e_antic/package-version.txt
@@ -1 +1 @@
-1.3.0
+2.0.0
diff --git a/build/pkgs/ecl/spkg-legacy-uninstall b/build/pkgs/ecl/spkg-legacy-uninstall
deleted file mode 100755
index 375d99ebcf1..00000000000
--- a/build/pkgs/ecl/spkg-legacy-uninstall
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env bash
-rm -f "$SAGE_LOCAL"/bin/ecl
-rm -rf "$SAGE_LOCAL"/include/ecl
-rm -rf "$SAGE_LOCAL"/lib/ecl
-rm -rf "$SAGE_LOCAL"/lib/ecl-*
-rm -rf "$SAGE_LOCAL"/lib/libecl.*
diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini
index 266e6bfac1c..b6a7efd95aa 100644
--- a/build/pkgs/eclib/checksums.ini
+++ b/build/pkgs/eclib/checksums.ini
@@ -1,5 +1,5 @@
 tarball=eclib-VERSION.tar.bz2
-sha1=bd49acf96c4e7246c8ca3e5d188ae1b03a3aeff3
-md5=42721f2f1343c49dc774763a57a85ca6
-cksum=3624641360
+sha1=3028ac95e1b76699f5f9e871ac706cda363ab842
+md5=c96e1bb39a50552aeac6675b907709b8
+cksum=2027019032
 upstream_url=https://github.com/JohnCremona/eclib/releases/download/vVERSION/eclib-VERSION.tar.bz2
diff --git a/build/pkgs/eclib/distros/fedora.txt b/build/pkgs/eclib/distros/fedora.txt
index 7861ae6de2b..2bcdd8cb9a5 100644
--- a/build/pkgs/eclib/distros/fedora.txt
+++ b/build/pkgs/eclib/distros/fedora.txt
@@ -1 +1,2 @@
-eclib eclib-devel
+eclib
+eclib-devel
diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt
index b5f339e560b..190b92f716b 100644
--- a/build/pkgs/eclib/package-version.txt
+++ b/build/pkgs/eclib/package-version.txt
@@ -1 +1 @@
-20230424
+20231212
diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4
index c98781201cc..133b44bfdc1 100644
--- a/build/pkgs/eclib/spkg-configure.m4
+++ b/build/pkgs/eclib/spkg-configure.m4
@@ -1,12 +1,12 @@
 SAGE_SPKG_CONFIGURE([eclib], [
   SAGE_SPKG_DEPCHECK([ntl pari flint], [
-    dnl Trac #31443, #34029: use existing eclib only if the version reported by pkg-config is correct
-    m4_pushdef([SAGE_ECLIB_VER],["20230424"])
+    dnl use existing eclib only if the version reported by pkg-config is recent enough
+    m4_pushdef([SAGE_ECLIB_VER],["20231212"])
     PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [
       AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [
         AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [
             mwrank_version=`$ac_path_MWRANK -V 2>&1`
-            AX_COMPARE_VERSION([$mwrank_version], [eq], [SAGE_ECLIB_VER], [
+            AX_COMPARE_VERSION([$mwrank_version], [ge], [SAGE_ECLIB_VER], [
                 ac_cv_path_MWRANK="$ac_path_MWRANK"
             ])
         ])
diff --git a/build/pkgs/eclib/spkg-install.in b/build/pkgs/eclib/spkg-install.in
index 2a83bdb5235..9618e8f5d19 100644
--- a/build/pkgs/eclib/spkg-install.in
+++ b/build/pkgs/eclib/spkg-install.in
@@ -3,35 +3,11 @@ CXXFLAGS="$CXXFLAGS_O3_NON_NATIVE"
 
 export CFLAGS CXXFLAGS
 
-
-echo "Deleting old versions of eclib libraries, which"
-echo "would interfere with new builds..."
-# Delete any pre-autotools libraries:
-rm -f "$SAGE_LOCAL"/lib/lib{curvesntl,g0nntl,jcntl,rankntl,mwrank}.*
-# Delete autotools libraries:
-rm -f "$SAGE_LOCAL"/lib/lib{e,j}c.*
-echo "Deleting old include directory..."
-rm -rf "$SAGE_LOCAL"/include/eclib/
-
-
 cd src/
 
-#############################################################
-# the workaround with SAGE_CONFIGURE_FLINT_ECLIB
-# introduced by #28401 may be removed once
-# github.com/JohnCremona/eclib/pull/57 is in  Sage
-#############################################################
-if test x$SAGE_FLINT_PREFIX = x; then
-   SAGE_CONFIGURE_FLINT_ECLIB="--with-flint"
-else
-   SAGE_CONFIGURE_FLINT_ECLIB="--with-flint=$SAGE_FLINT_PREFIX"
-fi
-
-echo $SAGE_CONFIGURE_FLINT_ECLIB
-
 sdh_configure $SAGE_CONFIGURE_NTL \
               $SAGE_CONFIGURE_PARI \
-              $SAGE_CONFIGURE_FLINT_ECLIB \
+              --with-flint=$SAGE_FLINT_PREFIX \
               --with-boost="no" \
               --disable-allprogs
 sdh_make
diff --git a/build/pkgs/ecm/distros/fedora.txt b/build/pkgs/ecm/distros/fedora.txt
index 45e62aa8032..0625c82b8b9 100644
--- a/build/pkgs/ecm/distros/fedora.txt
+++ b/build/pkgs/ecm/distros/fedora.txt
@@ -1 +1,2 @@
-gmp-ecm gmp-ecm-devel
+gmp-ecm
+gmp-ecm-devel
diff --git a/build/pkgs/ecm/spkg-install.in b/build/pkgs/ecm/spkg-install.in
index edc6bb2df5f..03c6c109d7d 100644
--- a/build/pkgs/ecm/spkg-install.in
+++ b/build/pkgs/ecm/spkg-install.in
@@ -232,17 +232,6 @@ sdh_configure $SAGE_CONFIGURE_GMP $ECM_CONFIGURE
 
 sdh_make
 
-###############################################################################
-# Remove old executable/header/libraries/manpage:
-###############################################################################
-
-echo
-echo "Build succeeded.  Now removing old binary, header file, manual page and libraries..."
-rm -f "$SAGE_LOCAL"/bin/ecm
-rm -f "$SAGE_LOCAL"/lib/libecm.*
-rm -f "$SAGE_LOCAL"/include/ecm.h
-rm -f "$SAGE_LOCAL"/share/man/man1/ecm.1
-
 ###############################################################################
 # Now install ECM:
 ###############################################################################
diff --git a/build/pkgs/editables/dependencies b/build/pkgs/editables/dependencies
index 47296a7bace..e0e94942dba 100644
--- a/build/pkgs/editables/dependencies
+++ b/build/pkgs/editables/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | flit_core $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/fflas_ffpack/distros/opensuse.txt b/build/pkgs/fflas_ffpack/distros/opensuse.txt
index 2dda5369b8d..cf55f69f450 100644
--- a/build/pkgs/fflas_ffpack/distros/opensuse.txt
+++ b/build/pkgs/fflas_ffpack/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(fflas-ffpack)"
+pkgconfig(fflas-ffpack)
diff --git a/build/pkgs/filelock/checksums.ini b/build/pkgs/filelock/checksums.ini
index 0d412bb2ca3..6c597d2d7aa 100644
--- a/build/pkgs/filelock/checksums.ini
+++ b/build/pkgs/filelock/checksums.ini
@@ -1,5 +1,5 @@
 tarball=filelock-VERSION-py3-none-any.whl
-sha1=74f5368865bf05ddc5b69949e4547ad25c078fc1
-md5=63b0f117cb65ef531ffafb566170661e
-cksum=1046951951
+sha1=f1fa92751023660a10b248f8559d09f3c461403f
+md5=d0af0f1a2ee56c24f630ece278cb062f
+cksum=3749691502
 upstream_url=https://pypi.io/packages/py3/f/filelock/filelock-VERSION-py3-none-any.whl
diff --git a/build/pkgs/filelock/dependencies b/build/pkgs/filelock/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/filelock/dependencies
+++ b/build/pkgs/filelock/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/filelock/package-version.txt b/build/pkgs/filelock/package-version.txt
index 871f80a34be..c10780c628a 100644
--- a/build/pkgs/filelock/package-version.txt
+++ b/build/pkgs/filelock/package-version.txt
@@ -1 +1 @@
-3.12.3
+3.13.1
diff --git a/build/pkgs/flint/distros/fedora.txt b/build/pkgs/flint/distros/fedora.txt
index 8df1db2cd08..12eb8937aea 100644
--- a/build/pkgs/flint/distros/fedora.txt
+++ b/build/pkgs/flint/distros/fedora.txt
@@ -1 +1,2 @@
-flint flint-devel
+flint
+flint-devel
diff --git a/build/pkgs/fplll/distros/fedora.txt b/build/pkgs/fplll/distros/fedora.txt
index ccf2e14dfee..29c3b897ae1 100644
--- a/build/pkgs/fplll/distros/fedora.txt
+++ b/build/pkgs/fplll/distros/fedora.txt
@@ -1 +1,2 @@
-libfplll libfplll-devel
+libfplll
+libfplll-devel
diff --git a/build/pkgs/fplll/distros/opensuse.txt b/build/pkgs/fplll/distros/opensuse.txt
index ee87c038706..2589161f43c 100644
--- a/build/pkgs/fplll/distros/opensuse.txt
+++ b/build/pkgs/fplll/distros/opensuse.txt
@@ -1,3 +1,3 @@
-"pkgconfig(fplll)"
+pkgconfig(fplll)
 fplll-devel
 fplll
diff --git a/build/pkgs/fplll/spkg-install.in b/build/pkgs/fplll/spkg-install.in
index 68c0cb70ad2..d898db82687 100644
--- a/build/pkgs/fplll/spkg-install.in
+++ b/build/pkgs/fplll/spkg-install.in
@@ -22,7 +22,3 @@ export CXX="$CXX"
 sdh_configure $CONFIGUREFLAGS
 sdh_make
 sdh_make_install
-
-# Pretend that the "libfplll" package is not installed. This is needed to
-# support renaming libfplll -> fplll done on Trac #24042
-rm -f "$SAGE_SPKG_INST/"libfplll-*
diff --git a/build/pkgs/free_fonts/SPKG.rst b/build/pkgs/free_fonts/SPKG.rst
new file mode 100644
index 00000000000..417954179a7
--- /dev/null
+++ b/build/pkgs/free_fonts/SPKG.rst
@@ -0,0 +1,23 @@
+free_fonts: a free family of scalable outline fonts
+===================================================
+
+Description
+-----------
+
+This dummy package represents the GNU free fonts: a free family of scalable
+outline fonts, suitable for general use on computers and for desktop
+publishing. It is Unicode-encoded for compatibility with all modern operating
+systems.
+
+We do not have an SPKG for it. The purpose of this dummy package is to
+associate system package lists with it.
+
+License
+-------
+
+GNU General Public License GPLv3+
+
+Upstream Contact
+----------------
+
+https://www.gnu.org/software/freefont/
diff --git a/build/pkgs/free_fonts/distros/alpine.txt b/build/pkgs/free_fonts/distros/alpine.txt
new file mode 100644
index 00000000000..756efd2627c
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/alpine.txt
@@ -0,0 +1 @@
+ttf-freefont
diff --git a/build/pkgs/free_fonts/distros/arch.txt b/build/pkgs/free_fonts/distros/arch.txt
new file mode 100644
index 00000000000..b03a4525c08
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/arch.txt
@@ -0,0 +1 @@
+gnu-free-fonts
diff --git a/build/pkgs/free_fonts/distros/conda.txt b/build/pkgs/free_fonts/distros/conda.txt
new file mode 100644
index 00000000000..bdf05c629cf
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/conda.txt
@@ -0,0 +1 @@
+open-fonts
diff --git a/build/pkgs/free_fonts/distros/debian.txt b/build/pkgs/free_fonts/distros/debian.txt
new file mode 100644
index 00000000000..9dae6737769
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/debian.txt
@@ -0,0 +1 @@
+fonts-freefont-otf
diff --git a/build/pkgs/free_fonts/distros/fedora.txt b/build/pkgs/free_fonts/distros/fedora.txt
new file mode 100644
index 00000000000..b03a4525c08
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/fedora.txt
@@ -0,0 +1 @@
+gnu-free-fonts
diff --git a/build/pkgs/free_fonts/distros/freebsd.txt b/build/pkgs/free_fonts/distros/freebsd.txt
new file mode 100644
index 00000000000..34b729ae37f
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/freebsd.txt
@@ -0,0 +1 @@
+x11-fonts/freefont-ttf
diff --git a/build/pkgs/free_fonts/distros/gentoo.txt b/build/pkgs/free_fonts/distros/gentoo.txt
new file mode 100644
index 00000000000..f9711d7a9f2
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/gentoo.txt
@@ -0,0 +1 @@
+media-fonts/freefont
diff --git a/build/pkgs/free_fonts/distros/macports.txt b/build/pkgs/free_fonts/distros/macports.txt
new file mode 100644
index 00000000000..d5a8b479117
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/macports.txt
@@ -0,0 +1 @@
+freefont-ttf
diff --git a/build/pkgs/free_fonts/distros/opensuse.txt b/build/pkgs/free_fonts/distros/opensuse.txt
new file mode 100644
index 00000000000..b03a4525c08
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/opensuse.txt
@@ -0,0 +1 @@
+gnu-free-fonts
diff --git a/build/pkgs/free_fonts/distros/repology.txt b/build/pkgs/free_fonts/distros/repology.txt
new file mode 100644
index 00000000000..0870af23017
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/repology.txt
@@ -0,0 +1 @@
+gnu-freefont
diff --git a/build/pkgs/free_fonts/distros/void.txt b/build/pkgs/free_fonts/distros/void.txt
new file mode 100644
index 00000000000..d5a8b479117
--- /dev/null
+++ b/build/pkgs/free_fonts/distros/void.txt
@@ -0,0 +1 @@
+freefont-ttf
diff --git a/build/pkgs/free_fonts/spkg-configure.m4 b/build/pkgs/free_fonts/spkg-configure.m4
new file mode 100644
index 00000000000..d6864f5b58c
--- /dev/null
+++ b/build/pkgs/free_fonts/spkg-configure.m4
@@ -0,0 +1,16 @@
+SAGE_SPKG_CONFIGURE([free_fonts], [
+    sage_spkg_install_free_fonts=yes
+    m4_foreach([font],
+               [FreeSerif.ttf,FreeSerif.otf],
+        [
+        AC_MSG_CHECKING([for ]font)
+        AS_IF([kpsewhich ]font[ >& AS_MESSAGE_LOG_FD 2>&1], [
+            AC_MSG_RESULT([yes])
+            sage_spkg_install_free_fonts=no
+            break
+        ], [
+            AC_MSG_RESULT([no])
+        ])
+    ])
+])
+
diff --git a/build/pkgs/free_fonts/type b/build/pkgs/free_fonts/type
new file mode 100644
index 00000000000..134d9bc32d5
--- /dev/null
+++ b/build/pkgs/free_fonts/type
@@ -0,0 +1 @@
+optional
diff --git a/build/pkgs/freetype/distros/macports.txt b/build/pkgs/freetype/distros/macports.txt
index 098479093ff..a2e684ddd92 100644
--- a/build/pkgs/freetype/distros/macports.txt
+++ b/build/pkgs/freetype/distros/macports.txt
@@ -1 +1,2 @@
-freetype
+# Broken as of 2022-05-01
+#freetype
diff --git a/build/pkgs/freetype/distros/opensuse.txt b/build/pkgs/freetype/distros/opensuse.txt
index 33f8f6b7bdd..9205ba71e27 100644
--- a/build/pkgs/freetype/distros/opensuse.txt
+++ b/build/pkgs/freetype/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(freetype2)"
+pkgconfig(freetype2)
diff --git a/build/pkgs/freetype/spkg-install.in b/build/pkgs/freetype/spkg-install.in
index c1615626e72..9d8c0075ec0 100644
--- a/build/pkgs/freetype/spkg-install.in
+++ b/build/pkgs/freetype/spkg-install.in
@@ -4,9 +4,3 @@ cd src
 GNUMAKE=${MAKE} sdh_configure --enable-freetype-config $FREETYPE_CONFIGURE
 sdh_make
 sdh_make_install
-
-# The following file may be present from old builds of freetype, and
-# its presence can break the matplotlib build. So remove it. (The
-# current version is $SAGE_LOCAL/include/freetype2/ftbuild.h.)
-
-rm -f "$SAGE_LOCAL/include/ft2build.h"
diff --git a/build/pkgs/frobby/spkg-install.in b/build/pkgs/frobby/spkg-install.in
index 1a97cc6b682..60ed2c67207 100644
--- a/build/pkgs/frobby/spkg-install.in
+++ b/build/pkgs/frobby/spkg-install.in
@@ -1,4 +1,3 @@
-rm -rf "$SAGE_LOCAL/bin/frobby"
 
 GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR
 ldflags="-Wl,-rpath,$SAGE_LOCAL/lib -L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags
@@ -12,14 +11,4 @@ if [ $? -ne 0 ]; then
    exit 1
 fi
 
-if [ ! -f bin/release/frobby ]; then
-   echo "Frobby executable not found."
-   exit 1
-fi
-
-$CP bin/release/frobby "$SAGE_LOCAL/bin/"
-
-if [ ! -f "$SAGE_LOCAL/bin/frobby" ]; then
-   echo "Frobby executable not copied."
-   exit 1
-fi
+sdh_install bin/release/frobby "$SAGE_LOCAL/bin"
diff --git a/build/pkgs/frobby/spkg-legacy-uninstall b/build/pkgs/frobby/spkg-legacy-uninstall
new file mode 100755
index 00000000000..a262741b2a9
--- /dev/null
+++ b/build/pkgs/frobby/spkg-legacy-uninstall
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+rm -f "$SAGE_LOCAL/bin/frobby"
diff --git a/build/pkgs/furo/spkg-install.in b/build/pkgs/furo/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/furo/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/gap/distros/debian.txt b/build/pkgs/gap/distros/debian.txt
index 883a55b9c20..66efa2824a6 100644
--- a/build/pkgs/gap/distros/debian.txt
+++ b/build/pkgs/gap/distros/debian.txt
@@ -1 +1,2 @@
+gap
 libgap-dev
diff --git a/build/pkgs/gap/distros/fedora.txt b/build/pkgs/gap/distros/fedora.txt
new file mode 100644
index 00000000000..6c561683bf4
--- /dev/null
+++ b/build/pkgs/gap/distros/fedora.txt
@@ -0,0 +1,5 @@
+gap
+gap-core
+gap-devel
+gap-libs
+libgap
diff --git a/build/pkgs/gap/spkg-configure.m4 b/build/pkgs/gap/spkg-configure.m4
new file mode 100644
index 00000000000..72a0a999da7
--- /dev/null
+++ b/build/pkgs/gap/spkg-configure.m4
@@ -0,0 +1,96 @@
+SAGE_SPKG_CONFIGURE([gap], [
+  # Default to installing the SPKG, if the check is run at all.
+  sage_spkg_install_gap=yes
+
+  m4_pushdef([GAP_MINVER],["4.12.2"])
+
+  SAGE_SPKG_DEPCHECK([ncurses readline zlib], [
+    AC_PATH_PROG(GAP, gap)
+    AS_IF([test -n "${GAP}"], [
+      AC_MSG_CHECKING([for gap version GAP_MINVER or newer])
+
+      # GAP will later add the "user" path to the list of root paths
+      # so long as we don't initialize GAP with -r in Sage. But we
+      # don't want to include it in the hard-coded list.
+      GAPRUN="${GAP} -r -q --bare --nointeract -c"
+      _cmd='Display(GAPInfo.KernelInfo.KERNEL_VERSION);'
+      GAP_VERSION=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+      AX_COMPARE_VERSION(["${GAP_VERSION}"], [ge], [GAP_MINVER], [
+        AC_MSG_RESULT([yes])
+        AC_MSG_CHECKING([for gap root paths])
+        _cmd='Display(JoinStringsWithSeparator(GAPInfo.RootPaths,";"));'
+        SYS_GAP_ROOT_PATHS=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+        AC_MSG_RESULT([$SYS_GAP_ROOT_PATHS])
+        AS_IF([test -n "${SYS_GAP_ROOT_PATHS}"], [
+          AC_MSG_CHECKING([for the PrimGrp, SmallGrp, and TransGrp packages])
+          # Check for a very minimal set of packages without which the
+          # sage test suite will fail. The crazy thing below is a
+          # "quadrigraph" for a square bracket.
+          _cmd="Display(@<:@"
+          _cmd="${_cmd} TestPackageAvailability(\"PrimGrp\"),"
+          _cmd="${_cmd} TestPackageAvailability(\"SmallGrp\"),"
+          _cmd="${_cmd} TestPackageAvailability(\"TransGrp\")"
+          _cmd="${_cmd} @:>@);"
+          _output=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+          AS_IF([test $? -eq 0], [
+            AS_CASE([$_output],
+              [*fail*],[AC_MSG_RESULT([no (at least one package missing)])],[
+                # default case, i.e. no "fail"
+                AC_MSG_RESULT([yes])
+
+                AC_MSG_CHECKING([if we can link against libgap])
+                # That was all for the CLI. Now we check for libgap,
+                # too. There's a long list of headers we need in
+                # src/sage/libs/gap/gap_includes.pxd, but libgap-api.h
+                # combined with the version test above should be
+                # sufficient even on systems where the headers are
+                # packaged separately.
+                _old_libs=$LIBS
+                LIBS="${LIBS} -lgap"
+                AC_LANG_PUSH([C])
+                AC_LINK_IFELSE([
+                  AC_LANG_PROGRAM(
+                    [[#include ]],
+                    [[
+                      int main(int argc, char** argv) {
+                        GAP_Initialize(0, 0, 0, 0, 0);
+                        return 0;
+                      }
+                    ]])
+                ],[
+                  AC_MSG_RESULT([yes])
+                  sage_spkg_install_gap=no
+                ],[
+                  AC_MSG_RESULT([no])
+                ])
+                AC_LANG_POP
+                LIBS="${_old_libs}"
+            ])
+          ], [
+            # The gap command itself failed
+            AC_MSG_RESULT([no (package check command failed)])
+          ])
+        ])
+      ],[
+        # Version too old
+        AC_MSG_RESULT([no])
+      ])
+    ])
+  ])
+
+  m4_popdef([GAP_MINVER])
+],[],[],[
+  # This is the post-check phase, where we make sage-conf
+  # substitutions, in this case of GAP_ROOT_PATHS. We begin with the
+  # two root paths used by the sage distribution. The '${prefix}' is
+  # a magic string that sage-conf will replace.
+  GAP_ROOT_PATHS='${prefix}/lib/gap;${prefix}/share/gap';
+
+  AS_IF([test "${sage_spkg_install_gap}" = "no"],[
+    # If we're using the system GAP, append the system root
+    # paths to the existing two sage paths.
+    GAP_ROOT_PATHS="${GAP_ROOT_PATHS};${SYS_GAP_ROOT_PATHS}"
+  ])
+
+  AC_SUBST(GAP_ROOT_PATHS, "${GAP_ROOT_PATHS}")
+])
diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in
index 47a7a8700ac..7d207a47a48 100644
--- a/build/pkgs/gap/spkg-install.in
+++ b/build/pkgs/gap/spkg-install.in
@@ -16,46 +16,43 @@ if [ "$SAGE_DEBUG" = yes ] ; then
     export CFLAGS="-O0 -g3 -DDEBUG_MASTERPOINTERS -DDEBUG_GLOBAL_BAGS -DDEBUG_FUNCTIONS_BAGS $CFLAGS"
 fi
 
-# LDFLAGS hack below needed by Semigroups package
-sdh_configure $SAGE_CONFIGURE_GMP LDFLAGS="-pthread" --prefix=$SAGE_LOCAL
+sdh_configure $SAGE_CONFIGURE_GMP --prefix=$SAGE_LOCAL
 sdh_make
-
 sdh_make_install
-# sdh_make install-headers install-libgap
-# The 'packagemanager' package expects this https://github.com/gap-packages/PackageManager/issues/105
-mkdir -p "$SAGE_LOCAL/lib/gap/bin"
 
 # Install only the minimal packages GAP needs to run
 sdh_install pkg/gapdoc pkg/primgrp pkg/smallgrp pkg/transgrp "$GAP_ROOT"/pkg
 
-# Install additional packages that are not strictly required, but that are
-# typically "expected" to be loaded: These are the default packages that are
-# autoloaded at GAP startup (via the PackagesToLoad UserPreference) with an
-# out-of-the-box GAP installation; see
-# https://github.com/sagemath/sage/issues/22626#comment:393 for discussion on this
-#
-# Also include atlasrep which is a dependency of tomlib
+# Install additional packages that are automatically loaded in the
+# default GAP configuration. The list can be found in lib/package.gi
+# as part of the "PackagesToLoad" user preference. Also include
+# atlasrep because it is a dependency of tomlib.
 sdh_install \
+    pkg/alnuth \
     pkg/atlasrep \
-    pkg/autodoc \
     pkg/autpgrp \
-    pkg/alnuth \
     pkg/crisp \
     pkg/ctbllib \
     pkg/factint \
     pkg/fga \
     pkg/irredsol \
     pkg/laguna \
-    pkg/packagemanager \
     pkg/polenta \
     pkg/polycyclic \
-    pkg/radiroot \
     pkg/resclasses \
     pkg/sophus \
     pkg/tomlib \
-    pkg/utils \
     "$GAP_ROOT"/pkg
 
+# Finally, install packagemanager for the people who reject both
+# sage's and their system's package managers. We have to create
+# the local bin directory first:
+#
+#   https://github.com/gap-packages/PackageManager/issues/105
+#
+mkdir -p "$SAGE_LOCAL/lib/gap/bin"
+sdh_install pkg/packagemanager "$GAP_ROOT"/pkg
+
 # TODO: This seems unnecessary--we are already installing all of doc/ to
 # GAP_ROOT, which is necessary for some functionality in GAP to work.  Do
 # we need this?  Maybe doc/gap could just be a symlink to gap/doc??
diff --git a/build/pkgs/gap/spkg-legacy-uninstall b/build/pkgs/gap/spkg-legacy-uninstall
deleted file mode 100755
index a8e5c59e1fb..00000000000
--- a/build/pkgs/gap/spkg-legacy-uninstall
+++ /dev/null
@@ -1,11 +0,0 @@
-#! /usr/bin/env bash
-# Remove existing GAP 4.x install(s)
-rm -rf "$SAGE_LOCAL/gap/gap-4."*
-rm -rf "$SAGE_SHARE/gap"
-rm -f "$SAGE_LOCAL/gap/latest"
-rm -f "$SAGE_LOCAL/bin/gap"
-rm -f "$SAGE_LOCAL/bin/gac"
-
-# Remove old libgap headers and library
-rm -rf "$SAGE_LOCAL/include/gap"
-rm -rf "$SAGE_LOCAL/lib/gap"
diff --git a/build/pkgs/gap_packages/SPKG.rst b/build/pkgs/gap_packages/SPKG.rst
index 67d3256637a..96238eef804 100644
--- a/build/pkgs/gap_packages/SPKG.rst
+++ b/build/pkgs/gap_packages/SPKG.rst
@@ -7,6 +7,20 @@ Description
 Several "official" and "undeposited" GAP packages available from
 https://www.gap-system.org/Packages/packages.html
 
+Installing this SPKG will install the corresponding GAP packages, but
+before you can use them in Sage, they still have to be loaded into
+either the GAP interface or libgap::
+
+  sage: gap.eval('LoadPackage("Grape")')  # optional - gap_packages
+  'true'
+  sage: libgap.LoadPackage("Grape")       # optional - gap_packages
+  true
+
+Those correspond to::
+
+  gap> LoadPackage("Grape");
+
+within the GAP interface and libgap, respectively.
 
 Upstream Contact
 ----------------
diff --git a/build/pkgs/gap_packages/spkg-install.in b/build/pkgs/gap_packages/spkg-install.in
index 6dff182a48f..7005cc3d322 100644
--- a/build/pkgs/gap_packages/spkg-install.in
+++ b/build/pkgs/gap_packages/spkg-install.in
@@ -1,5 +1,34 @@
-GAP_ROOT="$SAGE_LOCAL/lib/gap"
-PKG_DIR="$GAP_ROOT/pkg"
+# Ask GAP for the directory where sysinfo.gap lives. This is to
+# support system GAP installations. This root-path gathering
+# command is borrowed from gap's spkg-configure.m4 and modified
+# to separate the paths with spaces.
+GAPRUN="gap -r -q --bare --nointeract -c"
+_cmd='Display(JoinStringsWithSeparator(GAPInfo.RootPaths," "));'
+GAP_ROOT_PATHS=$(${GAPRUN} "${_cmd}")
+
+# Loop though GAP_ROOT_PATHS looking for sysinfo.gap
+GAP_ROOT=""
+for grp in $GAP_ROOT_PATHS; do
+    if [ -f "${grp}/sysinfo.gap" ]; then
+        GAP_ROOT=$grp
+        echo "found GAP root $GAP_ROOT"
+        break
+    fi
+done
+
+# Try the old sage default if nothing else worked.
+if [ -z "$GAP_ROOT" ]; then
+    GAP_ROOT="$SAGE_LOCAL/lib/gap"
+    echo "falling back to GAP root $GAP_ROOT"
+fi
+
+# And finally, throw an error ASAP if the build is going to fail anyway.
+if [ ! -f "${GAP_ROOT}/sysinfo.gap" ]; then
+    sdh_die "no sysinfo.gap in your gap root"
+fi
+
+# Where to install these packages
+PKG_DIR="$SAGE_LOCAL/lib/gap/pkg"
 
 PKG_SRC_DIR="$(pwd)/src/pkg"
 cd "$PKG_SRC_DIR"
@@ -12,6 +41,7 @@ cd "$PKG_SRC_DIR"
 
 sdh_install \
     aclib \
+    autodoc \
     corelg \
     crime \
     cryst \
@@ -31,11 +61,13 @@ sdh_install \
     polymaking \
     qpa \
     quagroup \
+    radiroot \
     repsn \
     singular \
     sla \
     sonata \
     toric \
+    utils \
     "$PKG_DIR"
 
 install_compiled_pkg()
diff --git a/build/pkgs/gc/distros/fedora.txt b/build/pkgs/gc/distros/fedora.txt
index 1d6df64a216..b6940f8b6aa 100644
--- a/build/pkgs/gc/distros/fedora.txt
+++ b/build/pkgs/gc/distros/fedora.txt
@@ -1 +1,2 @@
-gc gc-devel
+gc
+gc-devel
diff --git a/build/pkgs/gc/distros/opensuse.txt b/build/pkgs/gc/distros/opensuse.txt
index 4620f6a8cb4..f250631fcc2 100644
--- a/build/pkgs/gc/distros/opensuse.txt
+++ b/build/pkgs/gc/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(bdw-gc)"
+pkgconfig(bdw-gc)
diff --git a/build/pkgs/gf2x/checksums.ini b/build/pkgs/gf2x/checksums.ini
index 10a93c2c1bc..0eaf7657e40 100644
--- a/build/pkgs/gf2x/checksums.ini
+++ b/build/pkgs/gf2x/checksums.ini
@@ -2,4 +2,4 @@ tarball=gf2x-VERSION.tar.gz
 sha1=1b9c7e14031afc5488b9aa27f5501f78c90f00b4
 md5=842f087ce423c279dced26b85b0fd1d0
 cksum=3368093312
-upstream_url=https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-1.3.0.tar.gz
+upstream_url=https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-VERSION.tar.gz
diff --git a/build/pkgs/gf2x/distros/fedora.txt b/build/pkgs/gf2x/distros/fedora.txt
index 726d8da490c..3d1d2bb46bb 100644
--- a/build/pkgs/gf2x/distros/fedora.txt
+++ b/build/pkgs/gf2x/distros/fedora.txt
@@ -1 +1,2 @@
-gf2x gf2x-devel
+gf2x
+gf2x-devel
diff --git a/build/pkgs/gf2x/distros/opensuse.txt b/build/pkgs/gf2x/distros/opensuse.txt
index 9a15ad0abe3..9fb7b9fd16e 100644
--- a/build/pkgs/gf2x/distros/opensuse.txt
+++ b/build/pkgs/gf2x/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(gf2x)"
+pkgconfig(gf2x)
diff --git a/build/pkgs/gf2x/spkg-install.in b/build/pkgs/gf2x/spkg-install.in
index 057be0a5953..c8e4c83e6d2 100644
--- a/build/pkgs/gf2x/spkg-install.in
+++ b/build/pkgs/gf2x/spkg-install.in
@@ -1,7 +1,3 @@
-echo "Deleting old gf2x files."
-rm -f "$SAGE_LOCAL"/lib/libgf2x*
-rm -rf "$SAGE_LOCAL"/include/gf2x*
-
 cd src
 
 # Use newer version of config.guess and config.sub (see Trac #19727)
diff --git a/build/pkgs/gfan/spkg-legacy-uninstall b/build/pkgs/gfan/spkg-legacy-uninstall
deleted file mode 100755
index 74552b0b71c..00000000000
--- a/build/pkgs/gfan/spkg-legacy-uninstall
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /usr/bin/env bash
-echo "Removing old version of gfan (if any)..."
-rm -f "$SAGE_LOCAL"/bin/gfan*
diff --git a/build/pkgs/gfortran/distros/macports.txt b/build/pkgs/gfortran/distros/macports.txt
index c2878adf893..dfa7190f158 100644
--- a/build/pkgs/gfortran/distros/macports.txt
+++ b/build/pkgs/gfortran/distros/macports.txt
@@ -1 +1,2 @@
-gcc10 +gfortran
+gcc10
++gfortran
diff --git a/build/pkgs/giac/distros/arch.txt b/build/pkgs/giac/distros/arch.txt
index 7bdbd844d6c..f54c3286e4f 100644
--- a/build/pkgs/giac/distros/arch.txt
+++ b/build/pkgs/giac/distros/arch.txt
@@ -1 +1,2 @@
-libgiac giac
+libgiac
+giac
diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in
index e3eb86e6034..ceed3899be6 100644
--- a/build/pkgs/giac/spkg-install.in
+++ b/build/pkgs/giac/spkg-install.in
@@ -60,24 +60,6 @@ sdh_configure --disable-gui --disable-ao "$DISABLENLS" --enable-png=no --disable
 
 sdh_make
 
-#############################################################
-#   Clean old install
-#############################################################
-echo "Cleaning giac..."
-rm -f ${SAGE_LOCAL}/lib/libgiac*
-rm -f ${SAGE_LOCAL}/bin/icas
-rm -f ${SAGE_LOCAL}/bin/xcas
-rm -f ${SAGE_LOCAL}/bin/cas_help
-rm -f ${SAGE_LOCAL}/bin/pgiac
-rm -f ${SAGE_LOCAL}/bin/en_cas_help
-rm -f ${SAGE_LOCAL}/bin/es_cas_help
-rm -f ${SAGE_LOCAL}/bin/fr_cas_help
-rm -f ${SAGE_LOCAL}/bin/giac
-rm -f ${SAGE_LOCAL}/bin/xcasnew
-rm -rf ${SAGE_LOCAL}/share/giac
-rm -rf ${SAGE_LOCAL}/share/doc/giac
-rm -rf ${SAGE_LOCAL}/include/giac
-
 #############################################################
 #   Install
 #############################################################
diff --git a/build/pkgs/givaro/distros/fedora.txt b/build/pkgs/givaro/distros/fedora.txt
index 6848d8c4e7f..9016d00aaab 100644
--- a/build/pkgs/givaro/distros/fedora.txt
+++ b/build/pkgs/givaro/distros/fedora.txt
@@ -1 +1,2 @@
-givaro givaro-devel
+givaro
+givaro-devel
diff --git a/build/pkgs/givaro/distros/opensuse.txt b/build/pkgs/givaro/distros/opensuse.txt
index 2a8103e6f6b..8de5fadae90 100644
--- a/build/pkgs/givaro/distros/opensuse.txt
+++ b/build/pkgs/givaro/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(givaro)"
+pkgconfig(givaro)
diff --git a/build/pkgs/glpk/distros/cygwin.txt b/build/pkgs/glpk/distros/cygwin.txt
index 2e6c6a10cb8..0fbb959079e 100644
--- a/build/pkgs/glpk/distros/cygwin.txt
+++ b/build/pkgs/glpk/distros/cygwin.txt
@@ -1 +1,2 @@
-glpk libglpk-devel
+glpk
+libglpk-devel
diff --git a/build/pkgs/glpk/distros/fedora.txt b/build/pkgs/glpk/distros/fedora.txt
index 73ab7f14b14..e028b246574 100644
--- a/build/pkgs/glpk/distros/fedora.txt
+++ b/build/pkgs/glpk/distros/fedora.txt
@@ -1 +1,3 @@
-glpk glpk-devel glpk-utils
+glpk
+glpk-devel
+glpk-utils
diff --git a/build/pkgs/glpk/spkg-install.in b/build/pkgs/glpk/spkg-install.in
index a911c46f024..f592a562148 100644
--- a/build/pkgs/glpk/spkg-install.in
+++ b/build/pkgs/glpk/spkg-install.in
@@ -25,8 +25,4 @@ export CFLAGS LDFLAGS CPPFLAGS
 sdh_configure --with-gmp --disable-static
 sdh_make
 
-# Remove old libraries to make sure we can downgrade it if needed.
-# See https://github.com/sagemath/sage/issues/23596#comment:4 and later.
-rm -f "$SAGE_LOCAL"/lib/libglpk.*
-
 sdh_make_install
diff --git a/build/pkgs/gmp/distros/fedora.txt b/build/pkgs/gmp/distros/fedora.txt
index bbe9ee39cab..1436ed982e7 100644
--- a/build/pkgs/gmp/distros/fedora.txt
+++ b/build/pkgs/gmp/distros/fedora.txt
@@ -1 +1,2 @@
-gmp gmp-devel
+gmp
+gmp-devel
diff --git a/build/pkgs/gnumake_tokenpool/checksums.ini b/build/pkgs/gnumake_tokenpool/checksums.ini
index 62f631ea40a..e79058d27e3 100644
--- a/build/pkgs/gnumake_tokenpool/checksums.ini
+++ b/build/pkgs/gnumake_tokenpool/checksums.ini
@@ -1,5 +1,5 @@
 tarball=gnumake_tokenpool-VERSION-py3-none-any.whl
-sha1=a060f03e0306a85bc1a91a450e457be83ed371e9
-md5=834ccc4d6d52741c5eabac1bdb8f39b2
-cksum=1679797266
+sha1=3dfcc8c466c17f974d90694f81f4481c3d84aecc
+md5=5dae4c65e9609853085ae1970d4fe143
+cksum=612213211
 upstream_url=https://pypi.io/packages/py3/g/gnumake_tokenpool/gnumake_tokenpool-VERSION-py3-none-any.whl
diff --git a/build/pkgs/gnumake_tokenpool/install-requires.txt b/build/pkgs/gnumake_tokenpool/install-requires.txt
index 0ee4452edd3..759ca97a680 100644
--- a/build/pkgs/gnumake_tokenpool/install-requires.txt
+++ b/build/pkgs/gnumake_tokenpool/install-requires.txt
@@ -1 +1 @@
-gnumake-tokenpool
+gnumake-tokenpool >= 0.0.4
diff --git a/build/pkgs/gnumake_tokenpool/package-version.txt b/build/pkgs/gnumake_tokenpool/package-version.txt
index bcab45af15a..81340c7e72d 100644
--- a/build/pkgs/gnumake_tokenpool/package-version.txt
+++ b/build/pkgs/gnumake_tokenpool/package-version.txt
@@ -1 +1 @@
-0.0.3
+0.0.4
diff --git a/build/pkgs/graphs/checksums.ini b/build/pkgs/graphs/checksums.ini
index 285778ef3a7..7ec4a8a6ada 100644
--- a/build/pkgs/graphs/checksums.ini
+++ b/build/pkgs/graphs/checksums.ini
@@ -2,4 +2,4 @@ tarball=graphs-VERSION.tar.bz2
 sha1=c3b9fcbc92482efd6b7f6f3a33df5a78e1256aa1
 md5=4357919410e8ac2611c9fe643976c8ff
 cksum=2340933149
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/graphs-20210214.tar.bz2
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/graphs-VERSION.tar.bz2
diff --git a/build/pkgs/gsl/distros/fedora.txt b/build/pkgs/gsl/distros/fedora.txt
index 5577d09957b..ce35d3d80bd 100644
--- a/build/pkgs/gsl/distros/fedora.txt
+++ b/build/pkgs/gsl/distros/fedora.txt
@@ -1 +1,2 @@
-gsl gsl-devel
+gsl
+gsl-devel
diff --git a/build/pkgs/gsl/distros/opensuse.txt b/build/pkgs/gsl/distros/opensuse.txt
index ccfee4f4e43..c1867648576 100644
--- a/build/pkgs/gsl/distros/opensuse.txt
+++ b/build/pkgs/gsl/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(gsl)"
+pkgconfig(gsl)
diff --git a/build/pkgs/gsl/spkg-legacy-uninstall b/build/pkgs/gsl/spkg-legacy-uninstall
deleted file mode 100755
index 6c42f90b665..00000000000
--- a/build/pkgs/gsl/spkg-legacy-uninstall
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "$SAGE_LOCAL"/include/gsl
-rm -rf "$SAGE_LOCAL"/lib/libgsl*
diff --git a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
index 6d48eb14be8..6da100dad5a 100644
--- a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
+++ b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
@@ -1,5 +1,5 @@
-tarball=hatch_fancy_pypi_readme-VERSION.tar.gz
-sha1=8c8568bb86bdc65133e9d8b27493a464fa3320da
-md5=8755cce1a4a4d5e5d84992089801acbf
-cksum=2176903739
-upstream_url=https://pypi.io/packages/source/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION.tar.gz
+tarball=hatch_fancy_pypi_readme-VERSION-py3-none-any.whl
+sha1=4076ea14577b3c711a8345498d8f91b1c8a13d09
+md5=d7acd13333f6c71dcbfa62420c7f257b
+cksum=1527082323
+upstream_url=https://pypi.io/packages/py3/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION-py3-none-any.whl
diff --git a/build/pkgs/hatch_fancy_pypi_readme/dependencies b/build/pkgs/hatch_fancy_pypi_readme/dependencies
index cfb7c484697..36380aee4e7 100644
--- a/build/pkgs/hatch_fancy_pypi_readme/dependencies
+++ b/build/pkgs/hatch_fancy_pypi_readme/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) hatchling $(PYTHON)
+hatchling | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in b/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/hatch_vcs/SPKG.rst b/build/pkgs/hatch_vcs/SPKG.rst
index 51f4780749e..6c11d8d95fb 100644
--- a/build/pkgs/hatch_vcs/SPKG.rst
+++ b/build/pkgs/hatch_vcs/SPKG.rst
@@ -9,6 +9,8 @@ Hatch plugin for versioning with your preferred VCS
 License
 -------
 
+MIT
+
 Upstream Contact
 ----------------
 
diff --git a/build/pkgs/hatch_vcs/checksums.ini b/build/pkgs/hatch_vcs/checksums.ini
index 9c1290bda3b..e9908b1540e 100644
--- a/build/pkgs/hatch_vcs/checksums.ini
+++ b/build/pkgs/hatch_vcs/checksums.ini
@@ -1,5 +1,5 @@
-tarball=hatch_vcs-VERSION.tar.gz
-sha1=026b964066b38fd7e823900817fb9ea7af3f8d9b
-md5=c2f2cbe6851b7b2969cb4aa24c4b9b2f
-cksum=588874896
-upstream_url=https://pypi.io/packages/source/h/hatch_vcs/hatch_vcs-VERSION.tar.gz
+tarball=hatch_vcs-VERSION-py3-none-any.whl
+sha1=a4da813bf9a745ed2020bf462f4bc19a1ab7fc11
+md5=82bee9889b95170e550c98f8dd11bc61
+cksum=1575327634
+upstream_url=https://pypi.io/packages/py3/h/hatch_vcs/hatch_vcs-VERSION-py3-none-any.whl
diff --git a/build/pkgs/hatch_vcs/dependencies b/build/pkgs/hatch_vcs/dependencies
index cfb7c484697..36380aee4e7 100644
--- a/build/pkgs/hatch_vcs/dependencies
+++ b/build/pkgs/hatch_vcs/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) hatchling $(PYTHON)
+hatchling | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/hatch_vcs/package-version.txt b/build/pkgs/hatch_vcs/package-version.txt
index 0d91a54c7d4..1d0ba9ea182 100644
--- a/build/pkgs/hatch_vcs/package-version.txt
+++ b/build/pkgs/hatch_vcs/package-version.txt
@@ -1 +1 @@
-0.3.0
+0.4.0
diff --git a/build/pkgs/hatch_vcs/spkg-install.in b/build/pkgs/hatch_vcs/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/hatch_vcs/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini
index d21781abeba..5d8b02696c5 100644
--- a/build/pkgs/hatchling/checksums.ini
+++ b/build/pkgs/hatchling/checksums.ini
@@ -1,5 +1,5 @@
-tarball=hatchling-VERSION.tar.gz
-sha1=f3db8639e9bee89e2e2420d1bc7a048a910622c9
-md5=43f7203cacb6c3c178b93149b8a8151d
-cksum=235277633
-upstream_url=https://pypi.io/packages/source/h/hatchling/hatchling-VERSION.tar.gz
+tarball=hatchling-VERSION-py3-none-any.whl
+sha1=aa9d69b9dd820716440252d737a4aeaf9b4e541f
+md5=20e5ea4deea21f91759fb2269b71f0dd
+cksum=446304413
+upstream_url=https://pypi.io/packages/py3/h/hatchling/hatchling-VERSION-py3-none-any.whl
diff --git a/build/pkgs/hatchling/dependencies b/build/pkgs/hatchling/dependencies
index 479f1630979..b1769861625 100644
--- a/build/pkgs/hatchling/dependencies
+++ b/build/pkgs/hatchling/dependencies
@@ -1,4 +1,4 @@
- pathspec tomli editables pluggy packaging trove_classifiers | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ pathspec tomli editables pluggy packaging trove_classifiers | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt
index 84cc529467b..39893559155 100644
--- a/build/pkgs/hatchling/package-version.txt
+++ b/build/pkgs/hatchling/package-version.txt
@@ -1 +1 @@
-1.18.0
+1.20.0
diff --git a/build/pkgs/hatchling/spkg-install.in b/build/pkgs/hatchling/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/hatchling/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/igraph/checksums.ini b/build/pkgs/igraph/checksums.ini
index 279cba457a9..51192799072 100644
--- a/build/pkgs/igraph/checksums.ini
+++ b/build/pkgs/igraph/checksums.ini
@@ -1,5 +1,5 @@
 tarball=igraph-VERSION.tar.gz
-sha1=fc3c6627f889b13581b2b468e1b16aceff453cfc
-md5=10a3f325425970c75a7ba8359376e208
-cksum=3103730646
+sha1=40efbd2adf3c1cc0a2bb3e14f4c7898d053f1fe4
+md5=87d287445c44fdd19ad67a25e0e0859b
+cksum=2559618807
 upstream_url=https://github.com/igraph/igraph/releases/download/VERSION/igraph-VERSION.tar.gz
diff --git a/build/pkgs/igraph/distros/fedora.txt b/build/pkgs/igraph/distros/fedora.txt
index 8ca7a112d45..fa2dc51a2ce 100644
--- a/build/pkgs/igraph/distros/fedora.txt
+++ b/build/pkgs/igraph/distros/fedora.txt
@@ -1 +1,2 @@
-igraph igraph-devel
+igraph
+igraph-devel
diff --git a/build/pkgs/igraph/package-version.txt b/build/pkgs/igraph/package-version.txt
index 9b40aa6c214..1a46c7f13e7 100644
--- a/build/pkgs/igraph/package-version.txt
+++ b/build/pkgs/igraph/package-version.txt
@@ -1 +1 @@
-0.10.4
+0.10.8
diff --git a/build/pkgs/igraph/spkg-configure.m4 b/build/pkgs/igraph/spkg-configure.m4
index 30b5fcf9c53..5133fce98da 100644
--- a/build/pkgs/igraph/spkg-configure.m4
+++ b/build/pkgs/igraph/spkg-configure.m4
@@ -1,7 +1,9 @@
 SAGE_SPKG_CONFIGURE([igraph], [
   SAGE_SPKG_DEPCHECK([glpk openblas gmp], [
     dnl check for igraph with pkg-config
-    PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.10 igraph < 0.11], [], [
+    dnl Per upstream in https://github.com/sagemath/sage/pull/36750#issuecomment-1826998762:
+    dnl each python-igraph release is only guaranteed to be compatible with the same C/igraph that it bundles
+    PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.10.8 igraph < 0.10.9], [], [
         sage_spkg_install_igraph=yes])
   ])
 ])
diff --git a/build/pkgs/iml/distros/fedora.txt b/build/pkgs/iml/distros/fedora.txt
index 32307096d65..88a895a10f6 100644
--- a/build/pkgs/iml/distros/fedora.txt
+++ b/build/pkgs/iml/distros/fedora.txt
@@ -1 +1,2 @@
-iml iml-devel
+iml
+iml-devel
diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini
index 8c978b57c5b..506ef54e9a5 100644
--- a/build/pkgs/importlib_metadata/checksums.ini
+++ b/build/pkgs/importlib_metadata/checksums.ini
@@ -1,5 +1,5 @@
-tarball=importlib_metadata-VERSION.tar.gz
-sha1=0e5742cd1d559863573bfb4f6cb7ca9ad8dcf466
-md5=c04c814eee1abf42790cfa4bd0454af1
-cksum=1038169537
-upstream_url=https://pypi.io/packages/source/i/importlib_metadata/importlib_metadata-VERSION.tar.gz
+tarball=importlib_metadata-VERSION-py3-none-any.whl
+sha1=0e693ba704ac628065bab72a097dad3311213f4b
+md5=67eac786f2f9e8bf5879e88ef527250a
+cksum=1288292429
+upstream_url=https://pypi.io/packages/py3/i/importlib_metadata/importlib_metadata-VERSION-py3-none-any.whl
diff --git a/build/pkgs/importlib_metadata/package-version.txt b/build/pkgs/importlib_metadata/package-version.txt
index e029aa99b7d..66ce77b7ead 100644
--- a/build/pkgs/importlib_metadata/package-version.txt
+++ b/build/pkgs/importlib_metadata/package-version.txt
@@ -1 +1 @@
-6.8.0
+7.0.0
diff --git a/build/pkgs/importlib_metadata/spkg-install.in b/build/pkgs/importlib_metadata/spkg-install.in
deleted file mode 100644
index cc03b63f4f2..00000000000
--- a/build/pkgs/importlib_metadata/spkg-install.in
+++ /dev/null
@@ -1,14 +0,0 @@
-if [ -z "$SAGE_LOCAL" ]; then
-    echo >&2 "SAGE_LOCAL undefined ... exiting"
-    echo >&2 "Maybe run 'sage --sh'?"
-    exit 1
-fi
-
-cd src
-
-sdh_pip_install .
-
-if [ $? -ne 0 ]; then
-    echo "Error installing importlib_metadata ... exiting"
-    exit 1
-fi
diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini
index 39040fbf296..e733e142b52 100644
--- a/build/pkgs/importlib_resources/checksums.ini
+++ b/build/pkgs/importlib_resources/checksums.ini
@@ -1,5 +1,5 @@
-tarball=importlib_resources-VERSION.tar.gz
-sha1=4af82ed75a1672a45157bfa7d09c4dfd0605802a
-md5=525d238db212bdec2df06c0d4b479e73
-cksum=1494471486
-upstream_url=https://pypi.io/packages/source/i/importlib_resources/importlib_resources-VERSION.tar.gz
+tarball=importlib_resources-VERSION-py3-none-any.whl
+sha1=5caa4e8a9ee93123a5c3badb6edbc009b5d8494a
+md5=a4ba26d808eed58bde249276da04c9e1
+cksum=2487282894
+upstream_url=https://pypi.io/packages/py3/i/importlib_resources/importlib_resources-VERSION-py3-none-any.whl
diff --git a/build/pkgs/importlib_resources/dependencies b/build/pkgs/importlib_resources/dependencies
index 655283898b7..30f71335acb 100644
--- a/build/pkgs/importlib_resources/dependencies
+++ b/build/pkgs/importlib_resources/dependencies
@@ -1,4 +1,4 @@
- zipp | $(PYTHON_TOOLCHAIN) $(PYTHON)
+zipp | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/importlib_resources/package-version.txt b/build/pkgs/importlib_resources/package-version.txt
index dfda3e0b4f0..f3b5af39e43 100644
--- a/build/pkgs/importlib_resources/package-version.txt
+++ b/build/pkgs/importlib_resources/package-version.txt
@@ -1 +1 @@
-6.1.0
+6.1.1
diff --git a/build/pkgs/importlib_resources/spkg-install.in b/build/pkgs/importlib_resources/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/importlib_resources/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/info/distros/fedora.txt b/build/pkgs/info/distros/fedora.txt
index c0d8f74e0ad..c3ed35de973 100644
--- a/build/pkgs/info/distros/fedora.txt
+++ b/build/pkgs/info/distros/fedora.txt
@@ -1 +1,2 @@
-texinfo info
+texinfo
+info
diff --git a/build/pkgs/ipython/spkg-install.in b/build/pkgs/ipython/spkg-install.in
index 5096a907a95..058b1344dc2 100644
--- a/build/pkgs/ipython/spkg-install.in
+++ b/build/pkgs/ipython/spkg-install.in
@@ -1,7 +1,3 @@
-# Old installations of ipython can leave a symlink which can interfere
-# with proper installation.
-rm -f "$SAGE_LOCAL"/bin/ipython
-
 cd src
 
 sdh_pip_install .
diff --git a/build/pkgs/isl/distros/opensuse.txt b/build/pkgs/isl/distros/opensuse.txt
index 721c2bb0dcb..ec70829dde7 100644
--- a/build/pkgs/isl/distros/opensuse.txt
+++ b/build/pkgs/isl/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(isl)"
+pkgconfig(isl)
diff --git a/build/pkgs/jmol/spkg-legacy-uninstall b/build/pkgs/jmol/spkg-legacy-uninstall
deleted file mode 100755
index 6c9bd1a2c08..00000000000
--- a/build/pkgs/jmol/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-# Cleanup of previous installation
-rm -rf "${SAGE_SHARE}/jsmol/"
-rm -rf "${SAGE_SHARE}/jmol/"
-rm -f "${SAGE_LOCAL}/bin/jmol"
diff --git a/build/pkgs/kenzo/checksums.ini b/build/pkgs/kenzo/checksums.ini
index ad2a7c4cc60..7fff82ca271 100644
--- a/build/pkgs/kenzo/checksums.ini
+++ b/build/pkgs/kenzo/checksums.ini
@@ -1,5 +1,5 @@
-tarball=kenzo-1.1.10.tar.gz
-upstream_url=https://github.com/miguelmarco/kenzo/releases/download/1.1.10/kenzo-1.1.10.tar.gz
+tarball=kenzo-VERSION.tar.gz
+upstream_url=https://github.com/miguelmarco/kenzo/releases/download/VERSION/kenzo-VERSION.tar.gz
 sha1=76115aae9972090d5d51fee18592fc7a79461474
 md5=3a3d5350fb17304f03e614713e585ed4
 cksum=2981306888
diff --git a/build/pkgs/lcalc/spkg-legacy-uninstall b/build/pkgs/lcalc/spkg-legacy-uninstall
deleted file mode 100755
index 4cacdfd9367..00000000000
--- a/build/pkgs/lcalc/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -fr "$SAGE_LOCAL"/include/libLfunction
diff --git a/build/pkgs/libatomic_ops/distros/fedora.txt b/build/pkgs/libatomic_ops/distros/fedora.txt
index 3417f891c11..d1f77fa5ad7 100644
--- a/build/pkgs/libatomic_ops/distros/fedora.txt
+++ b/build/pkgs/libatomic_ops/distros/fedora.txt
@@ -1 +1,2 @@
-libatomic_ops libatomic_ops-devel
+libatomic_ops
+libatomic_ops-devel
diff --git a/build/pkgs/libatomic_ops/distros/opensuse.txt b/build/pkgs/libatomic_ops/distros/opensuse.txt
index 6e7e1c4cecb..0d82eae1b32 100644
--- a/build/pkgs/libatomic_ops/distros/opensuse.txt
+++ b/build/pkgs/libatomic_ops/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(atomic_ops)"
+pkgconfig(atomic_ops)
diff --git a/build/pkgs/libbraiding/checksums.ini b/build/pkgs/libbraiding/checksums.ini
index bb76fa5b604..466c33c66a5 100644
--- a/build/pkgs/libbraiding/checksums.ini
+++ b/build/pkgs/libbraiding/checksums.ini
@@ -1,5 +1,5 @@
-tarball=libbraiding-VERSION.tar.gz
-sha1=06610e47eb243b27aea0ad399b41614fcdb179c9
-md5=5466605026b90bdca7ca20852f88b5c5
-cksum=704753563
-upstream_url=https://github.com/miguelmarco/libbraiding/releases/download/1.1/libbraiding-1.1.tar.gz
+tarball=libbraiding-VERSION-actually-VERSION.tar.gz
+sha1=b7e13778784fe1e36e7c0cbd7a4c234a090cd1b2
+md5=0513967c81b783ea66336b7ad0562534
+cksum=3619705925
+upstream_url=https://github.com/miguelmarco/libbraiding/releases/download/VERSION/libbraiding-VERSION.tar.gz
diff --git a/build/pkgs/libffi/distros/fedora.txt b/build/pkgs/libffi/distros/fedora.txt
index 8d342f491c1..70c40a27925 100644
--- a/build/pkgs/libffi/distros/fedora.txt
+++ b/build/pkgs/libffi/distros/fedora.txt
@@ -1 +1,2 @@
-libffi libffi-devel
+libffi
+libffi-devel
diff --git a/build/pkgs/libffi/distros/opensuse.txt b/build/pkgs/libffi/distros/opensuse.txt
index 1a6986d429e..39fa66806ec 100644
--- a/build/pkgs/libffi/distros/opensuse.txt
+++ b/build/pkgs/libffi/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(libffi)"
+pkgconfig(libffi)
diff --git a/build/pkgs/libgd/distros/fedora.txt b/build/pkgs/libgd/distros/fedora.txt
index d27cc48549e..1f276d6eb09 100644
--- a/build/pkgs/libgd/distros/fedora.txt
+++ b/build/pkgs/libgd/distros/fedora.txt
@@ -1 +1,2 @@
-gd gd-devel
+gd
+gd-devel
diff --git a/build/pkgs/libgd/distros/macports.txt b/build/pkgs/libgd/distros/macports.txt
index d44f88f27cc..ee5e3357f06 100644
--- a/build/pkgs/libgd/distros/macports.txt
+++ b/build/pkgs/libgd/distros/macports.txt
@@ -1 +1,2 @@
-gd2
+# broken as of 2022-05-01 -- and also has too many dependencies
+#gd2
diff --git a/build/pkgs/libgd/distros/opensuse.txt b/build/pkgs/libgd/distros/opensuse.txt
index 48195aa6b44..bcf953527f8 100644
--- a/build/pkgs/libgd/distros/opensuse.txt
+++ b/build/pkgs/libgd/distros/opensuse.txt
@@ -1,2 +1,2 @@
 gd
-"pkgconfig(gdlib)"
+pkgconfig(gdlib)
diff --git a/build/pkgs/libgd/distros/slackware.txt b/build/pkgs/libgd/distros/slackware.txt
index 5d28bb87ae1..442f2e7f440 100644
--- a/build/pkgs/libgd/distros/slackware.txt
+++ b/build/pkgs/libgd/distros/slackware.txt
@@ -1,3 +1,8 @@
 gd
 # shared library dependencies of gd
-fontconfig libXpm libX11 libxcb libXau libXdmcp
+fontconfig
+libXpm
+libX11
+libxcb
+libXau
+libXdmcp
diff --git a/build/pkgs/libgd/spkg-legacy-uninstall b/build/pkgs/libgd/spkg-legacy-uninstall
deleted file mode 100755
index c67f4d2167d..00000000000
--- a/build/pkgs/libgd/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-# Critical to get rid of old versions, since they will break the install, since
-# at some point one of the libraries accidently links against what's in SAGE_LOCAL,
-# instead of what is in the build directory!
-rm "$SAGE_LOCAL"/lib/libgd.*
diff --git a/build/pkgs/liblzma/distros/fedora.txt b/build/pkgs/liblzma/distros/fedora.txt
index 813a264efd5..87d41237fa8 100644
--- a/build/pkgs/liblzma/distros/fedora.txt
+++ b/build/pkgs/liblzma/distros/fedora.txt
@@ -1 +1,2 @@
-xz xz-devel
+xz
+xz-devel
diff --git a/build/pkgs/liblzma/distros/opensuse.txt b/build/pkgs/liblzma/distros/opensuse.txt
index f84d903df3e..96db709ccb8 100644
--- a/build/pkgs/liblzma/distros/opensuse.txt
+++ b/build/pkgs/liblzma/distros/opensuse.txt
@@ -1,2 +1,2 @@
 xz
-"pkgconfig(liblzma)"
+pkgconfig(liblzma)
diff --git a/build/pkgs/libogg/distros/opensuse.txt b/build/pkgs/libogg/distros/opensuse.txt
index 886b7534f7b..cab88ad14f1 100644
--- a/build/pkgs/libogg/distros/opensuse.txt
+++ b/build/pkgs/libogg/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(ogg)"
+pkgconfig(ogg)
diff --git a/build/pkgs/libpng/distros/opensuse.txt b/build/pkgs/libpng/distros/opensuse.txt
index 3283fd2e697..40ec3d0a25c 100644
--- a/build/pkgs/libpng/distros/opensuse.txt
+++ b/build/pkgs/libpng/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(libpng16)"
+pkgconfig(libpng16)
diff --git a/build/pkgs/libsemigroups/distros/opensuse.txt b/build/pkgs/libsemigroups/distros/opensuse.txt
index aca27d4e0e3..3e58abf59c9 100644
--- a/build/pkgs/libsemigroups/distros/opensuse.txt
+++ b/build/pkgs/libsemigroups/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(libsemigroups)"
+pkgconfig(libsemigroups)
diff --git a/build/pkgs/libtheora/distros/opensuse.txt b/build/pkgs/libtheora/distros/opensuse.txt
index 19826d2aea6..156db81fdea 100644
--- a/build/pkgs/libtheora/distros/opensuse.txt
+++ b/build/pkgs/libtheora/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(theora)"
+pkgconfig(theora)
diff --git a/build/pkgs/linbox/distros/opensuse.txt b/build/pkgs/linbox/distros/opensuse.txt
index 1a52a7c51d3..efef6e5c743 100644
--- a/build/pkgs/linbox/distros/opensuse.txt
+++ b/build/pkgs/linbox/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(linbox)"
+pkgconfig(linbox)
diff --git a/build/pkgs/m4ri/distros/opensuse.txt b/build/pkgs/m4ri/distros/opensuse.txt
index c2a7c2c8f93..ea526e29d8e 100644
--- a/build/pkgs/m4ri/distros/opensuse.txt
+++ b/build/pkgs/m4ri/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(m4ri)"
+pkgconfig(m4ri)
diff --git a/build/pkgs/m4ri/spkg-install.in b/build/pkgs/m4ri/spkg-install.in
index c3ae2270406..0af3234ed16 100644
--- a/build/pkgs/m4ri/spkg-install.in
+++ b/build/pkgs/m4ri/spkg-install.in
@@ -24,9 +24,6 @@ else
     DISABLE_SSE2=""
 fi
 
-# otherwise we might run into problems with old headers
-rm -rf "$SAGE_LOCAL/include/m4ri"
-
 cd src
 
 # Configure and build M4RI
diff --git a/build/pkgs/m4rie/distros/opensuse.txt b/build/pkgs/m4rie/distros/opensuse.txt
index 4b8fbfa44df..78287631a6b 100644
--- a/build/pkgs/m4rie/distros/opensuse.txt
+++ b/build/pkgs/m4rie/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(m4rie)"
+pkgconfig(m4rie)
diff --git a/build/pkgs/m4rie/spkg-install.in b/build/pkgs/m4rie/spkg-install.in
index 925706f0581..bc0c13ffe1e 100644
--- a/build/pkgs/m4rie/spkg-install.in
+++ b/build/pkgs/m4rie/spkg-install.in
@@ -27,12 +27,6 @@ export CFLAGS
 export CPPFLAGS
 export LDFLAGS
 
-# otherwise we might run into problems with old headers
-
-if [ -d "$SAGE_LOCAL/include/m4rie" ]; then
-    rm -rf "$SAGE_LOCAL/include/m4rie"
-fi
-
 # build M4RIE
 
 cd $ROOT_DIR/src/
diff --git a/build/pkgs/mathjax/distros/gentoo.txt b/build/pkgs/mathjax/distros/gentoo.txt
index a347f8c2348..e0d633a9b98 100644
--- a/build/pkgs/mathjax/distros/gentoo.txt
+++ b/build/pkgs/mathjax/distros/gentoo.txt
@@ -1 +1 @@
-">=dev-libs/mathjax-3"
+>=dev-libs/mathjax-3
diff --git a/build/pkgs/mathjax/spkg-install.in b/build/pkgs/mathjax/spkg-install.in
index 31cdb426992..2eeaf6e84b4 100644
--- a/build/pkgs/mathjax/spkg-install.in
+++ b/build/pkgs/mathjax/spkg-install.in
@@ -1,4 +1,2 @@
 TARGET="${SAGE_SHARE}/mathjax"
-# Cleanup installed version
-rm -rf "${TARGET}"
 sdh_install src/* "${TARGET}"
diff --git a/build/pkgs/meataxe/spkg-install.in b/build/pkgs/meataxe/spkg-install.in
index 433e5ba7c86..231519fac23 100644
--- a/build/pkgs/meataxe/spkg-install.in
+++ b/build/pkgs/meataxe/spkg-install.in
@@ -1,16 +1,3 @@
-# Delete old (Shared)MeatAxe libraries. This ensures a sane state if
-# installation of this package fails: the mtx library should exist if
-# and only if meataxe is installed. In detail: the build-time check in
-# src/setup.py checks whether or not the "meataxe" package is
-# marked as installed but the run-time check for the matrix_gfpn_dense
-# module checks whether it can be imported. We need to ensure that these
-# two conditions are equivalent, otherwise strange things can happen.
-# See also https://github.com/sagemath/sage/issues/24359#comment:154
-#
-# This also deletes the static library left behind from the installation
-# of MeatAxe (as opposed to SharedMeatAxe).
-rm -f "$SAGE_LOCAL"/lib/libmtx.*
-
 # Directory where executables are installed.
 export MTXBIN="$SAGE_LOCAL"/bin
 
@@ -34,4 +21,3 @@ sdh_make_install
 if [ "$SAGE_SPKG_INSTALL_DOCS" = yes ] ; then
     $MAKE doc || sdh_die "Error documenting SharedMeatAxe"
 fi
-
diff --git a/build/pkgs/mpc/distros/fedora.txt b/build/pkgs/mpc/distros/fedora.txt
index 491a280bb72..19ae194c12c 100644
--- a/build/pkgs/mpc/distros/fedora.txt
+++ b/build/pkgs/mpc/distros/fedora.txt
@@ -1 +1,2 @@
-libmpc libmpc-devel
+libmpc
+libmpc-devel
diff --git a/build/pkgs/mpc/spkg-install.in b/build/pkgs/mpc/spkg-install.in
index b260b531f17..835bcc53ab2 100644
--- a/build/pkgs/mpc/spkg-install.in
+++ b/build/pkgs/mpc/spkg-install.in
@@ -12,13 +12,5 @@ EXTRA=""
 sdh_configure $SAGE_CONFIGURE_GMP $SAGE_CONFIGURE_MPFR $EXTRA
 sdh_make
 
-# Cleaning
-echo "Deleting old headers"
-rm -f "$SAGE_LOCAL"/include/mpc.h
-# Do not delete old libraries as this causes gcc to break during
-# parallel builds.
-# echo "Deleting old libraries"
-# rm -f "$SAGE_LOCAL"/lib/libmpc.*
-
 # Installing
 sdh_make_install
diff --git a/build/pkgs/mpfr/distros/opensuse.txt b/build/pkgs/mpfr/distros/opensuse.txt
index 564517323f0..dadf66949a4 100644
--- a/build/pkgs/mpfr/distros/opensuse.txt
+++ b/build/pkgs/mpfr/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(mpfr)"
+pkgconfig(mpfr)
diff --git a/build/pkgs/mpfr/spkg-install.in b/build/pkgs/mpfr/spkg-install.in
index a0ecd15ec43..b377e0a93b6 100644
--- a/build/pkgs/mpfr/spkg-install.in
+++ b/build/pkgs/mpfr/spkg-install.in
@@ -145,14 +145,6 @@ mpfr_build()
 {
     mpfr_configure
     sdh_make
-
-    echo
-    echo "Building MPFR succeeded.  Now deleting old headers..."
-    rm -f "$SAGE_LOCAL"/include/*mpfr*
-    # Do not delete old libraries as this causes gcc to break during
-    # parallel builds.
-    # rm -f "$SAGE_LOCAL"/lib/libmpfr.*
-
     sdh_make_install
 }
 
diff --git a/build/pkgs/mpfrcx/spkg-legacy-uninstall b/build/pkgs/mpfrcx/spkg-legacy-uninstall
deleted file mode 100755
index 3538ccc54e4..00000000000
--- a/build/pkgs/mpfrcx/spkg-legacy-uninstall
+++ /dev/null
@@ -1,4 +0,0 @@
-#! /usr/bin/env bash
-echo "Deleting old headers"
-rm -f "$SAGE_LOCAL"/include/mpfrcx.h
-rm -f "$SAGE_LOCAL"/lib/libmpfrcx.*
diff --git a/build/pkgs/nauty/checksums.ini b/build/pkgs/nauty/checksums.ini
index a9dd1246e91..f699a05e9fc 100644
--- a/build/pkgs/nauty/checksums.ini
+++ b/build/pkgs/nauty/checksums.ini
@@ -1,5 +1,5 @@
-tarball=nautyVERSION.tar.gz
-sha1=10c39117c55c69c18c6a107110e7c08f3d873652
-md5=7a82f4209f5d552da3078c67e5af872e
-cksum=2164796643
-upstream_url=https://pallini.di.uniroma1.it/nauty2_8_6.tar.gz
+tarball=nauty${VERSION}.tar.gz
+sha1=672e9fc9dfd07201af37ee65807a9b493331ed92
+md5=16c6edc1a8747c9281041b7c7092135f
+cksum=2663136901
+upstream_url=https://pallini.di.uniroma1.it/nauty${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}.tar.gz
diff --git a/build/pkgs/nauty/package-version.txt b/build/pkgs/nauty/package-version.txt
index 04e60eaa37a..b8635c72de3 100644
--- a/build/pkgs/nauty/package-version.txt
+++ b/build/pkgs/nauty/package-version.txt
@@ -1 +1 @@
-2.8.6.p1
+2.8.8.p0
diff --git a/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch b/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch
deleted file mode 100644
index 322b25326ee..00000000000
--- a/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch
+++ /dev/null
@@ -1,144 +0,0 @@
-From edb0474a4db8e69f971e4eebe18716309f5a7bb3 Mon Sep 17 00:00:00 2001
-From: Michael Orlitzky 
-Date: Tue, 17 Jan 2023 19:44:49 -0500
-Subject: [PATCH 1/1] Upstream fixes for gentreeg and gentourng.
-
-https://mailman.anu.edu.au/pipermail/nauty/2023-January/000903.html
----
- gentourng.c |  2 +-
- gentreeg.c  | 95 ++++++++++++++++++++++++++++-------------------------
- 2 files changed, 51 insertions(+), 46 deletions(-)
-
-diff --git a/gentourng.c b/gentourng.c
-index 634e5e8..5c7ffff 100644
---- a/gentourng.c
-+++ b/gentourng.c
-@@ -1408,7 +1408,7 @@ PLUGIN_INIT
-                 (*outproc)(outfile,g,1);
-             }
-         }
--        else
-+        else if (!connec || maxn != 2)
-         {
-             makeleveldata();
- 
-diff --git a/gentreeg.c b/gentreeg.c
-index 946d5f8..15bf87b 100644
---- a/gentreeg.c
-+++ b/gentreeg.c
-@@ -1,4 +1,4 @@
--/* gentree version 1.3; Brendan McKay Oct 2022 */
-+/* gentree version 1.4; Brendan McKay Dec 2022 */
- /* This program is a wrapper for the program FreeTrees.c written
-  * by Gang Li & Frank Ruskey.  See below for their original
-  * comments. */
-@@ -32,49 +32,54 @@ Counts for n=1..45:
-  1: 1
-  2: 1
-  3: 1
-- 4: 1
-- 5: 2
-- 6: 3
-- 7: 6
-- 8: 11
-- 9: 23
--10: 47
--11: 106
--12: 235
--13: 551
--14: 1301
--15: 3159
--16: 7741
--17: 19320
--18: 48629
--19: 123867
--20: 317955
--21: 823065
--22: 2144505
--23: 5623756
--24: 14828074
--25: 39299897
--26: 104636890
--27: 279793450
--28: 751065460
--29: 2023443032
--30: 5469566585
--31: 14830871802
--32: 40330829030
--33: 109972410221
--34: 300628862480
--35: 823779631721
--36: 2262366343746
--37: 6226306037178
--38: 17169677490714
--39: 47436313524262
--40: 131290543779126
--41: 363990257783343
--42: 1010748076717151
--43: 2810986483493475
--44: 7828986221515605
--45: 21835027912963086
--********************************/
-+ 4: 2
-+ 5: 3
-+ 6: 6
-+ 7: 11
-+ 8: 23
-+ 9: 47
-+10: 106
-+11: 235
-+12: 551
-+13: 1301
-+14: 3159
-+15: 7741
-+16: 19320
-+17: 48629
-+18: 123867
-+19: 317955
-+20: 823065
-+21: 2144505
-+22: 5623756
-+23: 14828074
-+24: 39299897
-+25: 104636890
-+26: 279793450
-+27: 751065460
-+28: 2023443032
-+29: 5469566585
-+30: 14830871802
-+31: 40330829030
-+32: 109972410221
-+33: 300628862480
-+34: 823779631721
-+35: 2262366343746
-+36: 6226306037178
-+37: 17169677490714
-+38: 47436313524262
-+39: 131290543779126
-+40: 363990257783343
-+41: 1010748076717151
-+42: 2810986483493475
-+43: 7828986221515605
-+44: 21835027912963086
-+45: 60978390985918906
-+46: 170508699155987862
-+47: 477355090753926460
-+48: 1337946100045842285
-+49: 3754194185716399992
-+50: 10545233702911509534
-+*******************************/
- 
- /* Comments on original program by original authors */
- /*==============================================================*/
-@@ -676,7 +681,7 @@ PLUGIN_INIT
-         }
-         else if (nv == 2)
-         {
--            if (res == 0 && maxdeg >= 1 && mindiam <= 1 && maxdiam >= 2)
-+            if (res == 0 && maxdeg >= 1 && mindiam <= 1 && maxdiam >= 1)
-             {
-                 par[1] = 0;
-                 par[2] = 1;
--- 
-2.38.2
-
diff --git a/build/pkgs/nauty/spkg-configure.m4 b/build/pkgs/nauty/spkg-configure.m4
index 4d5bbcf2091..a0873e355c4 100644
--- a/build/pkgs/nauty/spkg-configure.m4
+++ b/build/pkgs/nauty/spkg-configure.m4
@@ -1,12 +1,9 @@
-# We don't use the "converseg" program, but we need to ensure that we
-# only detect nauty >= 2.6 because we use the digraph6 format from
-# that version -- and converseg was added in nauty-2.6.
-#
-# We also don't use the "genposetg" program (added in nauty 2.8) yet.
-# We require it here to prepare Sage for the use of the major new features
-# added in 2.7 and 2.8 (https://pallini.di.uniroma1.it/changes24-28.txt).
+# We use the "genktreeg" program (added in nauty 2.8.8). This ensures that we
+# detect nauty >= 2.8.8 and so we can use the major new features added in nauty
+# since version 2.6. For instance, we use the digraph6 format from nauty >= 2.6
+# (https://pallini.di.uniroma1.it/changes24-28.txt).
 AC_DEFUN([SAGE_TEST_NAUTY_PROGS], [
-    m4_foreach([nautyprog], [directg, gentourng, geng, genbg, gentreeg, converseg, genposetg], [
+    m4_foreach([nautyprog], [directg, gentourng, geng, genbg, gentreeg, genktreeg, genposetg], [
       AC_PATH_PROG([$2]nautyprog, [[$1]nautyprog])
       AS_IF([test x$[$2]nautyprog = x], [sage_spkg_install_nauty=yes])
     ])
diff --git a/build/pkgs/nauty/spkg-install.in b/build/pkgs/nauty/spkg-install.in
index 5a2f957511d..a2557c3cd8e 100644
--- a/build/pkgs/nauty/spkg-install.in
+++ b/build/pkgs/nauty/spkg-install.in
@@ -13,11 +13,12 @@ sdh_make
 
 # No install target so we resort to manual copy
 PROGRAMS="
-addedgeg addptg amtog ancestorg assembleg biplabg catg complg converseg copyg countg cubhamg deledgeg
-delptg dimacs2g directg dreadnaut dretodot dretog edgetransg genbg genbgL geng gengL genposetg genquarticg genrang
-genspecialg gentourng gentreeg hamheuristic labelg linegraphg listg multig nbrhoodg
-newedgeg pickg planarg productg ranlabg shortg showg subdivideg twohamg underlyingg vcolg
-watercluster2 NRswitchg"
+addedgeg addptg amtog ancestorg assembleg biplabg catg complg converseg copyg
+countg countneg cubhamg deledgeg delptg dimacs2g directg dreadnaut dretodot
+dretog edgetransg genbg genbgL geng gengL genposetg genquarticg genrang
+genspecialg gentourng gentreeg genktreeg hamheuristic labelg linegraphg listg
+multig nbrhoodg newedgeg pickg planarg productg ranlabg ransubg shortg showg
+subdivideg twohamg underlyingg vcolg watercluster2 NRswitchg"
 sdh_install $PROGRAMS "$SAGE_LOCAL/bin"
 
 sdh_install nauty.h "$SAGE_LOCAL/include/nauty"
diff --git a/build/pkgs/nbconvert/spkg-install.in b/build/pkgs/nbconvert/spkg-install.in
deleted file mode 100644
index deba1bb42bb..00000000000
--- a/build/pkgs/nbconvert/spkg-install.in
+++ /dev/null
@@ -1 +0,0 @@
-cd src && sdh_pip_install .
diff --git a/build/pkgs/ncurses/distros/opensuse.txt b/build/pkgs/ncurses/distros/opensuse.txt
index b31c6886b17..f50c62d6ac4 100644
--- a/build/pkgs/ncurses/distros/opensuse.txt
+++ b/build/pkgs/ncurses/distros/opensuse.txt
@@ -1,2 +1,2 @@
-"pkgconfig(ncurses)"
-"pkgconfig(ncursesw)"
+pkgconfig(ncurses)
+pkgconfig(ncursesw)
diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini
index dd792a15a2e..6daf026581b 100644
--- a/build/pkgs/networkx/checksums.ini
+++ b/build/pkgs/networkx/checksums.ini
@@ -1,5 +1,5 @@
 tarball=networkx-VERSION.tar.gz
-sha1=d4b1d6117b7c54db61f6cbec8f0ccfb0f7d47293
-md5=1a9baa93b7fd4470c80e29a7a6d93ccf
-cksum=1675580484
+sha1=b12cf95ed8bc3fe568e3c8e023473a3767c43f8d
+md5=e81583dcb3d929b60660721912f3faed
+cksum=2601544693
 upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz
diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt
index 8c50098d8ae..e4604e3afd0 100644
--- a/build/pkgs/networkx/package-version.txt
+++ b/build/pkgs/networkx/package-version.txt
@@ -1 +1 @@
-3.1
+3.2.1
diff --git a/build/pkgs/ninja_build/spkg-configure.m4 b/build/pkgs/ninja_build/spkg-configure.m4
index fb6c4f00985..5b83d189801 100644
--- a/build/pkgs/ninja_build/spkg-configure.m4
+++ b/build/pkgs/ninja_build/spkg-configure.m4
@@ -3,8 +3,10 @@ SAGE_SPKG_CONFIGURE(
         dnl meson_python needs 1.8.2 or later
         AC_CACHE_CHECK([for ninja >= 1.8.2], [ac_cv_path_NINJA], [
         AC_PATH_PROGS_FEATURE_CHECK([NINJA], [ninja], [
+            dnl support both two- and three-component version schemes
+            dnl since samurai (a ninja alternative) uses two
             ninja_version=`$ac_path_NINJA --version 2>&1 \
-                | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'`
+                | $SED -n -e 's/\([[0-9]]*\(\.[[0-9]]*\)\{1,2\}\).*/\1/p'`
             AS_IF([test -n "$ninja_version"], [
                 AX_COMPARE_VERSION([$ninja_version], [ge], [1.8.2], [
                     ac_cv_path_NINJA="$ac_path_NINJA"
diff --git a/build/pkgs/normaliz/package-version.txt b/build/pkgs/normaliz/package-version.txt
index f870be23bad..2377fe517a0 100644
--- a/build/pkgs/normaliz/package-version.txt
+++ b/build/pkgs/normaliz/package-version.txt
@@ -1 +1 @@
-3.10.1
+3.10.1.p0
diff --git a/build/pkgs/normaliz/patches/flint3_d-torrance.patch b/build/pkgs/normaliz/patches/flint3_d-torrance.patch
new file mode 100644
index 00000000000..5a6e36c51c4
--- /dev/null
+++ b/build/pkgs/normaliz/patches/flint3_d-torrance.patch
@@ -0,0 +1,55 @@
+--- a/source/libnormaliz/vector_operations.h
++++ b/source/libnormaliz/vector_operations.h
+@@ -547,7 +547,10 @@
+ 
+     fmpq_poly_fit_length(flp, n);
+     for (size_t i = 0; i < poly_vector.size(); ++i) {
+-        fmpq_poly_set_coeff_mpq(flp, (slong)i, poly_vector[i].get_mpq_t());
++        fmpq_t fcurrent_coeff;
++        fmpq_init(fcurrent_coeff);
++        fmpq_set_mpq(fcurrent_coeff, poly_vector[i].get_mpq_t());
++        fmpq_poly_set_coeff_fmpq(flp, (slong)i, fcurrent_coeff);
+     }
+ }
+ 
+@@ -560,8 +563,11 @@
+     poly_vector.resize(length);
+     for (slong i = 0; i < length; i++) {
+         mpq_t current_coeff;
++        fmpq_t fcurrent_coeff;
+         mpq_init(current_coeff);
+-        fmpq_poly_get_coeff_mpq(current_coeff, flp, (slong)i);
++        fmpq_init(fcurrent_coeff);
++        fmpq_poly_get_coeff_fmpq(fcurrent_coeff, flp, (slong)i);
++        fmpq_get_mpq(current_coeff, fcurrent_coeff);
+         poly_vector[i] = mpq_class(current_coeff);
+     }
+ }
+--- a/source/libnormaliz/HilbertSeries.cpp
++++ b/source/libnormaliz/HilbertSeries.cpp
+@@ -72,7 +72,10 @@
+     slong n = (slong)nmzp.size();
+     fmpz_poly_fit_length(flp, n);
+     for (size_t i = 0; i < nmzp.size(); ++i) {
+-        fmpz_poly_set_coeff_mpz(flp, (slong)i, nmzp[i].get_mpz_t());
++        fmpz_t fc;
++        fmpz_init(fc);
++        fmpz_set_mpz(fc, nmzp[i].get_mpz_t());
++        fmpz_poly_set_coeff_fmpz(flp, (slong)i, fc);
+     }
+ }
+ 
+@@ -80,9 +83,12 @@
+     size_t n = (size_t)fmpz_poly_length(flp);
+     nmzp.resize(n);
+     mpz_t c;
++    fmpz_t fc;
+     mpz_init(c);
++    fmpz_init(fc);
+     for (size_t i = 0; i < nmzp.size(); ++i) {
+-        fmpz_poly_get_coeff_mpz(c, flp, i);
++        fmpz_poly_get_coeff_fmpz(fc, flp, i);
++        fmpz_get_mpz(c, fc);
+         nmzp[i] = mpz_class(c);
+     }
+     mpz_clear(c);
diff --git a/build/pkgs/normaliz/spkg-install.in b/build/pkgs/normaliz/spkg-install.in
index 0d329656614..683a3200e5b 100644
--- a/build/pkgs/normaliz/spkg-install.in
+++ b/build/pkgs/normaliz/spkg-install.in
@@ -8,6 +8,8 @@ cd src
 # that an error will be signalled if FLINT or E-ANTIC cannot be found, rather
 # than building normaliz without it.
 
+export ac_cv_lib_flint_fmpz_poly_set_coeff_mpz=yes
+
 sdh_configure --with-flint --with-e-antic --with-nauty
 sdh_make
 sdh_make_install
diff --git a/build/pkgs/ntl/spkg-install.in b/build/pkgs/ntl/spkg-install.in
index a26c3b5026e..d82ddf370e5 100644
--- a/build/pkgs/ntl/spkg-install.in
+++ b/build/pkgs/ntl/spkg-install.in
@@ -112,11 +112,6 @@ ntl_build()
 
 ntl_install()
 {
-    echo
-    echo "Removing old NTL files."
-    rm -rf "$SAGE_LOCAL"/lib/libntl*
-    rm -rf "$SAGE_LOCAL"/include/NTL
-
     echo
     echo "Installing NTL."
 
diff --git a/build/pkgs/numpy/spkg-legacy-uninstall b/build/pkgs/numpy/spkg-legacy-uninstall
deleted file mode 100755
index 9cb6a63a5a3..00000000000
--- a/build/pkgs/numpy/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/numpy
diff --git a/build/pkgs/openblas/distros/cygwin.txt b/build/pkgs/openblas/distros/cygwin.txt
index d2146131e38..055fa2733d1 100644
--- a/build/pkgs/openblas/distros/cygwin.txt
+++ b/build/pkgs/openblas/distros/cygwin.txt
@@ -1 +1,2 @@
-liblapack-devel libopenblas
+liblapack-devel
+libopenblas
diff --git a/build/pkgs/openblas/spkg-install.in b/build/pkgs/openblas/spkg-install.in
index 2f8aea512bc..dbfcb6c1a75 100644
--- a/build/pkgs/openblas/spkg-install.in
+++ b/build/pkgs/openblas/spkg-install.in
@@ -59,9 +59,6 @@ if ! (sdh_make libs $OPENBLAS_CONFIGURE && sdh_make netlib $OPENBLAS_CONFIGURE &
     fi
 fi
 
-# See https://github.com/sagemath/sage/issues/30335
-rm -f "$SAGE_LOCAL/lib/pkgconfig/cblas.pc" "$SAGE_LOCAL/lib/pkgconfig/blas.pc" "$SAGE_LOCAL/lib/pkgconfig/lapack.pc"
-
 sdh_make_install PREFIX="$SAGE_LOCAL" NO_STATIC=1 $OPENBLAS_CONFIGURE
 cd ..
 ./write_pc_file.py
diff --git a/build/pkgs/openblas/spkg-preinst.in b/build/pkgs/openblas/spkg-preinst.in
new file mode 100644
index 00000000000..ced306d2d4e
--- /dev/null
+++ b/build/pkgs/openblas/spkg-preinst.in
@@ -0,0 +1,2 @@
+# See https://github.com/sagemath/sage/issues/30335
+rm -f "$SAGE_LOCAL/lib/pkgconfig/cblas.pc" "$SAGE_LOCAL/lib/pkgconfig/blas.pc" "$SAGE_LOCAL/lib/pkgconfig/lapack.pc"
diff --git a/build/pkgs/openssl/distros/fedora.txt b/build/pkgs/openssl/distros/fedora.txt
index dda63a9f276..a9579db4fca 100644
--- a/build/pkgs/openssl/distros/fedora.txt
+++ b/build/pkgs/openssl/distros/fedora.txt
@@ -1 +1,2 @@
-openssl openssl-devel
+openssl
+openssl-devel
diff --git a/build/pkgs/openssl/distros/slackware.txt b/build/pkgs/openssl/distros/slackware.txt
index 6ed59c09128..14c745a6bc8 100644
--- a/build/pkgs/openssl/distros/slackware.txt
+++ b/build/pkgs/openssl/distros/slackware.txt
@@ -1 +1,2 @@
-openssl openssl-solibs
+openssl
+openssl-solibs
diff --git a/build/pkgs/pari/distros/conda.txt b/build/pkgs/pari/distros/conda.txt
index 88ec4bef346..018d16817d3 100644
--- a/build/pkgs/pari/distros/conda.txt
+++ b/build/pkgs/pari/distros/conda.txt
@@ -1,3 +1,6 @@
 pari=*=*_pthread
 # We add these data packages because they are checked by spkg-configure.m4
-pari-elldata pari-galdata pari-galpol pari-seadata
+pari-elldata
+pari-galdata
+pari-galpol
+pari-seadata
diff --git a/build/pkgs/pari/distros/fedora.txt b/build/pkgs/pari/distros/fedora.txt
index 95ba141d56c..d315900766c 100644
--- a/build/pkgs/pari/distros/fedora.txt
+++ b/build/pkgs/pari/distros/fedora.txt
@@ -2,7 +2,8 @@ pari-devel
 # spkg-configure checks for gp, gphelp.  Access to the documentation is crucial
 # for the cypari2 build.  #29342: By default configuration in /etc/dnf/dnf.conf,
 # installation of documentation may be suppressed; we override this.
-pari-gp --setopt=tsflags=
+pari-gp
+--setopt=tsflags=
 # spkg-configure checks for data
 pari-galdata
 pari-galpol
diff --git a/build/pkgs/pari/distros/gentoo.txt b/build/pkgs/pari/distros/gentoo.txt
index 449975ac8e1..cd13d54e8cc 100644
--- a/build/pkgs/pari/distros/gentoo.txt
+++ b/build/pkgs/pari/distros/gentoo.txt
@@ -1 +1,2 @@
-sci-mathematics/pari sci-mathematics/pari-data
+sci-mathematics/pari
+sci-mathematics/pari-data
diff --git a/build/pkgs/pari/distros/macports.txt b/build/pkgs/pari/distros/macports.txt
index 67abbc9dec6..8960baaba3e 100644
--- a/build/pkgs/pari/distros/macports.txt
+++ b/build/pkgs/pari/distros/macports.txt
@@ -1 +1,2 @@
-pari
+# incomplete, unusable for sage, causes cysignals build failure
+#pari
diff --git a/build/pkgs/pathspec/checksums.ini b/build/pkgs/pathspec/checksums.ini
index a98c270d6e1..a562884cca1 100644
--- a/build/pkgs/pathspec/checksums.ini
+++ b/build/pkgs/pathspec/checksums.ini
@@ -1,5 +1,5 @@
-tarball=pathspec-VERSION.tar.gz
-sha1=418ae4112af18af995c0f20a22e5a903b8ce50ae
-md5=28c87c3581b10152c4581d10fe33f765
-cksum=2161527634
-upstream_url=https://pypi.io/packages/source/p/pathspec/pathspec-VERSION.tar.gz
+tarball=pathspec-VERSION-py3-none-any.whl
+sha1=e31b7b2b1a59ab192eb2e92ac283211a11039769
+md5=53caa061bbda861c5b4766f41b084ec8
+cksum=1745881358
+upstream_url=https://pypi.io/packages/py3/p/pathspec/pathspec-VERSION-py3-none-any.whl
diff --git a/build/pkgs/pathspec/dependencies b/build/pkgs/pathspec/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/pathspec/dependencies
+++ b/build/pkgs/pathspec/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/pathspec/package-version.txt b/build/pkgs/pathspec/package-version.txt
index 5eef0f10e8c..34a83616bb5 100644
--- a/build/pkgs/pathspec/package-version.txt
+++ b/build/pkgs/pathspec/package-version.txt
@@ -1 +1 @@
-0.10.2
+0.12.1
diff --git a/build/pkgs/pathspec/spkg-install.in b/build/pkgs/pathspec/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/pathspec/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt b/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt
index ba80f6c445f..60563e056ca 100644
--- a/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt
+++ b/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt
@@ -1 +1 @@
-"perl(Term::ReadLine::Gnu)"
+perl(Term::ReadLine::Gnu)
diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini
index 52c2edb31f2..0558c5caa45 100644
--- a/build/pkgs/pip/checksums.ini
+++ b/build/pkgs/pip/checksums.ini
@@ -1,5 +1,5 @@
-tarball=pip-VERSION.tar.gz
-sha1=d1400a4eb662e4741ac68957f47bc97d600743f8
-md5=f0c9fba61e9d9badcc9921062e993d84
-cksum=309527365
-upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz
+tarball=pip-VERSION-py3-none-any.whl
+sha1=4b2baddc0673f73017e531648a9ee27e47925e7a
+md5=5d2d058044a3ae2800d18e358ddc72ca
+cksum=1470281176
+upstream_url=https://pypi.io/packages/py3/p/pip/pip-VERSION-py3-none-any.whl
diff --git a/build/pkgs/pip/dependencies b/build/pkgs/pip/dependencies
index dac3579f7e3..d45a397db36 100644
--- a/build/pkgs/pip/dependencies
+++ b/build/pkgs/pip/dependencies
@@ -1,4 +1,4 @@
- setuptools wheel | $(PYTHON)
+ | $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/pip/spkg-install.in b/build/pkgs/pip/spkg-install.in
index 651f5c1dc1e..567543149db 100644
--- a/build/pkgs/pip/spkg-install.in
+++ b/build/pkgs/pip/spkg-install.in
@@ -1,11 +1,6 @@
-cd src
+# pip can install its own wheel! But first we need to ensure that the
+# wheel file is on the PYTHONPATH
+export PYTHONPATH=$(pwd)/$(cd dist && for w in *.whl; do cp $w ..; echo $w; break; done)
 
-sdh_setup_bdist_wheel
-
-# pip can install its own wheel! But first we need to ensure that the pip
-# source directory in on the PYTHONPATH
-export PYTHONPATH="$(pwd)/src"
-
-# --ignore-installed makes sure that pip does not mistake
-# its own source tree as an existing installation
-sdh_store_and_pip_install_wheel --ignore-installed .
+# This line is the same as what sage-spkg uses to install the wheel.
+sdh_store_and_pip_install_wheel .
diff --git a/build/pkgs/pip/spkg-pipinst.in b/build/pkgs/pip/spkg-pipinst.in
index 3e3f6bbc94e..61007a3b8c8 100644
--- a/build/pkgs/pip/spkg-pipinst.in
+++ b/build/pkgs/pip/spkg-pipinst.in
@@ -1,5 +1,3 @@
-cd src
-
 # pip can install its own wheel! But first we need to ensure that the pip
-# source directory in on the PYTHONPATH
-export PYTHONPATH="$(pwd)/src"
+# wheelfile is on the PYTHONPATH
+export PYTHONPATH=$(pwd)/$(echo *.whl)
diff --git a/build/pkgs/planarity/checksums.ini b/build/pkgs/planarity/checksums.ini
index 6227637695a..309daa93278 100644
--- a/build/pkgs/planarity/checksums.ini
+++ b/build/pkgs/planarity/checksums.ini
@@ -2,4 +2,4 @@ tarball=planarity-VERSION.tar.gz
 sha1=8407bccf33c07bf0dae22d79b5e6ac7d89c62ea3
 md5=200116e6a67544c8e94f9de7c3ba1b1a
 cksum=4207261512
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/planarity-3.0.1.0.tar.gz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/planarity-VERSION.tar.gz
diff --git a/build/pkgs/planarity/distros/fedora.txt b/build/pkgs/planarity/distros/fedora.txt
index b107ebbf96f..e3d211ca5d1 100644
--- a/build/pkgs/planarity/distros/fedora.txt
+++ b/build/pkgs/planarity/distros/fedora.txt
@@ -1 +1,2 @@
-planarity planarity-devel
+planarity
+planarity-devel
diff --git a/build/pkgs/plantri/checksums.ini b/build/pkgs/plantri/checksums.ini
index 05dc13d01e7..f999a981a69 100644
--- a/build/pkgs/plantri/checksums.ini
+++ b/build/pkgs/plantri/checksums.ini
@@ -1,5 +1,5 @@
-tarball=plantri53.tar.gz
+tarball=plantri${VERSION_MAJOR}${VERSION_MINOR}.tar.gz
 sha1=a04aec2fa90c43f1c9bef59d041a54d8fa5bf562
 md5=ea765b3508dd56384f94ad1f032e2dd4
 cksum=3200215885
-upstream_url=https://users.cecs.anu.edu.au/~bdm/plantri/plantri53.tar.gz
+upstream_url=https://users.cecs.anu.edu.au/~bdm/plantri/plantri${VERSION_MAJOR}${VERSION_MINOR}.tar.gz
diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini
index 99113c48626..3757f701faf 100644
--- a/build/pkgs/platformdirs/checksums.ini
+++ b/build/pkgs/platformdirs/checksums.ini
@@ -1,5 +1,5 @@
-tarball=platformdirs-VERSION.tar.gz
-sha1=82e215dc9c45eedf9168584fede36d795714f677
-md5=a6ba3a442347fac346982acb597c8bf8
-cksum=2465457023
-upstream_url=https://pypi.io/packages/source/p/platformdirs/platformdirs-VERSION.tar.gz
+tarball=platformdirs-VERSION-py3-none-any.whl
+sha1=cafa761738da959f2df0a8a92da4c72fd8eaf93e
+md5=487007776ff343efc509b68d08cd7fd7
+cksum=162426958
+upstream_url=https://pypi.io/packages/py3/p/platformdirs/platformdirs-VERSION-py3-none-any.whl
diff --git a/build/pkgs/platformdirs/dependencies b/build/pkgs/platformdirs/dependencies
index 952ebfb66cd..644ad35f773 100644
--- a/build/pkgs/platformdirs/dependencies
+++ b/build/pkgs/platformdirs/dependencies
@@ -1,4 +1,4 @@
- setuptools_scm | $(PYTHON_TOOLCHAIN) hatchling hatch_vcs $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt
index afad818663d..ee74734aa22 100644
--- a/build/pkgs/platformdirs/package-version.txt
+++ b/build/pkgs/platformdirs/package-version.txt
@@ -1 +1 @@
-3.11.0
+4.1.0
diff --git a/build/pkgs/platformdirs/spkg-install.in b/build/pkgs/platformdirs/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/platformdirs/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/pluggy/dependencies b/build/pkgs/pluggy/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/pluggy/dependencies
+++ b/build/pkgs/pluggy/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/polylib/distros/opensuse.txt b/build/pkgs/polylib/distros/opensuse.txt
index f6432c31dfe..8b7f1e54de7 100644
--- a/build/pkgs/polylib/distros/opensuse.txt
+++ b/build/pkgs/polylib/distros/opensuse.txt
@@ -1,2 +1,2 @@
 polylib
-"pkgconfig(polylibgmp)"
+pkgconfig(polylibgmp)
diff --git a/build/pkgs/polytopes_db_4d/checksums.ini b/build/pkgs/polytopes_db_4d/checksums.ini
index edfee6b7841..d87e456c4a8 100644
--- a/build/pkgs/polytopes_db_4d/checksums.ini
+++ b/build/pkgs/polytopes_db_4d/checksums.ini
@@ -2,4 +2,4 @@ tarball=polytopes_db_4d-VERSION.spkg
 sha1=c9779821e365df2d7f9bc684f9e2ec0e95fb8650
 md5=fe775a26fd7b2afc187e9bfabfb1b86a
 cksum=3415837678
-upstream_url=http://ftp.sparcs.org/sage/spkg/huge/polytopes_db_4d-1.0.spkg
+upstream_url=http://ftp.sparcs.org/sage/spkg/huge/polytopes_db_4d-VERSION.spkg
diff --git a/build/pkgs/ptyprocess/distros/conda.txt b/build/pkgs/ptyprocess/distros/conda.txt
index 4a518435c60..964d3f79453 100644
--- a/build/pkgs/ptyprocess/distros/conda.txt
+++ b/build/pkgs/ptyprocess/distros/conda.txt
@@ -1,3 +1,3 @@
 # The version should be fixed to 0.5.1 (see https://github.com/sagemath/sage/issues/32147), but this is not available on conda-forge
-# thus don't install ptyprocess with conda, but let pip handle it 
+# thus don't install ptyprocess with conda, but let pip handle it
 # ptyprocess
diff --git a/build/pkgs/pycygwin/SPKG.rst b/build/pkgs/pycygwin/SPKG.rst
deleted file mode 100644
index fdf67a08dc1..00000000000
--- a/build/pkgs/pycygwin/SPKG.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-pycygwin: Python bindings for Cygwin's C API
-============================================
-
-Description
------------
-
-Python bindings for Cygwin's C API. Provides some utilities to help with
-the Cygwin port. Naturally, this package should only be installed on
-Cygwin--for other platforms its installation is a no-op.
-
-Website
--------
-
-https://github.com/embray/PyCygwin
diff --git a/build/pkgs/pycygwin/checksums.ini b/build/pkgs/pycygwin/checksums.ini
deleted file mode 100644
index 7ce896982a4..00000000000
--- a/build/pkgs/pycygwin/checksums.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-tarball=PyCygwin-VERSION.tar.gz
-sha1=4021dbf77c353051761d277f5490b38a701ba51b
-md5=12c30c847b144282178bc546d4e2cbcd
-cksum=2254892419
diff --git a/build/pkgs/pycygwin/dependencies b/build/pkgs/pycygwin/dependencies
deleted file mode 100644
index 1db13c07e43..00000000000
--- a/build/pkgs/pycygwin/dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
- cython | $(PYTHON_TOOLCHAIN) $(PYTHON)
-
-----------
-All lines of this file are ignored except the first.
diff --git a/build/pkgs/pycygwin/distros/repology.txt b/build/pkgs/pycygwin/distros/repology.txt
deleted file mode 100644
index eeeab661dc6..00000000000
--- a/build/pkgs/pycygwin/distros/repology.txt
+++ /dev/null
@@ -1 +0,0 @@
-python:pycygwin
diff --git a/build/pkgs/pycygwin/install-requires.txt b/build/pkgs/pycygwin/install-requires.txt
deleted file mode 100644
index 3bec679b60b..00000000000
--- a/build/pkgs/pycygwin/install-requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-pycygwin >=0.1
diff --git a/build/pkgs/pycygwin/package-version.txt b/build/pkgs/pycygwin/package-version.txt
deleted file mode 100644
index 49d59571fbf..00000000000
--- a/build/pkgs/pycygwin/package-version.txt
+++ /dev/null
@@ -1 +0,0 @@
-0.1
diff --git a/build/pkgs/pycygwin/spkg-install.in b/build/pkgs/pycygwin/spkg-install.in
deleted file mode 100644
index eeb8dcdf62c..00000000000
--- a/build/pkgs/pycygwin/spkg-install.in
+++ /dev/null
@@ -1,3 +0,0 @@
-if [ "$UNAME" = "CYGWIN" ]; then
-    cd src && sdh_pip_install .
-fi
diff --git a/build/pkgs/pycygwin/type b/build/pkgs/pycygwin/type
deleted file mode 100644
index a6a7b9cd726..00000000000
--- a/build/pkgs/pycygwin/type
+++ /dev/null
@@ -1 +0,0 @@
-standard
diff --git a/build/pkgs/pynormaliz/package-version.txt b/build/pkgs/pynormaliz/package-version.txt
index fc249e9a747..fae0ceb2953 100644
--- a/build/pkgs/pynormaliz/package-version.txt
+++ b/build/pkgs/pynormaliz/package-version.txt
@@ -1 +1 @@
-2.18
+2.18.p0
diff --git a/build/pkgs/python3/distros/macports.txt b/build/pkgs/python3/distros/macports.txt
index 6a2d05c5edb..92826c681b4 100644
--- a/build/pkgs/python3/distros/macports.txt
+++ b/build/pkgs/python3/distros/macports.txt
@@ -1 +1 @@
-python39
+python310
diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in
index 0c08814a242..4cd421bf4b8 100644
--- a/build/pkgs/python3/spkg-build.in
+++ b/build/pkgs/python3/spkg-build.in
@@ -62,17 +62,6 @@ fi
 # common in Cython-generated code.
 export EXTRA_CFLAGS="`testcflags.sh -Wno-unused $OLD_CFLAGS`"
 
-# Remove old symbolic link: it is not needed and its presence can
-# interfere with the Python build.
-rm -f "$SAGE_LOCAL/lib/python"
-
-# Remove old libraries. We really need to do this before building Python
-# since Python tries to import some modules (e.g. ctypes) at build-time.
-# We need to make sure that the old installed libraries in local/lib are
-# not used for that. See https://github.com/sagemath/sage/issues/24605
-rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"*
-
-
     # Note: --without-ensurepip ensures that setuptools+pip are *not* installed
     # automatically when installing python3. They will be installed instead by
     # the separate setuptools and pip packages; see
diff --git a/build/pkgs/python_igraph/checksums.ini b/build/pkgs/python_igraph/checksums.ini
index 7247680193c..e3ba512eb8f 100644
--- a/build/pkgs/python_igraph/checksums.ini
+++ b/build/pkgs/python_igraph/checksums.ini
@@ -1,5 +1,5 @@
 tarball=python-igraph-VERSION.tar.gz
-sha1=807a95ad4080d8eb500e7797325d6f11a5c46892
-md5=2ac3561dda7e7321789041261a29aba4
-cksum=754615899
+sha1=ef7ee85cb1bc83109c744d5dd6bbe5e37598ce3f
+md5=788bd70bd286651774f15b20af956081
+cksum=1447848766
 upstream_url=https://pypi.io/packages/source/i/igraph/igraph-VERSION.tar.gz
diff --git a/build/pkgs/python_igraph/package-version.txt b/build/pkgs/python_igraph/package-version.txt
index 9b40aa6c214..1a96df19c09 100644
--- a/build/pkgs/python_igraph/package-version.txt
+++ b/build/pkgs/python_igraph/package-version.txt
@@ -1 +1 @@
-0.10.4
+0.11.3
diff --git a/build/pkgs/qhull/distros/macports.txt b/build/pkgs/qhull/distros/macports.txt
index 95d316779cf..703b7aadad9 100644
--- a/build/pkgs/qhull/distros/macports.txt
+++ b/build/pkgs/qhull/distros/macports.txt
@@ -1 +1,2 @@
-qhull
+# broken as of 2022-05-01
+#qhull
diff --git a/build/pkgs/qhull/spkg-install.in b/build/pkgs/qhull/spkg-install.in
index 73810f54625..c3df534ad86 100644
--- a/build/pkgs/qhull/spkg-install.in
+++ b/build/pkgs/qhull/spkg-install.in
@@ -5,9 +5,4 @@ sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \
 
 sdh_make
 
-# clean old install
-rm -rf "${SAGE_LOCAL}"/include/libqhull
-rm -rf "${SAGE_LOCAL}"/include/qhull
-rm -rf "${SAGE_LOCAL}"/lib/libqhull*
-
 sdh_make_install
diff --git a/build/pkgs/r/distros/conda.txt b/build/pkgs/r/distros/conda.txt
index 42e26b8d8ed..4c235733c5f 100644
--- a/build/pkgs/r/distros/conda.txt
+++ b/build/pkgs/r/distros/conda.txt
@@ -1 +1,2 @@
-r r-essentials
+r
+r-essentials
diff --git a/build/pkgs/r/distros/fedora.txt b/build/pkgs/r/distros/fedora.txt
index a5148a636ee..088b10653bb 100644
--- a/build/pkgs/r/distros/fedora.txt
+++ b/build/pkgs/r/distros/fedora.txt
@@ -1 +1,2 @@
-R R-devel
+R
+R-devel
diff --git a/build/pkgs/r/distros/macports.txt b/build/pkgs/r/distros/macports.txt
index 331bae08fb7..5798a47ae13 100644
--- a/build/pkgs/r/distros/macports.txt
+++ b/build/pkgs/r/distros/macports.txt
@@ -1 +1,2 @@
-R
+# Broken as of 2022-05-01, and too many dependencies (X11)
+# R
diff --git a/build/pkgs/readline/distros/opensuse.txt b/build/pkgs/readline/distros/opensuse.txt
index fffe7fdb306..66fd20246e7 100644
--- a/build/pkgs/readline/distros/opensuse.txt
+++ b/build/pkgs/readline/distros/opensuse.txt
@@ -1,3 +1,3 @@
 readline-devel
 # not available on Leap 15.2:
-"pkgconfig(readline)"
+pkgconfig(readline)
diff --git a/build/pkgs/readline/spkg-install.in b/build/pkgs/readline/spkg-install.in
index 69b4e686d75..a9322e4bed8 100644
--- a/build/pkgs/readline/spkg-install.in
+++ b/build/pkgs/readline/spkg-install.in
@@ -4,16 +4,6 @@ sdh_configure --with-curses --enable-shared --disable-static
 echo "Now building static and shared readline libraries..."
 sdh_make
 
-echo "Build succeedeed.  Deleting old readline headers and libs"
-echo "before installing the new ones..."
-# (Note; Actually also readline does this by itself, but doing it
-# here, too, doesn't hurt either.)
-rm -rf "$SAGE_LOCAL"/include/readline/ "$SAGE_LOCAL"/lib/libreadline.*
-if [[ $? -ne 0 ]]; then
-    echo >&2 "Error removing old version of readline."
-    exit 1
-fi
-
 echo "Now installing the new readline headers and libraries..."
 sdh_make_install
 
diff --git a/build/pkgs/referencing/dependencies b/build/pkgs/referencing/dependencies
index 12067bf5c3e..39c6d3527b3 100644
--- a/build/pkgs/referencing/dependencies
+++ b/build/pkgs/referencing/dependencies
@@ -1,4 +1,4 @@
-attrs | $(PYTHON_TOOLCHAIN) $(PYTHON)
+attrs pyrsistent | $(PYTHON_TOOLCHAIN) $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt
index b136f4a2aff..3a9ab385d78 100644
--- a/build/pkgs/sage_conf/install-requires.txt
+++ b/build/pkgs/sage_conf/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sage-conf ~= 10.3b2
+sage-conf ~= 10.3b4
diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt
index a9f3dad3be9..0c2be2f77db 100644
--- a/build/pkgs/sage_docbuild/install-requires.txt
+++ b/build/pkgs/sage_docbuild/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sage-docbuild ~= 10.3b2
+sage-docbuild ~= 10.3b4
diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt
index 398467294a7..9e7d5a7398a 100644
--- a/build/pkgs/sage_setup/install-requires.txt
+++ b/build/pkgs/sage_setup/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sage-setup ~= 10.3b2
+sage-setup ~= 10.3b4
diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt
index 86ccd89829a..d7e9353d351 100644
--- a/build/pkgs/sage_sws2rst/install-requires.txt
+++ b/build/pkgs/sage_sws2rst/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sage-sws2rst ~= 10.3b2
+sage-sws2rst ~= 10.3b4
diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies
index fa6e98df7db..b1ebfd0825e 100644
--- a/build/pkgs/sagelib/dependencies
+++ b/build/pkgs/sagelib/dependencies
@@ -1,4 +1,4 @@
-FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran
+FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt
index 4760acdf3a5..4fddbc66d5f 100644
--- a/build/pkgs/sagelib/install-requires.txt
+++ b/build/pkgs/sagelib/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-standard ~= 10.3b2
+sagemath-standard ~= 10.3b4
diff --git a/build/pkgs/sagelib/spkg-install.in b/build/pkgs/sagelib/spkg-install.in
index 63cfd64fa59..39ffc25f270 100644
--- a/build/pkgs/sagelib/spkg-install.in
+++ b/build/pkgs/sagelib/spkg-install.in
@@ -51,6 +51,8 @@ if [ "$SAGE_EDITABLE" = yes ]; then
     # and renamed the distribution to "sagemath-standard").  There is no clean way to uninstall
     # them, so we just use rm.
     (cd "$SITEPACKAGESDIR" && rm -rf sage sage-[1-9]*.egg-info sage-[1-9]*.dist-info)
+    # Until https://github.com/sagemath/sage/issues/34209 switches us to PEP 660 editable wheels
+    export SETUPTOOLS_ENABLE_FEATURES=legacy-editable
     time sdh_pip_editable_install .
 
     if [ "$SAGE_WHEELS" = yes ]; then
diff --git a/build/pkgs/sagemath_bliss/install-requires.txt b/build/pkgs/sagemath_bliss/install-requires.txt
index 78450942be8..24bd560a768 100644
--- a/build/pkgs/sagemath_bliss/install-requires.txt
+++ b/build/pkgs/sagemath_bliss/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-bliss ~= 10.3b2
+sagemath-bliss ~= 10.3b4
diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt
index dd5175f57fe..73fde138fd8 100644
--- a/build/pkgs/sagemath_categories/install-requires.txt
+++ b/build/pkgs/sagemath_categories/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-categories ~= 10.3b2
+sagemath-categories ~= 10.3b4
diff --git a/build/pkgs/sagemath_coxeter3/install-requires.txt b/build/pkgs/sagemath_coxeter3/install-requires.txt
index 05f8575b6a0..0a7cf53f9cb 100644
--- a/build/pkgs/sagemath_coxeter3/install-requires.txt
+++ b/build/pkgs/sagemath_coxeter3/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-coxeter3 ~= 10.3b2
+sagemath-coxeter3 ~= 10.3b4
diff --git a/build/pkgs/sagemath_doc_pdf/dependencies b/build/pkgs/sagemath_doc_pdf/dependencies
index 21eb5295b36..7e3e7f136d2 100644
--- a/build/pkgs/sagemath_doc_pdf/dependencies
+++ b/build/pkgs/sagemath_doc_pdf/dependencies
@@ -1 +1 @@
-sagemath_doc_html texlive
+sagemath_doc_html texlive texlive_luatex free_fonts xindy
diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt
index 1eb9fe7e808..f2de063f119 100644
--- a/build/pkgs/sagemath_environment/install-requires.txt
+++ b/build/pkgs/sagemath_environment/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-environment ~= 10.3b2
+sagemath-environment ~= 10.3b4
diff --git a/build/pkgs/sagemath_mcqd/install-requires.txt b/build/pkgs/sagemath_mcqd/install-requires.txt
index cc46fb68674..f67c3865111 100644
--- a/build/pkgs/sagemath_mcqd/install-requires.txt
+++ b/build/pkgs/sagemath_mcqd/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-mcqd ~= 10.3b2
+sagemath-mcqd ~= 10.3b4
diff --git a/build/pkgs/sagemath_meataxe/install-requires.txt b/build/pkgs/sagemath_meataxe/install-requires.txt
index 76fc2369cb1..d574ec0df6d 100644
--- a/build/pkgs/sagemath_meataxe/install-requires.txt
+++ b/build/pkgs/sagemath_meataxe/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-meataxe ~= 10.3b2
+sagemath-meataxe ~= 10.3b4
diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt
index b1d01c37823..73ce709a5f5 100644
--- a/build/pkgs/sagemath_objects/install-requires.txt
+++ b/build/pkgs/sagemath_objects/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-objects ~= 10.3b2
+sagemath-objects ~= 10.3b4
diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt
index ce22d36e261..c9acb9a7e0b 100644
--- a/build/pkgs/sagemath_repl/install-requires.txt
+++ b/build/pkgs/sagemath_repl/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-repl ~= 10.3b2
+sagemath-repl ~= 10.3b4
diff --git a/build/pkgs/sagemath_sirocco/install-requires.txt b/build/pkgs/sagemath_sirocco/install-requires.txt
index 40949dd944f..8fd980605d7 100644
--- a/build/pkgs/sagemath_sirocco/install-requires.txt
+++ b/build/pkgs/sagemath_sirocco/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-sirocco ~= 10.3b2
+sagemath-sirocco ~= 10.3b4
diff --git a/build/pkgs/sagemath_tdlib/install-requires.txt b/build/pkgs/sagemath_tdlib/install-requires.txt
index cab1a072642..377b21f5a09 100644
--- a/build/pkgs/sagemath_tdlib/install-requires.txt
+++ b/build/pkgs/sagemath_tdlib/install-requires.txt
@@ -1,2 +1,2 @@
 # This file is updated on every release by the sage-update-version script
-sagemath-tdlib ~= 10.3b2
+sagemath-tdlib ~= 10.3b4
diff --git a/build/pkgs/sagetex/spkg-install.in b/build/pkgs/sagetex/spkg-install.in
index 55f0e9c76e5..058b1344dc2 100644
--- a/build/pkgs/sagetex/spkg-install.in
+++ b/build/pkgs/sagetex/spkg-install.in
@@ -1,6 +1,3 @@
 cd src
 
 sdh_pip_install .
-
-echo "Removing old SageTex version(s)"
-rm -rf $SAGE_LOCAL/share/texmf/tex/generic/sagetex
diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini
index 150e3c25335..1a88d2a7464 100644
--- a/build/pkgs/setuptools/checksums.ini
+++ b/build/pkgs/setuptools/checksums.ini
@@ -1,5 +1,5 @@
-tarball=setuptools-VERSION.tar.gz
-sha1=b0c9b16863c57d70adc22651906eea7eaee09803
-md5=d967ca2ba7f46db887daee2d5c9bd6a2
-cksum=2346145273
-upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz
+tarball=setuptools-VERSION-py3-none-any.whl
+sha1=4227225bb193e3a45542f45966caf777d4c913e8
+md5=f096ed836f4036a11aa277fa16dc09ff
+cksum=263664173
+upstream_url=https://pypi.io/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl
diff --git a/build/pkgs/setuptools/dependencies b/build/pkgs/setuptools/dependencies
index d45a397db36..644ad35f773 100644
--- a/build/pkgs/setuptools/dependencies
+++ b/build/pkgs/setuptools/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt
index fc312b9c8c1..2c021f541a8 100644
--- a/build/pkgs/setuptools/package-version.txt
+++ b/build/pkgs/setuptools/package-version.txt
@@ -1 +1 @@
-68.2.2
+69.0.2
diff --git a/build/pkgs/setuptools/spkg-install.in b/build/pkgs/setuptools/spkg-install.in
deleted file mode 100644
index 09a8b8f65ce..00000000000
--- a/build/pkgs/setuptools/spkg-install.in
+++ /dev/null
@@ -1,5 +0,0 @@
-cd src
-# Use --single-version-externally-managed to prevent setuptools from installing itself with easy_install
-python3 setup.py --no-user-cfg install \
-            --single-version-externally-managed --root="$SAGE_DESTDIR" || \
-        sdh_die "Error building / installing setuptools for Python ${vers}"
diff --git a/build/pkgs/setuptools_wheel/SPKG.rst b/build/pkgs/setuptools_wheel/SPKG.rst
deleted file mode 100644
index c78602a296a..00000000000
--- a/build/pkgs/setuptools_wheel/SPKG.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-setuptools_wheel: Build the setuptools package as a wheel
-=========================================================
-
-After installing setuptools and wheel, we build a wheel of setuptools
-to complete the set of wheels stored in our wheelhouse.
diff --git a/build/pkgs/setuptools_wheel/checksums.ini b/build/pkgs/setuptools_wheel/checksums.ini
deleted file mode 120000
index 4f64d3ce107..00000000000
--- a/build/pkgs/setuptools_wheel/checksums.ini
+++ /dev/null
@@ -1 +0,0 @@
-../setuptools/checksums.ini
\ No newline at end of file
diff --git a/build/pkgs/setuptools_wheel/dependencies b/build/pkgs/setuptools_wheel/dependencies
deleted file mode 100644
index dac3579f7e3..00000000000
--- a/build/pkgs/setuptools_wheel/dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
- setuptools wheel | $(PYTHON)
-
-----------
-All lines of this file are ignored except the first.
diff --git a/build/pkgs/setuptools_wheel/distros b/build/pkgs/setuptools_wheel/distros
deleted file mode 120000
index b22be5c01a6..00000000000
--- a/build/pkgs/setuptools_wheel/distros
+++ /dev/null
@@ -1 +0,0 @@
-../setuptools/distros
\ No newline at end of file
diff --git a/build/pkgs/setuptools_wheel/install-requires.txt b/build/pkgs/setuptools_wheel/install-requires.txt
deleted file mode 120000
index c5cfb4ff0a9..00000000000
--- a/build/pkgs/setuptools_wheel/install-requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-../setuptools/install-requires.txt
\ No newline at end of file
diff --git a/build/pkgs/setuptools_wheel/package-version.txt b/build/pkgs/setuptools_wheel/package-version.txt
deleted file mode 120000
index 5268dbec8f6..00000000000
--- a/build/pkgs/setuptools_wheel/package-version.txt
+++ /dev/null
@@ -1 +0,0 @@
-../setuptools/package-version.txt
\ No newline at end of file
diff --git a/build/pkgs/setuptools_wheel/spkg-configure.m4 b/build/pkgs/setuptools_wheel/spkg-configure.m4
deleted file mode 100644
index db71392a8b0..00000000000
--- a/build/pkgs/setuptools_wheel/spkg-configure.m4
+++ /dev/null
@@ -1 +0,0 @@
-SAGE_SPKG_CONFIGURE([setuptools_wheel], [SAGE_PYTHON_PACKAGE_CHECK([setuptools_wheel])])
diff --git a/build/pkgs/setuptools_wheel/spkg-install.in b/build/pkgs/setuptools_wheel/spkg-install.in
deleted file mode 100644
index 7fdd493b5aa..00000000000
--- a/build/pkgs/setuptools_wheel/spkg-install.in
+++ /dev/null
@@ -1,3 +0,0 @@
-cd src
-sdh_setup_bdist_wheel
-sdh_store_wheel .
diff --git a/build/pkgs/setuptools_wheel/type b/build/pkgs/setuptools_wheel/type
deleted file mode 100644
index a6a7b9cd726..00000000000
--- a/build/pkgs/setuptools_wheel/type
+++ /dev/null
@@ -1 +0,0 @@
-standard
diff --git a/build/pkgs/singular/spkg-install.in b/build/pkgs/singular/spkg-install.in
index 1c24a5d7ef8..c64820f4af1 100644
--- a/build/pkgs/singular/spkg-install.in
+++ b/build/pkgs/singular/spkg-install.in
@@ -11,44 +11,6 @@ if [ "x$SAGE_DEBUG" = "xyes" ]; then
     SINGULAR_CONFIGURE="$SINGULAR_CONFIGURE --enable-debug --disable-optimizationflags"
 fi
 
-remove_old_version()
-{
-    # the following is a little verbose but it ensures we leave no trace of 3.x
-    # _or_ 4.x
-    rm -f "$SAGE_LOCAL"/bin/*Singular*
-    rm -f "$SAGE_LOCAL"/bin/*singular*
-    rm -rf "$SAGE_LOCAL/include/singular" # 3.x and 4.x
-    rm -rf "$SAGE_LOCAL/include/factory" # 3.x and 4.x
-    rm -f "$SAGE_LOCAL/include/factor.h" # 3.x only
-    rm -f "$SAGE_LOCAL/include/factoryconf.h" # 3.x only
-    rm -rf "$SAGE_LOCAL/include/omalloc" #4.x only
-    rm -f "$SAGE_LOCAL/include/omalloc.h" # 3.x only
-    rm -f "$SAGE_LOCAL/include/omlimits.h" # 3.x only
-    rm -rf "$SAGE_LOCAL/include/resources" #4.x only
-    rm -rf "$SAGE_LOCAL/include/gfanlib" #4.x only
-
-    # Clean up all Singular-related libraries
-    libs=(
-        singular  # 3.x with lower case
-        singcf    # 3.x only additional archives
-        singfac   # 3.x only additional archives
-        Singular  # 4.x with upper case
-        polys
-        factory
-        omalloc
-        resources           # 3.x
-        singular_resources  # 4.x and up
-        gfan
-    )
-    for name in ${libs[*]}; do
-       rm -f "$SAGE_LOCAL"/lib/lib${name}*
-    done
-
-    rm -f "$SAGE_LOCAL"/lib/p_Procs_Field* # 3.x only
-    rm -rf "$SAGE_LOCAL/share/singular"
-    rm -f "$SAGE_LOCAL"/share/info/singular*
-}
-
 config()
 {
     # configure notes (dates from Singular 3.x, maybe outdated for 4.x):
@@ -92,7 +54,7 @@ build_singular()
 
 
 # Actually run all the functions defined above
-for i in remove_old_version config build_singular; do
+for i in config build_singular; do
     echo "############### Singular stage $i ###############"
     cd "$SRC" && $i
 done
diff --git a/build/pkgs/sqlite/checksums.ini b/build/pkgs/sqlite/checksums.ini
index 5cb33394ab3..777aa3953a3 100644
--- a/build/pkgs/sqlite/checksums.ini
+++ b/build/pkgs/sqlite/checksums.ini
@@ -1,5 +1,5 @@
-tarball=sqlite-autoconf-3360000.tar.gz
+tarball=sqlite-autoconf-${VERSION_MAJOR}${VERSION_MINOR}0${VERSION_MICRO}00.tar.gz
 sha1=a4bcf9e951bfb9745214241ba08476299fc2dc1e
 md5=f5752052fc5b8e1b539af86a3671eac7
 cksum=763219165
-upstream_url=https://www.sqlite.org/2021/sqlite-autoconf-3360000.tar.gz
+upstream_url=https://www.sqlite.org/2021/sqlite-autoconf-${VERSION_MAJOR}${VERSION_MINOR}0${VERSION_MICRO}00.tar.gz
diff --git a/build/pkgs/sqlite/distros/opensuse.txt b/build/pkgs/sqlite/distros/opensuse.txt
index e5831abec57..80861d24415 100644
--- a/build/pkgs/sqlite/distros/opensuse.txt
+++ b/build/pkgs/sqlite/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(sqlite3)"
+pkgconfig(sqlite3)
diff --git a/build/pkgs/sqlite/spkg-install.in b/build/pkgs/sqlite/spkg-install.in
index d13df2ba1b8..0ddfea7a619 100644
--- a/build/pkgs/sqlite/spkg-install.in
+++ b/build/pkgs/sqlite/spkg-install.in
@@ -1,5 +1,3 @@
-rm -f "$SAGE_LOCAL/bin/sqlite3"
-
 cd src
 
 export CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include"
diff --git a/build/pkgs/suitesparse/distros/fedora.txt b/build/pkgs/suitesparse/distros/fedora.txt
index 473f5afeb82..add9f88182c 100644
--- a/build/pkgs/suitesparse/distros/fedora.txt
+++ b/build/pkgs/suitesparse/distros/fedora.txt
@@ -1 +1,2 @@
-suitesparse suitesparse-devel
+suitesparse
+suitesparse-devel
diff --git a/build/pkgs/suitesparse/distros/gentoo.txt b/build/pkgs/suitesparse/distros/gentoo.txt
index 3b1b625f3e6..36650ca82e3 100644
--- a/build/pkgs/suitesparse/distros/gentoo.txt
+++ b/build/pkgs/suitesparse/distros/gentoo.txt
@@ -1 +1,4 @@
-sci-libs/amd sci-libs/cholmod sci-libs/suitesparseconfig sci-libs/umfpack
+sci-libs/amd
+sci-libs/cholmod
+sci-libs/suitesparseconfig
+sci-libs/umfpack
diff --git a/build/pkgs/suitesparse/distros/macports.txt b/build/pkgs/suitesparse/distros/macports.txt
index 45d8956e69e..93b99b56740 100644
--- a/build/pkgs/suitesparse/distros/macports.txt
+++ b/build/pkgs/suitesparse/distros/macports.txt
@@ -1 +1,2 @@
-SuiteSparse
+# broken as of 2022-05-01
+#SuiteSparse
diff --git a/build/pkgs/symengine/checksums.ini b/build/pkgs/symengine/checksums.ini
index 8eb65f05ef9..9ba59940a9f 100644
--- a/build/pkgs/symengine/checksums.ini
+++ b/build/pkgs/symengine/checksums.ini
@@ -1,5 +1,6 @@
 tarball=symengine-VERSION.tar.gz
-sha1=a2c8957f2099c9199751b165f107bf93d6823818
-md5=fe3c3fee1bd8dfdb9576fc2d28cb1076
-cksum=3544211225
+sha1=2dfee07108509963f3dbe3d9cad9de76d85e551f
+md5=4074f3c76570bdc2ae9914edafa29eb6
+cksum=3782541135
 upstream_url=https://github.com/symengine/symengine/releases/download/vVERSION/symengine-VERSION.tar.gz
+
diff --git a/build/pkgs/symengine/package-version.txt b/build/pkgs/symengine/package-version.txt
index af88ba82486..a8839f70de0 100644
--- a/build/pkgs/symengine/package-version.txt
+++ b/build/pkgs/symengine/package-version.txt
@@ -1 +1 @@
-0.11.1
+0.11.2
\ No newline at end of file
diff --git a/build/pkgs/symengine/spkg-configure.m4 b/build/pkgs/symengine/spkg-configure.m4
index c037d2e5f72..a6a78791dbc 100644
--- a/build/pkgs/symengine/spkg-configure.m4
+++ b/build/pkgs/symengine/spkg-configure.m4
@@ -1,7 +1,7 @@
 SAGE_SPKG_CONFIGURE([symengine], [
     m4_pushdef(SAGE_SYMENGINE_VERSION_MAJOR, [0])
     m4_pushdef(SAGE_SYMENGINE_VERSION_MINOR, [11])
-    SAGE_SPKG_DEPCHECK([gmp arb ecm flint mpc mpfr], [
+    SAGE_SPKG_DEPCHECK([gmp ecm flint mpc mpfr], [
         AC_CHECK_HEADER([symengine/symengine_config.h], [], [sage_spkg_install_symengine=yes])
         AC_MSG_CHECKING([whether we can link a program using symengine])
         SYMENGINE_SAVED_LIBS=$LIBS
diff --git a/build/pkgs/symmetrica/checksums.ini b/build/pkgs/symmetrica/checksums.ini
index 5f70ed5363c..6d06ebda462 100644
--- a/build/pkgs/symmetrica/checksums.ini
+++ b/build/pkgs/symmetrica/checksums.ini
@@ -2,4 +2,4 @@ tarball=symmetrica-VERSION.tar.xz
 sha1=0044cc087ff04267c246e730c6570d89f6e593af
 md5=cd4716c26b5c625a012c22656113ef6f
 cksum=1186250347
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/symmetrica-3.0.1.tar.xz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/symmetrica-VERSION.tar.xz
diff --git a/build/pkgs/sympow/checksums.ini b/build/pkgs/sympow/checksums.ini
index f6ad79b8f93..87bf0086178 100644
--- a/build/pkgs/sympow/checksums.ini
+++ b/build/pkgs/sympow/checksums.ini
@@ -2,4 +2,4 @@ tarball=sympow-vVERSION.tar.gz
 sha1=37a909c26009415197b5088a2f1b53dd3558f494
 md5=51f2c717c84ec9c2840af740751cf797
 cksum=1444149964
-upstream_url=https://github.com/mkoeppe/sympow/releases/download/v2.023.6/sympow-v2.023.6.tar.gz
+upstream_url=https://github.com/mkoeppe/sympow/releases/download/vVERSION/sympow-vVERSION.tar.gz
diff --git a/build/pkgs/tachyon/distros/fedora.txt b/build/pkgs/tachyon/distros/fedora.txt
index b1ee1e1bdbe..035020a8d5b 100644
--- a/build/pkgs/tachyon/distros/fedora.txt
+++ b/build/pkgs/tachyon/distros/fedora.txt
@@ -1 +1,2 @@
-tachyon tachyon-devel
+tachyon
+tachyon-devel
diff --git a/build/pkgs/texlive_luatex/SPKG.rst b/build/pkgs/texlive_luatex/SPKG.rst
new file mode 100644
index 00000000000..0bb7f65eb8b
--- /dev/null
+++ b/build/pkgs/texlive_luatex/SPKG.rst
@@ -0,0 +1,21 @@
+texlive_luatex: LuaTeX packages
+===============================
+
+Description
+-----------
+
+Packages for LuaTeX, a TeX engine using Lua as an embedded scripting and
+extension language, with native support for Unicode, OpenType/TrueType fonts,
+and both PDF and DVI output.
+
+The purpose of this dummy package is to associate system package lists with it.
+
+License
+-------
+
+GNU General Public License version 2.0 (GPLv2)
+
+Upstream Contact
+----------------
+
+https://www.luatex.org/
diff --git a/build/pkgs/texlive_luatex/dependencies b/build/pkgs/texlive_luatex/dependencies
new file mode 100644
index 00000000000..ba0ee3a029f
--- /dev/null
+++ b/build/pkgs/texlive_luatex/dependencies
@@ -0,0 +1 @@
+texlive
diff --git a/build/pkgs/texlive_luatex/distros/alpine.txt b/build/pkgs/texlive_luatex/distros/alpine.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/alpine.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/arch.txt b/build/pkgs/texlive_luatex/distros/arch.txt
new file mode 100644
index 00000000000..2eec092a978
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/arch.txt
@@ -0,0 +1 @@
+texlive-collection-luatex
diff --git a/build/pkgs/texlive_luatex/distros/cygwin.txt b/build/pkgs/texlive_luatex/distros/cygwin.txt
new file mode 100644
index 00000000000..ba0ee3a029f
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/cygwin.txt
@@ -0,0 +1 @@
+texlive
diff --git a/build/pkgs/texlive_luatex/distros/debian.txt b/build/pkgs/texlive_luatex/distros/debian.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/debian.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/fedora.txt b/build/pkgs/texlive_luatex/distros/fedora.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/fedora.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/gentoo.txt b/build/pkgs/texlive_luatex/distros/gentoo.txt
new file mode 100644
index 00000000000..6e651aec020
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/gentoo.txt
@@ -0,0 +1 @@
+dev-texlive/texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/macports.txt b/build/pkgs/texlive_luatex/distros/macports.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/macports.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/opensuse.txt b/build/pkgs/texlive_luatex/distros/opensuse.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/opensuse.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/distros/repology.txt b/build/pkgs/texlive_luatex/distros/repology.txt
new file mode 100644
index 00000000000..d2b702df88a
--- /dev/null
+++ b/build/pkgs/texlive_luatex/distros/repology.txt
@@ -0,0 +1 @@
+texlive-luatex
diff --git a/build/pkgs/texlive_luatex/spkg-configure.m4 b/build/pkgs/texlive_luatex/spkg-configure.m4
new file mode 100644
index 00000000000..6fb53bbc171
--- /dev/null
+++ b/build/pkgs/texlive_luatex/spkg-configure.m4
@@ -0,0 +1,10 @@
+SAGE_SPKG_CONFIGURE([texlive_luatex], [
+    sage_spkg_install_texlive_luatex=no
+    AC_MSG_CHECKING([for luaotfload-main.lua])
+    AS_IF([kpsewhich luaotfload-main.lua >& AS_MESSAGE_LOG_FD 2>&1], [
+        AC_MSG_RESULT([yes])
+    ], [
+        AC_MSG_RESULT([no])
+        sage_spkg_install_texlive_luatex=yes
+    ])
+])
diff --git a/build/pkgs/texlive_luatex/type b/build/pkgs/texlive_luatex/type
new file mode 100644
index 00000000000..134d9bc32d5
--- /dev/null
+++ b/build/pkgs/texlive_luatex/type
@@ -0,0 +1 @@
+optional
diff --git a/build/pkgs/trove_classifiers/checksums.ini b/build/pkgs/trove_classifiers/checksums.ini
index d59736dbac2..7350f444667 100644
--- a/build/pkgs/trove_classifiers/checksums.ini
+++ b/build/pkgs/trove_classifiers/checksums.ini
@@ -1,5 +1,5 @@
-tarball=trove-classifiers-VERSION.tar.gz
-sha1=c6821e6c6d57dc2eec2d4156d63cdca939373759
-md5=154ae4cdb69ac37bb35a35c6fbc477cd
-cksum=84032195
-upstream_url=https://pypi.io/packages/source/t/trove_classifiers/trove-classifiers-VERSION.tar.gz
+tarball=trove_classifiers-VERSION-py3-none-any.whl
+sha1=c341abee77b5c87d913b86dc587e544553f0658c
+md5=78e67f128f53b8417134429192810701
+cksum=3034057088
+upstream_url=https://pypi.io/packages/py3/t/trove_classifiers/trove_classifiers-VERSION-py3-none-any.whl
diff --git a/build/pkgs/trove_classifiers/dependencies b/build/pkgs/trove_classifiers/dependencies
index 2134b2a4179..cbe3cae5550 100644
--- a/build/pkgs/trove_classifiers/dependencies
+++ b/build/pkgs/trove_classifiers/dependencies
@@ -1,4 +1,4 @@
-$(PYTHON) calver | $(PYTHON_TOOLCHAIN)
+calver | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/trove_classifiers/package-version.txt b/build/pkgs/trove_classifiers/package-version.txt
index b82d6dba095..a33bd2f9968 100644
--- a/build/pkgs/trove_classifiers/package-version.txt
+++ b/build/pkgs/trove_classifiers/package-version.txt
@@ -1 +1 @@
-2023.11.14
+2023.11.29
diff --git a/build/pkgs/trove_classifiers/spkg-install.in b/build/pkgs/trove_classifiers/spkg-install.in
deleted file mode 100644
index 37ac1a53437..00000000000
--- a/build/pkgs/trove_classifiers/spkg-install.in
+++ /dev/null
@@ -1,2 +0,0 @@
-cd src
-sdh_pip_install .
diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini
index b7def1fa7e0..90a2a903926 100644
--- a/build/pkgs/virtualenv/checksums.ini
+++ b/build/pkgs/virtualenv/checksums.ini
@@ -1,5 +1,5 @@
 tarball=virtualenv-VERSION-py3-none-any.whl
-sha1=a17fc6409d29b7e7b1427f37496bfc0fa399f9bf
-md5=6374ee91c1ed02956a334aa01d9414ed
-cksum=31593789
+sha1=9c942063d76d85361f0567b59cce8238c57f1183
+md5=7dec1148d91180767ae908dc49a7ebf5
+cksum=3612060825
 upstream_url=https://pypi.io/packages/py3/v/virtualenv/virtualenv-VERSION-py3-none-any.whl
diff --git a/build/pkgs/virtualenv/dependencies b/build/pkgs/virtualenv/dependencies
index b0e964bba0c..148ef28948e 100644
--- a/build/pkgs/virtualenv/dependencies
+++ b/build/pkgs/virtualenv/dependencies
@@ -1,4 +1,4 @@
-distlib filelock platformdirs | $(PYTHON_TOOLCHAIN) $(PYTHON)
+distlib filelock platformdirs | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt
index b744a5a46cb..242a1e92b72 100644
--- a/build/pkgs/virtualenv/package-version.txt
+++ b/build/pkgs/virtualenv/package-version.txt
@@ -1 +1 @@
-20.24.4
+20.25.0
diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini
index b12a1973b0c..4f2b8c5c534 100644
--- a/build/pkgs/wheel/checksums.ini
+++ b/build/pkgs/wheel/checksums.ini
@@ -1,5 +1,5 @@
-tarball=wheel-VERSION.tar.gz
-sha1=34a787f7069762e267e5ed62ed31cc9c87ea910b
-md5=06271a9e90c948b7e93dd7ce0fd90272
-cksum=444550709
-upstream_url=https://pypi.io/packages/source/w/wheel/wheel-VERSION.tar.gz
+tarball=wheel-VERSION-py3-none-any.whl
+sha1=fcf4ad8d5d8216d661bc98eede0d9210cbc5b697
+md5=779d91395ceb12e15e3a585b30b53f9f
+cksum=1421399426
+upstream_url=https://pypi.io/packages/py3/w/wheel/wheel-VERSION-py3-none-any.whl
diff --git a/build/pkgs/wheel/dependencies b/build/pkgs/wheel/dependencies
index a8e327be793..644ad35f773 100644
--- a/build/pkgs/wheel/dependencies
+++ b/build/pkgs/wheel/dependencies
@@ -1,4 +1,4 @@
- setuptools | $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt
index 6599454d402..787ffc30a81 100644
--- a/build/pkgs/wheel/package-version.txt
+++ b/build/pkgs/wheel/package-version.txt
@@ -1 +1 @@
-0.41.2
+0.42.0
diff --git a/build/pkgs/wheel/spkg-install.in b/build/pkgs/wheel/spkg-install.in
deleted file mode 100644
index 5ad5c08fe0e..00000000000
--- a/build/pkgs/wheel/spkg-install.in
+++ /dev/null
@@ -1,13 +0,0 @@
-cd src
-
-python3 setup.py --no-user-cfg install \
-    --single-version-externally-managed --root="$SAGE_DESTDIR" || \
-        sdh_die "Error building / installing package 'wheel'"
-
-# Now build a wheel so that we have a complete set of wheels.
-
-# Because of staging, we cannot use the installed 'wheel' package yet.
-export PYTHONPATH="$(pwd)/src"
-
-sdh_setup_bdist_wheel
-sdh_store_wheel .
diff --git a/build/pkgs/xindy/SPKG.rst b/build/pkgs/xindy/SPKG.rst
new file mode 100644
index 00000000000..49733639b2f
--- /dev/null
+++ b/build/pkgs/xindy/SPKG.rst
@@ -0,0 +1,20 @@
+xindy: a general-purpose index processor
+========================================
+
+Description
+-----------
+
+This dummy package represents xindy: xindy was developed after an impasse had been encountered in the attempt to complete internationalisation of makeindex. Xindy can be used to process indexes for documents marked up using (LA)TEX, Nroff family and SGML-based languages. Xindy is highly configurable, both in markup terms and in terms of the collating order of the text being processed.
+
+We do not have an SPKG for it. The purpose of this dummy package is to
+associate system package lists with it.
+
+License
+-------
+
+GNU General Public License version 2.0 (GPLv2)
+
+Upstream Contact
+----------------
+
+http://www.xindy.org/
diff --git a/build/pkgs/xindy/distros/debian.txt b/build/pkgs/xindy/distros/debian.txt
new file mode 100644
index 00000000000..aeb5474c700
--- /dev/null
+++ b/build/pkgs/xindy/distros/debian.txt
@@ -0,0 +1 @@
+xindy
diff --git a/build/pkgs/xindy/distros/macports.txt b/build/pkgs/xindy/distros/macports.txt
new file mode 100644
index 00000000000..aeb5474c700
--- /dev/null
+++ b/build/pkgs/xindy/distros/macports.txt
@@ -0,0 +1 @@
+xindy
diff --git a/build/pkgs/xindy/distros/opensuse.txt b/build/pkgs/xindy/distros/opensuse.txt
new file mode 100644
index 00000000000..aeb5474c700
--- /dev/null
+++ b/build/pkgs/xindy/distros/opensuse.txt
@@ -0,0 +1 @@
+xindy
diff --git a/build/pkgs/xindy/distros/repology.txt b/build/pkgs/xindy/distros/repology.txt
new file mode 100644
index 00000000000..aeb5474c700
--- /dev/null
+++ b/build/pkgs/xindy/distros/repology.txt
@@ -0,0 +1 @@
+xindy
diff --git a/build/pkgs/xindy/spkg-configure.m4 b/build/pkgs/xindy/spkg-configure.m4
new file mode 100644
index 00000000000..07fbe71efa8
--- /dev/null
+++ b/build/pkgs/xindy/spkg-configure.m4
@@ -0,0 +1,5 @@
+SAGE_SPKG_CONFIGURE([xindy], [
+    sage_spkg_install_xindy=no
+    AC_PATH_PROG([XINDY], [xindy])
+    AS_IF([test -z "$XINDY"], [sage_spkg_install_xindy=yes])
+])
diff --git a/build/pkgs/xindy/type b/build/pkgs/xindy/type
new file mode 100644
index 00000000000..134d9bc32d5
--- /dev/null
+++ b/build/pkgs/xindy/type
@@ -0,0 +1 @@
+optional
diff --git a/build/pkgs/zeromq/distros/fedora.txt b/build/pkgs/zeromq/distros/fedora.txt
index 901dce11d4f..7ab081d69d9 100644
--- a/build/pkgs/zeromq/distros/fedora.txt
+++ b/build/pkgs/zeromq/distros/fedora.txt
@@ -1 +1,2 @@
-zeromq zeromq-devel
+zeromq
+zeromq-devel
diff --git a/build/pkgs/zeromq/distros/macports.txt b/build/pkgs/zeromq/distros/macports.txt
index 3f0b9927569..11d74369d60 100644
--- a/build/pkgs/zeromq/distros/macports.txt
+++ b/build/pkgs/zeromq/distros/macports.txt
@@ -1 +1,2 @@
-zmq-devel
+# Broken as of 2022-05-01
+#zmq-devel
diff --git a/build/pkgs/zeromq/distros/opensuse.txt b/build/pkgs/zeromq/distros/opensuse.txt
index bf80cc05be1..1d10c00df82 100644
--- a/build/pkgs/zeromq/distros/opensuse.txt
+++ b/build/pkgs/zeromq/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(libzmq)"
+pkgconfig(libzmq)
diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini
index 66cd13339d6..7c9d001b307 100644
--- a/build/pkgs/zipp/checksums.ini
+++ b/build/pkgs/zipp/checksums.ini
@@ -1,5 +1,5 @@
-tarball=zipp-VERSION.tar.gz
-sha1=a9f9aebc205b7829c43b34e79c3f87c42b183176
-md5=a4cf8c530da863c27a04251724436681
-cksum=1303269799
-upstream_url=https://pypi.io/packages/source/z/zipp/zipp-VERSION.tar.gz
+tarball=zipp-VERSION-py3-none-any.whl
+sha1=8dd92e1b777b02ec6e1ebe72926d32a82c58b246
+md5=d93f0b8485000b37800a6de09ed6c1cb
+cksum=3547254189
+upstream_url=https://pypi.io/packages/py3/z/zipp/zipp-VERSION-py3-none-any.whl
diff --git a/build/pkgs/zipp/dependencies b/build/pkgs/zipp/dependencies
index 995ddecb8f4..644ad35f773 100644
--- a/build/pkgs/zipp/dependencies
+++ b/build/pkgs/zipp/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) setuptools_scm $(PYTHON)
+ | pip $(PYTHON)
 
 ----------
 All lines of this file are ignored except the first.
diff --git a/build/pkgs/zipp/spkg-install.in b/build/pkgs/zipp/spkg-install.in
deleted file mode 100644
index 6d596586150..00000000000
--- a/build/pkgs/zipp/spkg-install.in
+++ /dev/null
@@ -1,14 +0,0 @@
-if [ -z "$SAGE_LOCAL" ]; then
-    echo >&2 "SAGE_LOCAL undefined ... exiting"
-    echo >&2 "Maybe run 'sage --sh'?"
-    exit 1
-fi
-
-cd src
-
-sdh_pip_install .
-
-if [ $? -ne 0 ]; then
-    echo "Error installing zipp ... exiting"
-    exit 1
-fi
diff --git a/build/pkgs/zlib/distros/opensuse.txt b/build/pkgs/zlib/distros/opensuse.txt
index 734537742c0..74ff91e4b69 100644
--- a/build/pkgs/zlib/distros/opensuse.txt
+++ b/build/pkgs/zlib/distros/opensuse.txt
@@ -1 +1 @@
-"pkgconfig(zlib)"
+pkgconfig(zlib)
diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py
index 1b84cbf6461..e182321d7da 100644
--- a/build/sage_bootstrap/cmdline.py
+++ b/build/sage_bootstrap/cmdline.py
@@ -292,6 +292,9 @@ def make_parser():
     parser_download.add_argument(
         '--on-error', choices=['stop', 'warn'], default='stop',
         help='What to do if the tarball cannot be downloaded')
+    parser.add_argument(
+        '--no-check-certificate', action='store_true',
+        help='Do not check SSL certificates for https connections')
 
     parser_upload = subparsers.add_parser(
         'upload', epilog=epilog_upload,
@@ -381,6 +384,12 @@ def run():
     elif args.subcommand == 'update-latest':
         app.update_latest_cls(args.package_name, commit=args.commit)
     elif args.subcommand == 'download':
+        if args.no_check_certificate:
+            try:
+                import ssl
+                ssl._create_default_https_context = ssl._create_unverified_context
+            except ImportError:
+                pass
         app.download_cls(args.package_name,
                          allow_upstream=args.allow_upstream,
                          on_error=args.on_error)
diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py
index f6a5425005a..43bfbffa47c 100644
--- a/build/sage_bootstrap/package.py
+++ b/build/sage_bootstrap/package.py
@@ -121,6 +121,42 @@ def tarball(self):
             self.__tarball = Tarball(self.tarball_filename, package=self)
         return self.__tarball
 
+    def _substitute_variables_once(self, pattern):
+        """
+        Substitute (at most) one occurrence of variables in ``pattern`` by the values.
+
+        These variables are ``VERSION``, ``VERSION_MAJOR``, ``VERSION_MINOR``,
+        ``VERSION_MICRO``, either appearing like this or in the form ``${VERSION_MAJOR}``
+        etc.
+
+        Return a tuple:
+        - the string with the substitution done or the original string
+        - whether a substitution was done
+        """
+        for var in ('VERSION_MAJOR', 'VERSION_MINOR', 'VERSION_MICRO', 'VERSION'):
+            # As VERSION is a substring of the other three, it needs to be tested last.
+            dollar_brace_var = '${' + var + '}'
+            if dollar_brace_var in pattern:
+                value = getattr(self, var.lower())
+                return pattern.replace(dollar_brace_var, value, 1), True
+            elif var in pattern:
+                value = getattr(self, var.lower())
+                return pattern.replace(var, value, 1), True
+        return pattern, False
+
+    def _substitute_variables(self, pattern):
+        """
+        Substitute all occurrences of ``VERSION`` in ``pattern`` by the actual version.
+
+        Likewise for ``VERSION_MAJOR``, ``VERSION_MINOR``, ``VERSION_MICRO``,
+        either appearing like this or in the form ``${VERSION}``, ``${VERSION_MAJOR}``,
+        etc.
+        """
+        not_done = True
+        while not_done:
+            pattern, not_done = self._substitute_variables_once(pattern)
+        return pattern
+
     @property
     def tarball_pattern(self):
         """
@@ -150,7 +186,7 @@ def tarball_filename(self):
         """
         pattern = self.tarball_pattern
         if pattern:
-            return pattern.replace('VERSION', self.version)
+            return self._substitute_variables(pattern)
         else:
             return None
 
@@ -177,7 +213,7 @@ def tarball_upstream_url(self):
         """
         pattern = self.tarball_upstream_url_pattern
         if pattern:
-            return pattern.replace('VERSION', self.version)
+            return self._substitute_variables(pattern)
         else:
             return None
 
@@ -212,6 +248,39 @@ def version(self):
         """
         return self.__version
 
+    @property
+    def version_major(self):
+        """
+        Return the major version
+
+        OUTPUT:
+
+        String. The package's major version.
+        """
+        return self.version.split('.')[0]
+
+    @property
+    def version_minor(self):
+        """
+        Return the minor version
+
+        OUTPUT:
+
+        String. The package's minor version.
+        """
+        return self.version.split('.')[1]
+
+    @property
+    def version_micro(self):
+        """
+        Return the micro version
+
+        OUTPUT:
+
+        String. The package's micro version.
+        """
+        return self.version.split('.')[2]
+
     @property
     def patchlevel(self):
         """
diff --git a/build/sage_bootstrap/updater.py b/build/sage_bootstrap/updater.py
index 7c1431967a1..67f1c961124 100644
--- a/build/sage_bootstrap/updater.py
+++ b/build/sage_bootstrap/updater.py
@@ -69,8 +69,11 @@ def _update_version(self, new_version):
     def download_upstream(self, download_url=None):
         tarball = self.package.tarball
         if download_url is None:
+            pattern = self.package.tarball_upstream_url_pattern
+            if pattern and 'VERSION' not in pattern:
+                print('Warning: upstream_url pattern does not use the VERSION variable')
             download_url = self.package.tarball_upstream_url
         if download_url is None:
             raise ValueError("package has no default upstream_url pattern, download_url needed")
-        print('Downloading tarball to {0}'.format(tarball.upstream_fqn))
+        print('Downloading tarball from {0} to {1}'.format(download_url, tarball.upstream_fqn))
         Download(download_url, tarball.upstream_fqn).run()
diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-conf/VERSION.txt
+++ b/pkgs/sage-conf/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sage-conf/_sage_conf/_conf.py.in b/pkgs/sage-conf/_sage_conf/_conf.py.in
index 895d6572d1e..ccc1c9695fb 100644
--- a/pkgs/sage-conf/_sage_conf/_conf.py.in
+++ b/pkgs/sage-conf/_sage_conf/_conf.py.in
@@ -9,6 +9,11 @@ VERSION = "@PACKAGE_VERSION@"
 SAGE_LOCAL = "@prefix@"
 SAGE_ROOT = "@SAGE_ROOT@"
 
+# The semicolon-separated list of GAP root paths. This is the list of
+# locations that are searched for GAP packages. This is passed directly
+# to GAP via the -l flag.
+GAP_ROOT_PATHS = "@GAP_ROOT_PATHS@".replace('${prefix}', SAGE_LOCAL)
+
 # The path to the standalone maxima executable.
 MAXIMA = "@SAGE_MAXIMA@".replace('${prefix}', SAGE_LOCAL)
 
diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-conf_conda/VERSION.txt
+++ b/pkgs/sage-conf_conda/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-conf_pypi/VERSION.txt
+++ b/pkgs/sage-conf_pypi/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-docbuild/VERSION.txt
+++ b/pkgs/sage-docbuild/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-setup/VERSION.txt
+++ b/pkgs/sage-setup/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sage-sws2rst/VERSION.txt
+++ b/pkgs/sage-sws2rst/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-bliss/VERSION.txt
+++ b/pkgs/sagemath-bliss/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-bliss/pyproject.toml.m4 b/pkgs/sagemath-bliss/pyproject.toml.m4
index df61e7fac6a..40bfebf2489 100644
--- a/pkgs/sagemath-bliss/pyproject.toml.m4
+++ b/pkgs/sagemath-bliss/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_conf
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-categories/VERSION.txt
+++ b/pkgs/sagemath-categories/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-categories/pyproject.toml.m4 b/pkgs/sagemath-categories/pyproject.toml.m4
index 72c75ccd11e..eed48a1db15 100644
--- a/pkgs/sagemath-categories/pyproject.toml.m4
+++ b/pkgs/sagemath-categories/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_wheel
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-coxeter3/VERSION.txt
+++ b/pkgs/sagemath-coxeter3/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-coxeter3/pyproject.toml.m4 b/pkgs/sagemath-coxeter3/pyproject.toml.m4
index a8d0e52a4b0..9f9c9dedd5e 100644
--- a/pkgs/sagemath-coxeter3/pyproject.toml.m4
+++ b/pkgs/sagemath-coxeter3/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
     SPKG_INSTALL_REQUIRES_cython
diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-environment/VERSION.txt
+++ b/pkgs/sagemath-environment/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-environment/pyproject.toml.m4 b/pkgs/sagemath-environment/pyproject.toml.m4
index 4061c8c46eb..44ddd190f99 100644
--- a/pkgs/sagemath-environment/pyproject.toml.m4
+++ b/pkgs/sagemath-environment/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_wheel
 ]
 build-backend = "setuptools.build_meta"
diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-mcqd/VERSION.txt
+++ b/pkgs/sagemath-mcqd/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-mcqd/pyproject.toml.m4 b/pkgs/sagemath-mcqd/pyproject.toml.m4
index c94aface3fd..55a58611334 100644
--- a/pkgs/sagemath-mcqd/pyproject.toml.m4
+++ b/pkgs/sagemath-mcqd/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
     SPKG_INSTALL_REQUIRES_cython
diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-meataxe/VERSION.txt
+++ b/pkgs/sagemath-meataxe/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-meataxe/pyproject.toml.m4 b/pkgs/sagemath-meataxe/pyproject.toml.m4
index 0c8fd46d8be..b4a504c4f1f 100644
--- a/pkgs/sagemath-meataxe/pyproject.toml.m4
+++ b/pkgs/sagemath-meataxe/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
     SPKG_INSTALL_REQUIRES_cython
diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-objects/VERSION.txt
+++ b/pkgs/sagemath-objects/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-objects/pyproject.toml.m4 b/pkgs/sagemath-objects/pyproject.toml.m4
index 9214f1ffb8d..a8d7d83a44a 100644
--- a/pkgs/sagemath-objects/pyproject.toml.m4
+++ b/pkgs/sagemath-objects/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_wheel
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-repl/VERSION.txt
+++ b/pkgs/sagemath-repl/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-repl/pyproject.toml.m4 b/pkgs/sagemath-repl/pyproject.toml.m4
index dfdfb692c9b..2bc276158a4 100644
--- a/pkgs/sagemath-repl/pyproject.toml.m4
+++ b/pkgs/sagemath-repl/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_wheel
 ]
 build-backend = "setuptools.build_meta"
diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-sirocco/VERSION.txt
+++ b/pkgs/sagemath-sirocco/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-sirocco/pyproject.toml.m4 b/pkgs/sagemath-sirocco/pyproject.toml.m4
index 55e400738d7..6f2874254ab 100644
--- a/pkgs/sagemath-sirocco/pyproject.toml.m4
+++ b/pkgs/sagemath-sirocco/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
     SPKG_INSTALL_REQUIRES_cython
diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/pkgs/sagemath-tdlib/VERSION.txt
+++ b/pkgs/sagemath-tdlib/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/pkgs/sagemath-tdlib/pyproject.toml.m4 b/pkgs/sagemath-tdlib/pyproject.toml.m4
index 8e3bcdd988f..d3efaf52cc1 100644
--- a/pkgs/sagemath-tdlib/pyproject.toml.m4
+++ b/pkgs/sagemath-tdlib/pyproject.toml.m4
@@ -2,7 +2,7 @@ include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*-
 [build-system]
 # Minimum requirements for the build system to execute.
 requires = [
-    SPKG_INSTALL_REQUIRES_setuptools_wheel
+    SPKG_INSTALL_REQUIRES_setuptools
     SPKG_INSTALL_REQUIRES_sage_setup
     SPKG_INSTALL_REQUIRES_sagemath_environment
     SPKG_INSTALL_REQUIRES_cython
diff --git a/src/VERSION.txt b/src/VERSION.txt
index 4d40853d20c..201b86839ab 100644
--- a/src/VERSION.txt
+++ b/src/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta4
diff --git a/src/bin/sage b/src/bin/sage
index ba353116e7c..a198ed8f273 100755
--- a/src/bin/sage
+++ b/src/bin/sage
@@ -628,7 +628,9 @@ fi
 
 if [ "$1" = '-gap' -o "$1" = '--gap' ]; then
     shift
-    exec gap "$@"
+    # Use "-A" to avoid warnings about missing packages. The gap
+    # interface and libgap within sage both already do this.
+    exec gap -A "$@"
 fi
 
 if [ "$1" = '-gap3' -o "$1" = '--gap3' ]; then
@@ -1001,6 +1003,7 @@ if [ "$1" = '-tox' -o "$1" = '--tox' ]; then
             exec tox -c "$SAGE_SRC" "$@"
         else
             echo "Run 'sage -i tox' to install"
+            exit 1
         fi
     else
         echo >&2 "error: Sage source directory or tox.ini not available"
@@ -1022,6 +1025,7 @@ if [ "$1" = '-pytest' -o "$1" = '--pytest' ]; then
             exec pytest --rootdir="$SAGE_SRC" --doctest-modules "$@" "$SAGE_SRC"
         else
             echo "Run 'sage -i pytest' to install"
+            exit 1
         fi
     else
         echo >&2 "error: Sage source directory or tox.ini not available"
diff --git a/src/bin/sage-env b/src/bin/sage-env
index 29b96c3c895..8fcfda48fb6 100644
--- a/src/bin/sage-env
+++ b/src/bin/sage-env
@@ -228,7 +228,7 @@ fi
 # depending on SAGE_ROOT and SAGE_LOCAL which are already defined.
 if [ -n "$SAGE_LOCAL" ]; then
     export SAGE_SHARE="$SAGE_LOCAL/share"
-    export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed"
+    export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed"  # deprecated
 fi
 if [ -n "$SAGE_SHARE" ]; then
     export SAGE_DOC="$SAGE_SHARE/doc/sage"
diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh
index 7d38e167623..701727e3543 100644
--- a/src/bin/sage-version.sh
+++ b/src/bin/sage-version.sh
@@ -4,6 +4,6 @@
 # which stops "setup.py develop" from rewriting it as a Python file.
 :
 # This file is auto-generated by the sage-update-version script, do not edit!
-SAGE_VERSION='10.3.beta2'
-SAGE_RELEASE_DATE='2023-12-13'
-SAGE_VERSION_BANNER='SageMath version 10.3.beta2, Release Date: 2023-12-13'
+SAGE_VERSION='10.3.beta4'
+SAGE_RELEASE_DATE='2023-12-26'
+SAGE_VERSION_BANNER='SageMath version 10.3.beta4, Release Date: 2023-12-26'
diff --git a/src/doc/bootstrap b/src/doc/bootstrap
index b6830ad7bb3..2371945a283 100755
--- a/src/doc/bootstrap
+++ b/src/doc/bootstrap
@@ -19,7 +19,6 @@ fi
 
 cd "$SAGE_ROOT"
 
-STRIP_COMMENTS="sed s/#.*//;"
 OUTPUT_DIR="src/doc/en/installation"
 mkdir -p "$OUTPUT_DIR"
 
@@ -37,9 +36,8 @@ for SYSTEM in arch debian fedora homebrew opensuse; do
     DEVELOP_SYSTEM_PACKAGES=
     for PKG_BASE in $(sage-package list --has-file distros/$SYSTEM.txt); do
                 PKG_SCRIPTS=build/pkgs/$PKG_BASE
-                SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/distros/$SYSTEM.txt
                 PKG_TYPE=$(cat $PKG_SCRIPTS/type)
-                PKG_SYSTEM_PACKAGES=$(echo $(${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE))
+                PKG_SYSTEM_PACKAGES=$(sage-get-system-packages $SYSTEM $PKG_BASE)
                 if [ -n "PKG_SYSTEM_PACKAGES" ]; then
                     if [ -f $PKG_SCRIPTS/spkg-configure.m4 ]; then
                        case "$PKG_BASE:$PKG_TYPE" in
@@ -75,10 +73,10 @@ for SYSTEM in arch debian fedora homebrew opensuse; do
     if [ "${BOOTSTRAP_QUIET}" = "no" ]; then
         echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/$SYSTEM"*.txt"
     fi
-    echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM.txt
-    echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $OPTIONAL_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-optional.txt
-    echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $RECOMMENDED_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-recommended.txt
-    echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $DEVELOP_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-develop.txt
+    echo "$(sage-print-system-package-command $SYSTEM --prompt --wrap --sudo install $(echo $(echo $SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM.txt
+    echo "$(sage-print-system-package-command $SYSTEM --prompt --wrap --sudo install $(echo $(echo $OPTIONAL_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-optional.txt
+    echo "$(sage-print-system-package-command $SYSTEM --prompt --wrap --sudo install $(echo $(echo $RECOMMENDED_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-recommended.txt
+    echo "$(sage-print-system-package-command $SYSTEM --prompt --wrap --sudo install $(echo $(echo $DEVELOP_SYSTEM_PACKAGES | xargs -n 1 echo | sort | uniq)))" > "$OUTPUT_DIR"/$SYSTEM-develop.txt
 done
 
 OUTPUT_DIR="src/doc/en/reference/spkg"
@@ -190,7 +188,7 @@ All External Packages
 EOF
 
 OUTPUT_INDEX="$OUTPUT_DIR"/index_alph.rst
-cat >> "$OUTPUT_INDEX" < "$OUTPUT_DIR"/$PKG_BASE.rst
-            echo >> "$OUTPUT_INDEX" "   $PKG_BASE"
+    echo "   $PKG_BASE"
 done
-cat >> "$OUTPUT_INDEX" < "$OUTPUT_INDEX"
+sage-package list --has-file SPKG.rst | OUTPUT_DIR=$OUTPUT_DIR OUTPUT_RST=1 xargs -P 0 -n 1 sage-spkg-info
diff --git a/src/doc/en/constructions/groups.rst b/src/doc/en/constructions/groups.rst
index 042d37088b1..da90efecfaa 100644
--- a/src/doc/en/constructions/groups.rst
+++ b/src/doc/en/constructions/groups.rst
@@ -180,8 +180,8 @@ Here's another way, working more directly with GAP::
     sage: print(gap.eval("normal := NormalSubgroups( G )"))
     [ Alt( [ 1 .. 5 ] ), Group(()) ]
     sage: G = gap.new("DihedralGroup( 10 )")
-    sage: G.NormalSubgroups()
-    [ Group( [ f1, f2 ] ), Group( [ f2 ] ), Group(  of ... ) ]
+    sage: G.NormalSubgroups().SortedList()
+    [ Group(  of ... ), Group( [ f2 ] ), Group( [ f1, f2 ] ) ]
     sage: print(gap.eval("G := SymmetricGroup( 4 )"))
     Sym( [ 1 .. 4 ] )
     sage: print(gap.eval("normal := NormalSubgroups( G );"))
diff --git a/src/doc/en/developer/coding_in_other.rst b/src/doc/en/developer/coding_in_other.rst
index 544add15b88..6fb31770815 100644
--- a/src/doc/en/developer/coding_in_other.rst
+++ b/src/doc/en/developer/coding_in_other.rst
@@ -714,7 +714,7 @@ dumps the user into an Octave interactive shell:
 
             OUTPUT:
 
-            An list x (if it exists) which solves M*x = b
+            A list x (if it exists) which solves M*x = b
 
             EXAMPLES::
 
diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst
index 4ed1762efe6..96eb302e2d3 100644
--- a/src/doc/en/developer/packaging.rst
+++ b/src/doc/en/developer/packaging.rst
@@ -512,10 +512,15 @@ containing files with names like ::
     arch.txt
     conda.txt
     debian.txt
+    fedora.txt
     homebrew.txt
     ...
 
-corresponding to different packaging systems.
+corresponding to different packaging systems. Each system package
+should appear on a separate line. If the shell-style variable reference
+``${PYTHON_MINOR}`` appears, it is replaced by the minor version of
+Python, e.g., 12 for Python 3.12.x. Everything on a line after a ``#``
+character is ignored, so comments can be included in the files.
 
 For example, if ``./configure`` detects that the Homebrew packaging
 system is in use, and if the current package can be provided by a
@@ -1048,9 +1053,11 @@ Sage mirrors when a new release is prepared.  On GitHub PRs
 upgrading a package, the PR description should no longer contain
 the upstream URL to avoid duplication of information.
 
-Note that, like the ``tarball`` field, the ``tpstream_url`` is a
+Note that, like the ``tarball`` field, the ``upstream_url`` is a
 template; the substring ``VERSION`` is substituted with the actual
-version.
+version. It can also be written as ``${VERSION}``, and it is possible
+to refer to the dot-separated components of a version by ``VERSION_MAJOR``,
+``VERSION_MINOR``, and ``VERSION_MICRO``.
 
 For Python packages available from PyPI, you should use an
 ``upstream_url`` from ``pypi.io``, which follows the format
diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst
index 44a9214b9fe..b2bc38353cb 100644
--- a/src/doc/en/developer/review.rst
+++ b/src/doc/en/developer/review.rst
@@ -134,7 +134,7 @@ the patches have been applied to a given release, the issue would
 remain in limbo.
 
 **No Patch Bombs**: Code that goes into Sage is peer-reviewed. If
-you show up with an 80,000 lines of code bundle that completely
+you show up with 80,000 lines of code bundle that completely
 rips out a subsystem and replaces it with something else, you can
 imagine that the review process will be a little tedious. These
 huge patch bombs are problematic for several reasons and we prefer
diff --git a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
index c6f11452ce8..101cbcdb4e7 100644
--- a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
+++ b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
@@ -83,15 +83,13 @@ rather than just a list of numbers.  This can be very powerful.
 
 ::
 
-    sage: for K in D.normal_subgroups():
+    sage: len(D.normal_subgroups())
+    7
+    sage: for K in sorted(D.normal_subgroups()):
     ....:     print(K)
-    Subgroup generated by [(1,2,3,4,5,6,7,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
-    Subgroup generated by [(1,2,3,4,5,6,7,8), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
-    Subgroup generated by [(1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
-    Subgroup generated by [(2,8)(3,7)(4,6), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
-    Subgroup generated by [(1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
-    Subgroup generated by [(1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
     Subgroup generated by [()] of (Dihedral group of order 16 as a permutation group)
+    ...
+    Subgroup generated by [(1,2,3,4,5,6,7,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
 
 We can access specific subgroups if we know the generators as a
 permutation group.
diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst
index 23152ac0449..457eb90f14e 100644
--- a/src/doc/en/reference/algebras/lie_algebras.rst
+++ b/src/doc/en/reference/algebras/lie_algebras.rst
@@ -19,6 +19,7 @@ Lie Algebras
    sage/algebras/lie_algebras/poincare_birkhoff_witt
    sage/algebras/lie_algebras/quotient
    sage/algebras/lie_algebras/rank_two_heisenberg_virasoro
+   sage/algebras/lie_algebras/representation
    sage/algebras/lie_algebras/structure_coefficients
    sage/algebras/lie_algebras/subalgebra
    sage/algebras/lie_algebras/symplectic_derivation
diff --git a/src/doc/en/reference/coercion/index.rst b/src/doc/en/reference/coercion/index.rst
index 27459609dc9..73dfd978008 100644
--- a/src/doc/en/reference/coercion/index.rst
+++ b/src/doc/en/reference/coercion/index.rst
@@ -323,7 +323,7 @@ Methods to implement
   the function is called on the potential codomain.  To indicate that
   there is no coercion from S to R (self), return ``False`` or
   ``None``. This is the default behavior.  If there is a coercion,
-  return ``True`` (in which case an morphism using
+  return ``True`` (in which case a morphism using
   ``R._element_constructor_`` will be created) or an actual
   :class:`Morphism` object with S as the domain and R as the codomain.
 
@@ -386,87 +386,97 @@ but that would obscure the main points being made here.)
 
 ::
 
-    class Localization(Ring):
-       def __init__(self, primes):
-           """
-           Localization of `\ZZ` away from primes.
-           """
-           Ring.__init__(self, base=ZZ)
-           self._primes = primes
-           self._populate_coercion_lists_()
-
-       def _repr_(self):
-           """
-           How to print self.
-           """
-           return "%s localized at %s" % (self.base(), self._primes)
-
-       def _element_constructor_(self, x):
-           """
-           Make sure x is a valid member of self, and return the constructed element.
-           """
-           if isinstance(x, LocalizationElement):
-               x = x._value
-           else:
-               x = QQ(x)
-           for p, e in x.denominator().factor():
-               if p not in self._primes:
-                   raise ValueError("Not integral at %s" % p)
-           return LocalizationElement(self, x)
-
-       def _coerce_map_from_(self, S):
-           """
-           The only things that coerce into this ring are:
-
-           - the integer ring
-
-           - other localizations away from fewer primes
-           """
-           if S is ZZ:
-               return True
-           elif isinstance(S, Localization):
-               return all(p in self._primes for p in S._primes)
-
-
-    class LocalizationElement(RingElement):
-
-       def __init__(self, parent, x):
-           RingElement.__init__(self, parent)
-           self._value = x
-
-
-       # We're just printing out this way to make it easy to see what's going on in the examples.
-
-       def _repr_(self):
-           return "LocalElt(%s)" % self._value
-
-       # Now define addition, subtraction, and multiplication of elements.
-       # Note that left and right always have the same parent.
-
-       def _add_(left, right):
-           return LocalizationElement(left.parent(), left._value + right._value)
-
-       def _sub_(left, right):
-           return LocalizationElement(left.parent(), left._value - right._value)
-
-       def _mul_(left, right):
-           return LocalizationElement(left.parent(), left._value * right._value)
-
-       # The basering was set to ZZ, so c is guaranteed to be in ZZ
-
-       def _rmul_(self, c):
-           return LocalizationElement(self.parent(), c * self._value)
-
-       def _lmul_(self, c):
-           return LocalizationElement(self.parent(), self._value * c)
+    sage: from sage.structure.richcmp import richcmp
+    sage: from sage.structure.element import Element
+
+    sage: class MyLocalization(Parent):
+    ....:     def __init__(self, primes):
+    ....:         r"""
+    ....:         Localization of `\ZZ` away from primes.
+    ....:         """
+    ....:         Parent.__init__(self, base=ZZ, category=Rings().Commutative())
+    ....:         self._primes = primes
+    ....:         self._populate_coercion_lists_()
+    ....:
+    ....:     def _repr_(self) -> str:
+    ....:         """
+    ....:         How to print ``self``.
+    ....:         """
+    ....:         return "%s localized at %s" % (self.base(), self._primes)
+    ....:
+    ....:     def _element_constructor_(self, x):
+    ....:         """
+    ....:         Make sure ``x`` is a valid member of ``self``, and return the constructed element.
+    ....:         """
+    ....:         if isinstance(x, MyLocalizationElement):
+    ....:             x = x._value
+    ....:         else:
+    ....:             x = QQ(x)
+    ....:         for p, e in x.denominator().factor():
+    ....:             if p not in self._primes:
+    ....:                 raise ValueError("not integral at %s" % p)
+    ....:         return self.element_class(self, x)
+    ....:
+    ....:     def _an_element_(self):
+    ....:         return self.element_class(self, 6)
+    ....:
+    ....:     def _coerce_map_from_(self, S):
+    ....:         """
+    ....:         The only things that coerce into this ring are:
+    ....:
+    ....:         - the integer ring
+    ....:
+    ....:         - other localizations away from fewer primes
+    ....:         """
+    ....:         if S is ZZ:
+    ....:             return True
+    ....:         if isinstance(S, MyLocalization):
+    ....:             return all(p in self._primes for p in S._primes)
+
+    sage: class MyLocalizationElement(Element):
+    ....:
+    ....:     def __init__(self, parent, x):
+    ....:         Element.__init__(self, parent)
+    ....:         self._value = x
+    ....:
+    ....:     # We are just printing out this way to make it easy to see what's going on in the examples.
+    ....:
+    ....:     def _repr_(self) -> str:
+    ....:         return f"LocalElt({self._value})"
+    ....:
+    ....:     # Now define addition, subtraction and multiplication of elements.
+    ....:     # Note that self and right always have the same parent.
+    ....:
+    ....:     def _add_(self, right):
+    ....:         return self.parent()(self._value + right._value)
+    ....:
+    ....:     def _sub_(self, right):
+    ....:         return self.parent()(self._value - right._value)
+    ....:
+    ....:     def _mul_(self, right):
+    ....:         return self.parent()(self._value * right._value)
+    ....:
+    ....:     # The basering was set to ZZ, so c is guaranteed to be in ZZ
+    ....:
+    ....:     def _rmul_(self, c):
+    ....:         return self.parent()(c * self._value)
+    ....:
+    ....:     def _lmul_(self, c):
+    ....:         return self.parent()(self._value * c)
+    ....:
+    ....:     def _richcmp_(self, other, op):
+    ....:         return richcmp(self._value, other._value, op)
+
+    sage: MyLocalization.element_class = MyLocalizationElement
 
 That's all there is to it. Now we can test it out:
 
-.. skip
+.. link
 
 ::
 
-    sage: R = Localization([2]); R
+    sage: TestSuite(R).run(skip="_test_pickling")
+    sage: R = MyLocalization([2]); R
     Integer Ring localized at [2]
     sage: R(1)
     LocalElt(1)
@@ -475,12 +485,12 @@ That's all there is to it. Now we can test it out:
     sage: R(1/3)
     Traceback (most recent call last):
     ...
-    ValueError: Not integral at 3
+    ValueError: not integral at 3
 
     sage: R.coerce(1)
     LocalElt(1)
     sage: R.coerce(1/4)
-    Traceback (click to the left for traceback)
+    Traceback (most recent call last):
     ...
     TypeError: no canonical coercion from Rational Field to Integer Ring localized at [2]
 
@@ -497,12 +507,10 @@ That's all there is to it. Now we can test it out:
     sage: R(3/4) * 7
     LocalElt(21/4)
 
-    sage: R.get_action(ZZ)
-    Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
     sage: cm = sage.structure.element.get_coercion_model()
     sage: cm.explain(R, ZZ, operator.add)
     Coercion on right operand via
-       Conversion map:
+       Coercion map:
          From: Integer Ring
          To:   Integer Ring localized at [2]
     Arithmetic performed after coercions.
@@ -510,12 +518,15 @@ That's all there is to it. Now we can test it out:
     Integer Ring localized at [2]
 
     sage: cm.explain(R, ZZ, operator.mul)
-    Action discovered.
-       Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
+    Coercion on right operand via
+        Coercion map:
+          From: Integer Ring
+          To:   Integer Ring localized at [2]
+    Arithmetic performed after coercions.
     Result lives in Integer Ring localized at [2]
     Integer Ring localized at [2]
 
-    sage: R6 = Localization([2,3]); R6
+    sage: R6 = MyLocalization([2,3]); R6
     Integer Ring localized at [2, 3]
     sage: R6(1/3) - R(1/2)
     LocalElt(-1/6)
@@ -525,12 +536,12 @@ That's all there is to it. Now we can test it out:
     sage: R.has_coerce_map_from(ZZ)
     True
     sage: R.coerce_map_from(ZZ)
-    Conversion map:
+    Coercion map:
      From: Integer Ring
      To:   Integer Ring localized at [2]
 
     sage: R6.coerce_map_from(R)
-    Conversion map:
+    Coercion map:
      From: Integer Ring localized at [2]
      To:   Integer Ring localized at [2, 3]
 
@@ -539,7 +550,7 @@ That's all there is to it. Now we can test it out:
 
     sage: cm.explain(R, R6, operator.mul)
     Coercion on left operand via
-       Conversion map:
+       Coercion map:
          From: Integer Ring localized at [2]
          To:   Integer Ring localized at [2, 3]
     Arithmetic performed after coercions.
diff --git a/src/doc/en/reference/databases/index.rst b/src/doc/en/reference/databases/index.rst
index 56289fa5236..7cff08ee68a 100644
--- a/src/doc/en/reference/databases/index.rst
+++ b/src/doc/en/reference/databases/index.rst
@@ -1,68 +1,40 @@
 Databases
 =========
 
-There are numerous specific mathematical databases either included
-in Sage or available as optional packages. Also, Sage includes two
-powerful general database packages.
+Sage includes mathematical databases as standard or optional packages,
+and provides convenient interfaces for easy access to the databases.
 
-Sage includes the ZOPE object oriented database ZODB, which
-"is a Python object persistence system. It provides transparent object-oriented persistency."
+Standard databases
+------------------
 
-Sage also includes the powerful relational database SQLite, along
-with a Python interface to SQLite. SQlite is a small C library that
-implements a self-contained, embeddable, zero-configuration SQL
-database engine.
+These databases are available in the standard install of Sage or if Sage can access
+Internet.
 
+.. toctree::
+   :maxdepth: 1
 
--  Transactions are atomic, consistent, isolated, and durable
-   (ACID) even after system crashes and power failures.
-
--  Zero-configuration - no setup or administration needed.
-
--  Implements most of SQL92. (Features not supported)
-
--  A complete database is stored in a single disk file.
-
--  Database files can be freely shared between machines with
-   different byte orders.
-
--  Supports databases up to 2 tebibytes (2^41 bytes) in size.
-
--  Strings and BLOBs up to 2 gibibytes (2^31 bytes) in size.
-
--  Small code footprint: less than 250KiB fully configured or less
-   than 150KiB with optional features omitted.
-
--  Faster than popular client/server database engines for most
-   common operations.
-
--  Simple, easy to use API.
-
--  TCL bindings included. Bindings for many other languages
-   available separately.
-
--  Well-commented source code with over 95% test coverage.
+   sage/databases/conway
+   sage/databases/oeis
+   sage/databases/sloane
+   sage/databases/findstat
 
--  Self-contained: no external dependencies.
+Optional databases
+------------------
 
--  Sources are in the public domain. Use for any purpose.
+The following databases require you to install optional packages before full access.
 
 .. toctree::
    :maxdepth: 1
 
+   sage/databases/cunningham_tables
+   sage/databases/knotinfo_db
+   sage/databases/cubic_hecke_db
+   sage/databases/symbolic_data
    sage/databases/cremona
    sage/databases/stein_watkins
    sage/databases/jones
-   sage/databases/oeis
-   sage/databases/sloane
-   sage/databases/findstat
-   sage/databases/conway
-   sage/databases/odlyzko
-   sage/databases/symbolic_data
-   sage/databases/cunningham_tables
    sage/databases/db_class_polynomials
    sage/databases/db_modular_polynomials
-   sage/databases/knotinfo_db
-   sage/databases/cubic_hecke_db
+   sage/databases/odlyzko
 
 .. include:: ../footer.txt
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index d810bed4869..3f207ec0539 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -4520,6 +4520,10 @@ REFERENCES:
 .. [Mat2002] Jiří Matousek, "Lectures on Discrete Geometry", Springer,
              2002
 
+.. [Mathas2004] Andrew Mathas.
+                *Matrix units and generic degrees for the Ariki-Koike algebras*.
+                J. Algebra. **281** (2004) pp. 695-730.
+
 .. [Mas1995] Mason, Geoffrey. *The quantum double of a finite group and its role
              in conformal field theory*. Groups '93 Galway/St. Andrews, Vol. 2,
              405-417, London Math. Soc. Lecture Note Ser., 212, Cambridge, 1995.
diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst
index d1a2bda863b..6fe991abf45 100644
--- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst
+++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst
@@ -5,7 +5,7 @@ Cremona's Databases
 -------------------
 
 Cremona's databases of elliptic curves are part of Sage. The curves up
-to conductor 10,000 come standard with Sage, and an there is an optional
+to conductor 10,000 come standard with Sage, and there is an optional
 download to gain access to his complete tables. From a shell, you
 should run ::
 
diff --git a/src/doc/en/thematic_tutorials/numerical_sage/f2py.rst b/src/doc/en/thematic_tutorials/numerical_sage/f2py.rst
index 78cea180e2f..57fd4f1b977 100644
--- a/src/doc/en/thematic_tutorials/numerical_sage/f2py.rst
+++ b/src/doc/en/thematic_tutorials/numerical_sage/f2py.rst
@@ -204,7 +204,7 @@ you only care about its value before the function is called not
 afterwards. So in the above n tells us how many fiboncci numbers to
 compute we need to specify this as an input, however we don't need
 to get n back as it doesn't contain anything new. Similarly A is
-intent(out) so we don't need A to have an specific value
+intent(out) so we don't need A to have a specific value
 beforehand, we just care about the contents afterwards. F2py
 generates a Python function so you only pass those declared
 intent(in) and supplies empty workspaces for the remaining
diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst
index 0d7907014b7..9c5c0919bcb 100644
--- a/src/doc/en/thematic_tutorials/sandpile.rst
+++ b/src/doc/en/thematic_tutorials/sandpile.rst
@@ -2865,7 +2865,7 @@ SandpileConfig
 
 **\***
 
-   If ``other`` is an configuration, the recurrent element equivalent
+   If ``other`` is a configuration, the recurrent element equivalent
    to the sum.  If ``other`` is an integer, the sum of configuration with
    itself ``other`` times.
 
diff --git a/src/doc/fr/tutorial/conf.py b/src/doc/fr/tutorial/conf.py
index 7208ddada17..f44315072ff 100644
--- a/src/doc/fr/tutorial/conf.py
+++ b/src/doc/fr/tutorial/conf.py
@@ -39,9 +39,6 @@
    'The Sage Group', 'manual'),
 ]
 
-# Additional LaTeX stuff for the French version
-#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n'
-
 # the definition of \\at in the standard preamble of the sphinx doc
 # conflicts with that in babel/french[b]
 latex_elements['preamble'] += '\\let\\at\\undefined'
diff --git a/src/doc/ja/a_tour_of_sage/conf.py b/src/doc/ja/a_tour_of_sage/conf.py
index bf1f84bf962..451d7cc5b58 100644
--- a/src/doc/ja/a_tour_of_sage/conf.py
+++ b/src/doc/ja/a_tour_of_sage/conf.py
@@ -25,6 +25,10 @@
 name = 'a_tour_of_sage'
 language = "ja"
 
+# The LaTeX engine to build the docs in Japanese.
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-latex_engine
+latex_engine = 'uplatex'
+
 # The name for this set of Sphinx documents.  If None, it defaults to
 # " v documentation".
 html_title = project + " v" + release
diff --git a/src/doc/ja/tutorial/conf.py b/src/doc/ja/tutorial/conf.py
index 9521bc91828..e4b79bd7875 100644
--- a/src/doc/ja/tutorial/conf.py
+++ b/src/doc/ja/tutorial/conf.py
@@ -25,6 +25,10 @@
 name = 'tutorial-jp'
 language = "ja"
 
+# The LaTeX engine to build the docs in Japanese.
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-latex_engine
+latex_engine = 'uplatex'
+
 # The name for this set of Sphinx documents.  If None, it defaults to
 # " v documentation".
 html_title = project + " v" + release
diff --git a/src/doc/ru/tutorial/conf.py b/src/doc/ru/tutorial/conf.py
index 0b7a8c4e3db..cda4d9762ed 100644
--- a/src/doc/ru/tutorial/conf.py
+++ b/src/doc/ru/tutorial/conf.py
@@ -38,6 +38,3 @@
   ('index', 'SageTutorial_ru.tex', 'Sage Tutorial in Russian',
    'The Sage Development Team', 'manual'),
 ]
-
-# Additional LaTeX stuff if necessary:
-#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n'
diff --git a/src/pyproject.toml.m4 b/src/pyproject.toml.m4
index 2b61ea041ea..2d60708affa 100644
--- a/src/pyproject.toml.m4
+++ b/src/pyproject.toml.m4
@@ -7,7 +7,7 @@ requires = [
     # https://github.com/pypa/pip/issues/6144
      esyscmd(`sage-get-system-packages install-requires-toml \
         sage_conf      \
-        setuptools_wheel \
+        setuptools \
         wheel          \
         sage_setup     \
         cypari         \
diff --git a/src/requirements.txt.m4 b/src/requirements.txt.m4
index 346ea3c6301..34c42860cf1 100644
--- a/src/requirements.txt.m4
+++ b/src/requirements.txt.m4
@@ -32,7 +32,6 @@ dnl ... already needed by sage.env
 pkgconfig==esyscmd(`printf $(sed "s/[.]p.*//;" ../pkgconfig/package-version.txt)')
 pplpy==esyscmd(`printf $(sed "s/[.]p.*//;" ../pplpy/package-version.txt)')
 primecountpy==esyscmd(`printf $(sed "s/[.]p.*//;" ../primecountpy/package-version.txt)')
-pycygwin==esyscmd(`printf $(sed "s/[.]p.*//;" ../pycygwin/package-version.txt)'); sys_platform == 'cygwin'
 requests==esyscmd(`printf $(sed "s/[.]p.*//;" ../requests/package-version.txt)')
 typing_extensions==esyscmd(`printf $(sed "s/[.]p.*//;" ../typing_extensions/package-version.txt)')
 
diff --git a/src/sage/algebras/cellular_basis.py b/src/sage/algebras/cellular_basis.py
index c80eee401ff..8c474420378 100644
--- a/src/sage/algebras/cellular_basis.py
+++ b/src/sage/algebras/cellular_basis.py
@@ -164,7 +164,7 @@ class CellularBasis(CombinatorialFreeModule):
          - C([2, 1], [[1, 3], [2]], [[1, 3], [2]])
          + C([3], [[1, 2, 3]], [[1, 2, 3]])
     """
-    def __init__(self, A):
+    def __init__(self, A, to_algebra=None, from_algebra=None, **kwargs):
         r"""
         Initialize ``self``.
 
@@ -181,21 +181,26 @@ def __init__(self, A):
 
         # TODO: Use instead A.category().Realizations() so
         #   operations are defined by coercion?
+        prefix = kwargs.pop('prefix', 'C')
         cat = Algebras(A.category().base_ring()).FiniteDimensional().WithBasis().Cellular()
         CombinatorialFreeModule.__init__(self, A.base_ring(), I,
-                                         prefix='C', bracket=False,
-                                         category=cat)
+                                         prefix=prefix, bracket=False,
+                                         category=cat, **kwargs)
 
         # Register coercions
-        if A._to_cellular_element is not NotImplemented:
-            to_cellular = A.module_morphism(A._to_cellular_element, codomain=self,
+        if from_algebra is None:
+            from_algebra = A._to_cellular_element
+        if to_algebra is None:
+            to_algebra = A._from_cellular_index
+        if from_algebra is not NotImplemented:
+            to_cellular = A.module_morphism(from_algebra, codomain=self,
                                             category=cat)
-        if A._from_cellular_index is NotImplemented:
+        if to_algebra is NotImplemented:
             from_cellular = ~to_cellular
         else:
-            from_cellular = self.module_morphism(A._from_cellular_index, codomain=A,
+            from_cellular = self.module_morphism(to_algebra, codomain=A,
                                                  category=cat)
-            if A._to_cellular_element is NotImplemented:
+            if from_algebra is NotImplemented:
                 to_cellular = ~from_cellular
         to_cellular.register_as_coercion()
         from_cellular.register_as_coercion()
diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py
index 35facfa7866..a65581f8db1 100644
--- a/src/sage/algebras/commutative_dga.py
+++ b/src/sage/algebras/commutative_dga.py
@@ -3087,7 +3087,7 @@ def is_cohomologous_to(self, other):
 
         def cohomology_class(self):
             r"""
-            Return the cohomology class of an homogeneous cycle, as an element
+            Return the cohomology class of a homogeneous cycle, as an element
             of the corresponding cohomology group.
 
             EXAMPLES::
diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py
index 5a3bd2ceb47..4f2a604aa8b 100644
--- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py
+++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py
@@ -1458,7 +1458,7 @@ def one_basis(self):
     def _an_element_(self):
         r"""
         Overwrite the original method from :mod:`~sage.combinat.free_module`
-        to obtain an more interesting element for ``TestSuite``.
+        to obtain a more interesting element for ``TestSuite``.
 
         EXAMPLES::
 
@@ -2233,7 +2233,7 @@ def _braid_image_by_basis_extension(self, braid_tietze):
             sage: CHA5._basis_extension
             [[4], [-4], [4, 1]]
 
-        case where the braid already has an corresponding basis element::
+        case where the braid already has a corresponding basis element::
 
             sage: CHA5._braid_image_by_basis_extension((1,)) # optional - database_cubic_hecke
             c0
diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pxd b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd
index 9005eae72fd..227ce2559f9 100644
--- a/src/sage/algebras/lie_algebras/lie_algebra_element.pxd
+++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd
@@ -50,6 +50,7 @@ cdef class LieObject(SageObject):
 
 cdef class LieGenerator(LieObject):
     cdef public str _name
+    cpdef lift(self, dict UEA_gens_dict)
 
 cdef class LieBracket(LieObject):
     cdef public LieObject _left
diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx
index f221fe6ab55..ca53753153b 100644
--- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx
+++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx
@@ -1232,9 +1232,9 @@ cdef class UntwistedAffineLieAlgebraElement(Element):
         return type(self)(self._parent, negate(self._t_dict),
                           -self._c_coeff, -self._d_coeff)
 
-    cpdef _acted_upon_(self, x, bint self_on_left) noexcept:
+    cpdef _acted_upon_(self, scalar, bint self_on_left) noexcept:
         """
-        Return ``self`` acted upon by ``x``.
+        Return ``self`` acted upon by ``scalar``.
 
         EXAMPLES::
 
@@ -1246,9 +1246,21 @@ cdef class UntwistedAffineLieAlgebraElement(Element):
             sage: -2 * x
             (-2*E[alpha[1]])#t^0 + (-2*h1)#t^-1 + -6*c + 4/5*d
         """
-        return type(self)(self._parent, scal(x, self._t_dict, self_on_left),
-                          x * self._c_coeff,
-                          x * self._d_coeff)
+        # This was copied and IDK if it still applies (TCS):
+        # With the current design, the coercion model does not have
+        # enough information to detect apriori that this method only
+        # accepts scalars; so it tries on some elements(), and we need
+        # to make sure to report an error.
+        scalar_parent = parent(scalar)
+        if scalar_parent != self._parent.base_ring():
+            # Temporary needed by coercion (see Polynomial/FractionField tests).
+            if self._parent.base_ring().has_coerce_map_from(scalar_parent):
+                scalar = self._parent.base_ring()(scalar)
+            else:
+                return None
+        return type(self)(self._parent, scal(scalar, self._t_dict, self_on_left),
+                          scalar * self._c_coeff,
+                          scalar * self._d_coeff)
 
     cpdef monomial_coefficients(self, bint copy=True) noexcept:
         """
@@ -1661,6 +1673,25 @@ cdef class LieGenerator(LieObject):
         """
         return self._word
 
+    cpdef lift(self, dict UEA_gens_dict):
+        """
+        Lift ``self`` to the universal enveloping algebra.
+
+        ``UEA_gens_dict`` should be the dictionary for the
+        generators of the universal enveloping algebra.
+
+        EXAMPLES::
+
+            sage: L = LieAlgebra(QQ, 'x,y,z')
+            sage: Lyn = L.Lyndon()
+            sage: x,y,z = Lyn.gens()
+            sage: x.lift()
+            x
+            sage: x.lift().parent()
+            Free Algebra on 3 generators (x, y, z) over Rational Field
+        """
+        return UEA_gens_dict[self._name]
+
 cdef class LieBracket(LieObject):
     """
     An abstract Lie bracket (formally, just a binary tree).
diff --git a/src/sage/algebras/lie_algebras/representation.py b/src/sage/algebras/lie_algebras/representation.py
new file mode 100644
index 00000000000..9d96c42c6fc
--- /dev/null
+++ b/src/sage/algebras/lie_algebras/representation.py
@@ -0,0 +1,435 @@
+r"""
+Representations of Lie algebras
+
+AUTHORS:
+
+- Travis Scrimshaw (2023-08-31): initial version
+"""
+
+# ****************************************************************************
+#       Copyright (C) 2023 Travis Scrimshaw 
+#
+# 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
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#                  https://www.gnu.org/licenses/
+# ****************************************************************************
+
+from sage.sets.family import Family, AbstractFamily
+from sage.combinat.free_module import CombinatorialFreeModule
+from sage.categories.modules import Modules
+from copy import copy
+
+
+class Representation_abstract:
+    r"""
+    Mixin class for (left) representations of Lie algebras.
+
+    INPUT:
+
+    - ``lie_algebra`` -- a Lie algebra
+    """
+    def __init__(self, lie_algebra):
+        r"""
+        Initialize ``self``.
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.sp(QQ, 6)
+            sage: R = L.trivial_representation()
+            sage: TestSuite(R).run()
+        """
+        self._lie_algebra = lie_algebra
+
+    def lie_algebra(self):
+        r"""
+        Return the Lie algebra whose representation ``self`` is.
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.sl(QQ, 4)
+            sage: R = L.trivial_representation()
+            sage: R.lie_algebra() is L
+            True
+        """
+        return self._lie_algebra
+
+    def side(self):
+        r"""
+        Return that ``self`` is a left representation.
+
+        OUTPUT:
+
+        - the string ``"left"``
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.sl(QQ, 4)
+            sage: R = L.trivial_representation()
+            sage: R.side()
+            'left'
+        """
+        return 'left'
+
+    def _test_representation(self, **options):
+        r"""
+        Check (on some elements) that ``self`` is a representation of the
+        given Lie algebra using the basis of the Lie algebra.
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.Heisenberg(QQ, 3)
+            sage: f = {b: b.adjoint_matrix() for b in L.basis()}
+            sage: R = L.representation(f)
+            sage: R._test_representation()
+        """
+        tester = self._tester(**options)
+        S = tester.some_elements()
+        elts = self._lie_algebra.basis()
+        if elts.cardinality() == float('inf'):
+            elts = list(elts.some_elements())
+        from sage.misc.misc import some_tuples
+        for x, y in some_tuples(elts, 2, tester._max_runs):
+            for v in S:
+                tester.assertEqual(x.bracket(y) * v, x * (y * v) - y * (x * v))
+
+
+class RepresentationByMorphism(CombinatorialFreeModule, Representation_abstract):
+    r"""
+    Representation of a Lie algebra defined by a Lie algebra morphism.
+
+    INPUT:
+
+    - ``lie_algebra`` -- a Lie algebra
+    - ``f`` -- the Lie algebra morphism defining the action of the basis
+      elements of ``lie_algebra``
+    - ``index_set`` -- (optional) the index set of the module basis
+    - ``on_basis`` -- (default: ``False``) the function ``f`` defines a
+      map from the basis elements or from a generic element of ``lie_algebra``
+
+    If ``f`` is encoded as a ``dict`` or ``Family``, then the keys must
+    be indices of the basis of ``lie_algebra`` and the values being the
+    corresponding matrix defining the action. This sets ``on_basis=True``.
+
+    EXAMPLES::
+
+        sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+        sage: f = {x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}
+        sage: L.representation(f)
+        Representation of Lie algebra on 2 generators (x, y) over Rational Field defined by:
+               [1 0]
+        x |--> [0 0]
+               [0 1]
+        y |--> [0 0]
+
+    We construct the direct sum of two copies of the trivial representation
+    for an infinite dimensional Lie algebra::
+
+        sage: L = lie_algebras.Affine(QQ, ['E',6,1])
+        sage: R = L.representation(lambda b: matrix.zero(QQ, 2), index_set=['a','b'])
+        sage: x = L.an_element()
+        sage: v = R.an_element(); v
+        2*R['a'] + 2*R['b']
+        sage: x * v
+        0
+
+    We construct a finite dimensional representation of the affline Lie algebra
+    of type `A_2^{(1)}`::
+
+        sage: L = lie_algebras.Affine(QQ, ['A',2,1]).derived_subalgebra()
+        sage: Phi_plus = list(RootSystem(['A',2]).root_lattice().positive_roots())
+        sage: def aff_action(key):
+        ....:     mat = matrix.zero(QQ, 3)
+        ....:     if key == 'c':  # central element
+        ....:         return mat
+        ....:     b, ell = key
+        ....:     if b in Phi_plus:  # positive root
+        ....:         ind = tuple(sorted(b.to_ambient().support()))
+        ....:         mat[ind] = 1
+        ....:         if ind[0] + 1 != ind[1]:
+        ....:             mat[ind] = -1
+        ....:     elif -b in Phi_plus:  # negative root
+        ....:         ind = tuple(sorted(b.to_ambient().support(), reverse=True))
+        ....:         mat[ind] = 1
+        ....:         if ind[0] - 1 != ind[1]:
+        ....:             mat[ind] = -1
+        ....:     else:  # must be in the Cartan
+        ....:         i = b.leading_support()
+        ....:         mat[i,i] = -1
+        ....:         mat[i-1,i-1] = 1
+        ....:     return mat
+        sage: F = Family(L.basis(), aff_action, name="lifted natural repr")
+        sage: R = L.representation(index_set=range(1,4), on_basis=F)
+        sage: x = L.an_element(); x
+        (E[alpha[2]] + E[alpha[1]] + h1 + h2 + E[-alpha[2]] + E[-alpha[1]])#t^0
+         + (E[-alpha[1] - alpha[2]])#t^1 + (E[alpha[1] + alpha[2]])#t^-1 + c
+        sage: v = R.an_element(); v
+        2*R[1] + 2*R[2] + 3*R[3]
+        sage: x * v
+        R[1] + 5*R[2] - 3*R[3]
+        sage: R._test_representation()  # verify that it is a representation
+    """
+    @staticmethod
+    def __classcall_private__(cls, lie_algebra, f=None, index_set=None, on_basis=False, **kwargs):
+        r"""
+        Normalize inpute to ensure a unique representation.
+
+        EXAMPLES::
+
+            sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+            sage: f1 = {'x': Matrix([[1,0],[0,0]]), 'y': Matrix([[0,1],[0,0]])}
+            sage: R1 = L.representation(f1)
+            sage: f2 = Family({x: Matrix([[1,0],[0,0]]), y: Matrix(QQ, [[0,1],[0,0]])})
+            sage: R2 = L.representation(f2)
+            sage: R1 is R2
+            True
+
+        TESTS::
+
+            sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+            sage: f = {'x': Matrix([[1,0]]), 'y': Matrix([[0,1]])}
+            sage: L.representation(f)
+            Traceback (most recent call last):
+            ...
+            ValueError: all matrices must be square
+
+            sage: f = {'x': Matrix([[1,0],[0,0]]), 'y': Matrix([[0]])}
+            sage: L.representation(f)
+            Traceback (most recent call last):
+            ...
+            ValueError: all matrices must be square of size 2
+
+            sage: L.representation(index_set=[1,2,3])
+            Traceback (most recent call last):
+            ...
+            ValueError: either 'f' or 'on_basis' must be specified
+            sage: L.representation(on_basis=lambda x: QQ.zero())
+            Traceback (most recent call last):
+            ...
+            ValueError: the index set needs to be specified
+        """
+        from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
+        base = lie_algebra.base_ring()
+        C = Modules(base).WithBasis().FiniteDimensional()
+        C = C.or_subcategory(kwargs.pop('category', C))
+        B = lie_algebra.basis()
+        if not isinstance(on_basis, bool):
+            f = on_basis
+            on_basis = True
+        if isinstance(f, AbstractFamily):
+            if f.cardinality() < float('inf'):
+                f = dict(f)
+            on_basis = True
+        if isinstance(f, dict):
+            data = {}
+            dim = None
+            for k, mat in f.items():
+                if k in B:
+                    k = k.leading_support()
+                if not mat.is_square():
+                    raise ValueError("all matrices must be square")
+                if dim is None:
+                    dim = mat.nrows()
+                elif mat.nrows() != dim or mat.ncols() != dim:
+                    raise ValueError("all matrices must be square of size {}".format(dim))
+                data[k] = mat.change_ring(base)
+                data[k].set_immutable()
+
+            if index_set is None:
+                index_set = FiniteEnumeratedSet(range(dim))
+            f = Family(data)
+            on_basis = True
+
+        if f is None:
+            raise ValueError("either 'f' or 'on_basis' must be specified")
+        if index_set is None:
+            raise ValueError("the index set needs to be specified")
+
+        index_set = FiniteEnumeratedSet(index_set)
+
+        return super(cls, RepresentationByMorphism).__classcall__(cls, lie_algebra,
+             f, index_set, on_basis, category=C, **kwargs)
+
+    def __init__(self, lie_algebra, f, index_set, on_basis, category, **kwargs):
+        r"""
+        Initialize ``self``.
+
+        EXAMPLES::
+
+            sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+            sage: f = {'x': Matrix([[1,0],[0,0]]), 'y': Matrix([[0,1],[0,0]])}
+            sage: R = L.representation(f)
+            sage: TestSuite(R).run()
+        """
+        if on_basis:
+            self._family = f
+            self._f = f.__getitem__
+        else:
+            self._f = f
+        prefix = kwargs.pop("prefix", 'R')
+        self._on_basis = on_basis
+
+        Representation_abstract.__init__(self, lie_algebra)
+        CombinatorialFreeModule.__init__(self, lie_algebra.base_ring(), index_set,
+                                         category=category, prefix=prefix, **kwargs)
+
+    def _repr_(self):
+        r"""
+        Return a string representation of ``self``.
+
+        EXAMPLES::
+
+            sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+            sage: f = {'x': Matrix([[1,0],[0,0]]), 'y': Matrix([[0,1],[0,0]])}
+            sage: L.representation(f)
+            Representation of Lie algebra on 2 generators (x, y) over Rational Field defined by:
+                   [1 0]
+            x |--> [0 0]
+                   [0 1]
+            y |--> [0 0]
+
+            sage: L = lie_algebras.Affine(QQ, ['E',6,1])
+            sage: F = Family(L.basis(), lambda b: matrix.zero(QQ, 2), name="zero map")
+            sage: L.representation(F, index_set=['a','b'], on_basis=True)
+            Representation of Affine Kac-Moody algebra of ['E', 6] in the Chevalley basis defined by:
+            Lazy family (zero map(i))_{i in Lazy family...}
+
+            sage: L.representation(lambda b: matrix.zero(QQ, 2), index_set=['a','b'])
+            Representation of Affine Kac-Moody algebra of ['E', 6] in the Chevalley basis defined by:
+             at 0x...>
+        """
+        ret = "Representation of {} defined by:".format(self._lie_algebra)
+        from sage.typeset.ascii_art import ascii_art
+        if self._on_basis:
+            B = self._lie_algebra.basis()
+            if B.cardinality() < float('inf'):
+                for k in B.keys():
+                    ret += '\n' + repr(ascii_art(B[k], self._f(k), sep=" |--> ", sep_baseline=0))
+            else:
+                ret += '\n' + repr(self._family)
+        else:
+            ret += '\n' + repr(self._f)
+        return ret
+
+    class Element(CombinatorialFreeModule.Element):
+        def _acted_upon_(self, scalar, self_on_left=False):
+            r"""
+            Return the action of ``scalar`` on ``self``.
+
+            EXAMPLES::
+
+                sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+                sage: f = {'x': Matrix([[1,0],[0,0]]), 'y': Matrix([[0,1],[0,0]])}
+                sage: R = L.representation(f)
+                sage: v = R.an_element(); v
+                2*R[0] + 2*R[1]
+                sage: x * v
+                2*R[0]
+                sage: y * v
+                2*R[0]
+                sage: (2*x + 5*y) * v
+                14*R[0]
+                sage: v * x
+                Traceback (most recent call last):
+                ...
+                TypeError: unsupported operand parent(s) for *: ...
+
+                sage: v = sum((i+4) * b for i, b in enumerate(R.basis())); v
+                4*R[0] + 5*R[1]
+                sage: (1/3*x - 5*y) * v
+                -71/3*R[0]
+
+                sage: L = lie_algebras.Affine(QQ, ['E',6,1])
+                sage: F = Family(L.basis(), lambda b: matrix.zero(QQ, 2), name="zero map")
+                sage: R = L.representation(F, index_set=['a','b'], on_basis=True)
+                sage: R.an_element()
+                2*R['a'] + 2*R['b']
+                sage: L.an_element() * R.an_element()
+                0
+            """
+            P = self.parent()
+            if scalar in P._lie_algebra:
+                if self_on_left:
+                    return None
+                if not self:  # we are (already) the zero vector
+                    return self
+                scalar = P._lie_algebra(scalar)
+                if not scalar:  # we are acting by zero
+                    return P.zero()
+                if P._on_basis:
+                    mat = sum(c * P._f(k) for k, c in scalar.monomial_coefficients(copy=False).items())
+                else:
+                    mat = P._f(scalar)
+                return P.from_vector(mat * self.to_vector())
+
+            return super()._acted_upon_(scalar, self_on_left)
+
+
+class TrivialRepresentation(CombinatorialFreeModule, Representation_abstract):
+    r"""
+    The trivial representation of a Lie algebra.
+
+    The trivial representation of a Lie algebra `L` over a commutative ring
+    `R` is the `1`-dimensional `R`-module on which every element of `L`
+    acts by zero.
+
+    INPUT:
+
+    - ``lie_algebra`` -- a Lie algebra
+
+    REFERENCES:
+
+    - :wikipedia:`Trivial_representation`
+    """
+    def __init__(self, lie_algebra, **kwargs):
+        r"""
+        Initialize ``self``.
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.VirasoroAlgebra(QQ)
+            sage: R = L.trivial_representation()
+            sage: TestSuite(R).run()
+        """
+        R = lie_algebra.base_ring()
+        cat = Modules(R).WithBasis().FiniteDimensional()
+        Representation_abstract.__init__(self, lie_algebra)
+        CombinatorialFreeModule.__init__(self, R, ['v'], prefix='T', category=cat, **kwargs)
+
+    def _repr_(self):
+        r"""
+        Return a string representation of ``self``.
+
+        EXAMPLES::
+
+            sage: L = lie_algebras.VirasoroAlgebra(QQ)
+            sage: L.trivial_representation()
+            Trivial representation of The Virasoro algebra over Rational Field
+        """
+        return "Trivial representation of {}".format(self._lie_algebra)
+
+    class Element(CombinatorialFreeModule.Element):
+        def _acted_upon_(self, scalar, self_on_left=False):
+            r"""
+            Return the action of ``scalar`` on ``self``.
+
+            EXAMPLES::
+
+                sage: L = lie_algebras.VirasoroAlgebra(QQ)
+                sage: R = L.trivial_representation()
+                sage: L.an_element() * R.an_element()
+                0
+                sage: R.an_element() * L.an_element()
+                Traceback (most recent call last):
+                ...
+                TypeError: unsupported operand parent(s) for *: ...
+                sage: 3 / 5 * R.an_element()
+                6/5*T['v']
+            """
+            P = self.parent()
+            if scalar in P._lie_algebra:
+                if self_on_left:
+                    return None
+                return P.zero()
+            return super()._acted_upon_(scalar, self_on_left)
diff --git a/src/sage/algebras/quantum_groups/fock_space.py b/src/sage/algebras/quantum_groups/fock_space.py
index 3daf03099d5..eb60fd89788 100644
--- a/src/sage/algebras/quantum_groups/fock_space.py
+++ b/src/sage/algebras/quantum_groups/fock_space.py
@@ -166,7 +166,7 @@ class FockSpace(Parent, UniqueRepresentation):
 
     To go between the canonical basis and the natural basis, for level 1
     Fock space, we follow the LLT algorithm [LLT1996]_. Indeed, we first
-    construct an basis `\{ A(\nu) \}` that is an approximation to the
+    construct a basis `\{ A(\nu) \}` that is an approximation to the
     lower global crystal basis, in the sense that it is bar-invariant,
     and then use Gaussian elimination to construct the lower global
     crystal basis. For higher level Fock space, we follow [Fayers2010]_,
diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py
index 5d3ef49f7c5..22fa53b5a29 100644
--- a/src/sage/arith/misc.py
+++ b/src/sage/arith/misc.py
@@ -3696,6 +3696,12 @@ def binomial(x, m, **kwds):
         0
         sage: binomial(RealField()('2.5'), 2)                                           # needs sage.rings.real_mpfr
         1.87500000000000
+        sage: binomial(Zp(5)(99),50)
+        3 + 4*5^3 + 2*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 5^9 + 2*5^10 + 3*5^11 +
+        4*5^12 + 4*5^13 + 2*5^14 + 3*5^15 + 3*5^16 + 4*5^17 + 4*5^18 + 2*5^19 + O(5^20)
+        sage: binomial(Qp(3)(2/3),2)
+        2*3^-2 + 2*3^-1 + 2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 +
+        2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + O(3^18)
         sage: n = var('n'); binomial(n, 2)                                              # needs sage.symbolic
         1/2*(n - 1)*n
         sage: n = var('n'); binomial(n, n)                                              # needs sage.symbolic
@@ -3792,6 +3798,27 @@ def binomial(x, m, **kwds):
         sage: binomial(n,2)                                                             # needs sage.symbolic
         1/2*(n - 1)*n
 
+    Test p-adic numbers::
+
+        sage: binomial(Qp(3)(-1/2),4) # p-adic number with valuation >= 0
+        1 + 3 + 2*3^2 + 3^3 + 2*3^4 + 3^6 + 3^7 + 3^8 + 3^11 + 2*3^14 + 2*3^16 + 2*3^17 + 2*3^19 + O(3^20)
+
+    Check that :trac:`35811` is fixed::
+
+        sage: binomial(Qp(3)(1/3),4) # p-adic number with negative valuation
+        2*3^-5 + 2*3^-4 + 3^-3 + 2*3^-2 + 2*3^-1 + 2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 +
+        2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + O(3^15)
+
+        sage: binomial(Qp(3)(1/3),10).parent()
+        3-adic Field with capped relative precision 20
+
+        sage: F.=Qq(9); binomial(w,4) # p-adic extension field
+        (w + 2)*3^-1 + (w + 1) + (2*w + 1)*3 + 2*w*3^2 + (2*w + 2)*3^3 + 2*w*3^4 + (2*w + 2)*3^5 +
+        2*w*3^6 + (2*w + 2)*3^7 + 2*w*3^8 + (2*w + 2)*3^9 + 2*w*3^10 + (2*w + 2)*3^11 + 2*w*3^12 +
+        (2*w + 2)*3^13 + 2*w*3^14 + (2*w + 2)*3^15 + 2*w*3^16 + (2*w + 2)*3^17 + 2*w*3^18 + O(3^19)
+        sage: F.=Qq(9); binomial(w,10).parent()
+        3-adic Unramified Extension Field in w defined by x^2 + 2*x + 2
+
     Invalid inputs::
 
         sage: x = polygen(ZZ)
@@ -3872,7 +3899,7 @@ def binomial(x, m, **kwds):
     # case 2: conversion to integers
     try:
         x = ZZ(x)
-    except TypeError:
+    except (TypeError, ValueError):
         pass
     else:
         # Check invertibility of factorial(m) in P
diff --git a/src/sage/calculus/interpolators.pyx b/src/sage/calculus/interpolators.pyx
index fb057f0fc2e..0e1f5fc7209 100644
--- a/src/sage/calculus/interpolators.pyx
+++ b/src/sage/calculus/interpolators.pyx
@@ -55,7 +55,7 @@ def polygon_spline(pts):
         ....:                 [lambda x: ps.derivative(real(x))], 0)
         sage: show(m.plot_colored() + m.plot_spiderweb())                               # needs sage.plot
 
-    Polygon approximation of an circle::
+    Polygon approximation of a circle::
 
         sage: pts = [e^(I*t / 25) for t in range(25)]
         sage: ps = polygon_spline(pts)
diff --git a/src/sage/calculus/ode.pyx b/src/sage/calculus/ode.pyx
index 32ccfab5dfc..d7d0fc133f8 100644
--- a/src/sage/calculus/ode.pyx
+++ b/src/sage/calculus/ode.pyx
@@ -37,27 +37,27 @@ cdef class PyFunctionWrapper:
         self.y_n = x
 
 cdef class ode_system:
-    cdef int  c_j(self,double t, double *y, double *dfdy,double *dfdt) noexcept: #void *params):
+    cdef int c_j(self, double t, double *y, double *dfdy, double *dfdt) noexcept:
         return 0
 
-    cdef int c_f(self,double t, double* y, double* dydt) noexcept:  #void *params):
+    cdef int c_f(self, double t, double* y, double* dydt) noexcept:
         return 0
 
-cdef int c_jac_compiled(double t, double *y, double *dfdy,double *dfdt, void * params) noexcept:
+cdef int c_jac_compiled(double t, const double *y, double *dfdy, double *dfdt, void *params) noexcept:
     cdef int status
     cdef ode_system wrapper
     wrapper =  params
-    status = wrapper.c_j(t,y,dfdy,dfdt)    #Could add parameters
+    status = wrapper.c_j(t, y, dfdy, dfdt)    # Could add parameters
     return status
 
-cdef int c_f_compiled(double t, double *y, double *dydt, void *params) noexcept:
+cdef int c_f_compiled(double t, const double *y, double *dydt, void *params) noexcept:
     cdef int status
     cdef ode_system wrapper
     wrapper =  params
-    status =  wrapper.c_f(t,y,dydt)  #Could add parameters
+    status =  wrapper.c_f(t, y, dydt)  # Could add parameters
     return status
 
-cdef int c_jac(double t,double *y,double *dfdy,double *dfdt,void *params) noexcept:
+cdef int c_jac(double t, const double *y, double *dfdy, double *dfdt, void *params) noexcept:
     cdef int i
     cdef int j
     cdef int y_n
@@ -84,7 +84,7 @@ cdef int c_jac(double t,double *y,double *dfdy,double *dfdt,void *params) noexce
     except Exception:
         return -1
 
-cdef int c_f(double t,double* y, double* dydt,void *params) noexcept:
+cdef int c_f(double t, const double *y, double *dydt, void *params) noexcept:
     cdef int i
     cdef int y_n
     cdef int param_n
diff --git a/src/sage/calculus/test_sympy.py b/src/sage/calculus/test_sympy.py
index b99d5d7857e..0c360f8acab 100644
--- a/src/sage/calculus/test_sympy.py
+++ b/src/sage/calculus/test_sympy.py
@@ -156,7 +156,7 @@
     
     sage: t1, t2
     (omega + x, omega + x)
-    sage: e = sympy.sin(var("y"))+sage.all.cos(sympy.Symbol("x"))
+    sage: e = sympy.sin(var("y")) + sage.functions.trig.cos(sympy.Symbol("x"))
     sage: type(e)
     
     sage: e
@@ -166,7 +166,7 @@
     
     sage: e
     cos(x) + sin(y)
-    sage: e = sage.all.cos(var("y")**3)**4+var("x")**2
+    sage: e = sage.functions.trig.cos(var("y")**3)**4+var("x")**2
     sage: e = e._sympy_()
     sage: e
     x**2 + cos(y**3)**4
diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py
index dfd693a9520..0d1f72eb17c 100644
--- a/src/sage/categories/algebras_with_basis.py
+++ b/src/sage/categories/algebras_with_basis.py
@@ -1,13 +1,13 @@
 r"""
 Algebras With Basis
 """
-#*****************************************************************************
+# ****************************************************************************
 #  Copyright (C) 2008      Teresa Gomez-Diaz (CNRS) 
 #                2008-2013 Nicolas M. Thiery 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
 #                  https://www.gnu.org/licenses/
-#******************************************************************************
+# *****************************************************************************
 
 from sage.categories.cartesian_product import CartesianProductsCategory
 from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring
@@ -106,7 +106,7 @@ class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring):
         sage: TestSuite(AlgebrasWithBasis(QQ)).run()
     """
 
-    def example(self, alphabet=('a','b','c')):
+    def example(self, alphabet=('a', 'b', 'c')):
         """
         Return an example of algebra with basis.
 
@@ -259,7 +259,7 @@ def one_from_cartesian_product_of_one_basis(self):
                     ....:                    SymmetricGroupAlgebra(QQ, 4)]).one()
                     B[(0, [1, 2, 3])] + B[(1, [1, 2, 3, 4])]
                 """
-                return self.sum_of_monomials( zip( self._sets_keys(), (set.one_basis() for set in self._sets)) )
+                return self.sum_of_monomials(zip(self._sets_keys(), (set.one_basis() for set in self._sets)))
 
             @lazy_attribute
             def one(self):
@@ -370,7 +370,8 @@ def product_on_basis(self, t1, t2):
 
                 TODO: optimize this implementation!
                 """
-                return tensor( (module.monomial(x1)*module.monomial(x2) for (module, x1, x2) in zip(self._sets, t1, t2)) ) #.
+                return tensor(module.monomial(x1) * module.monomial(x2)
+                              for module, x1, x2 in zip(self._sets, t1, t2))
 
         class ElementMethods:
             """
diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py
index 54e0df67278..87b66a6615f 100644
--- a/src/sage/categories/category.py
+++ b/src/sage/categories/category.py
@@ -565,7 +565,7 @@ def an_instance(cls):
     def __call__(self, x, *args, **opts):
         """
         Construct an object in this category from the data in ``x``,
-        or throw ``TypeError`` or ``NotImplementedError``.
+        or throw :class:`TypeError` or :class:`NotImplementedError`.
 
         If ``x`` is readily in ``self`` it is returned unchanged.
         Categories wishing to extend this minimal behavior should
@@ -583,7 +583,7 @@ def __call__(self, x, *args, **opts):
     def _call_(self, x):
         """
         Construct an object in this category from the data in ``x``,
-        or throw ``NotImplementedError``.
+        or throw :class:`NotImplementedError`.
 
         EXAMPLES::
 
diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py
index bb9dbbe2347..20668629649 100644
--- a/src/sage/categories/category_with_axiom.py
+++ b/src/sage/categories/category_with_axiom.py
@@ -500,7 +500,7 @@ class from the base category class::
       (bilinearity). Of course this should be implemented at the level
       of :class:`~.magmatic_algebras.MagmaticAlgebras`, if not higher.
 
-    - :class:`Bialgebras`: defining an bialgebra as an algebra and
+    - :class:`Bialgebras`: defining a bialgebra as an algebra and
       coalgebra where the coproduct is a morphism for the product.
 
     - :class:`Bimodules`: defining a bimodule as a left and right
@@ -2667,7 +2667,7 @@ def Blue_extra_super_categories(self):
         This currently fails because ``Blahs`` is the category where
         the axiom ``Blue`` is defined, and the specifications
         currently impose that a category defining an axiom should also
-        implement it (here in an category with axiom
+        implement it (here in a category with axiom
         ``Blahs.Blue``). In practice, due to this violation of the
         specifications, the axiom is lost during the join calculation.
 
diff --git a/src/sage/categories/complex_reflection_groups.py b/src/sage/categories/complex_reflection_groups.py
index 925ee1c228e..d9d30d41e40 100644
--- a/src/sage/categories/complex_reflection_groups.py
+++ b/src/sage/categories/complex_reflection_groups.py
@@ -21,7 +21,7 @@ class ComplexReflectionGroups(Category_singleton):
     The category of complex reflection groups.
 
     Let `V` be a complex vector space. A *complex reflection* is an
-    element of `\operatorname{GL}(V)` fixing an hyperplane pointwise
+    element of `\operatorname{GL}(V)` fixing a hyperplane pointwise
     and acting by multiplication by a root of unity on a complementary
     line.
 
diff --git a/src/sage/categories/covariant_functorial_construction.py b/src/sage/categories/covariant_functorial_construction.py
index e64bbaa35aa..ce4909a7b63 100644
--- a/src/sage/categories/covariant_functorial_construction.py
+++ b/src/sage/categories/covariant_functorial_construction.py
@@ -96,7 +96,7 @@ class CovariantFunctorialConstruction(UniqueRepresentation, SageObject):
        specify information and generic operations for elements of this
        category.
 
-     - ``_functor_name`` - an string which specifies the name of the
+     - ``_functor_name`` - a string which specifies the name of the
        functor, and also (when relevant) of the method on parents and
        elements used for calling the construction.
 
diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py
index 972d3b4d716..f3ef677638d 100644
--- a/src/sage/categories/coxeter_groups.py
+++ b/src/sage/categories/coxeter_groups.py
@@ -701,7 +701,7 @@ def _test_reduced_word(self, **options):
             for x in tester.some_elements():
                 red = x.reduced_word()
                 tester.assertEqual(self.from_reduced_word(red), x)
-                tester.assertEqual(self.prod((s[i] for i in red)), x)
+                tester.assertEqual(self.prod(s[i] for i in red), x)
 
         def simple_projection(self, i, side='right', length_increasing=True):
             r"""
diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py
index ae900a93e66..c2a9b8de655 100644
--- a/src/sage/categories/crystals.py
+++ b/src/sage/categories/crystals.py
@@ -578,7 +578,7 @@ def _Hom_(self, Y, category=None, **options):
             The sole purpose of this method is to construct the homset
             as a :class:`~sage.categories.crystals.CrystalHomset`. If
             ``category`` is specified and is not a subcategory of
-            :class:`Crystals`, a ``TypeError`` is raised instead.
+            :class:`Crystals`, a :class:`TypeError` is raised instead.
 
             This method is not meant to be called directly. Please use
             :func:`sage.categories.homset.Hom` instead.
diff --git a/src/sage/categories/discrete_valuation.py b/src/sage/categories/discrete_valuation.py
index 4bb61a5aa67..24b15bca488 100644
--- a/src/sage/categories/discrete_valuation.py
+++ b/src/sage/categories/discrete_valuation.py
@@ -147,22 +147,31 @@ def quo_rem(self, other):
             Return the quotient and remainder for Euclidean division
             of ``self`` by ``other``.
 
-            TESTS::
+            EXAMPLES::
 
                 sage: R. = GF(5)[[]]
                 sage: (q^2 + q).quo_rem(q)
                 (1 + q, 0)
                 sage: (q + 1).quo_rem(q^2)
                 (0, 1 + q)
+
+            TESTS::
+
                 sage: q.quo_rem(0)
                 Traceback (most recent call last):
                 ...
                 ZeroDivisionError: Euclidean division by the zero element not defined
 
+                sage: L = PowerSeriesRing(QQ, 't')
+                sage: t = L.gen()
+                sage: F = algebras.Free(L, ['A', 'B'])
+                sage: A, B = F.gens()
+                sage: f = t*A+t**2*B/2
             """
             if not other:
                 raise ZeroDivisionError("Euclidean division by the zero element not defined")
             P = self.parent()
+            other = P(other)
             if self.valuation() >= other.valuation():
                 return P(self / other), P.zero()
             else:
@@ -263,7 +272,7 @@ def residue_field(self):
 
         def _matrix_hessenbergize(self, H):
             r"""
-            Replace `H` with an Hessenberg form of it.
+            Replace `H` with a Hessenberg form of it.
 
             EXAMPLES::
 
diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py
index 8ae3270491c..a5e0bbcefaf 100644
--- a/src/sage/categories/enumerated_sets.py
+++ b/src/sage/categories/enumerated_sets.py
@@ -182,7 +182,8 @@ def __iter__(self):
             It is also possible to override ``__iter__`` method itself. Then
             the methods of the first column are defined using  ``__iter__``
 
-            If none of these are provided, raise a ``NotImplementedError``.
+            If none of these are provided, this raises
+            a :class:`NotImplementedError`.
 
             EXAMPLES:
 
@@ -928,7 +929,7 @@ def random_element(self):
             the probability is uniform.
 
             This is a generic implementation from the category
-            ``EnumeratedSets()``. It raise a ``NotImplementedError``
+            ``EnumeratedSets()``. It raises a :class:`NotImplementedError`
             since one does not know whether the set is finite.
 
             EXAMPLES::
diff --git a/src/sage/categories/facade_sets.py b/src/sage/categories/facade_sets.py
index 746622743fb..7bf2ac2f067 100644
--- a/src/sage/categories/facade_sets.py
+++ b/src/sage/categories/facade_sets.py
@@ -209,8 +209,8 @@ def _an_element_(self):
 
             For each parent ``self`` is a facade for, this default
             implementation tries the method ``an_element`` until it finds an
-            element in ``self``. If none is found raise a
-            ``NotImplementedError``.
+            element in ``self``. If none is found, this raises a
+            :class:`NotImplementedError`.
 
             EXAMPLES::
 
diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py
index 63ab62aabff..4f8bb141467 100644
--- a/src/sage/categories/fields.py
+++ b/src/sage/categories/fields.py
@@ -808,7 +808,7 @@ def inverse_of_unit(self):
                 1/2
 
             Trying to invert the zero element typically raises a
-            ``ZeroDivisionError``::
+            :class:`ZeroDivisionError`::
 
                 sage: QQ(0).inverse_of_unit()
                 Traceback (most recent call last):
diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py
index 15d0f490713..84e38b14976 100644
--- a/src/sage/categories/filtered_modules_with_basis.py
+++ b/src/sage/categories/filtered_modules_with_basis.py
@@ -156,7 +156,7 @@ def basis(self, d=None):
                  over Integer Ring(i))_{i in Partitions}
 
             Checking this method on a filtered algebra. Note that this
-            will typically raise a ``NotImplementedError`` when this
+            will typically raise a :class:`NotImplementedError` when this
             feature is not implemented. ::
 
                 sage: A = AlgebrasWithBasis(ZZ).Filtered().example()
diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py
index 1a5ed06c42a..b1e89676728 100644
--- a/src/sage/categories/finite_complex_reflection_groups.py
+++ b/src/sage/categories/finite_complex_reflection_groups.py
@@ -555,22 +555,22 @@ def milnor_fiber_poset(self):
                 sage: sum(x**P.rank(elt) for elt in P)
                 18*x^2 + 15*x + 1
 
-                sage: W = ReflectionGroup(4)                     # optional - gap3
-                sage: P = W.milnor_fiber_poset()                 # optional - gap3
-                sage: P                                          # optional - gap3
+                sage: # optional - gap3
+                sage: W = ReflectionGroup(4)
+                sage: P = W.milnor_fiber_poset(); P
                 Finite meet-semilattice containing 41 elements
-                sage: sum(x**P.rank(elt) for elt in P)           # optional - gap3
+                sage: sum(x**P.rank(elt) for elt in P)
                 24*x^2 + 16*x + 1
 
-                sage: W = ReflectionGroup([4,2,2])               # optional - gap3
-                sage: W.is_well_generated()                      # optional - gap3
+                sage: # optional - gap3
+                sage: W = ReflectionGroup([4,2,2])
+                sage: W.is_well_generated()
                 False
-                sage: P = W.milnor_fiber_poset()                 # optional - gap3
-                sage: P                                          # optional - gap3
+                sage: P = W.milnor_fiber_poset(); P
                 Finite poset containing 47 elements
-                sage: sum(x**P.rank(elt) for elt in P)           # optional - gap3
+                sage: sum(x**P.rank(elt) for elt in P)
                 16*x^3 + 24*x^2 + 6*x + 1
-                sage: P.is_meet_semilattice()                    # optional - gap3
+                sage: P.is_meet_semilattice()
                 False
             """
             I = self.index_set()
diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py
index 7461f6fabfd..c64b7fd8bf0 100644
--- a/src/sage/categories/finite_groups.py
+++ b/src/sage/categories/finite_groups.py
@@ -153,9 +153,9 @@ def conjugacy_classes(self):
             Return a list with all the conjugacy classes of the group.
 
             This will eventually be a fall-back method for groups not defined
-            over GAP. Right now just raises a ``NotImplementedError``, until
-            we include a non-GAP way of listing the conjugacy classes
-            representatives.
+            over GAP. Right now, it just raises a
+            :class:`NotImplementedError`, until we include a non-GAP
+            way of listing the conjugacy classes representatives.
 
             EXAMPLES::
 
diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py
index 49d9f2ba53f..68317bbb4c3 100644
--- a/src/sage/categories/finite_posets.py
+++ b/src/sage/categories/finite_posets.py
@@ -906,7 +906,7 @@ def birational_toggle(self, v, labelling):
             encoded to be understood by Sage. This implementation allows
             `\mathbf{K}` to be a semifield, not just a field. The birational
             `v`-toggle is only a rational map, so an exception (most
-            likely, ``ZeroDivisionError``) will be thrown if the
+            likely, :class:`ZeroDivisionError`) will be thrown if the
             denominator is zero.
 
             INPUT:
@@ -1121,7 +1121,7 @@ def birational_toggles(self, vs, labelling):
             encoded to be understood by Sage. This implementation allows
             `\mathbf{K}` to be a semifield, not just a field. The birational
             `v`-toggle is only a rational map, so an exception (most
-            likely, ``ZeroDivisionError``) will be thrown if the
+            likely, :class:`ZeroDivisionError`) will be thrown if the
             denominator is zero.
 
             INPUT:
@@ -1194,9 +1194,10 @@ def birational_rowmotion(self, labelling):
             `\mathbf{K}`-labellings and for an explanation of how
             `\mathbf{K}`-labellings are to be encoded to be understood
             by Sage. This implementation allows `\mathbf{K}` to be a
-            semifield, not just a field. Birational rowmotion is only a
-            rational map, so an exception (most likely, ``ZeroDivisionError``)
-            will be thrown if the denominator is zero.
+            semifield, not just a field. Birational rowmotion is only
+            a rational map, so an exception (most likely,
+            :class:`ZeroDivisionError`) will be thrown if the
+            denominator is zero.
 
             INPUT:
 
diff --git a/src/sage/categories/functor.pyx b/src/sage/categories/functor.pyx
index 32bd079fc73..e8cf2e5538f 100644
--- a/src/sage/categories/functor.pyx
+++ b/src/sage/categories/functor.pyx
@@ -74,9 +74,9 @@ cdef class Functor(SageObject):
       default call method:
 
       - ``_coerce_into_domain(self, x)``: Return an object of ``self``'s
-        domain, corresponding to ``x``, or raise a ``TypeError``.
+        domain, corresponding to ``x``, or raise a :class:`TypeError`.
 
-        - Default: Raise ``TypeError`` if ``x`` is not in ``self``'s domain.
+        - Default: Raise :class:`TypeError` if ``x`` is not in ``self``'s domain.
 
       - ``_apply_functor(self, x)``: Apply ``self`` to an object ``x`` of
         ``self``'s domain.
@@ -282,11 +282,12 @@ cdef class Functor(SageObject):
         NOTE:
 
         A subclass of :class:`Functor` may overload this method. It should
-        return an object of self's domain, and should raise a ``TypeError``
-        if this is impossible.
+        return an object of self's domain, and should raise a
+        :class:`TypeError` if this is impossible.
 
-        By default, the argument will not be changed, but a ``TypeError``
-        will be raised if the argument does not belong to the domain.
+        By default, the argument will not be changed, but a
+        :class:`TypeError` will be raised if the argument does not
+        belong to the domain.
 
         TESTS::
 
diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py
index 95fcebbb7a7..62d4816b208 100644
--- a/src/sage/categories/graded_algebras_with_basis.py
+++ b/src/sage/categories/graded_algebras_with_basis.py
@@ -224,8 +224,8 @@ def product_on_basis(self, t0, t1):
 
                 TODO: optimize this implementation!
                 """
-                basic = tensor_signed((module.monomial(x0) * module.monomial(x1)
-                                      for (module, x0, x1) in zip(self._sets, t0, t1)))
+                basic = tensor_signed(module.monomial(x0) * module.monomial(x1)
+                                      for (module, x0, x1) in zip(self._sets, t0, t1))
                 n = len(self._sets)
                 parity0 = [self._sets[idx].degree_on_basis(x0)
                            for (idx, x0) in enumerate(t0)]
diff --git a/src/sage/categories/hecke_modules.py b/src/sage/categories/hecke_modules.py
index 118e155654e..8a8da4f64ef 100644
--- a/src/sage/categories/hecke_modules.py
+++ b/src/sage/categories/hecke_modules.py
@@ -1,14 +1,14 @@
 r"""
 Hecke modules
 """
-#*****************************************************************************
+# ****************************************************************************
 #  Copyright (C) 2005      David Kohel 
 #                          William Stein 
 #                2008-2009 Nicolas M. Thiery 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-#******************************************************************************
+#                  https://www.gnu.org/licenses/
+# *****************************************************************************
 
 from sage.categories.category_types import Category_module
 from sage.categories.homsets import HomsetsCategory
@@ -114,7 +114,7 @@ def _Hom_(self, Y, category):
             The sole purpose of this method is to construct the homset
             as a :class:`~sage.modular.hecke.homspace.HeckeModuleHomspace`. If
             ``category`` is specified and is not a subcategory of
-            :class:`HeckeModules`, a ``TypeError`` is raised instead
+            :class:`HeckeModules`, a :class:`TypeError` is raised instead
 
             This method is not meant to be called directly. Please use
             :func:`sage.categories.homset.Hom` instead.
diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py
index 68e72a11b90..baf50566cc0 100644
--- a/src/sage/categories/highest_weight_crystals.py
+++ b/src/sage/categories/highest_weight_crystals.py
@@ -2,12 +2,12 @@
 r"""
 Highest Weight Crystals
 """
-#*****************************************************************************
+# ****************************************************************************
 #  Copyright (C) 2010    Anne Schilling 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-#******************************************************************************
+#                  https://www.gnu.org/licenses/
+# *****************************************************************************
 
 from sage.misc.cachefunc import cached_method
 from sage.categories.category_singleton import Category_singleton
@@ -15,6 +15,7 @@
                                       CrystalMorphismByGenerators)
 from sage.categories.tensor import TensorProductsCategory
 
+
 class HighestWeightCrystals(Category_singleton):
     """
     The category of highest weight crystals.
@@ -410,7 +411,8 @@ def _Hom_(self, Y, category=None, **options):
             The sole purpose of this method is to construct the homset as a
             :class:`~sage.categories.highest_weight_crystals.HighestWeightCrystalHomset`.
             If ``category`` is specified and is not a subcategory of
-            :class:`HighestWeightCrystals`, a ``TypeError`` is raised instead
+            :class:`HighestWeightCrystals`, a :class:`TypeError` is raised
+            instead
 
             This method is not meant to be called directly. Please use
             :func:`sage.categories.homset.Hom` instead.
diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py
index d2203299512..bf4426aff87 100644
--- a/src/sage/categories/homset.py
+++ b/src/sage/categories/homset.py
@@ -178,7 +178,7 @@ def Hom(X, Y, category=None, check=True):
     A parent (or a parent class of a category) may specify how to
     construct certain homsets by implementing a method ``_Hom_(self,
     codomain, category)``. This method should either construct the
-    requested homset or raise a ``TypeError``. This hook is currently
+    requested homset or raise a :class:`TypeError`. This hook is currently
     mostly used to create homsets in some specific subclass of
     :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`)::
 
diff --git a/src/sage/categories/infinite_enumerated_sets.py b/src/sage/categories/infinite_enumerated_sets.py
index 3c7a861a4b6..e65bf944b87 100644
--- a/src/sage/categories/infinite_enumerated_sets.py
+++ b/src/sage/categories/infinite_enumerated_sets.py
@@ -6,16 +6,17 @@
  - Florent Hivert (2009-11): initial revision.
 
 """
-#*****************************************************************************
+# ****************************************************************************
 #  Copyright (C) 2009 Florent Hivert 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-#******************************************************************************
+#                  https://www.gnu.org/licenses/
+# *****************************************************************************
 
 
 from sage.categories.category_with_axiom import CategoryWithAxiom
 
+
 class InfiniteEnumeratedSets(CategoryWithAxiom):
     """
     The category of infinite enumerated sets
@@ -99,9 +100,9 @@ def _test_enumerated_set_iter_cardinality(self, **options):
 
             For infinite enumerated sets:
 
-            * :meth:`.cardinality` is supposed to return `infinity`
+            * :meth:`.cardinality` is supposed to return ``infinity``
 
-            * :meth:`.list` is supposed to raise a ``NotImplementedError``.
+            * :meth:`.list` is supposed to raise a :class:`NotImplementedError`.
 
             EXAMPLES::
 
diff --git a/src/sage/categories/lie_algebras.py b/src/sage/categories/lie_algebras.py
index 32aa9f00041..4226c4dba61 100644
--- a/src/sage/categories/lie_algebras.py
+++ b/src/sage/categories/lie_algebras.py
@@ -697,6 +697,64 @@ def lie_group(self, name='G', **kwds):
                 Lie group G of Heisenberg algebra of rank 1 over Rational Field
             """
 
+        def trivial_representation(self):
+            """
+            Return the trivial representation of ``self``.
+
+            EXAMPLES::
+
+                sage: L = lie_algebras.strictly_upper_triangular_matrices(QQ, 4)
+                sage: L.trivial_representation()
+                Trivial representation of Lie algebra of 4-dimensional
+                 strictly upper triangular matrices over Rational Field
+            """
+            from sage.algebras.lie_algebras.representation import TrivialRepresentation
+            return TrivialRepresentation(self)
+
+        def representation(self, f=None, index_set=None, on_basis=False, **kwargs):
+            """
+            Return a representation of ``self``.
+
+            If no arguments are given, then this returns the trivial
+            representation.
+
+            Currently the only implemented method of constructing a
+            representation is by explicitly specifying the action of
+
+            * the elements of ``self`` by matrices;
+            * the basis elements of ``self`` using a ``dict`` or
+              a :func:`Family`;
+            * a function on basis elements (either passed as ``on_basis``
+              or setting ``on_basis=True``).
+
+            INPUT:
+
+            - ``f`` -- the function that defines the action
+            - ``index_set`` -- the index set of the representation
+            - ``on_basis`` -- (optional) see above
+
+            .. SEEALSO::
+
+                :class:`~sage.algebras.lie_algebras.representation.RepresentationByMorphism`
+
+            EXAMPLES::
+
+                sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}})
+                sage: f = {x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}
+                sage: L.representation(f)
+                Representation of Lie algebra on 2 generators (x, y) over Rational Field defined by:
+                       [1 0]
+                x |--> [0 0]
+                       [0 1]
+                y |--> [0 0]
+                sage: L.representation()
+                Trivial representation of Lie algebra on 2 generators (x, y) over Rational Field
+            """
+            if f is None and on_basis is False and index_set is None:
+                return self.trivial_representation(**kwargs)
+            from sage.algebras.lie_algebras.representation import RepresentationByMorphism
+            return RepresentationByMorphism(self, f, index_set, on_basis, **kwargs)
+
         def _test_jacobi_identity(self, **options):
             """
             Test that the Jacobi identity is satisfied on (not
diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py
index 603e68ad186..72b037c30b0 100644
--- a/src/sage/categories/magmas.py
+++ b/src/sage/categories/magmas.py
@@ -438,7 +438,7 @@ class CartesianProducts(CartesianProductsCategory):
             def extra_super_categories(self):
                 r"""
                 Implement the fact that a Cartesian product of commutative
-                additive magmas is still an commutative additive magmas.
+                additive magmas is still a commutative additive magmas.
 
                 EXAMPLES::
 
diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx
index fadd066d7f4..a931ceaa6cb 100644
--- a/src/sage/categories/map.pyx
+++ b/src/sage/categories/map.pyx
@@ -1880,7 +1880,7 @@ cdef class FormalCompositeMap(Map):
         """
         Tell whether ``self`` is injective.
 
-        It raises ``NotImplementedError`` if it can't be determined.
+        It raises :class:`NotImplementedError` if it cannot be determined.
 
         EXAMPLES::
 
@@ -1955,7 +1955,7 @@ cdef class FormalCompositeMap(Map):
         """
         Tell whether ``self`` is surjective.
 
-        It raises ``NotImplementedError`` if it can't be determined.
+        It raises :class:`NotImplementedError` if it cannot be determined.
 
         EXAMPLES::
 
diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py
index c4933d0657f..aad3469e072 100644
--- a/src/sage/categories/modules_with_basis.py
+++ b/src/sage/categories/modules_with_basis.py
@@ -1081,7 +1081,7 @@ def sum_of_monomials(self):
 
             INPUT:
 
-            - ``indices`` -- an list (or iterable) of indices of basis
+            - ``indices`` -- a list (or iterable) of indices of basis
               elements
 
             EXAMPLES::
diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py
index 3c070bd65b2..6f9829cd499 100644
--- a/src/sage/categories/number_fields.py
+++ b/src/sage/categories/number_fields.py
@@ -95,7 +95,7 @@ def __contains__(self, x):
     def _call_(self, x):
         r"""
         Construct an object in this category from the data in ``x``,
-        or raise a ``TypeError``.
+        or raise a :class:`TypeError`.
 
         EXAMPLES::
 
diff --git a/src/sage/categories/quotient_fields.py b/src/sage/categories/quotient_fields.py
index f5d8495489d..fe1b66a0fb4 100644
--- a/src/sage/categories/quotient_fields.py
+++ b/src/sage/categories/quotient_fields.py
@@ -632,7 +632,7 @@ def _derivative(self, var=None):
             Returns the derivative of this rational function with respect to the
             variable ``var``.
 
-            Over an ring with a working gcd implementation, the derivative of a
+            Over a ring with a working gcd implementation, the derivative of a
             fraction `f/g`, supposed to be given in lowest terms, is computed as
             `(f'(g/d) - f(g'/d))/(g(g'/d))`, where `d` is a greatest common
             divisor of `f` and `g`.
diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py
index 60ca92f971b..24a858f3525 100644
--- a/src/sage/categories/rings.py
+++ b/src/sage/categories/rings.py
@@ -387,7 +387,7 @@ def _Hom_(self, Y, category):
             The sole purpose of this method is to construct the homset
             as a :class:`~sage.rings.homset.RingHomset`. If
             ``category`` is specified and is not a subcategory of
-            :class:`Rings() `, a ``TypeError`` is raised instead
+            :class:`Rings() `, a :class:`TypeError` is raised instead
 
             This method is not meant to be called directly. Please use
             :func:`sage.categories.homset.Hom` instead.
@@ -1416,7 +1416,7 @@ def inverse_of_unit(self):
         def _divide_if_possible(self, y):
             """
             Divide ``self`` by ``y`` if possible and raise a
-            ``ValueError`` otherwise.
+            :class:`ValueError` otherwise.
 
             EXAMPLES::
 
diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py
index 16c117b286e..df0e3abee86 100644
--- a/src/sage/categories/sets_cat.py
+++ b/src/sage/categories/sets_cat.py
@@ -1830,7 +1830,7 @@ def __invert__(self):
             .. NOTE::
 
                 This is an optional method. A default implementation
-                raising ``NotImplementedError`` could be provided instead.
+                raising :class:`NotImplementedError` could be provided instead.
             """
 
         def is_injective(self):
diff --git a/src/sage/categories/simplicial_sets.py b/src/sage/categories/simplicial_sets.py
index 2b45c4c98cb..7b7bbf827af 100644
--- a/src/sage/categories/simplicial_sets.py
+++ b/src/sage/categories/simplicial_sets.py
@@ -1108,7 +1108,7 @@ def connectivity(self, max_dim=None):
                 return Infinity
 
         class Finite(CategoryWithAxiom):
-            class ParentMethods():
+            class ParentMethods:
 
                 def unset_base_point(self):
                     """
diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py
index 85cdce652b9..ed84dc12517 100644
--- a/src/sage/coding/abstract_code.py
+++ b/src/sage/coding/abstract_code.py
@@ -512,7 +512,7 @@ def list(self):
             (1, 0, 1, 0, 1, 0, 1)
             True
         """
-        return [x for x in self]
+        return list(self)
 
     def length(self):
         r"""
diff --git a/src/sage/coding/ag_code.py b/src/sage/coding/ag_code.py
index f705f75d476..0f189bb69a0 100644
--- a/src/sage/coding/ag_code.py
+++ b/src/sage/coding/ag_code.py
@@ -332,7 +332,7 @@ def designed_distance(self):
         """
         Return the designed distance of the AG code.
 
-        If the code is of dimension zero, then a ``ValueError`` is raised.
+        If the code is of dimension zero, then a :class:`ValueError` is raised.
 
         EXAMPLES::
 
@@ -576,7 +576,7 @@ def designed_distance(self):
         """
         Return the designed distance of the differential AG code.
 
-        If the code is of dimension zero, then a ``ValueError`` is raised.
+        If the code is of dimension zero, then a :class:`ValueError` is raised.
 
         EXAMPLES::
 
diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx
index 15231e6aa87..9ce2aa2f8ce 100644
--- a/src/sage/coding/ag_code_decoders.pyx
+++ b/src/sage/coding/ag_code_decoders.pyx
@@ -29,17 +29,18 @@ EXAMPLES::
 The ``decoder`` is now ready for correcting vectors received from a noisy
 channel::
 
-    sage: channel = channels.StaticErrorRateChannel(code.ambient_space(), tau)  # long time
-    sage: message_space = decoder.message_space()                   # long time
-    sage: message = message_space.random_element()                  # long time
-    sage: encoder = decoder.connected_encoder()                     # long time
-    sage: sent_codeword = encoder.encode(message)                   # long time
-    sage: received_vector = channel(sent_codeword)                  # long time
-    sage: (received_vector - sent_codeword).hamming_weight()        # long time
+    sage: # long time
+    sage: channel = channels.StaticErrorRateChannel(code.ambient_space(), tau)
+    sage: message_space = decoder.message_space()
+    sage: message = message_space.random_element()
+    sage: encoder = decoder.connected_encoder()
+    sage: sent_codeword = encoder.encode(message)
+    sage: received_vector = channel(sent_codeword)
+    sage: (received_vector - sent_codeword).hamming_weight()
     4
-    sage: decoder.decode_to_code(received_vector) == sent_codeword  # long time
+    sage: decoder.decode_to_code(received_vector) == sent_codeword
     True
-    sage: decoder.decode_to_message(received_vector) == message     # long time
+    sage: decoder.decode_to_message(received_vector) == message
     True
 
 AUTHORS:
diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx
index 6779708bfda..81065a7dbd3 100644
--- a/src/sage/coding/binary_code.pyx
+++ b/src/sage/coding/binary_code.pyx
@@ -3204,6 +3204,8 @@ cdef class BinaryCodeClassifier:
             ....:  [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]])
             sage: B = BinaryCode(M)
             sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
+
+            sage: # needs sage.groups
             sage: S = SymmetricGroup(M.ncols())
             sage: L = [S([x+1 for x in g]) for g in gens]
             sage: PermutationGroup(L).order()
@@ -3218,6 +3220,8 @@ cdef class BinaryCodeClassifier:
             ....:  [0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1]])
             sage: B = BinaryCode(M)
             sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
+
+            sage: # needs sage.groups
             sage: S = SymmetricGroup(M.ncols())
             sage: L = [S([x+1 for x in g]) for g in gens]
             sage: PermutationGroup(L).order()
@@ -3225,7 +3229,7 @@ cdef class BinaryCodeClassifier:
             sage: size
             2304
 
-            sage: M=Matrix(GF(2),[
+            sage: M = Matrix(GF(2),[
             ....:  [1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0],
             ....:  [0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0],
             ....:  [0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0],
@@ -3236,6 +3240,8 @@ cdef class BinaryCodeClassifier:
             ....:  [0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1]])
             sage: B = BinaryCode(M)
             sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
+
+            sage: # needs sage.groups
             sage: S = SymmetricGroup(M.ncols())
             sage: L = [S([x+1 for x in g]) for g in gens]
             sage: PermutationGroup(L).order()
@@ -3243,7 +3249,7 @@ cdef class BinaryCodeClassifier:
             sage: size
             136
 
-            sage: M=Matrix(GF(2),[
+            sage: M = Matrix(GF(2),[
             ....:  [0,1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1],
             ....:  [1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1,0],
             ....:  [0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,1,1,1,0,1,0,0],
@@ -3257,6 +3263,8 @@ cdef class BinaryCodeClassifier:
             ....:  [0,0,1,0,1,1,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,0]])
             sage: B = BinaryCode(M)
             sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
+
+            sage: # needs sage.groups
             sage: S = SymmetricGroup(M.ncols())
             sage: L = [S([x+1 for x in g]) for g in gens]
             sage: PermutationGroup(L).order()
@@ -3956,7 +3964,7 @@ cdef class BinaryCodeClassifier:
             sage: from sage.coding.binary_code import *
             sage: BC = BinaryCodeClassifier()
             sage: B = BinaryCode(Matrix(GF(2), [[1,1,1,1]]))
-            sage: BC.generate_children(B, 6, 4)
+            sage: BC.generate_children(B, 6, 4)                                         # needs sage.groups
             [
             [1 1 1 1 0 0]
             [0 1 0 1 1 1]
diff --git a/src/sage/coding/channel.py b/src/sage/coding/channel.py
index 50ea5f5cb1f..160afef9bd1 100644
--- a/src/sage/coding/channel.py
+++ b/src/sage/coding/channel.py
@@ -454,12 +454,12 @@ class ErrorErasureChannel(Channel):
     - ``space`` -- the input and output space
 
     - ``number_errors`` -- the number of errors created in each transmitted
-      message. It can be either an integer of a tuple. If an tuple is passed as
+      message. It can be either an integer of a tuple. If a tuple is passed as
       an argument, the number of errors will be a random integer between the
       two bounds of this tuple.
 
     - ``number_erasures`` -- the number of erasures created in each transmitted
-      message. It can be either an integer of a tuple. If an tuple is passed as an
+      message. It can be either an integer of a tuple. If a tuple is passed as an
       argument, the number of erasures will be a random integer between the
       two bounds of this tuple.
 
@@ -564,7 +564,7 @@ def transmit_unsafe(self, message):
         Return ``message`` with as many errors as ``self._number_errors`` in it,
         and as many erasures as ``self._number_erasures`` in it.
 
-        If ``self._number_errors`` was passed as an tuple for the number of errors, it will
+        If ``self._number_errors`` was passed as a tuple for the number of errors, it will
         pick a random integer between the bounds of the tuple and use it as the number of errors.
         It does the same with ``self._number_erasures``.
 
diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py
index c1e0eb77022..b192792f11f 100644
--- a/src/sage/coding/code_bounds.py
+++ b/src/sage/coding/code_bounds.py
@@ -192,10 +192,10 @@ def _check_n_q_d(n, q, d, field_based=True):
     Check that the length `n`, alphabet size `q` and minimum distance `d` type
     check and make sense for a code over a field.
 
-    More precisely, checks that the parameters are positive integers, that `q`
-    is a prime power for codes over a field, or, more generally, that
-    `q` is of size at least 2, and that `n >= d`. Raises a ``ValueError``
-    otherwise.
+    More precisely, this checks that the parameters are positive
+    integers, that `q` is a prime power for codes over a field, or,
+    more generally, that `q` is of size at least 2, and that `n >= d`.
+    This raises a :class:`ValueError` otherwise.
 
     TESTS::
 
diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py
index d96ad3c7627..6870c28ec87 100644
--- a/src/sage/coding/code_constructions.py
+++ b/src/sage/coding/code_constructions.py
@@ -155,8 +155,8 @@ def _is_a_splitting(S1, S2, n, return_automorphism=False):
     This is a special case of Theorem 6.4.3 in [HP2003]_.
     """
     R = IntegerModRing(n)
-    S1 = set(R(x) for x in S1)
-    S2 = set(R(x) for x in S2)
+    S1 = {R(x) for x in S1}
+    S2 = {R(x) for x in S2}
 
     # we first check whether (S1,S2) is a partition of R - {0}
     if (len(S1) + len(S2) != n-1 or len(S1) != len(S2) or
diff --git a/src/sage/coding/codecan/autgroup_can_label.pyx b/src/sage/coding/codecan/autgroup_can_label.pyx
index 29efd21af56..f656771e179 100644
--- a/src/sage/coding/codecan/autgroup_can_label.pyx
+++ b/src/sage/coding/codecan/autgroup_can_label.pyx
@@ -1,3 +1,4 @@
+# sage.doctest: needs sage.libs.pari
 r"""
 Canonical forms and automorphisms for linear codes over finite fields
 
diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py
index 3826734c09b..989d67e89d8 100644
--- a/src/sage/coding/databases.py
+++ b/src/sage/coding/databases.py
@@ -174,7 +174,7 @@ def best_linear_code_in_codetables_dot_de(n, k, F, verbose=False):
     i = s.find("
")
     j = s.find("
") if i == -1 or j == -1: - raise IOError("Error parsing data (missing pre tags).") + raise OSError("Error parsing data (missing pre tags).") return s[i+5:j].strip() diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index 82a07775300..503c31a3b39 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -31,7 +31,7 @@ class Decoder(SageObject): Every decoder class for linear codes (of any metric) should inherit from this abstract class. - To implement an decoder, you need to: + To implement a decoder, you need to: - inherit from :class:`Decoder` diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index 47c84b2c59b..e25c0a4faf4 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Delsarte (or linear programming) bounds @@ -24,9 +23,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import print_function, division def krawtchouk(n, q, l, x, check=True): @@ -142,7 +140,6 @@ def eberlein(n, w, k, u, check=True): default. Otherwise, pass it as it is. Use ``check=False`` at your own risk. - EXAMPLES:: sage: codes.bounds.eberlein(24,10,2,6) @@ -169,13 +166,12 @@ def eberlein(n, w, k, u, check=True): Traceback (most recent call last): ... TypeError: either m or x-m must be an integer - """ from sage.arith.misc import binomial from sage.arith.srange import srange - if 2*w > n: - return eberlein(n, n-w, k, u) + if 2 * w > n: + return eberlein(n, n - w, k, u) if check: from sage.rings.integer_ring import ZZ @@ -185,10 +181,10 @@ def eberlein(n, w, k, u, check=True): n = n0 return sum([(-1)**j*binomial(u, j)*binomial(w-u, k-j)*binomial(n-w-u, k-j) - for j in srange(0, k+1)]) + for j in srange(k + 1)]) -def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0): +def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0): r""" LP builder - common for the two functions; not exported. @@ -211,18 +207,18 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0): x_0 is a continuous variable (min=0, max=+oo) ... x_7 is a continuous variable (min=0, max=+oo) - """ from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram(maximization=True, solver=solver) A = p.new_variable(integer=isinteger, nonnegative=True) - p.set_objective(sum([A[r] for r in range(n+1)])) + p.set_objective(sum([A[r] for r in range(n + 1)])) p.add_constraint(A[0] == 1) for i in range(1, d): p.add_constraint(A[i] == 0) - for j in range(1, n+1): - rhs = sum([krawtchouk(n, q, j, r, check=False)*A[r] for r in range(n+1)]) + for j in range(1, n + 1): + rhs = sum([krawtchouk(n, q, j, r, check=False) * A[r] + for r in range(n + 1)]) p.add_constraint(0 <= rhs) if j >= d_star: p.add_constraint(0 <= rhs) @@ -230,7 +226,7 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0): p.add_constraint(0 == rhs) if maxc > 0: - p.add_constraint(sum([A[r] for r in range(n+1)]), max=maxc) + p.add_constraint(sum([A[r] for r in range(n + 1)]), max=maxc) return A, p @@ -272,10 +268,9 @@ def _delsarte_cwc_LP_building(n, d, w, solver, isinteger): Variables: x_0 is a continuous variable (min=0, max=+oo) x_1 is a continuous variable (min=0, max=+oo) - """ - from sage.numerical.mip import MixedIntegerLinearProgram from sage.arith.misc import binomial + from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram(maximization=True, solver=solver) A = p.new_variable(integer=isinteger, nonnegative=True) @@ -287,12 +282,14 @@ def _q(k, i): return mu_i*eberlein(n, w, i, k)/v_i for k in range(1, w+1): - p.add_constraint(sum([A[2*i]*_q(k, i) for i in range(d//2, w+1)]), min=-1) + p.add_constraint(sum([A[2*i]*_q(k, i) for i in range(d//2, w+1)]), + min=-1) return A, p -def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL", isinteger=False): +def delsarte_bound_constant_weight_code(n, d, w, return_data=False, + solver="PPL", isinteger=False): r""" Find the Delsarte bound on a constant weight code. @@ -337,15 +334,16 @@ def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL" sage: codes.bounds.delsarte_bound_constant_weight_code(17, 4, 3, isinteger=True) 43 - """ from sage.numerical.mip import MIPSolverException if d < 4: - raise ValueError("Violated constraint d>=4 for Binary Constant Weight Codes") + raise ValueError("Violated constraint d>=4 for " + "Binary Constant Weight Codes") if d >= 2*w or 2*w > n: - raise ValueError("Violated constraint d<2w<=n for Binary Constant Weight Codes") + raise ValueError("Violated constraint d<2w<=n for " + "Binary Constant Weight Codes") # minimum distance is even => if there is an odd lower bound on d we can # increase it by 1 @@ -356,23 +354,19 @@ def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL" try: bd = p.solve() except MIPSolverException as exc: - print("Solver exception: {}".format(exc)) - if return_data: - return A, p, False - return False + print(f"Solver exception: {exc}") + return (A, p, False) if return_data else False - if return_data: - return A, p, bd - else: - return int(bd) + return (A, p, bd) if return_data else int(bd) -def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isinteger=False): +def delsarte_bound_hamming_space(n, d, q, return_data=False, + solver="PPL", isinteger=False): r""" Find the Delsarte bound on codes in ``H_q^n`` of minimal distance ``d`` - Find the Delsarte bound [De1973]_ on the size of codes in the Hamming space ``H_q^n`` - of minimal distance ``d``. + Find the Delsarte bound [De1973]_ on the size of codes in + the Hamming space ``H_q^n`` of minimal distance ``d``. INPUT: @@ -444,94 +438,92 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isint try: bd = p.solve() except MIPSolverException as exc: - print("Solver exception: {}".format(exc)) - if return_data: - return A, p, False - return False + print(f"Solver exception: {exc}") + return (A, p, False) if return_data else False - if return_data: - return A, p, bd - else: - return bd + return (A, p, bd) if return_data else bd -def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_data=False, solver="PPL", isinteger=False): +def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_data=False, + solver="PPL", isinteger=False): r""" - Find a modified Delsarte bound on additive codes in Hamming space `H_q^n` of minimal distance `d` + Find a modified Delsarte bound on additive codes in Hamming space `H_q^n` of minimal distance `d`. - Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive codes in - Hamming space `H_q^n` of minimal distance ``d`` with minimal distance of the dual - code at least ``d_star``. If ``q_base`` is set to - non-zero, then ``q`` is a power of ``q_base``, and the code is, formally, linear over - ``F_{q_base}``. Otherwise it is assumed that ``q_base==q``. + Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive + codes in Hamming space `H_q^n` of minimal distance ``d`` with + minimal distance of the dual code at least ``d_star``. If + ``q_base`` is set to non-zero, then ``q`` is a power of + ``q_base``, and the code is, formally, linear over + ``F_{q_base}``. Otherwise it is assumed that ``q_base==q``. + INPUT: - INPUT: - - - ``n`` -- the code length + - ``n`` -- the code length - - ``d`` -- the (lower bound on) minimal distance of the code + - ``d`` -- the (lower bound on) minimal distance of the code - - ``q`` -- the size of the alphabet + - ``q`` -- the size of the alphabet - - ``d_star`` -- the (lower bound on) minimal distance of the dual code; - only makes sense for additive codes. + - ``d_star`` -- the (lower bound on) minimal distance of the dual code; + only makes sense for additive codes. - - ``q_base`` -- if ``0``, the code is assumed to be linear. Otherwise, - ``q=q_base^m`` and the code is linear over ``F_{q_base}``. + - ``q_base`` -- if ``0``, the code is assumed to be linear. Otherwise, + ``q=q_base^m`` and the code is linear over ``F_{q_base}``. - - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is - a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP - data. ``W`` need not be a weight distribution of a code, or, - if ``isinteger==False``, even have integer entries. + - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, + where ``W`` is a weights vector, and ``LP`` the Delsarte bound + LP; both of them are Sage LP data. ``W`` need not be a weight + distribution of a code, or, if ``isinteger==False``, even have + integer entries. - - ``solver`` -- the LP/ILP solver to be used. Defaults to ``'PPL'``. It is arbitrary - precision, thus there will be no rounding errors. With other solvers - (see :class:`MixedIntegerLinearProgram` for the list), you are on your own! + - ``solver`` -- the LP/ILP solver to be used. Defaults to ``'PPL'``. It is + arbitrary precision, thus there will be no rounding errors. With + other solvers (see :class:`MixedIntegerLinearProgram` for the + list), you are on your own! - - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather - that an LP solver. Can be very slow if set to ``True``. + - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), + rather that an LP solver. Can be very slow if set to ``True``. - EXAMPLES: + EXAMPLES: - The bound on dimension of linear `\GF{2}`-codes of length 11 and minimal distance 6:: + The bound on dimension of linear `\GF{2}`-codes of length 11 and minimal distance 6:: - sage: codes.bounds.delsarte_bound_additive_hamming_space(11, 6, 2) - 3 - sage: a,p,val = codes.bounds.delsarte_bound_additive_hamming_space(\ - 11, 6, 2, return_data=True) - sage: [j for i,j in p.get_values(a).items()] - [1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0] + sage: codes.bounds.delsarte_bound_additive_hamming_space(11, 6, 2) + 3 + sage: a,p,val = codes.bounds.delsarte_bound_additive_hamming_space(\ + 11, 6, 2, return_data=True) + sage: [j for i,j in p.get_values(a).items()] + [1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0] - The bound on the dimension of linear `\GF{4}`-codes of length 11 and minimal distance 3:: + The bound on the dimension of linear `\GF{4}`-codes of length 11 and minimal distance 3:: - sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4) - 8 + sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4) + 8 - The bound on the `\GF{2}`-dimension of additive `\GF{4}`-codes of length 11 and minimal - distance 3:: + The bound on the `\GF{2}`-dimension of additive `\GF{4}`-codes of length 11 and minimal + distance 3:: - sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,q_base=2) - 16 + sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,q_base=2) + 16 - Such a ``d_star`` is not possible:: + Such a ``d_star`` is not possible:: - sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,d_star=9) - Solver exception: PPL : There is no feasible solution - False + sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,d_star=9) + Solver exception: PPL : There is no feasible solution + False - TESTS:: + TESTS:: - sage: a,p,x = codes.bounds.delsarte_bound_additive_hamming_space(\ - 19,15,7,return_data=True,isinteger=True) - sage: [j for i,j in p.get_values(a).items()] - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 1, 34] - sage: codes.bounds.delsarte_bound_additive_hamming_space(19,15,7,solver='glpk') - 3 - sage: codes.bounds.delsarte_bound_additive_hamming_space(\ - 19,15,7, isinteger=True, solver='glpk') - 3 - """ + sage: a,p,x = codes.bounds.delsarte_bound_additive_hamming_space(\ + 19,15,7,return_data=True,isinteger=True) + sage: [j for i,j in p.get_values(a).items()] + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 1, 34] + sage: codes.bounds.delsarte_bound_additive_hamming_space(19,15,7,solver='glpk') + 3 + sage: codes.bounds.delsarte_bound_additive_hamming_space(\ + 19,15,7, isinteger=True, solver='glpk') + 3 + """ from sage.numerical.mip import MIPSolverException if q_base == 0: q_base = q @@ -550,18 +542,18 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_da m = kk*n # this is to emulate repeat/until block bd = q**n+1 - while q_base**m < bd: # need to solve the LP repeatedly, as this is a new constraint! + while q_base**m < bd: + # need to solve the LP repeatedly, as this is a new constraint! # we might become infeasible. More precisely, after rounding down # to the closest value of q_base^m, the LP, with the constraint that # the objective function is at most q_base^m, - A, p = _delsarte_LP_building(n, d, d_star, q, isinteger, solver, q_base**m) + A, p = _delsarte_LP_building(n, d, d_star, q, isinteger, + solver, q_base**m) try: bd = p.solve() except MIPSolverException as exc: print("Solver exception:", exc) - if return_data: - return A, p, False - return False + return (A, p, False) if return_data else False # rounding the bound down to the nearest power of q_base, for q=q_base^m # bd_r = roundres(log(bd, base=q_base)) m = -1 @@ -570,10 +562,7 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_da if q_base**(m+1) == bd: m += 1 - if return_data: - return A, p, m - else: - return m + return (A, p, m) if return_data else m def _delsarte_Q_LP_building(q, d, solver, isinteger): @@ -644,12 +633,13 @@ def _delsarte_Q_LP_building(q, d, solver, isinteger): p.add_constraint(A[i] == 0) for k in range(1, n): - p.add_constraint(sum([q[k][i]*A[i] for i in range(n)]), min=0) + p.add_constraint(sum([q[k][i] * A[i] for i in range(n)]), min=0) return A, p -def delsarte_bound_Q_matrix(q, d, return_data=False, solver="PPL", isinteger=False): +def delsarte_bound_Q_matrix(q, d, return_data=False, + solver="PPL", isinteger=False): r""" Delsarte bound on a code with Q matrix ``q`` and lower bound on min. dist. ``d``. @@ -701,25 +691,19 @@ def delsarte_bound_Q_matrix(q, d, return_data=False, solver="PPL", isinteger=Fal sage: a,p,val = codes.bounds.delsarte_bound_Q_matrix(q_matrix, 6, return_data=True) sage: [j for i,j in p.get_values(a).items()] [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] - """ - - from sage.structure.element import is_Matrix from sage.numerical.mip import MIPSolverException + from sage.structure.element import is_Matrix if not is_Matrix(q): - raise ValueError("Input to delsarte_bound_Q_matrix should be a sage Matrix()") + raise ValueError("Input to delsarte_bound_Q_matrix " + "should be a sage Matrix()") A, p = _delsarte_Q_LP_building(q, d, solver, isinteger) try: bd = p.solve() except MIPSolverException as exc: - print("Solver exception: {}".format(exc)) - if return_data: - return A, p, False - return False + print(f"Solver exception: {exc}") + return (A, p, False) if return_data else False - if return_data: - return A, p, bd - else: - return bd + return (A, p, bd) if return_data else bd diff --git a/src/sage/coding/goppa_code.py b/src/sage/coding/goppa_code.py index 2a974cf1ef2..a642fb86049 100644 --- a/src/sage/coding/goppa_code.py +++ b/src/sage/coding/goppa_code.py @@ -308,7 +308,7 @@ def distance_bound(self): [8, 2] Goppa code over GF(2) sage: C.distance_bound() 3 - sage: C.minimum_distance() + sage: C.minimum_distance() # needs sage.libs.gap 5 """ return 1 + (self._generating_pol).degree() diff --git a/src/sage/coding/guruswami_sudan/interpolation.py b/src/sage/coding/guruswami_sudan/interpolation.py index eea537a115a..62ed9f3a8f1 100644 --- a/src/sage/coding/guruswami_sudan/interpolation.py +++ b/src/sage/coding/guruswami_sudan/interpolation.py @@ -130,7 +130,7 @@ def eqs_affine(x0, y0): eqs = [] for i in range(s): for j in range(s - i): - eq = dict() + eq = {} for monomial in monomials: ihat = monomial[0] jhat = monomial[1] diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 0d84996a483..f8f051a00d9 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1224,7 +1224,7 @@ def galois_closure(self, F0): n = len(G.columns()) k = len(G.rows()) G0 = [[x**q0 for x in g.list()] for g in G.rows()] - G1 = [[x for x in g.list()] for g in G.rows()] + G1 = [list(g.list()) for g in G.rows()] G2 = G0+G1 MS = MatrixSpace(F,2*k,n) G3 = MS(G2) @@ -1342,7 +1342,7 @@ def minimum_distance(self, algorithm=None): .. NOTE:: - When using GAP, this raises a ``NotImplementedError`` if + When using GAP, this raises a :class:`NotImplementedError` if the base field of the code has size greater than 256 due to limitations in GAP. @@ -1657,7 +1657,7 @@ def permutation_automorphism_group(self, algorithm="partition"): size = Gp.Size() print("\n Using the %s codewords of weight %s \n Supergroup size: \n %s\n " % (wts[wt], wt, size)) Cwt = filter(lambda c: c.WeightCodeword() == wt, eltsC) # bottleneck 2 (repeated - matCwt = list(map(lambda c: c.VectorCodeword(), Cwt)) # for each i until stop = 1) + matCwt = [c.VectorCodeword() for c in Cwt] # for each i until stop = 1) if len(matCwt) > 0: A = libgap(matCwt).MatrixAutomorphisms() Gp = A.Intersection2(Gp) # bottleneck 3 diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 6885532757b..6ea8a904722 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -906,7 +906,7 @@ def unencode_nocheck(self, c): OUTPUT: - - An polynomial of degree less than ``self.code().order()``. + - A polynomial of degree less than ``self.code().order()``. EXAMPLES:: diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py index 6c08a4d885a..1d41f84d77f 100644 --- a/src/sage/coding/source_coding/huffman.py +++ b/src/sage/coding/source_coding/huffman.py @@ -367,9 +367,9 @@ def pop(): # Build the binary tree of a Huffman code, where the root of the tree # is associated with the empty string. self._build_code_from_tree(self._tree, d, prefix="") - self._index = dict((i, s) for i, (s, w) in enumerate(symbols)) - self._character_to_code = dict( - (s, d[i]) for i, (s, w) in enumerate(symbols)) + self._index = {i: s for i, (s, w) in enumerate(symbols)} + self._character_to_code = { + s: d[i] for i, (s, w) in enumerate(symbols)} def encode(self, string): r""" diff --git a/src/sage/coding/subfield_subcode.py b/src/sage/coding/subfield_subcode.py index 9ec9650c43a..c876ef73e63 100644 --- a/src/sage/coding/subfield_subcode.py +++ b/src/sage/coding/subfield_subcode.py @@ -38,7 +38,7 @@ class SubfieldSubcode(AbstractLinearCode): - ``subfield`` -- the base field of ``self``. - - ``embedding`` -- (default: ``None``) an homomorphism from ``subfield`` to + - ``embedding`` -- (default: ``None``) a homomorphism from ``subfield`` to ``original_code``'s base field. If ``None`` is provided, it will default to the first homomorphism of the list of homomorphisms Sage can build. diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index ee797d990fa..e25c0606769 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Abstract Recursive Trees @@ -73,7 +72,7 @@ # of it later. -class AbstractTree(): +class AbstractTree: """ Abstract Tree. @@ -1276,7 +1275,7 @@ def node_to_str(t): if hasattr(t, "label"): return str(t.label()) else: - return u"o" + return "o" # other possible choices for nodes would be u"█ ▓ ░ ╋ ╬" if self.is_empty(): @@ -1291,9 +1290,9 @@ def node_to_str(t): if len(self) == 1: repr_child = self[0]._unicode_art_() - sep = UnicodeArt([u" " * repr_child._root]) + sep = UnicodeArt([" " * repr_child._root]) t_repr = UnicodeArt([node_to_str(self)]) - repr_root = (sep + t_repr) * (sep + UnicodeArt([u"│"])) + repr_root = (sep + t_repr) * (sep + UnicodeArt(["│"])) t_repr = repr_root * repr_child t_repr._root = repr_child._root t_repr._baseline = t_repr._h - 1 @@ -1303,17 +1302,17 @@ def node_to_str(t): l_repr = [subtree._unicode_art_() for subtree in self] acc = l_repr.pop(0) whitesep = acc._root - lf_sep = u" " * whitesep + u"╭" + u"─" * (acc._l - acc._root) - ls_sep = u" " * whitesep + u"│" + u" " * (acc._l - acc._root) + lf_sep = " " * whitesep + "╭" + "─" * (acc._l - acc._root) + ls_sep = " " * whitesep + "│" + " " * (acc._l - acc._root) while l_repr: tr = l_repr.pop(0) - acc += UnicodeArt([u" "]) + tr + acc += UnicodeArt([" "]) + tr if not len(l_repr): - lf_sep += u"─" * (tr._root) + u"╮" - ls_sep += u" " * (tr._root) + u"│" + lf_sep += "─" * (tr._root) + "╮" + ls_sep += " " * (tr._root) + "│" else: - lf_sep += u"─" * (tr._root) + u"┬" + u"─" * (tr._l - tr._root) - ls_sep += u" " * (tr._root) + u"│" + u" " * (tr._l - tr._root) + lf_sep += "─" * (tr._root) + "┬" + "─" * (tr._l - tr._root) + ls_sep += " " * (tr._root) + "│" + " " * (tr._l - tr._root) mid = whitesep + (len(lf_sep) - whitesep) // 2 node = node_to_str(self) lf_sep = (lf_sep[:mid - len(node) // 2] + node + @@ -1358,10 +1357,10 @@ def canonical_labelling(self, shift=1): def to_hexacode(self): r""" - Transform a tree into an hexadecimal string. + Transform a tree into a hexadecimal string. The definition of the hexacode is recursive. The first letter is - the valence of the root as an hexadecimal (up to 15), followed by + the valence of the root as a hexadecimal (up to 15), followed by the concatenation of the hexacodes of the subtrees. This method only works for trees where every vertex has @@ -1501,7 +1500,7 @@ def create_node(self): . the matrix . and the edges """ - name = "".join((chr(ord(x) + 49) for x in str(num[0]))) + name = "".join(chr(ord(x) + 49) for x in str(num[0])) node = cmd + name nodes.append((name, (str(self.label()) if hasattr(self, "label") else "")) @@ -2460,11 +2459,11 @@ def map_labels(self, f): def from_hexacode(ch, parent=None, label='@'): r""" - Transform an hexadecimal string into a tree. + Transform a hexadecimal string into a tree. INPUT: - - ``ch`` -- an hexadecimal string + - ``ch`` -- a hexadecimal string - ``parent`` -- kind of trees to be produced. If ``None``, this will be ``LabelledOrderedTrees`` @@ -2510,11 +2509,11 @@ def from_hexacode(ch, parent=None, label='@'): def _from_hexacode_aux(ch, parent, label='@'): r""" - Transform an hexadecimal string into a tree and a remainder string. + Transform a hexadecimal string into a tree and a remainder string. INPUT: - - ``ch`` -- an hexadecimal string + - ``ch`` -- a hexadecimal string - ``parent`` -- kind of trees to be produced. diff --git a/src/sage/combinat/backtrack.py b/src/sage/combinat/backtrack.py index d0669580484..ab9c35bc23c 100644 --- a/src/sage/combinat/backtrack.py +++ b/src/sage/combinat/backtrack.py @@ -36,7 +36,7 @@ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest -class GenericBacktracker(): +class GenericBacktracker: r""" A generic backtrack tool for exploring a search space organized as a tree, with branch pruning, etc. diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 460969c062c..648ce9c6a26 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Baxter permutations """ diff --git a/src/sage/combinat/bijectionist.py b/src/sage/combinat/bijectionist.py index b6c5cf53f98..60df5195e9e 100644 --- a/src/sage/combinat/bijectionist.py +++ b/src/sage/combinat/bijectionist.py @@ -2455,7 +2455,7 @@ def solutions_iterator(self): yield from self._bmilp.solutions_iterator(False, []) -class _BijectionistMILP(): +class _BijectionistMILP: r""" Wrapper class for the MixedIntegerLinearProgram (MILP). diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index f58f7558a73..d4afa9387cf 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Binary Trees @@ -593,7 +592,7 @@ def _unicode_art_(self): o o o o """ def node_to_str(bt): - return str(bt.label()) if hasattr(bt, "label") else u"o" + return str(bt.label()) if hasattr(bt, "label") else "o" if self.is_empty(): from sage.typeset.unicode_art import empty_unicode_art @@ -609,14 +608,14 @@ def node_to_str(bt): node = node_to_str(self) rr_tree = self[1]._unicode_art_() if rr_tree._root > 2: - f_line = u" " * (rr_tree._root - 3) + node - s_line = u" " * (len(node) + rr_tree._root - 3) + u"╲" + f_line = " " * (rr_tree._root - 3) + node + s_line = " " * (len(node) + rr_tree._root - 3) + "╲" t_repr = UnicodeArt([f_line, s_line]) * rr_tree t_repr._root = rr_tree._root - 2 else: f_line = node - s_line = u" ╲" - t_line = u" " * (len(node) + 1) + s_line = " ╲" + t_line = " " * (len(node) + 1) t_repr = UnicodeArt([f_line, s_line]) * (UnicodeArt([t_line]) + rr_tree) t_repr._root = rr_tree._root t_repr._baseline = t_repr._h - 1 @@ -625,8 +624,8 @@ def node_to_str(bt): if self[1].is_empty(): node = node_to_str(self) lr_tree = self[0]._unicode_art_() - f_line = u" " * (lr_tree._root + 1) + node - s_line = u" " * lr_tree._root + u"╱" + f_line = " " * (lr_tree._root + 1) + node + s_line = " " * lr_tree._root + "╱" t_repr = UnicodeArt([f_line, s_line]) * lr_tree t_repr._root = lr_tree._root + 2 t_repr._baseline = t_repr._h - 1 @@ -638,10 +637,10 @@ def node_to_str(bt): nb_ = lr_tree._l - lr_tree._root + rr_tree._root - 1 nb_L = nb_ // 2 nb_R = nb_L + (nb_ % 2) - f_line = u" " * (lr_tree._root + 1) + u"_" * nb_L + node - f_line += u"_" * nb_R - s_line = u" " * lr_tree._root + u"╱" + u" " * (len(node) + rr_tree._root - 1 + (lr_tree._l - lr_tree._root)) + u"╲" - t_repr = UnicodeArt([f_line, s_line]) * (lr_tree + UnicodeArt([u" " * (len(node) + 2)]) + rr_tree) + f_line = " " * (lr_tree._root + 1) + "_" * nb_L + node + f_line += "_" * nb_R + s_line = " " * lr_tree._root + "╱" + " " * (len(node) + rr_tree._root - 1 + (lr_tree._l - lr_tree._root)) + "╲" + t_repr = UnicodeArt([f_line, s_line]) * (lr_tree + UnicodeArt([" " * (len(node) + 2)]) + rr_tree) t_repr._root = lr_tree._root + nb_L + 2 t_repr._baseline = t_repr._h - 1 return t_repr @@ -4262,7 +4261,7 @@ def random_element(self): EXAMPLES:: - sage: BinaryTrees(5).random_element() # random # needs sage.combinat + sage: BinaryTrees(5).random_element() # random # needs sage.combinat [., [., [., [., [., .]]]]] sage: BinaryTrees(0).random_element() # needs sage.combinat . diff --git a/src/sage/combinat/combinatorial_map.py b/src/sage/combinat/combinatorial_map.py index 5ec7871821c..84876e52f56 100644 --- a/src/sage/combinat/combinatorial_map.py +++ b/src/sage/combinat/combinatorial_map.py @@ -197,7 +197,7 @@ def combinatorial_map_wrapper(f=None, order=None, name=None): # combinatorial_map = combinatorial_map_wrapper -class CombinatorialMap(): +class CombinatorialMap: r""" This is a wrapper class for methods that are *combinatorial maps*. diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 4d98a25108d..8d01e9ac081 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Integer compositions diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index 18b1b85966c..8ee363fb275 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -120,7 +120,7 @@ def __init__(self, parent, t): CombinatorialElement.__init__(self, parent, t) - def _repr_diagram(self): + def _repr_diagram(self) -> str: r""" Return a string representation of ``self`` as an array. @@ -132,8 +132,8 @@ def _repr_diagram(self): 3 2 4 4 """ - return '\n'.join(("".join(("%3s" % str(x) for x in row)) - for row in self)) + return '\n'.join("".join("%3s" % str(x) for x in row) + for row in self) def __call__(self, *cell): r""" diff --git a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py index 51a04179364..e63e1a4a3ce 100644 --- a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py +++ b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py @@ -769,7 +769,7 @@ def _check_decreasing_hecke_factorization(t): """ if not isinstance(t, DecreasingHeckeFactorization): if not isinstance(t, (tuple, list)): - raise ValueError("t should be an list or tuple") + raise ValueError("t should be a list or tuple") for factor in t: if not isinstance(factor, (tuple, list)): raise ValueError("each factor in t should be a list or tuple") diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index 36f6c259942..2c859c4d492 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -285,7 +285,7 @@ def generate_signature(self, i): strsig = ''.join( x[0] for x in sig) reducedsig = strsig while re.search(r"\+\s*-",reducedsig): - reducedsig = re.sub(r"\+\s*-", lambda match : str().ljust(len(match.group(int(0)))) , reducedsig) + reducedsig = re.sub(r"\+\s*-", lambda match : ''.ljust(len(match.group(0))) , reducedsig) return (sig,reducedsig) def signature(self, i): diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index a1b94ccf541..5604a24665c 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -655,7 +655,7 @@ class Element(TensorProductOfSuperCrystalsElement): pass -class QueerSuperCrystalsMixin(): +class QueerSuperCrystalsMixin: """ Mixin class with methods for a finite queer supercrystal. """ diff --git a/src/sage/combinat/decorated_permutation.py b/src/sage/combinat/decorated_permutation.py index e7281b7d3bb..f687818ef25 100644 --- a/src/sage/combinat/decorated_permutation.py +++ b/src/sage/combinat/decorated_permutation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Decorated permutations diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 98b8f0e598e..5eb2b90802a 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Database of small combinatorial designs diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index b737b90b820..393ea4b9b47 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Difference families @@ -1114,7 +1113,7 @@ def mcfarland_1973_construction(q, s): D = [] for k, H in zip(K, V.subspaces(s)): for v in H: - D.append(G((tuple(v) + (k,)))) + D.append(G(tuple(v) + (k,))) return G,[D] @@ -1749,7 +1748,7 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru EXAMPLES:: sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set - sage: supplementary_difference_set_from_rel_diff_set(17) #random # needs sage.libs.pari + sage: supplementary_difference_set_from_rel_diff_set(17) #random # needs sage.libs.pari (Additive abelian group isomorphic to Z/16, [[(1), (5), (6), (7), (9), (13), (14), (15)], [(0), (2), (3), (5), (6), (10), (11), (13), (14)], diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index 0f5bdf339fa..2227a1e9f63 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -601,7 +601,7 @@ def _encode_attribute(string): else: return string -class XTree(): +class XTree: ''' A lazy class to wrap a rooted tree representing an XML document. The tree's nodes are tuples of the structure: @@ -774,7 +774,7 @@ def __len__(self): return len(self.xt_children) -class XTreeProcessor(): +class XTreeProcessor: ''' An incremental event-driven parser for ext-rep documents. The processing stages: diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 46aada66a6e..ad60d30a6a1 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -48,7 +48,7 @@ lazy_import('sage.libs.gap.libgap', 'libgap') -class IncidenceStructure(): +class IncidenceStructure: r""" A base class for incidence structures (i.e. hypergraphs, i.e. set systems) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index f67a2317a17..69b19540c22 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -291,12 +291,12 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): sage: designs.orthogonal_arrays.is_available(5+2, 5) # 5 MOLS of order 5 False - sage: designs.orthogonal_arrays.is_available(4+2,6) # 4 MOLS of order 6 # needs sage.schemes + sage: designs.orthogonal_arrays.is_available(4+2,6) # 4 MOLS of order 6 # needs sage.schemes False Sage, however, is not able to prove that the second MOLS do not exist:: - sage: designs.orthogonal_arrays.exists(4+2,6) # 4 MOLS of order 6 # needs sage.schemes + sage: designs.orthogonal_arrays.exists(4+2,6) # 4 MOLS of order 6 # needs sage.schemes Unknown If you ask for such a MOLS then you will respectively get an informative diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index b819d96c1db..312f6a4b375 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -2068,7 +2068,7 @@ def OA_from_wider_OA(OA,k): return OA return [L[:k] for L in OA] -class OAMainFunctions(): +class OAMainFunctions: r""" Functions related to orthogonal arrays. diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 54d684cb9d2..8ba2bc12681 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Two-graphs diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 2838d1e38df..b09c2b40cca 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -2158,7 +2158,7 @@ def diagrams(self): return self.support() -class UnitDiagramMixin(): +class UnitDiagramMixin: """ Mixin class for diagram algebras that have the unit indexed by the :func:`identity_set_partition`. @@ -4558,59 +4558,59 @@ def is_planar(sp): sage: da.is_planar( da.to_set_partition([[1,-1],[2,-2]])) True """ - #Singletons don't affect planarity + # Singletons don't affect planarity to_consider = [x for x in map(list, sp) if len(x) > 1] n = len(to_consider) for i in range(n): - #Get the positive and negative entries of this part + # Get the positive and negative entries of this part ap = [x for x in to_consider[i] if x > 0] an = [abs(x) for x in to_consider[i] if x < 0] - #Check if a includes numbers in both the top and bottom rows + # Check if a includes numbers in both the top and bottom rows if ap and an: for j in range(n): if i == j: continue - #Get the positive and negative entries of this part + # Get the positive and negative entries of this part bp = [x for x in to_consider[j] if x > 0] bn = [abs(x) for x in to_consider[j] if x < 0] - #Skip the ones that don't involve numbers in both - #the bottom and top rows + # Skip the ones that don't involve numbers in both + # the bottom and top rows if not bn or not bp: continue - #Make sure that if min(bp) > max(ap) - #then min(bn) > max(an) + # Make sure that if min(bp) > max(ap) + # then min(bn) > max(an) if max(bp) > max(ap): if min(bn) < min(an): return False - #Go through the bottom and top rows + # Go through the bottom and top rows for row in [ap, an]: if len(row) > 1: row.sort() for s in range(len(row)-1): if row[s] + 1 == row[s+1]: - #No gap, continue on + # No gap, continue on continue rng = list(range(row[s] + 1, row[s+1])) - #Go through and make sure any parts that - #contain numbers in this range are completely - #contained in this range + # Go through and make sure any parts that + # contain numbers in this range are completely + # contained in this range for j in range(n): if i == j: continue - #Make sure we make the numbers negative again - #if we are in the bottom row + # Make sure we make the numbers negative again + # if we are in the bottom row if row is ap: sr = set(rng) else: - sr = set((-1*x for x in rng)) + sr = set(-x for x in rng) sj = set(to_consider[j]) intersection = sr.intersection(sj) @@ -4773,12 +4773,11 @@ def to_set_partition(l, k=None): [{-1, 1}, {-2, 3}, {2}, {-4, 4}, {-5, 5}, {-3}] """ if k is None: - if l == []: + if not l: return [] - else: - k = max( (max( map(abs, x) ) for x in l) ) + k = max(max(map(abs, x)) for x in l) - to_be_added = set( list(range(1, ceil(k+1))) + [-1*x for x in range(1, ceil(k+1))] ) + to_be_added = set(list(range(1, ceil(k+1))) + [-x for x in range(1, ceil(k+1))]) sp = [] for part in l: @@ -4790,7 +4789,7 @@ def to_set_partition(l, k=None): i = to_be_added.pop() if -i in to_be_added: to_be_added.remove(-i) - sp.append(set([i,-i])) + sp.append(set([i, -i])) else: sp.append(set([i])) diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 41a3c199599..06cf887c936 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Dyck Words diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index a76958e6e73..cc4588e676e 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -104,7 +104,7 @@ :meth:`~FiniteStateMachine.has_state` | Checks for a state :meth:`~FiniteStateMachine.has_initial_state` | Checks for an initial state :meth:`~FiniteStateMachine.has_initial_states` | Checks for initial states - :meth:`~FiniteStateMachine.has_final_state` | Checks for an final state + :meth:`~FiniteStateMachine.has_final_state` | Checks for a final state :meth:`~FiniteStateMachine.has_final_states` | Checks for final states :meth:`~FiniteStateMachine.has_transition` | Checks for a transition :meth:`~FiniteStateMachine.is_deterministic` | Checks for a deterministic machine @@ -3667,7 +3667,7 @@ def __call__(self, *args, **kwargs): sage: H.states() [('A', 1), ('B', 1), ('B', 2)] - An automaton or transducer can also act on an input (an list + An automaton or transducer can also act on an input (a list or other iterable of letters):: sage: binary_inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, @@ -12144,7 +12144,7 @@ class Transducer(FiniteStateMachine): This creates a transducer, which is a finite state machine, whose transitions have input and output labels. - An transducer has additional features like creating a simplified + A transducer has additional features like creating a simplified transducer. See class :class:`FiniteStateMachine` for more information. diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index aacc520999a..94342f9b4a3 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -97,7 +97,7 @@ from sage.rings.rational_field import QQ -class AutomatonGenerators(): +class AutomatonGenerators: r""" A collection of constructors for several common automata. @@ -346,7 +346,7 @@ def transition_function(read, input): final_states=[word]) -class TransducerGenerators(): +class TransducerGenerators: r""" A collection of constructors for several common transducers. diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index d8092f4bea1..8a97fbdc796 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Free modules """ @@ -937,7 +936,7 @@ def get_order(self): EXAMPLES:: sage: QS2 = SymmetricGroupAlgebra(QQ,2) # needs sage.combinat - sage: QS2.get_order() # note: order changed on 2009-03-13 # needs sage.combinat + sage: QS2.get_order() # note: order changed on 2009-03-13 # needs sage.combinat [[2, 1], [1, 2]] """ if self._order is None: @@ -1670,7 +1669,7 @@ def _coerce_map_from_(self, R): return super()._coerce_map_from_(R) -class CartesianProductWithFlattening(): +class CartesianProductWithFlattening: """ A class for Cartesian product constructor, with partial flattening """ diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index 965c459720c..cb81e811ade 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -15,6 +15,7 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +from itertools import product from sage.categories.magmatic_algebras import MagmaticAlgebras from sage.categories.lie_algebras import LieAlgebras @@ -26,6 +27,7 @@ from sage.categories.functor import Functor from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.alphabet import Alphabet from sage.combinat.rooted_tree import (RootedTrees, RootedTree, LabelledRootedTrees, @@ -34,6 +36,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method +from sage.functions.other import factorial from sage.sets.family import Family from sage.structure.coerce_exceptions import CoercionException @@ -530,6 +533,135 @@ def nap_product(self): codomain=self), position=1) + def corolla(self, x, y, n, N): + """ + Return the corolla obtained with ``x`` as root and ``y`` as leaves. + + INPUT: + + - ``x``, ``y`` -- two elements + - ``n`` -- integer; width of the corolla + - ``N`` -- integer; truncation order (up to order ``N`` included) + + OUTPUT: + + the sum over all possible ways to graft ``n`` copies of ``y`` + on top of ``x`` (with at most ``N`` vertices in total) + + This operation can be defined by induction starting from the + pre-Lie product. + + EXAMPLES:: + + sage: A = algebras.FreePreLie(QQ) + sage: a = A.gen(0) + sage: b = A.corolla(a,a,1,4); b + B[[[]]] + sage: A.corolla(b,b,2,7) + B[[[[[]], [[]]]]] + 2*B[[[[]], [[[]]]]] + B[[[], [[]], [[]]]] + + sage: A = algebras.FreePreLie(QQ, 'o') + sage: a = A.gen(0) + sage: b = A.corolla(a,a,1,4) + + sage: A = algebras.FreePreLie(QQ,'ab') + sage: a, b = A.gens() + sage: A.corolla(a,b,1,4) + B[a[b[]]] + sage: A.corolla(b,a,3,4) + B[b[a[], a[], a[]]] + + sage: A.corolla(a+b,a+b,2,4) + B[a[a[], a[]]] + 2*B[a[a[], b[]]] + B[a[b[], b[]]] + B[b[a[], a[]]] + + 2*B[b[a[], b[]]] + B[b[b[], b[]]] + + TESTS:: + + sage: A = algebras.FreePreLie(QQ,'ab') + sage: a, b = A.gens() + sage: A.corolla(a,A.zero(),2,2) + 0 + """ + if not x or not y: + return self.zero() + + basering = self.base_ring() + vx = x.valuation() + vy = y.valuation() + min_deg = vy * n + vx + if min_deg > N: + return self.zero() + + try: + self.gen(0).support()[0].label() + labels = True + except AttributeError: + labels = False + + deg_x = x.maximal_degree() + deg_y = y.maximal_degree() + max_x = min(deg_x, N - n * vy) + max_y = min(deg_y, N - vx - (n - 1) * vy) + xx = x.truncate(max_x + 1) + yy = y.truncate(max_y + 1) + + y_homog = {i: list(yy.homogeneous_component(i)) + for i in range(vy, max_y + 1)} + resu = self.zero() + for k in range(min_deg, N + 1): # total degree of (x ; y, y, y, y) + for mx, coef_x in xx: + dx = mx.node_number() + step = self.zero() + for pi in IntegerVectors(k - dx, n, min_part=vy, max_part=max_y): + for ly in product(*[y_homog[part] for part in pi]): + coef_y = basering.prod(mc[1] for mc in ly) + arbres_y = [mc[0] for mc in ly] + step += coef_y * self.sum(self(t) + for t in corolla_gen(mx, arbres_y, labels)) + resu += coef_x * step + return resu + + def group_product(self, x, y, n, N=10): + r""" + Return the truncated group product of ``x`` and ``y``. + + This is a weighted sum of all corollas with up to ``n`` leaves, with + ``x`` as root and ``y`` as leaves. + + The result is computed up to order ``N`` (included). + + When considered with infinitely many terms and infinite precision, + this is an analogue of the Baker-Campbell-Hausdorff formula: it + defines an associative product on the completed free pre-Lie algebra. + + INPUT: + + - ``x``, ``y`` -- two elements + - ``n`` -- integer; the maximal width of corollas + - ``N`` -- integer (default: 10); truncation order + + EXAMPLES: + + In the free pre-Lie algebra with one generator:: + + sage: PL = algebras.FreePreLie(QQ) + sage: a = PL.gen(0) + sage: PL.group_product(a, a, 3, 3) + B[[]] + B[[[]]] + 1/2*B[[[], []]] + + In the free pre-Lie algebra with several generators:: + + sage: PL = algebras.FreePreLie(QQ,'@O') + sage: a, b = PL.gens() + sage: PL.group_product(a, b, 3, 3) + B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]] + sage: PL.group_product(a, b, 3, 10) + B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]] + 1/6*B[@[O[], O[], O[]]] + """ + br = self.base_ring() + return x + self.sum(self.corolla(x, y, i, N) * ~br(factorial(i)) + for i in range(1, n + 1)) + def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. @@ -703,6 +835,40 @@ def lift(self): for x, cf in self.monomial_coefficients(copy=False).items()} return UEA.element_class(UEA, data) + def valuation(self): + """ + Return the valuation of ``self``. + + EXAMPLES:: + + sage: a = algebras.FreePreLie(QQ).gen(0) + sage: a.valuation() + 1 + sage: (a*a).valuation() + 2 + + sage: a, b = algebras.FreePreLie(QQ,'ab').gens() + sage: (a+b).valuation() + 1 + sage: (a*b).valuation() + 2 + sage: (a*b+a).valuation() + 1 + + TESTS:: + + sage: z = algebras.FreePreLie(QQ).zero() + sage: z.valuation() + +Infinity + """ + if self == self.parent().zero(): + return Infinity + i = 0 + while True: + i += 1 + if self.homogeneous_component(i): + return i + class PreLieFunctor(ConstructionFunctor): """ @@ -872,3 +1038,106 @@ def _repr_(self): PreLie[x,y,z,t] """ return "PreLie[%s]" % ','.join(self.vars) + + +def tree_from_sortkey(ch, labels=True): + r""" + Transform a list of ``(valence, label)`` into a tree and a remainder. + + This is like an inverse of the ``sort_key`` method. + + INPUT: + + - ``ch`` -- a list of pairs ``(integer, label)`` + - ``labels`` -- (default ``True``) whether to use labelled trees + + OUTPUT: + + a pair ``(tree, remainder of the input)`` + + EXAMPLES:: + + sage: from sage.combinat.free_prelie_algebra import tree_from_sortkey + sage: a = algebras.FreePreLie(QQ).gen(0) + sage: t = (a*a*a*a).support() + sage: all(tree_from_sortkey(u.sort_key(), False)[0] == u for u in t) + True + + sage: a, b = algebras.FreePreLie(QQ,'ab').gens() + sage: t = (a*b*a*b).support() + sage: all(tree_from_sortkey(u.sort_key())[0] == u for u in t) + True + """ + if labels: + Trees = LabelledRootedTrees() + width, label = ch[0] + else: + Trees = RootedTrees() + width = ch[0] + + remainder = ch[1:] + if width == 0: + if labels: + return (Trees([], label), remainder) + return (Trees([]), remainder) + + branches = {} + for i in range(width): + tree, remainder = tree_from_sortkey(remainder, labels=labels) + branches[i] = tree + + if labels: + return (Trees(branches.values(), label), remainder) + return (Trees(branches.values()), remainder) + + +def corolla_gen(tx, list_ty, labels=True): + """ + Yield the terms in the corolla with given bottom tree and top trees. + + These are the possible terms in the simultaneous grafting of the + top trees on vertices of the bottom tree. + + INPUT: + + - ``tx`` -- a tree + - ``list_ty`` -- a list of trees + + EXAMPLES:: + + sage: from sage.combinat.free_prelie_algebra import corolla_gen + sage: a = algebras.FreePreLie(QQ).gen(0) + sage: ta = a.support()[0] + sage: list(corolla_gen(ta,[ta],False)) + [[[]]] + + sage: a, b = algebras.FreePreLie(QQ,'ab').gens() + sage: ta = a.support()[0] + sage: tb = b.support()[0] + sage: ab = (a*b).support()[0] + sage: list(corolla_gen(ta,[tb])) + [a[b[]]] + sage: list(corolla_gen(tb,[ta,ta])) + [b[a[], a[]]] + sage: list(corolla_gen(ab,[ab,ta])) + [a[a[], b[], a[b[]]], a[a[b[]], b[a[]]], a[a[], b[a[b[]]]], + a[b[a[], a[b[]]]]] + """ + n = len(list_ty) + zx = tx.sort_key() + nx = len(zx) + liste_zy = [t.sort_key() for t in list_ty] + for list_pos in product(range(nx), repeat=n): + new_zx = tuple(zx) + data = zip(list_pos, liste_zy) + sorted_data = sorted(data, reverse=True) + for pos_t in sorted_data: + if labels: + idx, lbl = new_zx[pos_t[0]] + new_zx = (new_zx[:pos_t[0]] + ((idx + 1, lbl),) + + pos_t[1] + new_zx[pos_t[0] + 1:]) + else: + idx = new_zx[pos_t[0]] + new_zx = (new_zx[:pos_t[0]] + (idx + 1,) + + pos_t[1] + new_zx[pos_t[0] + 1:]) + yield tree_from_sortkey(new_zx, labels=labels)[0] diff --git a/src/sage/combinat/growth.py b/src/sage/combinat/growth.py index 951d774931e..1b711ddde5f 100644 --- a/src/sage/combinat/growth.py +++ b/src/sage/combinat/growth.py @@ -4348,7 +4348,7 @@ def union(la, mu): ##################################################################### -class Rules(): +class Rules: """ Catalog of rules for growth diagrams. """ diff --git a/src/sage/combinat/hillman_grassl.py b/src/sage/combinat/hillman_grassl.py index 1abbc3d56bd..5a85d4e48d3 100644 --- a/src/sage/combinat/hillman_grassl.py +++ b/src/sage/combinat/hillman_grassl.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" The Hillman-Grassl correspondence diff --git a/src/sage/combinat/integer_lists/invlex.pyx b/src/sage/combinat/integer_lists/invlex.pyx index b517a108479..07036085ac0 100644 --- a/src/sage/combinat/integer_lists/invlex.pyx +++ b/src/sage/combinat/integer_lists/invlex.pyx @@ -99,7 +99,7 @@ class IntegerListsLex(IntegerLists, metaclass=ClasscallMetaclass): value is `\infty`. - ``min_slope`` -- an integer or `-\infty` (default: `-\infty`): - an lower bound on the slope between consecutive parts: + a lower bound on the slope between consecutive parts: ``min_slope <= l[i+1]-l[i]`` for ``0 <= i < len(l)-1`` - ``max_slope`` -- an integer or `+\infty` (defaults: `+\infty`) @@ -796,7 +796,7 @@ class IntegerListsLex(IntegerLists, metaclass=ClasscallMetaclass): cdef class IntegerListsBackend_invlex(IntegerListsBackend): """ - Cython back-end of an set of lists of integers with specified + Cython back-end of a set of lists of integers with specified constraints enumerated in inverse lexicographic order. """ def __init__(self, *args, check=True, **kwds): diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 456eea36f44..4f1176c62a8 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -48,7 +48,7 @@ from sage.rings.integer import Integer -def is_gale_ryser(r,s): +def is_gale_ryser(r, s): r""" Tests whether the given sequences satisfy the condition of the Gale-Ryser theorem. @@ -314,20 +314,20 @@ def gale_ryser_theorem(p1, p2, algorithm="gale", """ from sage.matrix.constructor import matrix - if not is_gale_ryser(p1,p2): + if not is_gale_ryser(p1, p2): return False - if algorithm == "ryser": # ryser's algorithm + if algorithm == "ryser": # ryser's algorithm from sage.combinat.permutation import Permutation # Sorts the sequences if they are not, and remembers the permutation # applied - tmp = sorted(enumerate(p1), reverse=True, key=lambda x:x[1]) + tmp = sorted(enumerate(p1), reverse=True, key=lambda x: x[1]) r = [x[1] for x in tmp] r_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] m = len(r) - tmp = sorted(enumerate(p2), reverse=True, key=lambda x:x[1]) + tmp = sorted(enumerate(p2), reverse=True, key=lambda x: x[1]) s = [x[1] for x in tmp] s_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()] @@ -340,12 +340,12 @@ def gale_ryser_theorem(p1, p2, algorithm="gale", k = i + 1 while k < m and r[i] == r[k]: k += 1 - if t >= k - i: # == number rows of the same length + if t >= k - i: # == number rows of the same length for j in range(i, k): r[j] -= 1 c[j] = 1 t -= k - i - else: # Remove the t last rows of that length + else: # Remove the t last rows of that length for j in range(k-t, k): r[j] -= 1 c[j] = 1 @@ -366,17 +366,17 @@ def gale_ryser_theorem(p1, p2, algorithm="gale", k1, k2 = len(p1), len(p2) p = MixedIntegerLinearProgram(solver=solver) b = p.new_variable(binary=True) - for (i,c) in enumerate(p1): - p.add_constraint(p.sum([b[i,j] for j in range(k2)]) == c) - for (i,c) in enumerate(p2): - p.add_constraint(p.sum([b[j,i] for j in range(k1)]) == c) + for (i, c) in enumerate(p1): + p.add_constraint(p.sum([b[i, j] for j in range(k2)]) == c) + for (i, c) in enumerate(p2): + p.add_constraint(p.sum([b[j, i] for j in range(k1)]) == c) p.set_objective(None) p.solve() b = p.get_values(b, convert=ZZ, tolerance=integrality_tolerance) M = [[0]*k2 for i in range(k1)] for i in range(k1): for j in range(k2): - M[i][j] = b[i,j] + M[i][j] = b[i, j] return matrix(M) else: @@ -780,6 +780,43 @@ def __contains__(self, x): return False return True + def _unrank_helper(self, x, rtn): + """ + Return the element at rank ``x`` by iterating through all integer vectors beginning with ``rtn``. + + INPUT: + + - ``x`` - a nonnegative integer + - ``rtn`` - a list of nonnegative integers + + + EXAMPLES:: + + sage: IV = IntegerVectors(k=5) + sage: IV._unrank_helper(10, [2,0,0,0,0]) + [1, 0, 0, 0, 1] + + sage: IV = IntegerVectors(n=7) + sage: IV._unrank_helper(100, [7,0,0,0]) + [2, 0, 0, 5] + + sage: IV = IntegerVectors(n=12, k=7) + sage: IV._unrank_helper(1000, [12,0,0,0,0,0,0]) + [5, 3, 1, 1, 1, 1, 0] + """ + ptr = 0 + while True: + current_rank = self.rank(rtn) + if current_rank < x: + rtn[ptr+1] = rtn[ptr] + rtn[ptr] = 0 + ptr += 1 + elif current_rank > x: + rtn[ptr] -= 1 + rtn[ptr-1] += 1 + else: + return self._element_constructor_(rtn) + class IntegerVectors_all(UniqueRepresentation, IntegerVectors): """ @@ -839,7 +876,10 @@ def __init__(self, n): sage: TestSuite(IV).run() """ self.n = n - IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) + if self.n == 0: + IntegerVectors.__init__(self, category=EnumeratedSets()) + else: + IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) def _repr_(self): """ @@ -898,6 +938,68 @@ def __contains__(self, x): return False return sum(x) == self.n + def rank(self, x): + """ + Return the rank of a given element. + + INPUT: + + - ``x`` -- a list with ``sum(x) == n`` + + EXAMPLES:: + + sage: IntegerVectors(n=5).rank([5,0]) + 1 + sage: IntegerVectors(n=5).rank([3,2]) + 3 + """ + if sum(x) != self.n: + raise ValueError("argument is not a member of IntegerVectors({},{})".format(self.n, None)) + + n, k, s = self.n, len(x), 0 + r = binomial(k + n - 1, n + 1) + for i in range(k - 1): + s += x[k - 1 - i] + r += binomial(s + i, i + 1) + return r + + def unrank(self, x): + """ + Return the element at given rank x. + + INPUT: + + - ``x`` -- an integer. + + EXAMPLES:: + + sage: IntegerVectors(n=5).unrank(2) + [4, 1] + sage: IntegerVectors(n=10).unrank(10) + [1, 9] + """ + rtn = [self.n] + while self.rank(rtn) <= x: + rtn.append(0) + rtn.pop() + + return IntegerVectors._unrank_helper(self, x, rtn) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: IntegerVectors(n=0).cardinality() + 1 + sage: IntegerVectors(n=10).cardinality() + +Infinity + """ + if self.n == 0: + return Integer(1) + return PlusInfinity() + class IntegerVectors_k(UniqueRepresentation, IntegerVectors): """ @@ -912,7 +1014,10 @@ def __init__(self, k): sage: TestSuite(IV).run() """ self.k = k - IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) + if self.k == 0: + IntegerVectors.__init__(self, category=EnumeratedSets()) + else: + IntegerVectors.__init__(self, category=InfiniteEnumeratedSets()) def _repr_(self): """ @@ -968,6 +1073,75 @@ def __contains__(self, x): return False return len(x) == self.k + def rank(self, x): + """ + Return the rank of a given element. + + INPUT: + + - ``x`` -- a list with ``len(x) == k`` + + EXAMPLES:: + + sage: IntegerVectors(k=5).rank([0,0,0,0,0]) + 0 + sage: IntegerVectors(k=5).rank([1,1,0,0,0]) + 7 + """ + if len(x) != self.k: + raise ValueError("argument is not a member of IntegerVectors({},{})".format(None, self.k)) + + n, k, s = sum(x), self.k, 0 + r = binomial(n + k - 1, k) + for i in range(k - 1): + s += x[k - 1 - i] + r += binomial(s + i, i + 1) + return r + + def unrank(self, x): + """ + Return the element at given rank x. + + INPUT: + + - ``x`` -- an integer such that x < self.cardinality()`` + + EXAMPLES:: + + sage: IntegerVectors(k=5).unrank(10) + [1, 0, 0, 0, 1] + sage: IntegerVectors(k=5).unrank(15) + [0, 0, 2, 0, 0] + sage: IntegerVectors(k=0).unrank(0) + [] + """ + if self.k == 0 and x != 0: + raise IndexError(f"Index {x} is out of range for the IntegerVector.") + rtn = [0]*self.k + if self.k == 0 and x == 0: + return rtn + + while self.rank(rtn) <= x: + rtn[0] += 1 + rtn[0] -= 1 + + return IntegerVectors._unrank_helper(self, x, rtn) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: IntegerVectors(k=0).cardinality() + 1 + sage: IntegerVectors(k=10).cardinality() + +Infinity + """ + if self.k == 0: + return Integer(1) + return PlusInfinity() + class IntegerVectors_nk(UniqueRepresentation, IntegerVectors): """ @@ -1010,11 +1184,11 @@ def _list_rec(self, n, k): res = [] if k == 1: - return [ (n, ) ] + return [(n, )] for nbar in range(n + 1): n_diff = n - nbar - for rest in self._list_rec( nbar , k - 1): + for rest in self._list_rec(nbar, k - 1): res.append((n_diff,) + rest) return res @@ -1153,17 +1327,49 @@ def rank(self, x): if x not in self: raise ValueError("argument is not a member of IntegerVectors({},{})".format(self.n, self.k)) - n = self.n - k = self.k - - r = 0 + k, s, r = self.k, 0, 0 for i in range(k - 1): - k -= 1 - n -= x[i] - r += binomial(k + n - 1, k) - + s += x[k - 1 - i] + r += binomial(s + i, i + 1) return r + def unrank(self, x): + """ + Return the element at given rank x. + + INPUT: + + - ``x`` -- an integer such that ``x < self.cardinality()`` + + EXAMPLES:: + + sage: IntegerVectors(4,5).unrank(30) + [1, 0, 1, 0, 2] + sage: IntegerVectors(2,3).unrank(5) + [0, 0, 2] + """ + if x >= self.cardinality(): + raise IndexError(f"Index {x} is out of range for the IntegerVector.") + rtn = [0]*self.k + rtn[0] = self.n + return IntegerVectors._unrank_helper(self, x, rtn) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: IntegerVectors(3,5).cardinality() + 35 + sage: IntegerVectors(99, 3).cardinality() + 5050 + sage: IntegerVectors(10^9 - 1, 3).cardinality() + 500000000500000000 + """ + n, k = self.n, self.k + return Integer(binomial(n + k - 1, n)) + class IntegerVectors_nnondescents(UniqueRepresentation, IntegerVectors): r""" @@ -1320,11 +1526,11 @@ def __init__(self, n=None, k=None, **constraints): category = FiniteEnumeratedSets() else: category = EnumeratedSets() - elif k is not None and 'max_part' in constraints: # n is None + elif k is not None and 'max_part' in constraints: # n is None category = FiniteEnumeratedSets() else: category = EnumeratedSets() - IntegerVectors.__init__(self, category=category) # placeholder category + IntegerVectors.__init__(self, category=category) # placeholder category def _repr_(self): """ diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py index 996d73a5efa..7877fedee44 100644 --- a/src/sage/combinat/integer_vectors_mod_permgroup.py +++ b/src/sage/combinat/integer_vectors_mod_permgroup.py @@ -1,6 +1,11 @@ # sage.doctest: needs sage.combinat sage.groups r""" Integer vectors modulo the action of a permutation group + +AUTHORS: + +* Nicolas Borie (2010-2012) - original module +* Jukka Kohonen (2023) - fast cardinality method, :issue:`36787`, :issue:`36681` """ # **************************************************************************** # Copyright (C) 2010-12 Nicolas Borie @@ -24,6 +29,12 @@ from sage.combinat.integer_vector import IntegerVectors +from sage.rings.power_series_ring import PowerSeriesRing +from sage.rings.rational_field import QQ +from sage.rings.integer import Integer +from sage.misc.misc_c import prod +from sage.arith.misc import binomial + class IntegerVectorsModPermutationGroup(UniqueRepresentation): r""" @@ -42,39 +53,39 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): v = \max_{\text{lex order}} \{g \cdot v | g \in G \} The action of `G` is on position. This means for example that the - simple transposition `s_1 = (1, 2)` swaps the first and the second entries - of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]` + simple transposition `s_1 = (1, 2)` swaps the first and the second + entries of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]` .. MATH:: s_1 \cdot v = [a_2, a_1, a_3, \dots , a_n] - This functions returns a parent which contains a single integer - vector by orbit under the action of the permutation group `G`. The - approach chosen here is to keep the maximal integer vector for the - lexicographic order in each orbit. Such maximal vector will be - called canonical integer vector under the action of the - permutation group `G`. + This function returns a parent which contains, from each orbit + orbit under the action of the permutation group `G`, a single + canonical vector. The canonical vector is the one that is maximal + within the orbit according to lexicographic order. INPUT: - ``G`` - a permutation group - ``sum`` - (default: None) - a nonnegative integer - ``max_part`` - (default: None) - a nonnegative integer setting the - maximum of entries of elements + maximum value for every element - ``sgs`` - (default: None) - a strong generating system of the group `G`. If you do not provide it, it will be calculated at the creation of the parent OUTPUT: - - If ``sum`` and ``max_part`` are None, it returns the infinite enumerated - set of all integer vectors (list of integers) maximal in their orbit for - the lexicographic order. + - If ``sum`` and ``max_part`` are None, it returns the infinite + enumerated set of all integer vectors (lists of integers) maximal + in their orbit for the lexicographic order. Exceptionally, if + the domain of ``G`` is empty, the result is a finite enumerated + set that contains one element, namely the empty vector. - - If ``sum`` is an integer, it returns a finite enumerated set containing - all integer vectors maximal in their orbit for the lexicographic order - and whose entries sum to ``sum``. + - If ``sum`` is an integer, it returns a finite enumerated set + containing all integer vectors maximal in their orbit for the + lexicographic order and whose entries sum to ``sum``. EXAMPLES: @@ -105,7 +116,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): The method :meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.is_canonical` - tests if any integer vector is maximal in its orbit. This method + tests if an integer vector is maximal in its orbit. This method is also used in the containment test:: sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]])) @@ -124,7 +135,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): sage: I.is_canonical('bla') Traceback (most recent call last): ... - AssertionError: bla should be a list or a integer vector + AssertionError: bla should be a list or an integer vector If you give a value to the extra argument ``sum``, the set returned will be a finite set containing only canonical vectors whose entries @@ -153,10 +164,32 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): sage: I.orbit([2,2,2]) {[2, 2, 2]} + Even without constraints, for an empty domain the result is + a singleton set:: + + sage: G = PermutationGroup([], domain=[]) + sage: sgs = tuple(tuple(s) for s in G.strong_generating_system()) + sage: list(IntegerVectorsModPermutationGroup(G, sgs=sgs)) + [[]] + + + .. WARNING:: + + Because of :issue:`36527`, permutation groups that have + different domains but similar generators can be erroneously + treated as the same group. This will silently produce + erroneous results. To avoid this issue, compute a strong + generating system for the group as:: + + sgs = tuple(tuple(s) for s in G.strong_generating_system()) + + and provide it as the optional ``sgs`` argument to the + constructor. + TESTS: Let us check that canonical integer vectors of the symmetric group - are just sorted list of integers:: + are just nonincreasing lists of integers:: sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time sage: p = iter(I) # long time @@ -164,9 +197,9 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): ....: v = list(next(p)) ....: assert sorted(v, reverse=True) == v - We now check that there is as much of canonical vectors under the - symmetric group `S_n` whose entries sum to `d` than partitions of - `d` of at most `n` parts:: + We now check that there are as many canonical vectors under the + symmetric group `S_n` whose entries sum to `d` as there are + partitions of `d` of at most `n` parts:: sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time sage: for i in range(10): # long time @@ -185,15 +218,16 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): 18 23 - We present a last corner case: trivial groups. For the trivial - group ``G`` acting on a list of length `n`, all integer vectors of - length `n` are canonical:: + Another corner case is trivial groups. For the trivial group ``G`` + acting on a list of length `n`, all integer vectors of length `n` + are canonical:: sage: # long time sage: G = PermutationGroup([[(6,)]]) sage: G.cardinality() 1 - sage: I = IntegerVectorsModPermutationGroup(G) + sage: sgs = tuple(tuple(s) for s in G.strong_generating_system()) + sage: I = IntegerVectorsModPermutationGroup(G, sgs=sgs) sage: for i in range(10): ....: d1 = I.subset(i).cardinality() ....: d2 = IntegerVectors(i,6).cardinality() @@ -209,6 +243,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation): 792 1287 2002 + """ @staticmethod def __classcall__(cls, G, sum=None, max_part=None, sgs=None): @@ -225,13 +260,22 @@ def __classcall__(cls, G, sum=None, max_part=None, sgs=None): sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 8, max_part=5) """ if sum is None and max_part is None: - return IntegerVectorsModPermutationGroup_All(G, sgs=sgs) + # No constraints. + if G.domain(): + # Nonempty domain, infinite set. + return IntegerVectorsModPermutationGroup_All(G, sgs=sgs) + else: + # Empty domain, singleton set. + return IntegerVectorsModPermutationGroup_with_constraints( + G, 0, max_part=-1, sgs=sgs) else: + # Some constraints, either sum or max_part or both. if sum is not None: assert sum == NN(sum) if max_part is not None: assert max_part == NN(max_part) - return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs) + return IntegerVectorsModPermutationGroup_with_constraints( + G, sum, max_part, sgs=sgs) class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, RecursivelyEnumeratedSet_forest): @@ -435,7 +479,7 @@ def is_canonical(self, v, check=True): False """ if check: - assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector' % v + assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or an integer vector' % v assert (self.n == len(v)), '%s should be of length %s' % (v, self.n) for p in v: assert (p == NN(p)), 'Elements of %s should be integers' % v @@ -634,17 +678,27 @@ def _repr_(self): sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S Integer vectors of length 4 and of sum 6 enumerated up to the action of Permutation Group with generators [(1,2,3,4)] sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4); S - Vectors of length 4 and of sum 6 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)] + Vectors of length 4 and of sum 6 whose entries are in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)] sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=4); S - Integer vectors of length 4 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)] + Integer vectors of length 4 whose entries are in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)] """ if self._sum is not None: if self._max_part >= 0: - return "Vectors of length %s and of sum %s whose entries is in {0, ..., %s} enumerated up to the action of %s" % (self.n, self._sum, self._max_part, self.permutation_group()) + return ("Vectors of length %s and of sum %s" + " whose entries are in {0, ..., %s}" + " enumerated up to the action of %s" + % (self.n, self._sum, self._max_part, + self.permutation_group())) else: - return "Integer vectors of length %s and of sum %s enumerated up to the action of %s" % (self.n, self._sum, self.permutation_group()) + return ("Integer vectors of length %s" + " and of sum %s" + " enumerated up to the action of %s" + % (self.n, self._sum, self.permutation_group())) else: - return "Integer vectors of length %s whose entries is in {0, ..., %s} enumerated up to the action of %s" % (self.n, self._max_part, self.permutation_group()) + return ("Integer vectors of length %s" + " whose entries are in {0, ..., %s}" + " enumerated up to the action of %s" + % (self.n, self._max_part, self.permutation_group())) def roots(self): r""" @@ -751,6 +805,7 @@ def __iter__(self): [2, 0, 2, 0] [2, 0, 1, 1] [1, 1, 1, 1] + sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=7, max_part=3) sage: for i in I: i [3, 3, 1, 0] @@ -763,18 +818,197 @@ def __iter__(self): [3, 1, 1, 2] [3, 0, 2, 2] [2, 2, 2, 1] + + Check that :issue:`36681` is fixed:: + + sage: G = PermutationGroup([], domain=[]) + sage: I = IntegerVectorsModPermutationGroup(G, sum=0) + sage: list(iter(I)) + [[]] + + Check that :issue:`36681` is fixed:: + + sage: G = PermutationGroup([], domain=[]) + sage: I = IntegerVectorsModPermutationGroup(G, sum=3) + sage: list(iter(I)) + [] + """ + # Special cases when domain is empty. + if self.n == 0: + if self._sum is not None and self._sum > 0: + # No empty vector can have positive sum. + return iter(()) + else: + # Sum is allowed to be zero. It does not matter what + # the maxpart is, the empty vector is a solution. + return iter([self([])]) + + # General case, nonempty domain. if self._max_part < 0: return self.elements_of_depth_iterator(self._sum) else: - SF = RecursivelyEnumeratedSet_forest((self([0]*(self.n), check=False),), - lambda x : [self(y, check=False) for y in canonical_children(self._sgs, x, self._max_part)], - algorithm='breadth') + SF = RecursivelyEnumeratedSet_forest( + (self([0]*(self.n), check=False),), + lambda x: [self(y, check=False) + for y in canonical_children( + self._sgs, x, self._max_part)], + algorithm='breadth') if self._sum is None: return iter(SF) else: return SF.elements_of_depth_iterator(self._sum) + def cardinality(self): + r""" + Return the number of integer vectors in the set. + + The algorithm utilises :wikipedia:`Cycle Index Theorem `, allowing + for a faster than a plain enumeration computation. + + EXAMPLES: + + With a trivial group all vectors are canonical:: + + sage: G = PermutationGroup([], domain=[1,2,3]) + sage: IntegerVectorsModPermutationGroup(G, 5).cardinality() + 21 + sage: IntegerVectors(5, 3).cardinality() + 21 + + With two interchangeable elements, the smaller one + ranges from zero to ``sum//2``:: + + sage: G = PermutationGroup([(1,2)]) + sage: IntegerVectorsModPermutationGroup(G, 1000).cardinality() + 501 + + Binary vectors up to full symmetry are first some ones and + then some zeros:: + + sage: G = SymmetricGroup(10) + sage: I = IntegerVectorsModPermutationGroup(G, max_part=1) + sage: I.cardinality() + 11 + + Binary vectors of constant weight, up to PGL(2,17), which + is 3-transitive, but not 4-transitive:: + + sage: G=PGL(2,17) + sage: I = IntegerVectorsModPermutationGroup(G, sum=3, max_part=1) + sage: I.cardinality() + 1 + sage: I = IntegerVectorsModPermutationGroup(G, sum=4, max_part=1) + sage: I.cardinality() + 3 + + TESTS: + + Check that :issue:`36681` is fixed:: + + sage: G = PermutationGroup([], domain=[]) + sage: sgs = tuple(tuple(t) for t in G.strong_generating_system()) + sage: V = IntegerVectorsModPermutationGroup(G, sum=1, sgs=sgs) + sage: V.cardinality() + 0 + + The case when both ``sum`` and ``max_part`` are specified:: + + sage: G = PermutationGroup([(1,2,3)]) + sage: I = IntegerVectorsModPermutationGroup(G, sum=10, max_part=5) + sage: I.cardinality() + 7 + + All permutation groups of degree 4:: + + sage: for G in SymmetricGroup(4).subgroups(): + ....: sgs = tuple(tuple(t) for t in G.strong_generating_system()) + ....: I1 = IntegerVectorsModPermutationGroup(G, sum=10, sgs=sgs) + ....: assert I1.cardinality() == len(list(I1)) + ....: I2 = IntegerVectorsModPermutationGroup(G, max_part=3, sgs=sgs) + ....: assert I2.cardinality() == len(list(I2)) + ....: I3 = IntegerVectorsModPermutationGroup(G, sum=10, max_part=3, sgs=sgs) + ....: assert I3.cardinality() == len(list(I3)) + + Symmetric group with sums 0 and 1:: + + sage: S10 = SymmetricGroup(10) + sage: IntegerVectorsModPermutationGroup(S10, 0).cardinality() + 1 + sage: IntegerVectorsModPermutationGroup(S10, 1).cardinality() + 1 + + Trivial group with sums 1 and 100:: + + sage: T10 = PermutationGroup([], domain=range(1, 11)) + sage: IntegerVectorsModPermutationGroup(T10, 1).cardinality() + 10 + sage: IntegerVectorsModPermutationGroup(T10, 100).cardinality() + 4263421511271 + + """ + G = self._permgroup + k = G.degree() # Vector length + d = self._sum # Required sum + m = self._max_part # Max of one entry, -1 for no limit + if m == -1: + m = d # Any entry cannot exceed total + + # Some easy special cases. + if k == 0: + # Empty vectors. There is only one, and it has zero sum. + # Here _max_part does not matter because any _max_part + # condition is vacuously true (with no parts). + if d == 0 or d is None: + return Integer(1) + else: + return Integer(0) + if d == 0 or m == 0: + # All-zero vectors. There is only one of them. + return Integer(1) + if d == 1: + # Vectors with one 1 and all other elements zero. + # The 1 can be placed in any orbit, and by symmetry + # it will be on the first element of the orbit. + return Integer(len(G.orbits())) + if d is not None and m >= d and G.is_trivial(): + # Simple calculation with stars and bars. + return Integer(binomial(d + k - 1, k - 1)) + + # General case. + # + # Cardinality is computed using the Cycle Index Theorem. We + # have two cases. With a fixed sum d we work with power + # series and extract the x^d coefficient. Without a fixed sum + # we can do with integer arithmetic. + Z = G.cycle_index() + + if d is None: + # Case 1. Without a fixed sum, the sum can be up to k*m. + result = sum(coeff * (m+1)**len(cycle_type) + for cycle_type, coeff in Z) + # Computed as Rational, but should have an integer value + # by now. + return Integer(result) + + # Case 2. Fixed sum d. Work with power series with enough + # precision that x^d is valid. + R = PowerSeriesRing(QQ, 'x', default_prec=d+1) + x = R.gen() + + # The figure-counting series, for max_part==m, is (1-t**(m+1)) + # / (1-t) = 1+t+...+t**m. For the function-counting series, + # we substitute x**cycle_length for t. + # + funcount = sum( + coeff * prod((1 - x**((m+1)*cycle_len)) / (1 - x**cycle_len) + for cycle_len in cycle_type) + for cycle_type, coeff in Z) + + # Extract the d'th degree coefficient. Computed as Rational, + # but should have an integer value by now. + return Integer(funcount[d]) + def is_canonical(self, v, check=True): r""" Return ``True`` if the integer list ``v`` is maximal in its @@ -798,7 +1032,7 @@ def is_canonical(self, v, check=True): True """ if check: - assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector' % v + assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or an integer vector' % v assert (self.n == len(v)), '%s should be of length %s' % (v, self.n) for p in v: assert (p == NN(p)), 'Elements of %s should be integers' % v @@ -980,7 +1214,7 @@ class Element(ClonableIntArray): sage: v = I.element_class(I, [3,2,0,0]) Traceback (most recent call last): ... - AssertionError: [3, 2, 0, 0] should be a integer vector of sum 4 + AssertionError: [3, 2, 0, 0] should be an integer vector of sum 4 """ def check(self): @@ -1000,7 +1234,7 @@ def check(self): AssertionError """ if self.parent()._sum is not None: - assert sum(self) == self.parent()._sum, '%s should be a integer vector of sum %s' % (self, self.parent()._sum) + assert sum(self) == self.parent()._sum, '%s should be an integer vector of sum %s' % (self, self.parent()._sum) if self.parent()._max_part >= 0: assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferior to %s' % (self, self.parent()._max_part) assert self.parent().is_canonical(self) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index c9f1e99984d..260a3aa6b48 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Tamari Interval-posets diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 0e4bbc41758..69feb00dd8b 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -4225,7 +4225,7 @@ def add_marking( cls, unmarkedT, marking, k, weight ): r""" Add markings to a partially marked strong tableau. - Given an partially marked standard tableau and a list of cells where the marks + Given a partially marked standard tableau and a list of cells where the marks should be placed along with a ``weight``, return the semi-standard marked strong tableau. The marking should complete the marking so that the result is a strong standard marked tableau. diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index 72c3ea9c52e..1c0215fa093 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -51,7 +51,7 @@ from sage.structure.unique_representation import UniqueRepresentation -class PuzzlePiece(): +class PuzzlePiece: r""" Abstract class for puzzle pieces. @@ -621,7 +621,7 @@ def edges(self) -> tuple: return ('north_west', 'north_east', 'south_east', 'south_west') -class PuzzlePieces(): +class PuzzlePieces: r""" Construct a valid set of puzzle pieces. @@ -1068,7 +1068,7 @@ def BK_pieces(max_letter): return pieces -class PuzzleFilling(): +class PuzzleFilling: r""" Create partial puzzles and provides methods to build puzzles from them. """ diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 0693941851e..29276a3ddad 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -324,7 +324,7 @@ def hadamard_matrix_miyamoto_construction(n, existence=False, check=True): r""" Construct Hadamard matrix using the Miyamoto construction. - If `q = n/4` is a prime power, and there exists an Hadamard matrix of order + If `q = n/4` is a prime power, and there exists a Hadamard matrix of order `q-1`, then a Hadamard matrix of order `n` can be constructed (see [Miy1991]_). INPUT: @@ -442,7 +442,7 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): - ``d`` -- (1,-1) list; the 1st row of `C` - ``c`` -- (1,-1) list; the 1st row of `D` - ``check`` -- boolean (default: ``True``); whether to check that the output - is an Hadamard matrix before returning it + is a Hadamard matrix before returning it EXAMPLES:: @@ -640,7 +640,7 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): def hadamard_matrix_156(): r""" - Construct an Hadamard matrix of order 156. + Construct a Hadamard matrix of order 156. The matrix is created using the construction detailed in [BH1965]_. This uses four circulant matrices of size `13\times 13`, @@ -1030,13 +1030,13 @@ def hadamard_matrix_from_sds(n, existence=False, check=True): def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): r""" - Create an Hadamard matrix using the contruction detailed in [CW1972]_. + Create a Hadamard matrix using the contruction detailed in [CW1972]_. Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) such that the entrywise product of two distinct matrices is always equal to `0` and that `\sum_{i=1}^{4}X_iX_i^\top = nI_n` holds, and four matrices `A, B, C, D` of order `m` with elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` and - `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n` holds, we construct an Hadamard matrix + `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n` holds, we construct a Hadamard matrix of order `4nm`. INPUT: @@ -1125,7 +1125,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): - ``n`` -- integer; the order of the matrix to be constructed - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix - is an Hadamard matrix before returning + is a Hadamard matrix before returning - ``existence`` -- boolean (default: ``False``); if ``True``, only check if the matrix exists. @@ -1298,7 +1298,7 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): - ``e3`` -- Matrix; the third Baumert-Hall unit - ``e4`` -- Matrix; the fourth Baumert-Hall unit - ``check`` -- boolean (default: ``True``); whether to check that the output - is an Hadamard matrix before returning it + is a Hadamard matrix before returning it EXAMPLES:: @@ -1352,11 +1352,11 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): r""" - Construct an Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples. + Construct a Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples. The function looks for Baumert-Hall units and Williamson type matrices from :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases` - and use them to construct an Hadamard matrix with the Turyn construction + and use them to construct a Hadamard matrix with the Turyn construction defined in :func:`hadamard_matrix_turyn_type`. INPUT: @@ -1365,7 +1365,7 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): - ``existence`` -- boolean (default: ``False``): if ``True``, only check if the matrix exists - ``check`` -- boolean (default: ``True``): if ``True``, check that the matrix - is an Hadamard matrix before returning + is a Hadamard matrix before returning EXAMPLES:: @@ -1407,7 +1407,7 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): def hadamard_matrix_spence_construction(n, existence=False, check=True): r""" - Create an Hadamard matrix of order `n` using the Spence construction. + Create a Hadamard matrix of order `n` using the Spence construction. This construction (detailed in [Spe1975]_), uses supplementary difference sets implemented in :func:`sage.combinat.designs.difference_family.supplementary_difference_set_from_rel_diff_set` to create the @@ -1419,7 +1419,7 @@ def hadamard_matrix_spence_construction(n, existence=False, check=True): - ``existence`` -- boolean (default: ``False``); if ``True``, only check if the matrix exists - ``check`` -- bolean (default: ``True``); if ``True``, check that the matrix - is an Hadamard matrix before returning + is a Hadamard matrix before returning OUTPUT: @@ -2968,7 +2968,7 @@ def skew_hadamard_matrix_from_good_matrices_smallcases(n, existence=False, check - ``existence`` -- boolean (default: ``False``); If ``True``, only return whether the Hadamard matrix can be constructed - ``check`` -- boolean (default: ``True``): if ``True``, check that the matrix - is an Hadamard matrix before returning it + is a Hadamard matrix before returning it OUTPUT: diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 1a369bba355..460c44a6441 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -1531,7 +1531,7 @@ def isotopism(p): if isinstance(p, tuple): # We have a single cycle: if isinstance(p[0], Integer): - return Permutation(tuple((x+1 for x in p))) + return Permutation(tuple(x+1 for x in p)) # We have a tuple of cycles: if isinstance(p[0], tuple): diff --git a/src/sage/combinat/misc.py b/src/sage/combinat/misc.py index 8034531ed19..2a4341e8f4c 100644 --- a/src/sage/combinat/misc.py +++ b/src/sage/combinat/misc.py @@ -19,7 +19,7 @@ from sage.misc.misc_c import prod -class DoublyLinkedList(): +class DoublyLinkedList: """ A doubly linked list class that provides constant time hiding and unhiding of entries. diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index c544d9ae2ef..967f6ad93af 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -3394,7 +3394,7 @@ def val(self, q='q'): Verifying Example 4.5 from [BCHOPSY2017]_:: - sage: B = crystals.Minimaj(3, 4, 2) # for `Val_{4,1}^{(3)}` # needs sage.modules + sage: B = crystals.Minimaj(3, 4, 2) # for `Val_{4,1}^{(3)}` # needs sage.modules sage: B.val() # needs sage.modules (q^2+q+1)*s[2, 1, 1] + q*s[2, 2] """ diff --git a/src/sage/combinat/ncsf_qsym/ncsf.py b/src/sage/combinat/ncsf_qsym/ncsf.py index 0d91379f408..91284a4186f 100644 --- a/src/sage/combinat/ncsf_qsym/ncsf.py +++ b/src/sage/combinat/ncsf_qsym/ncsf.py @@ -1999,7 +1999,7 @@ def image_of_L_k(k, i): return x[i-1] * image_of_L_k(k - 1, i - 1) + image_of_L_k(k, i - 1) def on_basis(comp): - return P.prod((image_of_L_k(k, n) for k in comp)) + return P.prod(image_of_L_k(k, n) for k in comp) return L._apply_module_morphism(L(self), on_basis, codomain=P) class MultiplicativeBases(Category_realization_of_parent): diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index 59f4a4ad115..5041dd434a4 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Ordered Rooted Trees diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index 6e9d2943cfd..7f223921d7e 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Output functions @@ -505,7 +504,7 @@ def ascii_art_table(data, use_unicode=False, convention="English"): def get_len(e): if e is None: return 0 - return len(e) - list(str(e)).count(u"\u0304") + return len(e) - list(str(e)).count("\u0304") else: def get_len(e): if e is None: @@ -692,7 +691,7 @@ def ascii_art_table_russian(data, use_unicode=False, compact=False): def get_len(e): if e is None: return 0 - return len(e) - list(str(e)).count(u"\u0304") + return len(e) - list(str(e)).count("\u0304") else: def get_len(e): if e is None: diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index 190eeedef72..53d35bca831 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -2088,7 +2088,7 @@ def is_k_directed(self, k) -> bool: INPUT: - - ``k`` -- An non negative integer. + - ``k`` -- A non negative integer. EXAMPLES:: @@ -3958,7 +3958,7 @@ def _latex_drawing(self): ... \end{tikzpicture} """ - latex.add_package_to_preamble_if_available(str("tikz")) + latex.add_package_to_preamble_if_available("tikz") tikz_options = self.get_tikz_options() res = "\n\\begin{tikzpicture}[scale=%s]" % (tikz_options['scale']) res += self.to_tikz() diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 7d16d5d3410..ee5a9fd85db 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Integer partitions @@ -668,23 +667,23 @@ def _unicode_art_(self): from sage.typeset.unicode_art import UnicodeArt if not self._list: - return UnicodeArt(u'∅', baseline=0) + return UnicodeArt('∅', baseline=0) if self.parent().options.convention == "English": data = list(self) else: data = list(reversed(self)) - txt = [u'┌' + u'┬' * (data[0] - 1) + u'┐'] + txt = ['┌' + '┬' * (data[0] - 1) + '┐'] for i in range(len(data) - 1): p = data[i] q = data[i + 1] if p < q: - txt += [u'├' + u'┼' * p + u'┬' * (q - p - 1) + u'┐'] + txt += ['├' + '┼' * p + '┬' * (q - p - 1) + '┐'] elif p == q: - txt += [u'├' + u'┼' * (p - 1) + u'┤'] + txt += ['├' + '┼' * (p - 1) + '┤'] else: - txt += [u'├' + u'┼' * q + u'┴' * (p - q - 1) + u'┘'] - txt += [u'└' + u'┴' * (data[-1] - 1) + u'┘'] + txt += ['├' + '┼' * q + '┴' * (p - q - 1) + '┘'] + txt += ['└' + '┴' * (data[-1] - 1) + '┘'] return UnicodeArt(txt, baseline=0) @@ -4900,14 +4899,14 @@ def remove_horizontal_border_strip(self, k): :class:`Partitions_with_constraints`:: sage: Partition([5,3,1]).remove_horizontal_border_strip(5) - The subpartitions of [5, 3, 1] obtained by removing an horizontal border strip of length 5 + The subpartitions of [5, 3, 1] obtained by removing a horizontal border strip of length 5 TESTS:: sage: Partition([3,2,2]).remove_horizontal_border_strip(2).list() [[3, 2], [2, 2, 1]] sage: Partition([3,2,2]).remove_horizontal_border_strip(2).first().parent() - The subpartitions of [3, 2, 2] obtained by removing an horizontal border strip of length 2 + The subpartitions of [3, 2, 2] obtained by removing a horizontal border strip of length 2 sage: Partition([]).remove_horizontal_border_strip(0).list() [[]] sage: Partition([]).remove_horizontal_border_strip(6).list() @@ -4919,7 +4918,7 @@ def remove_horizontal_border_strip(self, k): floor=self[1:] + [0], ceiling=self[:], max_slope=0, - name=f"The subpartitions of {self} obtained by removing an horizontal border strip of length {k}") + name=f"The subpartitions of {self} obtained by removing a horizontal border strip of length {k}") def k_conjugate(self, k): r""" @@ -5549,9 +5548,9 @@ def simple_module_dimension(self, base_ring=None): sage: Partition([2,2,1]).simple_module_dimension() 5 - sage: Partition([2,2,1]).specht_module_dimension(GF(3)) # optional - sage.rings.finite_rings + sage: Partition([2,2,1]).specht_module_dimension(GF(3)) # needs sage.rings.finite_rings 5 - sage: Partition([2,2,1]).simple_module_dimension(GF(3)) # optional - sage.rings.finite_rings + sage: Partition([2,2,1]).simple_module_dimension(GF(3)) # needs sage.rings.finite_rings 4 sage: for la in Partitions(6, regular=3): @@ -6736,9 +6735,9 @@ def random_element(self, measure='uniform'): EXAMPLES:: - sage: Partitions(5).random_element() # random # needs sage.libs.flint + sage: Partitions(5).random_element() # random # needs sage.libs.flint [2, 1, 1, 1] - sage: Partitions(5).random_element(measure='Plancherel') # random # needs sage.libs.flint + sage: Partitions(5).random_element(measure='Plancherel') # random # needs sage.libs.flint [2, 1, 1, 1] """ if measure == 'uniform': @@ -6756,7 +6755,7 @@ def random_element_uniform(self): sage: Partitions(5).random_element_uniform() # random # needs sage.libs.flint [2, 1, 1, 1] - sage: Partitions(20).random_element_uniform() # random # needs sage.libs.flint + sage: Partitions(20).random_element_uniform() # random # needs sage.libs.flint [9, 3, 3, 2, 2, 1] TESTS:: diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index a52fc93c338..b5caf31d84b 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -1931,7 +1931,7 @@ def to_set_partition(l, k=None): if not l: return Set([]) else: - k = max((max(map(abs, x)) for x in l)) + k = max(max(map(abs, x)) for x in l) to_be_added = Set(list(range(1, k + 1)) + [-x for x in range(1, k + 1)]) diff --git a/src/sage/combinat/partition_kleshchev.py b/src/sage/combinat/partition_kleshchev.py index 29936e4db39..df8b31b552e 100644 --- a/src/sage/combinat/partition_kleshchev.py +++ b/src/sage/combinat/partition_kleshchev.py @@ -861,7 +861,7 @@ def is_restricted(self): return _is_restricted(self.to_list(), KP._multicharge, KP._convention) -class KleshchevCrystalMixin(): +class KleshchevCrystalMixin: """ Mixin class for the crystal structure of a Kleshchev partition. """ diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index 70c8652841c..84e481cb621 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -215,7 +215,7 @@ def _repr_(self): sage: PerfectMatching([3,8,1,7,6,5,4,2]) [(1, 3), (2, 8), (4, 7), (5, 6)] """ - return '[' + ', '.join(('(' + repr(sorted(x))[1:-1] + ')' for x in self)) + ']' + return '[' + ', '.join('(' + repr(sorted(x))[1:-1] + ')' for x in self) + ']' def _latex_(self): r""" diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 3ed9a0a43fd..ba712588cf6 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Permutations diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index 21b8bb84334..b4f897b64f3 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -387,9 +387,9 @@ def _repr_diagram(self, show_box=False, use_unicode=False) -> str: drawing = [[" " for i in range(2 * x + y + z)] for j in range(y + z + 1)] - hori = u"_" if use_unicode else "_" - down = u"╲" if use_unicode else "\\" - up = u"╱" if use_unicode else "/" + hori = "_" if use_unicode else "_" + down = "╲" if use_unicode else "\\" + up = "╱" if use_unicode else "/" def superpose(l, c, letter): # add the given letter at line l and column c @@ -449,10 +449,10 @@ def add_leftside(i, j, k): check = False if not drawing: - return u"∅" if use_unicode else "" + return "∅" if use_unicode else "" if use_unicode: - return u'\n'.join(u"".join(row) for row in drawing) + return '\n'.join("".join(row) for row in drawing) return '\n'.join("".join(row) for row in drawing) def _ascii_art_(self): @@ -1548,7 +1548,7 @@ def __init__(self, box_size): EXAMPLES:: sage: PP = PlanePartitions([4,3,2]) - sage: TestSuite(PP).run() # long time, needs sage.modules + sage: TestSuite(PP).run() # long time # needs sage.modules """ super().__init__(box_size, category=FiniteEnumeratedSets()) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 8fb9055afdb..6af274fd18a 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -2298,8 +2298,8 @@ def is_orthocomplemented(self, unique=False): sage: D6.is_orthocomplemented(unique=True) # needs sage.groups False - sage: hexagon = LatticePoset({0:[1, 2], 1:[3], 2:[4], 3:[5], 4:[5]}) - sage: hexagon.is_orthocomplemented(unique=True) + sage: hexagon = LatticePoset({0: [1, 2], 1: [3], 2: [4], 3:[5], 4: [5]}) + sage: hexagon.is_orthocomplemented(unique=True) # needs sage.groups True .. SEEALSO:: @@ -4046,7 +4046,7 @@ def is_subdirectly_reducible(self, certificate=False): TESTS:: - sage: [posets.ChainPoset(i).is_subdirectly_reducible() for i in range(5)] + sage: [posets.ChainPoset(i).is_subdirectly_reducible() for i in range(5)] # needs sage.combinat [False, False, False, True, True] """ H = self._hasse_diagram @@ -4256,7 +4256,7 @@ def is_constructible_by_doublings(self, type) -> bool: sage: L = L.day_doubling([(3,0), (1,1), (2,1)]) # An upper pseudo-interval sage: L.is_constructible_by_doublings('upper') False - sage: L.is_constructible_by_doublings('convex') + sage: L.is_constructible_by_doublings('convex') # needs sage.combinat True An example of a lattice that can be constructed by doublings @@ -4378,18 +4378,20 @@ def is_isoform(self, certificate=False): EXAMPLES:: - sage: L = LatticePoset({1:[2, 3, 4], 2: [5, 6], 3: [6, 7], 4: [7], 5: [8], 6: [8], 7: [8]}) - sage: L.is_isoform() + sage: L = LatticePoset({1: [2, 3, 4], 2: [5, 6], 3: [6, 7], + ....: 4: [7], 5: [8], 6: [8], 7: [8]}) + sage: L.is_isoform() # needs sage.combinat True Every isoform lattice is (trivially) uniform, but the converse is not true:: - sage: L = LatticePoset({1: [2, 3, 6], 2: [4, 5], 3: [5], 4: [9, 8], 5: [7, 8], 6: [9], 7: [10], 8: [10], 9: [10]}) - sage: L.is_isoform(), L.is_uniform() + sage: L = LatticePoset({1: [2, 3, 6], 2: [4, 5], 3: [5], 4: [9, 8], + ....: 5: [7, 8], 6: [9], 7: [10], 8: [10], 9: [10]}) + sage: L.is_isoform(), L.is_uniform() # needs sage.combinat (False, True) - sage: L.is_isoform(certificate=True) + sage: L.is_isoform(certificate=True) # needs sage.combinat (False, {{1, 2, 4, 6, 9}, {3, 5, 7, 8, 10}}) .. SEEALSO:: @@ -4404,7 +4406,7 @@ def is_isoform(self, certificate=False): sage: [posets.ChainPoset(i).is_isoform() for i in range(5)] [True, True, True, False, False] - sage: posets.DiamondPoset(5).is_isoform() # Simple, so trivially isoform + sage: posets.DiamondPoset(5).is_isoform() # Simple, so trivially isoform # needs sage.combinat True """ ok = (True, None) if certificate else True @@ -4451,8 +4453,9 @@ def is_uniform(self, certificate=False): EXAMPLES:: - sage: L = LatticePoset({1: [2, 3, 4], 2: [6, 7], 3: [5], 4: [5], 5: [9, 8], 6: [9], 7: [10], 8: [10], 9: [10]}) - sage: L.is_uniform() + sage: L = LatticePoset({1: [2, 3, 4], 2: [6, 7], 3: [5], 4: [5], + ....: 5: [9, 8], 6: [9], 7: [10], 8: [10], 9: [10]}) + sage: L.is_uniform() # needs sage.combinat True Every uniform lattice is regular, but the converse is not true:: @@ -4461,7 +4464,7 @@ def is_uniform(self, certificate=False): sage: N6.is_uniform(), N6.is_regular() (False, True) - sage: N6.is_uniform(certificate=True) + sage: N6.is_uniform(certificate=True) # needs sage.combinat (False, {{1, 2, 3, 4}, {5, 6}}) .. SEEALSO:: @@ -4475,7 +4478,7 @@ def is_uniform(self, certificate=False): sage: [posets.ChainPoset(i).is_uniform() for i in range(5)] [True, True, True, False, False] - sage: posets.DiamondPoset(5).is_uniform() # Simple, so trivially uniform + sage: posets.DiamondPoset(5).is_uniform() # Simple, so trivially uniform # needs sage.combinat True """ ok = (True, None) if certificate else True @@ -4532,14 +4535,15 @@ def is_regular(self, certificate=False): EXAMPLES:: - sage: L = LatticePoset({1: [2, 3, 4], 2: [5, 6], 3: [8, 7], 4: [6, 7], 5: [8], 6: [9], 7: [9], 8: [9]}) - sage: L.is_regular() + sage: L = LatticePoset({1: [2, 3, 4], 2: [5, 6], 3: [8, 7], 4: [6, 7], + ....: 5: [8], 6: [9], 7: [9], 8: [9]}) + sage: L.is_regular() # needs sage.combinat True sage: N5 = posets.PentagonPoset() - sage: N5.is_regular() + sage: N5.is_regular() # needs sage.combinat False - sage: N5.is_regular(certificate=True) + sage: N5.is_regular(certificate=True) # needs sage.combinat (False, ({{0}, {1}, {2, 3}, {4}}, [0])) .. SEEALSO:: @@ -4552,7 +4556,7 @@ def is_regular(self, certificate=False): TESTS:: - sage: [posets.ChainPoset(i).is_regular() for i in range(5)] + sage: [posets.ChainPoset(i).is_regular() for i in range(5)] # needs sage.combinat [True, True, True, False, False] """ ok = (True, None) if certificate else True @@ -4598,6 +4602,7 @@ def is_simple(self, certificate=False): EXAMPLES:: + sage: # needs sage.combinat sage: posets.DiamondPoset(5).is_simple() # Smallest nontrivial example True sage: L = LatticePoset({1: [2, 3], 2: [4, 5], 3: [6], 4: [6], 5: [6]}) @@ -4611,11 +4616,11 @@ def is_simple(self, certificate=False): sage: L = LatticePoset({1: [2, 3, 4], 2: [5], 3: [5], 4: [6, 7], ....: 5: [8], 6: [8], 7: [8]}) - sage: L.is_simple() + sage: L.is_simple() # needs sage.combinat False sage: L = LatticePoset({1: [2, 3], 2: [4, 5], 3: [6, 7], 4: [8], ....: 5: [8], 6: [8], 7: [8]}) - sage: L.is_simple() + sage: L.is_simple() # needs sage.combinat True .. SEEALSO:: @@ -4652,9 +4657,10 @@ def subdirect_decomposition(self): EXAMPLES:: - sage: posets.ChainPoset(3).subdirect_decomposition() + sage: posets.ChainPoset(3).subdirect_decomposition() # needs sage.combinat [Finite lattice containing 2 elements, Finite lattice containing 2 elements] + sage: # needs sage.combinat sage: L = LatticePoset({1: [2, 4], 2: [3], 3: [6, 7], 4: [5, 7], ....: 5: [9, 8], 6: [9], 7: [9], 8: [10], 9: [10]}) sage: Ldecomp = L.subdirect_decomposition() @@ -4665,6 +4671,7 @@ def subdirect_decomposition(self): TESTS:: + sage: # needs sage.combinat sage: posets.ChainPoset(0).subdirect_decomposition() [Finite lattice containing 0 elements] sage: posets.ChainPoset(1).subdirect_decomposition() @@ -4676,7 +4683,7 @@ def subdirect_decomposition(self): has only one element:: sage: N5 = posets.PentagonPoset() - sage: N5.subdirect_decomposition() + sage: N5.subdirect_decomposition() # needs sage.combinat [Finite lattice containing 5 elements] """ H = self._hasse_diagram @@ -4737,6 +4744,7 @@ def congruence(self, S): EXAMPLES:: + sage: # needs sage.combinat sage: L = posets.DivisorLattice(12) sage: cong = L.congruence([[1, 3]]) sage: sorted(sorted(c) for c in cong) @@ -4745,19 +4753,20 @@ def congruence(self, S): {{1, 2, 4}, {3, 6, 12}} sage: L = LatticePoset({1: [2, 3], 2: [4], 3: [4], 4: [5]}) - sage: L.congruence([[1, 2]]) + sage: L.congruence([[1, 2]]) # needs sage.combinat {{1, 2}, {3, 4}, {5}} sage: L = LatticePoset({1: [2, 3], 2: [4, 5, 6], 4: [5], 5: [7, 8], ....: 6: [8], 3: [9], 7: [10], 8: [10], 9:[10]}) - sage: cong = L.congruence([[1, 2]]) - sage: cong[0] + sage: cong = L.congruence([[1, 2]]) # needs sage.combinat + sage: cong[0] # needs sage.combinat frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) .. SEEALSO:: :meth:`quotient` TESTS:: + sage: # needs sage.combinat sage: P = posets.PentagonPoset() sage: P.congruence([]) {{0}, {1}, {2}, {3}, {4}} @@ -4774,13 +4783,13 @@ def congruence(self, S): works:: sage: L = LatticePoset(DiGraph('P^??@_?@??B_?@??B??@_?@??B_?@??B??@??A??C??G??O???')) - sage: sorted(sorted(p) for p in L.congruence([[1,6]])) + sage: sorted(sorted(p) for p in L.congruence([[1,6]])) # needs sage.combinat [[0], [1, 6], [2], [3, 8], [4], [5, 10], [7, 12], [9, 14], [11], [13], [15], [16]] Simple lattice, i.e. a lattice without any nontrivial congruence:: sage: L = LatticePoset(DiGraph('GPb_@?OC@?O?')) - sage: L.congruence([[1,2]]) + sage: L.congruence([[1,2]]) # needs sage.combinat {{0, 1, 2, 3, 4, 5, 6, 7}} """ from sage.combinat.set_partition import SetPartition @@ -4821,6 +4830,7 @@ def quotient(self, congruence, labels='tuple'): EXAMPLES:: + sage: # needs sage.combinat sage: L = posets.PentagonPoset() sage: c = L.congruence([[0, 1]]) sage: I = L.quotient(c); I @@ -4831,6 +4841,7 @@ def quotient(self, congruence, labels='tuple'): sage: I.top() Finite lattice containing 3 elements + sage: # needs sage.combinat sage: B3 = posets.BooleanLattice(3) sage: c = B3.congruence([[0,1]]) sage: B2 = B3.quotient(c, labels='integer') @@ -4846,7 +4857,7 @@ def quotient(self, congruence, labels='tuple'): Finite lattice containing 0 elements sage: L = posets.PentagonPoset() - sage: L.quotient(L.congruence([[1]])).is_isomorphic(L) + sage: L.quotient(L.congruence([[1]])).is_isomorphic(L) # needs sage.combinat True """ if labels not in ['lattice', 'tuple', 'integer']: @@ -4894,6 +4905,7 @@ def congruences_lattice(self, labels='congruence'): EXAMPLES:: + sage: # needs sage.combinat sage: N5 = posets.PentagonPoset() sage: CL = N5.congruences_lattice(); CL Finite lattice containing 5 elements @@ -4903,12 +4915,13 @@ def congruences_lattice(self, labels='congruence'): [{{0, 1}, {2, 3, 4}}, {{0, 2, 3}, {1, 4}}] sage: C4 = posets.ChainPoset(4) - sage: CL = C4.congruences_lattice(labels='integer') - sage: CL.is_isomorphic(posets.BooleanLattice(3)) + sage: CL = C4.congruences_lattice(labels='integer') # needs sage.combinat + sage: CL.is_isomorphic(posets.BooleanLattice(3)) # needs sage.combinat True TESTS:: + sage: # needs sage.combinat sage: posets.ChainPoset(0).congruences_lattice() Finite lattice containing 1 elements sage: posets.ChainPoset(1).congruences_lattice() @@ -5002,6 +5015,7 @@ def feichtner_yuzvinsky_ring(self, G, use_defining=False, base_ring=None): We reproduce the example from Section 5 of [Coron2023]_:: + sage: # needs sage.geometry.polyhedron sage: H. = HyperplaneArrangements(QQ) sage: Arr = H(a-b, b-c, c-d, d-a) sage: P = LatticePoset(Arr.intersection_poset()) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 47792a7118f..cca9b098d4a 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Linear Extensions of Posets diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f8d10257c1f..0a8352274ee 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3745,9 +3745,9 @@ def magnitude(self) -> Integer: sage: P.magnitude() == m1*m2 True - sage: Poset({}).magnitude() + sage: Poset({}).magnitude() # needs sage.libs.flint 0 - sage: Poset({1:[]}).magnitude() + sage: Poset({1: []}).magnitude() # needs sage.libs.flint 1 """ H = self._hasse_diagram @@ -4314,7 +4314,7 @@ def coxeter_transformation(self): EXAMPLES:: - sage: posets.PentagonPoset().coxeter_transformation() + sage: posets.PentagonPoset().coxeter_transformation() # needs sage.libs.flint [ 0 0 0 0 -1] [ 0 0 0 1 -1] [ 0 1 0 0 -1] @@ -4327,8 +4327,8 @@ def coxeter_transformation(self): TESTS:: - sage: M = posets.PentagonPoset().coxeter_transformation() - sage: M ** 8 == 1 + sage: M = posets.PentagonPoset().coxeter_transformation() # needs sage.libs.flint + sage: M ** 8 == 1 # needs sage.libs.flint True """ return self._hasse_diagram.coxeter_transformation() @@ -4348,11 +4348,11 @@ def coxeter_polynomial(self): EXAMPLES:: sage: P = posets.PentagonPoset() - sage: P.coxeter_polynomial() + sage: P.coxeter_polynomial() # needs sage.libs.flint x^5 + x^4 + x + 1 sage: p = posets.SymmetricGroupWeakOrderPoset(3) # needs sage.groups - sage: p.coxeter_polynomial() # needs sage.groups + sage: p.coxeter_polynomial() # needs sage.groups sage.libs.flint x^6 + x^5 - x^3 + x + 1 .. SEEALSO:: @@ -4398,7 +4398,7 @@ def coxeter_smith_form(self, algorithm='singular'): TESTS:: sage: P = posets.PentagonPoset() - sage: P.coxeter_smith_form(algorithm='sage') + sage: P.coxeter_smith_form(algorithm='sage') # needs sage.libs.flint [1, 1, 1, 1, x^5 + x^4 + x + 1] sage: P.coxeter_smith_form(algorithm='gap') # needs sage.libs.gap [1, 1, 1, 1, x^5 + x^4 + x + 1] @@ -8099,33 +8099,33 @@ def is_eulerian(self, k=None, certificate=False): sage: P = Poset({0: [1, 2, 3], 1: [4, 5], 2: [4, 6], 3: [5, 6], ....: 4: [7, 8], 5: [7, 8], 6: [7, 8], 7: [9], 8: [9]}) - sage: P.is_eulerian() + sage: P.is_eulerian() # needs sage.libs.flint True sage: P = Poset({0: [1, 2, 3], 1: [4, 5, 6], 2: [4, 6], 3: [5,6], ....: 4: [7], 5:[7], 6:[7]}) - sage: P.is_eulerian() + sage: P.is_eulerian() # needs sage.libs.flint False Canonical examples of Eulerian posets are the face lattices of convex polytopes:: sage: P = polytopes.cube().face_lattice() # needs sage.geometry.polyhedron - sage: P.is_eulerian() # needs sage.geometry.polyhedron + sage: P.is_eulerian() # needs sage.geometry.polyhedron sage.libs.flint True A poset that is 3- but not 4-eulerian:: sage: P = Poset(DiGraph('MWW@_?W?@_?W??@??O@_?W?@_?W?@??O??')); P Finite poset containing 14 elements - sage: P.is_eulerian(k=3) + sage: P.is_eulerian(k=3) # needs sage.libs.flint True - sage: P.is_eulerian(k=4) + sage: P.is_eulerian(k=4) # needs sage.libs.flint False Getting an interval that is not Eulerian:: sage: P = posets.DivisorLattice(12) - sage: P.is_eulerian(certificate=True) + sage: P.is_eulerian(certificate=True) # needs sage.libs.flint (False, (1, 4)) TESTS:: @@ -8143,7 +8143,7 @@ def is_eulerian(self, k=None, certificate=False): ... ValueError: the poset is not graded - sage: posets.BooleanLattice(3).is_eulerian(k=123, certificate=True) + sage: posets.BooleanLattice(3).is_eulerian(k=123, certificate=True) # needs sage.libs.flint (True, None) """ if k is not None: diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 358b60ff322..bf346f03ad5 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" `q`-Analogues """ diff --git a/src/sage/combinat/ranker.py b/src/sage/combinat/ranker.py index c370800bf22..1d51b8a04d2 100644 --- a/src/sage/combinat/ranker.py +++ b/src/sage/combinat/ranker.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Rankers """ diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 173e061276b..161a7e05ec4 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -70,7 +70,7 @@ from sage.structure.unique_representation import UniqueRepresentation -class PrefixClosedSet(): +class PrefixClosedSet: def __init__(self, words): r""" A prefix-closed set. diff --git a/src/sage/combinat/regular_sequence.py b/src/sage/combinat/regular_sequence.py index c585e51e194..a2d5c09d94b 100644 --- a/src/sage/combinat/regular_sequence.py +++ b/src/sage/combinat/regular_sequence.py @@ -2337,7 +2337,7 @@ def from_recurrence(self, *args, **kwds): return self(mu, left, right) -class RecurrenceParser(): +class RecurrenceParser: r""" A parser for recurrence relations that allow the construction of a `k`-linear representation diff --git a/src/sage/combinat/rigged_configurations/rigged_partition.pyx b/src/sage/combinat/rigged_configurations/rigged_partition.pyx index 5b30b15608f..98a9047d6c9 100644 --- a/src/sage/combinat/rigged_configurations/rigged_partition.pyx +++ b/src/sage/combinat/rigged_configurations/rigged_partition.pyx @@ -7,7 +7,7 @@ configuration class. This is an internal class used by the rigged configurations and KR tableaux during the bijection, and is not to be used by the end-user. -We hold the partitions as an 1-dim array of positive integers where each +We hold the partitions as a 1-dim array of positive integers where each value corresponds to the length of the row. This is the shape of the partition which can be accessed by the regular index. diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index c9696b98fdb..a8aa765ee5d 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -55,9 +55,10 @@ class AmbientSpace(CombinatorialFreeModule): TESTS:: - sage: types = CartanType.samples(crystallographic = True)+[CartanType(["A",2],["C",5])] + sage: # needs sage.libs.gap + sage: types = CartanType.samples(crystallographic=True) + [CartanType(["A",2],["C",5])] sage: for e in [ct.root_system().ambient_space() for ct in types]: - ....: TestSuite(e).run() + ....: TestSuite(e).run() sage: e1 = RootSystem(['A',3]).ambient_lattice() sage: e2 = RootSystem(['B',3]).ambient_lattice() diff --git a/src/sage/combinat/root_system/associahedron.py b/src/sage/combinat/root_system/associahedron.py index 24140007081..448b1009036 100644 --- a/src/sage/combinat/root_system/associahedron.py +++ b/src/sage/combinat/root_system/associahedron.py @@ -129,7 +129,7 @@ def Associahedron(cartan_type, backend='ppl'): return parent(cartan_type) -class Associahedron_class_base(): +class Associahedron_class_base: r""" The base class of the Python class of an associahedron @@ -321,7 +321,7 @@ def Associahedra(base_ring, ambient_dim, backend='ppl'): raise ValueError("unknown backend") -class Associahedra_base(): +class Associahedra_base: """ Base class of parent of Associahedra of specified dimension diff --git a/src/sage/combinat/root_system/braid_move_calculator.py b/src/sage/combinat/root_system/braid_move_calculator.py index e932662bd5d..38931a00229 100644 --- a/src/sage/combinat/root_system/braid_move_calculator.py +++ b/src/sage/combinat/root_system/braid_move_calculator.py @@ -19,7 +19,7 @@ from sage.misc.cachefunc import cached_method -class BraidMoveCalculator(): +class BraidMoveCalculator: """ Helper class to compute braid moves. """ diff --git a/src/sage/combinat/root_system/cartan_matrix.py b/src/sage/combinat/root_system/cartan_matrix.py index c1ce04fba38..8019029d62a 100644 --- a/src/sage/combinat/root_system/cartan_matrix.py +++ b/src/sage/combinat/root_system/cartan_matrix.py @@ -452,7 +452,7 @@ def reflection_group(self, type="matrix"): EXAMPLES:: sage: C = CartanMatrix(['A',3]) # needs sage.graphs - sage: C.reflection_group() # needs sage.graphs + sage: C.reflection_group() # needs sage.graphs sage.libs.gap Weyl Group of type ['A', 3] (as a matrix group acting on the root space) """ RS = self.root_space() @@ -589,7 +589,7 @@ def subtype(self, index_set): """ ind = self.index_set() I = [ind.index(i) for i in index_set] - return CartanMatrix(self.matrix_from_rows_and_columns(I, I), index_set) + return CartanMatrix(self.matrix_from_rows_and_columns(I, I), index_set=index_set) def rank(self): r""" @@ -1135,13 +1135,39 @@ def find_cartan_type_from_matrix(CM): ['C', 3] relabelled by {1: 0, 2: 1, 3: 2} sage: CM = CartanMatrix([[2,-1,-2], [-1,2,-1], [-2,-1,2]]) sage: find_cartan_type_from_matrix(CM) + + TESTS: + + Check that :issue:`35987` is fixed:: + + sage: from sage.combinat.root_system.cartan_matrix import find_cartan_type_from_matrix + sage: cm = CartanMatrix(['A',7]).subtype([2,3,5]) + sage: find_cartan_type_from_matrix(cm) + A2xA1 relabelled by {1: 2, 2: 3, 3: 5} + + sage: cm = CartanMatrix(['B',10,1]).subtype([0,1,2,3,5,6,8,9,10]) + sage: ct = find_cartan_type_from_matrix(cm); ct + D4xB3xA2 relabelled by {1: 0, 2: 2, 3: 1, 4: 3, 5: 8, 6: 9, 7: 10, 8: 5, 9: 6} + sage: ct.dynkin_diagram() + O 3 + | + | + O---O---O + 0 2 1 + O---O=>=O + 8 9 10 + O---O + 5 6 + D4xB3xA2 relabelled by {1: 0, 2: 2, 3: 1, 4: 3, 5: 8, 6: 9, 7: 10, 8: 5, 9: 6} """ types = [] + relabel = [] for S in CM.dynkin_diagram().connected_components_subgraphs(): S = DiGraph(S) # We need a simple digraph here n = S.num_verts() # Build the list to test based upon rank if n == 1: + relabel.append({1: S.vertices()[0]}) types.append(CartanType(['A', 1])) continue @@ -1178,7 +1204,8 @@ def find_cartan_type_from_matrix(CM): T = DiGraph(ct.dynkin_diagram()) # We need a simple digraph here iso, match = T.is_isomorphic(S, certificate=True, edge_labels=True) if iso: - types.append(ct.relabel(match)) + types.append(ct) + relabel.append(match) found = True break @@ -1189,10 +1216,20 @@ def find_cartan_type_from_matrix(CM): T = DiGraph(ct.dynkin_diagram()) # We need a simple digraph here iso, match = T.is_isomorphic(S, certificate=True, edge_labels=True) if iso: - types.append(ct.relabel(match)) + types.append(ct) + relabel.append(match) found = True break if not found: return None - return CartanType(types) + if len(types) == 1: + # Irreducible, so just relabel + return CartanType(types[0]).relabel(relabel[0]) + ct = CartanType(types) + # ct._index_relabelling is a dict ``(ind, j): i``, where i is an index of + # ``ct``, ``ind`` is the position in the list of types, and j is the + # corresponding index of the type number ``ind``. + # In other words, the j-th node of ``types[ind]`` is the i-th node of ``ct``. + mapping = {i: relabel[d[0]][d[1]] for d, i in ct._index_relabelling.items()} + return ct.relabel(mapping) diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index 816fbadcd57..bd7ab5cdda4 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Cartan types @@ -603,7 +602,7 @@ def __call__(self, *args): return CartanType(t[:-1]).dual() elif t[-1] == "~": return CartanType(t[:-1]).affine() - elif t in ["Aoo", u"A∞"]: + elif t in ["Aoo", "A∞"]: return CartanType(['A', Infinity]) elif t == "A+oo": from . import type_A_infinity @@ -974,7 +973,7 @@ class options(GlobalOptions): CartanType.__doc__ = __doc__ -class CartanType_abstract(): +class CartanType_abstract: r""" Abstract class for Cartan types @@ -1438,7 +1437,7 @@ def is_implemented(self): EXAMPLES:: - sage: CartanType(["A",4,1]).is_implemented() + sage: CartanType(["A",4,1]).is_implemented() # needs sage.graphs True sage: CartanType(['H',3]).is_implemented() True @@ -3075,7 +3074,7 @@ def __getitem__(self, i): # For backward compatibility -class CartanType_simple_finite(): +class CartanType_simple_finite: def __setstate__(self, dict): """ Implements the unpickling of Cartan types pickled by Sage <= 4.0. diff --git a/src/sage/combinat/root_system/coxeter_group.py b/src/sage/combinat/root_system/coxeter_group.py index 8fadad6035c..012daffdf87 100644 --- a/src/sage/combinat/root_system/coxeter_group.py +++ b/src/sage/combinat/root_system/coxeter_group.py @@ -86,7 +86,7 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No [3 1 5] [2 5 1] - sage: W = CoxeterGroup(["H",3], implementation="reflection"); W # needs sage.rings.number_field + sage: W = CoxeterGroup(["H",3], implementation="reflection"); W # needs sage.libs.gap sage.rings.number_field Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: [1 3 2] @@ -116,7 +116,7 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No TESTS:: - sage: W = groups.misc.CoxeterGroup(["H",3]) # needs sage.groups + sage: W = groups.misc.CoxeterGroup(["H",3]) # needs sage.graphs sage.groups """ if implementation not in ["permutation", "matrix", "coxeter3", "reflection", "chevie", None]: raise ValueError("invalid type implementation") diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index 719d23c3409..57e6030b9c4 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -850,6 +850,7 @@ def bilinear_form(self, R=None): EXAMPLES:: + sage: # needs sage.libs.gap sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index ef1d4beb922..88a53e4bd98 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -357,7 +357,7 @@ def bilinear_form(self, R=None): EXAMPLES:: - sage: # needs sage.graphs sage.rings.number_field + sage: # needs sage.graphs sage.libs.gap sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] diff --git a/src/sage/combinat/root_system/fundamental_group.py b/src/sage/combinat/root_system/fundamental_group.py index 65426b786e3..8905471a5ab 100644 --- a/src/sage/combinat/root_system/fundamental_group.py +++ b/src/sage/combinat/root_system/fundamental_group.py @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.graphs +# sage.doctest: needs sage.graphs sage.groups r""" Fundamental Group of an Extended Affine Weyl Group diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 0278617a2e5..a1cb764e19a 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -426,7 +426,7 @@ def _inner_pq(self, pelt, qelt): def _inner_pp(self, pelt1, pelt2): """ - Symmetric form between an two elements of the weight lattice + Symmetric form between two elements of the weight lattice associated to ``self``. EXAMPLES:: @@ -860,21 +860,22 @@ def mult(self, mu): EXAMPLES:: + sage: # needs sage.libs.gap sage: L = RootSystem("B3~").weight_lattice(extended=True) sage: Lambda = L.fundamental_weights() sage: delta = L.null_root() sage: W = L.weyl_group(prefix="s") sage: [s0,s1,s2,s3] = W.simple_reflections() sage: V = IntegrableRepresentation(Lambda[0]) - sage: V.mult(Lambda[2]-2*delta) + sage: V.mult(Lambda[2] - 2*delta) 3 - sage: V.mult(Lambda[2]-Lambda[1]) + sage: V.mult(Lambda[2] - Lambda[1]) 0 - sage: weights = [w.action(Lambda[1]-4*delta) for w in [s1,s2,s0*s1*s2*s3]] + sage: weights = [w.action(Lambda[1] - 4*delta) for w in [s1,s2,s0*s1*s2*s3]] sage: weights [-Lambda[1] + Lambda[2] - 4*delta, - Lambda[1] - 4*delta, - -Lambda[1] + Lambda[2] - 4*delta] + Lambda[1] - 4*delta, + -Lambda[1] + Lambda[2] - 4*delta] sage: [V.mult(mu) for mu in weights] [35, 35, 35] @@ -1093,7 +1094,7 @@ def branch(self, i=None, weyl_character_ring=None, sequence=None, depth=5): sage: Lambda = RootSystem(['A',2,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(2*Lambda[0]) - sage: b = V.branch(); b + sage: b = V.branch(); b # needs sage.libs.gap [A2(0,0), A2(1,1), A2(0,0) + 2*A2(1,1) + A2(2,2), @@ -1104,7 +1105,7 @@ def branch(self, i=None, weyl_character_ring=None, sequence=None, depth=5): If the parameter ``weyl_character_ring`` is omitted, the ring may be recovered as the parent of one of the branched coefficients:: - sage: A2 = b[0].parent(); A2 + sage: A2 = b[0].parent(); A2 # needs sage.libs.gap The Weyl Character Ring of Type A2 with Integer Ring coefficients If `i` is not zero then you should specify the :class:`WeylCharacterRing` that you @@ -1125,8 +1126,8 @@ def branch(self, i=None, weyl_character_ring=None, sequence=None, depth=5): Thus we have a branching to `\mathfrak{sl}(2) \times \mathfrak{sl}(2) \times \mathfrak{sl}(2)`:: - sage: A1xA1xA1 = WeylCharacterRing("A1xA1xA1",style="coroots") - sage: V.branch(i=2,weyl_character_ring=A1xA1xA1) + sage: A1xA1xA1 = WeylCharacterRing("A1xA1xA1",style="coroots") # needs sage.libs.gap + sage: V.branch(i=2,weyl_character_ring=A1xA1xA1) # needs sage.libs.gap [A1xA1xA1(1,0,0), A1xA1xA1(0,1,2), A1xA1xA1(1,0,0) + A1xA1xA1(1,2,0) + A1xA1xA1(1,0,2), @@ -1159,7 +1160,7 @@ def branch(self, i=None, weyl_character_ring=None, sequence=None, depth=5): The nodes `0, 2, 3, 4` of ``F4~`` correspond to ``1, 4, 3, 2`` of ``A1xC3`` and so we encode this in a dictionary:: - sage: V.branch(i=1,weyl_character_ring=A1xC3,sequence={0:1,2:4,3:3,4:2}) # long time + sage: V.branch(i=1, weyl_character_ring=A1xC3, sequence={0:1,2:4,3:3,4:2}) # long time [A1xC3(1,0,0,0), A1xC3(0,0,0,1), A1xC3(1,0,0,0) + A1xC3(1,2,0,0), diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index a29b26fdde3..34e7e0817f1 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -72,7 +72,7 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): Here is the nonsymmetric Macdonald polynomial with leading term `[2,0,1]`:: - sage: E[L0([2,0,1])] + sage: E[L0([2,0,1])] # needs sage.libs.gap ((-q*q1-q*q2)/(-q*q1-q2))*B[(1, 1, 1)] + ((-q1-q2)/(-q*q1-q2))*B[(2, 1, 0)] + B[(2, 0, 1)] @@ -92,7 +92,7 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): sage: q,t = K.gens() sage: E = NonSymmetricMacdonaldPolynomials(["A",2,1], q=q, q1=t, q2=-1) sage: vars = K['x0,x1,x2'].gens() - sage: E[L0([2,0,1])].expand(vars) + sage: E[L0([2,0,1])].expand(vars) # needs sage.libs.gap (t - 1)/(q*t - 1)*x0^2*x1 + x0^2*x2 + (q*t - q)/(q*t - 1)*x0*x1*x2 sage: from sage.combinat.sf.ns_macdonald import E # needs sage.combinat @@ -104,7 +104,7 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): sage: E = NonSymmetricMacdonaldPolynomials(["G",2,1]) sage: L0 = E.keys() sage: omega = L0.fundamental_weights() - sage: E[ omega[2]-omega[1] ] + sage: E[omega[2] - omega[1]] ((-q*q1^3*q2-q*q1^2*q2^2)/(q*q1^4-q2^4))*B[(0, 0, 0)] + B[(1, -1, 0)] + ((-q1*q2^3-q2^4)/(q*q1^4-q2^4))*B[(1, 0, -1)] @@ -1684,7 +1684,7 @@ def __getitem__(self, mu): ... ValueError: 1/2*e[0] + 1/2*e[1] does not lift to a level 0 element of the affine weight lattice - sage: E[2*omega[2]] + sage: E[2*omega[2]] # needs sage.libs.gap ((q^2*q1^2+q^2*q1*q2)/(q^2*q1^2-q2^2))*B[(0, 0)] + ((-q^2*q1^2-q^2*q1*q2)/(-q^2*q1^2+q2^2))*B[(1, 0)] + B[(1, 1)] + ((-q^2*q1^2-q^2*q1*q2)/(-q^2*q1^2+q2^2))*B[(0, 1)] @@ -1751,7 +1751,7 @@ def eigenvalue_experimental(self, mu, l): sage: KL = RootSystem(["A",1,1]).ambient_space().algebra(K) sage: E = NonSymmetricMacdonaldPolynomials(KL,q, q1, q2) sage: L0 = E.keys() - sage: E.eigenvalues(L0([0,0])) # Checked by hand by Mark and Arun + sage: E.eigenvalues(L0([0,0])) # Checked by hand by Mark and Arun # needs sage.libs.gap [1/(q*t), t] sage: alpha = E.Y().keys().simple_roots() sage: E.eigenvalue_experimental(L0([0,0]), alpha[0]) # todo: not implemented @@ -1761,6 +1761,7 @@ def eigenvalue_experimental(self, mu, l): Some examples of eigenvalues (not mathematically checked!!!):: + sage: # needs sage.libs.gap sage: E.eigenvalues(L0([1,0])) [t, 1/(q*t)] sage: E.eigenvalues(L0([0,1])) @@ -1880,13 +1881,13 @@ def symmetric_macdonald_polynomial(self, mu): sage: q,v,t = K.gens() sage: E = NonSymmetricMacdonaldPolynomials(['A',2,1], q, v, -1/v) sage: om = E.L0().fundamental_weights() - sage: E.symmetric_macdonald_polynomial(om[2]) + sage: E.symmetric_macdonald_polynomial(om[2]) # needs sage.libs.gap B[(1, 1, 0)] + B[(1, 0, 1)] + B[(0, 1, 1)] - sage: E.symmetric_macdonald_polynomial(2*om[1]) + sage: E.symmetric_macdonald_polynomial(2*om[1]) # needs sage.libs.gap ((q*v^2+v^2-q-1)/(q*v^2-1))*B[(1, 1, 0)] + ((q*v^2+v^2-q-1)/(q*v^2-1))*B[(1, 0, 1)] + B[(2, 0, 0)] + ((q*v^2+v^2-q-1)/(q*v^2-1))*B[(0, 1, 1)] + B[(0, 2, 0)] + B[(0, 0, 2)] - sage: f = E.symmetric_macdonald_polynomial(E.L0()((2,1,0))); f + sage: f = E.symmetric_macdonald_polynomial(E.L0()((2,1,0))); f # needs sage.libs.gap ((2*q*v^4+v^4-q*v^2+v^2-q-2)/(q*v^4-1))*B[(1, 1, 1)] + B[(1, 2, 0)] + B[(1, 0, 2)] + B[(2, 1, 0)] + B[(2, 0, 1)] + B[(0, 1, 2)] + B[(0, 2, 1)] @@ -1899,11 +1900,11 @@ def symmetric_macdonald_polynomial(self, mu): x0^2*x1 + x0*x1^2 + x0^2*x2 + (2*q*t^2 - q*t - q + t^2 + t - 2)/(q*t^2 - 1)*x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 - sage: fe = f.expand(g.parent().gens()); fe + sage: fe = f.expand(g.parent().gens()); fe # needs sage.libs.gap x0^2*x1 + x0*x1^2 + x0^2*x2 + (2*q*v^4 - q*v^2 - q + v^4 + v^2 - 2)/(q*v^4 - 1)*x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 - sage: g.map_coefficients(lambda x: x.subs(t=v*v)) == fe + sage: g.map_coefficients(lambda x: x.subs(t=v*v)) == fe # needs sage.libs.gap True sage: E = NonSymmetricMacdonaldPolynomials(['C',3,1], q, v, -1/v) diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py index a1eec40e8df..c81d86b8484 100644 --- a/src/sage/combinat/root_system/plot.py +++ b/src/sage/combinat/root_system/plot.py @@ -830,7 +830,7 @@ "RootLatticeRealizations") -class PlotOptions(): +class PlotOptions: r""" A class for plotting options for root lattice realizations. diff --git a/src/sage/combinat/root_system/reflection_group_c.pyx b/src/sage/combinat/root_system/reflection_group_c.pyx index e2f5eff21b1..a44a5874b7f 100644 --- a/src/sage/combinat/root_system/reflection_group_c.pyx +++ b/src/sage/combinat/root_system/reflection_group_c.pyx @@ -1,4 +1,5 @@ # cython: wraparound=False, boundscheck=False +# sage.doctest: needs sage.graphs r""" This contains a few time-critical auxiliary cython functions for finite complex or real reflection groups. diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 03fde46bf8d..6bf0e9936ea 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.libs.gap +# sage.doctest: optional - gap3, needs sage.libs.gap r""" Finite complex reflection groups ---------------------------------- @@ -13,16 +13,15 @@ The point of entry to work with reflection groups is :func:`~sage.combinat.root_system.reflection_group_real.ReflectionGroup` which can be used with finite Cartan-Killing types:: - sage: ReflectionGroup(['A',2]) # optional - gap3 + sage: ReflectionGroup(['A',2]) Irreducible real reflection group of rank 2 and type A2 - sage: ReflectionGroup(['F',4]) # optional - gap3 + sage: ReflectionGroup(['F',4]) Irreducible real reflection group of rank 4 and type F4 - sage: ReflectionGroup(['H',3]) # optional - gap3 + sage: ReflectionGroup(['H',3]) Irreducible real reflection group of rank 3 and type H3 or with Shephard-Todd types:: - sage: # optional - gap3 sage: ReflectionGroup((1,1,3)) Irreducible real reflection group of rank 2 and type A2 sage: ReflectionGroup((2,1,3)) @@ -38,14 +37,14 @@ Also reducible types are allowed using concatenation:: - sage: ReflectionGroup(['A',3],(4,2,3)) # optional - gap3 + sage: ReflectionGroup(['A',3],(4,2,3)) Reducible complex reflection group of rank 6 and type A3 x G(4,2,3) Some special cases also occur, among them are:: - sage: W = ReflectionGroup((2,2,2)); W # optional - gap3 + sage: W = ReflectionGroup((2,2,2)); W Reducible real reflection group of rank 2 and type A1 x A1 - sage: W = ReflectionGroup((2,2,3)); W # optional - gap3 + sage: W = ReflectionGroup((2,2,3)); W Irreducible real reflection group of rank 3 and type A3 .. WARNING:: Uses the GAP3 package *Chevie* which is available as an @@ -57,13 +56,13 @@ We start with the example type `B_2`:: - sage: W = ReflectionGroup(['B',2]); W # optional - gap3 + sage: W = ReflectionGroup(['B',2]); W Irreducible real reflection group of rank 2 and type B2 Most importantly, observe that the group elements are usually represented by permutations of the roots:: - sage: for w in W: print(w) # optional - gap3 + sage: for w in W: print(w) () (1,3)(2,6)(5,7) (1,5)(2,4)(6,8) @@ -76,7 +75,7 @@ This has the drawback that one can hardly see anything. Usually, one would look at elements with either of the following methods:: - sage: for w in W: w.reduced_word() # optional - gap3 + sage: for w in W: w.reduced_word() [] [2] [1] @@ -86,7 +85,7 @@ [1, 2, 1] [1, 2, 1, 2] - sage: for w in W: w.reduced_word_in_reflections() # optional - gap3 + sage: for w in W: w.reduced_word_in_reflections() [] [2] [1] @@ -96,7 +95,7 @@ [4] [1, 3] - sage: for w in W: w.reduced_word(); w.to_matrix(); print("") # optional - gap3 + sage: for w in W: w.reduced_word(); w.to_matrix(); print("") [] [1 0] [0 1] @@ -133,7 +132,7 @@ The standard references for actions of complex reflection groups have the matrices acting on the right, so:: - sage: W.simple_reflection(1).to_matrix() # optional - gap3 + sage: W.simple_reflection(1).to_matrix() [-1 0] [ 2 1] @@ -233,8 +232,8 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio TESTS:: sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups - sage: W = ComplexReflectionGroups().example() # optional - gap3 - sage: TestSuite(W).run() # optional - gap3 + sage: W = ComplexReflectionGroups().example() + sage: TestSuite(W).run() """ W_components = [] reflection_type = [] @@ -331,9 +330,9 @@ def _irrcomp_repr_(self,W_type): TESTS:: - sage: W = ReflectionGroup(25,[4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 + sage: W = ReflectionGroup(25,[4,1,4],[1,1,4],[5,5,2]); W Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) - sage: for W_type in W._type: print(W._irrcomp_repr_(W_type)) # optional - gap3 + sage: for W_type in W._type: print(W._irrcomp_repr_(W_type)) ST25 G(4,1,4) A3 @@ -359,7 +358,7 @@ def _repr_(self): EXAMPLES:: - sage: W = ReflectionGroup(25, [4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 + sage: W = ReflectionGroup(25, [4,1,4],[1,1,4],[5,5,2]); W Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) """ type_str = '' @@ -379,8 +378,8 @@ def iteration_tracking_words(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W.iteration_tracking_words(): w # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W.iteration_tracking_words(): w () (1,4)(2,3)(5,6) (1,3)(2,5)(4,6) @@ -400,7 +399,6 @@ def index_set(self): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,4)) sage: W.index_set() (1, 2, 3) @@ -419,10 +417,10 @@ def simple_reflection(self, i): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.simple_reflection(1) # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.simple_reflection(1) (1,4)(2,3)(5,6) - sage: W.simple_reflections() # optional - gap3 + sage: W.simple_reflections() Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6)} """ return self.gens()[self._index_set_inverse[i]] @@ -439,9 +437,9 @@ def series(self): EXAMPLES:: - sage: ReflectionGroup((1,1,3)).series() # optional - gap3 + sage: ReflectionGroup((1,1,3)).series() ['A'] - sage: ReflectionGroup((3,1,3)).series() # optional - gap3 + sage: ReflectionGroup((3,1,3)).series() ['ST'] """ return [self._type[i]['series'] for i in range(len(self._type))] @@ -453,7 +451,6 @@ def hyperplane_index_set(self): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,4)) sage: W.hyperplane_index_set() (1, 2, 3, 4, 5, 6) @@ -478,20 +475,20 @@ def distinguished_reflections(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.distinguished_reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.distinguished_reflections() Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)} - sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) # optional - gap3 - sage: W.distinguished_reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) + sage: W.distinguished_reflections() Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6), 'c': (1,5)(2,4)(3,6)} - sage: W = ReflectionGroup((3,1,1)) # optional - gap3 - sage: W.distinguished_reflections() # optional - gap3 + sage: W = ReflectionGroup((3,1,1)) + sage: W.distinguished_reflections() Finite family {1: (1,2,3)} - sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 - sage: W.distinguished_reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),(3,1,2)) + sage: W.distinguished_reflections() Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), @@ -522,7 +519,6 @@ def distinguished_reflection(self, i): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,3)) sage: W.distinguished_reflection(1) (1,4)(2,3)(5,6) @@ -531,12 +527,12 @@ def distinguished_reflection(self, i): sage: W.distinguished_reflection(3) (1,5)(2,4)(3,6) - sage: W = ReflectionGroup((3,1,1),hyperplane_index_set=['a']) # optional - gap3 - sage: W.distinguished_reflection('a') # optional - gap3 + sage: W = ReflectionGroup((3,1,1),hyperplane_index_set=['a']) + sage: W.distinguished_reflection('a') (1,2,3) - sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 - sage: for i in range(W.number_of_reflection_hyperplanes()): # optional - gap3 + sage: W = ReflectionGroup((1,1,3),(3,1,2)) + sage: for i in range(W.number_of_reflection_hyperplanes()): ....: W.distinguished_reflection(i+1) (1,6)(2,5)(7,8) (1,5)(2,7)(6,8) @@ -563,8 +559,8 @@ def reflection_hyperplanes(self, as_linear_functionals=False, with_order=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for H in W.reflection_hyperplanes(): H # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for H in W.reflection_hyperplanes(): H Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 2] @@ -575,14 +571,14 @@ def reflection_hyperplanes(self, as_linear_functionals=False, with_order=False): Basis matrix: [ 1 -1] - sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 + sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H (1, -1/2) (1, -2) (1, 1) - sage: W = ReflectionGroup((2,1,2)) # optional - gap3 - sage: for H in W.reflection_hyperplanes(): H # optional - gap3 + sage: W = ReflectionGroup((2,1,2)) + sage: for H in W.reflection_hyperplanes(): H Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 1] @@ -596,13 +592,13 @@ def reflection_hyperplanes(self, as_linear_functionals=False, with_order=False): Basis matrix: [0 1] - sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 + sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H (1, -1) (1, -2) (0, 1) (1, 0) - sage: for H in W.reflection_hyperplanes(as_linear_functionals=True, with_order=True): H # optional - gap3 + sage: for H in W.reflection_hyperplanes(as_linear_functionals=True, with_order=True): H ((1, -1), 2) ((1, -2), 2) ((0, 1), 2) @@ -636,15 +632,15 @@ def reflection_hyperplane(self, i, as_linear_functional=False, with_order=False) EXAMPLES:: - sage: W = ReflectionGroup((2,1,2)) # optional - gap3 - sage: W.reflection_hyperplane(3) # optional - gap3 + sage: W = ReflectionGroup((2,1,2)) + sage: W.reflection_hyperplane(3) Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0] One can ask for the result as a linear form:: - sage: W.reflection_hyperplane(3, True) # optional - gap3 + sage: W.reflection_hyperplane(3, True) (0, 1) """ return self.reflection_hyperplanes(as_linear_functionals=as_linear_functional, with_order=with_order)[i] @@ -656,7 +652,6 @@ def reflection_index_set(self): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,4)) sage: W.reflection_index_set() (1, 2, 3, 4, 5, 6) @@ -677,20 +672,20 @@ def reflections(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.reflections() Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)} - sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) # optional - gap3 - sage: W.reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) + sage: W.reflections() Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6), 'c': (1,5)(2,4)(3,6)} - sage: W = ReflectionGroup((3,1,1)) # optional - gap3 - sage: W.reflections() # optional - gap3 + sage: W = ReflectionGroup((3,1,1)) + sage: W.reflections() Finite family {1: (1,2,3), 2: (1,3,2)} - sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 - sage: W.reflections() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),(3,1,2)) + sage: W.reflections() Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), @@ -714,7 +709,6 @@ def reflection(self,i): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,3)) sage: W.reflection(1) (1,4)(2,3)(5,6) @@ -723,10 +717,10 @@ def reflection(self,i): sage: W.reflection(3) (1,5)(2,4)(3,6) - sage: W = ReflectionGroup((3,1,1),reflection_index_set=['a','b']) # optional - gap3 - sage: W.reflection('a') # optional - gap3 + sage: W = ReflectionGroup((3,1,1),reflection_index_set=['a','b']) + sage: W.reflection('a') (1,2,3) - sage: W.reflection('b') # optional - gap3 + sage: W.reflection('b') (1,3,2) """ return self.reflections()[i] @@ -737,8 +731,8 @@ def reflection_character(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.reflection_character() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.reflection_character() [2, 0, -1] """ return self._gap_group.ReflectionCharacter().sage() @@ -760,13 +754,13 @@ def discriminant(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: W.discriminant() # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: W.discriminant() x0^6 - 3*x0^5*x1 - 3/4*x0^4*x1^2 + 13/2*x0^3*x1^3 - 3/4*x0^2*x1^4 - 3*x0*x1^5 + x1^6 - sage: W = ReflectionGroup(['B',2]) # optional - gap3 - sage: W.discriminant() # optional - gap3 + sage: W = ReflectionGroup(['B',2]) + sage: W.discriminant() x0^6*x1^2 - 6*x0^5*x1^3 + 13*x0^4*x1^4 - 12*x0^3*x1^5 + 4*x0^2*x1^6 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -787,16 +781,16 @@ def discriminant_in_invariant_ring(self, invariants=None): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.discriminant_in_invariant_ring() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.discriminant_in_invariant_ring() 6*t0^3*t1^2 - 18*t0^4*t2 + 9*t1^4 - 36*t0*t1^2*t2 + 24*t0^2*t2^2 - 8*t2^3 - sage: W = ReflectionGroup(['B',3]) # optional - gap3 - sage: W.discriminant_in_invariant_ring() # optional - gap3 + sage: W = ReflectionGroup(['B',3]) + sage: W.discriminant_in_invariant_ring() -t0^2*t1^2*t2 + 16*t0^3*t2^2 + 2*t1^3*t2 - 36*t0*t1*t2^2 + 108*t2^3 - sage: W = ReflectionGroup(['H',3]) # optional - gap3 - sage: W.discriminant_in_invariant_ring() # long time # optional - gap3 + sage: W = ReflectionGroup(['H',3]) + sage: W.discriminant_in_invariant_ring() # long time (-829*E(5) - 1658*E(5)^2 - 1658*E(5)^3 - 829*E(5)^4)*t0^15 + (213700*E(5) + 427400*E(5)^2 + 427400*E(5)^3 + 213700*E(5)^4)*t0^12*t1 + (-22233750*E(5) - 44467500*E(5)^2 - 44467500*E(5)^3 - 22233750*E(5)^4)*t0^9*t1^2 @@ -872,29 +866,29 @@ def is_crystallographic(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 + sage: W = ReflectionGroup((1,1,3)); W Irreducible real reflection group of rank 2 and type A2 - sage: W.is_crystallographic() # optional - gap3 + sage: W.is_crystallographic() True - sage: W = ReflectionGroup((2,1,3)); W # optional - gap3 + sage: W = ReflectionGroup((2,1,3)); W Irreducible real reflection group of rank 3 and type B3 - sage: W.is_crystallographic() # optional - gap3 + sage: W.is_crystallographic() True - sage: W = ReflectionGroup(23); W # optional - gap3 + sage: W = ReflectionGroup(23); W Irreducible real reflection group of rank 3 and type H3 - sage: W.is_crystallographic() # optional - gap3 + sage: W.is_crystallographic() False - sage: W = ReflectionGroup((3,1,3)); W # optional - gap3 + sage: W = ReflectionGroup((3,1,3)); W Irreducible complex reflection group of rank 3 and type G(3,1,3) - sage: W.is_crystallographic() # optional - gap3 + sage: W.is_crystallographic() False - sage: W = ReflectionGroup((4,2,2)); W # optional - gap3 + sage: W = ReflectionGroup((4,2,2)); W Irreducible complex reflection group of rank 2 and type G(4,2,2) - sage: W.is_crystallographic() # optional - gap3 + sage: W.is_crystallographic() False """ return self.is_real() and all(t.to_matrix().base_ring() is QQ for t in self.simple_reflections()) @@ -905,12 +899,12 @@ def number_of_irreducible_components(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.number_of_irreducible_components() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.number_of_irreducible_components() 1 - sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 - sage: W.number_of_irreducible_components() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),(2,1,3)) + sage: W.number_of_irreducible_components() 2 """ return len(self._type) @@ -922,12 +916,12 @@ def irreducible_components(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.irreducible_components() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.irreducible_components() [Irreducible real reflection group of rank 2 and type A2] - sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 - sage: W.irreducible_components() # optional - gap3 + sage: W = ReflectionGroup((1,1,3),(2,1,3)) + sage: W.irreducible_components() [Irreducible real reflection group of rank 2 and type A2, Irreducible real reflection group of rank 3 and type B3] """ @@ -949,21 +943,21 @@ def conjugacy_classes_representatives(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] [[], [1], [1, 2]] - sage: W = ReflectionGroup((1,1,4)) # optional - gap3 - sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + sage: W = ReflectionGroup((1,1,4)) + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] [[], [1], [1, 3], [1, 2], [1, 3, 2]] - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] [[], [1], [1, 1], [2, 1, 2, 1], [2, 1, 2, 1, 1], [2, 1, 1, 2, 1, 1], [2], [1, 2], [1, 1, 2]] - sage: W = ReflectionGroup(23) # optional - gap3 - sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + sage: W = ReflectionGroup(23) + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] [[], [1], [1, 2], @@ -985,22 +979,22 @@ def conjugacy_classes(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for C in W.conjugacy_classes(): sorted(C) # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for C in W.conjugacy_classes(): sorted(C) [()] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] - sage: W = ReflectionGroup((1,1,4)) # optional - gap3 - sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + sage: W = ReflectionGroup((1,1,4)) + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() True - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() True - sage: W = ReflectionGroup(23) # optional - gap3 - sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + sage: W = ReflectionGroup(23) + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() True """ return Family(self.conjugacy_classes_representatives(), @@ -1014,7 +1008,6 @@ def rank(self): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((1,1,3)) sage: W.rank() 2 @@ -1038,35 +1031,35 @@ def degrees(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,4)) # optional - gap3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((1,1,4)) + sage: W.degrees() (2, 3, 4) - sage: W = ReflectionGroup((2,1,4)) # optional - gap3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((2,1,4)) + sage: W.degrees() (2, 4, 6, 8) - sage: W = ReflectionGroup((4,1,4)) # optional - gap3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((4,1,4)) + sage: W.degrees() (4, 8, 12, 16) - sage: W = ReflectionGroup((4,2,4)) # optional - gap3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((4,2,4)) + sage: W.degrees() (4, 8, 8, 12) - sage: W = ReflectionGroup((4,4,4)) # optional - gap3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((4,4,4)) + sage: W.degrees() (4, 4, 8, 12) Examples of reducible types:: - sage: W = ReflectionGroup((1,1,4), (3,1,2)); W # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (3,1,2)); W Reducible complex reflection group of rank 5 and type A3 x G(3,1,2) - sage: W.degrees() # optional - gap3 + sage: W.degrees() (2, 3, 4, 3, 6) - sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 - sage: W.degrees() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) + sage: W.degrees() (2, 3, 4, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 2, 6, 10) """ if self.is_irreducible(): @@ -1085,32 +1078,32 @@ def codegrees(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,4)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((1,1,4)) + sage: W.codegrees() (2, 1, 0) - sage: W = ReflectionGroup((2,1,4)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((2,1,4)) + sage: W.codegrees() (6, 4, 2, 0) - sage: W = ReflectionGroup((4,1,4)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((4,1,4)) + sage: W.codegrees() (12, 8, 4, 0) - sage: W = ReflectionGroup((4,2,4)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((4,2,4)) + sage: W.codegrees() (12, 8, 4, 0) - sage: W = ReflectionGroup((4,4,4)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((4,4,4)) + sage: W.codegrees() (8, 8, 4, 0) - sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (3,1,2)) + sage: W.codegrees() (2, 1, 0, 3, 0) - sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 - sage: W.codegrees() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) + sage: W.codegrees() (2, 1, 0, 66, 60, 54, 48, 42, 36, 30, 24, 18, 12, 6, 0, 8, 4, 0) """ if self.is_irreducible(): @@ -1135,13 +1128,13 @@ def reflection_eigenvalues_family(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.reflection_eigenvalues_family() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.reflection_eigenvalues_family() Finite family {(): [0, 0], (1,4)(2,3)(5,6): [1/2, 0], (1,6,2)(3,5,4): [1/3, 2/3]} - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 - sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: reflection_eigenvalues = W.reflection_eigenvalues_family() + sage: for elt in sorted(reflection_eigenvalues.keys()): ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) () [0, 0] (1,3,9)(2,4,10)(6,11,17)(8,12,18)(14,19,23)(15,16,20)(21,22,24) [1/3, 0] @@ -1153,9 +1146,9 @@ def reflection_eigenvalues_family(self): (1,9,3)(2,24,16)(4,21,20)(5,13,7)(6,23,12)(8,17,19)(10,22,15)(11,14,18) [2/3, 2/3] (1,13,9,7,3,5)(2,14,24,18,16,11)(4,6,21,23,20,12)(8,22,17,15,19,10) [1/3, 5/6] - sage: W = ReflectionGroup(23) # optional - gap3 - sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 - sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 + sage: W = ReflectionGroup(23) + sage: reflection_eigenvalues = W.reflection_eigenvalues_family() + sage: for elt in sorted(reflection_eigenvalues.keys()): ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) () [0, 0, 0] (1,8,4)(2,21,3)(5,10,11)(6,18,17)(7,9,12)(13,14,15)(16,23,19)(20,25,26)(22,24,27)(28,29,30) [1/3, 2/3, 0] @@ -1187,8 +1180,8 @@ def reflection_eigenvalues(self, w, is_class_representative=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: ....: print('%s %s'%(w.reduced_word(), W.reflection_eigenvalues(w))) [] [0, 0] [2] [1/2, 0] @@ -1210,20 +1203,20 @@ def simple_roots(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.simple_roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.simple_roots() Finite family {1: (1, 0), 2: (0, 1)} - sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 - sage: W.simple_roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (2,1,2)) + sage: W.simple_roots() Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, 0, 1)} - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: W.simple_roots() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: W.simple_roots() Finite family {1: (1, 0), 2: (-1, 1)} - sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 - sage: W.simple_roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (3,1,2)) + sage: W.simple_roots() Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, -1, 1)} """ from sage.sets.family import Family @@ -1235,7 +1228,6 @@ def simple_root(self, i): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup(['A',3]) sage: W.simple_root(1) (1, 0, 0) @@ -1246,7 +1238,7 @@ def simple_root(self, i): TESTS:: - sage: W.simple_root(0) # optional - gap3 + sage: W.simple_root(0) Traceback (most recent call last): ... KeyError: 0 @@ -1262,20 +1254,20 @@ def simple_coroots(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.simple_coroots() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.simple_coroots() Finite family {1: (2, -1), 2: (-1, 2)} - sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 - sage: W.simple_coroots() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (2,1,2)) + sage: W.simple_coroots() Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, 2, -2), 5: (0, 0, 0, -1, 2)} - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: W.simple_coroots() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: W.simple_coroots() Finite family {1: (-2*E(3) - E(3)^2, 0), 2: (-1, 1)} - sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 - sage: W.simple_coroots() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (3,1,2)) + sage: W.simple_coroots() Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, -2*E(3) - E(3)^2, 0), 5: (0, 0, 0, -1, 1)} """ from sage.sets.family import Family @@ -1292,8 +1284,8 @@ def simple_coroot(self, i): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.simple_coroot(1) # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.simple_coroot(1) (2, -1, 0) """ return self.simple_coroots()[i] @@ -1310,14 +1302,14 @@ def independent_roots(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.independent_roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.independent_roots() Finite family {1: (1, 0), 2: (0, 1)} - sage: W = ReflectionGroup((4,2,3)) # optional - gap3 - sage: W.simple_roots() # optional - gap3 + sage: W = ReflectionGroup((4,2,3)) + sage: W.simple_roots() Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 3: (-1, 1, 0), 4: (0, -1, 1)} - sage: W.independent_roots() # optional - gap3 + sage: W.independent_roots() Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 4: (0, -1, 1)} """ Delta = self.simple_roots() @@ -1339,12 +1331,12 @@ def roots(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.roots() [(1, 0), (0, 1), (1, 1), (-1, 0), (0, -1), (-1, -1)] - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: W.roots() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: W.roots() [(1, 0), (-1, 1), (E(3), 0), (-E(3), 1), (0, 1), (1, -1), (0, E(3)), (1, -E(3)), (E(3)^2, 0), (-E(3)^2, 1), (E(3), -1), (E(3), -E(3)), (0, E(3)^2), (1, -E(3)^2), @@ -1352,16 +1344,16 @@ def roots(self): (E(3), -E(3)^2), (-E(3)^2, E(3)), (-1, E(3)^2), (-E(3), E(3)^2), (E(3)^2, -E(3)^2), (-E(3)^2, E(3)^2)] - sage: W = ReflectionGroup((4,2,2)) # optional - gap3 - sage: W.roots() # optional - gap3 + sage: W = ReflectionGroup((4,2,2)) + sage: W.roots() [(1, 0), (-E(4), 1), (-1, 1), (-1, 0), (E(4), 1), (1, 1), (0, -E(4)), (E(4), -1), (E(4), E(4)), (0, E(4)), (E(4), -E(4)), (0, 1), (1, -E(4)), (1, -1), (0, -1), (1, E(4)), (-E(4), 0), (-1, E(4)), (E(4), 0), (-E(4), E(4)), (-E(4), -1), (-E(4), -E(4)), (-1, -E(4)), (-1, -1)] - sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 - sage: W.roots() # optional - gap3 + sage: W = ReflectionGroup((1,1,4), (3,1,2)) + sage: W.roots() [(1, 0, 0, 0, 0), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0), (0, 0, 0, 1, 0), (0, 0, 0, -1, 1), (1, 1, 0, 0, 0), (0, 1, 1, 0, 0), (1, 1, 1, 0, 0), (-1, 0, 0, 0, 0), @@ -1390,16 +1382,16 @@ def braid_relations(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.braid_relations() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.braid_relations() [[[1, 2, 1], [2, 1, 2]]] - sage: W = ReflectionGroup((2,1,3)) # optional - gap3 - sage: W.braid_relations() # optional - gap3 + sage: W = ReflectionGroup((2,1,3)) + sage: W.braid_relations() [[[1, 2, 1, 2], [2, 1, 2, 1]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] - sage: W = ReflectionGroup((2,2,3)) # optional - gap3 - sage: W.braid_relations() # optional - gap3 + sage: W = ReflectionGroup((2,2,3)) + sage: W.braid_relations() [[[1, 2, 1], [2, 1, 2]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] """ if self.is_real(): @@ -1414,12 +1406,12 @@ def fundamental_invariants(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: W.fundamental_invariants() # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: W.fundamental_invariants() (-2*x0^2 + 2*x0*x1 - 2*x1^2, 6*x0^2*x1 - 6*x0*x1^2) - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: W.fundamental_invariants() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: W.fundamental_invariants() (x0^3 + x1^3, x0^3*x1^3) """ import re @@ -1455,11 +1447,11 @@ def jacobian_of_fundamental_invariants(self, invs=None): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: W.fundamental_invariants() # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: W.fundamental_invariants() (-2*x0^2 + 2*x0*x1 - 2*x1^2, 6*x0^2*x1 - 6*x0*x1^2) - sage: W.jacobian_of_fundamental_invariants() # optional - gap3 + sage: W.jacobian_of_fundamental_invariants() [ -4*x0 + 2*x1 2*x0 - 4*x1] [12*x0*x1 - 6*x1^2 6*x0^2 - 12*x0*x1] """ @@ -1484,8 +1476,8 @@ def primitive_vector_field(self, invs=None): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: W.primitive_vector_field() # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: W.primitive_vector_field() (3*x1/(6*x0^2 - 6*x0*x1 - 12*x1^2), 1/(6*x0^2 - 6*x0*x1 - 12*x1^2)) """ if not self.is_irreducible(): @@ -1508,8 +1500,8 @@ def apply_vector_field(self, f, vf=None): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: for x in W.primitive_vector_field()[0].parent().gens(): # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: for x in W.primitive_vector_field()[0].parent().gens(): ....: print(W.apply_vector_field(x)) 3*x1/(6*x0^2 - 6*x0*x1 - 12*x1^2) 1/(6*x0^2 - 6*x0*x1 - 12*x1^2) @@ -1535,23 +1527,23 @@ def cartan_matrix(self): EXAMPLES:: - sage: ReflectionGroup(['A',4]).cartan_matrix() # optional - gap3 + sage: ReflectionGroup(['A',4]).cartan_matrix() [ 2 -1 0 0] [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2] - sage: ReflectionGroup(['H',4]).cartan_matrix() # optional - gap3 + sage: ReflectionGroup(['H',4]).cartan_matrix() [ 2 E(5)^2 + E(5)^3 0 0] [E(5)^2 + E(5)^3 2 -1 0] [ 0 -1 2 -1] [ 0 0 -1 2] - sage: ReflectionGroup(4).cartan_matrix() # optional - gap3 + sage: ReflectionGroup(4).cartan_matrix() [-2*E(3) - E(3)^2 E(3)^2] [ -E(3)^2 -2*E(3) - E(3)^2] - sage: ReflectionGroup((4,2,2)).cartan_matrix() # optional - gap3 + sage: ReflectionGroup((4,2,2)).cartan_matrix() [ 2 -2*E(4) -2] [ E(4) 2 1 - E(4)] [ -1 1 + E(4) 2] @@ -1582,19 +1574,18 @@ def invariant_form(self, brute_force=False): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: F = W.invariant_form(); F # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: F = W.invariant_form(); F [ 1 -1/2 0] [-1/2 1 -1/2] [ 0 -1/2 1] To check that this is indeed the invariant form, see:: - sage: S = W.simple_reflections() # optional - gap3 - sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 + sage: S = W.simple_reflections() + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) True - sage: # optional - gap3 sage: W = ReflectionGroup(['B',3]) sage: F = W.invariant_form(); F [ 1 -1 0] @@ -1604,44 +1595,44 @@ def invariant_form(self, brute_force=False): sage: w * F * w.transpose().conjugate() == F True - sage: S = W.simple_reflections() # optional - gap3 - sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 + sage: S = W.simple_reflections() + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) True - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: F = W.invariant_form(); F # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: F = W.invariant_form(); F [1 0] [0 1] - sage: S = W.simple_reflections() # optional - gap3 - sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + sage: S = W.simple_reflections() + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) True It also worked for badly generated groups:: - sage: W = ReflectionGroup(7) # optional - gap3 - sage: W.is_well_generated() # optional - gap3 + sage: W = ReflectionGroup(7) + sage: W.is_well_generated() False - sage: F = W.invariant_form(); F # optional - gap3 + sage: F = W.invariant_form(); F [1 0] [0 1] - sage: S = W.simple_reflections() # optional - gap3 - sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + sage: S = W.simple_reflections() + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) True And also for reducible types:: - sage: W = ReflectionGroup(['B',3],(4,2,3),4,7); W # optional - gap3 + sage: W = ReflectionGroup(['B',3],(4,2,3),4,7); W Reducible complex reflection group of rank 10 and type B3 x G(4,2,3) x ST4 x ST7 - sage: F = W.invariant_form(); S = W.simple_reflections() # optional - gap3 - sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + sage: F = W.invariant_form(); S = W.simple_reflections() + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) True TESTS:: - sage: tests = [['A',3],['B',3],['F',4],(4,2,2),4,7] # optional - gap3 - sage: for ty in tests: # optional - gap3 + sage: tests = [['A',3],['B',3],['F',4],(4,2,2),4,7] + sage: for ty in tests: ....: W = ReflectionGroup(ty) ....: A = W.invariant_form() ....: B = W.invariant_form(brute_force=True) @@ -1713,8 +1704,8 @@ def _invariant_form_brute_force(self): EXAMPLES:: - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: W._invariant_form_brute_force() # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: W._invariant_form_brute_force() [1 0] [0 1] """ @@ -1774,7 +1765,6 @@ def invariant_form_standardization(self): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup((4,2,5)) sage: I = W.invariant_form() sage: A = W.invariant_form_standardization() @@ -1783,7 +1773,6 @@ def invariant_form_standardization(self): TESTS:: - sage: # optional - gap3 sage: W = ReflectionGroup(9) sage: A = W.invariant_form_standardization() sage: S = W.simple_reflections() @@ -1807,8 +1796,8 @@ def set_reflection_representation(self,refl_repr=None): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: w.to_matrix(); print("-----") [1 0] [0 1] ----- @@ -1828,8 +1817,8 @@ def set_reflection_representation(self,refl_repr=None): [-1 0] ----- - sage: W.set_reflection_representation({1: matrix([[0,1,0],[1,0,0],[0,0,1]]), 2: matrix([[1,0,0],[0,0,1],[0,1,0]])}) # optional - gap3 - sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 + sage: W.set_reflection_representation({1: matrix([[0,1,0],[1,0,0],[0,0,1]]), 2: matrix([[1,0,0],[0,0,1],[0,1,0]])}) + sage: for w in W: w.to_matrix(); print("-----") [1 0 0] [0 1 0] [0 0 1] @@ -1854,7 +1843,7 @@ def set_reflection_representation(self,refl_repr=None): [0 1 0] [1 0 0] ----- - sage: W.set_reflection_representation() # optional - gap3 + sage: W.set_reflection_representation() """ if refl_repr is None or set(refl_repr) == set(self.index_set()): self._reflection_representation = refl_repr @@ -1876,15 +1865,15 @@ def fake_degrees(self): EXAMPLES:: - sage: W = ReflectionGroup(12) # optional - gap3 - sage: W.fake_degrees() # optional - gap3 + sage: W = ReflectionGroup(12) + sage: W.fake_degrees() [1, q^12, q^11 + q, q^8 + q^4, q^7 + q^5, q^6 + q^4 + q^2, q^10 + q^8 + q^6, q^9 + q^7 + q^5 + q^3] - sage: W = ReflectionGroup(["H",4]) # optional - gap3 - sage: W.cardinality() # optional - gap3 + sage: W = ReflectionGroup(["H",4]) + sage: W.cardinality() 14400 - sage: sum(fdeg.subs(q=1)**2 for fdeg in W.fake_degrees()) # optional - gap3 + sage: sum(fdeg.subs(q=1)**2 for fdeg in W.fake_degrees()) 14400 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1918,7 +1907,6 @@ def coxeter_number(self, chi=None): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup(["H",4]) sage: W.coxeter_number() 30 @@ -1952,8 +1940,8 @@ def conjugacy_class_representative(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: ....: print('%s %s'%(w.reduced_word(), w.conjugacy_class_representative().reduced_word())) [] [] [2] [1] @@ -1974,8 +1962,8 @@ def conjugacy_class(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: sorted(w.conjugacy_class()) # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: sorted(w.conjugacy_class()) [()] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] @@ -2020,20 +2008,20 @@ def reflection_length(self, in_unitary_group=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: sorted([t.reflection_length() for t in W]) [0, 1, 1, 1, 2, 2] - sage: W = ReflectionGroup((2,1,2)) # optional - gap3 - sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + sage: W = ReflectionGroup((2,1,2)) + sage: sorted([t.reflection_length() for t in W]) [0, 1, 1, 1, 1, 2, 2, 2] - sage: W = ReflectionGroup((2,2,2)) # optional - gap3 - sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + sage: W = ReflectionGroup((2,2,2)) + sage: sorted([t.reflection_length() for t in W]) [0, 1, 1, 2] - sage: W = ReflectionGroup((3,1,2)) # optional - gap3 - sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + sage: W = ReflectionGroup((3,1,2)) + sage: sorted([t.reflection_length() for t in W]) [0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ W = self.parent() @@ -2058,9 +2046,9 @@ def _repr_(self): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 + sage: W = ReflectionGroup((1,1,3)); W Irreducible real reflection group of rank 2 and type A2 - sage: W = ReflectionGroup((3,1,4)); W # optional - gap3 + sage: W = ReflectionGroup((3,1,4)); W Irreducible complex reflection group of rank 4 and type G(3,1,4) """ type_str = self._irrcomp_repr_(self._type[0]) @@ -2096,8 +2084,8 @@ def is_coxeter_element(self, which_primitive=1, is_class_representative=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: ....: print('%s %s'%(w.reduced_word(), w.is_coxeter_element())) [] False [2] False @@ -2124,8 +2112,8 @@ def is_h_regular(self, is_class_representative=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: for w in W: ....: print('%s %s'%(w.reduced_word(), w.is_h_regular())) [] False [2] False @@ -2157,9 +2145,9 @@ def is_regular(self, h, is_class_representative=False): EXAMPLES:: - sage: W = ReflectionGroup((1,1,3)) # optional - gap3 - sage: h = W.coxeter_number() # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup((1,1,3)) + sage: h = W.coxeter_number() + sage: for w in W: ....: print("{} {}".format(w.reduced_word(), w.is_regular(h))) [] False [2] False @@ -2168,8 +2156,8 @@ def is_regular(self, h, is_class_representative=False): [2, 1] True [1, 2, 1] False - sage: W = ReflectionGroup(23); h = W.coxeter_number() # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup(23); h = W.coxeter_number() + sage: for w in W: ....: if w.is_regular(h): ....: w.reduced_word() [1, 2, 3] @@ -2199,7 +2187,6 @@ def is_regular(self, h, is_class_representative=False): Check that :trac:`25478` is fixed:: - sage: # optional - gap3 sage: W = ReflectionGroup(["A",5]) sage: w = W.from_reduced_word([1,2,3,5]) sage: w.is_regular(4) diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index 6cc6c6e25a2..4f01a5a2882 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -317,6 +317,7 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): EXAMPLES:: + sage: # needs sage.graphs sage: W = WeylGroup(['A',2], prefix='s', implementation="permutation") sage: for w in W: ....: w.reduced_word() @@ -835,6 +836,7 @@ cdef class RealReflectionGroupElement(ComplexReflectionGroupElement): EXAMPLES:: + sage: # needs sage.graphs sage: W = CoxeterGroup(['A',4], implementation="permutation") sage: s = W.simple_reflections() sage: w = s[2] * s[1] * s[3] diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py index aee6b1d9bd9..8ae2acee1c8 100644 --- a/src/sage/combinat/root_system/reflection_group_real.py +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -1,3 +1,4 @@ +# sage.doctest: optional - gap3 r""" Finite real reflection groups ------------------------------- @@ -16,11 +17,11 @@ The point of entry to work with reflection groups is :func:`~sage.combinat.root_system.reflection_group_real.ReflectionGroup` which can be used with finite Cartan-Killing types:: - sage: ReflectionGroup(['A',2]) # optional - gap3 + sage: ReflectionGroup(['A',2]) Irreducible real reflection group of rank 2 and type A2 - sage: ReflectionGroup(['F',4]) # optional - gap3 + sage: ReflectionGroup(['F',4]) Irreducible real reflection group of rank 4 and type F4 - sage: ReflectionGroup(['H',3]) # optional - gap3 + sage: ReflectionGroup(['H',3]) Irreducible real reflection group of rank 3 and type H3 AUTHORS: @@ -76,49 +77,49 @@ def ReflectionGroup(*args,**kwds): Cartan-Killing classification types:: - sage: W = ReflectionGroup(['A',3]); W # optional - gap3 + sage: W = ReflectionGroup(['A',3]); W Irreducible real reflection group of rank 3 and type A3 - sage: W = ReflectionGroup(['H',4]); W # optional - gap3 + sage: W = ReflectionGroup(['H',4]); W Irreducible real reflection group of rank 4 and type H4 - sage: W = ReflectionGroup(['I',5]); W # optional - gap3 + sage: W = ReflectionGroup(['I',5]); W Irreducible real reflection group of rank 2 and type I2(5) the complex infinite family `G(r,p,n)` with `p` divides `r`:: - sage: W = ReflectionGroup((1,1,4)); W # optional - gap3 + sage: W = ReflectionGroup((1,1,4)); W Irreducible real reflection group of rank 3 and type A3 - sage: W = ReflectionGroup((2,1,3)); W # optional - gap3 + sage: W = ReflectionGroup((2,1,3)); W Irreducible real reflection group of rank 3 and type B3 Chevalley-Shepard-Todd exceptional classification types:: - sage: W = ReflectionGroup(23); W # optional - gap3 + sage: W = ReflectionGroup(23); W Irreducible real reflection group of rank 3 and type H3 Cartan types and matrices:: - sage: ReflectionGroup(CartanType(['A',2])) # optional - gap3 + sage: ReflectionGroup(CartanType(['A',2])) Irreducible real reflection group of rank 2 and type A2 - sage: ReflectionGroup(CartanType((['A',2],['A',2]))) # optional - gap3 + sage: ReflectionGroup(CartanType((['A',2],['A',2]))) Reducible real reflection group of rank 4 and type A2 x A2 - sage: C = CartanMatrix(['A',2]) # optional - gap3 - sage: ReflectionGroup(C) # optional - gap3 + sage: C = CartanMatrix(['A',2]) + sage: ReflectionGroup(C) Irreducible real reflection group of rank 2 and type A2 multiples of the above:: - sage: W = ReflectionGroup(['A',2],['B',2]); W # optional - gap3 + sage: W = ReflectionGroup(['A',2],['B',2]); W Reducible real reflection group of rank 4 and type A2 x B2 - sage: W = ReflectionGroup(['A',2],4); W # optional - gap3 + sage: W = ReflectionGroup(['A',2],4); W Reducible complex reflection group of rank 4 and type A2 x ST4 - sage: W = ReflectionGroup((4,2,2),4); W # optional - gap3 + sage: W = ReflectionGroup((4,2,2),4); W Reducible complex reflection group of rank 4 and type G(4,2,2) x ST4 """ if not is_chevie_available(): @@ -258,8 +259,8 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio TESTS:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: TestSuite(W).run() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: TestSuite(W).run() """ W_types = tuple([tuple(W_type) if isinstance(W_type, (list,tuple)) else W_type for W_type in W_types]) @@ -283,8 +284,8 @@ def _repr_(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',3],['B',2],['I',5],['I',6]) # optional - gap3 - sage: W._repr_() # optional - gap3 + sage: W = ReflectionGroup(['A',3],['B',2],['I',5],['I',6]) + sage: W._repr_() 'Reducible real reflection group of rank 9 and type A3 x B2 x I2(5) x G2' """ type_str = '' @@ -325,9 +326,9 @@ def iteration(self, algorithm="breadth", tracking_words=True): EXAMPLES:: - sage: W = ReflectionGroup(["B",2]) # optional - gap3 + sage: W = ReflectionGroup(["B",2]) - sage: for w in W.iteration("breadth",True): # optional - gap3 + sage: for w in W.iteration("breadth",True): ....: print("%s %s"%(w, w._reduced_word)) () [] (1,3)(2,6)(5,7) [1] @@ -338,7 +339,7 @@ def iteration(self, algorithm="breadth", tracking_words=True): (1,7)(3,5)(4,8) [0, 1, 0] (1,5)(2,6)(3,7)(4,8) [0, 1, 0, 1] - sage: for w in W.iteration("depth", False): w # optional - gap3 + sage: for w in W.iteration("depth", False): w () (1,3)(2,6)(5,7) (1,5)(2,4)(6,8) @@ -360,9 +361,9 @@ def __iter__(self): EXAMPLES:: - sage: W = ReflectionGroup(["B",2]) # optional - gap3 + sage: W = ReflectionGroup(["B",2]) - sage: for w in W: print("%s %s"%(w, w._reduced_word)) # optional - gap3 + sage: for w in W: print("%s %s"%(w, w._reduced_word)) () [] (1,3)(2,6)(5,7) [1] (1,5)(2,4)(6,8) [0] @@ -381,12 +382,12 @@ def bipartite_index_set(self): EXAMPLES:: - sage: W = ReflectionGroup(["A",5]) # optional - gap3 - sage: W.bipartite_index_set() # optional - gap3 + sage: W = ReflectionGroup(["A",5]) + sage: W.bipartite_index_set() [[1, 3, 5], [2, 4]] - sage: W = ReflectionGroup(["A",5],index_set=['a','b','c','d','e']) # optional - gap3 - sage: W.bipartite_index_set() # optional - gap3 + sage: W = ReflectionGroup(["A",5],index_set=['a','b','c','d','e']) + sage: W.bipartite_index_set() [['a', 'c', 'e'], ['b', 'd']] """ L, R = self._gap_group.BipartiteDecomposition().sage() @@ -401,25 +402,25 @@ def cartan_type(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.cartan_type() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.cartan_type() ['A', 3] - sage: W = ReflectionGroup(['A',3], ['B',3]) # optional - gap3 - sage: W.cartan_type() # optional - gap3 + sage: W = ReflectionGroup(['A',3], ['B',3]) + sage: W.cartan_type() A3xB3 relabelled by {1: 3, 2: 2, 3: 1} TESTS: Check that dihedral types are handled properly:: - sage: W = ReflectionGroup(['I',3]); W # optional - gap3 + sage: W = ReflectionGroup(['I',3]); W Irreducible real reflection group of rank 2 and type A2 - sage: W = ReflectionGroup(['I',4]); W # optional - gap3 + sage: W = ReflectionGroup(['I',4]); W Irreducible real reflection group of rank 2 and type C2 - sage: W = ReflectionGroup(['I',5]); W # optional - gap3 + sage: W = ReflectionGroup(['I',5]); W Irreducible real reflection group of rank 2 and type I2(5) """ if len(self._type) == 1: @@ -440,8 +441,8 @@ def positive_roots(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 - sage: W.positive_roots() # optional - gap3 + sage: W = ReflectionGroup(['A',3], ['B',2]) + sage: W.positive_roots() [(1, 0, 0, 0, 0), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0), @@ -453,8 +454,8 @@ def positive_roots(self): (1, 1, 1, 0, 0), (0, 0, 0, 2, 1)] - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.positive_roots() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.positive_roots() [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (0, 1, 1), (1, 1, 1)] """ return self.roots()[:self.number_of_reflections()] @@ -465,8 +466,8 @@ def almost_positive_roots(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 - sage: W.almost_positive_roots() # optional - gap3 + sage: W = ReflectionGroup(['A',3], ['B',2]) + sage: W.almost_positive_roots() [(-1, 0, 0, 0, 0), (0, -1, 0, 0, 0), (0, 0, -1, 0, 0), @@ -483,8 +484,8 @@ def almost_positive_roots(self): (1, 1, 1, 0, 0), (0, 0, 0, 2, 1)] - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.almost_positive_roots() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.almost_positive_roots() [(-1, 0, 0), (0, -1, 0), (0, 0, -1), @@ -503,8 +504,8 @@ def root_to_reflection(self, root): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: for beta in W.roots(): W.root_to_reflection(beta) # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: for beta in W.roots(): W.root_to_reflection(beta) (1,4)(2,3)(5,6) (1,3)(2,5)(4,6) (1,5)(2,4)(3,6) @@ -527,8 +528,8 @@ def reflection_to_positive_root(self, r): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: for r in W.reflections(): # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: for r in W.reflections(): ....: print(W.reflection_to_positive_root(r)) (1, 0) (0, 1) @@ -557,15 +558,14 @@ def fundamental_weights(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 - sage: W.fundamental_weights() # optional - gap3 + sage: W = ReflectionGroup(['A',3], ['B',2]) + sage: W.fundamental_weights() Finite family {1: (3/4, 1/2, 1/4, 0, 0), 2: (1/2, 1, 1/2, 0, 0), 3: (1/4, 1/2, 3/4, 0, 0), 4: (0, 0, 0, 1, 1/2), 5: (0, 0, 0, 1, 1)} - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: W.fundamental_weights() # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: W.fundamental_weights() Finite family {1: (3/4, 1/2, 1/4), 2: (1/2, 1, 1/2), 3: (1/4, 1/2, 3/4)} - sage: # optional - gap3 sage: W = ReflectionGroup(['A',3]) sage: S = W.simple_reflections() sage: N = W.fundamental_weights() @@ -598,8 +598,8 @@ def fundamental_weight(self, i): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: [ W.fundamental_weight(i) for i in W.index_set() ] # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: [ W.fundamental_weight(i) for i in W.index_set() ] [(3/4, 1/2, 1/4), (1/2, 1, 1/2), (1/4, 1/2, 3/4)] """ return self.fundamental_weights()[i] @@ -611,8 +611,8 @@ def coxeter_diagram(self): EXAMPLES:: - sage: G = ReflectionGroup(['B',3]) # optional - gap3 - sage: G.coxeter_diagram().edges(labels=True, sort=True) # optional - gap3 + sage: G = ReflectionGroup(['B',3]) + sage: G.coxeter_diagram().edges(labels=True, sort=True) [(1, 2, 4), (2, 3, 3)] """ from sage.graphs.graph import Graph @@ -634,8 +634,8 @@ def coxeter_matrix(self): EXAMPLES:: - sage: G = ReflectionGroup(['A',3]) # optional - gap3 - sage: G.coxeter_matrix() # optional - gap3 + sage: G = ReflectionGroup(['A',3]) + sage: G.coxeter_matrix() [1 3 2] [3 1 3] [2 3 1] @@ -650,8 +650,8 @@ def right_coset_representatives(self, J): EXAMPLES:: - sage: W = ReflectionGroup(["A",3]) # optional - gap3 - sage: for J in Subsets([1,2,3]): W.right_coset_representatives(J) # optional - gap3 + sage: W = ReflectionGroup(["A",3]) + sage: for J in Subsets([1,2,3]): W.right_coset_representatives(J) [(), (2,5)(3,9)(4,6)(8,11)(10,12), (1,4)(2,8)(3,5)(7,10)(9,11), (1,7)(2,4)(5,6)(8,10)(11,12), (1,2,10)(3,6,5)(4,7,8)(9,12,11), (1,4,6)(2,3,11)(5,8,9)(7,10,12), (1,6,4)(2,11,3)(5,9,8)(7,12,10), @@ -705,8 +705,8 @@ def simple_root_index(self, i): EXAMPLES:: - sage: W = ReflectionGroup(['A',3]) # optional - gap3 - sage: [W.simple_root_index(i) for i in W.index_set()] # optional - gap3 + sage: W = ReflectionGroup(['A',3]) + sage: [W.simple_root_index(i) for i in W.index_set()] [0, 1, 2] """ return self._index_set_inverse[i] @@ -736,14 +736,12 @@ def bruhat_cone(self, x, y, side='upper', backend='cdd'): EXAMPLES:: - sage: # optional - gap3 sage: W = ReflectionGroup(['A',2]) sage: x = W.from_reduced_word([1]) sage: y = W.w0 sage: W.bruhat_cone(x, y) A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays - sage: # optional - gap3 sage: W = ReflectionGroup(['E',6]) sage: x = W.one() sage: y = W.w0 @@ -752,7 +750,6 @@ def bruhat_cone(self, x, y, side='upper', backend='cdd'): TESTS:: - sage: # optional - gap3 sage: W = ReflectionGroup(['A',2]) sage: x = W.one() sage: y = W.w0 @@ -805,8 +802,8 @@ def right_coset_representatives(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: for w in W: ....: rcr = w.right_coset_representatives() ....: print("%s %s"%(w.reduced_word(), ....: [v.reduced_word() for v in rcr])) @@ -834,8 +831,8 @@ def left_coset_representatives(self): EXAMPLES:: - sage: W = ReflectionGroup(['A',2]) # optional - gap3 - sage: for w in W: # optional - gap3 + sage: W = ReflectionGroup(['A',2]) + sage: for w in W: ....: lcr = w.left_coset_representatives() ....: print("%s %s"%(w.reduced_word(), ....: [v.reduced_word() for v in lcr])) @@ -856,7 +853,7 @@ def _repr_(self): EXAMPLES:: - sage: for i in [2..7]: ReflectionGroup(["I", i]) # optional - gap3 + sage: for i in [2..7]: ReflectionGroup(["I", i]) Reducible real reflection group of rank 2 and type A1 x A1 Irreducible real reflection group of rank 2 and type A2 Irreducible real reflection group of rank 2 and type C2 diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 80ed38c982f..d3e443d830b 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Root lattice realizations """ @@ -1117,7 +1116,7 @@ def nonnesting_partition_lattice(self, facade=False): sage: RS = R.root_lattice() sage: P = RS.nonnesting_partition_lattice(); P # needs sage.graphs Finite lattice containing 14 elements - sage: P.coxeter_transformation()**10 == 1 # needs sage.graphs + sage: P.coxeter_transformation()**10 == 1 # needs sage.graphs sage.libs.flint True sage: # needs sage.graphs @@ -1125,7 +1124,7 @@ def nonnesting_partition_lattice(self, facade=False): sage: RS = R.root_lattice() sage: P = RS.nonnesting_partition_lattice(); P Finite lattice containing 20 elements - sage: P.coxeter_transformation()**7 == 1 + sage: P.coxeter_transformation()**7 == 1 # needs sage.libs.flint True REFERENCES: @@ -1157,7 +1156,7 @@ def generalized_nonnesting_partition_lattice(self, m, facade=False): sage: RS = R.root_lattice() sage: P = RS.generalized_nonnesting_partition_lattice(2); P # needs sage.graphs Finite lattice containing 12 elements - sage: P.coxeter_transformation()**20 == 1 # needs sage.graphs + sage: P.coxeter_transformation()**20 == 1 # needs sage.graphs sage.libs.flint True """ Phi_plus = self.positive_roots() @@ -1789,8 +1788,8 @@ def tau_plus_minus(self): We explore the example of [CFZ2002]_ Eq.(1.3):: sage: S = RootSystem(['A',2]).root_lattice() - sage: taup, taum = S.tau_plus_minus() # needs sage.graphs - sage: for beta in S.almost_positive_roots(): # needs sage.graphs + sage: taup, taum = S.tau_plus_minus() # needs sage.graphs sage.libs.gap + sage: for beta in S.almost_positive_roots(): # needs sage.graphs sage.libs.gap ....: print("{} , {} , {}".format(beta, taup(beta), taum(beta))) -alpha[1] , alpha[1] , -alpha[1] alpha[1] , -alpha[1] , alpha[1] + alpha[2] @@ -1817,14 +1816,14 @@ def almost_positive_roots_decomposition(self): EXAMPLES:: - sage: RootSystem(['A',2]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs + sage: RootSystem(['A',2]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs sage.libs.gap [[-alpha[1], alpha[1], alpha[1] + alpha[2], alpha[2], -alpha[2]]] - sage: RootSystem(['B',2]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs + sage: RootSystem(['B',2]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs sage.libs.gap [[-alpha[1], alpha[1], alpha[1] + 2*alpha[2]], [-alpha[2], alpha[2], alpha[1] + alpha[2]]] - sage: RootSystem(['D',4]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs + sage: RootSystem(['D',4]).root_lattice().almost_positive_roots_decomposition() # needs sage.graphs sage.libs.gap [[-alpha[1], alpha[1], alpha[1] + alpha[2], alpha[2] + alpha[3] + alpha[4]], [-alpha[2], alpha[2], alpha[1] + alpha[2] + alpha[3] + alpha[4], alpha[1] + 2*alpha[2] + alpha[3] + alpha[4]], @@ -4262,8 +4261,8 @@ def weyl_action(self, element, inverse=False): Acting by an element of the Coxeter or Weyl group on a vector in its own lattice of definition (implemented by matrix multiplication on a vector):: - sage: w = wl.weyl_group().from_reduced_word([1, 2]) # needs sage.graphs - sage: mudom.weyl_action(w) # needs sage.graphs + sage: w = wl.weyl_group().from_reduced_word([1, 2]) # needs sage.graphs sage.libs.gap + sage: mudom.weyl_action(w) # needs sage.graphs sage.libs.gap Lambda[1] - 2*Lambda[3] Acting by an element of an isomorphic Coxeter or Weyl group (implemented by the diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index cb7fc672ac2..a375b3b8074 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -429,12 +429,13 @@ def max_quantum_element(self): EXAMPLES:: + sage: # needs sage.graphs sage.libs.gap sage: Qvee = RootSystem(['C',2]).coroot_lattice() - sage: Qvee.from_vector(vector([1,2])).max_quantum_element() # needs sage.graphs + sage: Qvee.from_vector(vector([1,2])).max_quantum_element() [2, 1, 2, 1] - sage: Qvee.from_vector(vector([1,1])).max_quantum_element() # needs sage.graphs + sage: Qvee.from_vector(vector([1,1])).max_quantum_element() [1, 2, 1] - sage: Qvee.from_vector(vector([0,2])).max_quantum_element() # needs sage.graphs + sage: Qvee.from_vector(vector([0,2])).max_quantum_element() [2] """ diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index a26b9afa75c..c80cffbdb87 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -158,7 +158,7 @@ class RootSystem(UniqueRepresentation, SageObject): In finite type `A`, we recover the natural representation of the symmetric group as group of permutation matrices:: - sage: RootSystem(["A",2]).ambient_space().weyl_group().simple_reflections() # needs sage.libs.pari + sage: RootSystem(["A",2]).ambient_space().weyl_group().simple_reflections() # needs sage.libs.gap sage.libs.pari Finite family {1: [0 1 0] [1 0 0] [0 0 1], @@ -169,7 +169,7 @@ class RootSystem(UniqueRepresentation, SageObject): In type `B`, `C`, and `D`, we recover the natural representation of the Weyl group as groups of signed permutation matrices:: - sage: RootSystem(["B",3]).ambient_space().weyl_group().simple_reflections() # needs sage.libs.pari + sage: RootSystem(["B",3]).ambient_space().weyl_group().simple_reflections() # needs sage.libs.gap sage.libs.pari Finite family {1: [0 1 0] [1 0 0] [0 0 1], @@ -203,15 +203,17 @@ class RootSystem(UniqueRepresentation, SageObject): Here is the orbit of the identity under the action of the finite group:: - sage: W = L.weyl_group() # needs sage.libs.pari - sage: S3 = [ w.action(id) for w in W.classical() ] # needs sage.graphs sage.libs.pari - sage: [L.classical()(x) for x in S3] # needs sage.graphs sage.libs.pari + sage: # needs sage.graphs sage.libs.gap sage.libs.pari + sage: W = L.weyl_group() + sage: S3 = [ w.action(id) for w in W.classical() ] + sage: [L.classical()(x) for x in S3] [(1, 2, 3), (3, 1, 2), (2, 3, 1), (2, 1, 3), (1, 3, 2), (3, 2, 1)] And the action of `s_0` on these yields:: - sage: s = W.simple_reflections() # needs sage.libs.pari - sage: [L.classical()(s[0].action(x)) for x in S3] # needs sage.graphs sage.libs.pari + sage: # needs sage.graphs sage.libs.gap sage.libs.pari + sage: s = W.simple_reflections() + sage: [L.classical()(s[0].action(x)) for x in S3] [(0, 2, 4), (-1, 1, 6), (-2, 3, 5), (0, 1, 5), (-1, 3, 4), (-2, 2, 6)] We can also plot various components of the ambient spaces:: diff --git a/src/sage/combinat/root_system/type_E.py b/src/sage/combinat/root_system/type_E.py index 5771d6d948f..7c3292d6ea7 100644 --- a/src/sage/combinat/root_system/type_E.py +++ b/src/sage/combinat/root_system/type_E.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Root system data for type E """ diff --git a/src/sage/combinat/root_system/type_F.py b/src/sage/combinat/root_system/type_F.py index 524dd1f7c35..5d699d31c5a 100644 --- a/src/sage/combinat/root_system/type_F.py +++ b/src/sage/combinat/root_system/type_F.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Root system data for type F """ diff --git a/src/sage/combinat/root_system/type_relabel.py b/src/sage/combinat/root_system/type_relabel.py index 350e290b32a..712937d08ce 100644 --- a/src/sage/combinat/root_system/type_relabel.py +++ b/src/sage/combinat/root_system/type_relabel.py @@ -10,7 +10,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute -from sage.sets.family import FiniteFamily +from sage.sets.family import Family, FiniteFamily from sage.combinat.root_system import cartan_type from sage.combinat.root_system import ambient_space from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations @@ -173,10 +173,10 @@ def __init__(self, type, relabelling): sage: rI5 = CartanType(['I',5]).relabel({1:0,2:1}) sage: rI5.root_system().ambient_space() """ - assert isinstance(relabelling, FiniteFamily) cartan_type.CartanType_decorator.__init__(self, type) - self._relabelling = relabelling._dictionary - self._relabelling_inverse = relabelling.inverse_family()._dictionary + relabelling = Family(relabelling) + self._relabelling = dict(relabelling.items()) + self._relabelling_inverse = dict(relabelling.inverse_family().items()) self._index_set = tuple(sorted(relabelling[i] for i in type.index_set())) # TODO: design an appropriate infrastructure to handle this # automatically? Maybe using categories and axioms? diff --git a/src/sage/combinat/root_system/weight_lattice_realizations.py b/src/sage/combinat/root_system/weight_lattice_realizations.py index be54faa6bcb..c33299f1012 100644 --- a/src/sage/combinat/root_system/weight_lattice_realizations.py +++ b/src/sage/combinat/root_system/weight_lattice_realizations.py @@ -551,7 +551,7 @@ def dynkin_diagram_automorphism_of_alcove_morphism(self, f): Translations by elements of the root lattice induce a trivial Dynkin diagram automorphism:: - sage: # needs sage.graphs + sage: # needs sage.graphs sage.libs.gap sage: R.dynkin_diagram_automorphism_of_alcove_morphism(alpha[0].translation) {0: 0, 1: 1, 2: 2} sage: R.dynkin_diagram_automorphism_of_alcove_morphism(alpha[1].translation) @@ -565,19 +565,19 @@ def dynkin_diagram_automorphism_of_alcove_morphism(self, f): sage: omega1 = Lambda[1] - Lambda[0] sage: omega2 = Lambda[2] - Lambda[0] - sage: # needs sage.graphs + sage: # needs sage.graphs sage.libs.gap sage: R.dynkin_diagram_automorphism_of_alcove_morphism(omega1.translation) {0: 1, 1: 2, 2: 0} sage: R.dynkin_diagram_automorphism_of_alcove_morphism(omega2.translation) {0: 2, 1: 0, 2: 1} - sage: # needs sage.graphs + sage: # needs sage.graphs sage.libs.gap sage: R = RootSystem(['C',2,1]).weight_lattice() sage: alpha = R.simple_roots() sage: R.dynkin_diagram_automorphism_of_alcove_morphism(alpha[1].translation) {0: 2, 1: 1, 2: 0} - sage: # needs sage.graphs + sage: # needs sage.graphs sage.libs.gap sage: R = RootSystem(['D',5,1]).weight_lattice() sage: Lambda = R.fundamental_weights() sage: omega1 = Lambda[1] - Lambda[0] @@ -704,9 +704,14 @@ def _test_reduced_word_of_translation(self, elements=None, **options): # dictionary assigning a simple root to its index rank_simple_roots = dict( (alpha[i],i) for i in self.index_set() ) + try: + W = self.weyl_group() + except ImportError: + return + for t in elements: t = t - self.base_ring()(t.level()/Lambda[0].level()) * Lambda[0] - w = self.weyl_group().from_reduced_word(self.reduced_word_of_translation(t)) + w = W.from_reduced_word(self.reduced_word_of_translation(t)) if self.null_root().is_zero(): # The following formula is only valid when the null root is zero tester.assertEqual(w.action(rho), rho + rho.level()*t) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 51baa89b661..a42929606b1 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.groups sage.modules +# sage.doctest: needs sage.graphs sage.groups sage.modules """ Weyl Character Rings """ @@ -691,7 +691,7 @@ def _demazure_weights(self, hwv, word="long", debug=False): dd = {} h = tuple(int(hwv.inner_product(alphacheck[j])) for j in self._space.index_set()) - dd[h] = int(1) + dd[h] = 1 return self._demazure_helper(dd, word=word, debug=debug) def _demazure_helper(self, dd, word="long", debug=False): diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index 2c787878768..96e5e29db5b 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -2968,7 +2968,7 @@ def _backward_format_output(self, obj1, obj2, output): return DecreasingHeckeFactorization(df) -class InsertionRules(): +class InsertionRules: r""" Catalog of rules for RSK-like insertion algorithms. """ diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index c7d5e98ea33..c08e1c7aef3 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -73,7 +73,7 @@ def _repr_(self): sage: S([[1,3],[2,4]]) {{1, 3}, {2, 4}} """ - return '{' + ', '.join(('{' + repr(sorted(x))[1:-1] + '}' for x in self)) + '}' + return '{' + ', '.join('{' + repr(sorted(x))[1:-1] + '}' for x in self) + '}' def __hash__(self): """ diff --git a/src/sage/combinat/set_partition_ordered.py b/src/sage/combinat/set_partition_ordered.py index 186835f7129..a76adfa8f9f 100644 --- a/src/sage/combinat/set_partition_ordered.py +++ b/src/sage/combinat/set_partition_ordered.py @@ -220,7 +220,7 @@ def _repr_(self): sage: OrderedSetPartition([[1,3],[2,4]]) [{1, 3}, {2, 4}] """ - return '[' + ', '.join(('{' + repr(sorted(x))[1:-1] + '}' for x in self)) + ']' + return '[' + ', '.join('{' + repr(sorted(x))[1:-1] + '}' for x in self) + ']' def check(self): """ diff --git a/src/sage/combinat/sf/k_dual.py b/src/sage/combinat/sf/k_dual.py index 4fa2e971e36..546695bc338 100644 --- a/src/sage/combinat/sf/k_dual.py +++ b/src/sage/combinat/sf/k_dual.py @@ -284,7 +284,7 @@ def _G_to_km_on_basis_single_level(self, w, m): return 0 ans = self.zero() for la in Partitions(m, max_part=self.k): - ans += g.homogeneous_basis_noncommutative_variables_zero_Hecke((la)).coefficient(w) * mon(la) + ans += g.homogeneous_basis_noncommutative_variables_zero_Hecke(la).coefficient(w) * mon(la) return ans def _AffineGrothendieck(self, w, m): diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 09a3ef98798..1358c5779df 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -1843,7 +1843,7 @@ def _creation_by_determinant_helper(self, k, part): INPUT: - ``self`` -- an element of the Macdonald `S` basis - - ``k`` -- an positive integer at least as big as the + - ``k`` -- a positive integer at least as big as the length of ``part`` - ``part`` -- a partition diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index bca95b7f9da..2b42ebb7be6 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -196,7 +196,7 @@ - Darij Grinberg (2013) Sym over rings that are not characteristic 0 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen # 2012 Anne Schilling # 2012 Mike Zabrocki @@ -210,8 +210,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -306,7 +306,7 @@ def is_SymmetricFunction(x): return isinstance(x, SymmetricFunctionAlgebra_generic.Element) ##################################################################### -## Bases categories +# Bases categories from sage.categories.realizations import Category_realization_of_parent @@ -1429,30 +1429,30 @@ def carlitz_shareshian_wachs(self, n, d, s, comparison=None): # be folly. if comparison is None: def check_word(w): - if sum((1 for i in range(n-1) if w[i] > w[i+1])) != d: + if sum(1 for i in range(n-1) if w[i] > w[i+1]) != d: return False - if sum((1 for i in range(n-1) if w[i] == w[i+1])) != s: + if sum(1 for i in range(n-1) if w[i] == w[i+1]) != s: return False return True elif comparison == -1: def check_word(w): - if sum((1 for i in range(n-1) if w[i] > w[i+1])) != d: + if sum(1 for i in range(n-1) if w[i] > w[i+1]) != d: return False - if sum((1 for i in range(n-1) if w[i] == w[i+1])) != s: + if sum(1 for i in range(n-1) if w[i] == w[i+1]) != s: return False return w[0] < w[-1] elif comparison == 0: def check_word(w): - if sum((1 for i in range(n-1) if w[i] > w[i+1])) != d: + if sum(1 for i in range(n-1) if w[i] > w[i+1]) != d: return False - if sum((1 for i in range(n-1) if w[i] == w[i+1])) != s: + if sum(1 for i in range(n-1) if w[i] == w[i+1]) != s: return False return w[0] == w[-1] elif comparison == 1: def check_word(w): - if sum((1 for i in range(n-1) if w[i] > w[i+1])) != d: + if sum(1 for i in range(n-1) if w[i] > w[i+1]) != d: return False - if sum((1 for i in range(n-1) if w[i] == w[i+1])) != s: + if sum(1 for i in range(n-1) if w[i] == w[i+1]) != s: return False return w[0] > w[-1] @@ -4619,10 +4619,10 @@ def internal_coproduct(self): @cached_function def hnimage(n): - return result_parent.sum((tensor([parent(s(lam)), parent(s(lam))]) - for lam in Partitions(n))) + return result_parent.sum(tensor([parent(s(lam)), parent(s(lam))]) + for lam in Partitions(n)) for lam, a in h(self): - result += a * prod((hnimage(i) for i in lam)) + result += a * prod(hnimage(i) for i in lam) return result kronecker_coproduct = internal_coproduct @@ -5588,7 +5588,7 @@ def _is_positive(self, s): True """ s_self = s(self) - return all(( _nonnegative_coefficients(c) for c in s_self.coefficients() )) + return all(_nonnegative_coefficients(c) for c in s_self.coefficients()) def degree(self): r""" diff --git a/src/sage/combinat/shard_order.py b/src/sage/combinat/shard_order.py index e40198fc27a..d33bc2173f6 100644 --- a/src/sage/combinat/shard_order.py +++ b/src/sage/combinat/shard_order.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Shard intersection order diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index e9a320cd0ea..a05a8b0bee8 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -553,7 +553,7 @@ def _ascii_art_table(self, unicode=False): else: l1 += uh + h*width if unicode: - l2 += u"{}{:^{width}}".format(v, e, width=width) + l2 += "{}{:^{width}}".format(v, e, width=width) else: l2 += "{}{:^{width}}".format(v, e, width=width) if i <= n: diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index 50618a6388f..9da145671bf 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Shuffle product of iterables diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 484b4ceb7f3..e525c67ee39 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Skew Partitions @@ -485,11 +484,11 @@ def _unicode_art_(self): out, inn = self inn = inn + [0] * (len(out) - len(inn)) if not any(self._list): - return UnicodeArt(u'∅') + return UnicodeArt('∅') if self.parent().options.convention == "French": - s, t, b, l, r, tr, tl, br, bl, x, h = list(u' ┴┬├┤┘└┐┌┼─') + s, t, b, l, r, tr, tl, br, bl, x, h = list(' ┴┬├┤┘└┐┌┼─') else: - s, t, b, l, r, tr, tl, br, bl, x, h = list(u' ┬┴├┤┐┌┘└┼─') + s, t, b, l, r, tr, tl, br, bl, x, h = list(' ┬┴├┤┐┌┘└┼─') # working with English conventions txt = [s * inn[0] + tl + t * (out[0] - inn[0] - 1) + tr] @@ -500,10 +499,10 @@ def _unicode_art_(self): i1 = inn[i + 1] if i0 == i1: - start = u' ' * i1 + l + start = ' ' * i1 + l d0 = 1 else: - start = u' ' * i1 + tl + start = ' ' * i1 + tl d0 = 0 if o0 == o1: diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 4718f29f199..3019522d21e 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Skew Tableaux diff --git a/src/sage/combinat/species/composition_species.py b/src/sage/combinat/species/composition_species.py index b60e5348985..ccfe0141803 100644 --- a/src/sage/combinat/species/composition_species.py +++ b/src/sage/combinat/species/composition_species.py @@ -116,7 +116,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) sage: c = L.generating_series()[:3] - sage: L._check() #False due to isomorphism types not being implemented # needs sage.libs.flint + sage: L._check() #False due to isomorphism types not being implemented # needs sage.libs.flint False sage: L == loads(dumps(L)) True @@ -234,7 +234,7 @@ def _cis(self, series_ring, base_ring): sage: E = species.SetSpecies() sage: C = species.CycleSpecies(weight=t) sage: S = E(C) - sage: S.isotype_generating_series()[:5] #indirect # needs sage.modules + sage: S.isotype_generating_series()[:5] #indirect # needs sage.modules [1, t, t^2 + t, t^3 + t^2 + t, t^4 + t^3 + 2*t^2 + t] We do the same thing with set partitions weighted by the number of diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index d3f1e9d3867..705dbd22494 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -553,7 +553,7 @@ def _series_helper(self, series_ring_class, prefix, base_ring=None): pass # Try to return things like self._gs_callable(base_ring). - # This is used when the subclass just provides an callable + # This is used when the subclass just provides a callable # for the coefficients of the generating series. Optionally, # the subclass can specify the order of the series. try: diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index 7672c8c7649..031940f5283 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -19,6 +19,7 @@ from sage.combinat.partition import _Partitions, Partitions_n from sage.combinat.tableau import Tableau, StandardTableaux_size, StandardTableaux_shape, StandardTableaux from sage.algebras.group_algebra import GroupAlgebra_class +from sage.algebras.cellular_basis import CellularBasis from sage.categories.weyl_groups import WeylGroups from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ @@ -688,8 +689,7 @@ def cell_poset(self): Finite poset containing 5 elements """ from sage.combinat.posets.posets import Poset - from sage.combinat.partition import Partitions - return Poset([Partitions(self.n), lambda x, y: y.dominates(x)]) + return Poset([Partitions_n(self.n), lambda x, y: y.dominates(x)]) def cell_module_indices(self, la): r""" @@ -732,40 +732,14 @@ def _from_cellular_index(self, x): sage: [S._from_cellular_index(i) for i in C.basis().keys()] [[1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1], [1, 2, 3] + [2, 1, 3], - [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [3, 1, 2], - [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1], - [1, 2, 3] + [1, 3, 2], + [1, 3, 2] + [3, 1, 2], + [1, 3, 2] + [2, 3, 1], + [1, 2, 3] + [3, 2, 1], [1, 2, 3]] sage: TestSuite(C).run() """ if ~factorial(self.n) not in self.base_ring(): - from sage.combinat.rsk import RSK_inverse - from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet - G = self.basis().keys() - R = self.base_ring() - one = R.one() - # check if the KL polynomials can be computed using ``coxeter3`` - try: - from sage.libs.coxeter3.coxeter_group import CoxeterGroup as Coxeter3Group - except ImportError: - # Falback to using the KL polynomial - from sage.combinat.kazhdan_lusztig import KazhdanLusztigPolynomial - from sage.groups.perm_gps.permgroup_named import SymmetricGroup - q = PolynomialRing(R, 'q').gen() - KLG = SymmetricGroup(self.n) - self._cellular_KL = KazhdanLusztigPolynomial(KLG, q) - polyfunc = self._cellular_KL.P - else: - self._cellular_KL = Coxeter3Group(['A', self.n+1]) - KLG = self._cellular_KL - polyfunc = self._cellular_KL.kazhdan_lusztig_polynomial - - def func(S, T, mult=None): - w = KLG.from_reduced_word(RSK_inverse(T, S, output="permutation").reduced_word()) - bruhat = RecursivelyEnumeratedSet([w], lambda u: u.bruhat_lower_covers(), structure='graded') - return self.element_class(self, {G.from_reduced_word(v.reduced_word()): R(c(q=one)) - for v in bruhat if (c := polyfunc(v, w))}) - + func = self.murphy_basis_element else: func = self.epsilon_ik @@ -2083,6 +2057,150 @@ def epsilon_ik(self, itab, ktab, star=0, mult='l2r'): else: return z.map_support(lambda x: x.inverse()) + def murphy_basis(self): + r""" + Return the :class:`Murphy basis + ` of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: M = SGA.murphy_basis() + sage: M(SGA.an_element()) + -C([1, 1, 1], [[1], [2], [3]], [[1], [2], [3]]) + + C([2, 1], [[1, 2], [3]], [[1, 2], [3]]) + + C([2, 1], [[1, 2], [3]], [[1, 3], [2]]) + + 2*C([2, 1], [[1, 3], [2]], [[1, 2], [3]]) + + 4*C([2, 1], [[1, 3], [2]], [[1, 3], [2]]) + - 3*C([3], [[1, 2, 3]], [[1, 2, 3]]) + """ + return MurphyBasis(self) + + @cached_method(key=lambda s, X, Y: (StandardTableaux()(X), StandardTableaux()(Y))) + def murphy_basis_element(self, S, T): + r""" + Return the Murphy basis element indexed by ``S`` and ``T``. + + .. SEEALSO:: + + :class:`~sage.combinat.symmetric_group_algebra.MurphyBasis` + + EXAMPLES:: + + sage: import itertools + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: for S, T in itertools.product(StandardTableaux([2,1]), repeat=2): + ....: print(S, T, SGA.murphy_basis_element(S, T)) + [[1, 3], [2]] [[1, 3], [2]] [1, 2, 3] + [2, 1, 3] + [[1, 3], [2]] [[1, 2], [3]] [1, 3, 2] + [3, 1, 2] + [[1, 2], [3]] [[1, 3], [2]] [1, 3, 2] + [2, 3, 1] + [[1, 2], [3]] [[1, 2], [3]] [1, 2, 3] + [3, 2, 1] + + TESTS:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: SGA.murphy_basis_element([[1,2,3,4]], [[1,2],[3,4]]) + Traceback (most recent call last): + ... + ValueError: [[1, 2, 3, 4]] is not an element of Standard tableaux of size 3 + sage: SGA.murphy_basis_element([[1,2,3]], [[1,2],[3]]) + Traceback (most recent call last): + ... + ValueError: S and T must have the same shape + """ + std_tab = StandardTableaux(self.n) + S = std_tab(S) + T = std_tab(T) + S = S.conjugate() + T = T.conjugate() + + la = S.shape() + if la != T.shape(): + raise ValueError("S and T must have the same shape") + + G = self.group() + ds = G(list(sum((row for row in S), ()))) + dt = G(list(sum((row for row in T), ()))) + return self.monomial(~ds) * self._row_stabilizer(la) * self.monomial(dt) + + @cached_method + def _row_stabilizer(self, la): + """ + Return the row stabilizer element of a canonical standard tableau + of shape ``la``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: for la in Partitions(3): + ....: print(la, SGA._row_stabilizer(la)) + [3] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + [2, 1] [1, 2, 3] + [2, 1, 3] + [1, 1, 1] [1, 2, 3] + """ + G = self.group() + return self.sum_of_monomials(G(list(w.tuple())) for w in la.young_subgroup()) + + def kazhdan_lusztig_cellular_basis(self): + r""" + Return the Kazhdan-Lusztig basis (at `q = 1`) of ``self`` + as a cellular basis. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: KL = SGA.kazhdan_lusztig_cellular_basis() + sage: KL(SGA.an_element()) + C([2, 1], [[1, 2], [3]], [[1, 2], [3]]) + + C([2, 1], [[1, 3], [2]], [[1, 2], [3]]) + + 2*C([2, 1], [[1, 3], [2]], [[1, 3], [2]]) + - 3*C([3], [[1, 2, 3]], [[1, 2, 3]]) + """ + return KLCellularBasis(self) + + @cached_method + def kazhdan_lusztig_basis_element(self, w): + r""" + Return the Kazhdan-Lusztig `C'_w` basis element at `q = 1`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: for w in SGA.group(): + ....: print(w, SGA.kazhdan_lusztig_basis_element(w)) + [1, 2, 3] [1, 2, 3] + [1, 3, 2] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] [1, 2, 3] + [2, 1, 3] + [2, 3, 1] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [3, 1, 2] + [3, 2, 1] [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + """ + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet + G = self.basis().keys() + R = self.base_ring() + one = R.one() + # check if the KL polynomials can be computed using ``coxeter3`` + try: + from sage.libs.coxeter3.coxeter_group import CoxeterGroup as Coxeter3Group + except ImportError: + # Falback to using the KL polynomial + from sage.combinat.kazhdan_lusztig import KazhdanLusztigPolynomial + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + q = PolynomialRing(R, 'q').gen() + self._KLG = SymmetricGroup(self.n) + self._cellular_KL = KazhdanLusztigPolynomial(self._KLG, q) + polyfunc = self._cellular_KL.P + else: + self._cellular_KL = Coxeter3Group(['A', self.n+1]) + self._KLG = self._cellular_KL + polyfunc = self._cellular_KL.kazhdan_lusztig_polynomial + + if w.parent() is not self._KLG: + w = self._KLG.from_reduced_word(w.reduced_word()) + bruhat = RecursivelyEnumeratedSet([w], lambda u: u.bruhat_lower_covers(), structure='graded') + return self.element_class(self, {G.from_reduced_word(v.reduced_word()): R(c(q=one)) + for v in bruhat if (c := polyfunc(v, w))}) + epsilon_ik_cache = {} @@ -2634,6 +2752,179 @@ def seminormal_test(n): raise ValueError("3.1.20 - %s, %s" % (tab, tab2)) return True + +####################### + + +class SGACellularBasis(CellularBasis): + r""" + A cellular basis of the symmetric group algebra. + """ + def __init__(self, SGA): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 3) + sage: M = SGA.murphy_basis() + sage: TestSuite(M).run() + sage: KL = SGA.kazhdan_lusztig_cellular_basis() + sage: TestSuite(KL).run() + """ + CellularBasis.__init__(self, SGA, self._to_sga) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: SGA.murphy_basis() + Murphy basis of Symmetric group algebra of order 4 over Finite Field of size 3 + sage: SGA.kazhdan_lusztig_cellular_basis() + Kazhdan-Lusztig basis of Symmetric group algebra of order 4 over Finite Field of size 3 + """ + return self._name + " basis of {}".format(self._algebra) + + @cached_method + def one_basis(self): + r""" + Return the index of the basis element for the multiplicative identity. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: M = SGA.murphy_basis() + sage: M.one_basis() + ([4], [[1, 2, 3, 4]], [[1, 2, 3, 4]]) + """ + la = _Partitions([self._algebra.n]) + col = la.standard_tableaux()[0] + return (la, col, col) + + @cached_method + def one(self): + r""" + Return the element `1` in ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: M = SGA.murphy_basis() + sage: M.one() + C([4], [[1, 2, 3, 4]], [[1, 2, 3, 4]]) + """ + return self.monomial(self.one_basis()) + + +class MurphyBasis(SGACellularBasis): + r""" + The Murphy basis of a symmetric group algebra. + + Let `R` be a commutative ring, and let `A = R[S_n]` denote the group + algebra (over `R`) of `S_n`. The *Murphy basis* is the basis of `A` + defined as follows. Let `S, T` be standard tableaux of shape `\lambda`. + Define `T^{\lambda}` as the standard tableau of shape `\lambda` with + the first row filled with `1, \ldots, \lambda_1`, the second row + `\lambda_1+1, \ldots, \lambda_1+\lambda_2`, and so on. Let `d(S)` be + the unique permutation such that `S = T^{\lambda} d(S)` under the natural + action. Then the Murphy basis element indexed by `S` and `T` is + + .. MATH:: + + M_{S'T'} = d(S)^{-1} R_{\lambda} d(T), + + where `S'` denotes the conjugate tableau. + The Murphy basis is a :class:`cellular basis + ` of `A`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 5) + sage: M = SGA.murphy_basis() + sage: for la in M.simple_module_parameterization(): + ....: CM = M.cell_module(la) + ....: print(la, CM.dimension(), CM.simple_module().dimension()) + [2, 2, 1] 5 4 + [3, 1, 1] 6 6 + [3, 2] 5 1 + [4, 1] 4 4 + [5] 1 1 + + REFERENCES: + + - [DJM1998]_ + - [Mathas2004]_ + """ + _name = "Murphy" + + def _to_sga(self, ind): + r""" + Return the element in the symmetric group algebra indexed by ``ind``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 3) + sage: M = SGA.murphy_basis() + sage: for ind in M.basis().keys(): + ....: print(ind, M._to_sga(ind)) + ([1, 1, 1], [[1], [2], [3]], [[1], [2], [3]]) [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + ([2, 1], [[1, 3], [2]], [[1, 3], [2]]) [1, 2, 3] + [2, 1, 3] + ([2, 1], [[1, 3], [2]], [[1, 2], [3]]) [1, 3, 2] + [3, 1, 2] + ([2, 1], [[1, 2], [3]], [[1, 3], [2]]) [1, 3, 2] + [2, 3, 1] + ([2, 1], [[1, 2], [3]], [[1, 2], [3]]) [1, 2, 3] + [3, 2, 1] + ([3], [[1, 2, 3]], [[1, 2, 3]]) [1, 2, 3] + """ + return self._algebra.murphy_basis_element(ind[1], ind[2]) + + +class KLCellularBasis(SGACellularBasis): + """ + The Kazhdan-Lusztig `C'` basis (at `q = 1`) of the symmetric group + algebra realized as a :class:`cellular basis + ` + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 5) + sage: KL = SGA.kazhdan_lusztig_cellular_basis() + sage: for la in KL.simple_module_parameterization(): + ....: CM = KL.cell_module(la) + ....: print(la, CM.dimension(), CM.simple_module().dimension()) + [2, 2, 1] 5 4 + [3, 1, 1] 6 6 + [3, 2] 5 1 + [4, 1] 4 4 + [5] 1 1 + """ + _name = "Kazhdan-Lusztig" + + def _to_sga(self, ind): + r""" + Return the element in the symmetric group algebra indexed by ``ind``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 3) + sage: KL = SGA.kazhdan_lusztig_cellular_basis() + sage: for ind in KL.basis().keys(): + ....: print(ind, KL._to_sga(ind)) + ([1, 1, 1], [[1], [2], [3]], [[1], [2], [3]]) [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + ([2, 1], [[1, 3], [2]], [[1, 3], [2]]) [1, 2, 3] + [2, 1, 3] + ([2, 1], [[1, 3], [2]], [[1, 2], [3]]) [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [3, 1, 2] + ([2, 1], [[1, 2], [3]], [[1, 3], [2]]) [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + ([2, 1], [[1, 2], [3]], [[1, 2], [3]]) [1, 2, 3] + [1, 3, 2] + ([3], [[1, 2, 3]], [[1, 2, 3]]) [1, 2, 3] + """ + from sage.combinat.rsk import RSK_inverse + S = ind[1] + T = ind[2] + w = RSK_inverse(T, S, output="permutation") + return self._algebra.kazhdan_lusztig_basis_element(w) + + ####################### diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index f716c7cd117..c4009853da6 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Tableaux diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 96fc0f0c51c..807823a4af7 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -279,7 +279,7 @@ class TableauTuple(CombinatorialElement): The :meth:`level` of a tableau tuple is the length of the tuples. This corresponds to the level of the corresponding highest weight module. - In sage a :class:`TableauTuple` looks an behaves like a real tuple of + In sage a :class:`TableauTuple` looks and behaves like a real tuple of (level 1) :class:`Tableaux`. Many of the operations which are defined on :class:`Tableau` extend to :class:`TableauTuples`. Tableau tuples of level 1 are just ordinary :class:`Tableau`. diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index a543b50461d..723557eb9ab 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -163,7 +163,7 @@ sage: T = TilingSolver(L, box=(8,8), reflection=True) sage: solution = next(T.solve()) # long time (7s) sage: G = sum([piece.show2d() for piece in solution], Graphics()) # long time (<1s), needs sage.plot - sage: G.show(aspect_ratio=1, axes=False) # long time (2s), needs sage.plot + sage: G.show(aspect_ratio=1, axes=False) # long time (2s), needs sage.plot Compute the number of solutions:: @@ -199,7 +199,7 @@ sage: T = TilingSolver(L, box=(8,8,1)) sage: solution = next(T.solve()) # long time (8s) sage: G = sum([p.show3d(size=0.85) for p in solution], Graphics()) # long time (<1s), needs sage.plot - sage: G.show(aspect_ratio=1, viewer='tachyon') # long time (2s), needs sage.plot + sage: G.show(aspect_ratio=1, viewer='tachyon') # long time (2s), needs sage.plot Let us compute the number of solutions:: diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index a5edc7146ea..6f9fad1c5dd 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Introduction to combinatorics in Sage diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index 1de8838df00..19ac17054b8 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Alphabet diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 35fd6e69c55..41613ed61e2 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Finite word diff --git a/src/sage/combinat/words/lyndon_word.py b/src/sage/combinat/words/lyndon_word.py index 4c978680380..209f9099f18 100644 --- a/src/sage/combinat/words/lyndon_word.py +++ b/src/sage/combinat/words/lyndon_word.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Lyndon words """ diff --git a/src/sage/combinat/words/morphic.py b/src/sage/combinat/words/morphic.py index 0db7a8db6fe..c036db8e2ec 100644 --- a/src/sage/combinat/words/morphic.py +++ b/src/sage/combinat/words/morphic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Morphic words diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 279c238718e..abb99d9f685 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Word morphisms/substitutions @@ -150,7 +149,7 @@ def get_cycles(f, domain): return cycles -class PeriodicPointIterator(): +class PeriodicPointIterator: r""" (Lazy) constructor of the periodic points of a word morphism. @@ -3158,7 +3157,7 @@ def immortal_letters(self): [] """ if not self.is_self_composable(): - raise TypeError(f'self ({self}) is not an self-composable') + raise TypeError(f'self ({self}) is not a self-composable') forward = {} backward = {letter: set() for letter in self._morph} diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 5c84a784533..5106615ae9b 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -425,7 +425,7 @@ def __init__(self, alphabet, steps): #Construction of the steps from sage.structure.element import Vector - if all((isinstance(x, Vector) for x in steps)): + if all(isinstance(x, Vector) for x in steps): vsteps = steps else: try: diff --git a/src/sage/combinat/words/word.py b/src/sage/combinat/words/word.py index 132195589e9..222501a5dfd 100644 --- a/src/sage/combinat/words/word.py +++ b/src/sage/combinat/words/word.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Word classes @@ -197,11 +196,11 @@ def Word(data=None, alphabet=None, length=None, datatype=None, caching=True, RSK #if a list of a semistandard and a standard tableau or a pair of lists from sage.combinat.tableau import Tableau if isinstance(RSK_data, (tuple, list)) and len(RSK_data) == 2 and \ - all((isinstance(x, Tableau) for x in RSK_data)): + all(isinstance(x, Tableau) for x in RSK_data): from sage.combinat.rsk import RSK_inverse return RSK_inverse(*RSK_data, output='word') elif isinstance(RSK_data, (tuple, list)) and len(RSK_data) == 2 and \ - all((isinstance(x, (list, tuple)) for x in RSK_data)): + all(isinstance(x, (list, tuple)) for x in RSK_data): from sage.combinat.rsk import RSK_inverse P,Q = map(Tableau, RSK_data) return RSK_inverse(P, Q, 'word') diff --git a/src/sage/combinat/words/word_datatypes.pyx b/src/sage/combinat/words/word_datatypes.pyx index daf21a97469..9ab88d799ba 100644 --- a/src/sage/combinat/words/word_datatypes.pyx +++ b/src/sage/combinat/words/word_datatypes.pyx @@ -583,7 +583,7 @@ cdef class WordDatatype_str(WordDatatype): INPUT: - - ``other`` - word represented by an str + - ``other`` - word represented by a str OUTPUT: diff --git a/src/sage/combinat/words/word_generators.py b/src/sage/combinat/words/word_generators.py index 58886bd9108..641319f2ee7 100644 --- a/src/sage/combinat/words/word_generators.py +++ b/src/sage/combinat/words/word_generators.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Common words @@ -314,7 +313,7 @@ def __reduce__(self): return self.__class__, (self.__p, self.__q, self.parent().alphabet()) -class WordGenerator(): +class WordGenerator: r""" Constructor of several famous words. diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index 0149df17598..2e3f82bede1 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Set of words diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index d24b87fe5d4..ce6c63f66f3 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -211,7 +211,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, [-4 -3 2 -5 0 0 0 0 0 1] ] - sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) + sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) # needs fpylll Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 3e1615e5fd4..7b0a3263a32 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -9,10 +9,11 @@ is included by default with Sage. It contains Weierstrass equations, rank, and torsion for curves up to conductor 10000. -The large database includes all curves in John Cremona's tables. It -also includes data related to the BSD conjecture and modular degrees -for all of these curves, and generators for the Mordell-Weil -groups. To install it, run the following in the shell:: +The large database includes all curves in John Cremona's tables. It also +includes data related to the BSD conjecture and modular degrees for all of +these curves, and generators for the Mordell-Weil groups. To install it via the +optional :ref:`database_cremona_ellcurve ` +package, run the following command in the shell :: sage -i database_cremona_ellcurve diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index b78e582d91e..d25ef5ca630 100644 --- a/src/sage/databases/cubic_hecke_db.py +++ b/src/sage/databases/cubic_hecke_db.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Cubic Hecke Database +Cubic Hecke database This module contains the class :class:`CubicHeckeDataBase` which serves as an interface to `Ivan Marin's data files @@ -31,10 +31,25 @@ of linear forms on the cubic Hecke algebra on at most four strands satisfying the Markov trace condition for its cubic Hecke subalgebras. +To use the database, you need to install the optional +:ref:`database_cubic_hecke ` package by the Sage +command :: + + sage -i database_cubic_hecke + +EXAMPLES:: + + sage: # optional - database_cubic_hecke + sage: from sage.databases.cubic_hecke_db import CubicHeckeDataBase + sage: cha_db = CubicHeckeDataBase() + sage: cha_db + + AUTHORS: -- Sebastian Oehms (May 2020): initial version -- Sebastian Oehms (March 2022): PyPi version and Markov trace functionality +- Sebastian Oehms (2020-05): initial version +- Sebastian Oehms (2022-03): PyPi version and Markov trace functionality + """ ############################################################################## diff --git a/src/sage/databases/cunningham_tables.py b/src/sage/databases/cunningham_tables.py index 9c23bf31816..94862b98561 100644 --- a/src/sage/databases/cunningham_tables.py +++ b/src/sage/databases/cunningham_tables.py @@ -1,5 +1,19 @@ r""" -Cunningham table +Cunningham tables + +This module provides :func:`cunningham_prime_factors`, which lists the prime +numbers occurring in the factorization of numbers of type `b^n+1` or `b^n-1` +with `b \in \{2,3,5,6,7,10,11,12\}`. For an introduction to Cunningham prime +factors, see :wikipedia:`Cunningham_Project`. The data becomes available if you +install the optional :ref:`cunningham_tables ` package by +the command :: + + sage -i cunningham_tables + +AUTHORS: + +- Yann Laigle-Chapuy (2009-10-18): initial version + """ import os @@ -16,12 +30,25 @@ def cunningham_prime_factors(): They occur in the factorization of numbers of type `b^n+1` or `b^n-1` with `b \in \{2,3,5,6,7,10,11,12\}`. - Data from http://cage.ugent.be/~jdemeyer/cunningham/ + EXAMPLES:: + + sage: # optional - cunningham_tables + sage: from sage.databases.cunningham_tables import cunningham_prime_factors + sage: cunningham_prime_factors() + [2, + 3, + 5, + 7, + 11, + 13, + 17, + ... """ file = os.path.join(SAGE_SHARE,'cunningham_tables','cunningham_prime_factors.sobj') if os.path.exists(file): return [Integer(_) for _ in load(file)] else: from warnings import warn - warn("You might consider installing the optional package for factoring Cunningham numbers with the following command: ``sage -i cunningham_tables``") + warn("You might consider installing the optional package for factoring Cunningham numbers" + " with the following command: ``sage -i cunningham_tables``") return [] diff --git a/src/sage/databases/db_class_polynomials.py b/src/sage/databases/db_class_polynomials.py index 642865a5d63..923d3a419ee 100644 --- a/src/sage/databases/db_class_polynomials.py +++ b/src/sage/databases/db_class_polynomials.py @@ -1,5 +1,23 @@ """ -Database of Hilbert Polynomials +Database of Hilbert polynomials + +This module gives access to the database of Hilbert class polynomials. To use +the database, you need to install the optional :ref:`database_kohel +` package by the Sage command :: + + sage -i database_kohel + +EXAMPLES:: + + sage: # optional - database_kohel + sage: db = HilbertClassPolynomialDatabase() + sage: db[32] + x^2 - 52250000*x + 12167000000 + +AUTHORS: + +- David Kohel (2006-08-04): initial version + """ # **************************************************************************** # Copyright (C) 2006 David Kohel @@ -45,8 +63,9 @@ def __getitem__(self, disc): r""" TESTS:: + sage: # optional - database_kohel sage: db = HilbertClassPolynomialDatabase() - sage: db[32] # optional - database_kohel + sage: db[32] x^2 - 52250000*x + 12167000000 sage: db[123913912] Traceback (most recent call last): @@ -66,16 +85,17 @@ class HilbertClassPolynomialDatabase(ClassPolynomialDatabase): EXAMPLES:: + sage: # optional - database_kohel sage: db = HilbertClassPolynomialDatabase() - sage: db[-4] # optional - database_kohel + sage: db[-4] x - 1728 - sage: db[-7] # optional - database_kohel + sage: db[-7] x + 3375 - sage: f = db[-23]; f # optional - database_kohel + sage: f = db[-23]; f x^3 + 3491750*x^2 - 5151296875*x + 12771880859375 - sage: f.discriminant().factor() # optional - database_kohel + sage: f.discriminant().factor() -1 * 5^18 * 7^12 * 11^4 * 17^2 * 19^2 * 23 - sage: db[-23] # optional - database_kohel + sage: db[-23] x^3 + 3491750*x^2 - 5151296875*x + 12771880859375 """ model = "Cls" diff --git a/src/sage/databases/db_modular_polynomials.py b/src/sage/databases/db_modular_polynomials.py index 4e0422539fc..025682c05ae 100644 --- a/src/sage/databases/db_modular_polynomials.py +++ b/src/sage/databases/db_modular_polynomials.py @@ -1,5 +1,26 @@ """ -Database of Modular Polynomials +Database of modular polynomials + +This module gives access to the database of modular polynomials. To use the +database, you need to install the optional :ref:`database_kohel +` package by the Sage command :: + + sage -i database_kohel + +EXAMPLES:: + + sage: # optional - database_kohel + sage: DBMP = ClassicalModularPolynomialDatabase() + sage: f = DBMP[29] + sage: f.degree() + 58 + sage: f.coefficient([28,28]) + 400152899204646997840260839128 + +AUTHORS: + +- David Kohel (2006-08-04): initial version + """ # **************************************************************************** # Copyright (C) 2006 William Stein @@ -48,8 +69,9 @@ def _dbz_to_integer_list(name): r""" TESTS:: + sage: # optional - database_kohel sage: from sage.databases.db_modular_polynomials import _dbz_to_integer_list - sage: _dbz_to_integer_list('PolMod/Atk/pol.002.dbz') # optional - database_kohel + sage: _dbz_to_integer_list('PolMod/Atk/pol.002.dbz') [[3, 0, 1], [2, 1, -1], [2, 0, 744], @@ -58,9 +80,9 @@ def _dbz_to_integer_list(name): [0, 2, 1], [0, 1, 7256], [0, 0, 15252992]] - sage: _dbz_to_integer_list('PolMod/Cls/pol.001.dbz') # optional - database_kohel + sage: _dbz_to_integer_list('PolMod/Cls/pol.001.dbz') [[1, 0, 1]] - sage: _dbz_to_integer_list('PolMod/Eta/pol.002.dbz') # optional - database_kohel + sage: _dbz_to_integer_list('PolMod/Eta/pol.002.dbz') [[3, 0, 1], [2, 0, 48], [1, 1, -1], [1, 0, 768], [0, 0, 4096]] """ from sage.rings.integer import Integer @@ -130,14 +152,14 @@ def __getitem__(self, level): EXAMPLES:: + sage: # optional - database_kohel sage: DBMP = ClassicalModularPolynomialDatabase() - sage: f = DBMP[29] # optional - database_kohel - sage: f.degree() # optional - database_kohel + sage: f = DBMP[29] + sage: f.degree() 58 - sage: f.coefficient([28,28]) # optional - database_kohel + sage: f.coefficient([28,28]) 400152899204646997840260839128 - - sage: DBMP[50] # optional - database_kohel + sage: DBMP[50] Traceback (most recent call last): ... ValueError: file not found in the Kohel database diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 5b8455a49a2..e1e33e2839c 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -1,38 +1,28 @@ # -*- coding: utf-8 -*- r""" -FindStat - the Combinatorial Statistic Finder. +FindStat - the search engine for combinatorial statistics and maps -The FindStat database can be found at:: +The interface to the FindStat database is :: sage: findstat() The Combinatorial Statistic Finder (https://www.findstat.org/) -Fix the following three notions: +We use the following three notions - A *combinatorial collection* is a set `S` with interesting combinatorial properties, - a *combinatorial map* is a combinatorially interesting map `f: S \to S'` between combinatorial collections, and - a *combinatorial statistic* is a combinatorially interesting map `s: S \to \ZZ`. -You can use the sage interface to FindStat to: +You can use the FindStat interface to - identify a combinatorial statistic or map given the values on a few small objects, - obtain more terms, formulae, references, etc. for a given statistic or map, - edit statistics and maps and submit new statistics. -AUTHORS: - -- Martin Rubey (2015): initial version. -- Martin Rubey (2020): rewrite, adapt to new FindStat API - -The main entry points ---------------------- -.. csv-table:: - :class: contentstable - :widths: 20, 40 - :delim: | +The main entry points to the database are - :func:`findstat` | search for matching statistics. - :func:`findmap` | search for matching maps. +- :func:`findstat` to search for matching statistics, +- :func:`findmap` to search for matching maps. A guided tour ------------- @@ -195,8 +185,10 @@ def mapping(sigma): :meth:`FindStatStatistic.submit` your changes for review by the FindStat team. -Classes and methods -------------------- +AUTHORS: + +- Martin Rubey (2015): initial version +- Martin Rubey (2020): rewrite, adapt to new FindStat API """ # **************************************************************************** @@ -910,7 +902,7 @@ def findstat(query=None, values=None, distribution=None, domain=None, must be ``None``. - a list of pairs of the form ``(object, value)``, or a - dictionary from sage objects to integer values. The keyword + dictionary from Sage objects to integer values. The keyword arguments ``depth`` and ``max_values`` are passed to the finder, ``values`` and ``distribution`` must be ``None``. @@ -1163,7 +1155,7 @@ def findmap(*args, **kwargs): forms: - a list of pairs of the form ``(object, value)``, or a - dictionary from sage objects to sage objects. + dictionary from Sage objects to Sage objects. - a list of pairs of the form ``(list of objects, list of values)``, or a single pair of the form ``(list of objects, @@ -1763,7 +1755,7 @@ def set_references_raw(self, value): def sage_code(self): r""" - Return the sage code associated with the statistic or map. + Return the Sage code associated with the statistic or map. OUTPUT: @@ -1842,7 +1834,7 @@ def first_terms(self): OUTPUT: - A dictionary from sage objects representing an element of the + A dictionary from Sage objects representing an element of the appropriate collection to integers. This method is overridden in :class:`FindStatStatisticQuery`. @@ -2261,7 +2253,7 @@ def set_first_terms(self, values): INPUT: - a list of pairs of the form ``(object, value)`` where - ``object`` is a sage object representing an element of the + ``object`` is a Sage object representing an element of the appropriate collection and ``value`` is an integer. This information is used when submitting the statistic with @@ -2298,7 +2290,7 @@ def code(self): OUTPUT: - A string. Contributors are encouraged to submit sage code in the form:: + A string. Contributors are encouraged to submit Sage code in the form:: def statistic(x): ... @@ -4061,9 +4053,9 @@ class FindStatCollection(Element, - an integer designating the FindStat id of the collection, or - - a sage object belonging to a collection, or + - a Sage object belonging to a collection, or - - an iterable producing a sage object belonging to a collection. + - an iterable producing a Sage object belonging to a collection. EXAMPLES:: @@ -4277,7 +4269,7 @@ def in_range(self, element): INPUT: - - ``element`` -- a sage object that belongs to the collection. + - ``element`` -- a Sage object that belongs to the collection. OUTPUT: @@ -4452,7 +4444,7 @@ def from_string(self): OUTPUT: - The function that produces the sage object given its FindStat + The function that produces the Sage object given its FindStat representation as a string. EXAMPLES:: diff --git a/src/sage/databases/jones.py b/src/sage/databases/jones.py index 26d4dbaa373..aaab1397f0a 100644 --- a/src/sage/databases/jones.py +++ b/src/sage/databases/jones.py @@ -1,9 +1,10 @@ r""" John Jones's tables of number fields -In order to use the Jones database, the optional database package -must be installed using the Sage command !sage -i -database_jones_numfield +In order to use the Jones database, the optional :ref:`database_jones_numfield +` package must be installed using the Sage command :: + + sage -i database_jones_numfield This is a table of number fields with bounded ramification and degree `\leq 6`. You can query the database for all number diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index df0b8310456..72e39796965 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -1,15 +1,29 @@ # -*- coding: utf-8 -*- r""" -KnotInfo Database +KnotInfo database -This module contains the class :class:`KnotInfoDataBase` and auxiliary classes -for it which serves as an interface to the lists of named knots and links provided +This module contains the class :class:`KnotInfoDataBase` and auxiliary classes +for it, which serves as an interface to the lists of named knots and links provided at the web-pages `KnotInfo `__ and `LinkInfo `__. +To use the database, you need to install the optional :ref:`database_knotinfo +` package by the Sage command :: + + sage -i database_knotinfo + +EXAMPLES:: + + sage: # optional - database_knotinfo + sage: from sage.databases.knotinfo_db import KnotInfoDataBase + sage: ki_db = KnotInfoDataBase() + sage: ki_db + + AUTHORS: -- Sebastian Oehms August 2020: initial version +- Sebastian Oehms (2020-08): initial version + """ ############################################################################## # Copyright (C) 2020 Sebastian Oehms diff --git a/src/sage/databases/odlyzko.py b/src/sage/databases/odlyzko.py index 547baad0625..4ee78b7de0a 100644 --- a/src/sage/databases/odlyzko.py +++ b/src/sage/databases/odlyzko.py @@ -1,12 +1,19 @@ """ -Tables of zeros of the Riemann-Zeta function +Database of the zeros of the Riemann zeta function + +The main access function to the database of the zeros of the Riemann zeta +function is :func:`zeta_zeros`. In order to use ``zeta_zeros()``, you need to +install the optional :ref:`database_odlyzko_zeta ` +package:: + + sage -i database_odlyzko_zeta AUTHORS: - William Stein: initial version +- Jeroen Demeyer (2015-01-20): converted ``database_odlyzko_zeta`` to new-style + package -- Jeroen Demeyer (2015-01-20): convert ``database_odlyzko_zeta`` to - new-style package """ #***************************************************************************** @@ -30,27 +37,20 @@ def zeta_zeros(): List of the imaginary parts of the first 2,001,052 zeros of the Riemann zeta function, accurate to within 4e-9. - In order to use ``zeta_zeros()``, you will need to - install the optional Odlyzko database package:: - - sage -i database_odlyzko_zeta - - You can see a list of all available optional packages with - ``sage --optional``. - REFERENCES: - http://www.dtc.umn.edu/~odlyzko/zeta_tables/index.html EXAMPLES: - The following example prints the imaginary part of the 13th + The following example shows the imaginary part of the 13th nontrivial zero of the Riemann zeta function:: - sage: zz = zeta_zeros() # optional - database_odlyzko_zeta - sage: zz[12] # optional - database_odlyzko_zeta + sage: # optional - database_odlyzko_zeta + sage: zz = zeta_zeros() + sage: zz[12] 59.347044003 - sage: len(zz) # optional - database_odlyzko_zeta + sage: len(zz) 2001052 """ from sage.misc.verbose import verbose diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index b6eeac3499c..e40ebc7c539 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -8,17 +8,6 @@ - identify a sequence from its first terms. - obtain more terms, formulae, references, etc. for a given sequence. -AUTHORS: - -- Thierry Monteil (2012-02-10 -- 2013-06-21): initial version. - -- Vincent Delecroix (2014): modifies continued fractions because of :trac:`14567` - -- Moritz Firsching (2016): modifies handling of dead sequence, see :trac:`17330` - -- Thierry Monteil (2019): refactorization (unique representation :trac:`28480`, - laziness :trac:`28627`) - EXAMPLES:: sage: oeis @@ -134,13 +123,14 @@ - Some infinite OEIS sequences are implemented in Sage, via the :mod:`sloane_functions ` module. -.. TODO:: +AUTHORS: - - in case of flood, suggest the user to install the off-line database instead. - - interface with the off-line database (or reimplement it). +- Thierry Monteil (2012-02-10 -- 2013-06-21): initial version. +- Vincent Delecroix (2014): modifies continued fractions because of :trac:`14567` +- Moritz Firsching (2016): modifies handling of dead sequence, see :trac:`17330` +- Thierry Monteil (2019): refactorization (unique representation :trac:`28480`, + laziness :trac:`28627`) -Classes and methods -------------------- """ # **************************************************************************** @@ -732,7 +722,7 @@ def online_update(self): except AttributeError: pass - def _field(self, key): + def _field(self, key, warn=True): r""" Return the ``key`` field of the entry of ``self``. @@ -751,7 +741,7 @@ def _field(self, key): for line in self.raw_entry().splitlines(): fields[line[1]].append(line[11:]) self._fields = fields - self.is_dead(warn_only=True) + self.is_dead(warn_only=warn) return self._fields[key] def id(self, format='A'): @@ -969,7 +959,7 @@ def author(self): """ return self._field('A')[0] - def keywords(self): + def keywords(self, warn=True): r""" Return the keywords associated to the sequence ``self``. @@ -995,7 +985,7 @@ def keywords(self): sage: s.keywords() ('nonn', 'hard') """ - return tuple(self._field('K')[0].split(',')) + return tuple(self._field('K', warn=warn)[0].split(',')) def natural_object(self): r""" @@ -1118,8 +1108,8 @@ def is_dead(self, warn_only=False): EXAMPLES: - A warn_only test is triggered as soon as some information on the - sequence is queried:: + A warning is triggered if any field of a dead sequence is accessed, + unless :meth:`is_dead` is called before:: sage: s = oeis(17) sage: s # optional -- internet @@ -1148,11 +1138,11 @@ def is_dead(self, warn_only=False): True """ if warn_only: - if 'dead' in self.keywords(): + if 'dead' in self.keywords(warn_only): from warnings import warn warn('This sequence is dead: "{}: {}"'.format(self.id(), self.name()), RuntimeWarning) else: - return 'dead' in self.keywords() + return 'dead' in self.keywords(warn_only) def is_finite(self): r""" @@ -2020,6 +2010,7 @@ def test_compile_sage_code(self): True """ if self.is_dead(): + self.is_dead(warn_only=True) return True filt = self.programs(language='sage') if filt: diff --git a/src/sage/databases/sloane.py b/src/sage/databases/sloane.py index d5802620066..61825257470 100644 --- a/src/sage/databases/sloane.py +++ b/src/sage/databases/sloane.py @@ -1,8 +1,8 @@ """ -Local copy of Sloane On-Line Encyclopedia of Integer Sequences +Local copy of the On-Line Encyclopedia of Integer Sequences -The SloaneEncyclopedia object provides access to a local copy of the database -containing only the sequences and their names. To use this you must download +The ``SloaneEncyclopedia`` object provides access to a local copy of the database +containing only the sequences and their names. To use this, you must download and install the database using ``SloaneEncyclopedia.install()``, or ``SloaneEncyclopedia.install_from_gz()`` if you have already downloaded the database manually. @@ -11,14 +11,14 @@ :: - sage: SloaneEncyclopedia[60843] # optional - sloane_database + sage: SloaneEncyclopedia[60843] # optional - sloane_database [1, 6, 21, 107] To get the name of a sequence, type :: - sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database + sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database 'Number of groups of order n.' To search locally for a particular subsequence, type @@ -33,7 +33,7 @@ :: - sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database + sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database [(15, [1, 2, 3, 4, 5, 7, 7, 8, 9, 11, 11, ... Results in either case are of the form [ (number, list) ]. @@ -65,11 +65,9 @@ sequence_name() to return the description of a sequence; and changed the data type for elements of each sequence from int to Integer. -- Thierry Monteil (2012-02-10): deprecate dead code and update related doc and +- Thierry Monteil (2012-02-10): deprecated dead code and update related doc and tests. -Classes and methods -------------------- """ # **************************************************************************** @@ -265,7 +263,8 @@ def load(self): try: file_seq = bz2.BZ2File(self.__file__, 'r') except IOError: - raise IOError("The Sloane Encyclopedia database must be installed. Use e.g. 'SloaneEncyclopedia.install()' to download and install it.") + raise IOError("The Sloane Encyclopedia database must be installed." + " Use e.g. 'SloaneEncyclopedia.install()' to download and install it.") self.__data__ = {} @@ -295,7 +294,8 @@ def load(self): self.__loaded_names__ = True except KeyError: # Some sequence in the names file is not in the database - raise KeyError("Sloane OEIS sequence and name files do not match. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).") + raise KeyError("Sloane OEIS sequence and name files do not match." + " Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).") except IOError: # The names database is not installed self.__loaded_names__ = False @@ -323,7 +323,8 @@ def sequence_name(self, N): """ self.load() if not self.__loaded_names__: - raise IOError("The Sloane OEIS names file is not installed. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).") + raise IOError("The Sloane OEIS names file is not installed." + " Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).") if N not in self.__data__: # sequence N does not exist return '' diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index 6abf39426dd..fa5f338391a 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -1,18 +1,16 @@ r""" The Stein-Watkins table of elliptic curves -Sage gives access to the Stein-Watkins table of elliptic curves, via an -optional package that you must install. This is a huge database of elliptic -curves. You can install the database (a 2.6GB package) with the command - -:: +Sage gives access to the Stein-Watkins table of elliptic curves, via the +optional :ref:`database_stein_watkins ` package +that you must install. This is a huge database of elliptic curves. You can +install the database (a 2.6GB package) with the command :: sage -i database_stein_watkins You can also automatically download a small version, which takes much less -time, using the command - -:: +time, via the optional :ref:`database_stein_watkins_mini ` +package using the command :: sage -i database_stein_watkins_mini diff --git a/src/sage/databases/symbolic_data.py b/src/sage/databases/symbolic_data.py index 7703a2216e5..083b3ced668 100644 --- a/src/sage/databases/symbolic_data.py +++ b/src/sage/databases/symbolic_data.py @@ -1,16 +1,15 @@ """ Ideals from the Symbolic Data project -This file implements a thin wrapper for the optional symbolic data set -of ideals as published on http://www.symbolicdata.org . From the -project website: +This module implements a thin wrapper for the optional symbolic dataset of +ideals as published on http://www.symbolicdata.org. From the project website: - For different purposes algorithms and implementations are tested + For different purposes, algorithms and implementations are tested on certified and reliable data. The development of tools and data for such tests is usually 'orthogonal' to the main implementation efforts, it requires different skills and technologies and is not loved by programmers. On the other hand, - in many cases tools and data could easily be reused - with slight + in many cases, tools and data could easily be reused - with slight modifications - across similar projects. The SymbolicData Project is set out to coordinate such efforts within the Computer Algebra Community. Commonly collected certified and reliable data can @@ -21,32 +20,36 @@ there are not yet well agreed aims of such a benchmarking. Nevertheless various (often high quality) special benchmarks are scattered through the literature. During the last - years efforts toward collection of test data for symbolic + years, efforts toward collection of test data for symbolic computations were intensified. They focused mainly on the creation of general benchmarks for different areas of symbolic computation and the collection of such activities on different Web site. For - further qualification of these efforts it would be of great + further qualification of these efforts, it would be of great benefit to create a commonly available digital archive of these special benchmark data scattered through the literature. This would provide the community with an electronic repository of certified data that could be addressed and extended during further development. +In order to use this dataset, you need to install the optional +:ref:`database_symbolic_data ` package by the Sage +command :: + + sage -i database_symbolic_data + EXAMPLES:: - sage: sd = SymbolicData(); sd # optional - database_symbolic_data + sage: # optional - database_symbolic_data + sage: sd = SymbolicData(); sd SymbolicData with 372 ideals - - sage: sd.ZeroDim__example_1 # optional - database_symbolic_data + sage: sd.ZeroDim__example_1 Ideal (x1^2 + x2^2 - 10, x1^2 + x1*x2 + 2*x2^2 - 16) of Multivariate Polynomial Ring in x1, x2 over Rational Field - - sage: sd.Katsura_3 # optional - database_symbolic_data + sage: sd.Katsura_3 Ideal (u0 + 2*u1 + 2*u2 + 2*u3 - 1, u1^2 + 2*u0*u2 + 2*u1*u3 - u2, 2*u0*u1 + 2*u1*u2 + 2*u2*u3 - u1, u0^2 + 2*u1^2 + 2*u2^2 + 2*u3^2 - u0) of Multivariate Polynomial Ring in u0, u1, u2, u3 over Rational Field - - sage: sd.get_ideal('Katsura_3',GF(127),'degrevlex') # optional - database_symbolic_data + sage: sd.get_ideal('Katsura_3', GF(127), 'degrevlex') Ideal (u0 + 2*u1 + 2*u2 + 2*u3 - 1, u1^2 + 2*u0*u2 + 2*u1*u3 - u2, 2*u0*u1 + 2*u1*u2 + 2*u2*u3 - u1, @@ -54,8 +57,20 @@ AUTHORS: -- Martin Albrecht +- Martin Albrecht (2007-02-19): initial version + """ + +# **************************************************************************** +# Copyright (C) 2007 Martin Albrecht +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + import os from xml.dom.minidom import parse from sage.rings.rational_field import QQ @@ -90,13 +105,11 @@ def get_ideal(self, name, base_ring=QQ, term_order="degrevlex"): INPUT: - - ``name`` - name as on the symbolic data website - - ``base_ring`` - base ring for the polynomial ring (default: ``QQ``) - - ``term_order`` - term order for the polynomial ring (default: ``degrevlex``) - - OUTPUT: + - ``name`` -- name as on the symbolic data website + - ``base_ring`` -- base ring for the polynomial ring (default: ``QQ``) + - ``term_order`` -- term order for the polynomial ring (default: ``degrevlex``) - ideal as given by ``name`` in ``PolynomialRing(base_ring,vars,term_order)`` + OUTPUT: ideal as given by ``name`` in ``PolynomialRing(base_ring,vars,term_order)`` EXAMPLES:: diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 4315cd1ba9f..efd28d10abe 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1806,12 +1806,13 @@ def parallel_dispatch(self): job_client = None try: from gnumake_tokenpool import JobClient, NoJobServer + except ImportError: + pass + else: try: - job_client = JobClient() + job_client = JobClient(use_cysignals=True) except NoJobServer: pass - except ImportError: - pass source_iter = iter(self.controller.sources) diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 8c7b29bf1d4..cae09766f5d 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -238,7 +238,7 @@ def _process_doc(self, doctests, doc, namespace, start): # Line number refers to the end of the docstring 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.optional_tags = frozenset(self.file_optional_tags) sigon.probed_tags = frozenset() dt.examples.append(sigon) doctests.append(dt) diff --git a/src/sage/dynamics/cellular_automata/solitons.py b/src/sage/dynamics/cellular_automata/solitons.py index 4afb3f104a2..72ec9ed0104 100644 --- a/src/sage/dynamics/cellular_automata/solitons.py +++ b/src/sage/dynamics/cellular_automata/solitons.py @@ -73,7 +73,7 @@ class SolitonCellularAutomata(SageObject): current state: 34......224....2223 - We then apply an standard evolution:: + We then apply a standard evolution:: sage: B.evolve() sage: B diff --git a/src/sage/env.py b/src/sage/env.py index a221f33bb82..39d09528788 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -177,7 +177,8 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st SAGE_LOCAL = var("SAGE_LOCAL", SAGE_VENV) SAGE_SHARE = var("SAGE_SHARE", join(SAGE_LOCAL, "share")) SAGE_DOC = var("SAGE_DOC", join(SAGE_SHARE, "doc", "sage")) -SAGE_SPKG_INST = var("SAGE_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed")) +SAGE_LOCAL_SPKG_INST = var("SAGE_LOCAL_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed")) +SAGE_SPKG_INST = var("SAGE_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed")) # deprecated # source tree of the Sage distribution SAGE_ROOT = var("SAGE_ROOT") # no fallback for SAGE_ROOT @@ -198,8 +199,7 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st GRAPHS_DATA_DIR = var("GRAPHS_DATA_DIR", join(SAGE_SHARE, "graphs")) ELLCURVE_DATA_DIR = var("ELLCURVE_DATA_DIR", join(SAGE_SHARE, "ellcurves")) POLYTOPE_DATA_DIR = var("POLYTOPE_DATA_DIR", join(SAGE_SHARE, "reflexive_polytopes")) -GAP_LIB_DIR = var("GAP_LIB_DIR", join(SAGE_LOCAL, "lib", "gap")) -GAP_SHARE_DIR = var("GAP_SHARE_DIR", join(SAGE_SHARE, "gap")) + COMBINATORIAL_DESIGN_DATA_DIR = var("COMBINATORIAL_DESIGN_DATA_DIR", join(SAGE_SHARE, "combinatorial_designs")) CREMONA_MINI_DATA_DIR = var("CREMONA_MINI_DATA_DIR", join(SAGE_SHARE, "cremona")) CREMONA_LARGE_DATA_DIR = var("CREMONA_LARGE_DATA_DIR", join(SAGE_SHARE, "cremona")) @@ -243,11 +243,13 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st # GAP memory and args SAGE_GAP_MEMORY = var('SAGE_GAP_MEMORY', None) -_gap_cmd = "gap -r" -if SAGE_GAP_MEMORY is not None: - _gap_cmd += " -s " + SAGE_GAP_MEMORY + " -o " + SAGE_GAP_MEMORY -SAGE_GAP_COMMAND = var('SAGE_GAP_COMMAND', _gap_cmd) +SAGE_GAP_COMMAND = var('SAGE_GAP_COMMAND', None) +# The semicolon-separated search path for GAP packages. It is passed +# directly to GAP via the -l flag. +GAP_ROOT_PATHS = var("GAP_ROOT_PATHS", + ";".join([join(SAGE_LOCAL, "lib", "gap"), + join(SAGE_LOCAL, "share", "gap")])) # post process if DOT_SAGE is not None and ' ' in DOT_SAGE: diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 0a1439b7530..962b743b0c8 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -576,7 +576,8 @@ def function_name(fn): Given a function, return a string giving a name for the function. For functions we recognize, we use our standard opcode name for the - function (so operator.add becomes 'add', and sage.all.sin becomes 'sin'). + function (so :func:`operator.add` becomes ``'add'``, and :func:`sage.functions.trig.sin` + becomes ``'sin'``). For functions we don't recognize, we try to come up with a name, but the name will be wrapped in braces; this is a signal that diff --git a/src/sage/ext_data/gap/sage.g b/src/sage/ext_data/gap/sage.g index 36e131146d6..ddca6e3fb44 100644 --- a/src/sage/ext_data/gap/sage.g +++ b/src/sage/ext_data/gap/sage.g @@ -134,3 +134,17 @@ end; # # LogTo("/tmp/gapsage.log"); # + + +# Load the GAP packages that GAP itself tries to autoload in the +# default configuration (see "PackagesToLoad" in lib/package.gi). The +# combination of passing -A to gap and these LoadPackage statements +# allows us to load the usual set of packages, but only if they are +# installed. So most people will get exactly the default behavior, +# but minimal installations won't throw warnings and fail tests. +_autoloads := [ "autpgrp", "alnuth", "crisp", "ctbllib", "factint", "fga", + "irredsol", "laguna", "polenta", "polycyclic", "resclasses", + "sophus", "tomlib" ]; +for p in _autoloads do + LoadPackage(p); +od; diff --git a/src/sage/ext_data/graphs/graph_plot_js.html b/src/sage/ext_data/graphs/graph_plot_js.html index 75d08f91662..c56ea71e2d0 100644 --- a/src/sage/ext_data/graphs/graph_plot_js.html +++ b/src/sage/ext_data/graphs/graph_plot_js.html @@ -237,7 +237,7 @@ return [cx+d*nx/nn,cy+d*ny/nn] } - // Applies an homothety to the points of the graph respecting the + // Applies a homothety to the points of the graph respecting the // aspect ratio, so that the graph takes the whole javascript // window and is centered function center_and_scale(graph){ diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index be55d44d0c2..d5669c3c9ff 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -236,7 +236,7 @@ def require(self): Traceback (most recent call last): ... FeatureNotPresentError: gap_package_ve1EeThu is not available. - `TestPackageAvailability("ve1EeThu")` evaluated to `fail` in GAP. + `LoadPackage("ve1EeThu")` evaluated to `fail` in GAP. """ presence = self.is_present() if not presence: @@ -393,22 +393,25 @@ def unhide(self): EXAMPLES: - Polycyclic is a standard GAP package since 4.10 (see :trac:`26856`). The - following test just fails if it is hidden. Thus, in the second - invocation no optional tag is needed:: + PolyCyclic is an optional GAP package. The following test + fails if it is hidden, regardless of whether it is installed + or not:: 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")) # needs sage.libs.gap + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap # optional - gap_packages_polycyclic Traceback (most recent call last): ... FeatureNotPresentError: gap_package_polycyclic is not available. Feature `gap_package_polycyclic` is hidden. Use method `unhide` to make it available again. + After unhiding the feature, the test should pass again if PolyCyclic + is installed and loaded:: + sage: Polycyclic.unhide() - sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap # optional - gap_packages_polycyclic Pcp-group with orders [ 0, 3, 4 ] """ self._hidden = False @@ -451,7 +454,7 @@ def __str__(self): Traceback (most recent call last): ... FeatureNotPresentError: gap_package_gapZuHoh8Uu is not available. - `TestPackageAvailability("gapZuHoh8Uu")` evaluated to `fail` in GAP. + `LoadPackage("gapZuHoh8Uu")` evaluated to `fail` in GAP. """ lines = ["{feature} is not available.".format(feature=self.feature.name)] if self.reason: @@ -481,7 +484,7 @@ class FeatureTestResult(): ``resolution``:: sage: presence.reason # needs sage.libs.gap - '`TestPackageAvailability("NOT_A_PACKAGE")` evaluated to `fail` in GAP.' + '`LoadPackage("NOT_A_PACKAGE")` evaluated to `fail` in GAP.' sage: bool(presence.resolution) False diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py index a989dcfc1fb..df5545e9c07 100644 --- a/src/sage/features/gap.py +++ b/src/sage/features/gap.py @@ -19,6 +19,9 @@ class GapPackage(Feature): r""" A :class:`~sage.features.Feature` describing the presence of a GAP package. + A GAP package is "present" if it *can be* loaded, not if it *has + been* loaded. + .. SEEALSO:: :class:`Feature sage.libs.gap <~sage.features.sagemath.sage__libs__gap>` @@ -42,9 +45,10 @@ def __init__(self, package, **kwds): def _is_present(self): r""" - Return whether the package is available in GAP. + Return whether or not the GAP package is present. - This does not check whether this package is functional. + If the package is installed but not yet loaded, it is loaded + first. This does *not* check that the package is functional. EXAMPLES:: @@ -57,8 +61,11 @@ def _is_present(self): except ImportError: return FeatureTestResult(self, False, reason="sage.libs.gap is not available") - command = 'TestPackageAvailability("{package}")'.format(package=self.package) + + # This returns "true" even if the package is already loaded. + command = 'LoadPackage("{package}")'.format(package=self.package) presence = libgap.eval(command) + if presence: return FeatureTestResult(self, True, reason="`{command}` evaluated to `{presence}` in GAP.".format(command=command, presence=presence)) diff --git a/src/sage/features/nauty.py b/src/sage/features/nauty.py index ebd2daeb311..4a07264f927 100644 --- a/src/sage/features/nauty.py +++ b/src/sage/features/nauty.py @@ -24,7 +24,7 @@ class NautyExecutable(Executable): EXAMPLES:: sage: from sage.features.nauty import NautyExecutable - sage: NautyExecutable('converseg').is_present() # optional - nauty + sage: NautyExecutable('converseg').is_present() # needs nauty FeatureTestResult('nauty_converseg', True) """ def __init__(self, name): @@ -49,7 +49,7 @@ class Nauty(JoinFeature): EXAMPLES:: sage: from sage.features.nauty import Nauty - sage: Nauty().is_present() # optional - nauty + sage: Nauty().is_present() # needs nauty FeatureTestResult('nauty', True) """ def __init__(self): @@ -62,7 +62,7 @@ def __init__(self): """ JoinFeature.__init__(self, "nauty", [NautyExecutable(name) - for name in ('directg', 'gentourng', 'geng', 'genbg', 'gentreeg', 'converseg')]) + for name in ('directg', 'gentourng', 'geng', 'genbg', 'gentreeg', 'genktreeg')]) def all_features(): diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 4097d3512b9..dd48c55ee6b 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -311,6 +311,29 @@ def __init__(self): spkg='sagemath_groups', type='standard') +class sage__libs__braiding(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.braiding`. + + EXAMPLES:: + + sage: from sage.features.sagemath import sage__libs__braiding + sage: sage__libs__braiding().is_present() # needs sage.libs.braiding + FeatureTestResult('sage.libs.braiding', True) + """ + + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__libs__braiding + sage: isinstance(sage__libs__braiding(), sage__libs__braiding) + True + """ + PythonModule.__init__(self, 'sage.libs.braiding', + spkg='sagemath_libbraiding', type='standard') + + class sage__libs__ecl(PythonModule): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.ecl`. @@ -330,7 +353,8 @@ def __init__(self): sage: isinstance(sage__libs__ecl(), sage__libs__ecl) True """ - PythonModule.__init__(self, 'sage.libs.ecl') + PythonModule.__init__(self, 'sage.libs.ecl', + spkg='sagemath_symbolics', type='standard') class sage__libs__flint(JoinFeature): @@ -1076,6 +1100,7 @@ def all_features(): sage__geometry__polyhedron(), sage__graphs(), sage__groups(), + sage__libs__braiding(), sage__libs__ecl(), sage__libs__flint(), sage__libs__gap(), diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index fae37766c96..b2d8af991e9 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -115,18 +115,19 @@ converted to Python integers (due to the preparser). Here is an example showing the Battle of the Sexes:: - sage: import gambit # optional - gambit - sage: g = gambit.Game.new_table([2,2]) # optional - gambit - sage: g[int(0), int(0)][int(0)] = int(2) # optional - gambit - sage: g[int(0), int(0)][int(1)] = int(1) # optional - gambit - sage: g[int(0), int(1)][int(0)] = int(0) # optional - gambit - sage: g[int(0), int(1)][int(1)] = int(0) # optional - gambit - sage: g[int(1), int(0)][int(0)] = int(0) # optional - gambit - sage: g[int(1), int(0)][int(1)] = int(0) # optional - gambit - sage: g[int(1), int(1)][int(0)] = int(1) # optional - gambit - sage: g[int(1), int(1)][int(1)] = int(2) # optional - gambit - sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit - sage: solver.solve(g) # optional - gambit + sage: # optional - gambit + sage: import gambit + sage: g = gambit.Game.new_table([2,2]) + sage: g[int(0), int(0)][int(0)] = int(2) + sage: g[int(0), int(0)][int(1)] = int(1) + sage: g[int(0), int(1)][int(0)] = int(0) + sage: g[int(0), int(1)][int(1)] = int(0) + sage: g[int(1), int(0)][int(0)] = int(0) + sage: g[int(1), int(0)][int(1)] = int(0) + sage: g[int(1), int(1)][int(0)] = int(1) + sage: g[int(1), int(1)][int(1)] = int(2) + sage: solver = gambit.nash.ExternalLCPSolver() + sage: solver.solve(g) [, , ] diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 811e00aec62..275907d7276 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -72,7 +72,8 @@ sage: B = matrix([[2, 1], [0, 3]]) sage: battle_of_the_sexes = NormalFormGame([A, B]) sage: battle_of_the_sexes - Normal Form Game with the following utilities: {(0, 0): [3, 2], (0, 1): [1, 1], (1, 0): [0, 0], (1, 1): [2, 3]} + Normal Form Game with the following utilities: {(0, 0): [3, 2], + (0, 1): [1, 1], (1, 0): [0, 0], (1, 1): [2, 3]} To obtain the Nash equilibria we run the ``obtain_nash()`` method. In the first few examples, we will use the 'support enumeration' algorithm. @@ -159,8 +160,10 @@ sage: y = var('y') sage: A = matrix([[1, -1], [-1, 1]]) - sage: p = plot((A * vector([y, 1 - y]))[0], y, 0, 1, color='blue', legend_label='$u_1(r_1, (y, 1-y))$', axes_labels=['$y$', '']) - sage: p += plot((A * vector([y, 1 - y]))[1], y, 0, 1, color='red', legend_label='$u_1(r_2, (y, 1-y))$'); p + sage: p = plot((A * vector([y, 1 - y]))[0], y, 0, 1, color='blue', + ....: legend_label='$u_1(r_1, (y, 1-y))$', axes_labels=['$y$', '']) + sage: p += plot((A * vector([y, 1 - y]))[1], y, 0, 1, color='red', + ....: legend_label='$u_1(r_2, (y, 1-y))$'); p Graphics object consisting of 2 graphics primitives We see that the only point at which player 1 is indifferent amongst @@ -281,7 +284,8 @@ sage: f[1,1][0] = 4 sage: f[1,1][1] = 4 sage: f - Normal Form Game with the following utilities: {(0, 0): [1, 3], (0, 1): [2, 3], (1, 0): [3, 1], (1, 1): [4, 4]} + Normal Form Game with the following utilities: {(0, 0): [1, 3], + (0, 1): [2, 3], (1, 0): [3, 1], (1, 1): [4, 4]} Once this game is constructed we can view the payoff matrices and solve the game:: @@ -401,7 +405,9 @@ sage: threegame.obtain_nash() Traceback (most recent call last): ... - NotImplementedError: Nash equilibrium for games with more than 2 players have not been implemented yet. Please see the gambit website (http://gambit.sourceforge.net/) that has a variety of available algorithms + NotImplementedError: Nash equilibrium for games with more than 2 players + have not been implemented yet. Please see the gambit website + (http://gambit.sourceforge.net/) that has a variety of available algorithms There are however a variety of such algorithms available in gambit, further compatibility between Sage and gambit is actively being developed: @@ -424,18 +430,18 @@ It is also possible to generate a Normal form game from a gambit Game:: - sage: from gambit import Game # optional - gambit - sage: gambitgame= Game.new_table([2, 2]) # optional - gambit - sage: gambitgame[int(0), int(0)][int(0)] = int(8) # optional - gambit - sage: gambitgame[int(0), int(0)][int(1)] = int(8) # optional - gambit - sage: gambitgame[int(0), int(1)][int(0)] = int(2) # optional - gambit - sage: gambitgame[int(0), int(1)][int(1)] = int(10) # optional - gambit - sage: gambitgame[int(1), int(0)][int(0)] = int(10) # optional - gambit - sage: gambitgame[int(1), int(0)][int(1)] = int(2) # optional - gambit - sage: gambitgame[int(1), int(1)][int(0)] = int(5) # optional - gambit - sage: gambitgame[int(1), int(1)][int(1)] = int(5) # optional - gambit - sage: g = NormalFormGame(gambitgame) # optional - gambit - sage: g # optional - gambit + sage: # optional - gambit + sage: from gambit import Game + sage: gambitgame= Game.new_table([2, 2]) + sage: gambitgame[int(0), int(0)][int(0)] = int(8) + sage: gambitgame[int(0), int(0)][int(1)] = int(8) + sage: gambitgame[int(0), int(1)][int(0)] = int(2) + sage: gambitgame[int(0), int(1)][int(1)] = int(10) + sage: gambitgame[int(1), int(0)][int(0)] = int(10) + sage: gambitgame[int(1), int(0)][int(1)] = int(2) + sage: gambitgame[int(1), int(1)][int(0)] = int(5) + sage: gambitgame[int(1), int(1)][int(1)] = int(5) + sage: g = NormalFormGame(gambitgame); g Normal Form Game with the following utilities: {(0, 0): [8.0, 8.0], (0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], @@ -476,9 +482,9 @@ sage: B = matrix([[min(i,j) + 2 * sign(i-j) for j in range(K, 1, -1)] ....: for i in range(K, 1, -1)]) sage: g = NormalFormGame([A, B]) - sage: g.obtain_nash(algorithm='lrs') # optional - lrslib + sage: g.obtain_nash(algorithm='lrs') # optional - lrslib [[(0, 0, 0, 0, 0, 0, 0, 0, 1), (0, 0, 0, 0, 0, 0, 0, 0, 1)]] - sage: g.obtain_nash(algorithm='LCP') # optional - gambit + sage: g.obtain_nash(algorithm='LCP') # optional - gambit [[(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)]] @@ -564,9 +570,9 @@ sage: A = matrix([[3,3],[2,5],[0,6]]) sage: B = matrix([[3,3],[2,6],[3,1]]) sage: degenerate_game = NormalFormGame([A,B]) - sage: degenerate_game.obtain_nash(algorithm='lrs') # random, optional - lrslib + sage: degenerate_game.obtain_nash(algorithm='lrs') # random, optional - lrslib [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (1/2, 3)], [(1, 0, 0), (1, 3)]] - sage: degenerate_game.obtain_nash(algorithm='LCP') # optional - gambit + sage: degenerate_game.obtain_nash(algorithm='LCP') # optional - gambit [[(0.0, 0.3333333333, 0.6666666667), (0.3333333333, 0.6666666667)], [(1.0, -0.0, 0.0), (0.6666666667, 0.3333333333)], [(1.0, 0.0, 0.0), (1.0, 0.0)]] @@ -705,22 +711,25 @@ def __init__(self, generator=None): sage: threegame.obtain_nash() Traceback (most recent call last): ... - NotImplementedError: Nash equilibrium for games with more than 2 players have not been implemented yet. Please see the gambit website (http://gambit.sourceforge.net/) that has a variety of available algorithms + NotImplementedError: Nash equilibrium for games with more than + 2 players have not been implemented yet. Please see the gambit + website (http://gambit.sourceforge.net/) that has a variety of + available algorithms Can initialise a game from a gambit game object:: - sage: from gambit import Game # optional - gambit - sage: gambitgame= Game.new_table([2, 2]) # optional - gambit - sage: gambitgame[int(0), int(0)][int(0)] = int(5) # optional - gambit - sage: gambitgame[int(0), int(0)][int(1)] = int(8) # optional - gambit - sage: gambitgame[int(0), int(1)][int(0)] = int(2) # optional - gambit - sage: gambitgame[int(0), int(1)][int(1)] = int(11) # optional - gambit - sage: gambitgame[int(1), int(0)][int(0)] = int(10) # optional - gambit - sage: gambitgame[int(1), int(0)][int(1)] = int(7) # optional - gambit - sage: gambitgame[int(1), int(1)][int(0)] = int(5) # optional - gambit - sage: gambitgame[int(1), int(1)][int(1)] = int(5) # optional - gambit - sage: g = NormalFormGame(gambitgame) # optional - gambit - sage: g # optional - gambit + sage: # optional - gambit + sage: from gambit import Game + sage: gambitgame= Game.new_table([2, 2]) + sage: gambitgame[int(0), int(0)][int(0)] = int(5) + sage: gambitgame[int(0), int(0)][int(1)] = int(8) + sage: gambitgame[int(0), int(1)][int(0)] = int(2) + sage: gambitgame[int(0), int(1)][int(1)] = int(11) + sage: gambitgame[int(1), int(0)][int(0)] = int(10) + sage: gambitgame[int(1), int(0)][int(1)] = int(7) + sage: gambitgame[int(1), int(1)][int(0)] = int(5) + sage: gambitgame[int(1), int(1)][int(1)] = int(5) + sage: g = NormalFormGame(gambitgame); g Normal Form Game with the following utilities: {(0, 0): [5.0, 8.0], (0, 1): [2.0, 11.0], (1, 0): [10.0, 7.0], @@ -790,10 +799,12 @@ def __delitem__(self, key): sage: B = matrix([[2, 0], [5, 4]]) sage: prisoners_dilemma = NormalFormGame([A, B]) sage: prisoners_dilemma - Normal Form Game with the following utilities: {(0, 0): [2, 2], (0, 1): [5, 0], (1, 0): [0, 5], (1, 1): [4, 4]} + Normal Form Game with the following utilities: {(0, 0): [2, 2], + (0, 1): [5, 0], (1, 0): [0, 5], (1, 1): [4, 4]} sage: del(prisoners_dilemma[(0,1)]) sage: prisoners_dilemma - Normal Form Game with the following utilities: {(0, 0): [2, 2], (1, 0): [0, 5], (1, 1): [4, 4]} + Normal Form Game with the following utilities: {(0, 0): [2, 2], + (1, 0): [0, 5], (1, 1): [4, 4]} """ self.utilities.pop(key, None) @@ -897,7 +908,8 @@ def _repr_(self): sage: p2 = matrix([[3, 3], [1, 4]]) sage: g = NormalFormGame([p1, p2]) sage: g - Normal Form Game with the following utilities: {(0, 0): [1, 3], (0, 1): [2, 3], (1, 0): [3, 1], (1, 1): [4, 4]} + Normal Form Game with the following utilities: {(0, 0): [1, 3], + (0, 1): [2, 3], (1, 0): [3, 1], (1, 1): [4, 4]} """ from pprint import pformat base_str = "Normal Form Game with the following utilities: {}" @@ -967,19 +979,19 @@ def _gambit_game(self, game): TESTS:: - sage: from gambit import Game # optional - gambit - sage: testgame = Game.new_table([2, 2]) # optional - gambit - sage: testgame[int(0), int(0)][int(0)] = int(8) # optional - gambit - sage: testgame[int(0), int(0)][int(1)] = int(8) # optional - gambit - sage: testgame[int(0), int(1)][int(0)] = int(2) # optional - gambit - sage: testgame[int(0), int(1)][int(1)] = int(10) # optional - gambit - sage: testgame[int(1), int(0)][int(0)] = int(10) # optional - gambit - sage: testgame[int(1), int(0)][int(1)] = int(2) # optional - gambit - sage: testgame[int(1), int(1)][int(0)] = int(5) # optional - gambit - sage: testgame[int(1), int(1)][int(1)] = int(5) # optional - gambit - sage: g = NormalFormGame() # optional - gambit - sage: g._gambit_game(testgame) # optional - gambit - sage: g # optional - gambit + sage: # optional - gambit + sage: from gambit import Game + sage: testgame = Game.new_table([2, 2]) + sage: testgame[int(0), int(0)][int(0)] = int(8) + sage: testgame[int(0), int(0)][int(1)] = int(8) + sage: testgame[int(0), int(1)][int(0)] = int(2) + sage: testgame[int(0), int(1)][int(1)] = int(10) + sage: testgame[int(1), int(0)][int(0)] = int(10) + sage: testgame[int(1), int(0)][int(1)] = int(2) + sage: testgame[int(1), int(1)][int(0)] = int(5) + sage: testgame[int(1), int(1)][int(1)] = int(5) + sage: g = NormalFormGame() + sage: g._gambit_game(testgame); g Normal Form Game with the following utilities: {(0, 0): [8.0, 8.0], (0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], @@ -1008,11 +1020,11 @@ def _gambit_(self, as_integer=False, maximization=True): TESTS:: - sage: from gambit import Game # optional - gambit + sage: # optional - gambit + sage: from gambit import Game sage: A = matrix([[2, 1], [1, 2.5]]) sage: g = NormalFormGame([A]) - sage: gg = g._gambit_() # optional - gambit - sage: gg # optional - gambit + sage: gg = g._gambit_(); gg NFG 1 R "" { "1" "2" } { { "1" "2" } @@ -1028,9 +1040,7 @@ def _gambit_(self, as_integer=False, maximization=True): } 1 2 3 4 - - sage: gg = g._gambit_(as_integer=True) # optional - gambit - sage: gg # optional - gambit + sage: gg = g._gambit_(as_integer=True); gg NFG 1 R "" { "1" "2" } { { "1" "2" } @@ -1049,11 +1059,11 @@ def _gambit_(self, as_integer=False, maximization=True): :: + sage: # optional - gambit sage: A = matrix([[2, 1], [1, 2.5]]) sage: B = matrix([[3, 2], [5.5, 4]]) sage: g = NormalFormGame([A, B]) - sage: gg = g._gambit_() # optional - gambit - sage: gg # optional - gambit + sage: gg = g._gambit_(); gg NFG 1 R "" { "1" "2" } { { "1" "2" } @@ -1069,9 +1079,7 @@ def _gambit_(self, as_integer=False, maximization=True): } 1 2 3 4 - - sage: gg = g._gambit_(as_integer = True) # optional - gambit - sage: gg # optional - gambit + sage: gg = g._gambit_(as_integer = True); gg NFG 1 R "" { "1" "2" } { { "1" "2" } @@ -1090,35 +1098,36 @@ def _gambit_(self, as_integer=False, maximization=True): :: - sage: threegame = NormalFormGame() # optional - gambit - sage: threegame.add_player(2) # optional - gambit - sage: threegame.add_player(2) # optional - gambit - sage: threegame.add_player(2) # optional - gambit - sage: threegame[0, 0, 0][0] = 3 # optional - gambit - sage: threegame[0, 0, 0][1] = 1 # optional - gambit - sage: threegame[0, 0, 0][2] = 4 # optional - gambit - sage: threegame[0, 0, 1][0] = 1 # optional - gambit - sage: threegame[0, 0, 1][1] = 5 # optional - gambit - sage: threegame[0, 0, 1][2] = 9 # optional - gambit - sage: threegame[0, 1, 0][0] = 2 # optional - gambit - sage: threegame[0, 1, 0][1] = 6 # optional - gambit - sage: threegame[0, 1, 0][2] = 5 # optional - gambit - sage: threegame[0, 1, 1][0] = 3 # optional - gambit - sage: threegame[0, 1, 1][1] = 5 # optional - gambit - sage: threegame[0, 1, 1][2] = 8 # optional - gambit - sage: threegame[1, 0, 0][0] = 9 # optional - gambit - sage: threegame[1, 0, 0][1] = 7 # optional - gambit - sage: threegame[1, 0, 0][2] = 9 # optional - gambit - sage: threegame[1, 0, 1][0] = 3 # optional - gambit - sage: threegame[1, 0, 1][1] = 2 # optional - gambit - sage: threegame[1, 0, 1][2] = 3 # optional - gambit - sage: threegame[1, 1, 0][0] = 8 # optional - gambit - sage: threegame[1, 1, 0][1] = 4 # optional - gambit - sage: threegame[1, 1, 0][2] = 6 # optional - gambit - sage: threegame[1, 1, 1][0] = 2 # optional - gambit - sage: threegame[1, 1, 1][1] = 6 # optional - gambit - sage: threegame[1, 1, 1][2] = 4 # optional - gambit - sage: threegame._gambit_(as_integer = True) # optional - gambit + sage: # optional - gambit + sage: threegame = NormalFormGame() + sage: threegame.add_player(2) + sage: threegame.add_player(2) + sage: threegame.add_player(2) + sage: threegame[0, 0, 0][0] = 3 + sage: threegame[0, 0, 0][1] = 1 + sage: threegame[0, 0, 0][2] = 4 + sage: threegame[0, 0, 1][0] = 1 + sage: threegame[0, 0, 1][1] = 5 + sage: threegame[0, 0, 1][2] = 9 + sage: threegame[0, 1, 0][0] = 2 + sage: threegame[0, 1, 0][1] = 6 + sage: threegame[0, 1, 0][2] = 5 + sage: threegame[0, 1, 1][0] = 3 + sage: threegame[0, 1, 1][1] = 5 + sage: threegame[0, 1, 1][2] = 8 + sage: threegame[1, 0, 0][0] = 9 + sage: threegame[1, 0, 0][1] = 7 + sage: threegame[1, 0, 0][2] = 9 + sage: threegame[1, 0, 1][0] = 3 + sage: threegame[1, 0, 1][1] = 2 + sage: threegame[1, 0, 1][2] = 3 + sage: threegame[1, 1, 0][0] = 8 + sage: threegame[1, 1, 0][1] = 4 + sage: threegame[1, 1, 0][2] = 6 + sage: threegame[1, 1, 1][0] = 2 + sage: threegame[1, 1, 1][1] = 6 + sage: threegame[1, 1, 1][2] = 4 + sage: threegame._gambit_(as_integer = True) NFG 1 R "" { "1" "2" "3" } { { "1" "2" } @@ -1274,7 +1283,9 @@ def add_player(self, num_strategies): sage: g.add_player(1) # Adding second player with 1 strategy sage: g.add_player(1) # Adding third player with 1 strategy sage: g - Normal Form Game with the following utilities: {(0, 0, 0): [False, False, False], (1, 0, 0): [False, False, False]} + Normal Form Game with the following utilities: + {(0, 0, 0): [False, False, False], + (1, 0, 0): [False, False, False]} """ self.players.append(_Player(num_strategies)) self._generate_utilities(True) @@ -1350,7 +1361,8 @@ def add_strategy(self, player): sage: t = matrix([[3, 2], [-1, 0]]) sage: example = NormalFormGame([s, t]) sage: example - Normal Form Game with the following utilities: {(0, 0): [1, 3], (0, 1): [0, 2], (1, 0): [-2, -1], (1, 1): [3, 0]} + Normal Form Game with the following utilities: {(0, 0): [1, 3], + (0, 1): [0, 2], (1, 0): [-2, -1], (1, 1): [3, 0]} sage: example.add_strategy(0) sage: example Normal Form Game with the following utilities: {(0, 0): [1, 3], @@ -1475,10 +1487,12 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): ....: [3, 4, 1], ....: [4, 1, 20]]) sage: g=NormalFormGame([A, B]) - sage: g.obtain_nash(algorithm='lrs') # optional - lrslib + sage: g.obtain_nash(algorithm='lrs') # optional - lrslib [[(0, 0, 0, 1), (0, 0, 1)]] - sage: g.obtain_nash(algorithm='lrs', maximization=False) # optional - lrslib - [[(2/3, 1/12, 1/4, 0), (6333/8045, 247/8045, 293/1609)], [(3/4, 0, 1/4, 0), (0, 11/307, 296/307)], [(5/6, 1/6, 0, 0), (98/99, 1/99, 0)]] + sage: g.obtain_nash(algorithm='lrs', maximization=False) # optional - lrslib + [[(2/3, 1/12, 1/4, 0), (6333/8045, 247/8045, 293/1609)], + [(3/4, 0, 1/4, 0), (0, 11/307, 296/307)], + [(5/6, 1/6, 0, 0), (98/99, 1/99, 0)]] This particular game has 3 Nash equilibria:: @@ -1490,7 +1504,9 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): ....: [3,1]]) sage: g = NormalFormGame([A, B]) sage: g.obtain_nash(algorithm='enumeration') - [[(0, 1/3, 2/3), (1/3, 2/3)], [(4/5, 1/5, 0), (2/3, 1/3)], [(1, 0, 0), (1, 0)]] + [[(0, 1/3, 2/3), (1/3, 2/3)], + [(4/5, 1/5, 0), (2/3, 1/3)], + [(1, 0, 0), (1, 0)]] Here is a slightly larger game:: @@ -1525,9 +1541,9 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): sage: fivegame = NormalFormGame([player1, player2]) sage: fivegame.obtain_nash(algorithm='enumeration') [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] - sage: fivegame.obtain_nash(algorithm='lrs') # optional - lrslib + sage: fivegame.obtain_nash(algorithm='lrs') # optional - lrslib [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] - sage: fivegame.obtain_nash(algorithm='LCP') # optional - gambit + sage: fivegame.obtain_nash(algorithm='LCP') # optional - gambit [[(1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0)]] Here are some examples of finding Nash equilibria for constant-sum games:: @@ -1536,24 +1552,24 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): sage: cg = NormalFormGame([A]) sage: cg.obtain_nash(algorithm='lp') [[(0.5, 0.5), (0.5, 0.5)]] - sage: cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin + sage: cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin [[(0.5, 0.5), (0.5, 0.5)]] sage: cg.obtain_nash(algorithm='lp', solver='PPL') [[(1/2, 1/2), (1/2, 1/2)]] - sage: cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit + sage: cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit [[(0.5, 0.5), (0.5, 0.5)]] sage: A = matrix([[2, 1], [1, 3]]) sage: cg = NormalFormGame([A]) sage: ne = cg.obtain_nash(algorithm='lp', solver='glpk') sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] [[[0.666667, 0.333333], [0.666667, 0.333333]]] - sage: ne = cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin + sage: ne = cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin [[[0.666667, 0.333333], [0.666667, 0.333333]]] sage: cg.obtain_nash(algorithm='lp', solver='PPL') [[(2/3, 1/3), (2/3, 1/3)]] - sage: ne = cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit + sage: ne = cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit [[[0.666667, 0.333333], [0.666667, 0.333333]]] sage: A = matrix([[1, 2, 1], [1, 1, 2], [2, 1, 1]]) sage: B = matrix([[2, 1, 2], [2, 2, 1], [1, 2, 2]]) @@ -1561,13 +1577,13 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): sage: ne = cg.obtain_nash(algorithm='lp', solver='glpk') sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] [[[0.333333, 0.333333, 0.333333], [0.333333, 0.333333, 0.333333]]] - sage: ne = cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin + sage: ne = cg.obtain_nash(algorithm='lp', solver='Coin') # optional - sage_numerical_backends_coin + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin [[[0.333333, 0.333333, 0.333333], [0.333333, 0.333333, 0.333333]]] sage: cg.obtain_nash(algorithm='lp', solver='PPL') [[(1/3, 1/3, 1/3), (1/3, 1/3, 1/3)]] - sage: ne = cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit + sage: ne = cg.obtain_nash(algorithm='lp', solver='gambit') # optional - gambit + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit [[[0.333333, 0.333333, 0.333333], [0.333333, 0.333333, 0.333333]]] sage: A = matrix([[160, 205, 44], ....: [175, 180, 45], @@ -1609,19 +1625,19 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): sage: gg = NormalFormGame([A]) sage: gg.obtain_nash(algorithm='enumeration') [[(0, 1), (0, 1)], [(0, 1), (1, 0)], [(1, 0), (0, 1)], [(1, 0), (1, 0)]] - sage: gg.obtain_nash(algorithm='lrs') # optional - lrs + sage: gg.obtain_nash(algorithm='lrs') # optional - lrs [[(0, 1), (0, 1)], [(0, 1), (1, 0)], [(1, 0), (0, 1)], [(1, 0), (1, 0)]] sage: gg.obtain_nash(algorithm='lp', solver='glpk') [[(1.0, 0.0), (1.0, 0.0)]] - sage: gg.obtain_nash(algorithm='LCP') # optional - gambit + sage: gg.obtain_nash(algorithm='LCP') # optional - gambit [[(1.0, 0.0), (1.0, 0.0)]] sage: gg.obtain_nash(algorithm='enumeration', maximization=False) [[(0, 1), (0, 1)], [(0, 1), (1, 0)], [(1, 0), (0, 1)], [(1, 0), (1, 0)]] - sage: gg.obtain_nash(algorithm='lrs', maximization=False) # optional - lrs + sage: gg.obtain_nash(algorithm='lrs', maximization=False) # optional - lrs [[(0, 1), (0, 1)], [(0, 1), (1, 0)], [(1, 0), (0, 1)], [(1, 0), (1, 0)]] sage: gg.obtain_nash(algorithm='lp', solver='glpk', maximization=False) [[(1.0, 0.0), (1.0, 0.0)]] - sage: gg.obtain_nash(algorithm='LCP', maximization=False) # optional - gambit + sage: gg.obtain_nash(algorithm='LCP', maximization=False) # optional - gambit [[(1.0, 0.0), (1.0, 0.0)]] Note that outputs for all algorithms are as lists of lists of @@ -1709,7 +1725,7 @@ def _solve_lrs(self, maximization=True): sage: A = matrix([[1, 2], [3, 4]]) sage: B = matrix([[3, 3], [1, 4]]) sage: C = NormalFormGame([A, B]) - sage: C._solve_lrs() # optional - lrslib + sage: C._solve_lrs() # optional - lrslib [[(0, 1), (0, 1)]] 2 random matrices:: @@ -1725,7 +1741,7 @@ def _solve_lrs(self, maximization=True): ....: [1, 0, 0, 0, 0,], ....: [1, -3, 1, 21, -2]]) sage: biggame = NormalFormGame([p1, p2]) - sage: biggame._solve_lrs() # optional - lrslib + sage: biggame._solve_lrs() # optional - lrslib [[(0, 0, 0, 20/21, 1/21), (11/12, 0, 0, 1/12, 0)]] Another test:: @@ -1737,7 +1753,7 @@ def _solve_lrs(self, maximization=True): ....: [6, -2, -3], ....: [-4, 6, -10]]) sage: biggame = NormalFormGame([p1, p2]) - sage: biggame._solve_lrs() # optional - lrslib + sage: biggame._solve_lrs() # optional - lrslib [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], @@ -1774,7 +1790,7 @@ def _solve_LCP(self, maximization): sage: a = matrix([[1, 0], [1, 4]]) sage: b = matrix([[2, 3], [2, 4]]) sage: c = NormalFormGame([a, b]) - sage: c._solve_LCP(maximization=True) # optional - gambit + sage: c._solve_LCP(maximization=True) # optional - gambit [[(0.0, 1.0), (0.0, 1.0)]] """ g = self._gambit_(maximization) @@ -1791,14 +1807,14 @@ def _solve_gambit_LP(self, maximization=True): sage: A = matrix([[2, 1], [1, 2.5]]) sage: g = NormalFormGame([A]) - sage: g._solve_gambit_LP() # optional - gambit + sage: g._solve_gambit_LP() # optional - gambit [[(0.6, 0.4), (0.6, 0.4)]] sage: A = matrix.identity(2) sage: g = NormalFormGame([A]) - sage: g._solve_gambit_LP() # optional - gambit + sage: g._solve_gambit_LP() # optional - gambit [[(0.5, 0.5), (0.5, 0.5)]] sage: g = NormalFormGame([A,A]) - sage: g._solve_gambit_LP() # optional - gambit + sage: g._solve_gambit_LP() # optional - gambit Traceback (most recent call last): ... RuntimeError: Method only valid for constant-sum games. @@ -1830,9 +1846,9 @@ def _solve_LP(self, solver='glpk', maximization=True): sage: g = NormalFormGame([A]) sage: g._solve_LP() [[(0.5, 0.5), (0.5, 0.5)]] - sage: g._solve_LP('gambit') # optional - gambit + sage: g._solve_LP('gambit') # optional - gambit [[(0.5, 0.5), (0.5, 0.5)]] - sage: g._solve_LP('Coin') # optional - sage_numerical_backends_coin + sage: g._solve_LP('Coin') # optional - sage_numerical_backends_coin [[(0.5, 0.5), (0.5, 0.5)]] sage: g._solve_LP('PPL') [[(1/2, 1/2), (1/2, 1/2)]] @@ -1841,11 +1857,11 @@ def _solve_LP(self, solver='glpk', maximization=True): sage: ne = g._solve_LP() sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] [[[0.666667, 0.333333], [0.666667, 0.333333]]] - sage: ne = g._solve_LP('gambit') # optional - gambit - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit + sage: ne = g._solve_LP('gambit') # optional - gambit + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - gambit [[[0.666667, 0.333333], [0.666667, 0.333333]]] - sage: ne = g._solve_LP('Coin') # optional - sage_numerical_backends_coin - sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin + sage: ne = g._solve_LP('Coin') # optional - sage_numerical_backends_coin + sage: [[[round(el, 6) for el in v] for v in eq] for eq in ne] # optional - sage_numerical_backends_coin [[[0.666667, 0.333333], [0.666667, 0.333333]]] sage: g._solve_LP('PPL') [[(2/3, 1/3), (2/3, 1/3)]] @@ -1950,7 +1966,9 @@ def _solve_enumeration(self, maximization=True): ....: [3, 2, 1, 1]]) sage: C = NormalFormGame([A, B]) sage: C._solve_enumeration() - [[(0, 0, 0, 1), (1, 0, 0, 0)], [(2/7, 0, 0, 5/7), (5/11, 0, 6/11, 0)], [(1, 0, 0, 0), (0, 0, 1, 0)]] + [[(0, 0, 0, 1), (1, 0, 0, 0)], + [(2/7, 0, 0, 5/7), (5/11, 0, 6/11, 0)], + [(1, 0, 0, 0), (0, 0, 1, 0)]] Again:: @@ -2485,11 +2503,11 @@ def is_degenerate(self, certificate=False): ....: [-17, 25, -97, -82], ....: [30, 31, -1, 50]]) sage: d_game = NormalFormGame([a, b]) - sage: d_game.obtain_nash(algorithm='lrs') # optional - lrslib + sage: d_game.obtain_nash(algorithm='lrs') # optional - lrslib [[(0, 0, 1, 0), (0, 1, 0, 0)], [(17/29, 0, 0, 12/29), (0, 0, 42/73, 31/73)], [(122/145, 0, 23/145, 0), (0, 1, 0, 0)]] - sage: d_game.obtain_nash(algorithm='LCP') # optional - gambit + sage: d_game.obtain_nash(algorithm='LCP') # optional - gambit [[(0.5862068966, 0.0, 0.0, 0.4137931034), (0.0, 0.0, 0.5753424658, 0.4246575342)]] sage: d_game.obtain_nash(algorithm='enumeration') @@ -2532,7 +2550,8 @@ def is_degenerate(self, certificate=False): sage: g.is_degenerate() Traceback (most recent call last): ... - NotImplementedError: Tests for Degeneracy is not yet implemented for games with more than two players. + NotImplementedError: Tests for Degeneracy is not yet implemented for + games with more than two players. """ if len(self.players) > 2: raise NotImplementedError("Tests for Degeneracy is not yet " @@ -2607,7 +2626,7 @@ def best_responses(self, strategy, player): sage: g.best_responses((3/4, 1/4), player=0) [0] - To get the best responses for Player 2 we pass the argument :code:`player=1` + To get the best responses for Player 2 we pass the argument :code:`player=1`:: sage: g.best_responses((4/5, 1/5, 0), player=1) [0, 1] @@ -2658,7 +2677,7 @@ def best_responses(self, strategy, player): ValueError: Strategy is not of correct dimension If the strategy is not a true probability vector then an error is - passed: + passed:: sage: A = matrix([[3, 0], [0, 3], [1.5, 1.5]]) sage: B = matrix([[4, 3], [2, 6], [3, 1]]) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 0784e4bc184..d528116f244 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -194,18 +194,19 @@ def format_gambit(self, gambit_game): Here we construct a two by two game in gambit:: - sage: import gambit # optional - gambit + sage: # optional - gambit + sage: import gambit sage: from sage.game_theory.parser import Parser - sage: g = gambit.Game.new_table([2,2]) # optional - gambit - sage: g[int(0), int(0)][int(0)] = int(2) # optional - gambit - sage: g[int(0), int(0)][int(1)] = int(1) # optional - gambit - sage: g[int(0), int(1)][int(0)] = int(0) # optional - gambit - sage: g[int(0), int(1)][int(1)] = int(0) # optional - gambit - sage: g[int(1), int(0)][int(0)] = int(0) # optional - gambit - sage: g[int(1), int(0)][int(1)] = int(0) # optional - gambit - sage: g[int(1), int(1)][int(0)] = int(1) # optional - gambit - sage: g[int(1), int(1)][int(1)] = int(2) # optional - gambit - sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + sage: g = gambit.Game.new_table([2,2]) + sage: g[int(0), int(0)][int(0)] = int(2) + sage: g[int(0), int(0)][int(1)] = int(1) + sage: g[int(0), int(1)][int(0)] = int(0) + sage: g[int(0), int(1)][int(1)] = int(0) + sage: g[int(1), int(0)][int(0)] = int(0) + sage: g[int(1), int(0)][int(1)] = int(0) + sage: g[int(1), int(1)][int(0)] = int(1) + sage: g[int(1), int(1)][int(1)] = int(2) + sage: solver = gambit.nash.ExternalLCPSolver() Here is the output of the LCP algorithm:: @@ -218,21 +219,24 @@ def format_gambit(self, gambit_game): The Parser class outputs the equilibrium:: sage: nasheq = Parser(LCP_output).format_gambit(g) # optional - gambit - sage: nasheq # optional - gambit - [[(1.0, 0.0), (1.0, 0.0)], [(0.6666666667, 0.3333333333), (0.3333333333, 0.6666666667)], [(0.0, 1.0), (0.0, 1.0)]] + sage: nasheq # optional - gambit + [[(1.0, 0.0), (1.0, 0.0)], + [(0.6666666667, 0.3333333333), (0.3333333333, 0.6666666667)], + [(0.0, 1.0), (0.0, 1.0)]] Here is another game:: - sage: g = gambit.Game.new_table([2,2]) # optional - gambit - sage: g[int(0), int(0)][int(0)] = int(4) # optional - gambit - sage: g[int(0), int(0)][int(1)] = int(8) # optional - gambit - sage: g[int(0), int(1)][int(0)] = int(0) # optional - gambit - sage: g[int(0), int(1)][int(1)] = int(1) # optional - gambit - sage: g[int(1), int(0)][int(0)] = int(1) # optional - gambit - sage: g[int(1), int(0)][int(1)] = int(3) # optional - gambit - sage: g[int(1), int(1)][int(0)] = int(1) # optional - gambit - sage: g[int(1), int(1)][int(1)] = int(0) # optional - gambit - sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + sage: # optional - gambit + sage: g = gambit.Game.new_table([2,2]) + sage: g[int(0), int(0)][int(0)] = int(4) + sage: g[int(0), int(0)][int(1)] = int(8) + sage: g[int(0), int(1)][int(0)] = int(0) + sage: g[int(0), int(1)][int(1)] = int(1) + sage: g[int(1), int(0)][int(0)] = int(1) + sage: g[int(1), int(0)][int(1)] = int(3) + sage: g[int(1), int(1)][int(0)] = int(1) + sage: g[int(1), int(1)][int(1)] = int(0) + sage: solver = gambit.nash.ExternalLCPSolver() Here is the LCP output:: @@ -248,26 +252,27 @@ def format_gambit(self, gambit_game): Here is a larger degenerate game:: - sage: g = gambit.Game.new_table([3,3]) # optional - gambit - sage: g[int(0), int(0)][int(0)] = int(-7) # optional - gambit - sage: g[int(0), int(0)][int(1)] = int(-9) # optional - gambit - sage: g[int(0), int(1)][int(0)] = int(-5) # optional - gambit - sage: g[int(0), int(1)][int(1)] = int(7) # optional - gambit - sage: g[int(0), int(2)][int(0)] = int(5) # optional - gambit - sage: g[int(0), int(2)][int(1)] = int(9) # optional - gambit - sage: g[int(1), int(0)][int(0)] = int(5) # optional - gambit - sage: g[int(1), int(0)][int(1)] = int(6) # optional - gambit - sage: g[int(1), int(1)][int(0)] = int(5) # optional - gambit - sage: g[int(1), int(1)][int(1)] = int(-2) # optional - gambit - sage: g[int(1), int(2)][int(0)] = int(3) # optional - gambit - sage: g[int(1), int(2)][int(1)] = int(-3) # optional - gambit - sage: g[int(2), int(0)][int(0)] = int(1) # optional - gambit - sage: g[int(2), int(0)][int(1)] = int(-4) # optional - gambit - sage: g[int(2), int(1)][int(0)] = int(-6) # optional - gambit - sage: g[int(2), int(1)][int(1)] = int(6) # optional - gambit - sage: g[int(2), int(2)][int(0)] = int(1) # optional - gambit - sage: g[int(2), int(2)][int(1)] = int(-10) # optional - gambit - sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + sage: # optional - gambit + sage: g = gambit.Game.new_table([3,3]) + sage: g[int(0), int(0)][int(0)] = int(-7) + sage: g[int(0), int(0)][int(1)] = int(-9) + sage: g[int(0), int(1)][int(0)] = int(-5) + sage: g[int(0), int(1)][int(1)] = int(7) + sage: g[int(0), int(2)][int(0)] = int(5) + sage: g[int(0), int(2)][int(1)] = int(9) + sage: g[int(1), int(0)][int(0)] = int(5) + sage: g[int(1), int(0)][int(1)] = int(6) + sage: g[int(1), int(1)][int(0)] = int(5) + sage: g[int(1), int(1)][int(1)] = int(-2) + sage: g[int(1), int(2)][int(0)] = int(3) + sage: g[int(1), int(2)][int(1)] = int(-3) + sage: g[int(2), int(0)][int(0)] = int(1) + sage: g[int(2), int(0)][int(1)] = int(-4) + sage: g[int(2), int(1)][int(0)] = int(-6) + sage: g[int(2), int(1)][int(1)] = int(6) + sage: g[int(2), int(2)][int(0)] = int(1) + sage: g[int(2), int(2)][int(1)] = int(-10) + sage: solver = gambit.nash.ExternalLCPSolver() Here is the LCP output:: diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 9f8da835585..d5646406882 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -253,7 +253,7 @@ class QuantuminoState(SageObject): r""" A state of the Quantumino puzzle. - Used to represent an solution or a partial solution of the Quantumino + Used to represent a solution or a partial solution of the Quantumino puzzle. INPUT: diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index c2a9252bd72..0cebb40237c 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Convex rational polyhedral cones @@ -554,7 +553,7 @@ def _ambient_space_point(body, data): An integral, rational, real algebraic, or numeric point of the ambient space of ``body`` is returned if ``data`` were - successfully interpreted in such a way. A ``TypeError`` is raised + successfully interpreted in such a way. A :class:`TypeError` is raised otherwise. TESTS:: @@ -1664,7 +1663,7 @@ def _contains(self, point, region='whole cone'): otherwise, in particular when ``point`` is incompatible with the ambient space. - A ``ValueError`` is raised if ``region`` is not one of the + A :class:`ValueError` is raised if ``region`` is not one of the three allowed values. TESTS:: @@ -2340,8 +2339,8 @@ def embed(self, cone): or :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.facet_of`. The cone returned by this method will have ``self`` as ambient. If ``cone`` - does not represent a valid cone of ``self``, ``ValueError`` exception - is raised. + does not represent a valid cone of ``self``, :class:`ValueError` + exception is raised. .. NOTE:: @@ -3017,8 +3016,8 @@ def intersection(self, other): - :class:`cone `. - Raises ``ValueError`` if the ambient space dimensions are not - compatible. + This raises :class:`ValueError` if the ambient space dimensions + are not compatible. EXAMPLES:: @@ -5349,7 +5348,7 @@ def random_element(self, ring=ZZ): Either a lattice element or vector contained in both this cone and its ambient vector space. If ``ring`` is ``ZZ``, a lattice element is returned; otherwise a vector is returned. If ``ring`` - is neither ``ZZ`` nor ``QQ``, then a ``NotImplementedError`` is + is neither ``ZZ`` nor ``QQ``, then a :class:`NotImplementedError` is raised. EXAMPLES: @@ -6267,7 +6266,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, A new, randomly generated cone. - A ``ValueError`` will be thrown under the following conditions: + A :class:`ValueError` will be thrown under the following conditions: * Any of ``min_ambient_dim``, ``max_ambient_dim``, ``min_rays``, or ``max_rays`` are negative. @@ -6425,7 +6424,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, provided. If the user requests too many rays in zero, one, or two dimensions, - a ``ValueError`` is thrown:: + a :class:`ValueError` is thrown:: sage: random_cone(max_ambient_dim=0, min_rays=1) Traceback (most recent call last): @@ -6513,7 +6512,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, sage: random_cone(lattice=L, strictly_convex=True) 0-d cone in 0-d lattice L - A ``ValueError`` is thrown if a non-solid cone is requested in a + A :class:`ValueError` is thrown if a non-solid cone is requested in a zero-dimensional lattice:: sage: L = ToricLattice(0) @@ -6527,7 +6526,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, ... ValueError: all cones are solid when max_ambient_dim is zero. - A ``ValueError`` is thrown if a solid cone is requested but the + A :class:`ValueError` is thrown if a solid cone is requested but the maximum number of rays is too few:: sage: random_cone(min_ambient_dim=4, max_rays=3, solid=True) @@ -6542,7 +6541,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, ValueError: max_rays must be at least 5 for a solid cone in this lattice. - A ``ValueError`` is thrown if a non-solid cone is requested but + A :class:`ValueError` is thrown if a non-solid cone is requested but ``min_rays`` guarantees a solid cone:: sage: random_cone(max_ambient_dim=4, min_rays=10, solid=False) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 3aecc84a55b..4ac1fdb497d 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -1956,7 +1956,7 @@ def cone_containing(self, *points): We think of the origin as of the smallest cone containing no rays at all. If there is no ray in ``self`` that contains all ``rays``, - a ``ValueError`` exception will be raised. + a :class:`ValueError` exception will be raised. EXAMPLES:: @@ -2352,8 +2352,8 @@ def embed(self, cone): or :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.facet_of`. The cone returned by this method will have ``self`` as ambient. If ``cone`` - does not represent a valid cone of ``self``, ``ValueError`` exception - is raised. + does not represent a valid cone of ``self``, :class:`ValueError` + exception is raised. .. NOTE:: @@ -3504,8 +3504,8 @@ def complex(self, base_ring=ZZ, extended=False): OUTPUT: The complex associated to the fan as a :class:`ChainComplex - `. Raises a - ``ValueError`` if the extended complex is requested for a + `. This raises a + :class:`ValueError` if the extended complex is requested for a non-complete fan. EXAMPLES:: diff --git a/src/sage/geometry/fan_morphism.py b/src/sage/geometry/fan_morphism.py index 1d3cd1db886..d00e87083f7 100644 --- a/src/sage/geometry/fan_morphism.py +++ b/src/sage/geometry/fan_morphism.py @@ -520,8 +520,8 @@ def _ray_index_map(self): domain fan is mapped to the origin. If it is `j`, then the `i`-th ray of the domain fan is mapped onto the `j`-th ray of the codomain fan. If there is a ray in the domain fan which is mapped into the relative - interior of a higher dimensional cone, a ``ValueError`` exception is - raised. + interior of a higher dimensional cone, a :class:`ValueError` + exception is raised. .. NOTE:: @@ -601,8 +601,8 @@ def _subdivide_domain_fan(self, check, verbose): OUTPUT: - none, but the domain fan of self is replaced with its minimal - refinement, if possible. Otherwise a ``ValueError`` exception is - raised. + refinement, if possible. Otherwise a :class:`ValueError` + exception is raised. TESTS:: @@ -763,11 +763,11 @@ def _subdivide_domain_fan(self, check, verbose): def _support_error(self): r""" - Raise a ``ValueError`` exception due to support incompatibility. + Raise a :class:`ValueError` exception due to support incompatibility. OUTPUT: - - none, a ``ValueError`` exception is raised. + - none, a :class:`ValueError` exception is raised. TESTS: @@ -806,10 +806,10 @@ def _validate(self): OUTPUT: - - none, but a ``ValueError`` exception is raised if there is a cone of - the domain fan of ``self`` which is not completely contained in a - single cone of the codomain fan of ``self``, or if one of these fans - does not sit in the appropriate lattice. + - none, but a :class:`ValueError` exception is raised if there is + a cone of the domain fan of ``self`` which is not completely + contained in a single cone of the codomain fan of ``self``, + or if one of these fans does not sit in the appropriate lattice. EXAMPLES:: diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py index fac28dfd161..ff509d0d97f 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Coercion Maps Between Hyperbolic Plane Models @@ -82,7 +81,7 @@ def _call_(self, x): """ C = self.codomain() if not C.is_bounded() and self.domain().is_bounded() and x.is_boundary(): - msg = u"boundary points are not implemented for the {}" + msg = "boundary points are not implemented for the {}" raise NotImplementedError(msg.format(C.name())) y = self.image_coordinates(x.coordinates()) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index 57354fad4d9..f8fb369bb39 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Hyperbolic Geodesics @@ -622,7 +621,7 @@ def is_parallel(self, other): def ideal_endpoints(self): r""" Return the ideal endpoints in bounded models. Raise a - ``NotImplementedError`` in models that are not bounded. + :class:`NotImplementedError` in models that are not bounded. EXAMPLES:: @@ -660,7 +659,7 @@ def ideal_endpoints(self): def complete(self): r""" Return the geodesic with ideal endpoints in bounded models. Raise a - ``NotImplementedError`` in models that are not bounded. + :class:`NotImplementedError` in models that are not bounded. In the following examples we represent complete geodesics by a dashed line. @@ -817,7 +816,7 @@ def common_perpendicula(self, other): r""" Return the unique hyperbolic geodesic perpendicular to two given geodesics, if such a geodesic exists. If none exists, raise a - ``ValueError``. + :class:`ValueError`. INPUT: @@ -1278,7 +1277,7 @@ def common_perpendicular(self, other): r""" Return the unique hyperbolic geodesic perpendicular to ``self`` and ``other``, if such a geodesic exists; otherwise raise a - ``ValueError``. + :class:`ValueError`. INPUT: diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py index 38f6f4266af..11f4c08add9 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Interface to Hyperbolic Models diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index 08d68b2c518..0d9e0ec4a08 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Hyperbolic Isometries @@ -489,7 +488,7 @@ def classification(self): def translation_length(self): r""" For hyperbolic elements, return the translation length; - otherwise, raise a ``ValueError``. + otherwise, raise a :class:`ValueError`. EXAMPLES:: @@ -512,7 +511,7 @@ def translation_length(self): def axis(self): r""" For a hyperbolic isometry, return the axis of the - transformation; otherwise raise a ``ValueError``. + transformation; otherwise raise a :class:`ValueError`. EXAMPLES:: @@ -593,7 +592,7 @@ def fixed_geodesic(self): def repelling_fixed_point(self): r""" For a hyperbolic isometry, return the attracting fixed point; - otherwise raise a ``ValueError``. + otherwise raise a :class:`ValueError`. OUTPUT: @@ -612,7 +611,7 @@ def repelling_fixed_point(self): def attracting_fixed_point(self): r""" For a hyperbolic isometry, return the attracting fixed point; - otherwise raise a `ValueError``. + otherwise raise a :class:`ValueError`. OUTPUT: @@ -735,7 +734,7 @@ def classification(self): #UHP def translation_length(self): #UHP r""" For hyperbolic elements, return the translation length; - otherwise, raise a ``ValueError``. + otherwise, raise a :class:`ValueError`. EXAMPLES:: @@ -843,9 +842,11 @@ def fixed_point_set(self): # UHP return self.domain().get_geodesic(*pts) return pts - def repelling_fixed_point(self): #UHP + def repelling_fixed_point(self): # UHP r""" - Return the repelling fixed point; otherwise raise a ``ValueError``. + Return the repelling fixed point. + + Otherwise, this raises a :class:`ValueError`. OUTPUT: @@ -867,9 +868,11 @@ def repelling_fixed_point(self): #UHP return self.domain().get_point(infinity) return self.domain().get_point(v[0] / v[1]) - def attracting_fixed_point(self): #UHP + def attracting_fixed_point(self): # UHP r""" - Return the attracting fixed point; otherwise raise a ``ValueError``. + Return the attracting fixed point. + + Otherwise, this raises a :class:`ValueError`. OUTPUT: diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py index 7890b222f37..33e5f0008fe 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Hyperbolic Models @@ -156,7 +155,7 @@ def _repr_(self): # Abstract sage: HyperbolicPlane().UHP() Hyperbolic plane in the Upper Half Plane Model """ - return u'Hyperbolic plane in the {}'.format(self._name) + return 'Hyperbolic plane in the {}'.format(self._name) def _element_constructor_(self, x, is_boundary=None, **graphics_options): # Abstract """ @@ -260,7 +259,7 @@ def point_in_model(self, p): def point_test(self, p): # Abstract r""" Test whether a point is in the model. If the point is in the - model, do nothing. Otherwise, raise a ``ValueError``. + model, do nothing. Otherwise, raise a :class:`ValueError`. EXAMPLES:: @@ -298,7 +297,7 @@ def boundary_point_in_model(self, p): # Abstract def bdry_point_test(self, p): # Abstract r""" Test whether a point is in the model. If the point is in the - model, do nothing; otherwise raise a ``ValueError``. + model, do nothing; otherwise raise a :class:`ValueError`. EXAMPLES:: @@ -340,7 +339,7 @@ def isometry_test(self, A): # Abstract Test whether an isometry ``A`` is in the model. If the isometry is in the model, do nothing. Otherwise, raise - a ``ValueError``. + a :class:`ValueError`. EXAMPLES:: @@ -1173,7 +1172,7 @@ def __init__(self, space): # name should really be 'Poincaré Disk Model', but utf8 is not # accepted by repr HyperbolicModel.__init__(self, space, - name=u'Poincare Disk Model', short_name="PD", + name='Poincare Disk Model', short_name="PD", bounded=True, conformal=True, dimension=2, isometry_group="PU(1, 1)", isometry_group_is_projective=True) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py index 6ae2e260aac..650c90a4403 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Hyperbolic Points diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 90e620b71bc..1b429cdf804 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Hyperplane Arrangements @@ -2723,7 +2722,7 @@ def region_containing_point(self, p): OUTPUT: - A polyhedron. A ``ValueError`` is raised if the point is not + A polyhedron. A :class:`ValueError` is raised if the point is not interior to a region, that is, sits on a hyperplane. EXAMPLES:: diff --git a/src/sage/geometry/hyperplane_arrangement/hyperplane.py b/src/sage/geometry/hyperplane_arrangement/hyperplane.py index 8d4b301f8d8..18e39e286a0 100644 --- a/src/sage/geometry/hyperplane_arrangement/hyperplane.py +++ b/src/sage/geometry/hyperplane_arrangement/hyperplane.py @@ -484,7 +484,7 @@ def orthogonal_projection(self, point): A vector in the ambient vector space that lies on the hyperplane. - In finite characteristic, a ``ValueError`` is raised if the + In finite characteristic, a :class:`ValueError` is raised if the the norm of the hyperplane normal is zero. EXAMPLES:: diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index 4ec9f9fdbdf..cdfaca2faf0 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -65,7 +65,7 @@ def make_parent(base_ring, dimension, names=None): return HyperplaneArrangements(base_ring, names=names) -class HyperplaneArrangementLibrary(): +class HyperplaneArrangementLibrary: """ The library of hyperplane arrangements. """ diff --git a/src/sage/geometry/integral_points.pxi b/src/sage/geometry/integral_points.pxi index 3ea46051b47..8d45f44714c 100644 --- a/src/sage/geometry/integral_points.pxi +++ b/src/sage/geometry/integral_points.pxi @@ -119,7 +119,7 @@ cpdef tuple parallelotope_points(spanning_points, lattice) noexcept: sage: parallelotope_points(c.rays(), c.lattice()) (N(0, 0), N(1, 1)) - A ``ValueError`` is raised if the ``spanning_points`` are not + A :class:`ValueError` is raised if the ``spanning_points`` are not linearly independent:: sage: rays = list(map(ToricLattice(2), [(1,1)]*2)) @@ -853,9 +853,9 @@ cdef class Inequality_int: OUTPUT: - Inequality `A x + b \geq 0`. A ``OverflowError`` is raised if a + Inequality `A x + b \geq 0`. A :class:`OverflowError` is raised if a machine integer is not long enough to hold the results. A - ``ValueError`` is raised if some of the input is not integral. + :class:`ValueError` is raised if some of the input is not integral. EXAMPLES:: diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 136cd370373..449143c8999 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Lattice and reflexive polytopes @@ -5017,8 +5016,8 @@ def part_of_point(self, i): Since a nef-partition induces a partition on the set of boundary lattice points of `\Delta^\circ`, the value of `j` is well-defined for all `i` but the one that corresponds to the origin, in which - case this method will raise a ``ValueError`` exception. (The origin - always belongs to all `\nabla_j`.) + case this method will raise a :class:`ValueError` exception. + (The origin always belongs to all `\nabla_j`.) See :class:`nef-partition ` class documentation for definitions and notation. diff --git a/src/sage/geometry/point_collection.pyx b/src/sage/geometry/point_collection.pyx index 3a67de459ed..aabb0bdcde3 100644 --- a/src/sage/geometry/point_collection.pyx +++ b/src/sage/geometry/point_collection.pyx @@ -756,7 +756,7 @@ cdef class PointCollection(SageObject): OUTPUT: - an integer if ``point`` is in ``self[start:stop]``, otherwise a - ``ValueError`` exception is raised. + :class:`ValueError` exception is raised. EXAMPLES:: diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index d6e3b075656..09200a60be9 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -163,7 +163,7 @@ class PolyhedralComplex(GenericCellComplex): - ``face_to_face_check`` -- boolean (default: ``False``); if ``True``, then the constructor checks whether the cells - are face-to-face, and it raises a ``ValueError`` if they are not + are face-to-face, and it raises a :class:`ValueError` if they are not - ``is_mutable`` and ``is_immutable`` -- boolean (default: ``True`` and ``False`` respectively); set ``is_mutable=False`` or ``is_immutable=True`` @@ -967,7 +967,7 @@ def __contains__(self, x): def __call__(self, x): """ If ``x`` is a polyhedron in this complex, return it. - Otherwise, raise a ``ValueError``. + Otherwise, raise a :class:`ValueError`. EXAMPLES:: @@ -1072,10 +1072,10 @@ def is_compact(self): def graph(self): """ - The 1-skeleton of this polyhedral complex, as a graph. + Return the 1-skeleton of this polyhedral complex, as a graph. - The vertices of the graph are of type ``vector``. Raises - a ``NotImplementedError`` if the polyhedral complex is unbounded. + The vertices of the graph are of type ``vector``. This raises + a :class:`NotImplementedError` if the polyhedral complex is unbounded. .. WARNING:: diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index f0e7e3ada8b..e35ea92fe67 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" The cdd backend for polyhedral computations """ diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 089e687f3f8..a2bce697e1c 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ The polymake backend for polyhedral computations diff --git a/src/sage/geometry/polyhedron/base4.py b/src/sage/geometry/polyhedron/base4.py index 5313d64b357..2ba2a696b30 100644 --- a/src/sage/geometry/polyhedron/base4.py +++ b/src/sage/geometry/polyhedron/base4.py @@ -70,7 +70,7 @@ def vertex_facet_graph(self, labels=True): This function constructs a directed bipartite graph. The nodes of the graph correspond to the vertices of the polyhedron - and the facets of the polyhedron. There is an directed edge + and the facets of the polyhedron. There is a directed edge from a vertex to a face if and only if the vertex is incident to the face. INPUT: @@ -406,7 +406,7 @@ def hasse_diagram(self): 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 + Faces of a mutable polyhedron are not hashable. Hence those are not suitable as vertices of the hasse diagram. Use the combinatorial polyhedron instead:: sage: # needs sage.rings.number_field diff --git a/src/sage/geometry/polyhedron/base7.py b/src/sage/geometry/polyhedron/base7.py index c1c06b90a28..99ca1b7ebe7 100644 --- a/src/sage/geometry/polyhedron/base7.py +++ b/src/sage/geometry/polyhedron/base7.py @@ -133,7 +133,7 @@ def centroid(self, engine='auto', **kwds): else: from sage.geometry.triangulation.point_configuration import PointConfiguration A, b = self.affine_hull_projection(as_affine_map=True, orthogonal=True, orthonormal=True, extend=True) - pc = PointConfiguration((A(v.vector()) for v in self.Vrep_generator())) + pc = PointConfiguration(A(v.vector()) for v in self.Vrep_generator()) barycenters = [sum(self.Vrepresentation(i).vector() for i in simplex)/(self.dim() + 1) for simplex in triangulation] volumes = [pc.volume(simplex) for simplex in triangulation] diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index 8c23470e0e3..cd7b7316a43 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -547,7 +547,7 @@ cdef tuple face_as_combinatorial_polyhedron(ListOfFaces facets, ListOfFaces Vrep else: delete = mem.allocarray(max(facets.n_faces(), facets.n_atoms()), sizeof(bint)) - # Set ``delete[i]`` to one if ``i`` is not an vertex of ``face``. + # Set ``delete[i]`` to one if ``i`` is not a vertex of ``face``. for i in range(Vrep.n_faces()): if face_issubset(face, Vrep.data.faces[i]): delete[i] = 0 diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index a1a59cdfe65..d48cffd5c5c 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Library of commonly used, famous, or interesting polytopes @@ -489,7 +488,7 @@ def gale_transform_to_primal(vectors, base_ring=None, backend=None): return m.right_kernel_matrix(basis='computed').columns() -class Polytopes(): +class Polytopes: """ A class of constructors for commonly used, famous, or interesting polytopes. @@ -1003,7 +1002,7 @@ def rhombic_dodecahedron(self, backend=None): sage: TestSuite(rd_norm).run() # optional - pynormaliz """ v = [[2,0,0],[-2,0,0],[0,2,0],[0,-2,0],[0,0,2],[0,0,-2]] - v.extend((itertools.product([1, -1], repeat=3))) + v.extend(itertools.product([1, -1], repeat=3)) return Polyhedron(vertices=v, base_ring=ZZ, backend=backend) def cuboctahedron(self, backend=None): @@ -2552,7 +2551,7 @@ def permutahedron(self, n, project=False, backend=None): sage: perm4.graph().is_isomorphic(graphs.BubbleSortGraph(4)) # needs sage.graphs True - As both Hrepresentation an Vrepresentation are known, the permutahedron can be set + As both Hrepresentation and Vrepresentation are known, the permutahedron can be set up with both using the backend ``field``. The following takes very very long time to recompute, e.g. with backend ``ppl``:: diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 5c2362cfdda..78767803eb0 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -144,7 +144,7 @@ def projection_func_identity(x): return list(x) -class ProjectionFuncStereographic(): +class ProjectionFuncStereographic: """ The stereographic (or perspective) projection onto a codimension-1 linear subspace with respect to a sphere centered at the origin. @@ -238,7 +238,7 @@ def __call__(self, x): return vector(RDF, [img[i] / denom for i in range(self.dim - 1)]) -class ProjectionFuncSchlegel(): +class ProjectionFuncSchlegel: """ The Schlegel projection from the given input point. diff --git a/src/sage/geometry/riemannian_manifolds/surface3d_generators.py b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py index ba8eff2ed2a..a75fdb0f2f2 100644 --- a/src/sage/geometry/riemannian_manifolds/surface3d_generators.py +++ b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py @@ -23,7 +23,7 @@ ParametrizedSurface3D -class SurfaceGenerators(): +class SurfaceGenerators: """ A class consisting of generators for several common parametrized surfaces in 3D. diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index a72f31f04d2..4a34a6a510e 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -677,7 +677,7 @@ def quotient(self, sub, check=True, by Sublattice Attempting to quotient one lattice by a sublattice of another - will result in a ``ValueError``:: + will result in a :class:`ValueError`:: sage: N = ToricLattice(3) sage: M = ToricLattice(3, name='M') @@ -1467,7 +1467,7 @@ def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=No sage: ToricLattice_quotient(N, N.span([N(1,2,3)])) 2-d lattice, quotient of 3-d lattice N by Sublattice - An ``ArithmeticError`` will be raised if ``W`` is not a + An :class:`ArithmeticError` will be raised if ``W`` is not a sublattice of ``V``:: sage: N = ToricLattice(3) diff --git a/src/sage/geometry/toric_lattice_element.pyx b/src/sage/geometry/toric_lattice_element.pyx index bc736243642..60782a805f4 100644 --- a/src/sage/geometry/toric_lattice_element.pyx +++ b/src/sage/geometry/toric_lattice_element.pyx @@ -300,7 +300,7 @@ cdef class ToricLatticeElement(Vector_integer_dense): # is wrong from our point of view. cpdef _dot_product_(self, Vector right) noexcept: """ - Raise a ``TypeError`` exception. + Raise a :class:`TypeError` exception. Dot product is not defined on toric lattices (there are actions of dual lattices on each other instead). @@ -311,7 +311,7 @@ cdef class ToricLatticeElement(Vector_integer_dense): OUTPUT: - - ``TypeError`` exception is raised. + - :class:`TypeError` exception is raised. TESTS:: diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index 950e3ba7ff8..2943daa49bd 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -746,7 +746,7 @@ def _unrecognized_option(option): OUTPUT: - - none, a ``KeyError`` exception is raised. + - none, a :class:`KeyError` exception is raised. TESTS:: diff --git a/src/sage/geometry/triangulation/base.pyx b/src/sage/geometry/triangulation/base.pyx index 61014429fdd..3161d19afba 100644 --- a/src/sage/geometry/triangulation/base.pyx +++ b/src/sage/geometry/triangulation/base.pyx @@ -588,7 +588,7 @@ cdef class PointConfiguration_base(Parent): def _assert_is_affine(self): """ - Raise a ``ValueError`` if the point configuration is not + Raise a :class:`ValueError` if the point configuration is not defined by affine points. EXAMPLES:: diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 68ac3119945..89d486b1e4f 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -733,7 +733,7 @@ def _TOPCOM_triangulate(self, verbose=True): OUTPUT: A :class:`~sage.geometry.triangulation.element.Triangulation` - satisfying all restrictions imposed. Raises a ``ValueError`` + satisfying all restrictions imposed. This raises a :class:`ValueError` if no such triangulation exists. EXAMPLES:: @@ -1037,7 +1037,7 @@ def triangulate(self, verbose=False): OUTPUT: A :class:`~sage.geometry.triangulation.element.Triangulation` - satisfying all restrictions imposed. Raises a ``ValueError`` + satisfying all restrictions imposed. This raises a :class:`ValueError` if no such triangulation exists. EXAMPLES:: diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 84b048c84c5..5c7aa0b61c8 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2298,7 +2298,7 @@ cdef double diameter_DiFUB(BoostVecWeightedDiGraphU g_boost, Return the diameter of a weighted directed graph. The ``DiFUB`` (Directed iterative Fringe Upper Bound) algorithm calculates - the exact value of the diameter of an weighted directed graph [CGLM2012]_. + the exact value of the diameter of a weighted directed graph [CGLM2012]_. This algorithm starts from a vertex found through a 2Dsweep call (a directed version of the 2sweep method). The worst case time complexity of the DiFUB @@ -2492,11 +2492,11 @@ cpdef diameter(G, algorithm=None, source=None, - ``algorithm`` -- string (default: ``None``); specifies the algorithm to use among: - - ``'2Dsweep'`` -- Computes lower bound on the diameter of an weighted + - ``'2Dsweep'`` -- Computes lower bound on the diameter of a weighted directed graph using the weighted version of the algorithm proposed in [Broder2000]_. See the code's documentation for more details. - - ``'DiFUB'`` -- Computes the diameter of an weighted directed graph + - ``'DiFUB'`` -- Computes the diameter of a weighted directed graph using the weighted version of the algorithm proposed in [CGLM2012]_. See the code's documentation for more details. diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index 7ca6c5dd124..70ce392a43c 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -624,8 +624,8 @@ cdef class CGraph: OUTPUT: - - Raise a ``NotImplementedError``. This method is not implemented in - this base class. A child class should provide a suitable + - Raise a :class:`NotImplementedError`. This method is not implemented + in this base class. A child class should provide a suitable implementation. .. SEEALSO:: @@ -1267,7 +1267,7 @@ cdef class CGraph: OUTPUT: - - Raise ``NotImplementedError``. This method is not implemented at + - Raise :class:`NotImplementedError`. This method is not implemented at the :class:`CGraph` level. A child class should provide a suitable implementation. diff --git a/src/sage/graphs/base/graph_backends.pyx b/src/sage/graphs/base/graph_backends.pyx index 863f61be013..77961c33cf6 100644 --- a/src/sage/graphs/base/graph_backends.pyx +++ b/src/sage/graphs/base/graph_backends.pyx @@ -6,7 +6,7 @@ This module implements :class:`GenericGraphBackend` (the base class for backends). Any graph backend must redefine the following methods (for which -:class:`GenericGraphBackend` raises a ``NotImplementedError``) +:class:`GenericGraphBackend` raises a :class:`NotImplementedError`) .. csv-table:: :class: contentstable diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 0a03affa422..6bb65e6ad33 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -1,5 +1,4 @@ # autopep8: off -# -*- coding: utf-8 -*- r""" Bipartite graphs diff --git a/src/sage/graphs/bliss.pyx b/src/sage/graphs/bliss.pyx index eac56e21f0d..c623b5fe402 100644 --- a/src/sage/graphs/bliss.pyx +++ b/src/sage/graphs/bliss.pyx @@ -396,9 +396,10 @@ cpdef canonical_form(G, partition=None, return_graph=False, use_edge_labels=True canonical graph of ``G`` or its set of edges - ``use_edge_labels`` -- boolean (default: ``True``); whether to consider - edge labels. The edge labels are assumed to be hashable and sortable. If - this is not the case (ie a ``TypeError`` is raised), the algorithm will - consider the string representations of the labels instead of the labels. + edge labels. The edge labels are assumed to be hashable and + sortable. If this is not the case (ie a :class:`TypeError` is + raised), the algorithm will consider the string representations + of the labels instead of the labels. - ``certificate`` -- boolean (default: ``False``); when set to ``True``, returns the labeling of G into a canonical graph diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 5c8166bd540..910fcc88df8 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -3969,7 +3969,7 @@ cdef class TriconnectivitySPQR: self.in_adj[eh_index] = e_virt_node # end type-1 search - # if an path starts at edge e, empty the tstack. + # if a path starts at edge e, empty the tstack. if self.starts_path[e_index]: while self.__tstack_not_eos(): self.t_stack_top -= 1 diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 8ba57d90b3c..58f902b10cd 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Directed graphs @@ -1195,7 +1194,7 @@ def neighbor_in_iterator(self, vertex): """ Return an iterator over the in-neighbors of ``vertex``. - An vertex `u` is an in-neighbor of a vertex `v` if `uv` in an edge. + A vertex `u` is an in-neighbor of a vertex `v` if `uv` in an edge. EXAMPLES:: @@ -1672,7 +1671,7 @@ def feedback_edge_set(self, constraint_generation=True, value_only=False, p = MixedIntegerLinearProgram(constraint_generation=True, maximization=False, solver=solver) - # An variable for each edge + # A variable for each edge b = p.new_variable(binary=True) # Variables are binary, and their coefficient in the objective is @@ -3187,9 +3186,9 @@ def topological_sort(self, implementation="default"): """ Return a topological sort of the digraph if it is acyclic. - If the digraph contains a directed cycle, a ``TypeError`` is raised. As - topological sorts are not necessarily unique, different implementations - may yield different results. + If the digraph contains a directed cycle, a :class:`TypeError` + is raised. As topological sorts are not necessarily unique, + different implementations may yield different results. A topological sort is an ordering of the vertices of the digraph such that each vertex comes before all of its successors. That is, if `u` @@ -3269,7 +3268,8 @@ def topological_sort_generator(self): Return an iterator over all topological sorts of the digraph if it is acyclic. - If the digraph contains a directed cycle, a ``TypeError`` is raised. + If the digraph contains a directed cycle, a :class:`TypeError` + is raised. A topological sort is an ordering of the vertices of the digraph such that each vertex comes before all of its successors. That is, if u comes diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 2b29e0e73ef..d182f49afb1 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -74,7 +74,7 @@ from sage.graphs.graph import Graph -class DiGraphGenerators(): +class DiGraphGenerators: r""" A class consisting of constructors for several common digraphs, including orderly generation of isomorphism class representatives. diff --git a/src/sage/graphs/domination.py b/src/sage/graphs/domination.py index 7f631fcfe28..6323cdd29e4 100644 --- a/src/sage/graphs/domination.py +++ b/src/sage/graphs/domination.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Domination diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index 5dbacb4de26..0dc2c00baef 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Basic graphs @@ -400,7 +399,7 @@ def CompleteGraph(n): G.set_pos({0: (0, 0)}) else: G._circle_embedding(list(range(n)), angle=pi/2) - G.add_edges(((i, j) for i in range(n) for j in range(i + 1, n))) + G.add_edges((i, j) for i in range(n) for j in range(i + 1, n)) return G def CorrelationGraph(seqs, alpha, include_anticorrelation): diff --git a/src/sage/graphs/generators/chessboard.py b/src/sage/graphs/generators/chessboard.py index dfe5b15b273..a76d6f98de7 100644 --- a/src/sage/graphs/generators/chessboard.py +++ b/src/sage/graphs/generators/chessboard.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Chessboard graphs diff --git a/src/sage/graphs/generators/degree_sequence.py b/src/sage/graphs/generators/degree_sequence.py index f5e68d89c79..05c021403b0 100644 --- a/src/sage/graphs/generators/degree_sequence.py +++ b/src/sage/graphs/generators/degree_sequence.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Graphs with a given degree sequence @@ -98,8 +97,8 @@ def DegreeSequenceBipartite(s1, s2): True Some sequences being incompatible if, for example, their sums are different, - the functions raises a ``ValueError`` when no graph corresponding to the - degree sequences exists:: + the functions raises a :class:`ValueError` when no graph corresponding + to the degree sequences exists:: sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,1],[5,5]) # needs sage.combinat sage.modules Traceback (most recent call last): diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 85ea52a4571..c962378cd25 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Various families of graphs @@ -594,7 +593,7 @@ def BarbellGraph(n1, n2): OUTPUT: - A barbell graph of order ``2*n1 + n2``. A ``ValueError`` is + A barbell graph of order ``2*n1 + n2``. A :class:`ValueError` is returned if ``n1 < 2`` or ``n2 < 0``. PLOTTING: @@ -929,7 +928,7 @@ def BubbleSortGraph(n): OUTPUT: The bubble sort graph `B(n)` on `n` symbols. If `n < 1`, a - ``ValueError`` is returned. + :class:`ValueError` is returned. EXAMPLES:: @@ -2714,7 +2713,7 @@ def SwitchedSquaredSkewHadamardMatrixGraph(n): G = SquaredSkewHadamardMatrixGraph(n).complement() G.add_vertex((4 * n - 1)**2) G.seidel_switching(list(range((4 * n - 1) * (2 * n - 1)))) - G.name("switch skewhad^2+*_" + str((n))) + G.name("switch skewhad^2+*_" + str(n)) return G @@ -3920,7 +3919,13 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None): sage: G.is_strongly_regular(parameters=True) # needs sage.modules sage.rings.finite_rings (45, 22, 10, 11) - Supplying ``G`` and ``L`` (constructed from the automorphism group of ``G``). :: + Supplying ``G`` and ``L`` (constructed from the automorphism group + of ``G``). The entries of L can't be tested directly because + there's some unpredictability in the way that GAP chooses a + representative in ``NormalSubgroups()``, the function that + underlies our own + :meth:`~sage.groups.perm_gps.permgroup.PermutationGroup_generic.normal_subgroups` + method:: sage: # needs sage.groups sage.libs.gap sage.rings.finite_rings sage: G = graphs.PaleyGraph(9) @@ -3931,18 +3936,7 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None): ....: for z in subg] sage: ff = list(map(lambda y: (y[0]-1,y[1]-1), ....: Permutation(map(lambda x: 1+r.index(x^-1), r)).cycle_tuples()[1:])) - sage: L = sum(i*(r[a]-r[b]) for i,(a,b) in zip(range(1,len(ff)+1), ff)); L - [ 0 1 -1 -3 -2 -4 3 4 2] - [-1 0 1 -4 -3 -2 2 3 4] - [ 1 -1 0 -2 -4 -3 4 2 3] - [ 3 4 2 0 1 -1 -3 -2 -4] - [ 2 3 4 -1 0 1 -4 -3 -2] - [ 4 2 3 1 -1 0 -2 -4 -3] - [-3 -2 -4 3 4 2 0 1 -1] - [-4 -3 -2 2 3 4 -1 0 1] - [-2 -4 -3 4 2 3 1 -1 0] - - sage: # needs sage.groups sage.libs.gap sage.modules sage.rings.finite_rings + sage: L = sum(i*(r[a]-r[b]) for i,(a,b) in zip(range(1,len(ff)+1), ff)) sage: G.relabel(range(9)) sage: G3x3 = graphs.MathonPseudocyclicStronglyRegularGraph(2, G=G, L=L) sage: G3x3.is_strongly_regular(parameters=True) diff --git a/src/sage/graphs/generators/intersection.py b/src/sage/graphs/generators/intersection.py index 0d19d30f9ea..0434806fb73 100644 --- a/src/sage/graphs/generators/intersection.py +++ b/src/sage/graphs/generators/intersection.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Intersection graphs diff --git a/src/sage/graphs/generators/platonic_solids.py b/src/sage/graphs/generators/platonic_solids.py index d3bc60a9974..5a572611539 100644 --- a/src/sage/graphs/generators/platonic_solids.py +++ b/src/sage/graphs/generators/platonic_solids.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" 1-skeletons of Platonic solids diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 22c1f583f6a..e0868b36e21 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Random graphs diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 3b47f29f2ff..bde0cafc0a8 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Various small graphs diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index f131aa446e2..76ec2b4f9ae 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Graphs from the World Map diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0f0f995bfbd..e6677c26803 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Generic graphs (common to directed/undirected) @@ -2933,8 +2932,8 @@ def _check_embedding_validity(self, embedding=None, boolean=True): ``_embedding`` - ``boolean`` -- boolean (default: ``True``); -- whether to return a - boolean answer or raise a ``ValueError`` exception if the embedding is - invalid + boolean answer or raise a :class:`ValueError` exception + if the embedding is invalid EXAMPLES:: @@ -3443,8 +3442,8 @@ def allow_multiple_edges(self, new, check=True, keep_label='any'): .. WARNING:: ``'min'`` and ``'max'`` only works if the labels can be compared. A - ``TypeError`` might be raised when working with non-comparable - objects in Python 3. + :class:`TypeError` might be raised when working with non-comparable + objects. EXAMPLES: @@ -4092,7 +4091,7 @@ def density(self): if n < 2: return Rational(0) if self._directed: - return Rational(self.size()) / Rational((n ** 2 - n)) + return Rational(self.size()) / Rational(n ** 2 - n) return Rational(self.size()) / Rational((n ** 2 - n) / 2) def is_bipartite(self, certificate=False): @@ -8359,7 +8358,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, EXAMPLES: - The `3 \times 3`-grid has an Hamiltonian path, an hamiltonian path + The `3 \times 3`-grid has a Hamiltonian path, a hamiltonian path starting from vertex `(0, 0)` and ending at vertex `(2, 2)`, but no Hamiltonian path starting from `(0, 0)` and ending at `(0, 1)`:: @@ -8531,7 +8530,7 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, g.add_edge(v, new_s, 0) # - # We now search for an Hamiltonian Cycle in g + # We now search for a Hamiltonian Cycle in g # from sage.categories.sets_cat import EmptySetError try: @@ -14065,7 +14064,7 @@ def subgraph_search_count(self, G, induced=False): .. SEEALSO:: - - :meth:`~GenericGraph.subgraph_search` -- finds an subgraph + - :meth:`~GenericGraph.subgraph_search` -- finds a subgraph isomorphic to `H` inside of a graph `G` - :meth:`~GenericGraph.subgraph_search_iterator` -- iterator over @@ -14174,7 +14173,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): .. SEEALSO:: - - :meth:`~GenericGraph.subgraph_search` -- finds an subgraph + - :meth:`~GenericGraph.subgraph_search` -- finds a subgraph isomorphic to `H` inside of `G` - :meth:`~GenericGraph.subgraph_search_count` -- counts the number @@ -18894,8 +18893,8 @@ def to_simple(self, to_undirected=True, keep_label='any', immutable=None): .. WARNING:: ``'min'`` and ``'max'`` only works if the labels can be compared. A - ``TypeError`` might be raised when working with non-comparable - objects in Python 3. + :class:`TypeError` might be raised when working with non-comparable + objects. - ``immutable`` -- boolean (default: ``Non``); whether to create a mutable/immutable copy. ``immutable=None`` (default) means that the @@ -21634,7 +21633,7 @@ def graphviz_string(self, **options): - a HSV sequence in a string such as ``".52,.386,.22"`` - - an hexadecimal code such as ``"#DA3305"`` + - a hexadecimal code such as ``"#DA3305"`` - a 3-tuple of floating point (to be interpreted as RGB tuple). In this case the 3-tuple is converted in hexadecimal code. @@ -25341,7 +25340,7 @@ def graph_isom_equivalent_non_edge_labeled_graph(g, partition=None, standard_lab for el, part in edge_partition: # The multiplicity of a label is the number of edges from u to v # it represents - m = sum((y[1] for y in el)) + m = sum(y[1] for y in el) if m in tmp: tmp[m].append(part) else: diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index d27b3b65047..653a36d43f3 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -501,7 +501,7 @@ def length_and_string_from_graph6(s): INPUT: - - ``s`` -- a graph6 string describing an binary vector (and encoding its + - ``s`` -- a graph6 string describing a binary vector (and encoding its length). EXAMPLES:: @@ -1349,7 +1349,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, find_path = (find_path > 0) if G.is_clique(induced=False): - # We have an hamiltonian path since n >= 2, but we have an hamiltonian + # We have a hamiltonian path since n >= 2, but we have a hamiltonian # cycle only if n >= 3 return find_path or n >= 3, list(G) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 8988765d767..0308fbca960 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Undirected graphs @@ -885,7 +884,7 @@ class Graph(GenericGraph): sage: Graph(igraph.Graph(directed=True)) # optional - python_igraph Traceback (most recent call last): ... - ValueError: An *undirected* igraph graph was expected. To build an directed graph, call the DiGraph constructor. + ValueError: An *undirected* igraph graph was expected. To build a directed graph, call the DiGraph constructor. sage: # needs sage.modules sage: m = matrix([[0, -1], [-1, 0]]) @@ -1206,7 +1205,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'igraph': if data.is_directed(): raise ValueError("An *undirected* igraph graph was expected. " - "To build an directed graph, call the DiGraph " + "To build a directed graph, call the DiGraph " "constructor.") self.add_vertices(range(data.vcount())) @@ -3285,8 +3284,8 @@ def bounded_outdegree_orientation(self, bound, solver=None, verbose=False, OUTPUT: - A DiGraph representing the orientation if it exists. A ``ValueError`` - exception is raised otherwise. + A DiGraph representing the orientation if it exists. + A :class:`ValueError` exception is raised otherwise. ALGORITHM: @@ -7985,7 +7984,7 @@ def modular_decomposition(self, algorithm=None, style='tuple'): vertex of `C` has a neighbor outside of it. * An anticomponent `C` (or the union of some --but not all-- of them) of - an non-anticonnected graph `G`, for the same reason (it is just the + a non-anticonnected graph `G`, for the same reason (it is just the complement of the previous graph !). These modules being of special interest, the disjoint union of graphs is diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 7a0c71c2fb1..c994475bd6b 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Modular Decomposition diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx index 991f06efcce..803f5a09771 100644 --- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx @@ -1083,37 +1083,31 @@ def label_nice_tree_decomposition(nice_TD, root, directed=False): EXAMPLES:: sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition, label_nice_tree_decomposition - sage: bip_one_four = graphs.CompleteBipartiteGraph(1, 4) - sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True) - sage: nice_TD = make_nice_tree_decomposition(bip_one_four, bip_one_four_TD) + sage: claw = graphs.CompleteBipartiteGraph(1, 3) + sage: claw_TD = claw.treewidth(certificate=True) + sage: nice_TD = make_nice_tree_decomposition(claw, claw_TD) sage: root = sorted(nice_TD.vertices())[0] sage: label_TD = label_nice_tree_decomposition(nice_TD, root, directed=True) - sage: print(label_TD.name()) - Labelled Nice tree decomposition of Tree decomposition - sage: for node in sorted(label_TD): + sage: label_TD.name() + 'Labelled Nice tree decomposition of Tree decomposition' + sage: for node in sorted(label_TD): # random ....: print(node, label_TD.get_vertex(node)) (0, {}) forget (1, {0}) forget (2, {0, 1}) intro (3, {0}) forget - (4, {0, 4}) join - (5, {0, 4}) intro - (6, {0, 4}) intro - (7, {0}) forget - (8, {0}) forget - (9, {0, 3}) intro - (10, {0, 2}) intro - (11, {3}) intro - (12, {2}) intro - (13, {}) leaf - (14, {}) leaf + (4, {0, 3}) intro + (5, {0}) forget + (6, {0, 2}) intro + (7, {2}) intro + (8, {}) leaf """ from sage.graphs.digraph import DiGraph from sage.graphs.graph import Graph directed_TD = DiGraph(nice_TD.breadth_first_search(start=root, edges=True), format='list_of_edges', - name='Labelled {}'.format(nice_TD)) + name='Labelled {}'.format(nice_TD.name())) # The loop starts from the root node # We assume the tree decomposition is valid and nice, diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 0677028e451..58a52a309f2 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -475,7 +475,7 @@ def wrap_name(x): from . import graph -class GraphGenerators(): +class GraphGenerators: r""" A class consisting of constructors for several common graphs, as well as orderly generation of isomorphism class representatives. See the @@ -1192,6 +1192,121 @@ def nauty_genbg(self, options="", debug=False): G = BipartiteGraph(s[:-1], format='graph6', partition=partition) yield G + def nauty_genktreeg(self, options="", debug=False): + r""" + Return a generator which creates all `k`-trees using nauty.. + + A `k`-tree is an undirected graph formed by starting with a complete + graph on `k + 1` vertices and then repeatedly add vertices in such a + way that each added vertex `v` has exactly `k` neighbors `U` such that, + together, the `k + 1` vertices formed by `v` and `U` form a clique. + See the :wikipedia:`K-tree` for more details. + + INPUT: + + - ``options`` -- string (default: ``""``); a string passed to + ``genktreeg`` as if it was run at a system command line. At a minimum, + you *must* pass the number of vertices you desire. Sage expects the + graphs to be in nauty's "graph6" format, do not set an option to + change this default or results will be unpredictable. + + - ``debug`` -- boolean (default: ``False``); if ``True`` the first line + of ``genktreeg``'s output to standard error is captured and the first + call to the generator's ``next()`` function will return this line as a + string. A line leading with ">A" indicates a successful initiation of + the program with some information on the arguments, while a line + beginning with ">E" indicates an error with the input. + + The possible options, obtained as output of ``genktreeg --help``:: + + n : the number of vertices + -k : the value of `k`(default: 2) + res/mod : only generate subset res out of subsets 0..mod-1 + -l : canonically label output graphs + + Options which cause ``genktreeg`` to use an output format different than + the graph6 format are not listed above (-u, -s, -h) as they will confuse + the creation of a Sage graph. The res/mod option can be useful when + using the output in a routine run several times in parallel. + + OUTPUT: + + A generator which will produce the graphs as Sage graphs. + These will be simple graphs: no loops, no multiple edges, no + directed edges. + + EXAMPLES: + + A `k`-tree is a maximal graph with treewidth `k`:: + + sage: # needs nauty + sage: gen = graphs.nauty_genktreeg("10 -k4") + sage: G = next(gen); G + Graph on 10 vertices + sage: G.treewidth() + 4 + + A list of all 2-trees with 6, 7 and 8 vertices. This agrees with + :oeis:`A054581`:: + + sage: # needs nauty + sage: gen = graphs.nauty_genktreeg("6") + sage: len(list(gen)) + 5 + sage: gen = graphs.nauty_genktreeg("7") + sage: len(list(gen)) + 12 + sage: gen = graphs.nauty_genktreeg("8") + sage: len(list(gen)) + 39 + + The ``debug`` switch can be used to examine ``geng``'s reaction to the + input in the ``options`` string. We illustrate success. (A failure + will be a string beginning with ">E".) Passing the "-q" switch to + ``geng`` will suppress the indicator of a successful initiation, and so + the first returned value might be an empty string if ``debug`` is + ``True``:: + + sage: gen = graphs.nauty_genktreeg("7", debug=True) # needs nauty + sage: print(next(gen)) # needs nauty + >A ...genktreeg k=2 n=7 + + TESTS: + + Wrong input:: + + sage: # needs nauty + sage: list(graphs.nauty_genktreeg("4 -k5", debug=True)) + ['>E genktreeg: n cannot be less than k\n'] + sage: list(graphs.nauty_genktreeg("10 -k 4", debug=True)) + ['>E genktreeg -k: missing argument value\n'] + sage: list(graphs.nauty_genktreeg("-c3", debug=False)) + Traceback (most recent call last): + ... + ValueError: wrong format of parameter option + """ + import shlex + from sage.features.nauty import NautyExecutable + geng_path = NautyExecutable("genktreeg").absolute_filename() + sp = subprocess.Popen(shlex.quote(geng_path) + " {0}".format(options), shell=True, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True, + encoding='latin-1') + msg = sp.stderr.readline() + if debug: + yield msg + elif msg.startswith('>E'): + raise ValueError('wrong format of parameter option') + gen = sp.stdout + while True: + try: + s = next(gen) + except StopIteration: + # Exhausted list of graphs from nauty geng + return + G = graph.Graph(s[:-1], format='graph6') + yield G + def cospectral_graphs(self, vertices, matrix_function=None, graphs=None): r""" Find all sets of graphs on ``vertices`` vertices (with diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 193afc9c4eb..4e08af6a4fc 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -559,7 +559,7 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): for u in D: if len(set(D[u])) != len(D[u]): if multiedges is False: - v = next((v for v in D[u] if D[u].count(v) > 1)) + v = next(v for v in D[u] if D[u].count(v) > 1) raise ValueError("non-multigraph got several edges (%s, %s)" % (u, v)) multiedges = True break diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py index 1cd878bec5a..bff200a1a86 100644 --- a/src/sage/graphs/graph_latex.py +++ b/src/sage/graphs/graph_latex.py @@ -625,7 +625,7 @@ def set_option(self, option_name, option_value=None): - ``option_name`` -- a string for a latex option contained in the list ``sage.graphs.graph_latex.GraphLatex.__graphlatex_options``. - A ``ValueError`` is raised if the option is not allowed. + A :class:`ValueError` is raised if the option is not allowed. - ``option_value`` -- a value for the option. If omitted, or set to ``None``, the option will use the default value. diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index 4291e024001..f5218e225f1 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -32,7 +32,7 @@ """ -class HypergraphGenerators(): +class HypergraphGenerators: r""" A class consisting of constructors for common hypergraphs. """ diff --git a/src/sage/graphs/lovasz_theta.py b/src/sage/graphs/lovasz_theta.py index 49bc7e32092..5335a597ebf 100644 --- a/src/sage/graphs/lovasz_theta.py +++ b/src/sage/graphs/lovasz_theta.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Lovász theta-function of graphs diff --git a/src/sage/graphs/partial_cube.py b/src/sage/graphs/partial_cube.py index 147f1acb097..79b1edc957c 100644 --- a/src/sage/graphs/partial_cube.py +++ b/src/sage/graphs/partial_cube.py @@ -363,7 +363,7 @@ def is_partial_cube(G, certificate=False): # Make a digraph with edges labeled by the equivalence classes in unionfind g = DiGraph({v: {w: unionfind.find((v, w)) for w in G[v]} for v in G}) - # Associates to a vertex the token that acts on it, an check that + # Associates to a vertex the token that acts on it, and check that # no two edges on a single vertex have the same label action = {} for v in g: diff --git a/src/sage/graphs/pq_trees.py b/src/sage/graphs/pq_trees.py index 2ddd39691e8..68063bc8e73 100644 --- a/src/sage/graphs/pq_trees.py +++ b/src/sage/graphs/pq_trees.py @@ -558,7 +558,7 @@ def set_contiguous(self, v): In any case, the sets containing ``v`` are contiguous when this function ends. If there is no possibility of doing so, the function - raises a ``ValueError`` exception. + raises a :class:`ValueError` exception. EXAMPLES: @@ -837,7 +837,7 @@ def set_contiguous(self, v): In any case, the sets containing ``v`` are contiguous when this function ends. If there is no possibility of doing so, the function - raises a ``ValueError`` exception. + raises a :class:`ValueError` exception. EXAMPLES: diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index 40f6d923656..b52dcff829a 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -555,7 +555,7 @@ def _compute_coordinates(g, x): g.set_pos(coordinates) # Setting _pos attribute to store coordinates -class TreeNode(): +class TreeNode: """ A class to represent each node in the trees used by ``_realizer`` and ``_compute_coordinates`` when finding a planar geometric embedding in diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 4095448b606..632016b0703 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -2829,7 +2829,7 @@ def strongly_regular_graph(int v, int k, int l, int mu=-1, bint existence=False, ... ValueError: There exists no (5, 5, 5, 5)-strongly regular graph - An set of parameters proved in a paper to be infeasible:: + A set of parameters proved in a paper to be infeasible:: sage: graphs.strongly_regular_graph(324,57,0,12,existence=True) # needs sage.combinat sage.modules False diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index e82150eeb9c..4868577c892 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -218,7 +218,7 @@ def edge_multiplicities(G): ######## -class Ear(): +class Ear: r""" An ear is a sequence of vertices @@ -372,7 +372,7 @@ def removed_from(self, G): ################## -class EdgeSelection(): +class EdgeSelection: pass diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index f2e79c92353..66558a9ffb0 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -581,8 +581,8 @@ def is_subgroup(left, right): sage: G.is_subgroup(G) True - sage: H = G.subgroup([G.1]) # needs sage.libs.gap - sage: H.is_subgroup(G) # needs sage.libs.gap + sage: H = G.subgroup([G.1]) # needs sage.libs.gap # optional - gap_package_polycyclic + sage: H.is_subgroup(G) # needs sage.libs.gap # optional - gap_package_polycyclic True sage: G. = AbelianGroup(2) @@ -1206,7 +1206,7 @@ def subgroup(self, gensH, names="f"): EXAMPLES:: - sage: # needs sage.libs.gap + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup(3, [2,3,4]); G Multiplicative Abelian group isomorphic to C2 x C3 x C4 sage: H = G.subgroup([a*b,a]); H @@ -1361,7 +1361,7 @@ def number_of_subgroups(self, order=None): 0 sage: AbelianGroup([1,3,1]).number_of_subgroups(order=2) 0 - sage: AbelianGroup([1,3,0,1]).number_of_subgroups(order=3) # needs sage.libs.gap + sage: AbelianGroup([1,3,0,1]).number_of_subgroups(order=3) # needs sage.libs.gap # optional - gap_package_polycyclic 1 sage: AbelianGroup([1,3,1]).number_of_subgroups(order=-2) Traceback (most recent call last): @@ -1451,12 +1451,12 @@ def subgroups(self, check=False): EXAMPLES:: - sage: AbelianGroup([2,3]).subgroups() # needs sage.libs.gap + sage: AbelianGroup([2,3]).subgroups() # needs sage.libs.gap # optional - gap_package_polycyclic [Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0*f1^2}, Multiplicative Abelian subgroup isomorphic to C2 generated by {f0}, Multiplicative Abelian subgroup isomorphic to C3 generated by {f1}, Trivial Abelian subgroup] - sage: len(AbelianGroup([2,4,8]).subgroups()) # needs sage.libs.gap + sage: len(AbelianGroup([2,4,8]).subgroups()) # needs sage.libs.gap # optional - gap_package_polycyclic 81 TESTS:: @@ -1467,10 +1467,10 @@ def subgroups(self, check=False): Check that :trac:`14196` is fixed:: sage: B = AbelianGroup([1,2]) - sage: B.subgroups() # needs sage.libs.gap + sage: B.subgroups() # needs sage.libs.gap # optional - gap_package_polycyclic [Multiplicative Abelian subgroup isomorphic to C2 generated by {f1}, Trivial Abelian subgroup] - sage: B.subgroups(check=True) # needs sage.libs.gap + sage: B.subgroups(check=True) # needs sage.libs.gap # optional - gap_package_polycyclic [Multiplicative Abelian subgroup isomorphic to C2 generated by {f1}, Trivial Abelian subgroup] """ @@ -1534,10 +1534,10 @@ def subgroup_reduced(self, elts, verbose=False): EXAMPLES:: sage: G = AbelianGroup([4,4]) - sage: G.subgroup( [ G([1,0]), G([1,2]) ]) # needs sage.libs.gap + sage: G.subgroup( [ G([1,0]), G([1,2]) ]) # needs sage.libs.gap # optional - gap_package_polycyclic Multiplicative Abelian subgroup isomorphic to C2 x C4 generated by {f0, f0*f1^2} - sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ]) # needs sage.libs.gap + sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ]) # needs sage.libs.gap # optional - gap_package_polycyclic Multiplicative Abelian subgroup isomorphic to C2 x C4 generated by {f0^2*f1^2, f0^3} """ @@ -1568,7 +1568,7 @@ def torsion_subgroup(self, n=None): EXAMPLES:: - sage: # needs sage.libs.gap + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G = AbelianGroup([2, 3]) sage: G.torsion_subgroup() Multiplicative Abelian subgroup isomorphic to C2 x C3 generated @@ -1587,7 +1587,7 @@ def torsion_subgroup(self, n=None): :: sage: G = AbelianGroup([2, 2*3, 2*3*5, 0, 2*3*5*7, 2*3*5*7*11]) - sage: G.torsion_subgroup(5) # needs sage.libs.gap + sage: G.torsion_subgroup(5) # needs sage.libs.gap # optional - gap_package_polycyclic Multiplicative Abelian subgroup isomorphic to C5 x C5 x C5 generated by {f2^6, f4^42, f5^462} """ if n is None: @@ -1620,7 +1620,7 @@ def __init__(self, ambient, gens, names="f", category=None): """ EXAMPLES:: - sage: # needs sage.libs.gap + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: F = AbelianGroup(5, [30,64,729], names=list("abcde")) sage: a,b,c,d,e = F.gens() sage: F.subgroup([a^3,b]) @@ -1675,23 +1675,23 @@ def __init__(self, ambient, gens, names="f", category=None): Infinite groups can also be handled:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G = AbelianGroup([3,4,0], names="abc") sage: a,b,c = G.gens() - sage: F = G.subgroup([a, b^2, c]); F # needs sage.libs.gap + sage: F = G.subgroup([a, b^2, c]); F Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z generated by {a, b^2, c} - - sage: F.gens_orders() # needs sage.libs.gap + sage: F.gens_orders() (2, 3, 0) - sage: F.gens() # needs sage.libs.gap + sage: F.gens() (a, b^2, c) - sage: F.order() # needs sage.libs.gap + sage: F.order() +Infinity Testing issue :trac:`18863`:: sage: G = AbelianGroup(5,[2]) - sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) # needs sage.libs.gap + sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) # needs sage.libs.gap # optional - gap_package_polycyclic Multiplicative Abelian subgroup isomorphic to Z generated by {f0*f1^-2*f2^3*f3^-4*f4} """ @@ -1725,35 +1725,38 @@ def __contains__(self, x): EXAMPLES:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup(2) - sage: A = G.subgroup([a]) # needs sage.libs.gap + sage: A = G.subgroup([a]) sage: a in G True - sage: a in A # needs sage.libs.gap + sage: a in A True TESTS: Check that :trac:`32910` is fixed:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup(2, [4, 576]) sage: Hgens = [a^2, a*b^2] - sage: H = G.subgroup(Hgens) # needs sage.libs.gap - sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")] # needs sage.libs.gap + sage: H = G.subgroup(Hgens) + sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")] [False, False, False, True, False] Check that :trac:`31507` is fixed:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G = AbelianGroup(2, gens_orders=[16, 16]) sage: f0, f1 = G.gens() - sage: H = G.subgroup([f0*f1^3]) # needs sage.libs.gap - sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)] # needs sage.libs.gap + sage: H = G.subgroup([f0*f1^3]) + sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)] [False, False, True, False] sage: G. = AbelianGroup(2) sage: Hgens = [a*b, a*b^-1] - sage: H = G.subgroup(Hgens) # needs sage.libs.gap - sage: b^2 in H # needs sage.libs.gap + sage: H = G.subgroup(Hgens) # optional - gap_package_polycyclic + sage: b^2 in H # optional - gap_package_polycyclic True """ if not isinstance(x, AbelianGroupElement): @@ -1781,9 +1784,10 @@ def ambient_group(self): EXAMPLES:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup([2,3,4]) - sage: H = G.subgroup([a, b^2]) # needs sage.libs.gap - sage: H.ambient_group() is G # needs sage.libs.gap + sage: H = G.subgroup([a, b^2]) + sage: H.ambient_group() is G True """ return self._ambient_group @@ -1805,19 +1809,17 @@ def equals(left, right): EXAMPLES:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G = AbelianGroup(3, [2,3,4], names="abc"); G Multiplicative Abelian group isomorphic to C2 x C3 x C4 sage: a,b,c = G.gens() - sage: F = G.subgroup([a,b^2]); F # needs sage.libs.gap + sage: F = G.subgroup([a,b^2]); F Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2} - sage: F = AbelianGroup(2) sage: A = G.subgroup([a]) sage: B = G.subgroup([b]) @@ -1849,8 +1851,8 @@ def _repr_(self): sage: G. = AbelianGroup(2) sage: G._repr_() 'Multiplicative Abelian group isomorphic to Z x Z' - sage: A = G.subgroup([a]) # needs sage.libs.gap - sage: A._repr_() # needs sage.libs.gap + sage: A = G.subgroup([a]) # needs sage.libs.gap # optional - gap_package_polycyclic + sage: A._repr_() # needs sage.libs.gap # optional - gap_package_polycyclic 'Multiplicative Abelian subgroup isomorphic to Z generated by {a}' """ eldv = self._abinvs @@ -1872,11 +1874,12 @@ def gens(self): EXAMPLES:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup(2) - sage: A = G.subgroup([a]) # needs sage.libs.gap + sage: A = G.subgroup([a]) sage: G.gens() (a, b) - sage: A.gens() # needs sage.libs.gap + sage: A.gens() (a,) """ return self._gens @@ -1887,9 +1890,10 @@ def gen(self, n): EXAMPLES:: + sage: # needs sage.libs.gap # optional - gap_package_polycyclic sage: G. = AbelianGroup(2) - sage: A = G.subgroup([a]) # needs sage.libs.gap - sage: A.gen(0) # needs sage.libs.gap + sage: A = G.subgroup([a]) + sage: A.gen(0) a """ return self._gens[n] diff --git a/src/sage/groups/abelian_gps/abelian_group_morphism.py b/src/sage/groups/abelian_gps/abelian_group_morphism.py index e56b1c68afd..e4b7b82b42b 100644 --- a/src/sage/groups/abelian_gps/abelian_group_morphism.py +++ b/src/sage/groups/abelian_gps/abelian_group_morphism.py @@ -59,14 +59,14 @@ class AbelianGroupMorphism(Morphism): sage: x,y = H.gens() sage: from sage.groups.abelian_gps.abelian_group_morphism import AbelianGroupMorphism - sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) + sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) # optional - gap_package_polycyclic TESTS:: sage: G. = AbelianGroup(2,[2,3]) sage: H. = AbelianGroup(3,[2,3,4]) - sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) - sage: Hom(G,H) == phi.parent() + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic + sage: Hom(G,H) == phi.parent() # optional - gap_package_polycyclic True AUTHORS: @@ -126,11 +126,11 @@ def _libgap_(self): sage: H = AbelianGroup(2,[2,3],names="xy"); H Multiplicative Abelian group isomorphic to C2 x C3 sage: x,y = H.gens() - sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) - sage: libgap(phi) + sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) # optional - gap_package_polycyclic + sage: libgap(phi) # optional - gap_package_polycyclic [ f1, f2 ] -> [ f1, f2 ] - sage: phi = AbelianGroupMorphism(H,G,[x,y],[a*c**2,b]) - sage: libgap(phi) + sage: phi = AbelianGroupMorphism(H,G,[x,y],[a*c**2,b]) # optional - gap_package_polycyclic + sage: libgap(phi) # optional - gap_package_polycyclic [ f1, f2 ] -> [ f1*f4, f2 ] """ G = libgap(self.domain()) @@ -158,16 +158,16 @@ def kernel(self): sage: G = AbelianGroup(2,[2,3],names="xy"); G Multiplicative Abelian group isomorphic to C2 x C3 sage: x,y = G.gens() - sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) - sage: phi.kernel() + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic + sage: phi.kernel() # optional - gap_package_polycyclic Group([ ]) sage: H = AbelianGroup(3,[2,2,2],names="abc") sage: a,b,c = H.gens() sage: G = AbelianGroup(2,[2,2],names="x") sage: x,y = G.gens() - sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,a]) - sage: phi.kernel() + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,a]) # optional - gap_package_polycyclic + sage: phi.kernel() # optional - gap_package_polycyclic Group([ f1*f2 ]) """ return libgap(self).Kernel() @@ -186,11 +186,11 @@ def image(self, S): sage: G = AbelianGroup(2,[2,3],names="xy") sage: x,y = G.gens() - sage: subG = G.subgroup([x]) + sage: subG = G.subgroup([x]) # optional - gap_package_polycyclic sage: H = AbelianGroup(3,[2,3,4],names="abc") sage: a,b,c = H.gens() - sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) - sage: phi.image(subG) + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic + sage: phi.image(subG) # optional - gap_package_polycyclic Multiplicative Abelian subgroup isomorphic to C2 generated by {a} """ return self.codomain().subgroup([self(g) for g in S.gens()]) @@ -206,10 +206,10 @@ def _call_(self, g): sage: a,b,c = H.gens() sage: G = AbelianGroup(2, [2,3], names="xy") sage: x,y = G.gens() - sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) - sage: phi(y*x) + sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic + sage: phi(y*x) # optional - gap_package_polycyclic a*b - sage: phi(y^2) + sage: phi(y^2) # optional - gap_package_polycyclic b^2 """ # g.word_problem is faster in general than word_problem(g) diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 85318aa6f83..c6012254630 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.number_field r""" Dual groups of Finite Multiplicative Abelian Groups @@ -25,7 +26,6 @@ sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde') sage: (a, b, c, d, e) = F.gens() - sage: # needs sage.rings.number_field sage: Fd = F.dual_group(names='ABCDE') sage: Fd.base_ring() Cyclotomic Field of order 2520 and degree 576 @@ -82,7 +82,6 @@ def is_DualAbelianGroup(x): EXAMPLES:: - sage: # needs sage.rings.number_field sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroup sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde")) sage: Fd = F.dual_group() @@ -105,7 +104,7 @@ class DualAbelianGroup_class(UniqueRepresentation, AbelianGroupBase): EXAMPLES:: sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde") - sage: F.dual_group() # needs sage.rings.number_field + sage: F.dual_group() Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 2520 and degree 576 @@ -123,7 +122,7 @@ def __init__(self, G, names, base_ring): EXAMPLES:: sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde") - sage: F.dual_group() # needs sage.rings.number_field + sage: F.dual_group() Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 2520 and degree 576 """ @@ -180,9 +179,9 @@ def _repr_(self): EXAMPLES:: sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde') - sage: Fd = F.dual_group(names='ABCDE', # needs sage.rings.number_field + sage: Fd = F.dual_group(names='ABCDE', ....: base_ring=CyclotomicField(2*5*7*8*9)) - sage: Fd # indirect doctest # needs sage.rings.number_field + sage: Fd # indirect doctest Dual of Abelian Group isomorphic to Z/2Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 5040 and degree 1152 sage: Fd = F.dual_group(names='ABCDE', base_ring=CC) # needs sage.rings.real_mpfr @@ -209,8 +208,8 @@ def _latex_(self): EXAMPLES:: sage: F = AbelianGroup(3, [2]*3) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: Fd._latex_() # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: Fd._latex_() '$\\mathrm{DualAbelianGroup}( AbelianGroup ( 3, (2, 2, 2) ) )$' """ return r"$\mathrm{DualAbelianGroup}( AbelianGroup ( %s, %s ) )$" % (self.ngens(), self.gens_orders()) @@ -251,7 +250,6 @@ def gen(self, i=0): EXAMPLES:: - sage: # needs sage.rings.number_field sage: F = AbelianGroup(3, [1,2,3], names='a') sage: Fd = F.dual_group(names="A") sage: Fd.0 @@ -279,8 +277,8 @@ def gens(self): EXAMPLES:: - sage: F = AbelianGroup([7,11]).dual_group() # needs sage.rings.number_field - sage: F.gens() # needs sage.rings.number_field + sage: F = AbelianGroup([7,11]).dual_group() + sage: F.gens() (X0, X1) """ n = self.group().ngens() @@ -293,8 +291,8 @@ def ngens(self): EXAMPLES:: sage: F = AbelianGroup([7]*100) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: Fd.ngens() # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: Fd.ngens() 100 """ return self.group().ngens() @@ -310,8 +308,8 @@ def gens_orders(self): EXAMPLES:: sage: F = AbelianGroup([5]*1000) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: invs = Fd.gens_orders(); len(invs) 1000 """ return self.group().gens_orders() @@ -325,8 +323,8 @@ def invariants(self): EXAMPLES:: sage: F = AbelianGroup([5]*1000) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: invs = Fd.gens_orders(); len(invs) 1000 """ # TODO: deprecate @@ -340,9 +338,9 @@ def __contains__(self, X): sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde") sage: a,b,c,d,e = F.gens() - sage: Fd = F.dual_group(names="ABCDE") # needs sage.rings.number_field - sage: A,B,C,D,E = Fd.gens() # needs sage.rings.number_field - sage: A*B^2*D^7 in Fd # needs sage.rings.number_field + sage: Fd = F.dual_group(names="ABCDE") + sage: A,B,C,D,E = Fd.gens() + sage: A*B^2*D^7 in Fd True """ return X.parent() == self and is_DualAbelianGroupElement(X) @@ -354,8 +352,8 @@ def order(self): EXAMPLES:: sage: G = AbelianGroup([2,3,9]) - sage: Gd = G.dual_group() # needs sage.rings.number_field - sage: Gd.order() # needs sage.rings.number_field + sage: Gd = G.dual_group() + sage: Gd.order() 54 """ G = self.group() @@ -368,10 +366,10 @@ def is_commutative(self): EXAMPLES:: sage: G = AbelianGroup([2,3,9]) - sage: Gd = G.dual_group() # needs sage.rings.number_field - sage: Gd.is_commutative() # needs sage.rings.number_field + sage: Gd = G.dual_group() + sage: Gd.is_commutative() True - sage: Gd.is_abelian() # needs sage.rings.number_field + sage: Gd.is_abelian() True """ return True @@ -384,8 +382,8 @@ def list(self): EXAMPLES:: sage: G = AbelianGroup([2,3], names="ab") - sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field - sage: Gd.list() # needs sage.rings.number_field + sage: Gd = G.dual_group(names="AB") + sage: Gd.list() (1, B, B^2, A, A*B, A*B^2) """ if not self.is_finite(): @@ -400,8 +398,8 @@ def __iter__(self): EXAMPLES:: sage: G = AbelianGroup([2,3], names="ab") - sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field - sage: [X for X in Gd] # needs sage.rings.number_field + sage: Gd = G.dual_group(names="AB") + sage: [X for X in Gd] [1, B, B^2, A, A*B, A*B^2] sage: # needs sage.rings.real_mpfr diff --git a/src/sage/groups/abelian_gps/dual_abelian_group_element.py b/src/sage/groups/abelian_gps/dual_abelian_group_element.py index 407323d4f34..6fdb8a68c4e 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group_element.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group_element.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.number_field """ Elements (characters) of the dual group of a finite Abelian group @@ -8,13 +9,12 @@ sage: F Multiplicative Abelian group isomorphic to C2 x C3 x C5 x C7 x C8 - sage: Fd = F.dual_group(names="ABCDE"); Fd # needs sage.rings.number_field + sage: Fd = F.dual_group(names="ABCDE"); Fd Dual of Abelian Group isomorphic to Z/2Z x Z/3Z x Z/5Z x Z/7Z x Z/8Z over Cyclotomic Field of order 840 and degree 192 The elements of the dual group can be evaluated on elements of the original group:: - sage: # needs sage.rings.number_field sage: a,b,c,d,e = F.gens() sage: A,B,C,D,E = Fd.gens() sage: A*B^2*D^7 @@ -71,10 +71,10 @@ def is_DualAbelianGroupElement(x) -> bool: EXAMPLES:: sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroupElement - sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group() # needs sage.rings.number_field - sage: is_DualAbelianGroupElement(F) # needs sage.rings.number_field + sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group() + sage: is_DualAbelianGroupElement(F) False - sage: is_DualAbelianGroupElement(F.an_element()) # needs sage.rings.number_field + sage: is_DualAbelianGroupElement(F.an_element()) True """ return isinstance(x, DualAbelianGroupElement) @@ -96,7 +96,6 @@ def __call__(self, g): EXAMPLES:: - sage: # needs sage.rings.number_field sage: F = AbelianGroup(5, [2,3,5,7,8], names="abcde") sage: a,b,c,d,e = F.gens() sage: Fd = F.dual_group(names="ABCDE") @@ -147,7 +146,6 @@ def word_problem(self, words): EXAMPLES:: - sage: # needs sage.rings.number_field sage: G = AbelianGroup(5,[3, 5, 5, 7, 8], names="abcde") sage: Gd = G.dual_group(names="abcde") sage: a,b,c,d,e = Gd.gens() @@ -156,7 +154,7 @@ def word_problem(self, words): sage: w = a^7*b^3*c^5*d^4*e^4 sage: x = a^3*b^2*c^2*d^3*e^5 sage: y = a^2*b^4*c^2*d^4*e^5 - sage: e.word_problem([u,v,w,x,y]) + sage: e.word_problem([u,v,w,x,y]) # needs sage.libs.gap [[b^2*c^2*d^3*e^5, 245]] """ from sage.libs.gap.libgap import libgap diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index bfcdd282df1..7b5e4695fb1 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -204,7 +204,10 @@ def __init__(self, degree, ring): sage: G = AffineGroup(2, GF(5)); G Affine Group of degree 2 over Finite Field of size 5 + + sage: # needs sage.libs.gap (for gens) sage: TestSuite(G).run() + sage: G.category() Category of finite groups @@ -289,8 +292,10 @@ def cardinality(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: AffineGroup(6, GF(5)).cardinality() 172882428468750000000000000000 + sage: AffineGroup(6, ZZ).cardinality() +Infinity """ @@ -464,6 +469,7 @@ def random_element(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4, GF(3)) sage: G.random_element() # random [2 0 1 2] [1] @@ -498,6 +504,7 @@ def some_elements(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4,5) sage: G.some_elements() [ [2 0 0 0] [1] diff --git a/src/sage/groups/affine_gps/euclidean_group.py b/src/sage/groups/affine_gps/euclidean_group.py index 47de04c6544..59fb411b925 100644 --- a/src/sage/groups/affine_gps/euclidean_group.py +++ b/src/sage/groups/affine_gps/euclidean_group.py @@ -146,6 +146,8 @@ class EuclideanGroup(AffineGroup): True sage: G = EuclideanGroup(2, GF(5)); G Euclidean Group of degree 2 over Finite Field of size 5 + + sage: # needs sage.libs.gap (for gens) sage: TestSuite(G).run() REFERENCES: diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 52fbe9365d6..7df4dc8a69a 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -78,11 +78,14 @@ class AffineGroupElement(MultiplicativeGroupElement): EXAMPLES:: sage: G = AffineGroup(2, GF(3)) + + sage: # needs sage.libs.gap sage: g = G.random_element() sage: type(g) sage: G(g.matrix()) == g True + sage: G(2) [2 0] [0] x |-> [0 2] x + [0] @@ -107,6 +110,7 @@ def __init__(self, parent, A, b=0, convert=True, check=True): TESTS:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4, GF(5)) sage: g = G.random_element() sage: TestSuite(g).run() @@ -200,6 +204,7 @@ def matrix(self): Composition of affine group elements equals multiplication of the matrices:: + sage: # needs sage.libs.gap sage: g1 = G.random_element() sage: g2 = G.random_element() sage: g1.matrix() * g2.matrix() == (g1*g2).matrix() diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 18d95ef86f2..bd787d3c153 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -72,7 +72,7 @@ from sage.combinat.permutation import Permutation from sage.combinat.permutation import Permutations from sage.combinat.subset import Subsets -from sage.features import PythonModule +from sage.features.sagemath import sage__libs__braiding from sage.groups.artin import FiniteTypeArtinGroup, FiniteTypeArtinGroupElement from sage.groups.finitely_presented import FinitelyPresentedGroup from sage.groups.finitely_presented import GroupMorphismWithGensImages @@ -80,7 +80,6 @@ from sage.functions.generalized import sign from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroupElement -from sage.knots.knot import Knot from sage.libs.gap.libgap import libgap from sage.matrix.constructor import identity_matrix, matrix from sage.misc.lazy_attribute import lazy_attribute @@ -98,7 +97,8 @@ ['leftnormalform', 'rightnormalform', 'centralizer', 'supersummitset', 'greatestcommondivisor', 'leastcommonmultiple', 'conjugatingbraid', 'ultrasummitset', 'thurston_type', 'rigidity', 'sliding_circuits'], - feature=PythonModule('sage.libs.braiding', spkg='libbraiding', type='standard')) + feature=sage__libs__braiding()) +lazy_import('sage.knots.knot', 'Knot') class Braid(FiniteTypeArtinGroupElement): diff --git a/src/sage/groups/galois_group.py b/src/sage/groups/galois_group.py index 5a48380eb21..a7439d18ab3 100644 --- a/src/sage/groups/galois_group.py +++ b/src/sage/groups/galois_group.py @@ -10,15 +10,17 @@ - David Roe (2019): initial version """ -from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup from sage.groups.abelian_gps.abelian_group import AbelianGroup_class, AbelianGroup_subgroup -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method -from sage.structure.category_object import normalize_names +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.lazy_import import lazy_import from sage.rings.integer_ring import ZZ +lazy_import('sage.groups.galois_group_perm', ['GaloisGroup_perm', 'GaloisSubgroup_perm']) +lazy_import('sage.groups.perm_gps.permgroup', 'PermutationGroup') + + def _alg_key(self, algorithm=None, recompute=False): r""" Return a key for use in cached_method calls. @@ -40,6 +42,7 @@ def _alg_key(self, algorithm=None, recompute=False): algorithm = self._get_algorithm(algorithm) return algorithm + class _GMixin: r""" This class provides some methods for Galois groups to be used for both permutation groups @@ -151,6 +154,7 @@ def _gc_map(self): """ return self._gcdata[1] + class _GaloisMixin(_GMixin): """ This class provides methods for Galois groups, allowing concrete instances @@ -275,6 +279,7 @@ def is_galois(self): """ return self.order() == self._field_degree + class _SubGaloisMixin(_GMixin): """ This class provides methods for subgroups of Galois groups, allowing concrete instances @@ -339,164 +344,6 @@ def _gcdata(self): """ return self._ambient_group._gcdata -class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic): - r""" - The group of automorphisms of a Galois closure of a given field. - - INPUT: - - - ``field`` -- a field, separable over its base - - - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field - - - ``gc_numbering`` -- boolean, whether to express permutations in terms of the - roots of the defining polynomial of the splitting field (versus the defining polynomial - of the original extension). The default value may vary based on the type of field. - """ - @abstract_method - def transitive_number(self, algorithm=None, recompute=False): - """ - The transitive number (as in the GAP and Magma databases of transitive groups) - for the action on the roots of the defining polynomial of the top field. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field - sage: G = K.galois_group() # needs sage.rings.number_field - sage: G.transitive_number() # needs sage.rings.number_field - 2 - """ - - @lazy_attribute - def _gens(self): - """ - The generators of this Galois group as permutations of the roots. It's important that this - be computed lazily, since it's often possible to compute other attributes (such as the order - or transitive number) more cheaply. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field - sage: G._gens # needs sage.rings.number_field - [(1,2,3,5), (1,4,3,2,5)] - """ - return NotImplemented - - def __init__(self, field, algorithm=None, names=None, gc_numbering=False): - r""" - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field - sage: G = K.galois_group() # needs sage.rings.number_field - sage: TestSuite(G).run() # needs sage.rings.number_field - """ - self._field = field - self._default_algorithm = algorithm - self._base = field.base_field() - self._gc_numbering = gc_numbering - if names is None: - # add a c for Galois closure - names = field.variable_name() + 'c' - self._gc_names = normalize_names(1, names) - # We do only the parts of the initialization of PermutationGroup_generic - # that don't depend on _gens - from sage.categories.permutation_groups import PermutationGroups - category = PermutationGroups().FinitelyGenerated().Finite() - # Note that we DON'T call the __init__ method for PermutationGroup_generic - # Instead, the relevant attributes are computed lazily - super(PermutationGroup_generic, self).__init__(category=category) - - @lazy_attribute - def _deg(self): - r""" - The number of moved points in the permutation representation. - - This will be the degree of the original number field if `_gc_numbering`` - is ``False``, or the degree of the Galois closure otherwise. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) - sage: G = K.galois_group(gc_numbering=False); G - Galois group 5T3 (5:4) with order 20 of x^5 - 2 - sage: G._deg - 5 - sage: G = K.galois_group(gc_numbering=True); G._deg - 20 - """ - if self._gc_numbering: - return self.order() - else: - try: - return self._field.degree() - except NotImplementedError: # relative number fields don't support degree - return self._field.relative_degree() - - @lazy_attribute - def _domain(self): - r""" - The integers labeling the roots on which this Galois group acts. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) - sage: G = K.galois_group(gc_numbering=False); G - Galois group 5T3 (5:4) with order 20 of x^5 - 2 - sage: G._domain - {1, 2, 3, 4, 5} - sage: G = K.galois_group(gc_numbering=True); G._domain - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} - """ - return FiniteEnumeratedSet(range(1, self._deg+1)) - - @lazy_attribute - def _domain_to_gap(self): - r""" - Dictionary implementing the identity (used by PermutationGroup_generic). - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field - sage: G._domain_to_gap[5] # needs sage.rings.number_field - 5 - """ - return {key: i+1 for i, key in enumerate(self._domain)} - - @lazy_attribute - def _domain_from_gap(self): - r""" - Dictionary implementing the identity (used by PermutationGroup_generic). - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field - sage: G._domain_from_gap[20] # needs sage.rings.number_field - 20 - """ - return {i+1: key for i, key in enumerate(self._domain)} - - def ngens(self): - r""" - Number of generators of this Galois group - - EXAMPLES:: - - sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field - 1 - """ - return len(self._gens) class GaloisGroup_ab(_GaloisMixin, AbelianGroup_class): r""" @@ -551,7 +398,7 @@ def permutation_group(self): EXAMPLES:: - sage: GF(3^10).galois_group().permutation_group() # needs sage.rings.finite_rings + sage: GF(3^10).galois_group().permutation_group() # needs sage.libs.gap sage.rings.finite_rings Permutation Group with generators [(1,2,3,4,5,6,7,8,9,10)] """ return PermutationGroup(gap_group=self._gap_().RegularActionHomomorphism().Image()) @@ -568,11 +415,12 @@ def transitive_number(self, algorithm=None, recompute=False): sage: from sage.groups.galois_group import GaloisGroup_ab sage: Gtest = GaloisGroup_ab(field=None, generator_orders=(2,2,4)) - sage: Gtest.transitive_number() + sage: Gtest.transitive_number() # needs sage.libs.gap 2 """ return ZZ(self.permutation_group()._gap_().TransitiveIdentification()) + class GaloisGroup_cyc(GaloisGroup_ab): r""" Cyclic Galois groups @@ -614,17 +462,6 @@ def signature(self): """ return ZZ(1) if (self._field.degree() % 2) else ZZ(-1) -class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin): - """ - Subgroups of Galois groups (implemented as permutation groups), specified - by giving a list of generators. - - Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order - to enable creation without determining a list of generators, - we require that generators for a subgroup be specified during initialization, - as specified in the ``__init__`` method of permutation subgroups. - """ - pass class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin): """ @@ -633,5 +470,4 @@ class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin): pass -GaloisGroup_perm.Subgroup = GaloisSubgroup_perm GaloisGroup_ab.Subgroup = GaloisSubgroup_ab diff --git a/src/sage/groups/galois_group_perm.py b/src/sage/groups/galois_group_perm.py new file mode 100644 index 00000000000..162e5174143 --- /dev/null +++ b/src/sage/groups/galois_group_perm.py @@ -0,0 +1,186 @@ +r""" +Galois groups of field extensions as permutation groups +""" + +from sage.groups.galois_group import _GaloisMixin, _SubGaloisMixin +from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup +from sage.misc.abstract_method import abstract_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.structure.category_object import normalize_names + + +class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic): + r""" + The group of automorphisms of a Galois closure of a given field. + + INPUT: + + - ``field`` -- a field, separable over its base + + - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field + + - ``gc_numbering`` -- boolean, whether to express permutations in terms of the + roots of the defining polynomial of the splitting field (versus the defining polynomial + of the original extension). The default value may vary based on the type of field. + """ + @abstract_method + def transitive_number(self, algorithm=None, recompute=False): + """ + The transitive number (as in the GAP and Magma databases of transitive groups) + for the action on the roots of the defining polynomial of the top field. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field + sage: G = K.galois_group() # needs sage.rings.number_field + sage: G.transitive_number() # needs sage.rings.number_field + 2 + """ + + @lazy_attribute + def _gens(self): + """ + The generators of this Galois group as permutations of the roots. It's important that this + be computed lazily, since it's often possible to compute other attributes (such as the order + or transitive number) more cheaply. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field + sage: G._gens # needs sage.rings.number_field + [(1,2,3,5), (1,4,3,2,5)] + """ + return NotImplemented + + def __init__(self, field, algorithm=None, names=None, gc_numbering=False): + r""" + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field + sage: G = K.galois_group() # needs sage.rings.number_field + sage: TestSuite(G).run() # needs sage.rings.number_field + """ + self._field = field + self._default_algorithm = algorithm + self._base = field.base_field() + self._gc_numbering = gc_numbering + if names is None: + # add a c for Galois closure + names = field.variable_name() + 'c' + self._gc_names = normalize_names(1, names) + # We do only the parts of the initialization of PermutationGroup_generic + # that don't depend on _gens + from sage.categories.permutation_groups import PermutationGroups + category = PermutationGroups().FinitelyGenerated().Finite() + # Note that we DON'T call the __init__ method for PermutationGroup_generic + # Instead, the relevant attributes are computed lazily + super(PermutationGroup_generic, self).__init__(category=category) + + @lazy_attribute + def _deg(self): + r""" + The number of moved points in the permutation representation. + + This will be the degree of the original number field if `_gc_numbering`` + is ``False``, or the degree of the Galois closure otherwise. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._deg + 5 + sage: G = K.galois_group(gc_numbering=True); G._deg + 20 + """ + if self._gc_numbering: + return self.order() + else: + try: + return self._field.degree() + except NotImplementedError: # relative number fields don't support degree + return self._field.relative_degree() + + @lazy_attribute + def _domain(self): + r""" + The integers labeling the roots on which this Galois group acts. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._domain + {1, 2, 3, 4, 5} + sage: G = K.galois_group(gc_numbering=True); G._domain + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + """ + return FiniteEnumeratedSet(range(1, self._deg+1)) + + @lazy_attribute + def _domain_to_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field + sage: G._domain_to_gap[5] # needs sage.rings.number_field + 5 + """ + return {key: i+1 for i, key in enumerate(self._domain)} + + @lazy_attribute + def _domain_from_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field + sage: G._domain_from_gap[20] # needs sage.rings.number_field + 20 + """ + return {i+1: key for i, key in enumerate(self._domain)} + + def ngens(self): + r""" + Number of generators of this Galois group + + EXAMPLES:: + + sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field + 1 + """ + return len(self._gens) + + +class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin): + """ + Subgroups of Galois groups (implemented as permutation groups), specified + by giving a list of generators. + + Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order + to enable creation without determining a list of generators, + we require that generators for a subgroup be specified during initialization, + as specified in the ``__init__`` method of permutation subgroups. + """ + pass + + +GaloisGroup_perm.Subgroup = GaloisSubgroup_perm diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index 69bffca1b9f..9ef099fa93e 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -117,10 +117,12 @@ from copy import copy +from sage.arith.misc import integer_ceil, integer_floor, xlcm +from sage.arith.srange import xsrange +from sage.misc.functional import log from sage.misc.misc_c import prod import sage.rings.integer_ring as integer_ring import sage.rings.integer -from sage.arith.srange import xsrange # # Lists of names (as strings) which the user may use to identify one @@ -550,10 +552,10 @@ def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=No It also works with matrices:: - sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.rings.finite_rings + sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.modules sage.rings.finite_rings ....: [14601, 41019, 30188], ....: [3081, 736, 27092]]) - sage: discrete_log_rho(A^1234567, A) # needs sage.rings.finite_rings + sage: discrete_log_rho(A^1234567, A) # needs sage.modules sage.rings.finite_rings 1234567 Beware, the order must be prime:: @@ -1234,7 +1236,7 @@ def order_from_multiple(P, m, plist=None, factorization=None, check=True, sage: order_from_multiple(w, 230, operation='*') 23 - sage: # needs sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings sage: F = GF(2^1279,'a') sage: n = F.cardinality() - 1 # Mersenne prime sage: order_from_multiple(F.random_element(), n, @@ -1396,8 +1398,7 @@ def order_from_bounds(P, bounds, d=None, operation='+', if d > 1: Q = multiple(P, d, operation=operation) lb, ub = bounds - bounds = (sage.arith.all.integer_ceil(lb / d), - sage.arith.all.integer_floor(ub / d)) + bounds = (integer_ceil(lb / d), integer_floor(ub / d)) # Use generic bsgs to find n=d*m with lb<=n<=ub and n*P=0 @@ -1486,7 +1487,7 @@ def merge_points(P1, P2, operation='+', if n2.divides(n1): return (g1, n1) - m, k1, k2 = sage.arith.all.xlcm(n1, n2) + m, k1, k2 = xlcm(n1, n2) m1 = n1 // k1 m2 = n2 // k2 g1 = multiple(g1, m1, operation=operation) diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index cd2521e3478..ef639a0c367 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -35,9 +35,9 @@ def is_Group(x): EXAMPLES:: - sage: F. = FreeGroup() # needs sage.combinat + sage: F. = FreeGroup() # needs sage.groups sage: from sage.groups.group import is_Group - sage: is_Group(F) # needs sage.combinat + sage: is_Group(F) # needs sage.groups True sage: is_Group("a string") False @@ -140,7 +140,7 @@ cdef class Group(Parent): EXAMPLES:: - sage: SL(2, 7).is_commutative() # needs sage.modules sage.rings.finite_rings + sage: SL(2, 7).is_commutative() # needs sage.libs.gap sage.modules sage.rings.finite_rings False """ return self.is_abelian() @@ -186,6 +186,39 @@ cdef class Group(Parent): """ return self.order() != infinity + def is_trivial(self): + r""" + Return ``True`` if this group is the trivial group. + + A group is trivial, if it consists only of the identity + element. + + .. WARNING:: + + It is in principle undecidable whether a group is + trivial, for example, if the group is given by a finite + presentation. Thus, this method may not terminate. + + EXAMPLES:: + + sage: groups.presentation.Cyclic(1).is_trivial() + True + + sage: G. = FreeGroup('a, b') + sage: H = G / (a^2, b^3, a*b*~a*~b) + sage: H.is_trivial() + False + + A non-trivial presentation of the trivial group:: + + sage: F. = FreeGroup() + sage: J = F / ((~a)*b*a*(~b)^2, (~b)*a*b*(~a)^2) + sage: J.is_trivial() + True + """ + return self.order() == 1 + + def is_multiplicative(self): r""" Returns True if the group operation is given by \* (rather than @@ -212,8 +245,8 @@ cdef class Group(Parent): EXAMPLES:: - sage: G = AbelianGroup([2,3,4,5]) # needs sage.groups - sage: G.an_element() # needs sage.groups + sage: G = AbelianGroup([2,3,4,5]) # needs sage.modules + sage: G.an_element() # needs sage.modules f0*f1*f2*f3 """ return self.prod(self.gens()) diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py index 6b865805298..d1c647f1286 100644 --- a/src/sage/groups/matrix_gps/coxeter_group.py +++ b/src/sage/groups/matrix_gps/coxeter_group.py @@ -79,7 +79,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene We can create Coxeter groups from Coxeter matrices:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]]); W Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [ 1 6 3] @@ -150,7 +150,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene graphs, we can input a Coxeter graph. Following the standard convention, edges with no label (i.e. labelled by ``None``) are treated as 3:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)]) sage: W = CoxeterGroup(G); W Coxeter group over Universal Cyclotomic Field with Coxeter matrix: @@ -165,7 +165,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels of `\infty` are given by `-1` in the Coxeter matrix:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)]) sage: W = CoxeterGroup(G) sage: W.coxeter_matrix() @@ -183,7 +183,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene [2 3 1 3 3] [2 2 3 1 2] [2 2 3 2 1] - sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.rings.number_field + sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.libs.gap sage.rings.number_field Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: @@ -240,15 +240,15 @@ def __init__(self, coxeter_matrix, base_ring, index_set): EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) - sage: TestSuite(W).run() # long time + sage: TestSuite(W).run() # long time - sage: # needs sage.rings.number_field + sage: # long time, needs sage.rings.number_field sage.symbolic sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) - sage: TestSuite(W).run() # long time + sage: TestSuite(W).run() sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) - sage: TestSuite(W).run(max_runs=30) # long time + sage: TestSuite(W).run(max_runs=30) sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) - sage: TestSuite(W).run(max_runs=30) # long time + sage: TestSuite(W).run(max_runs=30) We check that :trac:`16630` is fixed:: @@ -340,7 +340,7 @@ def _repr_(self): EXAMPLES:: - sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.rings.number_field + sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.libs.gap sage.rings.number_field Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] @@ -383,8 +383,8 @@ def coxeter_matrix(self): sage: W.coxeter_matrix() [1 3] [3 1] - sage: W = CoxeterGroup(['H',3]) # needs sage.rings.number_field - sage: W.coxeter_matrix() + sage: W = CoxeterGroup(['H',3]) # needs sage.libs.gap sage.rings.number_field + sage: W.coxeter_matrix() # needs sage.libs.gap sage.rings.number_field [1 3 2] [3 1 5] [2 5 1] @@ -423,7 +423,7 @@ def is_finite(self): EXAMPLES:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: [l for l in range(2, 9) if ....: CoxeterGroup([[1,3,2],[3,1,l],[2,l,1]]).is_finite()] [2, 3, 4, 5] @@ -482,8 +482,8 @@ def order(self): sage: W = CoxeterGroup([[1,3],[3,1]]) sage: W.order() 6 - sage: W = CoxeterGroup([[1,-1],[-1,1]]) - sage: W.order() + sage: W = CoxeterGroup([[1,-1],[-1,1]]) # needs sage.libs.gap + sage: W.order() # needs sage.libs.gap +Infinity """ if self.is_finite(): @@ -593,7 +593,7 @@ def positive_roots(self): sage: W.positive_roots() ((1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1), (0, 1, 1), (0, 0, 1)) - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup(['I',5], implementation='reflection') sage: W.positive_roots() ((1, 0), @@ -651,7 +651,7 @@ def roots(self): (0, -1, -1), (0, 0, -1)) - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup(['I',5], implementation='reflection') sage: len(W.roots()) 10 diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index c8098ad578a..ef6ba42a105 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -9,7 +9,7 @@ sage: F = GF(3) sage: gens = [matrix(F, 2, [1,0, -1,1]), matrix(F, 2, [1,1,0,1])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_classes_representatives() + sage: G.conjugacy_classes_representatives() # needs sage.libs.gap ( [1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2] [0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0] @@ -161,6 +161,8 @@ def QuaternionMatrixGroupGF3(): is the product of `I` and `J`. :: sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3 + + sage: # needs sage.libs.gap sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 @@ -176,22 +178,23 @@ def QuaternionMatrixGroupGF3(): TESTS:: - sage: groups.matrix.QuaternionGF3() # needs sage.modules sage.rings.finite_rings + sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators ( [1 1] [2 1] [1 2], [1 1] ) + sage: # needs sage.groups sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True - sage: H = DihedralGroup(4) # needs sage.groups - sage: H.order() # needs sage.groups + sage: H = DihedralGroup(4) + sage: H.order() 8 - sage: QP.is_abelian(), H.is_abelian() # needs sage.groups + sage: QP.is_abelian(), H.is_abelian() (False, False) - sage: QP.is_isomorphic(H) # needs sage.groups + sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -340,6 +343,7 @@ class FinitelyGeneratedMatrixGroup_generic(MatrixGroup_generic): sage: MatrixGroup(m1, m2) == MatrixGroup(m2, m1) False + sage: # needs sage.libs.gap sage: G = GL(2, GF(3)) sage: H = G.as_matrix_group() sage: H == G, G == H @@ -415,6 +419,7 @@ def gen(self, i): EXAMPLES:: + sage: # needs sage.libs.gap sage: H = GL(2, GF(3)) sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]]) sage: G = H.subgroup([h1, h2]) @@ -436,6 +441,7 @@ def ngens(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: H = GL(2, GF(3)) sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]]) sage: G = H.subgroup([h1, h2]) diff --git a/src/sage/groups/matrix_gps/finitely_generated_gap.py b/src/sage/groups/matrix_gps/finitely_generated_gap.py index af676af3673..add6875a802 100644 --- a/src/sage/groups/matrix_gps/finitely_generated_gap.py +++ b/src/sage/groups/matrix_gps/finitely_generated_gap.py @@ -120,16 +120,14 @@ def as_permutation_group(self, algorithm=None, seed=None): 21499084800 sage: P = G.as_permutation_group() sage: Psmaller = G.as_permutation_group(algorithm="smaller", seed=6) - sage: P == Psmaller - False sage: P.cardinality() 21499084800 sage: P.degree() 144 sage: Psmaller.cardinality() 21499084800 - sage: Psmaller.degree() # random - 80 + sage: Psmaller.degree() <= P.degree() + True .. NOTE:: diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 65a8c13645c..822bd576afd 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -16,6 +16,8 @@ Special Linear Group of degree 2 over Integer Ring sage: G = SL(2, GF(3)); G Special Linear Group of degree 2 over Finite Field of size 3 + + sage: # needs sage.libs.gap sage: G.is_finite() True sage: G.conjugacy_classes_representatives() @@ -94,32 +96,38 @@ def GL(n, R, var='a'): EXAMPLES:: sage: G = GL(6, GF(5)) - sage: G.order() - 11064475422000000000000000 sage: G.base_ring() Finite Field of size 5 sage: G.category() Category of finite groups + + sage: # needs sage.libs.gap + sage: G.order() + 11064475422000000000000000 sage: TestSuite(G).run() sage: G = GL(6, QQ) sage: G.category() Category of infinite groups + + sage: # needs sage.libs.gap sage: TestSuite(G).run() Here is the Cayley graph of (relatively small) finite General Linear Group:: + sage: # needs sage.graphs sage.libs.gap sage: g = GL(2,3) - sage: d = g.cayley_graph(); d # needs sage.graphs + sage: d = g.cayley_graph(); d Digraph on 48 vertices - sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.graphs sage.plot + sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.plot ....: vertex_labels=False) Graphics object consisting of 144 graphics primitives - sage: d.plot3d(color_by_label=True) # long time # needs sage.graphs sage.plot + sage: d.plot3d(color_by_label=True) # long time # needs sage.plot Graphics3d Object :: + sage: # needs sage.libs.gap sage: F = GF(3); MS = MatrixSpace(F, 2, 2) sage: gens = [MS([[2,0], [0,1]]), MS([[2,1], [2,0]])] sage: G = MatrixGroup(gens) @@ -213,10 +221,13 @@ def SL(n, R, var='a'): Special Linear Group of degree 15 over Finite Field of size 7 sage: G.category() Category of finite groups + + sage: # needs sage.libs.gap sage: G.order() 1956712595698146962015219062429586341124018007182049478916067369638713066737882363393519966343657677430907011270206265834819092046250232049187967718149558134226774650845658791865745408000000 sage: len(G.gens()) 2 + sage: G = SL(2, ZZ); G Special Linear Group of degree 2 over Integer Ring sage: G.category() @@ -231,6 +242,8 @@ def SL(n, R, var='a'): sage: G = SL(3, ZZ); G Special Linear Group of degree 3 over Integer Ring + + sage: # needs sage.libs.gap sage: G.gens() ( [0 1 0] [ 0 1 0] [1 1 0] diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index c0c22456320..192c4e4d4cf 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -5,10 +5,10 @@ Loading, saving, ... works:: + sage: # needs sage.libs.gap sage: G = GL(2,5); G General Linear Group of degree 2 over Finite Field of size 5 sage: TestSuite(G).run() - sage: g = G.1; g [4 1] [4 0] @@ -159,7 +159,7 @@ def as_matrix_group(self): EXAMPLES:: sage: G = SU(4, GF(5)) # needs sage.rings.finite_rings - sage: G.as_matrix_group() # needs sage.rings.finite_rings + sage: G.as_matrix_group() # needs sage.libs.gap sage.rings.finite_rings Matrix group over Finite Field in a of size 5^2 with 2 generators ( [ a 0 0 0] [ 1 0 4*a + 3 0] [ 0 2*a + 3 0 0] [ 1 0 0 0] @@ -167,7 +167,8 @@ def as_matrix_group(self): [ 0 0 0 3*a], [ 0 3*a + 1 0 0] ) - sage: G = GO(3,GF(5)) + sage: # needs sage.libs.gap + sage: G = GO(3, GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field of size 5 with 2 generators ( [2 0 0] [0 1 0] @@ -316,8 +317,8 @@ def _repr_option(self, key): EXAMPLES:: - sage: SO3 = groups.matrix.SO(3, QQ) # needs sage.groups sage.modules - sage: SO3._repr_option('element_ascii_art') # needs sage.groups sage.modules + sage: SO3 = groups.matrix.SO(3, QQ) + sage: SO3._repr_option('element_ascii_art') True """ if key == 'element_ascii_art': @@ -479,6 +480,7 @@ def __richcmp__(self, other, op): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = GL(2,3) sage: H = MatrixGroup(G.gens()) sage: H == G @@ -486,6 +488,7 @@ def __richcmp__(self, other, op): sage: G == H True + sage: # needs sage.libs.gap sage: MS = MatrixSpace(QQ, 2, 2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G == G @@ -542,3 +545,34 @@ def __richcmp__(self, other, op): if lx != rx: return richcmp_not_equal(lx, rx, op) return rich_to_bool(op, 0) + + def is_trivial(self): + r""" + Return ``True`` if this group is the trivial group. + + A group is trivial, if it consists only of the identity + element, that is, if all its generators are the identity. + + EXAMPLES:: + + sage: MatrixGroup([identity_matrix(3)]).is_trivial() + True + sage: SL(2, ZZ).is_trivial() + False + sage: CoxeterGroup(['B',3], implementation="matrix").is_trivial() + False + + TESTS:: + + sage: CoxeterGroup(['A',0], implementation="matrix").is_trivial() + True + sage: MatrixGroup([matrix(SR, [[1,x], [0,1]])]).is_trivial() + False + sage: G = MatrixGroup([identity_matrix(3), identity_matrix(3)]) + sage: G.ngens() + 2 + sage: G.is_trivial() + True + + """ + return all(g.is_one() for g in self.gens()) diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index 4568c43d326..98841d9f0af 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -10,6 +10,8 @@ Special Linear Group of degree 2 over Integer Ring sage: G = SL(2, GF(3)); G Special Linear Group of degree 2 over Finite Field of size 3 + + sage: # needs sage.libs.gap sage: G.is_finite() True sage: G.conjugacy_classes_representatives() @@ -288,12 +290,13 @@ def __richcmp__(self, other, op): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = GL(2,3) sage: G == MatrixGroup(G.gens()) True - sage: # needs sage.rings.finite_rings - sage: G = groups.matrix.GL(4,2) # needs sage.modules + sage: # needs sage.libs.gap sage.rings.finite_rings + sage: G = groups.matrix.GL(4,2) sage: H = MatrixGroup(G.gens()) sage: G == H True diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index 9e90d3ac0dc..8d622f7b14f 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -39,6 +39,8 @@ sage: G = SO(4, GF(7), 1); G Special Orthogonal Group of degree 4 and form parameter 1 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: G.random_element() # random [4 3 5 2] [6 6 4 0] @@ -154,7 +156,7 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): Check that :trac:`26028` is fixed:: - sage: GO(3,25).order() # indirect doctest # needs sage.rings.finite_rings + sage: GO(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings 31200 Check that :trac:`28054` is fixed:: @@ -270,6 +272,8 @@ def GO(n, R, e=0, var='a', invariant_form=None): sage: GO(3, GF(7)) General Orthogonal Group of degree 3 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: GO(3, GF(7)).order() 672 sage: GO(3, GF(7)).gens() @@ -323,6 +327,7 @@ def GO(n, R, e=0, var='a', invariant_form=None): TESTS:: + sage: # needs sage.libs.gap sage: TestSuite(GO3).run() sage: groups.matrix.GO(2, 3, e=-1) General Orthogonal Group of degree 2 and form parameter -1 over Finite Field of size 3 @@ -378,6 +383,7 @@ def SO(n, R, e=None, var='a', invariant_form=None): sage: G = SO(3,GF(5)); G Special Orthogonal Group of degree 3 over Finite Field of size 5 + sage: # needs sage.libs.gap sage: G = SO(3,GF(5)) sage: G.gens() ( @@ -385,7 +391,6 @@ def SO(n, R, e=None, var='a', invariant_form=None): [0 3 0] [0 2 0] [4 0 0] [0 0 1], [0 3 1], [2 0 4] ) - sage: G = SO(3,GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field of size 5 with 3 generators ( [2 0 0] [3 2 3] [1 4 4] @@ -483,6 +488,7 @@ def invariant_bilinear_form(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: GO(2,3,+1).invariant_bilinear_form() [0 1] [1 0] @@ -503,7 +509,7 @@ def invariant_bilinear_form(self): TESTS:: - sage: GO3m.invariant_form() + sage: GO3m.invariant_form() # needs sage.libs.gap [1 0 0] [0 2 0] [0 0 3] diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index e0f3324b645..ec61d8a0634 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -5,6 +5,8 @@ sage: G = Sp(4, GF(7)); G Symplectic Group of degree 4 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: g = prod(G.gens()); g [3 0 3 0] [1 0 0 0] @@ -138,6 +140,7 @@ def Sp(n, R, var='a', invariant_form=None): sage: groups.matrix.Sp(2, 3) # needs sage.modules sage.rings.finite_rings Symplectic Group of degree 2 over Finite Field of size 3 + sage: # needs sage.libs.gap sage: G = Sp(4,5) sage: TestSuite(G).run() """ diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index ed6221cb61d..379d8276571 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -8,11 +8,11 @@ sage: # needs sage.rings.finite_rings sage: G = SU(3,5) - sage: G.order() + sage: G.order() # needs sage.libs.gap 378000 sage: G Special Unitary Group of degree 3 over Finite Field in a of size 5^2 - sage: G.gens() + sage: G.gens() # needs sage.libs.gap ( [ a 0 0] [4*a 4 1] [ 0 2*a + 2 0] [ 4 4 0] @@ -100,7 +100,7 @@ def _UG(n, R, special, var='a', invariant_form=None): TESTS:: - sage: GU(3,25).order() # indirect doctest # needs sage.rings.finite_rings + sage: GU(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings 3961191000000 """ prefix = 'General' @@ -195,7 +195,7 @@ def GU(n, R, var='a', invariant_form=None): sage: G = GU(3, 7); G # needs sage.rings.finite_rings General Unitary Group of degree 3 over Finite Field in a of size 7^2 - sage: G.gens() # needs sage.rings.finite_rings + sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings ( [ a 0 0] [6*a 6 1] [ 0 1 0] [ 6 6 0] @@ -207,7 +207,7 @@ def GU(n, R, var='a', invariant_form=None): sage: G = GU(3, 5, var='beta') # needs sage.rings.finite_rings sage: G.base_ring() # needs sage.rings.finite_rings Finite Field in beta of size 5^2 - sage: G.gens() # needs sage.rings.finite_rings + sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings ( [ beta 0 0] [4*beta 4 1] [ 0 1 0] [ 4 4 0] diff --git a/src/sage/groups/pari_group.py b/src/sage/groups/pari_group.py index bbcb1c0a6c9..4d1ca0bff22 100644 --- a/src/sage/groups/pari_group.py +++ b/src/sage/groups/pari_group.py @@ -6,8 +6,10 @@ """ from sage.libs.pari import pari +from sage.misc.lazy_import import lazy_import from sage.rings.integer import Integer -from sage.groups.perm_gps.permgroup_named import TransitiveGroup + +lazy_import('sage.groups.perm_gps.permgroup_named', 'TransitiveGroup') class PariGroup(): diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 8e2e6d82c10..2d73c8e7cf2 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -344,6 +344,7 @@ def PermutationGroup(gens=None, *args, **kwds): Note that we provide generators for the acting group. The permutation group we construct is its homomorphic image:: + sage: # needs sage.combinat sage: a = lambda g, x: vector(g*x, immutable=True) sage: X = [vector(x, immutable=True) for x in GF(3)^2] sage: G = SL(2,3); G.gens() @@ -1365,6 +1366,38 @@ def ngens(self): """ return len(self.gens()) + def is_trivial(self): + r""" + Return ``True`` if this group is the trivial group. + + A permutation group is trivial, if it consists only of the + identity element, that is, if it has no generators or only + trivial generators. + + EXAMPLES:: + + sage: G = PermutationGroup([], domain=["a", "b", "c"]) + sage: G.is_trivial() + True + sage: SymmetricGroup(0).is_trivial() + True + sage: SymmetricGroup(1).is_trivial() + True + sage: SymmetricGroup(2).is_trivial() + False + sage: DihedralGroup(1).is_trivial() + False + + TESTS:: + + sage: G = PermutationGroup([[], []], canonicalize=False) + sage: G.ngens() + 2 + sage: G.is_trivial() + True + """ + return all(g.is_one() for g in self._gens) + @cached_method def one(self): """ @@ -1547,6 +1580,7 @@ def representative_action(self,x,y): TESTS:: + sage: # needs sage.graphs sage: g = graphs.PetersenGraph() sage: g.relabel(list("abcdefghik")) sage: g.vertices(sort=True) @@ -2337,13 +2371,14 @@ def socle(self): EXAMPLES:: sage: G = SymmetricGroup(4) - sage: G.socle() + sage: s = G.socle(); s Subgroup generated by [(1,2)(3,4), (1,4)(2,3)] of (Symmetric group of order 4! as a permutation group) - sage: G.socle().socle() - Subgroup generated by [(1,2)(3,4), (1,4)(2,3)] of - (Subgroup generated by [(1,2)(3,4), (1,4)(2,3)] - of (Symmetric group of order 4! as a permutation group)) + + The socle of the socle is, essentially, the socle:: + + sage: s.socle() == s.subgroup(s.gens()) + True """ return self.subgroup(gap_group=self._gap_().Socle()) @@ -3706,6 +3741,7 @@ def has_regular_subgroup(self, return_group=False): But the automorphism group of Petersen's graph does not:: + sage: # needs sage.graphs sage: G = graphs.PetersenGraph().automorphism_group() sage: G.has_regular_subgroup() False @@ -3767,6 +3803,7 @@ def blocks_all(self, representatives=True): Picking an interesting group:: + sage: # needs sage.graphs sage: g = graphs.DodecahedralGraph() sage: g.is_vertex_transitive() True @@ -3776,12 +3813,12 @@ def blocks_all(self, representatives=True): Computing its blocks representatives:: - sage: ag.blocks_all() + sage: ag.blocks_all() # needs sage.graphs [[0, 15]] Now the full block:: - sage: sorted(ag.blocks_all(representatives = False)[0]) + sage: sorted(ag.blocks_all(representatives=False)[0]) # needs sage.graphs [[0, 15], [1, 16], [2, 12], [3, 13], [4, 9], [5, 10], [6, 11], [7, 18], [8, 17], [14, 19]] @@ -3977,6 +4014,7 @@ def minimal_generating_set(self): EXAMPLES:: + sage: # needs sage.graphs sage: g = graphs.CompleteGraph(4) sage: g.relabel(['a','b','c','d']) sage: mgs = g.automorphism_group().minimal_generating_set(); len(mgs) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 0a8087a20d5..26afa44edda 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -177,7 +177,7 @@ gap(...), x.[tab], and docs, e.g., gap.function? and x.function? """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -189,13 +189,13 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction from .gap_workspace import gap_workspace_file, prepare_workspace_dir from sage.cpython.string import bytes_to_str -from sage.env import SAGE_EXTCODE, SAGE_GAP_COMMAND, SAGE_GAP_MEMORY +from sage.env import SAGE_EXTCODE, SAGE_GAP_COMMAND, SAGE_GAP_MEMORY, GAP_ROOT_PATHS from sage.misc.misc import is_in_string from sage.misc.cachefunc import cached_method from sage.misc.instancedoc import instancedoc @@ -217,7 +217,17 @@ first_try = True -gap_cmd = SAGE_GAP_COMMAND +if SAGE_GAP_COMMAND is None: + # Passing -A allows us to use a minimal GAP installation without + # producing errors at start-up. The files sage.g and sage.gaprc are + # used to load any additional packages that may be available. + gap_cmd = f'gap -A -l "{GAP_ROOT_PATHS}"' + if SAGE_GAP_MEMORY is not None: + gap_cmd += " -s " + SAGE_GAP_MEMORY + " -o " + SAGE_GAP_MEMORY +else: + gap_cmd = SAGE_GAP_COMMAND + + if platform.processor() == 'ia64' and os.path.exists('/usr/bin/prctl'): # suppress unaligned access to 0x..., ip=0x... warnings gap_cmd = 'prctl --unaligned=silent ' + gap_cmd @@ -234,7 +244,7 @@ def gap_command(use_workspace_cache=True, local=True): return gap_cmd, True -############ Classes with methods for both the GAP3 and GAP4 interface +# ########### Classes with methods for both the GAP3 and GAP4 interface class Gap_generic(ExtraTabCompletion, Expect): r""" @@ -277,10 +287,10 @@ def _synchronize(self, timeout=0.5, cmd='%s;'): E = self._expect from sage.misc.prandom import randrange rnd = randrange(2147483647) - cmd = str(rnd)+';' + cmd = str(rnd) + ';' try: E.sendline(cmd) - E.expect(r'@[nf][@J\s>]*'+str(rnd), timeout=timeout) + E.expect(r'@[nf][@J\s>]*' + str(rnd), timeout=timeout) E.send(' ') E.expect('@i', timeout=timeout) except pexpect.TIMEOUT: @@ -443,7 +453,7 @@ def load_package(self, pkg, verbose=False): print("Loading GAP package {}".format(pkg)) x = self.eval('LoadPackage("{}")'.format(pkg)) if x == 'fail': - raise RuntimeError("Error loading Gap package "+str(pkg)+". " + + raise RuntimeError("Error loading Gap package " + str(pkg) + ". " + "You may want to install gap_packages SPKG.") def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds): @@ -497,10 +507,10 @@ def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds): ' -\n\\\\-' """ # '" - #We remove all of the comments: On each line, we try - #to find a pound sign. If we find it, we check to see if - #it is occurring in a string. If it is not in a string, we - #strip off the comment. + # We remove all of the comments: On each line, we try + # to find a pound sign. If we find it, we check to see if + # it is occurring in a string. If it is not in a string, we + # strip off the comment. if not split_lines: input_line = str(x) else: @@ -510,17 +520,17 @@ def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds): while pound_position != -1: if not is_in_string(line, pound_position): line = line[:pound_position] - pound_position = line.find('#',pound_position+1) - input_line += " "+line + pound_position = line.find('#', pound_position + 1) + input_line += " " + line if not input_line.endswith(';'): input_line += ';' result = Expect.eval(self, input_line, **kwds) if not newlines: - result = result.replace("\\\n","") + result = result.replace("\\\n", "") return result.rstrip() def _execute_line(self, line, wait_for_prompt=True, expect_eof=False): - if self._expect is None: # interface is down + if self._expect is None: # interface is down self._start() E = self._expect try: @@ -530,9 +540,9 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False): except OSError: raise RuntimeError("Error evaluating %s in %s" % (line, self)) if not wait_for_prompt: - return (b'',b'') + return (b'', b'') if len(line) == 0: - return (b'',b'') + return (b'', b'') try: terminal_echo = [] # to be discarded normal_outputs = [] # GAP stdout @@ -546,43 +556,43 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False): warnings.warn( "possibly wrong version of GAP package " "interface. Crossing fingers and continuing.") - elif x == 1: #@@ + elif x == 1: # @@ current_outputs.append(b'@') - elif x == 2: #special char + elif x == 2: # special char c = ord(E.after[1:2]) - ord(b'A') + 1 s = bytes([c]) current_outputs.append(s) - elif x == 3: # garbage collection info, ignore + elif x == 3: # garbage collection info, ignore pass - elif x == 4: # @e -- break loop + elif x == 4: # @e -- break loop E.sendline("quit;") - elif x == 5: # @c completion, doesn't seem to happen when -p is in use + elif x == 5: # @c completion, doesn't seem to happen when -p is in use warnings.warn("I didn't think GAP could do this") - elif x == 6: # @f GAP error message + elif x == 6: # @f GAP error message current_outputs = error_outputs - elif x == 7: # @h help text, but this stopped happening with new help + elif x == 7: # @h help text, but this stopped happening with new help warnings.warn("I didn't think GAP could do this") - elif x == 8: # @i awaiting normal input + elif x == 8: # @i awaiting normal input break - elif x == 9: # @m finished running a child + elif x == 9: # @m finished running a child pass # there is no need to do anything - elif x == 10: #@n normal output line + elif x == 10: # @n normal output line current_outputs = normal_outputs - elif x == 11: #@r echoing input + elif x == 11: # @r echoing input current_outputs = terminal_echo - elif x == 12: #@sN shouldn't happen + elif x == 12: # @sN shouldn't happen warnings.warn("this should never happen") - elif x == 13: #@w GAP is trying to send a Window command + elif x == 13: # @w GAP is trying to send a Window command warnings.warn("this should never happen") - elif x == 14: #@x seems to be safely ignorable + elif x == 14: # @x seems to be safely ignorable pass - elif x == 15:#@z GAP starting a subprocess + elif x == 15: # @z GAP starting a subprocess pass # there is no need to do anything except pexpect.EOF: if not expect_eof: - raise RuntimeError("Unexpected EOF from %s executing %s" % (self,line)) + raise RuntimeError("Unexpected EOF from %s executing %s" % (self, line)) except IOError: - raise RuntimeError("IO Error from %s executing %s" % (self,line)) + raise RuntimeError("IO Error from %s executing %s" % (self, line)) return (b"".join(normal_outputs), b"".join(error_outputs)) def _keyboard_interrupt(self): @@ -687,8 +697,8 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if error += "\nRunning gap_reset_workspace()..." self.quit() gap_reset_workspace() - error = error.replace('\r','') - raise RuntimeError("%s produced error output\n%s\n executing %s" % (self, error,line)) + error = error.replace('\r', '') + raise RuntimeError("%s produced error output\n%s\n executing %s" % (self, error, line)) if not normal: return '' @@ -762,7 +772,7 @@ def _contains(self, v1, v2): sage: 2 in gap('Integers') True """ - return self.eval('%s in %s' % (v1,v2)) == "true" + return self.eval('%s in %s' % (v1, v2)) == "true" def _true_symbol(self): """ @@ -858,20 +868,21 @@ def function_call(self, function, args=None, kwds=None): args, kwds = self._convert_args_kwds(args, kwds) self._check_valid_function_name(function) - #Here we have to do some magic because not all GAP - #functions return a value. If you try to store their - #results to a variable, then GAP will complain. Thus, before - #we evaluate the function, we make it so that the marker string - #is in the 'last' variable in GAP. If the function returns a - #value, then that value will be in 'last', otherwise it will - #be the marker. + # Here we have to do some magic because not all GAP + # functions return a value. If you try to store their + # results to a variable, then GAP will complain. Thus, before + # we evaluate the function, we make it so that the marker string + # is in the 'last' variable in GAP. If the function returns a + # value, then that value will be in 'last', otherwise it will + # be the marker. marker = '__SAGE_LAST__:="__SAGE_LAST__";;' cmd = "%s(%s);;" % (function, ",".join([s.name() for s in args] + - ['%s=%s' % (key,value.name()) for key, value in kwds.items()])) + [f'{key}={value.name()}' + for key, value in kwds.items()])) if len(marker) + len(cmd) <= self._eval_using_file_cutoff: # We combine the two commands so we only run eval() once and the # only output would be from the second command - res = self.eval(marker+cmd) + res = self.eval(marker + cmd) else: self.eval(marker) res = self.eval(cmd) @@ -1035,7 +1046,8 @@ def _matrix_(self, R): from sage.matrix.matrix_space import MatrixSpace M = MatrixSpace(R, n, m) - entries = [[R(self[r,c]) for c in range(1,m+1)] for r in range(1,n+1)] + entries = [[R(self[r, c]) for c in range(1, m + 1)] + for r in range(1, n + 1)] return M(entries) @@ -1089,7 +1101,7 @@ def __init__(self, max_workspace_size=None, self.__seq = 0 self._seed = seed - def set_seed(self,seed=None): + def set_seed(self, seed=None): """ Set the seed for gap interpreter. @@ -1194,8 +1206,8 @@ def _start(self): self.save_workspace() # Now, as self._expect exists, we can compile some useful pattern: self._compiled_full_pattern = self._expect.compile_pattern_list([ - r'@p\d+\.','@@','@[A-Z]',r'@[123456!"#$%&][^+]*\+', - '@e','@c','@f','@h','@i','@m','@n','@r',r'@s\d',r'@w.*\+','@x','@z']) + r'@p\d+\.', '@@', '@[A-Z]', r'@[123456!"#$%&][^+]*\+', + '@e', '@c', '@f', '@h', '@i', '@m', '@n', '@r', r'@s\d', r'@w.*\+', '@x', '@z']) # read everything up to the first "ready" prompt self._expect.expect("@i") @@ -1236,10 +1248,9 @@ def cputime(self, t=None): """ if t is not None: return self.cputime() - t - else: - self.eval('_r_ := Runtimes();') - r = sum(eval(self.eval('[_r_.user_time, _r_.system_time, _r_.user_time_children, _r_.system_time_children]'))) - return r/1000.0 + self.eval('_r_ := Runtimes();') + r = sum(eval(self.eval('[_r_.user_time, _r_.system_time, _r_.user_time_children, _r_.system_time_children]'))) + return r / 1000.0 def save_workspace(self): r""" @@ -1339,7 +1350,7 @@ def set(self, var, value): sage: gap.get('x') '2' """ - cmd = ('%s:=%s;;' % (var, value)).replace('\n','') + cmd = ('%s:=%s;;' % (var, value)).replace('\n', '') self._eval_line(cmd, allow_use_file=True) def get(self, var, use_file=False): @@ -1356,10 +1367,10 @@ def get(self, var, use_file=False): tmp = self._local_tmpfile() if os.path.exists(tmp): os.unlink(tmp) - self.eval('PrintTo("%s", %s);' % (tmp,var), strip=False) + self.eval('PrintTo("%s", %s);' % (tmp, var), strip=False) with open(tmp) as f: r = f.read() - r = r.strip().replace("\\\n","") + r = r.strip().replace("\\\n", "") os.unlink(tmp) return r else: @@ -1470,7 +1481,7 @@ def _tab_completion(self): True """ names = eval(self.eval('NamesSystemGVars()')) + \ - eval(self.eval('NamesUserGVars()')) + eval(self.eval('NamesUserGVars()')) return [n for n in names if n[0] in string.ascii_letters] @@ -1498,20 +1509,21 @@ def gap_reset_workspace(max_workspace_size=None, verbose=False): We temporarily need to change the worksheet filename, and to set ``first_try=True`` to ensure that the new workspace is created:: + sage: # long time sage: ORIGINAL_WORKSPACE = sage.interfaces.gap.WORKSPACE sage: saved_first_try = sage.interfaces.gap.first_try sage: sage.interfaces.gap.first_try = True sage: sage.interfaces.gap.WORKSPACE = tmp_filename() sage: from multiprocessing import Process sage: import time - sage: gap = Gap() # long time (reset GAP session) + sage: gap = Gap() # reset GAP session sage: P = [Process(target=gap, args=("14242",)) for i in range(4)] - sage: for p in P: # long time, indirect doctest + sage: for p in P: # indirect doctest ....: p.start() ....: time.sleep(float(0.2)) - sage: for p in P: # long time + sage: for p in P: ....: p.join() - sage: os.unlink(sage.interfaces.gap.WORKSPACE) # long time + sage: os.unlink(sage.interfaces.gap.WORKSPACE) sage: sage.interfaces.gap.WORKSPACE = ORIGINAL_WORKSPACE sage: sage.interfaces.gap.first_try = saved_first_try """ @@ -1572,8 +1584,8 @@ def _latex_(self): P = self._check_valid() try: s = P.eval('LaTeXObj(%s)' % self.name()) - s = s.replace('\\\\','\\').replace('"','') - s = s.replace('%\\n',' ') + s = s.replace('\\\\', '\\').replace('"', '') + s = s.replace('%\\n', ' ') return s except RuntimeError: return str(self) @@ -1581,7 +1593,7 @@ def _latex_(self): @cached_method def _tab_completion(self): """ - Return additional tab completion entries + Return additional tab completion entries. OUTPUT: @@ -1595,10 +1607,11 @@ def _tab_completion(self): """ P = self.parent() v = P.eval(r'\$SAGE.OperationsAdmittingFirstArgument(%s)' % self.name()) - v = v.replace('Tester(','').replace('Setter(','').replace(')','').replace('\n', '') + v = v.replace('Tester(', '').replace('Setter(', '').replace(')', '').replace('\n', '') v = v.split(',') - v = [ oper.split('"')[1] for oper in v ] - v = [ oper for oper in v if all(ch in string.ascii_letters for ch in oper) ] + v = (oper.split('"')[1] for oper in v) + v = [oper for oper in v + if all(ch in string.ascii_letters for ch in oper)] return sorted(set(v)) @@ -1708,13 +1721,13 @@ def gfq_gap_to_sage(x, F): return F(0) i1 = s.index("(") i2 = s.index(")") - q = eval(s[i1+1:i2].replace('^','**')) + q = eval(s[i1 + 1:i2].replace('^', '**')) if not F.cardinality().is_power_of(q): raise ValueError('%r has no subfield of size %r' % (F, q)) if s.find(')^') == -1: e = 1 else: - e = int(s[i2+2:]) + e = int(s[i2 + 2:]) if F.degree() == 1: g = F(gap.eval('Int(Z(%s))' % q)) elif F.is_conway(): @@ -1724,6 +1737,7 @@ def gfq_gap_to_sage(x, F): raise ValueError('%r is not prime or defined by a Conway polynomial' % F) return g**e + def intmod_gap_to_sage(x): r""" INPUT: @@ -1827,5 +1841,5 @@ def gap_console(): if not get_display_manager().is_in_terminal(): raise RuntimeError('Can use the console only in the terminal. Try %%gap magics instead.') cmd, _ = gap_command(use_workspace_cache=False) - cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','console.g') + cmd += ' ' + os.path.join(SAGE_EXTCODE, 'gap', 'console.g') os.system(cmd) diff --git a/src/sage/interfaces/gap_workspace.py b/src/sage/interfaces/gap_workspace.py index c104664143a..a2c97260077 100644 --- a/src/sage/interfaces/gap_workspace.py +++ b/src/sage/interfaces/gap_workspace.py @@ -17,7 +17,7 @@ import time import hashlib import subprocess -from sage.env import DOT_SAGE, HOSTNAME, GAP_LIB_DIR, GAP_SHARE_DIR +from sage.env import DOT_SAGE, HOSTNAME, GAP_ROOT_PATHS def gap_workspace_file(system="gap", name="workspace", dir=None): @@ -60,8 +60,12 @@ def gap_workspace_file(system="gap", name="workspace", dir=None): if dir is None: dir = os.path.join(DOT_SAGE, 'gap') - data = f'{GAP_LIB_DIR}:{GAP_SHARE_DIR}' - for path in GAP_LIB_DIR, GAP_SHARE_DIR: + data = f'{GAP_ROOT_PATHS}' + for path in GAP_ROOT_PATHS.split(";"): + if not path: + # If GAP_ROOT_PATHS begins or ends with a semicolon, + # we'll get one empty path. + continue sysinfo = os.path.join(path, "sysinfo.gap") if os.path.exists(sysinfo): data += subprocess.getoutput(f'. "{sysinfo}" && echo ":$GAP_VERSION:$GAParch"') diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index 59a9a1e26a5..e7354e05c70 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -85,10 +85,6 @@ def jmolpath(self): """ jmolpath = os.path.join(JMOL_DIR, "JmolData.jar") - if sys.platform == 'cygwin': - import cygwin - jmolpath = cygwin.cygpath(jmolpath, 'w') - return jmolpath def is_jmol_available(self): @@ -129,8 +125,6 @@ def export_image(self, - datafile -- full path to the data file Jmol can read or text of a script telling Jmol what to read or load. - If it is a script and the platform is cygwin, the filenames in - the script should be in native windows format. - datafile_cmd -- (default ``'script'``) ``'load'`` or ``'script'`` should be ``"load"`` for a data file. @@ -180,10 +174,6 @@ def export_image(self, sage: archive = NamedTemporaryFile(suffix=".zip") sage: D.export_jmol(archive.name) # needs sage.plot sage: archive_native = archive.name - sage: import sys - sage: if sys.platform == 'cygwin': - ....: import cygwin - ....: archive_native = cygwin.cygpath(archive_native, 'w') sage: script = f'set defaultdirectory "f{archive_native}"\n' sage: script += 'script SCRIPT\n' sage: with NamedTemporaryFile(suffix=".png") as testfile: # optional - java, needs sage.plot @@ -198,12 +188,6 @@ def export_image(self, jmolpath = self.jmolpath() target_native = targetfile - if sys.platform == 'cygwin': - import cygwin - target_native = cygwin.cygpath(target_native, 'w') - if datafile_cmd != 'script': - datafile = cygwin.cygpath(datafile, 'w') - launchscript = "" if (datafile_cmd != 'script'): launchscript = "load " diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index e403b04dab7..d6866117d57 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -131,11 +131,12 @@ def Sphere(n): EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: s2 = Sphere(2) # optional - kenzo - sage: s2 # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import Sphere + sage: s2 = Sphere(2) + sage: s2 [K1 Simplicial-Set] - sage: [s2.homology(i) for i in range(8)] # optional - kenzo + sage: [s2.homology(i) for i in range(8)] [Z, 0, Z, 0, 0, 0, 0, 0] """ kenzosphere = __sphere__(n) @@ -162,11 +163,12 @@ def MooreSpace(m, n): EXAMPLES:: - sage: from sage.interfaces.kenzo import MooreSpace # optional - kenzo - sage: m24 = MooreSpace(2,4) # optional - kenzo - sage: m24 # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import MooreSpace + sage: m24 = MooreSpace(2,4) + sage: m24 [K10 Simplicial-Set] - sage: [m24.homology(i) for i in range(8)] # optional - kenzo + sage: [m24.homology(i) for i in range(8)] [Z, 0, 0, 0, C2, 0, 0, 0] """ kenzomoore = __moore__(m, n) @@ -387,10 +389,11 @@ def table(self, p, i1, i2, j1, j2): EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: S2 = Sphere(2) # optional - kenzo - sage: EMS = S2.em_spectral_sequence() # optional - kenzo - sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import Sphere + sage: S2 = Sphere(2) + sage: EMS = S2.em_spectral_sequence() + sage: EMS.table(0, -2, 2, -2, 2) 0 Z 0 0 0 0 0 0 0 0 0 0 Z 0 0 @@ -427,15 +430,16 @@ def homology(self, n): OUTPUT: - - An homology group. + - A homology group. EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: s2 = Sphere(2) # optional - kenzo - sage: s2 # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import Sphere + sage: s2 = Sphere(2) + sage: s2 [K1 Simplicial-Set] - sage: s2.homology(2) # optional - kenzo + sage: s2.homology(2) Z """ echcm1 = __echcm__(self._kenzo) @@ -490,15 +494,16 @@ def basis(self, dim): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: for i in range(6): # optional - kenzo + sage: for i in range(6): ....: print("Basis in dimension %i: %s" % (i, kenzo_chcm.basis(i))) Basis in dimension 0: ['G0G0', 'G0G1', 'G0G2'] Basis in dimension 1: ['G1G0', 'G1G1'] @@ -506,7 +511,6 @@ def basis(self, dim): Basis in dimension 3: ['G3G0', 'G3G1'] Basis in dimension 4: ['G4G0', 'G4G1'] Basis in dimension 5: ['G5G0', 'G5G1', 'G5G2'] - """ return __basis_aux1__(self._kenzo, dim).python() @@ -603,26 +607,27 @@ def differential(self, dim=None, comb=None): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: kenzo_chcm.basis(4) # optional - kenzo + sage: kenzo_chcm.basis(4) ['G4G0', 'G4G1'] - sage: kenzo_chcm.differential(4, [1, 'G4G0']) # optional - kenzo + sage: kenzo_chcm.differential(4, [1, 'G4G0']) ----------------------------------------------------------------------{CMBN 3} <1 * G3G0> <3 * G3G1> ------------------------------------------------------------------------------ - sage: kenzo_chcm.basis(5) # optional - kenzo + sage: kenzo_chcm.basis(5) ['G5G0', 'G5G1', 'G5G2'] - sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 4} <6 * G4G0> @@ -748,10 +753,11 @@ def homotopy_group(self, n): EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: s2 = Sphere(2) # optional - kenzo - sage: p = s2.cartesian_product(s2) # optional - kenzo - sage: p.homotopy_group(3) # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import Sphere + sage: s2 = Sphere(2) + sage: p = s2.cartesian_product(s2) + sage: p.homotopy_group(3) Multiplicative Abelian group isomorphic to Z x Z @@ -780,10 +786,11 @@ def em_spectral_sequence(self): EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: S2 = Sphere(2) # optional - kenzo - sage: EMS = S2.em_spectral_sequence() # optional - kenzo - sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import Sphere + sage: S2 = Sphere(2) + sage: EMS = S2.em_spectral_sequence() + sage: EMS.table(0, -2, 2, -2, 2) 0 Z 0 0 0 0 0 0 0 0 0 0 Z 0 0 @@ -1090,15 +1097,16 @@ def KChainComplex(chain_complex): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: kenzo_chcm.homology(5) # optional - kenzo + sage: kenzo_chcm.homology(5) Z x Z """ d = chain_complex.differential() @@ -1242,8 +1250,9 @@ def KFiniteSimplicialSet(sset): EXAMPLES:: + sage: # optional - kenzo sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet - sage: from sage.interfaces.kenzo import KFiniteSimplicialSet # optional - kenzo + sage: from sage.interfaces.kenzo import KFiniteSimplicialSet sage: s0 = AbstractSimplex(0, name='s0') sage: s1 = AbstractSimplex(0, name='s1') sage: s2 = AbstractSimplex(0, name='s2') @@ -1253,15 +1262,15 @@ def KFiniteSimplicialSet(sset): sage: s012 = AbstractSimplex(2, name='s012') sage: Triangle = SimplicialSet({s01: (s1, s0),\ ....: s02: (s2, s0), s12: (s2, s1)}, base_point = s0) - sage: KTriangle = KFiniteSimplicialSet(Triangle) # optional - kenzo - sage: KTriangle.homology(1) # optional - kenzo + sage: KTriangle = KFiniteSimplicialSet(Triangle) + sage: KTriangle.homology(1) Z - sage: KTriangle.basis(1) # optional - kenzo + sage: KTriangle.basis(1) ['CELL_1_0', 'CELL_1_1', 'CELL_1_2'] sage: S1 = simplicial_sets.Sphere(1) sage: S3 = simplicial_sets.Sphere(3) - sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3)) # optional - kenzo - sage: KS1vS3.homology(3) # optional - kenzo + sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3)) + sage: KS1vS3.homology(3) Z """ from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets @@ -1389,18 +1398,19 @@ def source_complex(self): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo - sage: differential_morphism # optional - kenzo + sage: differential_morphism = kenzo_chcm.differential() + sage: differential_morphism [K... Morphism (degree -1): K... -> K...] - sage: differential_morphism.source_complex() # optional - kenzo + sage: differential_morphism.source_complex() [K... Chain-Complex] """ return KenzoChainComplex(__sorc_aux__(self._kenzo)) @@ -1415,18 +1425,19 @@ def target_complex(self): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo - sage: differential_morphism # optional - kenzo + sage: differential_morphism = kenzo_chcm.differential() + sage: differential_morphism [K... Morphism (degree -1): K... -> K...] - sage: differential_morphism.target_complex() # optional - kenzo + sage: differential_morphism.target_complex() [K... Chain-Complex] """ return KenzoChainComplex(__trgt_aux__(self._kenzo)) @@ -1441,22 +1452,23 @@ def degree(self): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree=-1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo - sage: differential_morphism # optional - kenzo + sage: differential_morphism = kenzo_chcm.differential() + sage: differential_morphism [K... Morphism (degree -1): K... -> K...] - sage: differential_morphism.degree() # optional - kenzo + sage: differential_morphism.degree() -1 - sage: differential_morphism.composite(differential_morphism).degree() # optional - kenzo + sage: differential_morphism.composite(differential_morphism).degree() -2 - sage: kenzo_chcm.null_morphism().degree() # optional - kenzo + sage: kenzo_chcm.null_morphism().degree() 0 """ return __degr_aux__(self._kenzo).python() @@ -1485,51 +1497,52 @@ def evaluation(self, dim, comb): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo - sage: differential_morphism # optional - kenzo + sage: differential_morphism = kenzo_chcm.differential() + sage: differential_morphism [K... Morphism (degree -1): K... -> K...] - sage: dif_squared = differential_morphism.composite(differential_morphism) # optional - kenzo - sage: dif_squared # optional - kenzo + sage: dif_squared = differential_morphism.composite(differential_morphism) + sage: dif_squared [K... Morphism (degree -2): K... -> K...] - sage: kenzo_chcm.basis(5) # optional - kenzo + sage: kenzo_chcm.basis(5) ['G5G0', 'G5G1', 'G5G2'] - sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 4} <6 * G4G0> <-3 * G4G1> ------------------------------------------------------------------------------ - sage: differential_morphism.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: differential_morphism.evaluation(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 4} <6 * G4G0> <-3 * G4G1> ------------------------------------------------------------------------------ - sage: dif_squared.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: dif_squared.evaluation(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 3} ------------------------------------------------------------------------------ - sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo - sage: idx2 = idnt.sum(idnt) # optional - kenzo - sage: idnt.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: idnt = kenzo_chcm.identity_morphism() + sage: idx2 = idnt.sum(idnt) + sage: idnt.evaluation(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 5} <1 * G5G0> <2 * G5G2> ------------------------------------------------------------------------------ - sage: idx2.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + sage: idx2.evaluation(5, [1, 'G5G0', 2, 'G5G2']) ----------------------------------------------------------------------{CMBN 5} <2 * G5G0> @@ -1556,30 +1569,31 @@ def opposite(self): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo - sage: idnt # optional - kenzo + sage: idnt = kenzo_chcm.identity_morphism() + sage: idnt [K... Morphism (degree 0): K... -> K...] - sage: opps_id = idnt.opposite() # optional - kenzo - sage: opps_id # optional - kenzo + sage: opps_id = idnt.opposite() + sage: opps_id [K... Morphism (degree 0): K... -> K...] - sage: kenzo_chcm.basis(4) # optional - kenzo + sage: kenzo_chcm.basis(4) ['G4G0', 'G4G1'] - sage: idnt.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: idnt.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <2 * G4G0> <-5 * G4G1> ------------------------------------------------------------------------------ - sage: opps_id.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: opps_id.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <-2 * G4G0> @@ -1653,36 +1667,37 @@ def sum(self, object=None): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo - sage: idnt # optional - kenzo + sage: idnt = kenzo_chcm.identity_morphism() + sage: idnt [K... Morphism (degree 0): K... -> K...] - sage: opps_id = idnt.opposite() # optional - kenzo - sage: opps_id # optional - kenzo + sage: opps_id = idnt.opposite() + sage: opps_id [K... Morphism (degree 0): K... -> K...] - sage: null = kenzo_chcm.null_morphism() # optional - kenzo - sage: null # optional - kenzo + sage: null = kenzo_chcm.null_morphism() + sage: null [K... Morphism (degree 0): K... -> K...] - sage: idx2 = idnt.sum(idnt) # optional - kenzo - sage: idx5 = idx2.sum( # optional - kenzo + sage: idx2 = idnt.sum(idnt) + sage: idx5 = idx2.sum( ....: (opps_id, idnt, idnt, null, idx2.sum(idnt), opps_id)) - sage: kenzo_chcm.basis(4) # optional - kenzo + sage: kenzo_chcm.basis(4) ['G4G0', 'G4G1'] - sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <4 * G4G0> <-10 * G4G1> ------------------------------------------------------------------------------ - sage: idx5.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: idx5.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <10 * G4G0> @@ -1719,36 +1734,37 @@ def substract(self, object=None): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplex sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) - sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo - sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo - sage: kenzo_chcm # optional - kenzo + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) + sage: kenzo_chcm [K... Chain-Complex] - sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo - sage: idnt # optional - kenzo + sage: idnt = kenzo_chcm.identity_morphism() + sage: idnt [K... Morphism (degree 0): K... -> K...] - sage: opps_id = idnt.opposite() # optional - kenzo - sage: opps_id # optional - kenzo + sage: opps_id = idnt.opposite() + sage: opps_id [K... Morphism (degree 0): K... -> K...] - sage: null = kenzo_chcm.null_morphism() # optional - kenzo - sage: null # optional - kenzo + sage: null = kenzo_chcm.null_morphism() + sage: null [K... Morphism (degree 0): K... -> K...] - sage: idx2 = idnt.substract(opps_id) # optional - kenzo - sage: opps_idx2 = idx2.substract( # optional - kenzo + sage: idx2 = idnt.substract(opps_id) + sage: opps_idx2 = idx2.substract( ....: (opps_id, idnt, idnt, null, idx2.substract(opps_id))) - sage: kenzo_chcm.basis(4) # optional - kenzo + sage: kenzo_chcm.basis(4) ['G4G0', 'G4G1'] - sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <4 * G4G0> <-10 * G4G1> ------------------------------------------------------------------------------ - sage: opps_idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + sage: opps_idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) ----------------------------------------------------------------------{CMBN 4} <-4 * G4G0> @@ -1954,16 +1970,16 @@ def KChainComplexMorphism(morphism): EXAMPLES:: - sage: from sage.interfaces.kenzo import KChainComplexMorphism # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import KChainComplexMorphism sage: C = ChainComplex({0: identity_matrix(ZZ, 1)}) sage: D = ChainComplex({0: zero_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)}) sage: f = Hom(C,D)({0: identity_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)}) - sage: g = KChainComplexMorphism(f) # optional - kenzo - sage: g # optional - kenzo + sage: g = KChainComplexMorphism(f); g [K... Morphism (degree 0): K... -> K...] - sage: g.source_complex() # optional - kenzo + sage: g.source_complex() [K... Chain-Complex] - sage: g.target_complex() # optional - kenzo + sage: g.target_complex() [K... Chain-Complex] """ source = KChainComplex(morphism.domain()) @@ -2017,21 +2033,22 @@ def BicomplexSpectralSequence(l): EXAMPLES:: - sage: from sage.interfaces.kenzo import BicomplexSpectralSequence # optional - kenzo + sage: # optional - kenzo + sage: from sage.interfaces.kenzo import BicomplexSpectralSequence sage: C1 = ChainComplex({1: matrix(ZZ, 0, 2, [])}, degree_of_differential=-1) sage: C2 = ChainComplex({1: matrix(ZZ, 1, 2, [1, 0])},degree_of_differential=-1) sage: C3 = ChainComplex({0: matrix(ZZ, 0,2 , [])},degree_of_differential=-1) sage: M1 = Hom(C2,C1)({1: matrix(ZZ, 2, 2, [2, 0, 0, 2])}) sage: M2 = Hom(C3,C2)({0: matrix(ZZ, 1, 2, [2, 0])}) sage: l = [M1, M2] - sage: E = BicomplexSpectralSequence(l) # optional - kenzo - sage: E.group(2,0,1) # optional - kenzo + sage: E = BicomplexSpectralSequence(l) + sage: E.group(2,0,1) Additive abelian group isomorphic to Z/2 + Z - sage: E.table(3,0,2,0,2) # optional - kenzo + sage: E.table(3,0,2,0,2) 0 0 0 Z/2 + Z/4 0 0 0 0 Z - sage: E.matrix(2,2,0) # optional - kenzo + sage: E.matrix(2,2,0) [ 0 0] [-4 0] """ diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 9b11f02a85f..fde9556858d 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -239,7 +239,7 @@ def __init__(self, maxread=None, script_subdirectory=None, def __reduce__(self): """ - Used in serializing an Macaulay2 interface. + Used in serializing a Macaulay2 interface. EXAMPLES:: diff --git a/src/sage/interfaces/matlab.py b/src/sage/interfaces/matlab.py index 74354b77412..8ecfc3447cd 100644 --- a/src/sage/interfaces/matlab.py +++ b/src/sage/interfaces/matlab.py @@ -300,11 +300,11 @@ def chdir(self, directory): def sage2matlab_matrix_string(self, A): """ - Return an matlab matrix from a Sage matrix. + Return a matlab matrix from a Sage matrix. INPUT: A Sage matrix with entries in the rationals or reals. - OUTPUT: A string that evaluates to an Matlab matrix. + OUTPUT: A string that evaluates to a Matlab matrix. EXAMPLES:: diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 7efc51038ea..01ae082cca6 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -833,7 +833,7 @@ def __init__(self, formula, x^2 + x*y + 3*x*z + 2*y*z + 2*z^2 - x - z < 0, \ -2*x + 1 < 0, -x*y - x*z - 2*y*z - 2*z^2 + z < 0, \ x + 3*y + 3*z - 1 < 0] - sage: qepcad(conds, memcells=2000000) # optional - qepcad + sage: qepcad(conds, memcells=3000000) # optional - qepcad 2 x - 1 > 0 /\ z > 0 /\ z - y < 0 /\ 3 z + 3 y + x - 1 < 0 """ self._cell_cache = {} diff --git a/src/sage/interfaces/scilab.py b/src/sage/interfaces/scilab.py index 3229fd3d82c..a275306dbea 100644 --- a/src/sage/interfaces/scilab.py +++ b/src/sage/interfaces/scilab.py @@ -249,11 +249,12 @@ def set_seed(self, seed=None): EXAMPLES:: - sage: from sage.interfaces.scilab import Scilab # optional - scilab - sage: s = Scilab() # optional - scilab - sage: s.set_seed(1) # optional - scilab + sage: # optional - scilab + sage: from sage.interfaces.scilab import Scilab + sage: s = Scilab() + sage: s.set_seed(1) 1 - sage: [s.rand() for i in range(5)] # optional - scilab + sage: [s.rand() for i in range(5)] [ 0.6040239, @@ -408,7 +409,7 @@ def sage2scilab_matrix_string(self, A): A Sage matrix with entries in the rationals or reals. OUTPUT: - A string that evaluates to an Scilab matrix. + A string that evaluates to a Scilab matrix. EXAMPLES:: @@ -535,7 +536,7 @@ def scilab_console(): EXAMPLES:: - sage: from sage.interfaces.scilab import scilab_console # optional - scilab + sage: from sage.interfaces.scilab import scilab_console # optional - scilab sage: scilab_console() # optional - scilab; not tested ___________________________________________ scilab-5.0.3 @@ -569,7 +570,7 @@ def scilab_version(): EXAMPLES:: - sage: from sage.interfaces.scilab import scilab_version # optional - scilab + sage: from sage.interfaces.scilab import scilab_version # optional - scilab sage: scilab_version() # optional - scilab 'scilab-...' """ diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index a2f48a996ed..0ef601e124c 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -97,26 +97,26 @@ If you have `SnapPy `__ installed inside -Sage you can obtain an instance of :class:`~spherogram.links.links_base.Link`, +Sage, you can obtain an instance of :class:`~spherogram.links.links_base.Link`, too:: + sage: # optional - snappy sage: L6 = KnotInfo.L6a1_0 - sage: l6s = L6.link(snappy=True); l6s # optional - snappy + sage: l6s = L6.link(snappy=True); l6s Plink failed to import tkinter. - - sage: type(l6s) # optional - snappy + sage: type(l6s) sage: l6 = L6.link() - sage: l6 == l6s.sage_link() # optional - snappy + sage: l6 == l6s.sage_link() True - sage: L6.link(L6.items.name, snappy=True) # optional - snappy + sage: L6.link(L6.items.name, snappy=True) - sage: l6sn = _ # optional - snappy - sage: l6s == l6sn # optional - snappy + sage: l6sn = _ + sage: l6s == l6sn False - sage: l6m = l6.mirror_image() # optional - snappy - sage: l6sn.sage_link().is_isotopic(l6m) # optional - snappy + sage: l6m = l6.mirror_image() + sage: l6sn.sage_link().is_isotopic(l6m) True But observe that the name conversion to SnapPy does not distinguish orientation @@ -238,14 +238,16 @@ from enum import Enum from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import lazy_import from sage.misc.sage_eval import sage_eval from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation from sage.rings.integer_ring import ZZ -from sage.groups.braid import BraidGroup from sage.knots.knot import Knots from sage.databases.knotinfo_db import KnotInfoColumns, db +lazy_import('sage.groups.braid', 'BraidGroup') + def eval_knotinfo(string, locals={}, to_tuple=True): r""" @@ -694,8 +696,7 @@ def braid_length(self): @cached_method def braid(self): r""" - Return the braid notation of self as an instance of :class:`~sage.groups.braid.Braid`. - + Return the braid notation of ``self`` as an instance of :class:`~sage.groups.braid.Braid`. EXAMPLES:: diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index f110091f20b..eb3d51a883d 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -610,10 +610,17 @@ def __ne__(self, other): """ return not self.__eq__(other) - def braid(self): + def braid(self, remove_loops=False): r""" Return a braid representation of ``self``. + INPUT: + + - ``remove_loops`` -- boolean (default: ``False``). If set to ``True`` + loops will be removed first. This can reduce the number of strands + needed for an ambient isotopic braid closure. However, this can lead + to a loss of the regular isotopy. + OUTPUT: an element in the braid group .. WARNING:: @@ -634,6 +641,14 @@ def braid(self): sage: L.braid() (s0*s1^-1)^2*s1^-1 + using ``remove_loops=True``:: + + sage: L = Link([[2, 7, 1, 1], [7, 3, 9, 2], [4, 11, 3, 9], [11, 5, 5, 4]]) + sage: L.braid() + s0*s1^-1*s2*s3^-1 + sage: L.braid(remove_loops=True) + 1 + TESTS:: sage: L = Link([]) @@ -648,7 +663,20 @@ def braid(self): sage: A = Link([[[1, 2, -2, -1, -3, -4, 4, 3]], [1, 1, 1, 1]]) sage: A.braid() s0*s1*s2*s3 + + Check that :issue:`36884` is solved:: + + sage: L = Link([[1, 7, 2, 6], [3, 1, 4, 8], [5, 5, 6, 4], [7, 3, 8, 2]]) + sage: L.braid() + s0^3*s1*s0*s1^-1 + sage: L.braid(remove_loops=True) + s^3 """ + if remove_loops: + L = self.remove_loops() + if L != self: + return L.braid(remove_loops=remove_loops) + if self._braid is not None: return self._braid @@ -657,8 +685,8 @@ def braid(self): if len(comp) > 1: L1 = Link(comp[0]) L2 = Link(flatten(comp[1:], max_level=1)) - b1 = L1.braid() - b2 = L2.braid() + b1 = L1.braid(remove_loops=remove_loops) + b2 = L2.braid(remove_loops=remove_loops) n1 = b1.parent().strands() n2 = b2.parent().strands() t1 = list(b1.Tietze()) @@ -667,13 +695,26 @@ def braid(self): self._braid = B(t1 + t2) return self._braid - # look for possible Vogel moves, perform them and call recursively to the modified link pd_code = self.pd_code() if not pd_code: B = BraidGroup(2) self._braid = B.one() return self._braid + # look for possible Vogel moves, perform them and call recursively to the modified link + def idx(cross, edge): + r""" + Return the index of an edge in a crossing taking loops into account. + A loop appears as an edge which occurs twice in the crossing. + In all cases the second occurrence is the correct one needed in + the Vogel algorithm (see :issue:`36884`). + """ + i = cross.index(edge) + if cross.count(edge) > 1: + return cross.index(edge, i+1) + else: + return i + seifert_circles = self.seifert_circles() newedge = max(flatten(pd_code)) + 1 for region in self.regions(): @@ -702,12 +743,12 @@ def braid(self): # C1 C2 existing crossings # ------------------------------------------------- C1 = newPD[newPD.index(heads[a])] - C1[C1.index(a)] = newedge + 1 + C1[idx(C1, a)] = newedge + 1 C2 = newPD[newPD.index(tails[b])] - C2[C2.index(b)] = newedge + 2 + C2[idx(C2, b)] = newedge + 2 newPD.append([newedge + 3, newedge, b, a]) # D newPD.append([newedge + 2, newedge, newedge + 3, newedge + 1]) # E - self._braid = Link(newPD).braid() + self._braid = Link(newPD).braid(remove_loops=remove_loops) return self._braid else: # ------------------------------------------------- @@ -723,12 +764,12 @@ def braid(self): # / \ # ------------------------------------------------- C1 = newPD[newPD.index(heads[-a])] - C1[C1.index(-a)] = newedge + 1 + C1[idx(C1, -a)] = newedge + 1 C2 = newPD[newPD.index(tails[-b])] - C2[C2.index(-b)] = newedge + 2 + C2[idx(C2, -b)] = newedge + 2 newPD.append([newedge + 2, newedge + 1, newedge + 3, newedge]) # D newPD.append([newedge + 3, -a, -b, newedge]) # E - self._braid = Link(newPD).braid() + self._braid = Link(newPD).braid(remove_loops=remove_loops) return self._braid # We are in the case where no Vogel moves are necessary. @@ -2362,6 +2403,50 @@ def regions(self): regions.append(region) return regions + def remove_loops(self): + r""" + Return an ambient isotopic link in which all loops are removed. + + EXAMPLES:: + + sage: b = BraidGroup(4)((3, 2, -1, -1)) + sage: L = Link(b) + sage: L.remove_loops() + Link with 2 components represented by 2 crossings + sage: K4 = Link([[1, 7, 2, 6], [3, 1, 4, 8], [5, 5, 6, 4], [7, 3, 8, 2]]) + sage: K3 = K4.remove_loops() + sage: K3.pd_code() + [[1, 7, 2, 4], [3, 1, 4, 8], [7, 3, 8, 2]] + sage: U = Link([[1, 2, 2, 1]]) + sage: U.remove_loops() + Link with 1 component represented by 0 crossings + """ + pd = self.pd_code() + new_pd = [] + loop_crossings = [] + for cr in pd: + if len(set(cr)) == 4: + new_pd.append(list(cr)) + else: + loop_crossings.append(cr) + if not loop_crossings: + return self + if not new_pd: + # trivial knot + return type(self)([]) + new_edges = flatten(new_pd) + for cr in loop_crossings: + rem = set([e for e in cr if e in new_edges]) + if len(rem) == 2: + # put remaining edges together + a, b = sorted(rem) + for ncr in new_pd: + if b in ncr: + ncr[ncr.index(b)] = a + break + res = type(self)(new_pd) + return res.remove_loops() + @cached_method def mirror_image(self): r""" diff --git a/src/sage/libs/coxeter3/coxeter.pyx b/src/sage/libs/coxeter3/coxeter.pyx index 83cd1dccaee..0ef308c7aee 100644 --- a/src/sage/libs/coxeter3/coxeter.pyx +++ b/src/sage/libs/coxeter3/coxeter.pyx @@ -1,6 +1,7 @@ # distutils: language = c++ # distutils: libraries = coxeter3 # sage_setup: distribution = sagemath-coxeter3 +# sage.doctest: optional - coxeter3 """ Low level part of the interface to Fokko Ducloux's Coxeter 3 library @@ -35,10 +36,10 @@ cdef class String: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: s = String("hello"); s # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: s = String("hello"); s hello - sage: del s # optional - coxeter3 + sage: del s """ self.x = c_String(str_to_bytes(s)) @@ -46,9 +47,9 @@ cdef class String: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: s = String('Hi') # optional - coxeter3 - sage: s # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: s = String('Hi') + sage: s Hi """ return bytes_to_str(self.x.ptr()) @@ -62,9 +63,9 @@ cdef class String: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: s = String('hello') # optional - coxeter3 - sage: hash(s) == hash('hello') # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: s = String('hello') + sage: hash(s) == hash('hello') True """ return hash(repr(self)) @@ -73,17 +74,17 @@ cdef class String: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: ta1 = String('A') # optional - coxeter3 - sage: ta2 = String('A') # optional - coxeter3 - sage: tb = String('b') # optional - coxeter3 - sage: ta1 == ta2 # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: ta1 = String('A') + sage: ta2 = String('A') + sage: tb = String('b') + sage: ta1 == ta2 True - sage: tb != ta1 # optional - coxeter3 + sage: tb != ta1 True - sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1]) # optional - coxeter3 + sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1]) True - sage: all([tb > ta1, tb >= ta1, tb >= tb]) # optional - coxeter3 + sage: all([tb > ta1, tb >= ta1, tb >= tb]) True """ if type(other) is not type(self): @@ -113,9 +114,9 @@ cdef class String: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: s = String('Hi') # optional - coxeter3 - sage: len(s) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: s = String('Hi') + sage: len(s) 2 """ return self.x.length() @@ -124,9 +125,9 @@ cdef class String: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import String # optional - coxeter3 - sage: s = String('Hi') # optional - coxeter3 - sage: TestSuite(s).run() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import String + sage: s = String('Hi') + sage: TestSuite(s).run() """ return (String, (repr(self),) ) @@ -138,10 +139,10 @@ cdef class Type: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: t = Type('A'); t # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: t = Type('A'); t A - sage: del t # optional - coxeter3 + sage: del t """ self.x = c_Type(str_to_bytes(s)) @@ -149,8 +150,8 @@ cdef class Type: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: t = Type('A'); t # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: t = Type('A'); t A """ return bytes_to_str(self.x.name().ptr()) @@ -159,9 +160,9 @@ cdef class Type: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: t = Type('A') # optional - coxeter3 - sage: t.name() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: t = Type('A') + sage: t.name() A """ return String(bytes_to_str(self.x.name().ptr())) @@ -175,12 +176,12 @@ cdef class Type: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: a = Type('A') # optional - coxeter3 - sage: b = Type('B') # optional - coxeter3 - sage: hash(a) == hash(b) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: a = Type('A') + sage: b = Type('B') + sage: hash(a) == hash(b) False - sage: d = {a: 1, b: 2} # optional - coxeter3 + sage: d = {a: 1, b: 2} """ return hash(('Type', self.name())) @@ -188,17 +189,17 @@ cdef class Type: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: ta1 = Type('A') # optional - coxeter3 - sage: ta2 = Type('A') # optional - coxeter3 - sage: tb = Type('b') # optional - coxeter3 - sage: ta1 == ta2 # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: ta1 = Type('A') + sage: ta2 = Type('A') + sage: tb = Type('b') + sage: ta1 == ta2 True - sage: tb != ta1 # optional - coxeter3 + sage: tb != ta1 True - sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1]) # optional - coxeter3 + sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1]) True - sage: all([tb > ta1, tb >= ta1, tb >= tb]) # optional - coxeter3 + sage: all([tb > ta1, tb >= ta1, tb >= tb]) True """ if type(other) is not type(self): @@ -226,25 +227,26 @@ cdef class Type: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import Type # optional - coxeter3 - sage: t = Type('A') # optional - coxeter3 - sage: TestSuite(t).run() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import Type + sage: t = Type('A') + sage: TestSuite(t).run() """ return (Type, (repr(self), )) + cdef class CoxGroup(SageObject): def __cinit__(self, cartan_type): """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]); W # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]); W Coxeter group of type A and rank 5 Coxeter 3 segfault's on the trivial Coxeter group; so we catch this and raise a not implemented error:: - sage: W = CoxGroup(['A', 0]); W # optional - coxeter3 + sage: W = CoxGroup(['A', 0]); W Traceback (most recent call last): ... NotImplementedError: Coxeter group of type ['A',0] using Coxeter 3 not yet implemented @@ -252,8 +254,8 @@ cdef class CoxGroup(SageObject): Successfully initializes from a relabeled Cartan type:: sage: ctype = CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1}) - sage: W = CoxGroup(ctype) # optional - coxeter3 - sage: CoxeterMatrix(W.coxeter_matrix(), ctype.index_set()) == CoxeterMatrix(ctype) # optional - coxeter3 + sage: W = CoxGroup(ctype) + sage: CoxeterMatrix(W.coxeter_matrix(), ctype.index_set()) == CoxeterMatrix(ctype) True """ from sage.combinat.root_system.cartan_type import CartanType @@ -303,9 +305,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W._ordering_from_cartan_type(CartanType(['A',5])) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W._ordering_from_cartan_type(CartanType(['A',5])) [1, 2, 3, 4, 5] """ from sage.arith.srange import srange @@ -340,9 +342,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: A4 = CoxGroup(['A', 4]) # optional - coxeter3 - sage: d = {A4: True} # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: A4 = CoxGroup(['A', 4]) + sage: d = {A4: True} """ return hash((self.__class__.__name__, self.type(), self.rank())) @@ -350,21 +352,21 @@ cdef class CoxGroup(SageObject): """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: A4 = CoxGroup(['A', 4]) # optional - coxeter3 - sage: A5 = CoxGroup(['A', 5]) # optional - coxeter3 - sage: B4 = CoxGroup(['B', 4]) # optional - coxeter3 - sage: A4 == A4 # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: A4 = CoxGroup(['A', 4]) + sage: A5 = CoxGroup(['A', 5]) + sage: B4 = CoxGroup(['B', 4]) + sage: A4 == A4 True - sage: A4 != B4 # optional - coxeter3 + sage: A4 != B4 True - sage: A4 < B4 # optional - coxeter3 + sage: A4 < B4 True - sage: A5 > A4 # optional - coxeter3 + sage: A5 > A4 True - sage: A4 >= A4 # optional - coxeter3 + sage: A4 >= A4 True - sage: B4 >= A5 # optional - coxeter3 + sage: B4 >= A5 True """ if type(other) is not type(self): @@ -394,9 +396,9 @@ cdef class CoxGroup(SageObject): """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: TestSuite((W)).run() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: TestSuite((W)).run() """ return (CoxGroup, (self.cartan_type,)) @@ -406,9 +408,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: del W # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: del W """ del self.x @@ -418,8 +420,8 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]); W # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]); W Coxeter group of type A and rank 5 """ return "Coxeter group of type %s and rank %s"%(self.type(), self.rank()) @@ -428,9 +430,9 @@ cdef class CoxGroup(SageObject): """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: list(iter(W)) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 2]) + sage: list(iter(W)) [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]] """ return CoxGroupIterator(self) @@ -441,9 +443,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: W.bruhat_interval([], [1,2]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 2]) + sage: W.bruhat_interval([], [1,2]) [[], [1], [2], [1, 2]] """ cdef CoxGroupElement ww = CoxGroupElement(self, w) @@ -469,9 +471,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.orderings() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.orderings() ({1: 1, 2: 2, 3: 3, 4: 4, 5: 5}, {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}) """ return self.in_ordering, self.out_ordering @@ -484,9 +486,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.type() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.type() A """ return Type(bytes_to_str(self.x.type().name().ptr())) @@ -497,9 +499,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.rank() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.rank() 5 """ return self.x.rank() @@ -510,12 +512,12 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.order() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.order() 720 - sage: W = CoxGroup(['A', 3, 1]) # optional - coxeter3 - sage: W.order() # optional - coxeter3 + sage: W = CoxGroup(['A', 3, 1]) + sage: W.order() +Infinity """ if self.is_finite(): @@ -530,12 +532,12 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.is_finite() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.is_finite() True - sage: W = CoxGroup(['A', 3, 1]) # optional - coxeter3 - sage: W.is_finite() # optional - coxeter3 + sage: W = CoxGroup(['A', 3, 1]) + sage: W.is_finite() False """ return isFiniteType(self.x) @@ -548,11 +550,11 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: W.full_context() # optional - coxeter3 - sage: W = CoxGroup(['A', 2,1]) # optional - coxeter3 - sage: W.full_context() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 2]) + sage: W.full_context() + sage: W = CoxGroup(['A', 2,1]) + sage: W.full_context() Traceback (most recent call last): ... TypeError: group needs to be finite @@ -563,20 +565,19 @@ cdef class CoxGroup(SageObject): if not fcoxgroup.isFullContext(): fcoxgroup.fullContext() - def long_element(self): """ Return the longest word in a finite Coxeter group. EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.long_element() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.long_element() [1, 2, 1, 3, 2, 1, 4, 3, 2, 1, 5, 4, 3, 2, 1] - sage: W = CoxGroup(['A', 3, 1]) # optional - coxeter3 - sage: W.long_element() # optional - coxeter3 + sage: W = CoxGroup(['A', 3, 1]) + sage: W.long_element() Traceback (most recent call last): ... TypeError: group needs to be finite @@ -601,10 +602,10 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: w = [1,1,3,5,4,5,4] # optional - coxeter3 - sage: W.__call__(w) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: w = [1,1,3,5,4,5,4] + sage: W.__call__(w) [3, 4, 5] """ return CoxGroupElement(self, w).reduced() @@ -615,9 +616,9 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.coxeter_matrix() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.coxeter_matrix() [1 3 2 2 2] [3 1 3 2 2] [2 3 1 3 2] @@ -649,11 +650,11 @@ cdef class CoxGroup(SageObject): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup# optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W.coxeter_graph() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W.coxeter_graph() Graph on 5 vertices - sage: W.coxeter_graph().edges(sort=True) # optional - coxeter3 + sage: W.coxeter_graph().edges(sort=True) [(1, 2, None), (2, 3, None), (3, 4, None), (4, 5, None)] """ from sage.graphs.graph import Graph @@ -669,26 +670,25 @@ cdef class CoxGroup(SageObject): return g - cdef class CoxGroupElement: def __init__(self, CoxGroup group, w, normal_form=True): """ TESTS:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement + sage: W = CoxGroup(['A', 5]) + sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w [2, 1, 2, 1, 1] - sage: w = CoxGroupElement(W, [1,1,4,5,4], normal_form=False); w # optional - coxeter3 + sage: w = CoxGroupElement(W, [1,1,4,5,4], normal_form=False); w [1, 1, 4, 5, 4] - sage: w = CoxGroupElement(W, [1,1,4,5,4]); w # optional - coxeter3 + sage: w = CoxGroupElement(W, [1,1,4,5,4]); w [4, 5, 4] - sage: W = CoxGroup(['A', 4]) # optional - coxeter3 - sage: CoxGroupElement(W, [1,2,3,2,3]) # optional - coxeter3 + sage: W = CoxGroup(['A', 4]) + sage: CoxGroupElement(W, [1,2,3,2,3]) [1, 3, 2] - sage: W = CoxGroup(['A', 4]) # optional - coxeter3 - sage: w = CoxGroupElement(W, [1,2,3,2,3]) # optional - coxeter3 - sage: del w # optional - coxeter3 + sage: W = CoxGroup(['A', 4]) + sage: w = CoxGroupElement(W, [1,2,3,2,3]) + sage: del w """ self.group = (group).x self._parent_group = group @@ -705,10 +705,10 @@ cdef class CoxGroupElement: TESTS:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement # optional - coxeter3 - sage: W = CoxGroup(['A', 4]) # optional - coxeter3 - sage: w = CoxGroupElement(W, [1,2,3,2,3]) # optional - coxeter3 - sage: w._coxnumber() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement + sage: W = CoxGroup(['A', 4]) + sage: w = CoxGroupElement(W, [1,2,3,2,3]) + sage: w._coxnumber() 7 """ return int(self.group.extendContext(self.word)) @@ -717,10 +717,10 @@ cdef class CoxGroupElement: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: TestSuite(w).run() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: TestSuite(w).run() """ return (CoxGroupElement, (self._parent_group, list(self))) @@ -730,11 +730,10 @@ cdef class CoxGroupElement: EXAMPLES:: - - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: ~w # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: ~w [3, 2, 1] """ return CoxGroupElement(self._parent_group, reversed(self)) @@ -747,10 +746,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: w.parent_group() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: w.parent_group() Coxeter group of type A and rank 5 """ @@ -762,20 +761,20 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: w[0] # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: w[0] 1 - sage: w[2] # optional - coxeter3 + sage: w[2] 3 - sage: w[:-2] # optional - coxeter3 + sage: w[:-2] [1] - sage: w[-2:] # optional - coxeter3 + sage: w[-2:] [2, 3] - sage: w[3:0:-1] # optional - coxeter3 + sage: w[3:0:-1] [3, 2] - sage: w[4] # optional - coxeter3 + sage: w[4] Traceback (most recent call last): ... IndexError: The index (4) is out of range. @@ -796,9 +795,9 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]); w # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]); w [1, 2, 3] """ @@ -813,11 +812,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: v = W([2,3,4]) # optional - coxeter3 - sage: hash(w) == hash(v) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A', 5]) + sage: w = W([1,2,3]) + sage: v = W([2,3,4]) + sage: hash(w) == hash(v) False """ return hash((self.__class__.__name__, self.parent_group(), tuple(self))) @@ -826,23 +825,23 @@ cdef class CoxGroupElement: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: V = CoxGroup(['A', 6]) # optional - coxeter3 - sage: w1 = W([1,2,3]) # optional - coxeter3 - sage: w2 = W([2,3,4]) # optional - coxeter3 - sage: v1 = V([1,2,3]) # optional - coxeter3 - sage: w1 == w1 # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A', 5]) + sage: V = CoxGroup(['A', 6]) + sage: w1 = W([1,2,3]) + sage: w2 = W([2,3,4]) + sage: v1 = V([1,2,3]) + sage: w1 == w1 True - sage: w1 != w2 # optional - coxeter3 + sage: w1 != w2 True - sage: all([w1 < w2, w1 <= w2, w1 <= w1]) # optional - coxeter3 + sage: all([w1 < w2, w1 <= w2, w1 <= w1]) True - sage: all([w2 > w1, w2 >= w1, w2 >= w2]) # optional - coxeter3 + sage: all([w2 > w1, w2 >= w1, w2 >= w2]) True - sage: w1 == v1 # optional - coxeter3 + sage: w1 == v1 False - sage: w1 != v1 # optional - coxeter3 + sage: w1 != v1 True """ if type(other) is not type(self): @@ -874,10 +873,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: [a for a in w] # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: [a for a in w] [1, 2, 3] """ return (self[i] for i in range(len(self))) @@ -888,10 +887,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: len(w) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3]) + sage: len(w) 3 """ return self.word.length() @@ -902,10 +901,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,1]) # optional - coxeter3 - sage: w.left_descents() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,1]) + sage: w.left_descents() [1, 2] """ return LFlags_to_list(self._parent_group, self.group.ldescent(self.word)) @@ -916,10 +915,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,1]) # optional - coxeter3 - sage: w.right_descents() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,1]) + sage: w.right_descents() [1, 2] """ return LFlags_to_list(self._parent_group, self.group.rdescent(self.word)) @@ -930,15 +929,15 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3,4,5,4]) # optional - coxeter3 - sage: v = W([1,2,4,5,4]) # optional - coxeter3 - sage: v.bruhat_le(w) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3,4,5,4]) + sage: v = W([1,2,4,5,4]) + sage: v.bruhat_le(w) True - sage: w.bruhat_le(w) # optional - coxeter3 + sage: w.bruhat_le(w) True - sage: w.bruhat_le(v) # optional - coxeter3 + sage: w.bruhat_le(v) False """ cdef CoxGroupElement ww = CoxGroupElement(self._parent_group, w) @@ -950,10 +949,10 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',2]) # optional - coxeter3 - sage: x = W([1,2,1]) # optional - coxeter3 - sage: x.is_two_sided_descent(1) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',2]) + sage: x = W([1,2,1]) + sage: x.is_two_sided_descent(1) True """ cdef Generator ss = self._parent_group.in_ordering[s] @@ -973,11 +972,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',2]) # optional - coxeter3 - sage: W([1,2,1]).coatoms() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',2]) + sage: W([1,2,1]).coatoms() [[2, 1], [1, 2]] - sage: W([]).coatoms() # optional - coxeter3 + sage: W([]).coatoms() [] """ cdef c_List_CoxWord list = c_List_CoxWord(0) @@ -1002,11 +1001,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: w = CoxGroupElement(W, [2,1,2], normal_form=False); w # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement + sage: W = CoxGroup(['A', 5]) + sage: w = CoxGroupElement(W, [2,1,2], normal_form=False); w [2, 1, 2] - sage: w.normal_form() # optional - coxeter3 + sage: w.normal_form() [1, 2, 1] """ @@ -1020,11 +1019,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement + sage: W = CoxGroup(['A', 5]) + sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w [2, 1, 2, 1, 1] - sage: w.reduced() # optional - coxeter3 + sage: w.reduced() [1, 2, 1] """ cdef CoxGroupElement res = self._new() @@ -1035,11 +1034,11 @@ cdef class CoxGroupElement: """ EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W([1]) * W([1]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W([1]) * W([1]) [] - sage: W([1,2]) * W([1]) # optional - coxeter3 + sage: W([1,2]) * W([1]) [1, 2, 1] """ cdef CoxGroupElement res = self._new() @@ -1053,11 +1052,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 5]) # optional - coxeter3 - sage: W([]).poincare_polynomial() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 5]) + sage: W([]).poincare_polynomial() 1 - sage: W([1,2,1]).poincare_polynomial() # optional - coxeter3 + sage: W([1,2,1]).poincare_polynomial() t^3 + 2*t^2 + 2*t + 1 """ cdef CoxGroup W = self.parent_group() @@ -1072,7 +1071,6 @@ cdef class CoxGroupElement: coefficients[result[j].length()] += 1 return ZZ['t'](coefficients) - def kazhdan_lusztig_polynomial(self, v): """ Return the Kazhdan-Lusztig polynomial `P_{u,v}` where `u` is ``self``. @@ -1082,11 +1080,11 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: W([]).kazhdan_lusztig_polynomial([1,2,1]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup + sage: W = CoxGroup(['A', 2]) + sage: W([]).kazhdan_lusztig_polynomial([1,2,1]) 1 - sage: W([1,2,1]).kazhdan_lusztig_polynomial([]) # optional - coxeter3 + sage: W([1,2,1]).kazhdan_lusztig_polynomial([]) 0 """ cdef CoxGroupElement vv @@ -1114,15 +1112,15 @@ cdef class CoxGroupElement: EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,3,4,5,4]) # optional - coxeter3 - sage: v = W([1,2,4,5,4]) # optional - coxeter3 - sage: w.mu_coefficient(v) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,3,4,5,4]) + sage: v = W([1,2,4,5,4]) + sage: w.mu_coefficient(v) 0 - sage: w.mu_coefficient(w) # optional - coxeter3 + sage: w.mu_coefficient(w) 0 - sage: v.mu_coefficient(w) # optional - coxeter3 + sage: v.mu_coefficient(w) 1 """ cdef CoxGroupElement vv = CoxGroupElement(self._parent_group, v) @@ -1130,16 +1128,17 @@ cdef class CoxGroupElement: cdef CoxNbr y = self.group.extendContext(vv.word) return ZZ(self.group.mu(x,y)) + cdef LFlags_to_list(CoxGroup parent, LFlags f) noexcept: """ Return the right descent set of this element. EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import * # optional - coxeter3 - sage: W = CoxGroup(['A',5]) # optional - coxeter3 - sage: w = W([1,2,1]) # optional - coxeter3 - sage: w.right_descents() # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import * + sage: W = CoxGroup(['A',5]) + sage: w = W([1,2,1]) + sage: w.right_descents() [1, 2] """ cdef Generator s @@ -1151,6 +1150,7 @@ cdef LFlags_to_list(CoxGroup parent, LFlags f) noexcept: f1 = f1 & (f1-1) return l + class CoxGroupIterator(): def __init__(self, group): """ @@ -1164,10 +1164,10 @@ class CoxGroupIterator(): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: it = CoxGroupIterator(W) # optional - coxeter3 - sage: [next(it) for i in range(W.order())] # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator + sage: W = CoxGroup(['A', 2]) + sage: it = CoxGroupIterator(W) + sage: [next(it) for i in range(W.order())] [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]] """ self.group = group @@ -1181,10 +1181,10 @@ class CoxGroupIterator(): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: it = iter(W) # optional - coxeter3 - sage: it is iter(it) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator + sage: W = CoxGroup(['A', 2]) + sage: it = iter(W) + sage: it is iter(it) True """ return self @@ -1195,10 +1195,10 @@ class CoxGroupIterator(): EXAMPLES:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 - sage: it = CoxGroupIterator(W) # optional - coxeter3 - sage: next(it) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator + sage: W = CoxGroup(['A', 2]) + sage: it = CoxGroupIterator(W) + sage: next(it) [] """ if self.n >= self.order: @@ -1217,8 +1217,8 @@ def get_CoxGroup(cartan_type): """ TESTS:: - sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator # optional - coxeter3 - sage: W = CoxGroup(['A', 2]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator + sage: W = CoxGroup(['A', 2]) """ from sage.combinat.root_system.cartan_type import CartanType cartan_type = CartanType(cartan_type) diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index bb5a3bc024a..fd5f93ad506 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -1,14 +1,15 @@ # sage_setup: distribution = sagemath-coxeter3 +# sage.doctest: optional - coxeter3 """ Coxeter Groups implemented with Coxeter3 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009-2013 Mike Hansen # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.libs.coxeter3.coxeter import get_CoxGroup, CoxGroupElement from sage.misc.cachefunc import cached_method @@ -31,10 +32,10 @@ def __classcall__(cls, cartan_type, *args, **options): """ TESTS:: - sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup # optional - coxeter3 - sage: CoxeterGroup(['B',2]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup + sage: CoxeterGroup(['B',2]) Coxeter group of type ['B', 2] implemented by Coxeter3 - sage: CoxeterGroup(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) # optional - coxeter3 + sage: CoxeterGroup(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) Coxeter group of type ['B', 3] relabelled by {1: 3, 2: 2, 3: 1} implemented by Coxeter3 """ @@ -46,15 +47,15 @@ def __init__(self, cartan_type): """ TESTS:: - sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup # optional - coxeter3 - sage: CoxeterGroup(['A',2]) # optional - coxeter3 + sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup + sage: CoxeterGroup(['A',2]) Coxeter group of type ['A', 2] implemented by Coxeter3 As degrees and codegrees are not implemented, they are skipped in the testsuite:: sage: to_skip = ['_test_degrees', '_test_codegrees'] - sage: TestSuite(CoxeterGroup(['A',2])).run(skip=to_skip) # optional - coxeter3 + sage: TestSuite(CoxeterGroup(['A',2])).run(skip=to_skip) """ category = CoxeterGroups() if cartan_type.is_finite(): @@ -67,9 +68,9 @@ def _repr_(self): """ EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3'); W # optional - coxeter3 # indirect doctest + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3'); W # indirect doctest Coxeter group of type ['A', 3] implemented by Coxeter3 - sage: W = CoxeterGroup(['A', 3, 1], implementation='coxeter3'); W # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3, 1], implementation='coxeter3'); W Coxeter group of type ['A', 3, 1] implemented by Coxeter3 """ return "Coxeter group of type %s implemented by Coxeter3" % (self.cartan_type()) @@ -78,8 +79,8 @@ def __iter__(self): """ EXAMPLES:: - sage: W = CoxeterGroup(['A', 2], implementation='coxeter3') # optional - coxeter3 - sage: list(W) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 2], implementation='coxeter3') + sage: list(W) [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]] """ for x in self._coxgroup: @@ -91,8 +92,8 @@ def cartan_type(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.cartan_type() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.cartan_type() ['A', 3] """ return self._cartan_type @@ -103,11 +104,11 @@ def index_set(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.index_set() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.index_set() (1, 2, 3) - sage: C = CoxeterGroup(['A', 3,1], implementation='coxeter3') # optional - coxeter3 - sage: C.index_set() # optional - coxeter3 + sage: C = CoxeterGroup(['A', 3,1], implementation='coxeter3') + sage: C.index_set() (0, 1, 2, 3) """ return self.cartan_type().index_set() @@ -118,8 +119,8 @@ def bruhat_interval(self, u, v): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.bruhat_interval([1],[3,1,2,3]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.bruhat_interval([1],[3,1,2,3]) [[1], [1, 2], [1, 3], [1, 2, 3], [1, 3, 2], [1, 2, 3, 2]] """ u, v = self(u), self(v) @@ -131,8 +132,8 @@ def cardinality(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.cardinality() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.cardinality() 24 """ return self._coxgroup.order() @@ -143,8 +144,8 @@ def one(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.one() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.one() [] """ @@ -156,9 +157,9 @@ def simple_reflections(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: s = W.simple_reflections() # optional - coxeter3 - sage: s[2]*s[1]*s[2] # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: s = W.simple_reflections() + sage: s[2]*s[1]*s[2] [1, 2, 1] """ from sage.sets.family import Family @@ -172,10 +173,10 @@ def from_reduced_word(self, w): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.from_reduced_word([1, 3]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.from_reduced_word([1, 3]) [1, 3] - sage: W.from_reduced_word([3, 1]) # optional - coxeter3 + sage: W.from_reduced_word([3, 1]) [1, 3] """ return self.element_class(self, w) @@ -186,8 +187,8 @@ def rank(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.rank() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.rank() 3 """ return self._coxgroup.rank() @@ -198,8 +199,8 @@ def is_finite(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.is_finite() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.is_finite() True """ return self._coxgroup.is_finite() @@ -211,10 +212,10 @@ def length(self, x): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.length(W([1,2])) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.length(W([1,2])) 2 - sage: W.length(W([1,1])) # optional - coxeter3 + sage: W.length(W([1,1])) 0 """ @@ -230,12 +231,12 @@ def coxeter_matrix(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: m = W.coxeter_matrix(); m # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: m = W.coxeter_matrix(); m [1 3 2] [3 1 3] [2 3 1] - sage: m.index_set() == W.index_set() # optional - coxeter3 + sage: m.index_set() == W.index_set() True """ @@ -247,11 +248,11 @@ def root_system(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: R = W.root_system(); R # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: R = W.root_system(); R Root system of type ['A', 3] - sage: alpha = R.root_space().basis() # optional - coxeter3 - sage: alpha[2] + alpha[3] # optional - coxeter3 + sage: alpha = R.root_space().basis() + sage: alpha[2] + alpha[3] alpha[2] + alpha[3] """ return self.cartan_type().root_system() @@ -262,8 +263,8 @@ def _an_element_(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W._an_element_() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W._an_element_() [] """ @@ -275,8 +276,8 @@ def m(self, i, j): TESTS:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.m(1, 1) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.m(1, 1) doctest:warning...: DeprecationWarning: the .m(i, j) method has been deprecated; use .coxeter_matrix()[i,j] instead. See https://github.com/sagemath/sage/issues/30237 for details. @@ -303,13 +304,13 @@ def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.kazhdan_lusztig_polynomial([], [1,2, 1]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W.kazhdan_lusztig_polynomial([], [1,2, 1]) 1 - sage: W.kazhdan_lusztig_polynomial([1],[3,2]) # optional - coxeter3 + sage: W.kazhdan_lusztig_polynomial([1],[3,2]) 0 - sage: W = CoxeterGroup(['A',3],implementation='coxeter3') # optional - coxeter3 - sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2]) # optional - coxeter3 + sage: W = CoxeterGroup(['A',3],implementation='coxeter3') + sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2]) q + 1 .. NOTE:: @@ -325,7 +326,7 @@ def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): In particular, `P_{u,u}=1`:: - sage: all(W.kazhdan_lusztig_polynomial(u,u) == 1 for u in W) # optional - coxeter3 + sage: all(W.kazhdan_lusztig_polynomial(u,u) == 1 for u in W) True This convention differs from Theorem 2.7 in [LT1998]_ by: @@ -336,20 +337,20 @@ def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): To access the Leclerc-Thibon convention use:: - sage: W = CoxeterGroup(['A',3],implementation='coxeter3') # optional - coxeter3 - sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2],constant_term_one=False) # optional - coxeter3 + sage: W = CoxeterGroup(['A',3],implementation='coxeter3') + sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2],constant_term_one=False) q^3 + q TESTS: We check that Coxeter3 and Sage's implementation give the same results:: - sage: C = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 + sage: C = CoxeterGroup(['B', 3], implementation='coxeter3') sage: W = WeylGroup("B3",prefix="s") sage: [s1,s2,s3] = W.simple_reflections() sage: R. = LaurentPolynomialRing(QQ) sage: KL = KazhdanLusztigPolynomial(W,q) - sage: all(KL.P(1,w) == C.kazhdan_lusztig_polynomial([],w.reduced_word()) for w in W) # optional - coxeter3 # long (15s) + sage: all(KL.P(1,w) == C.kazhdan_lusztig_polynomial([],w.reduced_word()) for w in W) # long (15s) True """ u, v = self(u), self(v) @@ -384,30 +385,30 @@ def parabolic_kazhdan_lusztig_polynomial(self, u, v, J, constant_term_one=True): EXAMPLES:: - sage: W = CoxeterGroup(['A',3], implementation='coxeter3') # optional - coxeter3 - sage: W.parabolic_kazhdan_lusztig_polynomial([],[3,2],[1,3]) # optional - coxeter3 + sage: W = CoxeterGroup(['A',3], implementation='coxeter3') + sage: W.parabolic_kazhdan_lusztig_polynomial([],[3,2],[1,3]) 0 - sage: W.parabolic_kazhdan_lusztig_polynomial([2],[2,1,3,2],[1,3]) # optional - coxeter3 + sage: W.parabolic_kazhdan_lusztig_polynomial([2],[2,1,3,2],[1,3]) q - sage: C = CoxeterGroup(['A',3,1], implementation='coxeter3') # optional - coxeter3 - sage: C.parabolic_kazhdan_lusztig_polynomial([],[1],[0]) # optional - coxeter3 + sage: C = CoxeterGroup(['A',3,1], implementation='coxeter3') + sage: C.parabolic_kazhdan_lusztig_polynomial([],[1],[0]) 1 - sage: C.parabolic_kazhdan_lusztig_polynomial([],[1,2,1],[0]) # optional - coxeter3 + sage: C.parabolic_kazhdan_lusztig_polynomial([],[1,2,1],[0]) 1 - sage: C.parabolic_kazhdan_lusztig_polynomial([],[0,1,0,1,2,1],[0]) # optional - coxeter3 + sage: C.parabolic_kazhdan_lusztig_polynomial([],[0,1,0,1,2,1],[0]) q sage: w=[1, 2, 1, 3, 0, 2, 1, 0, 3, 0, 2] sage: v=[1, 2, 1, 3, 0, 1, 2, 1, 0, 3, 0, 2, 1, 0, 3, 0, 2] - sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3]) # optional - coxeter3 + sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3]) q^2 + q - sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3],constant_term_one=False) # optional - coxeter3 + sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3],constant_term_one=False) q^4 + q^2 TESTS:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: type(W.parabolic_kazhdan_lusztig_polynomial([2],[],[1])) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: type(W.parabolic_kazhdan_lusztig_polynomial([2],[],[1])) """ u = self(u) @@ -431,19 +432,19 @@ def __init__(self, parent, x): """ TESTS:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W([2,1,2]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W([2,1,2]) [1, 2, 1] Check that :trac:`32266` is fixed:: - sage: A3 = CoxeterGroup('A3', implementation='coxeter3') # optional - coxeter3 - sage: s1,s2,s3 = A3.simple_reflections() # optional - coxeter3 - sage: s1*s3 # optional - coxeter3 + sage: A3 = CoxeterGroup('A3', implementation='coxeter3') + sage: s1,s2,s3 = A3.simple_reflections() + sage: s1*s3 [1, 3] - sage: s3*s1 # optional - coxeter3 + sage: s3*s1 [1, 3] - sage: s3*s1 == s1*s3 # optional - coxeter3 + sage: s3*s1 == s1*s3 True """ if not isinstance(x, CoxGroupElement): @@ -457,9 +458,9 @@ def __iter__(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,1]) # optional - coxeter3 - sage: list(iter(w)) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: w = W([1,2,1]) + sage: list(iter(w)) [1, 2, 1] """ return iter(self.value) @@ -470,9 +471,9 @@ def coatoms(self): EXAMPLES:: - sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: w.coatoms() # optional - coxeter3 + sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') + sage: w = W([1,2,3]) + sage: w.coatoms() [[2, 3], [3, 1], [1, 2]] """ W = self.parent() @@ -484,20 +485,20 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: v = W([3,1,2]) # optional - coxeter3 - sage: v < w # optional - coxeter3 + sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') + sage: w = W([1,2,3]) + sage: v = W([3,1,2]) + sage: v < w False - sage: w < v # optional - coxeter3 + sage: w < v True Some tests for equality:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W([1,2,1]) == W([2,1,2]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W([1,2,1]) == W([2,1,2]) True - sage: W([1,2,1]) == W([2,1]) # optional - coxeter3 + sage: W([1,2,1]) == W([2,1]) False """ return richcmp(list(self), list(other), op) @@ -508,9 +509,9 @@ def reduced_word(self): EXAMPLES:: - sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: w.reduced_word() # optional - coxeter3 + sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') + sage: w = W([1,2,3]) + sage: w.reduced_word() [1, 2, 3] """ return list(self) @@ -521,9 +522,9 @@ def __invert__(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,3]) # optional - coxeter3 - sage: ~w # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: w = W([1,2,3]) + sage: ~w [3, 2, 1] """ return self.__class__(self.parent(), ~self.value) @@ -534,11 +535,11 @@ def __getitem__(self, i): """ EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: w0 = W([1,2,1]) # optional - coxeter3 - sage: w0[0] # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: w0 = W([1,2,1]) + sage: w0[0] 1 - sage: w0[1] # optional - coxeter3 + sage: w0[1] 2 """ @@ -549,13 +550,13 @@ def _mul_(self, y): """ EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: s = W.gens() # optional - coxeter3 - sage: s[1]._mul_(s[1]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: s = W.gens() + sage: s[1]._mul_(s[1]) [] - sage: s[1]*s[2]*s[1] # optional - coxeter3 + sage: s[1]*s[2]*s[1] [1, 2, 1] - sage: s[2]*s[1]*s[2] # optional - coxeter3 + sage: s[2]*s[1]*s[2] [1, 2, 1] """ return self.__class__(self.parent(), self.value * y.value) @@ -564,11 +565,11 @@ def __len__(self): """ EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: w = W([1,2,1]) # optional - coxeter3 - sage: w.length() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: w = W([1,2,1]) + sage: w.length() 3 - sage: len(w) # optional - coxeter3 + sage: len(w) 3 """ return len(self.value) @@ -581,8 +582,8 @@ def bruhat_le(self, v): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W([]).bruhat_le([1,2,1]) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W([]).bruhat_le([1,2,1]) True """ v = self.parent()(v) @@ -594,18 +595,17 @@ def poincare_polynomial(self): EXAMPLES:: - sage: W = CoxeterGroup(['A', 2], implementation='coxeter3') # optional - coxeter3 - sage: W.long_element().poincare_polynomial() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 2], implementation='coxeter3') + sage: W.long_element().poincare_polynomial() t^3 + 2*t^2 + 2*t + 1 - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W([2,1,3,2]).poincare_polynomial() # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: W([2,1,3,2]).poincare_polynomial() t^4 + 4*t^3 + 5*t^2 + 3*t + 1 - sage: W([1,2,3,2,1]).poincare_polynomial() # optional - coxeter3 + sage: W([1,2,3,2,1]).poincare_polynomial() t^5 + 4*t^4 + 6*t^3 + 5*t^2 + 3*t + 1 - - sage: rw = sage.combinat.permutation.from_reduced_word # optional - coxeter3 - sage: p = [w.poincare_polynomial() for w in W] # optional - coxeter3 - sage: [rw(w.reduced_word()) for i,w in enumerate(W) if p[i] != p[i].reverse()] # optional - coxeter3 + sage: rw = sage.combinat.permutation.from_reduced_word + sage: p = [w.poincare_polynomial() for w in W] + sage: [rw(w.reduced_word()) for i,w in enumerate(W) if p[i] != p[i].reverse()] [[3, 4, 1, 2], [4, 2, 3, 1]] """ return self.value.poincare_polynomial() @@ -616,10 +616,10 @@ def has_right_descent(self, i): EXAMPLES:: - sage: W = CoxeterGroup(['A', 4], implementation='coxeter3') # optional - coxeter3 - sage: W([1,2]).has_right_descent(1) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 4], implementation='coxeter3') + sage: W([1,2]).has_right_descent(1) False - sage: W([1,2]).has_right_descent(2) # optional - coxeter3 + sage: W([1,2]).has_right_descent(2) True """ return i in self.value.right_descents() @@ -630,10 +630,10 @@ def has_left_descent(self, i): EXAMPLES:: - sage: W = CoxeterGroup(['A', 4], implementation='coxeter3') # optional - coxeter3 - sage: W([1,2]).has_left_descent(1) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 4], implementation='coxeter3') + sage: W([1,2]).has_left_descent(1) True - sage: W([1,2]).has_left_descent(2) # optional - coxeter3 + sage: W([1,2]).has_left_descent(2) False """ return i in self.value.left_descents() @@ -648,15 +648,15 @@ def action(self, v): EXAMPLES:: - sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 - sage: R = W.root_system().root_space() # optional - coxeter3 - sage: v = R.an_element(); v # optional - coxeter3 + sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') + sage: R = W.root_system().root_space() + sage: v = R.an_element(); v 2*alpha[1] + 2*alpha[2] + 3*alpha[3] - sage: w = W([1,2,3]) # optional - coxeter3 - sage: w.action(v) # optional - coxeter3 + sage: w = W([1,2,3]) + sage: w.action(v) -alpha[1] + alpha[2] + alpha[3] """ - #TODO: Find a better way to do this + # TODO: Find a better way to do this W = self.parent().root_system().root_space().weyl_group() w = W.from_reduced_word(list(self)) return w.action(v) @@ -675,14 +675,14 @@ def action_on_rational_function(self, f): EXAMPLES:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: S = PolynomialRing(QQ, 'x,y,z').fraction_field() # optional - coxeter3 - sage: x,y,z = S.gens() # optional - coxeter3 - sage: W([1]).action_on_rational_function(x+y+z) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') + sage: S = PolynomialRing(QQ, 'x,y,z').fraction_field() + sage: x,y,z = S.gens() + sage: W([1]).action_on_rational_function(x+y+z) (x^2*y + x*z + 1)/x - sage: W([2]).action_on_rational_function(x+y+z) # optional - coxeter3 + sage: W([2]).action_on_rational_function(x+y+z) (x*y^2 + y^2*z + 1)/y - sage: W([3]).action_on_rational_function(x+y+z) # optional - coxeter3 + sage: W([3]).action_on_rational_function(x+y+z) (y*z^2 + x*z + 1)/z """ Q = f.parent() @@ -693,7 +693,9 @@ def action_on_rational_function(self, f): n = W.rank() if Q.ngens() != n: - raise ValueError("the number of generators for the polynomial ring must be the same as the rank of the root system") + raise ValueError("the number of generators for the polynomial " + "ring must be the same as the rank of the " + "root system") basis_elements = [alpha[i] for i in W.index_set()] basis_to_order = {s: i for i, s in enumerate(W.index_set())} @@ -704,7 +706,7 @@ def action_on_rational_function(self, f): exponents = poly.exponents() for exponent in exponents: - #Construct something in the root lattice from the exponent vector + # Construct something in the root lattice from the exponent vector exponent = sum(e*b for e, b in zip(exponent, basis_elements)) exponent = self.action(exponent) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index e522bf5bc03..1f0f451a2c2 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -553,7 +553,7 @@ cdef class GapElement(RingElement): INPUT: - - ``mut`` - (boolean) whether to return an mutable copy + - ``mut`` - (boolean) whether to return a mutable copy EXAMPLES:: @@ -1778,7 +1778,7 @@ cdef class GapElement_FiniteField(GapElement): OUTPUT: - An Sage finite field element. The isomorphism is chosen such + A Sage finite field element. The isomorphism is chosen such that the Gap ``PrimitiveRoot()`` maps to the Sage :meth:`~sage.rings.finite_rings.finite_field_prime_modn.multiplicative_generator`. diff --git a/src/sage/libs/gap/libgap.pyx b/src/sage/libs/gap/libgap.pyx index 3803f32b191..63400adab4c 100644 --- a/src/sage/libs/gap/libgap.pyx +++ b/src/sage/libs/gap/libgap.pyx @@ -46,7 +46,7 @@ equivalent:: sage: type(_) - sage: libgap.eval('5/3 + 7*E(3)').sage() + sage: libgap.eval('5/3 + 7*E(3)').sage() # needs sage.rings.number_field 7*zeta3 + 5/3 sage: gens_of_group = libgap.AlternatingGroup(4).GeneratorsOfGroup() @@ -265,7 +265,7 @@ class Gap(Parent): sage: libgap.has_coerce_map_from(ZZ) True - sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y']) + sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y']) # needs sage.rings.number_field True """ return True @@ -362,11 +362,12 @@ class Gap(Parent): We gracefully handle the case that the conversion fails (:trac:`18039`):: - sage: F. = GF(9, modulus="first_lexicographic") - sage: libgap(Matrix(F, [[a]])) + sage: F. = GF(9, modulus="first_lexicographic") # needs sage.rings.finite_rings + sage: libgap(Matrix(F, [[a]])) # needs sage.rings.finite_rings Traceback (most recent call last): ... - NotImplementedError: conversion of (Givaro) finite field element to GAP not implemented except for fields defined by Conway polynomials. + NotImplementedError: conversion of (Givaro) finite field element to GAP + not implemented except for fields defined by Conway polynomials. """ ring = M.base_ring() try: diff --git a/src/sage/libs/gap/sage.gaprc b/src/sage/libs/gap/sage.gaprc index 39c878f2329..258db942a98 100644 --- a/src/sage/libs/gap/sage.gaprc +++ b/src/sage/libs/gap/sage.gaprc @@ -1,3 +1,31 @@ # This file is run by Sage when initializing libgap via GAP_Initialize, and may # contain bug fixes/workarounds and/or any Sage-specific patches necessary for # Sage's libgap interface. + + +# Load the GAP packages that GAP itself tries to autoload in the +# default configuration (see "PackagesToLoad" in lib/package.gi). The +# combination of passing -A to gap and these LoadPackage statements +# allows us to load the usual set of packages, but only if they are +# installed. So most people will get exactly the default behavior, +# but minimal installations won't throw warnings and fail tests. +# +# We also temporarily lower the InfoLevel of the InfoWarning class so +# that e.g., +# +# #I polycyclic package is not available. Check that the name is correct +# #I and it is present in one of the GAP root directories (see '??RootPaths') +# +# is not output to the console. +# +_orig_warn_level := InfoLevel(InfoWarning); +SetInfoLevel(InfoWarning, 0); + +_autoloads := [ "autpgrp", "alnuth", "crisp", "ctbllib", "factint", "fga", + "irredsol", "laguna", "polenta", "polycyclic", "resclasses", + "sophus", "tomlib" ]; +for p in _autoloads do + LoadPackage(p); +od; + +SetInfoLevel(InfoWarning, _orig_warn_level); diff --git a/src/sage/libs/gap/saved_workspace.py b/src/sage/libs/gap/saved_workspace.py index 7636707f557..fdaf18f4644 100644 --- a/src/sage/libs/gap/saved_workspace.py +++ b/src/sage/libs/gap/saved_workspace.py @@ -8,7 +8,7 @@ import os import glob -from sage.env import GAP_LIB_DIR +from sage.env import GAP_ROOT_PATHS from sage.interfaces.gap_workspace import gap_workspace_file @@ -31,7 +31,13 @@ def timestamp(): """ libgap_dir = os.path.dirname(__file__) libgap_files = glob.glob(os.path.join(libgap_dir, '*')) - gap_packages = glob.glob(os.path.join(GAP_LIB_DIR, 'pkg', '*')) + gap_packages = [] + for d in GAP_ROOT_PATHS.split(";"): + if d: + # If GAP_ROOT_PATHS begins or ends with a semicolon, + # we'll get one empty d. + gap_packages += glob.glob(os.path.join(d, 'pkg', '*')) + files = libgap_files + gap_packages if len(files) == 0: print('Unable to find LibGAP files.') diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index d37fe84f029..8685dc08fa5 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -217,28 +217,29 @@ cdef initialize() noexcept: # initialize GAP. cdef char* argv[16] argv[0] = "sage" - argv[1] = "-l" - s = str_to_bytes(sage.env.GAP_LIB_DIR + ";" + sage.env.GAP_SHARE_DIR, FS_ENCODING, "surrogateescape") - argv[2] = s - - argv[3] = "-m" - argv[4] = "64m" - - argv[5] = "-q" # no prompt! - argv[6] = "-E" # don't use readline as this will interfere with Python - argv[7] = "--nointeract" # Implies -T - argv[8] = "-x" # set the "screen" width so that GAP is less likely to - argv[9] = "4096" # insert newlines when printing objects + argv[1] = "-A" + argv[2] = "-l" + s = str_to_bytes(sage.env.GAP_ROOT_PATHS, FS_ENCODING, "surrogateescape") + argv[3] = s + + argv[4] = "-m" + argv[5] = "64m" + + argv[6] = "-q" # no prompt! + argv[7] = "-E" # don't use readline as this will interfere with Python + argv[8] = "--nointeract" # Implies -T + argv[9] = "-x" # set the "screen" width so that GAP is less likely to + argv[10] = "4096" # insert newlines when printing objects # 4096 unfortunately is the hard-coded max, but should # be long enough for most cases - cdef int argc = 10 # argv[argc] must be NULL + cdef int argc = 11 # argv[argc] must be NULL gap_mem = sage.env.SAGE_GAP_MEMORY if gap_mem is not None: argc += 2 - argv[10] = "-s" + argv[11] = "-s" s1 = str_to_bytes(gap_mem, FS_ENCODING, "surrogateescape") - argv[11] = s1 - argv[4] = s1 + argv[12] = s1 + argv[5] = s1 from sage.libs.gap.saved_workspace import workspace workspace, workspace_is_up_to_date = workspace() diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index 2e4fec38020..fa941804293 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -54,7 +54,7 @@ def ntl_GF2E_random(ntl_GF2EContext_class ctx): INPUT: - - ``ctx`` -- the GF2E context for which an random element should be created + - ``ctx`` -- the GF2E context for which a random element should be created EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index f6781a68c7b..5e6ef736763 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -507,7 +507,7 @@ cdef class ntl_GF2X(): def hex(ntl_GF2X self): r""" - Return an hexadecimal representation of this element. + Return a hexadecimal representation of this element. It is the same as setting \code{ntl.GF2XHexOutput(True)} and representing this element afterwards. However it should be faster and diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi index 22d15b71b45..36c87c75efa 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi +++ b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi @@ -332,7 +332,7 @@ cdef inline int celement_pow(ZZ_pEX_c* res, ZZ_pEX_c* x, long e, ZZ_pEX_c *modul sage: (x+1)^(-2) 1/(x^2 + 2*x + 1) sage: f = x+(a+1) - sage: f**50 == sum(binomial(50,i)*(a+1)**i*x**(50-i) for i in range(51)) + sage: f**50 == sum(binomial(50,i)*(a+1)**i*x**(50-i) for i in range(51)) # needs sage.symbolic True TESTS: diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index aa6c5515432..e36216d6395 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -1052,7 +1052,7 @@ cdef extern from "singular/polys/sbuckets.h": #assumes length <= 0 || pLength(p) == length void sBucketClearMerge(sBucket *bucket, poly **p, int *length) - #add contents of sBucket into polynomial an clear bucket + #add contents of sBucket into polynomial and clear bucket #(can handle repeated monomials) void sBucketClearAdd(sBucket *bucket, poly **p, int *length) diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 3a7820e01ec..4b5a793f762 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -814,7 +814,7 @@ def open_subset(self, name, latex_name=None, coord_def={}, supersets=None): sage: U.default_chart() is X.restrict(U) True - An point in ``U``:: + A point in ``U``:: sage: p = U.an_element(); p Point on the 2-dimensional differentiable manifold M diff --git a/src/sage/manifolds/topological_submanifold.py b/src/sage/manifolds/topological_submanifold.py index 314a6a16a1b..9b722907152 100644 --- a/src/sage/manifolds/topological_submanifold.py +++ b/src/sage/manifolds/topological_submanifold.py @@ -70,7 +70,7 @@ class TopologicalSubmanifold(TopologicalManifold): a topological embedding `\phi` from `N` to `M` (i.e. `\phi` is an homeomorphism onto its image). - In the case where `\phi` is only an topological immersion (i.e. is only + In the case where `\phi` is only a topological immersion (i.e. is only locally an embedding), one says that `N` is an *immersed submanifold*. The map `\phi` can also depend on one or multiple parameters. diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 4acd46db4de..0c2aa9eda4b 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -216,10 +216,10 @@ cdef class Matrix(Matrix1): from sage.matrix.constructor import matrix if self.is_sparse(): return matrix({ij: self[ij].subs(*args, **kwds) for ij in self.nonzero_positions()}, - nrows=self._nrows, ncols=self._ncols, sparse=True) + nrows=self._nrows, ncols=self._ncols, sparse=True) else: return matrix([a.subs(*args, **kwds) for a in self.list()], - nrows=self._nrows, ncols=self._ncols, sparse=False) + nrows=self._nrows, ncols=self._ncols, sparse=False) def solve_left(self, B, check=True): """ @@ -3183,14 +3183,12 @@ cdef class Matrix(Matrix1): """ # Validate assertions - # if not self.is_square(): raise ValueError("self must be a square matrix") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # Extract parameters - # cdef Matrix M = self n = M._ncols R = M._base_ring @@ -3199,7 +3197,6 @@ cdef class Matrix(Matrix1): # Corner cases # N.B. We already tested for M to be square, hence we do not need to # test for 0 x n or m x 0 matrices. - # if n == 0: return S.one() @@ -3215,7 +3212,6 @@ cdef class Matrix(Matrix1): # # N.B. The documentation is still 1-based, although the code, after # having been ported from Magma to Sage, is 0-based. - # from sage.matrix.constructor import matrix F = [R.zero()] * n @@ -3226,18 +3222,15 @@ cdef class Matrix(Matrix1): for t in range(1,n): # Set a(1, t) to be M(<=t, t) - # for i in range(t+1): a.set_unsafe(0, i, M.get_unsafe(i, t)) # Set A[1, t] to be the (t)th entry in a[1, t] - # A[0] = M.get_unsafe(t, t) for p in range(1, t): # Set a(p, t) to the product of M[<=t, <=t] * a(p-1, t) - # for i in range(t+1): s = R.zero() for j in range(t+1): @@ -3245,11 +3238,9 @@ cdef class Matrix(Matrix1): a.set_unsafe(p, i, s) # Set A[p, t] to be the (t)th entry in a[p, t] - # A[p] = a.get_unsafe(p, t) # Set A[t, t] to be M[t, <=t] * a(p-1, t) - # s = R.zero() for j in range(t+1): s = s + M.get_unsafe(t, j) * a.get_unsafe(t-1, j) @@ -3262,7 +3253,7 @@ cdef class Matrix(Matrix1): F[p] = s - A[p] X = S.gen(0) - f = X ** n + S(list(reversed(F))) + f = X**n + S(list(reversed(F))) return f @@ -3634,7 +3625,7 @@ cdef class Matrix(Matrix1): # using the rows of a matrix.) Also see Cohen's first GTM, # Algorithm 2.2.9. - cdef Py_ssize_t i, m, n, + cdef Py_ssize_t i, m, n n = self._nrows cdef Matrix c @@ -18607,8 +18598,8 @@ def _matrix_power_symbolic(A, n): sage: A = matrix(ZZ, [[1,-1], [-1,1]]) sage: A^(2*n+1) # needs sage.symbolic - [ 1/2*2^(2*n + 1) -1/2*2^(2*n + 1)] - [-1/2*2^(2*n + 1) 1/2*2^(2*n + 1)] + [ 1/2*2^(2*n + 1) + 1/2*kronecker_delta(0, 2*n + 1) -1/2*2^(2*n + 1) + 1/2*kronecker_delta(0, 2*n + 1)] + [-1/2*2^(2*n + 1) + 1/2*kronecker_delta(0, 2*n + 1) 1/2*2^(2*n + 1) + 1/2*kronecker_delta(0, 2*n + 1)] Check if :trac:`23215` is fixed:: @@ -18618,12 +18609,25 @@ def _matrix_power_symbolic(A, n): -1/2*I*(a + I*b)^k + 1/2*I*(a - I*b)^k, 1/2*I*(a + I*b)^k - 1/2*I*(a - I*b)^k, 1/2*(a + I*b)^k + 1/2*(a - I*b)^k] + + Check if :trac:`36838` is fixed:Checking symbolic power of + nilpotent matrix:: + + sage: A = matrix([[0,1],[0,0]]); A + [0 1] + [0 0] + sage: n = var('n'); n + n + sage: B = A^n; B + [ kronecker_delta(0, n) n*kronecker_delta(1, n)] + [ 0 kronecker_delta(0, n)] """ from sage.rings.qqbar import AlgebraicNumber from sage.matrix.constructor import matrix from sage.functions.other import binomial from sage.symbolic.ring import SR from sage.rings.qqbar import QQbar + from sage.functions.generalized import kronecker_delta got_SR = A.base_ring() == SR @@ -18656,8 +18660,17 @@ def _matrix_power_symbolic(A, n): # D^i(f) / i! with f = x^n and D = differentiation wrt x if hasattr(mk, 'radical_expression'): mk = mk.radical_expression() - vk = [(binomial(n, i) * mk**(n-i)).simplify_full() - for i in range(nk)] + + + # When the variable "mk" is equal to zero, it is advisable to employ the Kronecker delta function + # instead of utilizing the numerical value zero. This choice is made to encompass scenarios where + # the power of zero is also equal to zero. + if mk: + vk = [(binomial(n, i) * mk._pow_(n-i)).simplify_full() + for i in range(nk)] + else: + vk = [(binomial(n, i).simplify_full() * kronecker_delta(n,i)) + for i in range(nk)] # Form block Mk and insert it in M Mk = matrix(SR, [[SR.zero()]*i + vk[:nk-i] for i in range(nk)]) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index 6faf51eb6ad..73ae9d3552d 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -22,7 +22,7 @@ from sage.rings.infinity import Infinity # We assume that H is square cpdef hessenbergize_cdvf(Matrix_generic_dense H) noexcept: r""" - Replace `H` with an Hessenberg form of it. + Replace `H` with a Hessenberg form of it. .. NOTE:: diff --git a/src/sage/matrix/matrix_integer_dense_hnf.py b/src/sage/matrix/matrix_integer_dense_hnf.py index a899ae9f6e0..30db173966f 100644 --- a/src/sage/matrix/matrix_integer_dense_hnf.py +++ b/src/sage/matrix/matrix_integer_dense_hnf.py @@ -236,8 +236,8 @@ def double_det(A, b, c, proof): INPUT: - A -- an (n-1) x n matrix - - b -- an 1 x n matrix - - c -- an 1 x n matrix + - b -- a 1 x n matrix + - c -- a 1 x n matrix - proof -- whether or not to compute the det modulo enough times to provably compute the determinant. diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index 740864af9ce..fa1d95c9bf6 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -2903,7 +2903,7 @@ cdef class PlusMinusOneMatrix(LeanMatrix): cdef LeanMatrix stack(self, LeanMatrix M) noexcept: """ - Warning: assumes ``M`` is an PlusMinusOneMatrix instance of right + Warning: assumes ``M`` is a PlusMinusOneMatrix instance of right dimensions! """ cdef PlusMinusOneMatrix A @@ -3360,7 +3360,7 @@ cdef class RationalMatrix(LeanMatrix): cdef LeanMatrix stack(self, LeanMatrix M) noexcept: """ - Warning: assumes ``M`` is an RationalMatrix instance of right + Warning: assumes ``M`` is a RationalMatrix instance of right dimensions! """ cdef RationalMatrix A diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx index 1c943ed6203..ad24e104925 100644 --- a/src/sage/matroids/set_system.pyx +++ b/src/sage/matroids/set_system.pyx @@ -591,7 +591,7 @@ cdef class SetSystem: cpdef _heuristic_partition(self, SetSystem P=None, EP=None) noexcept: """ - Return an heuristic ordered partition into singletons of the ground + Return a heuristic ordered partition into singletons of the ground set of the hypergraph whose edges are the subsets in this SetSystem. This partition obtained as follows: make an equitable diff --git a/src/sage/misc/abstract_method.py b/src/sage/misc/abstract_method.py index 7d8ca8edb4c..6179c5a2b8f 100644 --- a/src/sage/misc/abstract_method.py +++ b/src/sage/misc/abstract_method.py @@ -48,7 +48,7 @@ def abstract_method(f=None, optional=False): sage: A.my_method - The current policy is that a ``NotImplementedError`` is raised + The current policy is that a :class:`NotImplementedError` is raised when accessing the method through an instance, even before the method is called:: @@ -193,7 +193,7 @@ def _sage_src_lines_(self): sage: src[0] 'def version():\n' sage: lines - 19 + 18 """ from sage.misc.sageinspect import sage_getsourcelines return sage_getsourcelines(self._f) diff --git a/src/sage/misc/banner.py b/src/sage/misc/banner.py index a428de42005..9d231926705 100644 --- a/src/sage/misc/banner.py +++ b/src/sage/misc/banner.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" SageMath version and banner info """ @@ -58,14 +57,14 @@ def banner_text(full=True): if not full: return version() - bars = u"─" * 68 + bars = "─" * 68 s = [] a = s.append - a(u'┌' + bars + u'┐') - a(u"\n│ %-66s │\n" % version()) + a('┌' + bars + '┐') + a("\n│ %-66s │\n" % version()) python_version = sys.version_info[:3] - a(u"│ %-66s │\n" % 'Using Python {}.{}.{}. Type "help()" for help.'.format(*python_version)) - a(u'└' + bars + u'┘') + a("│ %-66s │\n" % 'Using Python {}.{}.{}. Type "help()" for help.'.format(*python_version)) + a('└' + bars + '┘') pre = version_dict()['prerelease'] try: import sage.all @@ -75,15 +74,15 @@ def banner_text(full=True): if pre or not have_sage_all: red_in = '\033[31m' red_out = '\033[0m' - bars2 = bars.replace(u'─', u'━') + bars2 = bars.replace('─', '━') a('\n') - a(red_in + u'┏' + bars2 + u'┓' + '\n') + a(red_in + '┏' + bars2 + '┓' + '\n') if pre: - a(u"┃ %-66s ┃\n" % 'Warning: this is a prerelease version, and it may be unstable.') + a("┃ %-66s ┃\n" % 'Warning: this is a prerelease version, and it may be unstable.') if not have_sage_all: - a(u"┃ %-66s ┃\n" % 'Warning: sage.all is not available; this is a limited REPL.') - a(u'┗' + bars2 + u'┛' + red_out) - return u''.join(s) + a("┃ %-66s ┃\n" % 'Warning: sage.all is not available; this is a limited REPL.') + a('┗' + bars2 + '┛' + red_out) + return ''.join(s) def banner(): diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index 966ee34514b..979497b43f9 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Attribute and method calling """ diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 39f66c9295a..0cd7a47916e 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Converting Dictionary @@ -26,7 +25,7 @@ This is used e.g. in the result of a variety, to allow access to the result no matter how a generator is identified:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.singular sage.rings.number_field sage: K. = QQ[] sage: I = ideal([x^2 + 2*y - 5, x + y + 3]) sage: V = sorted(I.variety(AA), key=str) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 2af3bc65943..781e02b3191 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -370,7 +370,7 @@ def cython(filename, verbose=0, compile_message=False, try: with open(name + ".lis") as f: cython_messages = f.read() - except IOError: + except OSError: cython_messages = "Error compiling Cython file" except CompileError: raise RuntimeError(cython_messages.strip()) diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py index 9b03e7237a0..82496752609 100644 --- a/src/sage/misc/edit_module.py +++ b/src/sage/misc/edit_module.py @@ -133,7 +133,7 @@ def template_fields(template): """ dict = {} dummy = None - while not(dummy): + while not dummy: try: dummy = template.substitute(dict) except KeyError as inst: @@ -168,10 +168,10 @@ def set_edit_template(template_string): """ global edit_template - if not(isinstance(template_string, Template)): + if not isinstance(template_string, Template): template_string = Template(template_string) fields = set(template_fields(template_string)) - if not(fields <= set(['file', 'line']) and ('file' in fields)): + if not (fields <= set(['file', 'line']) and ('file' in fields)): raise ValueError("Only ${file} and ${line} are allowed as template variables, and ${file} must occur.") edit_template = template_string @@ -253,7 +253,7 @@ def edit(obj, editor=None, bg=None): if editor: set_editor(editor) - elif not(edit_template): + elif not edit_template: try: ED = os.environ['EDITOR'] EDITOR = ED.split() @@ -263,7 +263,7 @@ def edit(obj, editor=None, bg=None): except (ValueError, KeyError, IndexError): raise ValueError("Use set_edit_template() to set a default") - if not(edit_template): + if not edit_template: raise ValueError("Use set_edit_template() to set a default") filename, lineno = file_and_line(obj) diff --git a/src/sage/misc/element_with_label.py b/src/sage/misc/element_with_label.py index 831b012e31a..e7ae38264be 100644 --- a/src/sage/misc/element_with_label.py +++ b/src/sage/misc/element_with_label.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Elements with labels. @@ -170,4 +169,4 @@ def __ne__(self, other): sage: a != x False """ - return not(self == other) + return not (self == other) diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py index 2fb34a2234e..a0c6323270c 100644 --- a/src/sage/misc/explain_pickle.py +++ b/src/sage/misc/explain_pickle.py @@ -461,8 +461,8 @@ def run_pickle(self, p): sage: sib(pe.run_pickle('T\5\0\0\0hello.')) # py2 {atomic:'hello'} """ - for (op, arg, pos) in genops(p): - assert(not(self.stopped)) + for op, arg, pos in genops(p): + assert not self.stopped try: handler = getattr(self, op.name) except AttributeError: @@ -472,8 +472,8 @@ def run_pickle(self, p): else: handler(arg) - assert(self.stopped) - assert(len(self.stack) == 1) + assert self.stopped + assert len(self.stack) == 1 return self.stack[0] def check_value(self, v): @@ -493,7 +493,7 @@ def check_value(self, v): AssertionError sage: pe.check_value(sib(7)) """ - assert(isinstance(v, (SageInputExpression, PickleObject))) + assert isinstance(v, (SageInputExpression, PickleObject)) def push(self, v): r""" @@ -2442,13 +2442,13 @@ def unpickle_build(obj, state): slots = None if state is not None: - assert(isinstance(state, dict)) + assert isinstance(state, dict) d = obj.__dict__ for k, v in state.items(): d[k] = v if slots is not None: - assert(isinstance(slots, dict)) + assert isinstance(slots, dict) for k, v in slots.items(): setattr(obj, k, v) @@ -2635,11 +2635,11 @@ def pers_load(s): if cpickle_ok: cpickle_repr = repr(cpickle_res) - assert(current_repr == generic_repr == cpickle_repr) + assert current_repr == generic_repr == cpickle_repr print("result: " + current_repr) else: - assert(current_repr == generic_repr) + assert current_repr == generic_repr print("result: " + current_repr + " (cPickle raised an exception!)") diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 03697f08b7c..7a2bb8c0803 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Functional notation diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index df469ca8189..f409dacbddc 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -33,10 +33,9 @@ from tempfile import TemporaryDirectory from sage.misc.cachefunc import cached_function, cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.structure.sage_object import SageObject -from sage.misc.lazy_import import lazy_import -lazy_import('sage.misc.html', ('MathJax', 'MathJaxExpr'), deprecation=31536) COMMON_HEADER = r'''\usepackage{amsmath} \usepackage{amssymb} @@ -66,111 +65,6 @@ ''') -@cached_function -def have_latex() -> bool: - """ - Return ``True`` if this computer has the program ``latex``. - - If this computer does not have LaTeX installed, you may obtain it - from http://ctan.org/. - - EXAMPLES:: - - sage: from sage.misc.latex import have_latex - sage: have_latex() # random - True - """ - from .superseded import deprecation - deprecation(32650, 'the function have_latex() is replaced by: ' - 'from sage.features.latex import latex;latex().is_present()') - from sage.features.latex import latex - return latex().is_present() - - -@cached_function -def have_pdflatex() -> bool: - """ - Return ``True`` if this computer has the program ``pdflatex``. - - If this computer does not have pdflatex installed, you may obtain it - from http://ctan.org/. - - EXAMPLES:: - - sage: from sage.misc.latex import have_pdflatex - sage: have_pdflatex() # random - True - """ - from .superseded import deprecation - deprecation(32650, 'the function have_pdflatex() is replaced by: ' - 'from sage.features.latex import pdflatex;pdflatex().is_present()') - from sage.features.latex import pdflatex - return pdflatex().is_present() - - -@cached_function -def have_xelatex() -> bool: - """ - Return ``True`` if this computer has the program ``xelatex``. - - If this computer does not have xelatex installed, you may obtain it - from http://ctan.org/. - - EXAMPLES:: - - sage: from sage.misc.latex import have_xelatex - sage: have_xelatex() # random - True - """ - from .superseded import deprecation - deprecation(32650, 'the function have_xelatex() is replaced by: ' - 'from sage.features.latex import xelatex;xelatex().is_present()') - from sage.features.latex import xelatex - return xelatex().is_present() - - -@cached_function -def have_dvipng() -> bool: - """ - Return ``True`` if this computer has the program ``dvipng``. - - If this computer does not have dvipng installed, you may obtain it - from http://sourceforge.net/projects/dvipng/ - - EXAMPLES:: - - sage: from sage.misc.latex import have_dvipng - sage: have_dvipng() # random - True - """ - from .superseded import deprecation - deprecation(32650, 'the function have_dvipng() is replaced by: ' - 'from sage.features.dvipng import dvipng;dvipng().is_present()') - from sage.features.dvipng import dvipng - return dvipng().is_present() - -@cached_function -def have_convert() -> bool: - """ - Return ``True`` if this computer has the program ``convert``. - - If this computer does not have convert installed, you may obtain it - (along with the rest of the ImageMagick suite) from - http://www.imagemagick.org - - EXAMPLES:: - - sage: from sage.misc.latex import have_convert - sage: have_convert() # random - True - """ - from .superseded import deprecation - deprecation(32650, 'the function have_convert() is replaced by: ' - 'from sage.features.imagemagick import imagemagick;imagemagick().is_present()') - from sage.features.imagemagick import ImageMagick - return ImageMagick().is_present() - - def list_function(x): r""" Returns the LaTeX code for a list ``x``. @@ -207,7 +101,7 @@ def tuple_function(x, combine_all=False): - ``x`` -- a tuple - - ``combine_all`` -- boolean (Default: ``False``) If ``combine_all`` is + - ``combine_all`` -- boolean (default: ``False``) If ``combine_all`` is ``True``, then it does not return a tuple and instead returns a string with all the elements separated by a single space. It does not collapse tuples which are inside tuples. @@ -315,11 +209,9 @@ def str_function(x): INPUT: - - ``x`` -- a string. - - OUTPUT: + - ``x`` -- a string - A string + OUTPUT: A string EXAMPLES:: @@ -436,10 +328,10 @@ class LatexExpr(str): Normally, objects of this class are created by a :func:`latex` call. It is also possible to generate :class:`LatexExpr` directly from a string, which must contain valid LaTeX code for typesetting in math mode (without dollar - signs). In the Sage notebook, use - :func:`~sage.repl.rich_output.pretty_print.pretty_print` or the "Typeset" - checkbox to actually see the typeset LaTeX code; alternatively, from - either the command-line or the notebook, use the :func:`view` function. + signs). In the Jupyter notebook, use + :func:`~sage.repl.rich_output.pretty_print.pretty_print` to actually see + the typeset LaTeX code; alternatively, from either the command-line or the + notebook, use the :func:`view` function. INPUT: @@ -583,6 +475,30 @@ def has_latex_attr(x) -> bool: return hasattr(x, '_latex_') and not isinstance(x, type) +@cached_function +def default_engine(): + """ + Return the default latex engine and the official name of the engine. + + This is determined by availability of the popular engines on the user's + system. It is assumed that at least latex is available. + + EXAMPLES:: + + sage: from sage.misc.latex import default_engine + sage: default_engine() # random + ('lualatex', 'LuaLaTeX') + """ + from sage.features.latex import pdflatex, xelatex, lualatex + if lualatex().is_present(): + return 'lualatex', 'LuaLaTeX' + if xelatex().is_present(): + return 'xelatex', 'XeLaTeX' + if pdflatex().is_present(): + return 'pdflatex', 'pdfLaTeX' + return 'latex', 'LaTeX' + + class _Latex_prefs_object(SageObject): """ An object that holds LaTeX global preferences. @@ -598,23 +514,38 @@ def __init__(self, bb=False, delimiters=["(", ")"], sage: latex_prefs = _Latex_prefs_object() sage: TestSuite(latex_prefs).run(skip ="_test_pickling") """ - self._option = {} - self._option["blackboard_bold"] = bb - self._option["matrix_delimiters"] = list(delimiters) - self._option["vector_delimiters"] = list(delimiters) - self._option["matrix_column_alignment"] = matrix_column_alignment - self._option["macros"] = "" - self._option["preamble"] = "" - self._option["engine"] = "pdflatex" - self._option["engine_name"] = "LaTeX" + self.__option = {} + self.__option["blackboard_bold"] = bb + self.__option["matrix_delimiters"] = list(delimiters) + self.__option["vector_delimiters"] = list(delimiters) + self.__option["matrix_column_alignment"] = matrix_column_alignment + self.__option["macros"] = "" + self.__option["preamble"] = "" + + @lazy_attribute + def _option(self): + """ + This attribute contains the preferences list. + EXAMPLES:: + + sage: from sage.misc.latex import _Latex_prefs_object + sage: _Latex_prefs_object()._option # random + {'blackboard_bold': False, + 'matrix_delimiters': ['(', ')'], + 'vector_delimiters': ['(', ')'], + 'matrix_column_alignment': 'r', + 'macros': '', + 'preamble': '', + 'engine': 'lualatex', + 'engine_name': 'LuaLaTeX'} + """ + self.__option["engine"] = default_engine()[0] + self.__option["engine_name"] = default_engine()[1] + return self.__option -_Latex_prefs = _Latex_prefs_object() -############################################################## -# The Latex class is used to make slides and LaTeX output in -# the Sage Notebook -######################################### +_Latex_prefs = _Latex_prefs_object() def latex_extra_preamble(): @@ -664,25 +595,25 @@ def latex_extra_preamble(): def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_in_background=False): """ This runs LaTeX on the TeX file "filename.tex". It produces files - "filename.dvi" (or "filename.pdf"` if engine is either ``pdflatex`` - or ``xelatex``) and if ``png`` is ``True``, "filename.png". If ``png`` - is ``True`` and dvipng cannot convert the dvi file to png (because of - postscript specials or other issues), then dvips is called, and the - PS file is converted to a png file. + ``filename.dvi`` (or ``filename.pdf``` if ``engine`` is either ``'pdflatex'``, + ``'xelatex'``, or ``'lualatex'``) and if ``png`` is ``True``, ``filename.png``. + If ``png`` is ``True`` and ``dvipng`` cannot convert the dvi file to png + (because of postscript specials or other issues), then ``dvips`` is called, and + the PS file is converted to a png file. INPUT: - - ``filename`` -- string: file to process, including full path + - ``filename`` -- string; file to process, including full path - - ``debug`` -- bool (optional, default ``False``): whether to print + - ``debug`` -- bool (optional, default ``False``); whether to print verbose debugging output - - ``density`` -- integer (optional, default 150): how big output + - ``density`` -- integer (optional, default 150); how big output image is. - ``engine`` -- string: latex engine to use. - - ``png`` -- bool (optional, default ``False``): whether to produce a + - ``png`` -- bool (optional, default ``False``); whether to produce a png file. - ``do_in_background`` -- bool (optional, default ``False``). Unused, @@ -690,23 +621,23 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i OUTPUT: - A string which could be a string starting with 'Error' (if - there was a problem), or it could be 'pdf' or 'dvi'. If - engine is latex or ``None``, then a dvi file is created, but if there - appear to be problems with it (because of PS special commands, for - example), then a pdf file is created instead. The function - returns 'dvi' or 'pdf' to indicate which type of file is created. - (Detecting problems requires that dvipng be installed; if it is - not, then the dvi file is not checked for problems and 'dvi' is - returned.) If engine is pdflatex or xelatex and there are no errors, then - 'pdf' is returned. + A string which could be a string starting with ``'Error'`` (if there was a + problem), or it could be ``'pdf'`` or ``'dvi'``. If ``engine`` is + ``'latex'`` or ``None``, then a dvi file is created, but if there appear to + be problems with it (because of PS special commands, for example), then a + pdf file is created instead. The function returns ``'dvi'`` or ``'pdf'`` + to indicate which type of file is created. (Detecting problems requires + that ``dvipng`` be installed; if it is not, then the dvi file is not checked + for problems and ``'dvi'`` is returned.) If ``engine`` is ``'pdflatex'``, + ``'xelatex'`` or ``'lualatex'`` and there are no errors, then ``'pdf'`` is + returned. .. WARNING:: If ``png`` is ``True``, then when using latex (the default), you - must have 'dvipng' (or 'dvips' and 'convert') installed on your + must have ``dvipng`` (or ``dvips`` and ``convert``) installed on your operating system, or this command will not work. When using - pdflatex or xelatex, you must have 'convert' installed. + ``pdflatex``, ``xelatex`` or ``lualatex``, you must have ``convert`` installed. EXAMPLES:: @@ -740,6 +671,12 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i command = "xelatex" suffix = "pdf" return_suffix = "pdf" + elif engine == "lualatex": + from sage.features.latex import lualatex + lualatex().require() + command = "lualatex" + suffix = "pdf" + return_suffix = "pdf" else: raise ValueError("Unsupported LaTeX engine.") @@ -758,10 +695,8 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i print("Go to http://sourceforge.net/projects/dvipng/ and") print("http://www.imagemagick.org to download these programs.") return "Error" - # if png output + pdflatex, check to see if convert is installed. - elif engine == "pdflatex": - ImageMagick().require() - elif engine == "xelatex": + # if png output + [pdf|xe|lua]latex, check to see if convert is installed. + elif engine in ["pdflatex", "xelatex", "lualatex"]: ImageMagick().require() # check_validity: check to see if the dvi file is okay by trying # to convert to a png file. if this fails, return_suffix will be @@ -814,7 +749,7 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i def subpcall(x): return not call(x, stdout=redirect, stderr=redirect, cwd=base) - if engine == "pdflatex" or engine == "xelatex": + if engine in ['pdflatex', 'xelatex', 'lualatex']: if debug: print(lt) if png: @@ -871,12 +806,17 @@ def subpcall(x): try: with open(base + '/' + filename + '.log') as f: print(f.read()) - except IOError: + except OSError: pass return "Error latexing slide." return return_suffix +# ------------------------------------------------------- +# The Latex class is used to make slides and LaTeX output +# ------------------------------------------------------- + + class LatexCall: r""" Typeset Sage objects via a ``__call__`` method to this class, @@ -981,7 +921,7 @@ class Latex(LatexCall): sage: LatexExpr(r"y \neq") + latex(x^20 + 1) # needs sage.symbolic y \neq x^{20} + 1 """ - def __init__(self, debug=False, slide=False, density=150, pdflatex=None, engine=None): + def __init__(self, debug=False, slide=False, density=150, engine=None): """ Initialize the latex builder. @@ -993,7 +933,6 @@ def __init__(self, debug=False, slide=False, density=150, pdflatex=None, engine= """ self.__debug = debug self.__slide = slide - self.__pdflatex = pdflatex self.__engine = engine self.__density = density @@ -1046,41 +985,36 @@ def _latex_preparse(self, s, locals): s = s[:i] + k + t[j + 1:] def eval(self, x, globals, strip=False, filename=None, debug=None, - density=None, pdflatex=None, engine=None, locals={}): + density=None, engine=None, locals={}): r""" Compile the formatted tex given by ``x`` as a png and writes the output file to the directory given by ``filename``. INPUT: - - ``globals`` -- a globals dictionary + - ``globals`` -- a globals dictionary - - ``x`` -- string to evaluate. + - ``x`` -- string to evaluate - - ``strip`` -- ignored + - ``strip`` -- ignored - - ``filename`` -- output filename + - ``filename`` -- output filename - - ``debug`` -- whether to print verbose debugging - output + - ``debug`` -- whether to print verbose debugging output - - ``density`` -- how big output image is. + - ``density`` -- how big output image is - - ``pdflatex`` -- whether to use pdflatex. This is deprecated. Use - ``engine`` option instead. + - ``engine`` -- latex engine to use. Currently ``'latex'``, + ``'pdflatex'``, ``'xelatex'`` and ``'lualatex'`` are supported - - ``engine`` -- latex engine to use. Currently latex, pdflatex, and - xelatex are supported. - - - ``locals`` - extra local variables used when - evaluating Sage code in ``x``. + - ``locals`` - extra local variables used when evaluating Sage code in ``x`` .. WARNING:: - When using latex (the default), you must have 'dvipng' (or - 'dvips' and 'convert') installed on your operating system, - or this command will not work. When using pdflatex or xelatex, you - must have 'convert' installed. + When using ``'latex'`` (the default), you must have ``dvipng`` (or + ``dvips`` and ``convert``) installed on your operating system, or + this command will not work. When using ``'pdflatex'``, ``'xelatex'`` + or ``'lualatex'``, you must have ``convert`` installed. OUTPUT: @@ -1151,7 +1085,7 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, def blackboard_bold(self, t=None): r"""nodetex Controls whether Sage uses blackboard bold or ordinary bold - face for typesetting ZZ, RR, etc. + face for typesetting ``ZZ``, ``RR``, etc. INPUT: @@ -1207,18 +1141,18 @@ def matrix_delimiters(self, left=None, right=None): Good choices for ``left`` and ``right`` are any delimiters which LaTeX understands and knows how to resize; some examples are: - - parentheses: '(', ')' - - brackets: '[', ']' - - braces: '\\{', '\\}' - - vertical lines: '|' - - angle brackets: '\\langle', '\\rangle' + - parentheses: ``'('``, ``')'`` + - brackets: ``'['``, ``']'`` + - braces: ``'\\{'``, ``'\\}'`` + - vertical lines: ``'|'`` + - angle brackets: ``'\\langle'``, ``'\\rangle'`` .. NOTE:: - Putting aside aesthetics, you may combine these in any way - imaginable; for example, you could set ``left`` to be a - right-hand bracket ']' and ``right`` to be a right-hand - brace '\\}', and it will be typeset correctly. + Putting aside aesthetics, you may combine these in any way + imaginable; for example, you could set ``left`` to be a right-hand + bracket ``']'`` and ``right`` to be a right-hand brace ``'\\}'``, + and it will be typeset correctly. EXAMPLES:: @@ -1228,12 +1162,12 @@ def matrix_delimiters(self, left=None, right=None): \left(\begin{array}{r} 17 \end{array}\right) - sage: latex.matrix_delimiters("[", "]") + sage: latex.matrix_delimiters('[', ']') sage: latex(a) \left[\begin{array}{r} 17 \end{array}\right] - sage: latex.matrix_delimiters(left="\\{") + sage: latex.matrix_delimiters(left='\\{') sage: latex(a) \left\{\begin{array}{r} 17 @@ -1243,7 +1177,7 @@ def matrix_delimiters(self, left=None, right=None): Restore defaults:: - sage: latex.matrix_delimiters("(", ")") + sage: latex.matrix_delimiters('(', ')') """ if left is None and right is None: return _Latex_prefs._option['matrix_delimiters'] @@ -1269,18 +1203,18 @@ def vector_delimiters(self, left=None, right=None): Good choices for ``left`` and ``right`` are any delimiters which LaTeX understands and knows how to resize; some examples are: - - parentheses: '(', ')' - - brackets: '[', ']' - - braces: '\\{', '\\}' - - vertical lines: '|' - - angle brackets: '\\langle', '\\rangle' + - parentheses: ``'('``, ``')'`` + - brackets: ``'['``, ``']'`` + - braces: ``'\\{'``, ``'\\}'`` + - vertical lines: ``'|'`` + - angle brackets: ``'\\langle'``, ``'\\rangle'`` .. NOTE:: - Putting aside aesthetics, you may combine these in any way - imaginable; for example, you could set ``left`` to be a - right-hand bracket ']' and ``right`` to be a right-hand - brace '\\}', and it will be typeset correctly. + Putting aside aesthetics, you may combine these in any way + imaginable; for example, you could set ``left`` to be a right-hand + bracket ``']'`` and ``right`` to be a right-hand brace ``'\\}'``, and it + will be typeset correctly. EXAMPLES:: @@ -1288,10 +1222,10 @@ def vector_delimiters(self, left=None, right=None): sage: a = vector(QQ, [1,2,3]) sage: latex(a) \left(1,\,2,\,3\right) - sage: latex.vector_delimiters("[", "]") + sage: latex.vector_delimiters('[', ']') sage: latex(a) \left[1,\,2,\,3\right] - sage: latex.vector_delimiters(right="\\}") + sage: latex.vector_delimiters(right='\\}') sage: latex(a) \left[1,\,2,\,3\right\} sage: latex.vector_delimiters() @@ -1299,7 +1233,7 @@ def vector_delimiters(self, left=None, right=None): Restore defaults:: - sage: latex.vector_delimiters("(", ")") + sage: latex.vector_delimiters('(', ')') """ if left is None and right is None: return _Latex_prefs._option['vector_delimiters'] @@ -1387,7 +1321,7 @@ def check_file(self, file_name, more_info=""): - ``file_name`` -- a string - - ``more_info`` -- a string (default: "") + - ``more_info`` -- a string (default: ``""``) Emit a warning if the local LaTeX installation does not include ``file_name``. The string ``more_info`` is appended @@ -1417,7 +1351,7 @@ def check_file(self, file_name, more_info=""): def extra_macros(self, macros=None): r"""nodetex - String containing extra LaTeX macros to use with %latex and %html. + String containing extra LaTeX macros to use with ``%latex`` and ``%html``. INPUT: @@ -1500,7 +1434,7 @@ def extra_preamble(self, s=None): def add_to_preamble(self, s): r"""nodetex Append to the string ``s`` of extra LaTeX macros, for use with - %latex. + ``%latex``. EXAMPLES:: @@ -1523,7 +1457,7 @@ def add_to_preamble(self, s): sage: latex.extra_preamble() '\\DeclareMathOperator{\\Ext}{Ext}\\usepackage{xypic}' - Now one can put various xypic diagrams into a %latex cell, such as + Now one can put various xypic diagrams into a ``%latex`` cell, such as :: @@ -1576,7 +1510,7 @@ def engine(self, e=None): INPUT: - - ``e`` -- 'latex', 'pdflatex', 'xelatex' or ``None`` + - ``e`` -- ``'latex'``, ``'pdflatex'``, ``'xelatex'``, ``'lualatex'`` or ``None`` If ``e`` is ``None``, return the current engine. @@ -1590,14 +1524,14 @@ def engine(self, e=None): EXAMPLES:: - sage: latex.engine() - 'pdflatex' + sage: latex.engine() # random + 'lualatex' sage: latex.engine("latex") sage: latex.engine() 'latex' - sage: latex.engine("xelatex") + sage: latex.engine("pdflatex") sage: latex.engine() - 'xelatex' + 'pdflatex' """ if e is None: return _Latex_prefs._option["engine"] @@ -1611,8 +1545,12 @@ def engine(self, e=None): elif e == "xelatex": _Latex_prefs._option["engine"] = e _Latex_prefs._option["engine_name"] = "XeLaTeX" + elif e == "lualatex": + _Latex_prefs._option["engine"] = e + _Latex_prefs._option["engine_name"] = "LuaLaTeX" else: - raise ValueError("%s is not a supported LaTeX engine. Use latex, pdflatex, or xelatex" % e) + raise ValueError("%s is not a supported LaTeX engine. Use latex, pdflatex, xelatex, or lualatex" % e) + # Note: latex used to be a separate function, which by default was # only loaded in command-line mode: in the old notebook, @@ -1621,13 +1559,10 @@ def engine(self, e=None): # function. This has been changed around so that the contents of the # old latex function are now in Latex.__call__; thus the following # assignment. - - latex = Latex() # Ensure that latex appear in the sphinx doc as a function # so that the link :func:`latex` is correctly set up. latex.__doc__ = Latex.__call__.__doc__ -######################################### def _latex_file_(objects, title='SAGE', debug=False, @@ -1640,22 +1575,22 @@ def _latex_file_(objects, title='SAGE', debug=False, INPUT: - - ``objects`` -- list (or object) + - ``objects`` -- list (or object) - - ``title`` -- string (default: 'Sage'): title for the document + - ``title`` -- string (default: 'Sage'); title for the document - - ``math_left`` -- string (default: '\\['), left delimiter for math mode + - ``math_left`` -- string (default: '\\['), left delimiter for math mode - - ``math_right`` -- string (default: '\\]'), right delimiter for math mode + - ``math_right`` -- string (default: '\\]'), right delimiter for math mode - - ``debug`` -- bool (default: False): print verbose output + - ``debug`` -- bool (default: False); print verbose output - - ``sep`` -- string (default: ''): separator between math objects + - ``sep`` -- string (default: ``''``); separator between math objects - - ``tiny`` -- bool (default: False): use 'tiny' font. + - ``tiny`` -- bool (default: False); use 'tiny' font. - - ``extra_preamble`` -- string (default: ''): extra LaTeX commands, - inserted before "\\begin{document}" + - ``extra_preamble`` -- string (default: ``''``); extra LaTeX commands, + inserted before ``"\\begin{document}"`` This creates a string intended to be a LaTeX file containing the LaTeX representations of objects. It contains the following: @@ -1674,7 +1609,7 @@ def _latex_file_(objects, title='SAGE', debug=False, Then if ``objects`` contains more than one element, for each remaining element: - - the string ``sep``: you can use this, for example, to add + - the string ``sep``; you can use this, for example, to add vertical space between objects with ``sep='\\vspace{15mm}'``, or to add a horizontal line between objects with ``sep='\\hrule'``, or to insert a page break between objects @@ -1682,7 +1617,7 @@ def _latex_file_(objects, title='SAGE', debug=False, - the LaTeX representation of the element - The string ends with '\\end{document}'. + The string ends with ``'\\end{document}'``. EXAMPLES:: @@ -1757,7 +1692,7 @@ def _latex_file_(objects, title='SAGE', debug=False, def view(objects, title='Sage', debug=False, sep='', tiny=False, - pdflatex=None, engine=None, viewer=None, tightpage=True, margin=None, + engine=None, viewer=None, tightpage=True, margin=None, mode='inline', combine_all=False, **kwds): r"""nodetex Compute a latex representation of each object in objects, compile, @@ -1766,50 +1701,49 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, INPUT: - - ``objects`` -- list (or object) + - ``objects`` -- list (or object) - - ``title`` -- string (default: ``'Sage'``): title for the + - ``title`` -- string (default: ``'Sage'``); title for the document - - ``debug`` -- bool (default: ``False``): print verbose + - ``debug`` -- bool (default: ``False``); print verbose output - - ``sep`` -- string (default: ''): separator between + - ``sep`` -- string (default: ``''``); separator between math objects - - ``tiny`` -- bool (default: ``False``): use tiny font. - - - ``pdflatex`` -- bool (default: ``False``): use pdflatex. This is - deprecated. Use ``'engine'`` option instead. + - ``tiny`` -- bool (default: ``False``); use tiny font. - - ``engine`` -- string or ``None`` (default: ``None``). Can take the + - ``engine`` -- string or ``None`` (default: ``None``); can take the following values: - - ``None`` -- the value defined in the LaTeX global preferences - ``latex.engine()`` is used. + - ``None`` -- the value defined in the LaTeX global preferences + ``latex.engine()`` is used. - - ``'pdflatex'`` -- compilation does tex -> pdf + - ``'pdflatex'`` -- compilation does ``tex`` -> ``pdf`` - - ``'xelatex'`` -- compilation does tex -> pdf + - ``'xelatex'`` -- compilation does ``tex`` -> ``pdf`` - - ``'latex'`` -- compilation first tries tex -> dvi -> png and if an - error occurs then tries dvi -> ps -> pdf. This is slower than - ``'pdflatex'`` and known to be broken when overfull hbox are detected. + - ``'lualatex'`` -- compilation does ``tex`` -> ``pdf`` - - ``viewer`` -- string or ``None`` (default: ``None``): specify a viewer + - ``'latex'`` -- compilation first tries ``tex`` -> ``dvi`` -> ``png`` and if an + error occurs then tries ``dvi`` -> ``ps`` -> ``pdf``. This is slower than + ``'pdflatex'`` and known to be broken when overfull hboxes are detected. + + - ``viewer`` -- string or ``None`` (default: ``None``); specify a viewer to use; currently the only options are ``None`` and ``'pdf'``. - - ``tightpage`` -- bool (default: ``True``): use the LaTeX package - 'preview' with the 'tightpage' option. + - ``tightpage`` -- bool (default: ``True``); use the LaTeX package + ``preview`` with the 'tightpage' option. - - ``margin`` -- float or ``None`` (default: ``None``): adds a margin + - ``margin`` -- float or ``None`` (default: ``None``); adds a margin of ``margin`` mm; has no affect if the option ``tightpage`` is ``False``. - - ``mode`` -- string (default: ``'inline'``): ``'display'`` for + - ``mode`` -- string (default: ``'inline'``); ``'display'`` for displaymath or ``'inline'`` for inline math - - ``combine_all`` -- bool (default: ``False``): If ``combine_all`` is + - ``combine_all`` -- bool (default: ``False``); if ``combine_all`` is ``True`` and the input is a tuple, then it does not return a tuple and instead returns a string with all the elements separated by a single space. @@ -1829,20 +1763,17 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, adds a horizontal line between objects, and ``sep='\\newpage'`` inserts a page break between objects. - If ``pdflatex`` is ``True``, then the latex engine is set to - pdflatex. - - If the ``engine`` is either ``pdflatex`` or ``xelatex``, it produces - a pdf file. Otherwise, it produces a dvi file, and if the program dvipng is - installed, it checks the dvi file by trying to convert it to a png - file. If this conversion fails, the dvi file probably contains - some postscript special commands or it has other issues which - might make displaying it a problem; in this case, the file is - converted to a pdf file, which is then displayed. + If the ``engine`` is either ``'pdflatex'``, ``'xelatex'``, or ``'lualatex'``, + it produces a pdf file. Otherwise, it produces a dvi file, and if the program + ``dvipng`` is installed, it checks the dvi file by trying to convert it to a + png file. If this conversion fails, the dvi file probably contains some + postscript special commands or it has other issues which might make + displaying it a problem; in this case, the file is converted to a pdf file, + which is then displayed. Setting ``viewer`` to ``'pdf'`` forces the use of a separate viewer, even in notebook mode. This also sets the latex engine to be - ``pdflatex`` if the current engine is latex. + ``pdflatex`` if the current engine is ``latex``. Setting the option ``tightpage`` to ``True`` (this is the default setting) tells LaTeX to use the package 'preview' with the 'tightpage' option. @@ -1921,7 +1852,7 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, s = _latex_file_(objects, title=title, sep=sep, tiny=tiny, debug=debug, **latex_options) if engine is None: engine = _Latex_prefs._option["engine"] - if pdflatex or (viewer == "pdf" and engine == "latex"): + if viewer == "pdf" and engine == "latex": engine = "pdflatex" # command line or notebook with viewer @@ -1970,42 +1901,37 @@ def run_viewer(): def png(x, filename, density=150, debug=False, - do_in_background=False, tiny=False, pdflatex=True, engine='pdflatex'): + do_in_background=False, tiny=False, engine=None): """ Create a png image representation of ``x`` and save to the given filename. INPUT: - - ``x`` -- object to be displayed - - - ``filename`` -- file in which to save the image + - ``x`` -- object to be displayed - - ``density`` -- integer (default: 150) + - ``filename`` -- file in which to save the image - - ``debug`` -- bool (default: ``False``): print verbose - output + - ``density`` -- integer (default: 150) - - ``do_in_background`` -- bool (default: ``False``): Unused, - kept for backwards compatibility + - ``debug`` -- bool (default: ``False``); print verbose output - - ``tiny`` -- bool (default: ``False``): use 'tiny' font + - ``do_in_background`` -- bool (default: ``False``); Unused, kept for + backwards compatibility - - ``pdflatex`` -- bool (default: ``True``): use pdflatex. This option is - deprecated. Use ``engine`` option instead. See below. + - ``tiny`` -- bool (default: ``False``); use tiny font - - ``engine`` -- (default: ``'pdflatex'``) ``'latex'``, ``'pdflatex'``, - or ``'xelatex'`` + - ``engine`` -- (default: ``None``) ``'latex'``, ``'pdflatex'``, + ``'xelatex'`` or ``'lualatex'`` EXAMPLES:: + sage: # optional - imagemagick latex, needs sage.plot sage: from sage.misc.latex import png sage: import tempfile - sage: with tempfile.NamedTemporaryFile(suffix=".png") as f: # random # optional - imagemagick latex, needs sage.plot + sage: with tempfile.NamedTemporaryFile(suffix=".png") as f: # random ....: png(ZZ[x], f.name) """ - if not pdflatex: - engine = "latex" import sage.plot.all if sage.plot.graphics.is_Graphics(x): x.save(filename) @@ -2014,6 +1940,8 @@ def png(x, filename, density=150, debug=False, s = _latex_file_([x], math_left='$\\displaystyle', math_right='$', title='', debug=debug, tiny=tiny, extra_preamble='\\textheight=2\\textheight') + if engine is None: + engine = _Latex_prefs._option["engine"] # path name for permanent png output abs_path_to_png = os.path.abspath(filename) # temporary directory to store stuff @@ -2076,13 +2004,11 @@ def repr_lincomb(symbols, coeffs): INPUT: - - ``symbols`` -- list of symbols + - ``symbols`` -- list of symbols - - ``coeffs`` -- list of coefficients of the symbols + - ``coeffs`` -- list of coefficients of the symbols - OUTPUT: - - A string + OUTPUT: A string EXAMPLES:: @@ -2213,9 +2139,7 @@ def latex_varify(a, is_fname=False): - ``a`` -- string - OUTPUT: - - A string + OUTPUT: A string EXAMPLES:: @@ -2260,7 +2184,7 @@ def latex_variable_name(x, is_fname=False): 2. If the variable name is suffixed by a number, we put the number in the subscript. - 3. If the variable name contains an '_' we start the subscript at + 3. If the variable name contains an ``'_'`` we start the subscript at the underscore. Note that #3 trumps rule #2. 4. If a component of the variable is a Greek letter, escape it @@ -2467,7 +2391,7 @@ def _repr_(self): make sure that you have the most recent version of the TeX package pstricks installed. Run 'latex.add_to_preamble("\\usepackage{pstricks}")' and try viewing it again. Call 'view' with the option `engine='latex'` --- the default behavior is to use pdflatex, which does not work with +-- the default behavior is to use lualatex, which does not work with pstricks. From the command line, this should pop open a nice window with a picture of forces acting on a mass on a pendulum.""" diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index d1d9d77d864..c24b076d805 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Standalone LaTeX Document class and TikzPicture diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index 1f07870b8ec..74c3756a004 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -77,7 +77,7 @@ cdef class _lazy_attribute(): def _sage_src_lines_(self): r""" - Returns the source code location for the wrapped function. + Return the source code location for the wrapped function. EXAMPLES:: @@ -87,15 +87,14 @@ cdef class _lazy_attribute(): sage: src[0] 'def banner():\n' sage: lines - 89 + 88 """ from sage.misc.sageinspect import sage_getsourcelines return sage_getsourcelines(self.f) - def __get__(self, a, cls): """ - Implements the attribute access protocol. + Implement the attribute access protocol. EXAMPLES:: @@ -143,6 +142,7 @@ cdef class _lazy_attribute(): raise return result + class lazy_attribute(_lazy_attribute): r""" A lazy attribute for an object is like a usual attribute, except @@ -509,7 +509,7 @@ class lazy_attribute(_lazy_attribute): class lazy_class_attribute(lazy_attribute): """ - A lazy class attribute for an class is like a usual class attribute, + A lazy class attribute for a class is like a usual class attribute, except that, instead of being computed when the class is constructed, it is computed on the fly the first time it is accessed, either through the class itself or trough on of its objects. diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index d69b82128f1..1c93c811b2e 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -336,8 +336,8 @@ def __len__(self): """ Return the cardinality of this iterator as an int. - Raises a ``TypeError`` if the cardinality does not fit into a Python - int. + This raises a :class:`TypeError` if the cardinality does not fit + into a Python int. EXAMPLES:: diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index dcae7d0c0fc..d64c53ac3e8 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -152,7 +152,7 @@ def spkg_type(name): return None try: f = open(os.path.join(SAGE_PKGS, name, "type")) - except IOError: + except OSError: # Probably an empty directory => ignore return None @@ -390,7 +390,7 @@ def _spkg_inst_dirs(): """ Generator for the installation manifest directories as resolved paths. - It yields first ``SAGE_SPKG_INST``, then ``SAGE_VENV_SPKG_INST``, + It yields first ``SAGE_LOCAL_SPKG_INST``, then ``SAGE_VENV_SPKG_INST``, if defined; but it both resolve to the same directory, it only yields one element. @@ -402,7 +402,7 @@ def _spkg_inst_dirs(): """ last_inst_dir = None - for inst_dir in (sage.env.SAGE_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST): + for inst_dir in (sage.env.SAGE_LOCAL_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST): if inst_dir: inst_dir = Path(inst_dir).resolve() if inst_dir.is_dir() and inst_dir != last_inst_dir: diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 3749a4f2a91..7d0e14cf799 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -615,7 +615,7 @@ def handle_file(root, file): if package in ordinary_packages: pass elif ((missing_all_files := distributions_per_directives - package_distributions_per_all_files[package]) - and not(missing_all_files == set(['']) and len(distributions_per_directives) < 2)): + and not (missing_all_files == set(['']) and len(distributions_per_directives) < 2)): s = '' if len(missing_all_files) == 1 else 's' print(f'{package}: missing file{s} ' + ', '.join(_all_filename(distribution) for distribution in missing_all_files)) diff --git a/src/sage/misc/random_testing.py b/src/sage/misc/random_testing.py index 917d8789b4d..9dfb866bbc4 100644 --- a/src/sage/misc/random_testing.py +++ b/src/sage/misc/random_testing.py @@ -184,7 +184,7 @@ def test_add_commutes(trials, verbose=False): b = QQ.random_element() if verbose: print("a == {}, b == {} ...".format(a, b)) - assert(a + b == b + a) + assert a + b == b + a if verbose: print("Passes!") @@ -258,6 +258,6 @@ def test_add_is_mul(trials, verbose=False): b = QQ.random_element() if verbose: print("a == {}, b == {} ...".format(a, b)) - assert(a + b == a * b) + assert a + b == a * b if verbose: print("Passes!") diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py index 3415ea83058..4f872f2691b 100644 --- a/src/sage/misc/replace_dot_all.py +++ b/src/sage/misc/replace_dot_all.py @@ -298,7 +298,7 @@ def process_line(location, line, replacements, row_index, verbose=False): sage: from sage.misc.replace_dot_all import * sage: location = os.path.join(sage.env.SAGE_SRC, 'sage/plot/arc.py') sage: replacements = find_replacements(location, package_regex='sage[.]plot[.]all', verbose=True); replacements - [[476, 24, 'from sage.plot.graphics import Graphics']] + [[477, 24, 'from sage.plot.graphics import Graphics']] sage: with open(location, "r") as file: ....: lines = file.readlines() sage: row_index, col_number, *_ = replacements[0] diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index 00e8d96ee9e..be38a27d683 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -3475,11 +3475,11 @@ def verify_same(a, b): """ from sage.structure.element import is_Element if is_Element(a): - assert(a.parent() == b.parent()) + assert a.parent() == b.parent() else: - assert(type(a) is type(b)) + assert type(a) is type(b) if isinstance(a, (RealIntervalFieldElement, ComplexIntervalFieldElement)): - assert(a.endpoints() == b.endpoints()), "Expected %s == %s" % (a, b) + assert a.endpoints() == b.endpoints(), "Expected %s == %s" % (a, b) return if not (a == b): diff --git a/src/sage/misc/sage_timeit.py b/src/sage/misc/sage_timeit.py index 2f9c255543b..89c813e563b 100644 --- a/src/sage/misc/sage_timeit.py +++ b/src/sage/misc/sage_timeit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Accurate timing information for Sage commands @@ -90,9 +89,9 @@ def __repr__(self): 1 loop, best of 2: 3.14 ns per loop """ if self.stats[0] > 1: - s = u"%d loops, best of %d: %.*g %s per loop" % self.stats + s = "%d loops, best of %d: %.*g %s per loop" % self.stats else: - s = u"%d loop, best of %d: %.*g %s per loop" % self.stats + s = "%d loop, best of %d: %.*g %s per loop" % self.stats if isinstance(s, str): return s @@ -217,7 +216,7 @@ def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, prec if stmt == "": return '' - units = [u"s", u"ms", u"μs", u"ns"] + units = ["s", "ms", "μs", "ns"] scaling = [1, 1e3, 1e6, 1e9] timer = timeit_.Timer() diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 4d7c3f9c19c..9dd1defedea 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2256,7 +2256,7 @@ class ParentMethods: if B is None: raise AttributeError except AttributeError: - raise IOError("could not get source code") + raise OSError("could not get source code") return sage_getsourcelines(B) # M should just be the top-most module. # Hence, normally it is just 'sage' @@ -2269,14 +2269,14 @@ class ParentMethods: if B is None: raise AttributeError except AttributeError: - raise IOError("could not get source code") + raise OSError("could not get source code") return sage_getsourcelines(B) lines, base_lineno = sage_getsourcelines(M) # the rest of the function is copied from # inspect.findsource if not lines: - raise IOError('could not get source code') + raise OSError('could not get source code') if inspect.ismodule(obj): return lines, base_lineno @@ -2301,7 +2301,7 @@ class ParentMethods: candidates.sort() return inspect.getblock(lines[candidates[0][1]:]), candidates[0][1]+base_lineno else: - raise IOError('could not find class definition') + raise OSError('could not find class definition') if inspect.ismethod(obj): obj = obj.__func__ @@ -2313,7 +2313,7 @@ class ParentMethods: obj = obj.f_code if inspect.iscode(obj): if not hasattr(obj, 'co_firstlineno'): - raise IOError('could not find function definition') + raise OSError('could not find function definition') pat = re.compile(r'^(\s*def\s)|(.*(? #include "farey.hpp" -#include "farey_symbol.h" +#include "sage/modular/arithgroup/farey_symbol.h" using namespace std; diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index 47ff7c34796..bab9b856ae1 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -117,7 +117,6 @@ def __init__(self, parent, M, check=True, **kwargs): EXAMPLES:: - sage: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup, HeckeTriangleGroupElement sage: lam = PolynomialRing(ZZ, 'lam').gen() sage: M = matrix([[-1, 0], [-lam^4 + 5*lam^2 + lam - 5, -1]]) diff --git a/src/sage/modular/multiple_zeta_F_algebra.py b/src/sage/modular/multiple_zeta_F_algebra.py index 89ac3436cb2..863a369940c 100644 --- a/src/sage/modular/multiple_zeta_F_algebra.py +++ b/src/sage/modular/multiple_zeta_F_algebra.py @@ -563,7 +563,7 @@ def homogeneous_from_vector(self, vec, N): OUTPUT: - an homogeneous element of :func:`F_ring` over this base ring + a homogeneous element of :func:`F_ring` over this base ring .. SEEALSO:: :meth:`F_algebra.homogeneous_to_vector` diff --git a/src/sage/modules/fp_graded/free_module.py b/src/sage/modules/fp_graded/free_module.py index f8f2fac3f72..ea736cc54ea 100755 --- a/src/sage/modules/fp_graded/free_module.py +++ b/src/sage/modules/fp_graded/free_module.py @@ -985,7 +985,7 @@ def resolution(self, k, top_dim=None, verbose=False): INPUT: - - ``k`` -- an non-negative integer + - ``k`` -- a non-negative integer - ``top_dim`` -- stop the computation at this degree. Ignored, for compatibility with :meth:`sage.modules.fp_graded.module.FPModule.resolution`. diff --git a/src/sage/modules/fp_graded/module.py b/src/sage/modules/fp_graded/module.py index cb715523ad7..af7329820de 100755 --- a/src/sage/modules/fp_graded/module.py +++ b/src/sage/modules/fp_graded/module.py @@ -1155,7 +1155,7 @@ def resolution(self, k, top_dim=None, verbose=False): INPUT: - - ``k`` -- an non-negative integer + - ``k`` -- a non-negative integer - ``top_dim`` -- stop the computation at this degree (optional, default ``None``, but required if the algebra is not finite-dimensional) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 7f9679a31d2..6e01cccc82f 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -772,7 +772,7 @@ def span(gens, base_ring=None, check=True, already_echelonized=False): def basis_seq(V, vecs): """ - This converts a list vecs of vectors in V to an Sequence of + This converts a list vecs of vectors in V to a Sequence of immutable vectors. Should it? I.e. in most ``other`` parts of the system the return type diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index b98e6f89c4b..9192c65be92 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -323,9 +323,9 @@ def _module_constructor(self, V, W, check=False): INPUT: - - ``V`` -- an module + - ``V`` -- a module - - ``W`` -- an submodule of ``V`` over the same base ring + - ``W`` -- a submodule of ``V`` over the same base ring - ``check`` -- bool (default: ``False``); diff --git a/src/sage/modules/vector_integer_sparse.pyx b/src/sage/modules/vector_integer_sparse.pyx index 5fe48656495..d5f2958ea59 100644 --- a/src/sage/modules/vector_integer_sparse.pyx +++ b/src/sage/modules/vector_integer_sparse.pyx @@ -91,7 +91,7 @@ cdef Py_ssize_t mpz_binary_search(mpz_t* v, Py_ssize_t n, mpz_t x, Py_ssize_t* i n -- integer (length of array v) x -- mpz_t (integer) OUTPUT: - position of x (as an Py_ssize_t) + position of x (as a Py_ssize_t) ins -- (call be pointer), the insertion point if x is not found. """ cdef Py_ssize_t i, j, k, c diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index db2ff9ee4a5..f366fe1bdf1 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -27,6 +27,7 @@ from sage.misc.superseded import deprecation from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art from sage.data_structures.blas_dict cimport add, negate, scal, axpy +from sage.categories.modules import _Fields cdef class IndexedFreeModuleElement(ModuleElement): @@ -958,6 +959,12 @@ cdef class IndexedFreeModuleElement(ModuleElement): Traceback (most recent call last): ... TypeError: unsupported operand type(s) for /: 'str' and 'CombinatorialFreeModule_with_category.element_class' + + sage: L = LazyPowerSeriesRing(QQ, 't') + sage: t = L.gen() + sage: F = algebras.Free(L, ['A', 'B']) + sage: A, B = F.gens() + sage: f = t*A + t**2*B/2 """ if not isinstance(left, IndexedFreeModuleElement): return NotImplemented @@ -966,7 +973,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): F = self._parent B = self.base_ring() D = self._monomial_coefficients - if not B.is_field(): + if B not in _Fields: return type(self)(F, {k: c._divide_if_possible(x) for k, c in D.items()}) diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd index 612324a5424..e73cc408d09 100644 --- a/src/sage/numerical/mip.pxd +++ b/src/sage/numerical/mip.pxd @@ -3,8 +3,11 @@ cdef extern from *: cdef int REAL = -1 cdef int INTEGER = 0 +from sage.sets.family cimport FiniteFamily from sage.structure.sage_object cimport SageObject from sage.numerical.backends.generic_backend cimport GenericBackend + + cdef class MIPVariable @@ -26,10 +29,8 @@ cdef class MixedIntegerLinearProgram(SageObject): cpdef sum(self, L) noexcept -cdef class MIPVariable(SageObject): +cdef class MIPVariable(FiniteFamily): cdef MixedIntegerLinearProgram _p - cdef dict _dict - cdef bint _dynamic_indices cdef int _vtype cdef str _name cdef object _lower_bound diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 1e6e6caa7bd..803724bf29b 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError): pass -cdef class MIPVariable(SageObject): +cdef class MIPVariable(FiniteFamily): r""" ``MIPVariable`` is a variable used by the class ``MixedIntegerLinearProgram``. @@ -3286,17 +3286,16 @@ cdef class MIPVariable(SageObject): MIPVariable with 0 real components, >= 0 """ - self._dict = {} + super().__init__({}) self._p = mip self._vtype = vtype self._lower_bound = lower_bound self._upper_bound = upper_bound self._name = name - self._dynamic_indices = True if indices is not None: for i in indices: self[i] # creates component - self._dynamic_indices = False + self._keys = indices def __copy__(self): r""" @@ -3398,9 +3397,9 @@ cdef class MIPVariable(SageObject): """ cdef int j - if i in self._dict: - return self._dict[i] - if not self._dynamic_indices: + if i in self._dictionary: + return self._dictionary[i] + if self._keys is not None: raise IndexError("{} does not index a component of {}".format(i, self)) zero = self._p._backend.zero() name = self._name + "[" + str(i) + "]" if self._name else None @@ -3415,7 +3414,7 @@ cdef class MIPVariable(SageObject): name=name) v = self._p.linear_functions_parent()({j : 1}) self._p._variables[v] = j - self._dict[i] = v + self._dictionary[i] = v return v def copy_for_mip(self, mip): @@ -3461,8 +3460,8 @@ cdef class MIPVariable(SageObject): """ cdef MIPVariable cp = type(self)(mip, self._vtype, self._name, self._lower_bound, self._upper_bound) - cp._dict = copy(self._dict) - cp._dynamic_indices = self._dynamic_indices + cp._dictionary = copy(self._dictionary) + cp._keys = self._keys return cp def set_min(self, min): @@ -3501,7 +3500,7 @@ cdef class MIPVariable(SageObject): """ self._lower_bound = min - for v in self._dict.values(): + for v in self._dictionary.values(): self._p.set_min(v,min) def set_max(self, max): @@ -3537,7 +3536,7 @@ cdef class MIPVariable(SageObject): True """ self._upper_bound = max - for v in self._dict.values(): + for v in self._dictionary.values(): self._p.set_max(v,max) def _repr_(self): @@ -3570,9 +3569,9 @@ cdef class MIPVariable(SageObject): """ s = 'MIPVariable{0} with {1} {2} component{3}'.format( " " + self._name if self._name else "", - len(self._dict), + len(self._dictionary), {0:"binary", -1:"real", 1:"integer"}[self._vtype], - "s" if len(self._dict) != 1 else "") + "s" if len(self._dictionary) != 1 else "") if (self._vtype != 0) and (self._lower_bound is not None): s += ', >= {0}'.format(self._lower_bound) if (self._vtype != 0) and (self._upper_bound is not None): @@ -3591,11 +3590,11 @@ cdef class MIPVariable(SageObject): sage: sorted(v.keys()) [0, 1] """ - return self._dict.keys() + return self._dictionary.keys() def items(self): r""" - Return the pairs (keys,value) contained in the dictionary. + Return the pairs (keys, value) contained in the dictionary. EXAMPLES:: @@ -3605,7 +3604,7 @@ cdef class MIPVariable(SageObject): sage: sorted(v.items()) [(0, x_0), (1, x_1)] """ - return self._dict.items() + return self._dictionary.items() def values(self): r""" @@ -3619,7 +3618,7 @@ cdef class MIPVariable(SageObject): sage: sorted(v.values(), key=str) [x_0, x_1] """ - return self._dict.values() + return self._dictionary.values() def mip(self): r""" diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 66fea7f394f..097ac97c1dc 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -1786,7 +1786,7 @@ def send_partial_result(self): r""" Send results to the MapReduce process. - Send the result stored in ``self._res`` to the master an reinitialize it to + Send the result stored in ``self._res`` to the master and reinitialize it to ``master.reduce_init``. EXAMPLES:: diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index c917452e2be..0ce77981f34 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -453,7 +453,8 @@ def make_image(self, frame, filename, **kwds): ....: floor(G.ymin()), ceil(G.ymax())) ....: G.save_image(filename, **kwds) - sage: B = MyAnimation([graphs.CompleteGraph(n) for n in range(7,11)], figsize=5) + sage: B = MyAnimation([graphs.CompleteGraph(n) + ....: for n in range(7,11)], figsize=5) sage: d = B.png() sage: v = os.listdir(d); v.sort(); v ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index 84a62554118..8da333ee12b 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -56,6 +56,7 @@ def __init__(self, x, y, r1, r2, angle, s1, s2, options): EXAMPLES:: + sage: # needs sage.symbolic sage: A = arc((2,3),1,1,pi/4,(0,pi)) sage: A[0].x == 2 True diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 26bb131f713..324a87bd17b 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -398,7 +398,7 @@ def _render_on_subplot(self, subplot): class CheckNthSubPath(): def __init__(self, patch, n): """ - creates an callable object that returns True if the + creates a callable object that returns True if the provided path is the n-th path from the patch. """ self._patch = patch diff --git a/src/sage/plot/colors.py b/src/sage/plot/colors.py index 3281199400b..5eeddc373fd 100644 --- a/src/sage/plot/colors.py +++ b/src/sage/plot/colors.py @@ -215,7 +215,7 @@ def mod_one(x): 0.0 sage: mod_one(-11/7) 0.4285714285714286 - sage: mod_one(pi) + mod_one(-pi) + sage: mod_one(pi) + mod_one(-pi) # needs sage.symbolic 1.0 """ x = float(x) @@ -361,7 +361,7 @@ def rgbcolor(c, space='rgb'): class Color(): def __init__(self, r='#0000ff', g=None, b=None, space='rgb'): """ - An Red-Green-Blue (RGB) color model color object. For most + A Red-Green-Blue (RGB) color model color object. For most consumer-grade devices (e.g., CRTs, LCDs, and printers), as well as internet applications, this is a point in the sRGB absolute color space. The Hue-Saturation-Lightness (HSL), @@ -1146,9 +1146,10 @@ def hue(h, s=1, v=1): This function makes it easy to sample a broad range of colors for graphics:: + sage: # needs sage.symbolic sage: p = Graphics() sage: for phi in xsrange(0, 2 * pi, 1 / pi): - ....: p += plot(sin(x + phi), (x, -7, 7), rgbcolor = hue(phi)) + ....: p += plot(sin(x + phi), (x, -7, 7), rgbcolor=hue(phi)) sage: p Graphics object consisting of 20 graphics primitives diff --git a/src/sage/plot/ellipse.py b/src/sage/plot/ellipse.py index 821906ecf0a..788db0fd76b 100644 --- a/src/sage/plot/ellipse.py +++ b/src/sage/plot/ellipse.py @@ -220,7 +220,7 @@ def plot3d(self): TESTS:: sage: from sage.plot.ellipse import Ellipse - sage: Ellipse(0,0,2,1,pi/4,{}).plot3d() + sage: Ellipse(0,0,2,1,pi/4,{}).plot3d() # needs sage.symbolic Traceback (most recent call last): ... NotImplementedError diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index adcccff059b..b6465b8f9a9 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -64,7 +64,7 @@ def is_Graphics(x): sage: from sage.plot.graphics import is_Graphics sage: is_Graphics(1) False - sage: is_Graphics(disk((0.0, 0.0), 1, (0, pi/2))) + sage: is_Graphics(disk((0.0, 0.0), 1, (0, pi/2))) # needs sage.symbolic True """ return isinstance(x, Graphics) @@ -1359,6 +1359,7 @@ def _set_scale(self, subplot, scale=None, base=None): EXAMPLES:: + sage: # needs sage.symbolic sage: p = plot(x, 1, 10) sage: fig = p.matplotlib() sage: ax = fig.get_axes()[0] @@ -1373,6 +1374,7 @@ def _set_scale(self, subplot, scale=None, base=None): TESTS:: + sage: # needs sage.symbolic sage: p._set_scale(ax, 'log') Traceback (most recent call last): ... @@ -1797,7 +1799,7 @@ def show(self, **kwds): :: - sage: G.show(scale='semilogy', base=(3,2)) # base ignored for x-axis # needs sage.symbolic + sage: G.show(scale='semilogy', base=(3,2)) # base ignored for x-axis # needs sage.symbolic The scale can be also given as a 2-tuple or a 3-tuple.:: @@ -1816,7 +1818,7 @@ def show(self, **kwds): some examples.:: sage: G = list_plot([10**i for i in range(10)]) # long time, needs sage.symbolic - sage: G.show(scale='semilogy') # long time + sage: G.show(scale='semilogy') # long time, needs sage.symbolic :: diff --git a/src/sage/plot/histogram.py b/src/sage/plot/histogram.py index 388c2d1391d..e10229417fc 100644 --- a/src/sage/plot/histogram.py +++ b/src/sage/plot/histogram.py @@ -274,8 +274,8 @@ def histogram(datalist, **options): sage: nv = normalvariate sage: H = histogram([nv(0, 1) for _ in range(1000)], bins=20, density=True, range=[-5, 5]) - sage: P = plot(1/sqrt(2*pi)*e^(-x^2/2), (x, -5, 5), color='red', linestyle='--') - sage: H+P + sage: P = plot(1/sqrt(2*pi)*e^(-x^2/2), (x, -5, 5), color='red', linestyle='--') # needs sage.symbolic + sage: H + P # needs sage.symbolic Graphics object consisting of 2 graphics primitives .. PLOT:: diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py index c945946484a..8e9cb97891f 100644 --- a/src/sage/plot/line.py +++ b/src/sage/plot/line.py @@ -559,6 +559,7 @@ def line2d(points, **options): A purple plot of the Riemann zeta function `\zeta(1/2 + it)`, `0 < t < 30`:: + sage: # needs sage.libs.pari sage.rings.complex_double sage: i = CDF.gen() sage: v = [zeta(0.5 + n/10 * i) for n in range(300)] sage: L = [(z.real(), z.imag()) for z in v] @@ -590,6 +591,7 @@ def line2d(points, **options): A red, blue, and green "cool cat":: + sage: # needs sage.symbolic sage: G = plot(-cos(x), -2, 2, thickness=5, rgbcolor=(0.5,1,0.5)) sage: P = polygon([[1,2], [5,6], [5,0]], rgbcolor=(1,0,0)) sage: Q = polygon([(-x,y) for x,y in P[0]], rgbcolor=(0,0,1)) diff --git a/src/sage/plot/matrix_plot.py b/src/sage/plot/matrix_plot.py index f113f5aaa62..abaeb3e6fc4 100644 --- a/src/sage/plot/matrix_plot.py +++ b/src/sage/plot/matrix_plot.py @@ -535,7 +535,7 @@ def matrix_plot(mat, xrange=None, yrange=None, **options): :: - sage: matrix_plot([[sin(x), cos(x)], [1, 0]]) + sage: matrix_plot([[sin(x), cos(x)], [1, 0]]) # needs sage.symbolic Traceback (most recent call last): ... TypeError: mat must be a Matrix or a two dimensional array diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index b614b2ef3db..b25ba785326 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1763,7 +1763,7 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) .. PLOT:: - g = plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7],[-1/2,0,1/2]]) + g = plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7], [-1/2,0,1/2]]) sphinx_plot(g) :: @@ -1799,7 +1799,7 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) .. PLOT:: - g = plot(x**2, (x,0,3), ticks=[[1,2.5],[0.5,1,2]], tick_formatter=[["$x_1$","$x_2$"],["$y_1$","$y_2$","$y_3$"]]) + g = plot(x**2, (x,0,3), ticks=[[1,2.5],[0.5,1,2]], tick_formatter=[["$x_1$","$x_2$"], ["$y_1$","$y_2$","$y_3$"]]) sphinx_plot(g) You can force Type 1 fonts in your figures by providing the relevant @@ -1891,7 +1891,7 @@ def f(x): return (floor(x)+0.5) / (1-(x-0.5)**2) sage: plot(arcsec(x/2), -2, 2) # plot should be empty; no valid points Graphics object consisting of 0 graphics primitives - sage: plot(sqrt(x^2-1), -2, 2) # [-1, 1] is excluded automatically + sage: plot(sqrt(x^2 - 1), -2, 2) # [-1, 1] is excluded automatically Graphics object consisting of 2 graphics primitives .. PLOT:: @@ -2573,7 +2573,7 @@ def parametric_plot(funcs, *args, **kwargs): is 1, so that circles look like circles. :: sage: t = var('t') - sage: parametric_plot( (cos(t), sin(t)), (t, 0, 2*pi)) + sage: parametric_plot((cos(t), sin(t)), (t, 0, 2*pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: @@ -2613,23 +2613,25 @@ def parametric_plot(funcs, *args, **kwargs): .. PLOT:: - t =var('t') + t = var('t') g = parametric_plot((t, t**2), (t, -4, 4), fill=True) sphinx_plot(g) A filled Hypotrochoid:: - sage: parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], (x,0, 8*pi), fill=True) + sage: parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], + ....: (x, 0, 8*pi), fill=True) Graphics object consisting of 2 graphics primitives .. PLOT:: - g = parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], (x,0, 8*pi), fill=True) + g = parametric_plot([cos(x) + 2 * cos(x/4), sin(x) - 2 * sin(x/4)], (x, 0, 8*pi), fill=True) sphinx_plot(g) :: - sage: parametric_plot( (5*cos(x), 5*sin(x), x), (x,-12, 12), plot_points=150, color="red") # long time + sage: parametric_plot((5*cos(x), 5*sin(x), x), (x, -12, 12), # long time + ....: plot_points=150, color="red") Graphics3d Object .. PLOT:: @@ -2829,7 +2831,8 @@ def polar_plot(funcs, *args, **kwds): Fill the area between two functions:: - sage: polar_plot(cos(4*x) + 1.5, 0, 2*pi, fill=0.5 * cos(4*x) + 2.5, fillcolor='orange') + sage: polar_plot(cos(4*x) + 1.5, 0, 2*pi, fill=0.5 * cos(4*x) + 2.5, + ....: fillcolor='orange') Graphics object consisting of 2 graphics primitives .. PLOT:: @@ -2839,7 +2842,8 @@ def polar_plot(funcs, *args, **kwds): Fill the area between several spirals:: - sage: polar_plot([(1.2+k*0.2)*log(x) for k in range(6)], 1, 3 * pi, fill={0: [1], 2: [3], 4: [5]}) + sage: polar_plot([(1.2+k*0.2)*log(x) for k in range(6)], 1, 3 * pi, + ....: fill={0: [1], 2: [3], 4: [5]}) Graphics object consisting of 9 graphics primitives .. PLOT:: @@ -2895,7 +2899,7 @@ def list_plot(data, plotjoined=False, **kwargs): EXAMPLES:: - sage: list_plot([i^2 for i in range(5)]) # long time + sage: list_plot([i^2 for i in range(5)]) # long time Graphics object consisting of 1 graphics primitive .. PLOT:: @@ -3008,7 +3012,9 @@ def list_plot(data, plotjoined=False, **kwargs): sage: list_plot(x_coords, y_coords) Traceback (most recent call last): ... - TypeError: The second argument 'plotjoined' should be boolean (True or False). If you meant to plot two lists 'x' and 'y' against each other, use 'list_plot(list(zip(x,y)))'. + TypeError: The second argument 'plotjoined' should be boolean (True or False). + If you meant to plot two lists 'x' and 'y' against each other, + use 'list_plot(list(zip(x,y)))'. Dictionaries with numeric keys and values can be plotted:: @@ -3055,12 +3061,12 @@ def list_plot(data, plotjoined=False, **kwargs): Instead this will work. We drop the point `(0,1)`.:: - sage: list_plot(list(zip(range(1,len(yl)), yl[1:])), scale='loglog') # long time + sage: list_plot(list(zip(range(1,len(yl)), yl[1:])), scale='loglog') # long time Graphics object consisting of 1 graphics primitive We use :func:`list_plot_loglog` and plot in a different base.:: - sage: list_plot_loglog(list(zip(range(1,len(yl)), yl[1:])), base=2) # long time + sage: list_plot_loglog(list(zip(range(1,len(yl)), yl[1:])), base=2) # long time Graphics object consisting of 1 graphics primitive .. PLOT:: @@ -3267,22 +3273,22 @@ def plot_semilogy(funcs, *args, **kwds): EXAMPLES:: - sage: plot_semilogy(exp, (1,10)) # long time # plot in semilogy scale, base 10 + sage: plot_semilogy(exp, (1, 10)) # long time # plot in semilogy scale, base 10 Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot_semilogy(exp, (1,10)) # long time # plot in semilogy scale, base 10 + g = plot_semilogy(exp, (1,10)) # long time # plot in semilogy scale, base 10 sphinx_plot(g) :: - sage: plot_semilogy(exp, (1,10), base=2) # long time # with base 2 + sage: plot_semilogy(exp, (1, 10), base=2) # long time # with base 2 Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot_semilogy(exp, (1,10), base=2) # long time # with base 2 + g = plot_semilogy(exp, (1,10), base=2) # long time # with base 2 sphinx_plot(g) """ @@ -3505,13 +3511,14 @@ def reshape(v, n, m): :: - sage: M = [[plot(sin(k*x),(x,-pi,pi)) for k in range(3)],[plot(cos(j*x),(x,-pi,pi)) for j in [3..5]]] + sage: M = [[plot(sin(k*x), (x,-pi,pi)) for k in range(3)], + ....: [plot(cos(j*x), (x,-pi,pi)) for j in [3..5]]] sage: graphics_array(M,6,1) # long time (up to 4s on sage.math, 2012) Graphics Array of size 6 x 1 TESTS:: - sage: L = [plot(sin(k*x),(x,-pi,pi)) for k in [1..3]] + sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in [1..3]] sage: graphics_array(L,0,-1) # indirect doctest Traceback (most recent call last): ... @@ -3620,6 +3627,7 @@ def h(x): return sin(4*x) It is possible to use ``figsize`` to change the size of the plot as a whole:: + sage: x = var('x') sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in [1..3]] sage: ga = graphics_array(L) sage: ga.show(figsize=[5,3]) # smallish and compact @@ -3860,7 +3868,8 @@ def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, TESTS:: sage: from sage.plot.plot import adaptive_refinement - sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, adaptive_recursion=0) + sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01, + ....: adaptive_recursion=0) [] sage: adaptive_refinement(sin, (0,0), (pi,0), adaptive_tolerance=0.01) [(0.125*pi, 0.3826834323650898), (0.1875*pi, 0.5555702330196022), @@ -3881,7 +3890,8 @@ def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, sage: f(x) = sin(1/x) sage: n1 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.01)); n1 15 - sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=10, adaptive_tolerance=0.01)); n2 + sage: n2 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_recursion=10, + ....: adaptive_tolerance=0.01)); n2 79 sage: n3 = len(adaptive_refinement(f, (0,0), (pi,0), adaptive_tolerance=0.001)); n3 26 diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 7588cde2e27..b9dc7b310ca 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -285,13 +285,8 @@ cdef class Graphics3d(SageObject): tachyon.png.save_as(preview_png) else: # Java needs absolute paths - # On cygwin, they should be native ones scene_native = scene_zip - if sys.platform == 'cygwin': - import cygwin - scene_native = cygwin.cygpath(scene_native, 'w') - script = '''set defaultdirectory "{0}"\nscript SCRIPT\n'''.format(scene_native) jdata.export_image(targetfile=preview_png, datafile=script, image_type="PNG", @@ -2003,9 +1998,10 @@ end_scene""".format( This works when faces have more then 3 sides:: - sage: P = polytopes.dodecahedron() # needs sage.geometry.polyhedron - sage: Q = P.plot().all[-1] # needs sage.geometry.polyhedron - sage: print(Q.stl_binary()[:40].decode('ascii')) # needs sage.geometry.polyhedron + sage: # needs sage.geometry.polyhedron sage.groups + sage: P = polytopes.dodecahedron() + sage: Q = P.plot().all[-1] + sage: print(Q.stl_binary()[:40].decode('ascii')) STL binary file / made by SageMath / ### """ import struct @@ -2065,9 +2061,10 @@ end_scene""".format( Now works when faces have more then 3 sides:: - sage: P = polytopes.dodecahedron() # needs sage.geometry.polyhedron - sage: Q = P.plot().all[-1] # needs sage.geometry.polyhedron - sage: print(Q.stl_ascii_string().splitlines()[:7]) # needs sage.geometry.polyhedron + sage: # needs sage.geometry.polyhedron sage.groups + sage: P = polytopes.dodecahedron() + sage: Q = P.plot().all[-1] + sage: print(Q.stl_ascii_string().splitlines()[:7]) ['solid surface', 'facet normal 0.0 0.5257311121191338 0.8506508083520399', ' outer loop', diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index b8e4ddffce6..825c140f8ea 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -530,7 +530,8 @@ def list_plot3d_tuples(v, interpolation_type, **kwds): .. PLOT:: - sphinx_plot(list_plot3d([[1, 1, 1], [1, 2, 1], [0, 1, 3], [1, 0, 4]], point_list=True)) + sphinx_plot(list_plot3d([[1, 1, 1], [1, 2, 1], [0, 1, 3], [1, 0, 4]], + point_list=True)) :: diff --git a/src/sage/plot/plot3d/revolution_plot3d.py b/src/sage/plot/plot3d/revolution_plot3d.py index b140c463c29..f3220dcd1a3 100644 --- a/src/sage/plot/plot3d/revolution_plot3d.py +++ b/src/sage/plot/plot3d/revolution_plot3d.py @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.plot +# sage.doctest: needs sage.plot sage.symbolic """ Surfaces of revolution diff --git a/src/sage/plot/plot3d/transform.pyx b/src/sage/plot/plot3d/transform.pyx index fefbb26dede..b6d8a2f643b 100644 --- a/src/sage/plot/plot3d/transform.pyx +++ b/src/sage/plot/plot3d/transform.pyx @@ -174,11 +174,11 @@ def rotate_arbitrary(v, double theta): sage: rotate_arbitrary((1,2,3), -1).det() 1.0000000000000002 - sage: rotate_arbitrary((1,1,1), 2*pi/3) * vector(RDF, (1,2,3)) # rel tol 2e-15 + sage: rotate_arbitrary((1,1,1), 2*pi/3) * vector(RDF, (1,2,3)) # rel tol 2e-15 # needs sage.symbolic (1.9999999999999996, 2.9999999999999996, 0.9999999999999999) sage: rotate_arbitrary((1,2,3), 5) * vector(RDF, (1,2,3)) # rel tol 2e-15 (1.0000000000000002, 2.0, 3.000000000000001) - sage: rotate_arbitrary((1,1,1), pi/7)^7 # rel tol 2e-15 + sage: rotate_arbitrary((1,1,1), pi/7)^7 # rel tol 2e-15 # needs sage.symbolic [-0.33333333333333337 0.6666666666666671 0.6666666666666665] [ 0.6666666666666665 -0.33333333333333337 0.6666666666666671] [ 0.6666666666666671 0.6666666666666667 -0.33333333333333326] @@ -194,7 +194,7 @@ def rotate_arbitrary(v, double theta): Setup some variables:: - sage: vx,vy,vz,theta = var('x y z theta') + sage: vx,vy,vz,theta = var('x y z theta') # needs sage.symbolic Symbolic rotation matrices about X and Y axis:: @@ -205,37 +205,37 @@ def rotate_arbitrary(v, double theta): way to tell Maxima that `x^2+y^2+z^2=1` which would make for a much cleaner calculation:: - sage: vy = sqrt(1-vx^2-vz^2) + sage: vy = sqrt(1-vx^2-vz^2) # needs sage.symbolic Now we rotate about the `x`-axis so `v` is in the `xy`-plane:: - sage: t = arctan(vy/vz)+pi/2 - sage: m = rotX(t) - sage: new_y = vy*cos(t) - vz*sin(t) + sage: t = arctan(vy/vz)+pi/2 # needs sage.symbolic + sage: m = rotX(t) # needs sage.symbolic + sage: new_y = vy*cos(t) - vz*sin(t) # needs sage.symbolic And rotate about the `z` axis so `v` lies on the `x` axis:: - sage: s = arctan(vx/new_y) + pi/2 - sage: m = rotZ(s) * m + sage: s = arctan(vx/new_y) + pi/2 # needs sage.symbolic + sage: m = rotZ(s) * m # needs sage.symbolic Rotating about `v` in our old system is the same as rotating about the `x`-axis in the new:: - sage: m = rotX(theta) * m + sage: m = rotX(theta) * m # needs sage.symbolic Do some simplifying here to avoid blow-up:: - sage: m = m.simplify_rational() + sage: m = m.simplify_rational() # needs sage.symbolic Now go back to the original coordinate system:: - sage: m = rotZ(-s) * m - sage: m = rotX(-t) * m + sage: m = rotZ(-s) * m # needs sage.symbolic + sage: m = rotX(-t) * m # needs sage.symbolic And simplify every single entry (which is more effective that simplify the whole matrix like above):: - sage: m.parent()([x.simplify_full() for x in m._list()]) # long time; random + sage: m.parent()([x.simplify_full() for x in m._list()]) # random # long time, needs sage.symbolic [ -(cos(theta) - 1)*x^2 + cos(theta) -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x + sin(theta)*abs(z) -((cos(theta) - 1)*x*z^2 + sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z] [ -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x - sin(theta)*abs(z) (cos(theta) - 1)*x^2 + (cos(theta) - 1)*z^2 + 1 -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) - x*z*sin(theta))/abs(z)] [ -((cos(theta) - 1)*x*z^2 - sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) + x*z*sin(theta))/abs(z) -(cos(theta) - 1)*z^2 + cos(theta)] diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index 880452d9430..555fe01b3ef 100755 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -1928,7 +1928,7 @@ def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True): if D > 0: # Indefinite if D.is_square(): b = D.sqrt() - c = ZZ(0) + c = ZZ.zero() # -b/2 < a <= b/2 for a in xsrange((-b/2).floor() + 1, (b/2).floor() + 1): if not primitive_only or (gcd([a, b, c]) == 1): diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index d9c298f5f0a..8946683a988 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -1202,7 +1202,7 @@ def two_adic_symbol(A, val): return [[s[0]+m0] + s[1:] for s in sym + two_adic_symbol(A, val)] -class Genus_Symbol_p_adic_ring(): +class Genus_Symbol_p_adic_ring: r""" Local genus symbol over a `p`-adic ring. @@ -2132,7 +2132,7 @@ def direct_sum(self, other): def excess(self): r""" - Returns the `p`-excess of the quadratic form whose Hessian + Return the `p`-excess of the quadratic form whose Hessian matrix is the symmetric matrix `A`. When `p = 2`, the `p`-excess is called the oddity. @@ -2242,7 +2242,7 @@ def norm(self): sage: G = Genus(matrix(ZZ,2,[0, 1, 1, 0])) sage: G.local_symbol(2).norm() 2 - """ + """ if self.rank() == 0: return ZZ(0) p = self.prime() @@ -2321,7 +2321,7 @@ def compartments(self): return canonical_2_adic_compartments(symbol) -class GenusSymbol_global_ring(): +class GenusSymbol_global_ring: r""" This represents a collection of local genus symbols (at primes) and signature information which represent the genus of a diff --git a/src/sage/quadratic_forms/qfsolve.py b/src/sage/quadratic_forms/qfsolve.py index 85116e43f9e..ff91830874c 100644 --- a/src/sage/quadratic_forms/qfsolve.py +++ b/src/sage/quadratic_forms/qfsolve.py @@ -213,7 +213,7 @@ def solve(self, c=0): if not c: x = qfsolve(M) if isinstance(x, Integer): - raise ArithmeticError("no solution found (local obstruction at {})".format(x)) + raise ArithmeticError(f"no solution found (local obstruction at {x})") return x # If c != 0, define a new quadratic form Q = self - c*z^2 @@ -228,7 +228,7 @@ def solve(self, c=0): x = qfsolve(N) # Raise an error if qfsolve() doesn't find a solution if isinstance(x, Integer): - raise ArithmeticError("no solution found (local obstruction at {})".format(x)) + raise ArithmeticError(f"no solution found (local obstruction at {x})") # Let z be the last term of x, and remove z from x z = x[-1] diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 3db01b9fe2f..c78e7badeb4 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -305,200 +305,200 @@ class QuadraticForm(SageObject): # Routines to compute the p-adic local normal form lazy_import("sage.quadratic_forms.quadratic_form__local_normal_form", [ - "find_entry_with_minimal_scale_at_prime", - "local_normal_form", - "jordan_blocks_by_scale_and_unimodular", - "jordan_blocks_in_unimodular_list_by_scale_power" - ]) + "find_entry_with_minimal_scale_at_prime", + "local_normal_form", + "jordan_blocks_by_scale_and_unimodular", + "jordan_blocks_in_unimodular_list_by_scale_power" + ]) # Routines to perform elementary variable substitutions from sage.quadratic_forms.quadratic_form__variable_substitutions import \ - swap_variables, \ - multiply_variable, \ - divide_variable, \ - scale_by_factor, \ - extract_variables, \ - elementary_substitution, \ - add_symmetric + swap_variables, \ + multiply_variable, \ + divide_variable, \ + scale_by_factor, \ + extract_variables, \ + elementary_substitution, \ + add_symmetric # Routines to compute p-adic field invariants from sage.quadratic_forms.quadratic_form__local_field_invariants import \ - rational_diagonal_form, \ - _rational_diagonal_form_and_transformation, \ - signature_vector, \ - signature, \ - hasse_invariant, \ - hasse_invariant__OMeara, \ - is_hyperbolic, \ - is_anisotropic, \ - is_isotropic, \ - anisotropic_primes, \ - compute_definiteness, \ - compute_definiteness_string_by_determinants, \ - is_positive_definite, \ - is_negative_definite, \ - is_indefinite, \ - is_definite + rational_diagonal_form, \ + _rational_diagonal_form_and_transformation, \ + signature_vector, \ + signature, \ + hasse_invariant, \ + hasse_invariant__OMeara, \ + is_hyperbolic, \ + is_anisotropic, \ + is_isotropic, \ + anisotropic_primes, \ + compute_definiteness, \ + compute_definiteness_string_by_determinants, \ + is_positive_definite, \ + is_negative_definite, \ + is_indefinite, \ + is_definite # Routines to compute local densities by the reduction procedure from sage.quadratic_forms.quadratic_form__local_density_congruence import \ - count_modp_solutions__by_Gauss_sum, \ - local_good_density_congruence_odd, \ - local_good_density_congruence_even, \ - local_good_density_congruence, \ - local_zero_density_congruence, \ - local_badI_density_congruence, \ - local_badII_density_congruence, \ - local_bad_density_congruence, \ - local_density_congruence, \ - local_primitive_density_congruence + count_modp_solutions__by_Gauss_sum, \ + local_good_density_congruence_odd, \ + local_good_density_congruence_even, \ + local_good_density_congruence, \ + local_zero_density_congruence, \ + local_badI_density_congruence, \ + local_badII_density_congruence, \ + local_bad_density_congruence, \ + local_density_congruence, \ + local_primitive_density_congruence # Routines to compute local densities by counting solutions of various types from sage.quadratic_forms.quadratic_form__count_local_2 import \ - count_congruence_solutions_as_vector, \ - count_congruence_solutions, \ - count_congruence_solutions__good_type, \ - count_congruence_solutions__zero_type, \ - count_congruence_solutions__bad_type, \ - count_congruence_solutions__bad_type_I, \ - count_congruence_solutions__bad_type_II + count_congruence_solutions_as_vector, \ + count_congruence_solutions, \ + count_congruence_solutions__good_type, \ + count_congruence_solutions__zero_type, \ + count_congruence_solutions__bad_type, \ + count_congruence_solutions__bad_type_I, \ + count_congruence_solutions__bad_type_II # Routines to be called by the user to compute local densities lazy_import("sage.quadratic_forms.quadratic_form__local_density_interfaces", [ - "local_density", - "local_primitive_density" - ]) + "local_density", + "local_primitive_density" + ]) # Routines for computing with ternary forms from sage.quadratic_forms.quadratic_form__ternary_Tornaria import \ - disc, \ - content, \ - adjoint, \ - antiadjoint, \ - is_adjoint, \ - reciprocal, \ - omega, \ - delta, \ - level__Tornaria, \ - discrec, \ - hasse_conductor, \ - clifford_invariant, \ - clifford_conductor, \ - basiclemma, \ - basiclemmavec, \ - xi, \ - xi_rec, \ - lll, \ - representation_number_list, \ - representation_vector_list, \ - is_zero, \ - is_zero_nonsingular, \ - is_zero_singular + disc, \ + content, \ + adjoint, \ + antiadjoint, \ + is_adjoint, \ + reciprocal, \ + omega, \ + delta, \ + level__Tornaria, \ + discrec, \ + hasse_conductor, \ + clifford_invariant, \ + clifford_conductor, \ + basiclemma, \ + basiclemmavec, \ + xi, \ + xi_rec, \ + lll, \ + representation_number_list, \ + representation_vector_list, \ + is_zero, \ + is_zero_nonsingular, \ + is_zero_singular # Routines to compute the theta function from sage.quadratic_forms.quadratic_form__theta import \ - theta_series, \ - theta_series_degree_2, \ - theta_by_pari, \ - theta_by_cholesky + theta_series, \ + theta_series_degree_2, \ + theta_by_pari, \ + theta_by_cholesky # Routines to compute the product of all local densities lazy_import("sage.quadratic_forms.quadratic_form__siegel_product", [ - "siegel_product" - ]) + "siegel_product" + ]) # Routines to compute p-neighbors from sage.quadratic_forms.quadratic_form__neighbors import \ - find_primitive_p_divisible_vector__random, \ - find_primitive_p_divisible_vector__next, \ - find_p_neighbor_from_vec, \ - neighbor_iteration, \ - orbits_lines_mod_p + find_primitive_p_divisible_vector__random, \ + find_primitive_p_divisible_vector__next, \ + find_p_neighbor_from_vec, \ + neighbor_iteration, \ + orbits_lines_mod_p # Routines to reduce a given quadratic form from sage.quadratic_forms.quadratic_form__reduction_theory import \ - reduced_binary_form1, \ - reduced_ternary_form__Dickson, \ - reduced_binary_form, \ - minkowski_reduction, \ - minkowski_reduction_for_4vars__SP + reduced_binary_form1, \ + reduced_ternary_form__Dickson, \ + reduced_binary_form, \ + minkowski_reduction, \ + minkowski_reduction_for_4vars__SP # Wrappers for Conway-Sloane genus routines (in ./genera/) lazy_import("sage.quadratic_forms.quadratic_form__genus", [ - "global_genus_symbol", - "local_genus_symbol", - "CS_genus_symbol_list" - ]) + "global_genus_symbol", + "local_genus_symbol", + "CS_genus_symbol_list" + ]) # Routines to compute local masses for ZZ. lazy_import("sage.quadratic_forms.quadratic_form__mass", [ - "shimura_mass__maximal", - "GHY_mass__maximal" - ]) + "shimura_mass__maximal", + "GHY_mass__maximal" + ]) lazy_import("sage.quadratic_forms.quadratic_form__mass__Siegel_densities", [ - "mass__by_Siegel_densities", - "Pall_mass_density_at_odd_prime", - "Watson_mass_at_2", - "Kitaoka_mass_at_2", - "mass_at_two_by_counting_mod_power" - ]) + "mass__by_Siegel_densities", + "Pall_mass_density_at_odd_prime", + "Watson_mass_at_2", + "Kitaoka_mass_at_2", + "mass_at_two_by_counting_mod_power" + ]) lazy_import("sage.quadratic_forms.quadratic_form__mass__Conway_Sloane_masses", [ - "parity", - "is_even", - "is_odd", - "conway_species_list_at_odd_prime", - "conway_species_list_at_2", - "conway_octane_of_this_unimodular_Jordan_block_at_2", - "conway_diagonal_factor", - "conway_cross_product_doubled_power", - "conway_type_factor", - "conway_p_mass", - "conway_standard_p_mass", - "conway_standard_mass", - "conway_mass" -# conway_generic_mass, \ -# conway_p_mass_adjustment - ]) + "parity", + "is_even", + "is_odd", + "conway_species_list_at_odd_prime", + "conway_species_list_at_2", + "conway_octane_of_this_unimodular_Jordan_block_at_2", + "conway_diagonal_factor", + "conway_cross_product_doubled_power", + "conway_type_factor", + "conway_p_mass", + "conway_standard_p_mass", + "conway_standard_mass", + "conway_mass" + # conway_generic_mass, \ + # conway_p_mass_adjustment + ]) # Routines to check local representability of numbers lazy_import("sage.quadratic_forms.quadratic_form__local_representation_conditions", [ - "local_representation_conditions", - "is_locally_universal_at_prime", - "is_locally_universal_at_all_primes", - "is_locally_universal_at_all_places", - "is_locally_represented_number_at_place", - "is_locally_represented_number" - ]) + "local_representation_conditions", + "is_locally_universal_at_prime", + "is_locally_universal_at_all_primes", + "is_locally_universal_at_all_places", + "is_locally_represented_number_at_place", + "is_locally_represented_number" + ]) # Routines to make a split local covering of the given quadratic form. from sage.quadratic_forms.quadratic_form__split_local_covering import \ - cholesky_decomposition, \ - vectors_by_length, \ - complementary_subform_to_vector, \ - split_local_cover + cholesky_decomposition, \ + vectors_by_length, \ + complementary_subform_to_vector, \ + split_local_cover # Routines to make automorphisms of the given quadratic form. lazy_import("sage.quadratic_forms.quadratic_form__automorphisms", [ - "basis_of_short_vectors", - "short_vector_list_up_to_length", - "short_primitive_vector_list_up_to_length", - "_compute_automorphisms", - "automorphism_group", - "automorphisms", - "number_of_automorphisms", - "set_number_of_automorphisms" - ]) + "basis_of_short_vectors", + "short_vector_list_up_to_length", + "short_primitive_vector_list_up_to_length", + "_compute_automorphisms", + "automorphism_group", + "automorphisms", + "number_of_automorphisms", + "set_number_of_automorphisms" + ]) # Routines to test the local and global equivalence/isometry of two quadratic forms. from sage.quadratic_forms.quadratic_form__equivalence_testing import \ - is_globally_equivalent_to, \ - is_locally_equivalent_to, \ - has_equivalent_Jordan_decomposition_at_prime, \ - is_rationally_isometric + is_globally_equivalent_to, \ + is_locally_equivalent_to, \ + has_equivalent_Jordan_decomposition_at_prime, \ + is_rationally_isometric # Routines for solving equations of the form Q(x) = c. lazy_import("sage.quadratic_forms.qfsolve", [ - "solve" - ]) + "solve" + ]) # Genus lazy_import("sage.quadratic_forms.genera.genus", @@ -617,7 +617,7 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ # Verify the size of the matrix is an integer >= 0 n = ZZ(n) if n < 0: - raise ValueError("the size must be a non-negative integer, not {}".format(n)) + raise ValueError(f"the size must be a non-negative integer, not {n}") # Store the relevant variables N = n * (n + 1) // 2 @@ -738,7 +738,7 @@ def _repr_(self): if (i > j): out_str += "* " else: - out_str += str(self[i,j]) + " " + out_str += str(self[i, j]) + " " out_str += "]" return out_str @@ -762,7 +762,7 @@ def _latex_(self): if (i > j): out_str += " * & " else: - out_str += str(self[i,j]) + " & " + out_str += str(self[i, j]) + " & " # if i < (n-1): # out_str += "\\" out_str += "\\end{array} \\right]" @@ -1051,7 +1051,7 @@ def __call__(self, v): if is_Matrix(v): # Check that v has the correct number of rows if v.nrows() != n: - raise TypeError("the matrix must have {} rows".format(n)) + raise TypeError(f"the matrix must have {n} rows") # Create the new quadratic form m = v.ncols() @@ -1061,7 +1061,7 @@ def __call__(self, v): elif (is_Vector(v) or isinstance(v, (list, tuple))): # Check the vector/tuple/list has the correct length if not (len(v) == n): - raise TypeError("your vector needs to have length {}".format(n)) + raise TypeError(f"your vector needs to have length {n}") # TO DO: Check that the elements can be coerced into the base ring of Q -- on first elt. if len(v) > 0: @@ -1291,7 +1291,7 @@ def gcd(self): raise TypeError("the given quadratic form must be defined over ZZ") return GCD(self.coefficients()) - def polynomial(self,names='x'): + def polynomial(self, names='x'): r""" Return the quadratic form as a polynomial in `n` variables. @@ -1335,7 +1335,7 @@ def polynomial(self,names='x'): M = matrix(B, n) for i in range(n): for j in range(i, n): - M[i,j] = self[i,j] + M[i, j] = self[i, j] try: R = PolynomialRing(self.base_ring(), names, n) except Exception: @@ -1379,17 +1379,16 @@ def from_polynomial(poly): raise ValueError('polynomial has monomials of degree != 2') base = R.base_ring() vs = R.gens() - coeffs = [] - for i, v in enumerate(vs): - for w in vs[i:]: - coeffs.append(poly.monomial_coefficient(v*w)) + coeffs = [poly.monomial_coefficient(v * w) + for i, v in enumerate(vs) for w in vs[i:]] return QuadraticForm(base, len(vs), coeffs) - def is_primitive(self): + def is_primitive(self) -> bool: """ - Determines if the given integer-valued form is primitive - (i.e. not an integer (`> 1`) multiple of another integer-valued - quadratic form). + Determine if the given integer-valued form is primitive. + + This means not an integer (`> 1`) multiple of another integer-valued + quadratic form. EXAMPLES:: @@ -1399,9 +1398,8 @@ def is_primitive(self): sage: Q = QuadraticForm(ZZ, 2, [2,4,8]) sage: Q.is_primitive() False - """ - return (self.gcd() == 1) + return self.gcd() == 1 def primitive(self): r""" @@ -1630,9 +1628,9 @@ def level(self): for i in range(self.dim()): for j in range(i, self.dim()): if (i == j): - inv_denoms += [denominator(mat_inv[i,j] / 2)] + inv_denoms += [denominator(mat_inv[i, j] / 2)] else: - inv_denoms += [denominator(mat_inv[i,j])] + inv_denoms += [denominator(mat_inv[i, j])] lvl = LCM(inv_denoms) lvl = Ideal(self.base_ring()(lvl)).gen() ############################################################## diff --git a/src/sage/quadratic_forms/quadratic_form__automorphisms.py b/src/sage/quadratic_forms/quadratic_form__automorphisms.py index 89b2c079478..4b6e3594f83 100644 --- a/src/sage/quadratic_forms/quadratic_form__automorphisms.py +++ b/src/sage/quadratic_forms/quadratic_form__automorphisms.py @@ -78,10 +78,8 @@ def basis_of_short_vectors(self, show_lengths=False): vector_list_by_length[l].append(vector([-x for x in v])) # Make a matrix from the column vectors (in order of ascending length). - sorted_list = [] - for i in range(len(vector_list_by_length)): - for v in vector_list_by_length[i]: - sorted_list.append(v) + sorted_list = [v for i in range(len(vector_list_by_length)) + for v in vector_list_by_length[i]] sorted_matrix = Matrix(sorted_list).transpose() # Determine a basis of vectors of minimal length @@ -205,7 +203,7 @@ def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): parilens = pari(r"(M,v) -> vector(#v, i, (v[i]~ * M * v[i])\2)")(self, parilist) # Sort the vectors into lists by their length - vec_sorted_list = [list() for i in range(len_bound)] + vec_sorted_list = [[] for i in range(len_bound)] for i in range(len(parilist)): length = int(parilens[i]) # In certain trivial cases, PARI can sometimes return longer @@ -349,7 +347,7 @@ def automorphisms(self): 48 sage: 2^3 * factorial(3) 48 - sage: len(Q.automorphisms()) + sage: len(Q.automorphisms()) # needs sage.libs.gap 48 :: @@ -357,14 +355,14 @@ def automorphisms(self): sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() 16 - sage: aut = Q.automorphisms() - sage: len(aut) + sage: aut = Q.automorphisms() # needs sage.libs.gap + sage: len(aut) # needs sage.libs.gap 16 - sage: all(Q(M) == Q for M in aut) + sage: all(Q(M) == Q for M in aut) # needs sage.libs.gap True sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) - sage: sorted(Q.automorphisms()) + sage: sorted(Q.automorphisms()) # needs sage.libs.gap [ [-1 0 0] [1 0 0] [ 0 -1 0] [0 1 0] diff --git a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py index 148fcbd21f2..2bd4df8f890 100644 --- a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py +++ b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py @@ -442,9 +442,9 @@ def is_rationally_isometric(self, other, return_matrix=False): sage: V.is_rationally_isometric(W) Traceback (most recent call last): ... - NotImplementedError: This only tests regular forms + NotImplementedError: this only tests regular forms - Forms must have the same base ring otherwise a `TypeError` is raised:: + Forms must have the same base ring otherwise a :class:`TypeError` is raised:: sage: # needs sage.rings.number_field sage: K1. = QuadraticField(5) @@ -491,7 +491,7 @@ def is_rationally_isometric(self, other, return_matrix=False): True """ if self.Gram_det() == 0 or other.Gram_det() == 0: - raise NotImplementedError("This only tests regular forms") + raise NotImplementedError("this only tests regular forms") if self.base_ring() != other.base_ring(): raise TypeError("forms must have the same base ring.") diff --git a/src/sage/quadratic_forms/quadratic_form__genus.py b/src/sage/quadratic_forms/quadratic_form__genus.py index 8727645b453..52468e9889a 100644 --- a/src/sage/quadratic_forms/quadratic_form__genus.py +++ b/src/sage/quadratic_forms/quadratic_form__genus.py @@ -133,10 +133,8 @@ def CS_genus_symbol_list(self, force_recomputation=False): pass # Otherwise recompute and cache the list - list_of_CS_genus_symbols = [] - - for p in prime_divisors(2 * self.det()): - list_of_CS_genus_symbols.append(self.local_genus_symbol(p)) + list_of_CS_genus_symbols = [self.local_genus_symbol(p) + for p in prime_divisors(2 * self.det())] self.__CS_genus_symbol_list = list_of_CS_genus_symbols return list_of_CS_genus_symbols diff --git a/src/sage/quadratic_forms/quadratic_form__local_representation_conditions.py b/src/sage/quadratic_forms/quadratic_form__local_representation_conditions.py index 888f4ae2832..c337d2994a0 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_representation_conditions.py +++ b/src/sage/quadratic_forms/quadratic_form__local_representation_conditions.py @@ -17,7 +17,7 @@ from sage.rings.rational_field import QQ -class QuadraticFormLocalRepresentationConditions(): +class QuadraticFormLocalRepresentationConditions: """ A class for dealing with the local conditions of a quadratic form, and checking local representability of numbers. diff --git a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py index a99eb439e0f..9133a84b76f 100644 --- a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py +++ b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py @@ -197,8 +197,8 @@ def Watson_mass_at_2(self): s_max = max(scale_list) # Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale - diag_dict = dict((i, Null_Form) for i in range(s_min - 2, s_max + 4)) # Initialize with the zero form - dim2_dict = dict((i, Null_Form) for i in range(s_min, s_max + 4)) # Initialize with the zero form + diag_dict = {i: Null_Form for i in range(s_min - 2, s_max + 4)} # Initialize with the zero form + dim2_dict = {i: Null_Form for i in range(s_min, s_max + 4)} # Initialize with the zero form for s, L in Jordan_Blocks: i = 0 while i < L.dim() - 1 and L[i, i + 1] == 0: # Find where the 2x2 blocks start @@ -210,8 +210,8 @@ def Watson_mass_at_2(self): diag_dict[s] = L # Step 2: Compute three dictionaries of invariants (for n_j, m_j, nu_j) - n_dict = dict((j, 0) for j in range(s_min + 1, s_max + 2)) - m_dict = dict((j, 0) for j in range(s_min, s_max + 4)) + n_dict = {j: 0 for j in range(s_min + 1, s_max + 2)} + m_dict = {j: 0 for j in range(s_min, s_max + 4)} for s, L in Jordan_Blocks: n_dict[s + 1] = L.dim() if diag_dict[s].dim() == 0: @@ -219,8 +219,8 @@ def Watson_mass_at_2(self): else: m_dict[s + 1] = ZZ(L.dim() - 1) // ZZ(2) - nu_dict = dict((j, n_dict[j + 1] - 2 * m_dict[j + 1]) - for j in range(s_min, s_max + 1)) + nu_dict = {j: n_dict[j + 1] - 2 * m_dict[j + 1] + for j in range(s_min, s_max + 1)} nu_dict[s_max + 1] = 0 # Step 3: Compute the e_j dictionary @@ -280,8 +280,8 @@ def Kitaoka_mass_at_2(self): s_max = max(scale_list) # Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale - diag_dict = dict((i, Null_Form) for i in range(s_min - 2, s_max + 4)) # Initialize with the zero form - dim2_dict = dict((i, Null_Form) for i in range(s_min, s_max + 4)) # Initialize with the zero form + diag_dict = {i: Null_Form for i in range(s_min - 2, s_max + 4)} # Initialize with the zero form + dim2_dict = {i: Null_Form for i in range(s_min, s_max + 4)} # Initialize with the zero form for s, L in Jordan_Blocks: i = 0 while i < L.dim() - 1 and L[i, i + 1] == 0: # Find where the 2x2 blocks start diff --git a/src/sage/quadratic_forms/ternary_qf.py b/src/sage/quadratic_forms/ternary_qf.py index 2f3a57fa713..2808c04548f 100644 --- a/src/sage/quadratic_forms/ternary_qf.py +++ b/src/sage/quadratic_forms/ternary_qf.py @@ -921,7 +921,7 @@ def find_p_neighbor_from_vec(self, p, v, mat=False): Test that it works with (0, 0, 1):: - sage: Q.find_p_neighbor_from_vec(3, (0,0,1)) + sage: Q.find_p_neighbor_from_vec(3, (0,0,1)) # needs sage.libs.pari Ternary quadratic form with integer coefficients: [1 3 3] [-2 0 -1] diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 10ccdc0c2c8..ba17b9244b4 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -419,13 +419,6 @@ def threejs_offline_scripts(self): script = os.path.join(THREEJS_DIR, '{}/three.min.js'.format(_required_threejs_version())) - if sys.platform == 'cygwin': - import cygwin - - def normpath(p): - return 'file:///' + cygwin.cygpath(p, 'w').replace('\\', '/') - script = normpath(script) - return '\n'.format(script) diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index 9a27c9d61eb..61d8238332d 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -604,7 +604,7 @@ def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, precision=None, normalized=True): r""" Return the asymptotic expansion of the coefficients of - an power series with specified pole and logarithmic singularity. + a power series with specified pole and logarithmic singularity. More precisely, this extracts the `n`-th coefficient diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index 447c25fa64d..6613c5bdf32 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -1193,7 +1193,7 @@ def __invert__(self): OUTPUT: - An growth element. + A growth element. .. NOTE:: diff --git a/src/sage/rings/bernoulli_mod_p.pyx b/src/sage/rings/bernoulli_mod_p.pyx index e9bf4fbf358..ee73d6ad209 100644 --- a/src/sage/rings/bernoulli_mod_p.pyx +++ b/src/sage/rings/bernoulli_mod_p.pyx @@ -23,6 +23,8 @@ AUTHOR: # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.arith.misc import is_prime, primitive_root + cimport sage.rings.fast_arith import sage.rings.fast_arith cdef sage.rings.fast_arith.arith_int arith_int @@ -135,12 +137,12 @@ def bernoulli_mod_p(int p): if p <= 2: raise ValueError("p (=%s) must be a prime >= 3" % p) - if not sage.arith.all.is_prime(p): + if not is_prime(p): raise ValueError("p (=%s) must be a prime" % p) cdef int g, gSqr, gInv, gInvSqr, isOdd - g = sage.arith.all.primitive_root(p) + g = primitive_root(p) gInv = arith_int.c_inverse_mod_int(g, p) gSqr = (( g) * g) % p gInvSqr = (( gInv) * gInv) % p @@ -303,7 +305,7 @@ def bernoulli_mod_p_single(long p, long k): if p <= 2: raise ValueError("p (=%s) must be a prime >= 3" % p) - if not sage.arith.all.is_prime(p): + if not is_prime(p): raise ValueError("p (=%s) must be a prime" % p) cdef long x = bernmm_bern_modp(p, k) diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 4d9353e57b8..48d585384f6 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -89,9 +89,8 @@ from numbers import Integral -from sage.categories.fields import Fields +from sage.categories.rings import Rings from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.rings.ring import CommutativeRing from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -100,6 +99,7 @@ from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.fraction_field import FractionField from sage.structure.element import FieldElement, parent +from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.interfaces.gp import Gp @@ -110,7 +110,7 @@ def CFiniteSequences(base_ring, names=None, category=None): r""" - Return the ring of C-Finite sequences. + Return the commutative ring of C-Finite sequences. The ring is defined over a base ring (`\ZZ` or `\QQ` ) and each element is represented by its ordinary generating function (ogf) @@ -154,15 +154,15 @@ def CFiniteSequences(base_ring, names=None, category=None): elif len(names) > 1: raise NotImplementedError("Multidimensional o.g.f. not implemented.") if category is None: - category = Fields() - if not (base_ring in (QQ, ZZ)): + category = Rings().Commutative() + if base_ring not in [QQ, ZZ]: raise ValueError("O.g.f. base not rational.") polynomial_ring = PolynomialRing(base_ring, names) return CFiniteSequences_generic(polynomial_ring, category) class CFiniteSequence(FieldElement, - metaclass=InheritComparisonClasscallMetaclass): + metaclass=InheritComparisonClasscallMetaclass): r""" Create a C-finite sequence given its ordinary generating function. @@ -174,7 +174,7 @@ class CFiniteSequence(FieldElement, OUTPUT: - - A CFiniteSequence object + A CFiniteSequence object EXAMPLES:: @@ -247,7 +247,7 @@ class CFiniteSequence(FieldElement, @staticmethod def __classcall_private__(cls, ogf): r""" - Ensures that elements created by :class:`CFiniteSequence` have the same + Ensure that elements created by :class:`CFiniteSequence` have the same parent than the ones created by the parent itself and follow the category framework (they should be instance of :class:`CFiniteSequences` automatic element class). @@ -299,9 +299,8 @@ def __classcall_private__(cls, ogf): sage: f4.parent() The ring of C-Finite sequences in y over Rational Field """ - br = ogf.base_ring() - if not (br in (QQ, ZZ)): + if br not in [QQ, ZZ]: br = QQ # if the base ring of the o.g.f is not QQ, we force it to QQ and see if the o.g.f converts nicely # trying to figure out the ogf variables @@ -388,7 +387,6 @@ def __init__(self, parent, ogf): # determine start values (may be different from _get_item_ values) alen = max(self._deg, num.degree() + 1) R = LaurentSeriesRing(br, parent.variable_name(), default_prec=alen) - rem = num % den if den != 1: self._a = R(num / den).list() else: @@ -400,7 +398,7 @@ def __init__(self, parent, ogf): self._ogf = ogf - def _repr_(self): + def _repr_(self) -> str: """ Return textual definition of sequence. @@ -414,10 +412,8 @@ def _repr_(self): if self._deg == 0: if self.ogf() == 0: return 'Constant infinite sequence 0.' - else: - return 'Finite sequence ' + str(self._a) + ', offset = ' + str(self._off) - else: - return 'C-finite sequence, generated by ' + str(self.ogf()) + return 'Finite sequence ' + str(self._a) + ', offset = ' + str(self._off) + return 'C-finite sequence, generated by ' + str(self.ogf()) def __hash__(self): r""" @@ -656,7 +652,8 @@ def __getitem__(self, key): if isinstance(key, slice): m = max(key.start, key.stop) return [self[ii] for ii in range(*key.indices(m + 1))] - elif isinstance(key, Integral): + + if isinstance(key, Integral): n = key - self._off if n < 0: return 0 @@ -679,8 +676,8 @@ def __getitem__(self, key): den = P((den * nden).list()[::2]) n //= 2 return wp + num[0] / den[0] - else: - raise TypeError("invalid argument type") + + raise TypeError("invalid argument type") def ogf(self): """ @@ -726,14 +723,14 @@ def denominator(self): """ return self.ogf().denominator() - def recurrence_repr(self): + def recurrence_repr(self) -> str: """ Return a string with the recurrence representation of the C-finite sequence. OUTPUT: - - A string + A string EXAMPLES:: @@ -892,7 +889,7 @@ def closed_form(self, n='n'): return expr -class CFiniteSequences_generic(CommutativeRing, UniqueRepresentation): +class CFiniteSequences_generic(Parent, UniqueRepresentation): r""" The class representing the ring of C-Finite Sequences @@ -912,13 +909,13 @@ class CFiniteSequences_generic(CommutativeRing, UniqueRepresentation): def __init__(self, polynomial_ring, category): r""" - Create the ring of CFiniteSequences over ``base_ring`` + Create the ring of CFiniteSequences over ``base_ring``. INPUT: - ``base_ring`` -- the base ring for the o.g.f (either ``QQ`` or ``ZZ``) - ``names`` -- an iterable of variables (should contain only one variable) - - ``category`` -- the category of the ring (default: ``Fields()``) + - ``category`` -- the category of the ring (default: ``Rings().Commutative()``) TESTS:: @@ -940,11 +937,14 @@ def __init__(self, polynomial_ring, category): base_ring = polynomial_ring.base_ring() self._polynomial_ring = polynomial_ring self._fraction_field = FractionField(self._polynomial_ring) - CommutativeRing.__init__(self, base_ring, self._polynomial_ring.gens(), category) + if category is None: + category = Rings().Commutative() + Parent.__init__(self, base_ring, names=self._polynomial_ring.gens(), + category=category) def _repr_(self): r""" - Return the string representation of ``self`` + Return the string representation of ``self``. EXAMPLES:: @@ -956,7 +956,7 @@ def _repr_(self): def _element_constructor_(self, ogf): r""" - Construct a C-Finite Sequence + Construct a C-Finite Sequence. INPUT: @@ -986,9 +986,9 @@ def _element_constructor_(self, ogf): ogf = self.fraction_field()(ogf) return self.element_class(self, ogf) - def ngens(self): + def ngens(self) -> int: r""" - Return the number of generators of ``self`` + Return the number of generators of ``self``. EXAMPLES:: @@ -1026,6 +1026,18 @@ def gen(self, i=0): raise ValueError("{} has only one generator (i=0)".format(self)) return self.polynomial_ring().gen() + def gens(self) -> tuple: + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: C. = CFiniteSequences(QQ) + sage: C.gens() + (x,) + """ + return (self.gen(0),) + def an_element(self): r""" Return an element of C-Finite Sequences. @@ -1043,7 +1055,7 @@ def an_element(self): x = self.gen() return self((2 - x) / (1 - x - x**2)) - def __contains__(self, x): + def __contains__(self, x) -> bool: """ Return ``True`` if x is an element of ``CFiniteSequences`` or canonically coerces to this ring. @@ -1194,7 +1206,7 @@ def guess(self, sequence, algorithm='sage'): sage: r = C.guess([1,2,3,4,5]) Traceback (most recent call last): ... - ValueError: Sequence too short for guessing. + ValueError: sequence too short for guessing With Berlekamp-Massey, if an odd number of values is given, the last one is dropped. So with an odd number of values the result may not generate the last value:: @@ -1205,10 +1217,11 @@ def guess(self, sequence, algorithm='sage'): [1, 2, 4, 8, 16] """ S = self.polynomial_ring() + if algorithm == 'bm': from sage.matrix.berlekamp_massey import berlekamp_massey if len(sequence) < 2: - raise ValueError('Sequence too short for guessing.') + raise ValueError('sequence too short for guessing') R = PowerSeriesRing(QQ, 'x') if len(sequence) % 2: sequence.pop() @@ -1217,10 +1230,11 @@ def guess(self, sequence, algorithm='sage'): numerator = R(S(sequence) * denominator, prec=l).truncate() return CFiniteSequence(numerator / denominator) - elif algorithm == 'pari': + + if algorithm == 'pari': global _gp if len(sequence) < 6: - raise ValueError('Sequence too short for guessing.') + raise ValueError('sequence too short for guessing') if _gp is None: _gp = Gp() _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\ @@ -1236,37 +1250,35 @@ def guess(self, sequence, algorithm='sage'): den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1]) if num == 0: return 0 - else: - return CFiniteSequence(num / den) - else: - from sage.matrix.constructor import matrix - from sage.arith.misc import integer_ceil as ceil - from numpy import trim_zeros - seq = sequence[:] - while seq and sequence[-1] == 0: - seq.pop() - l = len(seq) - if l == 0: - return 0 - if l < 6: - raise ValueError('Sequence too short for guessing.') - - hl = ceil(ZZ(l) / 2) - A = matrix([sequence[k: k + hl] for k in range(hl)]) - K = A.kernel() - if K.dimension() == 0: - return 0 - R = PolynomialRing(QQ, 'x') - den = R(trim_zeros(K.basis()[-1].list()[::-1])) - if den == 1: - return 0 - offset = next((i for i, x in enumerate(sequence) if x), None) - S = PowerSeriesRing(QQ, 'x', default_prec=l - offset) - num = S(R(sequence) * den).truncate(ZZ(l) // 2 + 1) - if num == 0 or sequence != S(num / den).list(): - return 0 - else: - return CFiniteSequence(num / den) + return CFiniteSequence(num / den) + + from sage.matrix.constructor import matrix + from sage.arith.misc import integer_ceil as ceil + from numpy import trim_zeros + seq = sequence[:] + while seq and sequence[-1] == 0: + seq.pop() + l = len(seq) + if l == 0: + return 0 + if l < 6: + raise ValueError('sequence too short for guessing') + + hl = ceil(ZZ(l) / 2) + A = matrix([sequence[k: k + hl] for k in range(hl)]) + K = A.kernel() + if K.dimension() == 0: + return 0 + R = PolynomialRing(QQ, 'x') + den = R(trim_zeros(K.basis()[-1].list()[::-1])) + if den == 1: + return 0 + offset = next((i for i, x in enumerate(sequence) if x), None) + S = PowerSeriesRing(QQ, 'x', default_prec=l - offset) + num = S(R(sequence) * den).truncate(ZZ(l) // 2 + 1) + if num == 0 or sequence != S(num / den).list(): + return 0 + return CFiniteSequence(num / den) r""" diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index e804964f501..4b9e7f2aefa 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -290,7 +290,7 @@ cdef class ComplexIntervalFieldElement(FieldElement): Exact and nearly exact points are still visible:: - sage: # needs sage.plot + sage: # needs sage.plot sage.symbolic sage: plot(CIF(pi, 1), color='red') + plot(CIF(1, e), color='purple') + plot(CIF(-1, -1)) Graphics object consisting of 6 graphics primitives diff --git a/src/sage/rings/continued_fraction.py b/src/sage/rings/continued_fraction.py index a813816ac50..0021dc2745d 100644 --- a/src/sage/rings/continued_fraction.py +++ b/src/sage/rings/continued_fraction.py @@ -208,12 +208,20 @@ import numbers import sage.rings.abc + +from sage.misc.lazy_import import lazy_import from sage.rings.infinity import Infinity from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.structure.richcmp import rich_to_bool, richcmp_method from sage.structure.sage_object import SageObject +lazy_import('sage.combinat.words.abstract_word', 'Word_class') +lazy_import('sage.combinat.words.finite_word', 'FiniteWord_class') +lazy_import('sage.combinat.words.infinite_word', 'InfiniteWord_class') +lazy_import('sage.combinat.words.word', 'Word') + + ZZ_0 = Integer(0) ZZ_1 = Integer(1) ZZ_m1 = Integer(-1) @@ -2490,7 +2498,10 @@ def continued_fraction_list(x, type="std", partial_convergents=False, cf = None - from sage.rings.real_mpfr import RealLiteral + try: + from sage.rings.real_mpfr import RealLiteral + except ImportError: + RealLiteral = () if isinstance(x, RealLiteral): from sage.rings.real_mpfi import RealIntervalField x = RealIntervalField(x.prec())(x) @@ -2661,7 +2672,6 @@ def continued_fraction(x, value=None): pass # input for finite or ultimately periodic partial quotient expansion - from sage.combinat.words.finite_word import FiniteWord_class if isinstance(x, FiniteWord_class): x = list(x) @@ -2675,12 +2685,10 @@ def continued_fraction(x, value=None): return ContinuedFraction_periodic(x1, x2) # input for infinite partial quotient expansion - from sage.combinat.words.infinite_word import InfiniteWord_class from sage.misc.lazy_list import lazy_list_generic if isinstance(x, (lazy_list_generic, InfiniteWord_class)): return ContinuedFraction_infinite(x, value) - from sage.combinat.words.abstract_word import Word_class if isinstance(x, Word_class): raise ValueError("word with unknown length cannot be converted " "to continued fractions") diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index 2cd1bc1b1aa..d7d4fe6ebdf 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -1,7 +1,7 @@ r""" Derivations -Let `A` be a ring and `B` be an bimodule over `A`. +Let `A` be a ring and `B` be a bimodule over `A`. A derivation `d : A \to B` is an additive map that satisfies the Leibniz rule @@ -281,7 +281,7 @@ def __init__(self, domain, codomain, twist=None): if twist is not None: if not (isinstance(twist, Map) and twist.category_for().is_subcategory(Rings())): - raise TypeError("the twisting homomorphism must be an homomorphism of rings") + raise TypeError("the twisting homomorphism must be a homomorphism of rings") if twist.domain() is not domain: map = twist.domain().coerce_map_from(domain) if map is None: @@ -1249,7 +1249,7 @@ def precompose(self, morphism): sage: D.precompose(D) Traceback (most recent call last): ... - TypeError: you must give an homomorphism of rings + TypeError: you must give a homomorphism of rings TESTS:: @@ -1266,7 +1266,7 @@ def precompose(self, morphism): else: raise TypeError("the given ring does not coerce to the domain of the derivation") elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())): - raise TypeError("you must give an homomorphism of rings") + raise TypeError("you must give a homomorphism of rings") M = RingDerivationModule(morphism.domain(), parent.defining_morphism() * morphism) arg = [ ] for x in M.dual_basis(): @@ -1316,7 +1316,7 @@ def postcompose(self, morphism): sage: Dx.precompose(Dy) Traceback (most recent call last): ... - TypeError: you must give an homomorphism of rings + TypeError: you must give a homomorphism of rings """ parent = self.parent() @@ -1326,7 +1326,7 @@ def postcompose(self, morphism): else: raise TypeError("the codomain of the derivation does not coerce to the given ring") elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())): - raise TypeError("you must give an homomorphism of rings") + raise TypeError("you must give a homomorphism of rings") M = RingDerivationModule(parent.domain(), morphism * parent.defining_morphism()) arg = [ ] for x in M.dual_basis(): @@ -2281,7 +2281,7 @@ def precompose(self, morphism): else: raise TypeError("the given ring does not coerce to the domain of the derivation") elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())): - raise TypeError("you must give an homomorphism of rings") + raise TypeError("you must give a homomorphism of rings") M = RingDerivationModule(morphism.domain(), parent.defining_morphism() * morphism, parent.twisting_morphism() * morphism) return M(self._scalar) @@ -2324,7 +2324,7 @@ def postcompose(self, morphism): else: raise TypeError("the codomain of the derivation does not coerce to the given ring") elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())): - raise TypeError("you must give an homomorphism of rings") + raise TypeError("you must give a homomorphism of rings") M = RingDerivationModule(parent.domain(), morphism * parent.defining_morphism(), morphism * parent.twisting_morphism()) return M(morphism(self._scalar)) diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 8af0867a9f5..ab029287ba6 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -586,8 +586,8 @@ cdef class FinitePolyExtElement(FiniteRingElement): Finite Field in a of size 19^2 sage: b = a**20 sage: p = FinitePolyExtElement.charpoly(b, "x", algorithm="pari") - sage: q = FinitePolyExtElement.charpoly(b, "x", algorithm="matrix") # needs sage.modules - sage: q == p # needs sage.modules + sage: q = FinitePolyExtElement.charpoly(b, "x", algorithm="matrix") # needs sage.modules + sage: q == p # needs sage.modules True sage: p x^2 + 15*x + 4 diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 7619ec7185f..a4727e3311d 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -56,10 +56,11 @@ from cysignals.signals cimport sig_on, sig_off from cypari2.paridecl cimport * +import sage.arith.misc + from sage.misc.randstate cimport current_randstate from sage.rings.finite_rings.element_pari_ffelt cimport FiniteFieldElement_pari_ffelt from sage.structure.richcmp cimport richcmp -import sage.arith.all from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack @@ -414,9 +415,6 @@ cdef class Cache_givaro(Cache_base): # Reduce to pari e = e.__pari__() - elif isinstance(e, sage.libs.gap.element.GapElement_FiniteField): - return e.sage(ring=self.parent) - elif isinstance(e, GapElement): from sage.libs.gap.libgap import libgap return libgap(e).sage(ring=self.parent) @@ -434,6 +432,13 @@ cdef class Cache_givaro(Cache_base): return ret else: + try: + from sage.libs.gap.element import GapElement_FiniteField + except ImportError: + pass + else: + if isinstance(e, GapElement_FiniteField): + return e.sage(ring=self.parent) raise TypeError("unable to coerce %r" % type(e)) cdef GEN t @@ -524,7 +529,7 @@ cdef class Cache_givaro(Cache_base): INPUT: - - ``n`` -- integer representation of an finite field element + - ``n`` -- integer representation of a finite field element OUTPUT: @@ -1568,7 +1573,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): raise ArithmeticError("Multiplicative order of 0 not defined.") n = (self._cache).order_c() - 1 order = Integer(1) - for p, e in sage.arith.all.factor(n): + for p, e in sage.arith.misc.factor(n): # Determine the power of p that divides the order. a = self**(n / (p**e)) while a != 1: diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 60d275ea5ae..bb490c163a8 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -274,7 +274,6 @@ cdef class Cache_ntl_gf2e(Cache_base): cdef FiniteField_ntl_gf2eElement x cdef FiniteField_ntl_gf2eElement g cdef Py_ssize_t i - from sage.libs.gap.element import GapElement_FiniteField if is_IntegerMod(e): e = e.lift() @@ -334,14 +333,18 @@ cdef class Cache_ntl_gf2e(Cache_base): # Reduce to pari e = e.__pari__() - elif isinstance(e, GapElement_FiniteField): - return e.sage(ring=self._parent) - elif isinstance(e, GapElement): from sage.libs.gap.libgap import libgap return libgap(e).sage(ring=self._parent) else: + try: + from sage.libs.gap.element import GapElement_FiniteField + except ImportError: + pass + else: + if isinstance(e, GapElement_FiniteField): + return e.sage(ring=self._parent) raise TypeError("unable to coerce %r" % type(e)) cdef GEN t diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index ca262f6f668..4f37f6c3043 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -611,6 +611,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): EXAMPLES:: + sage: # needs sage.modules sage: k. = GF(2^20, impl='pari_ffelt') sage: e = k.random_element() sage: f = loads(dumps(e)) diff --git a/src/sage/rings/finite_rings/finite_field_givaro.py b/src/sage/rings/finite_rings/finite_field_givaro.py index 2dd96beeb34..9f6abdb40b4 100644 --- a/src/sage/rings/finite_rings/finite_field_givaro.py +++ b/src/sage/rings/finite_rings/finite_field_givaro.py @@ -462,7 +462,7 @@ def int_to_log(self, n): INPUT: - - ``n`` -- integer representation of an finite field element + - ``n`` -- integer representation of a finite field element OUTPUT: diff --git a/src/sage/rings/finite_rings/galois_group.py b/src/sage/rings/finite_rings/galois_group.py index 293803f1e98..f11b72bc681 100644 --- a/src/sage/rings/finite_rings/galois_group.py +++ b/src/sage/rings/finite_rings/galois_group.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" Galois groups of Finite Fields """ @@ -109,7 +110,7 @@ def _element_constructor_(self, x, check=True): Frob^2 sage: G(G.gens()[0]) Frob - sage: G([(1,3,2)]) + sage: G([(1,3,2)]) # needs sage.libs.gap Frob^2 sage: G(k.hom(k.gen()^3, k)) Frob diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 0bb4ce21f4b..eb35543a20e 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -617,13 +617,13 @@ def multiplicative_subgroups(self): EXAMPLES:: sage: # needs sage.groups - sage: Integers(5).multiplicative_subgroups() + sage: Integers(5).multiplicative_subgroups() # optional - gap_package_polycyclic ((2,), (4,), ()) - sage: Integers(15).multiplicative_subgroups() + sage: Integers(15).multiplicative_subgroups() # optional - gap_package_polycyclic ((11, 7), (11, 4), (2,), (11,), (14,), (7,), (4,), ()) - sage: Integers(2).multiplicative_subgroups() + sage: Integers(2).multiplicative_subgroups() # optional - gap_package_polycyclic ((),) - sage: len(Integers(341).multiplicative_subgroups()) + sage: len(Integers(341).multiplicative_subgroups()) # optional - gap_package_polycyclic 80 TESTS:: @@ -632,7 +632,7 @@ def multiplicative_subgroups(self): ((),) sage: IntegerModRing(2).multiplicative_subgroups() # needs sage.groups ((),) - sage: IntegerModRing(3).multiplicative_subgroups() # needs sage.groups + sage: IntegerModRing(3).multiplicative_subgroups() # needs sage.groups # optional - gap_package_polycyclic ((2,), ()) """ return tuple(tuple(g.value() for g in H.gens()) diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index e84b303769a..b79416eb1d7 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -128,7 +128,7 @@ First over a small non-prime field:: Residue field in ubar of Fractional ideal (47, 517/55860*u^5 + 235/3724*u^4 + 9829/13965*u^3 + 54106/13965*u^2 + 64517/27930*u + 755696/13965) - sage: I.groebner_basis() + sage: I.groebner_basis() # needs sage.libs.singular [X + (-19*ubar^2 - 5*ubar - 17)*Y] And now over a large prime field:: @@ -144,11 +144,11 @@ And now over a large prime field:: 4398046511119 sage: S. = PolynomialRing(Rf, order='lex') sage: I = ideal([2*X - Y^2, Y + Z]) - sage: I.groebner_basis() + sage: I.groebner_basis() # needs sage.libs.singular [X + 2199023255559*Z^2, Y + Z] sage: S. = PolynomialRing(Rf, order='deglex') sage: I = ideal([2*X - Y^2, Y + Z]) - sage: I.groebner_basis() + sage: I.groebner_basis() # needs sage.libs.singular [Z^2 + 4398046511117*X, Y + Z] """ diff --git a/src/sage/rings/finite_rings/residue_field_pari_ffelt.pyx b/src/sage/rings/finite_rings/residue_field_pari_ffelt.pyx index f21a3b8d9bb..e9962c3ccde 100644 --- a/src/sage/rings/finite_rings/residue_field_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/residue_field_pari_ffelt.pyx @@ -118,7 +118,7 @@ class ResidueFiniteField_pari_ffelt(ResidueField_generic, FiniteField_pari_ffelt sage: R. = GF(5)[]; P = R.ideal(4*t^12 + 3*t^11 + 4*t^10 + t^9 + t^8 + 3*t^7 + 2*t^6 + 3*t^4 + t^3 + 3*t^2 + 2) sage: k. = P.residue_field() - sage: V = k.vector_space(map=False); v = V([1,2,3,4,5,6,7,8,9,0,1,2]); k(v) # indirect doctest # needs sage.modules + sage: V = k.vector_space(map=False); v = V([1,2,3,4,5,6,7,8,9,0,1,2]); k(v) # indirect doctest # needs sage.modules 2*a^11 + a^10 + 4*a^8 + 3*a^7 + 2*a^6 + a^5 + 4*a^3 + 3*a^2 + 2*a + 1 """ try: diff --git a/src/sage/rings/function_field/function_field_rational.py b/src/sage/rings/function_field/function_field_rational.py index e763a673a15..226d87d084c 100644 --- a/src/sage/rings/function_field/function_field_rational.py +++ b/src/sage/rings/function_field/function_field_rational.py @@ -138,7 +138,7 @@ def __init__(self, constant_field, names, category=None): sage: K. = FunctionField(CC); K # needs sage.rings.real_mpfr Rational function field in t over Complex Field with 53 bits of precision - sage: TestSuite(K).run() # long time (5s) # needs sage.rings.real_mpfr + sage: TestSuite(K).run() # long time (5s) # needs sage.rings.real_mpfr sage: FunctionField(QQ[I], 'alpha') # needs sage.rings.number_field Rational function field in alpha over diff --git a/src/sage/rings/generic.py b/src/sage/rings/generic.py index 83f923cee5c..99bf690bef6 100644 --- a/src/sage/rings/generic.py +++ b/src/sage/rings/generic.py @@ -20,6 +20,7 @@ class ProductTree: (the famous *Fast* Fourier Transform) can be implemented as follows using the :meth:`remainders` method of this class:: + sage: # needs sage.rings.finite_rings sage: from sage.rings.generic import ProductTree sage: F = GF(65537) sage: a = F(1111) @@ -62,6 +63,7 @@ class ProductTree: :: + sage: # needs sage.libs.pari sage: vs = prime_range(100) sage: tree = ProductTree(vs) sage: tree.root().factor() @@ -71,7 +73,7 @@ class ProductTree: We can access the individual layers of the tree:: - sage: tree.layers + sage: tree.layers # needs sage.libs.pari [(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97), (6, 35, 143, 323, 667, 1147, 1763, 2491, 3599, 4757, 5767, 7387, 97), (210, 46189, 765049, 4391633, 17120443, 42600829, 97), @@ -87,8 +89,8 @@ def __init__(self, leaves): EXAMPLES:: sage: from sage.rings.generic import ProductTree - sage: vs = prime_range(100) - sage: tree = ProductTree(vs) + sage: vs = prime_range(100) # needs sage.libs.pari + sage: tree = ProductTree(vs) # needs sage.libs.pari """ V = tuple(leaves) self.layers = [V] @@ -182,6 +184,7 @@ def remainders(self, x): EXAMPLES:: + sage: # needs sage.libs.pari sage: from sage.rings.generic import ProductTree sage: vs = prime_range(100) sage: tree = ProductTree(vs) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index c990c200e57..415b0ff7d80 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -4076,7 +4076,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): """ if self.is_zero(): raise ArithmeticError("support of 0 not defined") - return sage.arith.all.prime_factors(self) + from sage.arith.misc import prime_factors + + return prime_factors(self) def coprime_integers(self, m): """ @@ -7539,7 +7541,7 @@ def _check_global_dummy_Integer(): # It operates on the following principles: # # - The pool starts out empty. -# - When an new integer is needed, one from the pool is returned +# - When a new integer is needed, one from the pool is returned # if available, otherwise a new Integer object is created # - When an integer is collected, it will add it to the pool # if there is room, otherwise it will be deallocated. diff --git a/src/sage/rings/localization.py b/src/sage/rings/localization.py index 20c090da681..ea7534349fa 100644 --- a/src/sage/rings/localization.py +++ b/src/sage/rings/localization.py @@ -584,7 +584,7 @@ class Localization(IntegralDomain, UniqueRepresentation): Accordingly, this class is inherited from :class:`IntegralDomain` and can only be used in that context. Furthermore, the base ring should support :meth:`sage.structure.element.CommutativeRingElement.divides` and the exact division operator `//` (:meth:`sage.structure.element.Element.__floordiv__`) in order to guarantee - an successful application. + a successful application. INPUT: diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index a9cb8eda29e..c65f75a248c 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1255,7 +1255,7 @@ cdef class RingHomomorphism(RingMap): Ideals in quotient rings over ``QQbar`` do not support reduction yet, so the graph is constructed in the ambient ring instead:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.singular sage.rings.number_field sage: A. = QQbar['z,w'].quotient('z*w - 1') sage: B. = QQbar['x,y'].quotient('2*x^2 + y^2 - 1') sage: f = A.hom([QQbar(2).sqrt()*x + QQbar(I)*y, @@ -1447,7 +1447,7 @@ cdef class RingHomomorphism(RingMap): An isomorphism between the algebraic torus and the circle over a number field:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.singular sage.rings.number_field sage: K. = QuadraticField(-1) sage: A. = K['z,w'].quotient('z*w - 1') sage: B. = K['x,y'].quotient('x^2 + y^2 - 1') diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 3d96666e222..8ceaa9c5154 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -204,24 +204,29 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +import sage.misc.latex as latex -from sage.rings.ring import CommutativeRing -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.infinity import infinity +from sage.rings.multi_power_series_ring_element import MPowerSeries from sage.rings.polynomial.polynomial_ring import is_PolynomialRing +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.polynomial.term_order import TermOrder from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic, is_PowerSeriesRing - -from sage.rings.infinity import infinity -import sage.misc.latex as latex +from sage.rings.ring import CommutativeRing from sage.structure.nonexact import Nonexact -from sage.rings.multi_power_series_ring_element import MPowerSeries from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() + from sage.categories.integral_domains import IntegralDomains _IntegralDomains = IntegralDomains() +try: + from sage.rings.laurent_series_ring import LaurentSeriesRing +except ImportError: + LaurentSeriesRing = () + def is_MPowerSeriesRing(x): """ @@ -731,8 +736,8 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): return False if all(v == 0 for v in im_gens): return True - from .laurent_series_ring import is_LaurentSeriesRing - if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing(codomain) or is_LaurentSeriesRing(codomain): + + if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing(codomain) or isinstance(codomain, LaurentSeriesRing): try: B = all(v.valuation() > 0 or v.is_nilpotent() for v in im_gens) except NotImplementedError: diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 68ca166f6df..4b6b201375f 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -1071,7 +1071,7 @@ def reduction_step_complex_case(place, B0, list_of_gens, torsion_gen, c13): A = A.transpose() - # Note that l is the an lower bound on the square of the magnitude of the shortest non-zero vector in the lattice generated by A + # Note that l is the lower bound on the square of the magnitude of the shortest non-zero vector in the lattice generated by A l = minimal_vector(A, zero_vector(ZZ,n+1)) # Checking hypotheses of Lemma 5.3 in our paper: diff --git a/src/sage/rings/number_field/bdd_height.py b/src/sage/rings/number_field/bdd_height.py index 0681aaef58e..2b3d18a6578 100644 --- a/src/sage/rings/number_field/bdd_height.py +++ b/src/sage/rings/number_field/bdd_height.py @@ -531,7 +531,7 @@ def packet_height(n, pair, u): possible_norm_set.add(m * class_group_rep_norms[n]) bdd_ideals = bdd_norm_pr_ideal_gens(K, possible_norm_set) - # Stores it in form of an dictionary and gives lambda(g)_approx for key g + # Stores it in form of a dictionary and gives lambda(g)_approx for key g for norm in possible_norm_set: gens = bdd_ideals[norm] for g in gens: diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 2e6b876e541..9ea602dc1aa 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -10,9 +10,9 @@ """ from sage.structure.sage_object import SageObject -from sage.groups.galois_group import _alg_key, GaloisGroup_perm, GaloisSubgroup_perm +from sage.groups.galois_group import _alg_key +from sage.groups.galois_group_perm import GaloisGroup_perm, GaloisSubgroup_perm from sage.groups.perm_gps.permgroup import standardize_generator - from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.misc.superseded import deprecation from sage.misc.cachefunc import cached_method diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index e34606e085b..6e6d0fc36e4 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -82,7 +82,6 @@ import sage.libs.ntl.all as ntl -import sage.interfaces.gap import sage.rings.complex_mpfr from sage.rings.polynomial.polynomial_element import Polynomial @@ -92,14 +91,14 @@ import sage.rings.real_double import sage.rings.real_lazy -from sage.rings.finite_rings.integer_mod import mod - - +from sage.arith.misc import euler_phi, factor, gcd, next_prime from sage.misc.fast_methods import WithEqualityById from sage.misc.functional import is_odd, lift from sage.misc.lazy_import import lazy_import from sage.misc.misc_c import prod +from sage.misc.sage_eval import sage_eval from sage.rings.infinity import Infinity +from sage.rings.finite_rings.integer_mod import mod from sage.categories.number_fields import NumberFields import sage.rings.ring @@ -204,7 +203,6 @@ def proof_flag(t): from sage.misc.latex import latex -import sage.arith.all as arith import sage.rings.infinity as infinity from sage.rings.rational import Rational from sage.rings.integer import Integer @@ -371,18 +369,20 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, One can embed into any other field:: - sage: K. = NumberField(x^3-2, embedding=CC.gen()-0.6) + sage: K. = NumberField(x^3 - 2, embedding=CC.gen() - 0.6) sage: CC(a) -0.629960524947436 + 1.09112363597172*I - sage: L = Qp(5) # needs sage.rings.padics + + sage: # needs sage.rings.padics + sage: L = Qp(5) sage: f = polygen(L)^3 - 2 - sage: K. = NumberField(x^3-2, embedding=f.roots()[0][0]) + sage: K. = NumberField(x^3 - 2, embedding=f.roots()[0][0]) sage: a + L(1) 4 + 2*5^2 + 2*5^3 + 3*5^4 + 5^5 + 4*5^6 + 2*5^8 + 3*5^9 + 4*5^12 + 4*5^14 + 4*5^15 + 3*5^16 + 5^17 + 5^18 + 2*5^19 + O(5^20) - sage: L. = NumberField(x^6-x^2+1/10, embedding=1) - sage: K. = NumberField(x^3-x+1/10, embedding=b^2) - sage: a+b + sage: L. = NumberField(x^6 - x^2 + 1/10, embedding=1) + sage: K. = NumberField(x^3 - x + 1/10, embedding=b^2) + sage: a + b b^2 + b sage: CC(a) == CC(b)^2 True @@ -409,7 +409,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, Note that the codomain of the embedding must be ``QQbar`` or ``AA`` for this to work (see :trac:`20184`):: - sage: N. = NumberField(x^3 + 2,embedding=1) + sage: N. = NumberField(x^3 + 2, embedding=1) sage: 1 < g False sage: g > 1 @@ -493,7 +493,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, The following has been fixed in :trac:`8800`:: sage: P. = QQ[] - sage: K. = NumberField(x^3 - 5,embedding=0) + sage: K. = NumberField(x^3 - 5, embedding=0) sage: L. = K.extension(x^2 + a) sage: F, R = L.construction() sage: F(R) == L # indirect doctest @@ -1309,7 +1309,7 @@ class NumberField_generic(WithEqualityById, number_field_base.NumberField): This example was suggested on sage-nt; see :trac:`18942`:: sage: G = DirichletGroup(80) # needs sage.modular - sage: for chi in G: # long time # needs sage.modular + sage: for chi in G: # long time # needs sage.modular ....: D = ModularSymbols(chi, 2, -1).cuspidal_subspace().new_subspace().decomposition() ....: for f in D: ....: elt = f.q_eigenform(10, 'alpha')[3] @@ -1561,7 +1561,7 @@ def construction(self): :: sage: P. = QQ[] - sage: K. = NumberField(x^3-5,embedding=0) + sage: K. = NumberField(x^3-5, embedding=0) sage: L. = K.extension(x^2+a) sage: a*b a*b @@ -1940,7 +1940,7 @@ def _convert_from_str(self, x): sage: k('theta25^3 + (1/3)*theta25') -1/3*theta25 - 1 """ - w = sage.misc.all.sage_eval(x, locals=self.gens_dict()) + w = sage_eval(x, locals=self.gens_dict()) if not (is_Element(w) and w.parent() is self): return self(w) else: @@ -3317,6 +3317,7 @@ def conductor(self, check_abelian=True): EXAMPLES:: + sage: # needs sage.groups sage: K = CyclotomicField(27) sage: k = K.subfields(9)[0][0] sage: k.conductor() @@ -3387,27 +3388,29 @@ def dirichlet_group(self): EXAMPLES:: + sage: # needs sage.groups sage.modular sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^3 + x^2 - 36*x - 4) sage: K.conductor() 109 - sage: K.dirichlet_group() + sage: K.dirichlet_group() # optional - gap_package_polycyclic [Dirichlet character modulo 109 of conductor 1 mapping 6 |--> 1, Dirichlet character modulo 109 of conductor 109 mapping 6 |--> zeta3, Dirichlet character modulo 109 of conductor 109 mapping 6 |--> -zeta3 - 1] + sage: # needs sage.modular sage: K = CyclotomicField(44) sage: L = K.subfields(5)[0][0] - sage: X = L.dirichlet_group() - sage: X + sage: X = L.dirichlet_group(); X # optional - gap_package_polycyclic [Dirichlet character modulo 11 of conductor 1 mapping 2 |--> 1, Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5, Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^2, Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3, - Dirichlet character modulo 11 of conductor 11 mapping 2 |--> -zeta5^3 - zeta5^2 - zeta5 - 1] - sage: X[4]^2 + Dirichlet character modulo 11 of conductor 11 + mapping 2 |--> -zeta5^3 - zeta5^2 - zeta5 - 1] + sage: X[4]^2 # optional - gap_package_polycyclic Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3 - sage: X[4]^2 in X + sage: X[4]^2 in X # optional - gap_package_polycyclic True """ # todo : turn this into an abelian group rather than a list. @@ -3962,10 +3965,8 @@ def primes_of_bounded_norm(self, B): from sage.rings.fast_arith import prime_range if self is QQ: - # return arith.primes(B+1) return prime_range(B + 1, algorithm="pari_isprime") else: - # P = [pp for p in arith.primes(B+1) for pp in self.primes_above(p)] P = (pp for p in prime_range(B + 1, algorithm="pari_isprime") for pp in self.primes_above(p)) P = [p for p in P if p.norm() <= B] @@ -4016,11 +4017,9 @@ def primes_of_bounded_norm_iter(self, B): from sage.rings.fast_arith import prime_range if self is QQ: - # for p in arith.primes(B+1): for p in prime_range(B + 1, algorithm="pari_isprime"): yield p else: - # for p in arith.primes(B+1): for p in prime_range(B + 1, algorithm="pari_isprime"): for pp in self.primes_above(p): if pp.norm() <= B: @@ -4133,7 +4132,7 @@ def completely_split_primes(self, B=200): from sage.rings.fast_arith import prime_range from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.arith.misc import factor + split_primes = [] for p in prime_range(B): Fp = GF(p) @@ -4530,7 +4529,8 @@ def _gap_init_(self): """ if not self.is_absolute(): raise NotImplementedError("Currently, only simple algebraic extensions are implemented in gap") - G = sage.interfaces.gap.gap + from sage.interfaces.gap import gap as G + q = self.polynomial() if q.variable_name() != 'E': return 'CallFuncList(function() local %s,E; %s:=Indeterminate(%s,"%s"); E:=AlgebraicExtension(%s,%s,"%s"); return E; end,[])' % (q.variable_name(), q.variable_name(), G(self.base_ring()).name(), q.variable_name(), G(self.base_ring()).name(), repr(self.polynomial()), str(self.gen())) @@ -4608,6 +4608,7 @@ def class_group(self, proof=None, names='c'): Class groups of Hecke polynomials tend to be very small:: + sage: # needs sage.modular sage: f = ModularForms(97, 2).T(2).charpoly() sage: f.factor() (x - 3) * (x^3 + 4*x^2 + 3*x - 1) * (x^4 - 3*x^3 - x^2 + 6*x - 1) @@ -5980,7 +5981,7 @@ def decomposition_type(self, p): sage: R. = ZZ[] sage: K. = NumberField(x^20 + 3*x^18 + 15*x^16 + 28*x^14 + 237*x^12 + 579*x^10 ....: + 1114*x^8 + 1470*x^6 + 2304*x^4 + 1296*x^2 + 729) - sage: K.is_galois() + sage: K.is_galois() # needs sage.groups True sage: K.discriminant().factor() 2^20 * 3^10 * 53^10 @@ -6147,6 +6148,7 @@ def is_galois(self): EXAMPLES:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: NumberField(x^2 + 1, 'i').is_galois() True @@ -6173,6 +6175,7 @@ def is_abelian(self): EXAMPLES:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: NumberField(x^2 + 1, 'i').is_abelian() True @@ -6233,6 +6236,7 @@ def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=Non EXAMPLES:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: k. = NumberField(x^2 - 14) # a Galois extension sage: G = k.galois_group(); G @@ -6244,13 +6248,14 @@ def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=Non sage: G.artin_symbol(k.primes_above(3)[0]) (1,2) - sage: k. = NumberField(x^3 - x + 1) # not Galois + sage: # needs sage.groups + sage: k. = NumberField(x^3 - x + 1) # not Galois sage: G = k.galois_group(names='c'); G Galois group 3T2 (S3) with order 6 of x^3 - x + 1 sage: G.gen(0) (1,2,3)(4,5,6) - sage: NumberField(x^3 + 2*x + 1, 'a').galois_group(algorithm='magma') # optional - magma + sage: NumberField(x^3 + 2*x + 1, 'a').galois_group(algorithm='magma') # optional - magma, needs sage.groups Galois group Transitive group number 2 of degree 3 of the Number Field in a with defining polynomial x^3 + 2*x + 1 @@ -6259,6 +6264,7 @@ def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=Non :: + sage: # needs sage.groups sage: K. = NumberField(x^3 - 2) sage: L. = K.galois_closure(); L Number Field in b1 with defining polynomial x^6 + 108 @@ -6284,6 +6290,7 @@ def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=Non change in the future, so it's better to explicitly call :meth:`absolute_field` if that is the desired behavior:: + sage: # needs sage.groups sage: x = polygen(QQ) sage: K. = NumberField(x^2 + 1) sage: R. = PolynomialRing(K) @@ -6298,6 +6305,7 @@ def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=Non We check that the changes in :trac:`28782` won't break code that used v1 Galois groups:: + sage: # needs sage.groups sage: G = NumberField(x^3 - 2, 'a').galois_group(type="pari") ...DeprecationWarning: the different Galois types have been merged into one class See https://github.com/sagemath/sage/issues/28782 for details. @@ -7427,6 +7435,7 @@ def S_unit_solutions(self, S=[], prec=106, include_exponents=False, include_boun EXAMPLES:: + sage: # needs sage.rings.padics sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^2 + x + 1) sage: S = K.primes_above(3) @@ -7435,7 +7444,8 @@ def S_unit_solutions(self, S=[], prec=106, include_exponents=False, include_boun You can get the exponent vectors:: - sage: K.S_unit_solutions(S, include_exponents=True) # random, due to ordering + sage: # needs sage.rings.padics + sage: K.S_unit_solutions(S, include_exponents=True) # random, due to ordering [((2, 1), (4, 0), xi + 2, -xi - 1), ((5, -1), (4, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), ((5, 0), (1, 0), -xi, xi + 1), @@ -7443,6 +7453,7 @@ def S_unit_solutions(self, S=[], prec=106, include_exponents=False, include_boun And the computed bound:: + sage: # needs sage.rings.padics sage: solutions, bound = K.S_unit_solutions(S, prec=100, include_bound=True) sage: bound 7 @@ -7545,7 +7556,7 @@ def zeta(self, n=2, all=False): # First check if the degree of K is compatible # with an inclusion QQ(\zeta_n) -> K. - if sage.arith.all.euler_phi(n).divides(K.absolute_degree()): + if euler_phi(n).divides(K.absolute_degree()): w, zeta_w = self.pari_nf().nfrootsof1() w = w.sage() zeta_w = K(zeta_w) @@ -7786,17 +7797,17 @@ def valuation(self, prime): sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^2 + 1) - sage: K.valuation(2) + sage: K.valuation(2) # needs sage.rings.padics 2-adic valuation It can also be unramified in ``R``:: - sage: K.valuation(3) + sage: K.valuation(3) # needs sage.rings.padics 3-adic valuation A ``prime`` that factors into pairwise distinct factors, results in an error:: - sage: K.valuation(5) + sage: K.valuation(5) # needs sage.rings.padics Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not @@ -7805,12 +7816,12 @@ def valuation(self, prime): The valuation can also be selected by giving a valuation on the base ring that extends uniquely:: - sage: CyclotomicField(5).valuation(ZZ.valuation(5)) + sage: CyclotomicField(5).valuation(ZZ.valuation(5)) # needs sage.rings.padics 5-adic valuation When the extension is not unique, this does not work:: - sage: K.valuation(ZZ.valuation(5)) + sage: K.valuation(ZZ.valuation(5)) # needs sage.rings.padics Traceback (most recent call last): ... ValueError: The valuation Gauss valuation induced by 5-adic valuation does not @@ -7821,6 +7832,7 @@ def valuation(self, prime): `G` to infinity. This lets us specify which extension of the 5-adic valuation we care about in the above example:: + sage: # needs sage.rings.padics sage: R. = QQ[] sage: G5 = GaussValuation(R, QQ.valuation(5)) sage: v = K.valuation(G5.augmentation(x + 2, infinity)) @@ -7831,6 +7843,7 @@ def valuation(self, prime): Note that you get the same valuation, even if you write down the pseudo-valuation differently:: + sage: # needs sage.rings.padics sage: ww = K.valuation(G5.augmentation(x + 3, infinity)) sage: w is ww True @@ -7841,6 +7854,7 @@ def valuation(self, prime): completion, i.e., if it is not possible to write down one of the factors within the number field:: + sage: # needs sage.rings.padics sage: v = G5.augmentation(x + 3, 1) sage: K.valuation(v) [ 5-adic valuation, v(x + 3) = 1 ]-adic valuation @@ -7848,7 +7862,7 @@ def valuation(self, prime): Finally, ``prime`` can also be a fractional ideal of a number field if it singles out an extension of a `p`-adic valuation of the base field:: - sage: K.valuation(K.fractional_ideal(a + 1)) + sage: K.valuation(K.fractional_ideal(a + 1)) # needs sage.rings.padics 2-adic valuation .. SEEALSO:: @@ -8224,7 +8238,7 @@ def _coerce_from_other_number_field(self, x): The following was fixed in :trac:`8800`:: sage: P. = QQ[] - sage: K. = NumberField(x^3 - 5,embedding=0) + sage: K. = NumberField(x^3 - 5, embedding=0) sage: L. = K.extension(x^2 + a) sage: F,R = L.construction() sage: F(R) == L #indirect doctest @@ -9121,6 +9135,7 @@ def _galois_closure_and_embedding(self, names=None): For medium-sized Galois groups of fields with small discriminants, this computation is feasible:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^6 + 4*x^2 + 2) sage: K.galois_group().order() @@ -9168,6 +9183,7 @@ def galois_closure(self, names=None, map=False): EXAMPLES:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^4 - 2) sage: M = K.galois_closure('b'); M @@ -9179,12 +9195,14 @@ def galois_closure(self, names=None, map=False): :: + sage: # needs sage.groups sage: phi = K.embeddings(L)[0] sage: phi(K.0) 1/120*a2^5 + 19/60*a2 sage: phi(K.0).minpoly() x^4 - 2 + sage: # needs sage.groups sage: L, phi = K.galois_closure('b', map=True) sage: L Number Field in b with defining polynomial x^8 + 28*x^4 + 2500 @@ -9196,6 +9214,7 @@ def galois_closure(self, names=None, map=False): A cyclotomic field is already Galois:: + sage: # needs sage.groups sage: K. = NumberField(cyclotomic_polynomial(23)) sage: L. = K.galois_closure() sage: L @@ -9207,6 +9226,7 @@ def galois_closure(self, names=None, map=False): Let's make sure we're renaming correctly:: + sage: # needs sage.groups sage: K. = NumberField(x^4 - 2) sage: L, phi = K.galois_closure('cc', map=True) sage: L @@ -9291,6 +9311,7 @@ def embeddings(self, K): EXAMPLES:: + sage: # needs sage.groups sage: x = polygen(QQ, 'x') sage: K. = NumberField(x^3 - 2) sage: L. = K.galois_closure(); L @@ -10940,6 +10961,7 @@ def _gap_init_(self): TESTS:: + sage: # needs sage.libs.gap sage: K = CyclotomicField(8) sage: gap(K) # indirect doctest CF(8) @@ -10953,13 +10975,15 @@ def _gap_init_(self): a genuine representation of cyclotomic fields in the GAP interface -- see :trac:`5618`. :: + sage: # needs sage.groups sage: H = AlternatingGroup(4) sage: g = H((1,4,3)) sage: K = H.subgroup([g]) sage: z = CyclotomicField(3).an_element(); z zeta3 sage: c = K.character([1,z,z**2]); c - Character of Subgroup generated by [(1,4,3)] of (Alternating group of order 4!/2 as a permutation group) + Character of Subgroup generated by [(1,4,3)] of + (Alternating group of order 4!/2 as a permutation group) sage: c(g^2); z^2 zeta3 -zeta3 - 1 @@ -10972,10 +10996,11 @@ def _libgap_(self): TESTS:: + sage: # needs sage.libs.gap sage: K = CyclotomicField(8) - sage: K._libgap_() # needs sage.libs.gap + sage: K._libgap_() CF(8) - sage: libgap(K) # indirect doctest # needs sage.libs.gap + sage: libgap(K) # indirect doctest CF(8) """ from sage.libs.gap.libgap import libgap @@ -11608,7 +11633,7 @@ def embeddings(self, K): # zeta not defined return super().embeddings(K) else: - X = (m for m in range(n) if arith.gcd(m, n) == 1) + X = (m for m in range(n) if gcd(m, n) == 1) v = [self.hom([z**i], check=False) for i in X] else: v = [] @@ -11779,7 +11804,7 @@ def next_split_prime(self, p=2): """ n = self._n() while True: - p = arith.next_prime(p) + p = next_prime(p) if p % n == 1: return p @@ -11853,7 +11878,7 @@ def _multiplicative_order_table(self): zeta = self.zeta(n) # todo: this desperately needs to be optimized!!! for i in range(n): - t[x.polynomial()] = n // arith.GCD(m, n) # multiplicative_order of (zeta_n)**m + t[x.polynomial()] = n // gcd(m, n) # multiplicative_order of (zeta_n)**m x *= zeta m += 1 self.__multiplicative_order_table = t @@ -12276,7 +12301,7 @@ def hilbert_class_field_defining_polynomial(self, name='x'): Note that this polynomial is not the actual Hilbert class polynomial: see ``hilbert_class_polynomial``:: - sage: K.hilbert_class_polynomial() + sage: K.hilbert_class_polynomial() # needs sage.schemes x^3 + 3491750*x^2 - 5151296875*x + 12771880859375 :: @@ -12329,11 +12354,11 @@ def hilbert_class_polynomial(self, name='x'): EXAMPLES:: sage: K. = QuadraticField(-3) - sage: K.hilbert_class_polynomial() + sage: K.hilbert_class_polynomial() # needs sage.schemes x sage: K. = QuadraticField(-31) - sage: K.hilbert_class_polynomial(name='z') + sage: K.hilbert_class_polynomial(name='z') # needs sage.schemes z^3 + 39491307*z^2 - 58682638134*z + 1566028350940383 """ D = self.discriminant() @@ -12709,14 +12734,14 @@ def _splitting_classes_gens_(K, m, d): sage: from sage.rings.number_field.number_field import _splitting_classes_gens_ sage: K = CyclotomicField(101) sage: L = K.subfields(20)[0][0] - sage: L.conductor() + sage: L.conductor() # needs sage.groups 101 - sage: _splitting_classes_gens_(L,101,20) # needs sage.libs.gap + sage: _splitting_classes_gens_(L,101,20) # needs sage.libs.gap # optional - gap_package_polycyclic [95] sage: K = CyclotomicField(44) sage: L = K.subfields(4)[0][0] - sage: _splitting_classes_gens_(L,44,4) # needs sage.libs.gap + sage: _splitting_classes_gens_(L,44,4) # needs sage.libs.gap # optional - gap_package_polycyclic [37] sage: K = CyclotomicField(44) @@ -12726,9 +12751,9 @@ def _splitting_classes_gens_(K, m, d): sage: L Number Field in zeta44_0 with defining polynomial x^5 - 2*x^4 - 16*x^3 + 24*x^2 + 48*x - 32 with zeta44_0 = 3.837971894457990? - sage: L.conductor() + sage: L.conductor() # needs sage.groups 11 - sage: _splitting_classes_gens_(L,11,5) # needs sage.libs.gap + sage: _splitting_classes_gens_(L,11,5) # needs sage.libs.gap # optional - gap_package_polycyclic [10] """ @@ -12746,7 +12771,7 @@ def map_Zmstar_to_Zm(h): Hgens = [] H = Zmstar.subgroup([]) p = 0 - Horder = arith.euler_phi(m) / d + Horder = euler_phi(m) / d for g in Zmstar: if H.order() == Horder: break diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 19c9e95ed39..28105221ac7 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -1002,6 +1002,7 @@ def is_maximal(self): EXAMPLES:: + sage: x = polygen(ZZ) sage: K. = NumberField(x^3 + 3); K Number Field in a with defining polynomial x^3 + 3 sage: K.ideal(5).is_maximal() @@ -1017,6 +1018,7 @@ def is_prime(self): EXAMPLES:: + sage: x = polygen(ZZ) sage: K. = NumberField(x^2 - 17); K Number Field in a with defining polynomial x^2 - 17 sage: K.ideal(5).is_prime() # inert prime @@ -1031,7 +1033,7 @@ def is_prime(self): Check that we do not factor the norm of the ideal, this used to take half an hour, see :trac:`33360`:: - sage: K. = NumberField([x^2-2,x^2-3,x^2-5]) + sage: K. = NumberField([x^2 - 2, x^2 - 3, x^2 - 5]) sage: t = (((-2611940*c + 1925290/7653)*b - 1537130/7653*c ....: + 10130950)*a + (1343014/7653*c - 8349770)*b ....: + 6477058*c - 2801449990/4002519) @@ -1112,6 +1114,7 @@ def _cache_bnfisprincipal(self, proof=None, gens=False): Check that no warnings are triggered from PARI/GP (see :trac:`30801`):: + sage: x = polygen(ZZ) sage: K. = NumberField(x^2 - x + 112941801) sage: I = K.ideal((112941823, a + 49942513)) sage: I.is_principal() @@ -1494,7 +1497,7 @@ def decomposition_group(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() + sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ return self.number_field().galois_group().decomposition_group(self) @@ -1510,9 +1513,9 @@ def ramification_group(self, v): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) + sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) - sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) + sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) # needs sage.groups Subgroup generated by [()] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ @@ -1528,7 +1531,7 @@ def inertia_group(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() + sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ return self.ramification_group(0) @@ -1594,7 +1597,7 @@ def artin_symbol(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol() + sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol() # needs sage.groups (1,2) """ return self.number_field().galois_group().artin_symbol(self) diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index af44569f167..9b416d2ad02 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.geometry.polyhedron sage.libs.linbox sage.modules sage.rings.number_field r""" Enumeration of totally real fields: relative extensions @@ -51,7 +52,7 @@ sage: [ f[0] for f in ls ] [725, 1125, 1600, 2000, 2225, 2525, 3600, 4225, 4400, 4525, 5125, 5225, 5725, 6125, 7225, 7600, 7625, 8000, 8525, 8725, 9225] - sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls] + sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls] # needs sage.groups [False, True, True, True, False, False, True, True, False, False, False, False, False, True, True, False, False, True, False, False, False] Eight out of 21 such fields are Galois (with Galois group `C_4` diff --git a/src/sage/rings/padics/padic_extension_leaves.py b/src/sage/rings/padics/padic_extension_leaves.py index ef0ed071a2c..d63dc734097 100644 --- a/src/sage/rings/padics/padic_extension_leaves.py +++ b/src/sage/rings/padics/padic_extension_leaves.py @@ -303,7 +303,7 @@ class UnramifiedExtensionRingFixedMod(UnramifiedExtensionGeneric, pAdicFixedModR TESTS:: sage: R. = ZqFM(27,1000) # needs sage.libs.flint - sage: TestSuite(R).run(skip='_test_log',max_runs=4) # long time # needs sage.libs.flint + sage: TestSuite(R).run(skip='_test_log',max_runs=4) # long time # needs sage.libs.flint """ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, implementation='FLINT'): """ diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 88797ddf4ed..ffafe15313c 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -3830,7 +3830,7 @@ cdef class pAdicGenericElement(LocalGenericElement): In its simplest form, computes the inverse of ``p``-th root of this element. - This is an helper function used in :meth:`nth_root` + This is a helper function used in :meth:`nth_root` and :meth:`primitive_root_of_unity`. INPUT: diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index cb0207c10d4..d1590d0391f 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -1360,7 +1360,7 @@ class pAdicFromLimitValuation(FiniteExtensionFromLimitValuation, pAdicValuation_ TESTS:: - sage: TestSuite(v).run(skip='_test_shift') # long time # needs sage.rings.number_field + sage: TestSuite(v).run(skip='_test_shift') # long time # needs sage.rings.number_field The ``_test_shift`` test fails because the parent of the shift is incorrect, see :trac:`23971`:: diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index cbbd42c8ccf..4c66eeca40c 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -21,7 +21,11 @@ sage: x = polygen(ZZ) sage: (x^5 - x - 1).roots(ring=CIF) - [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)] + [(1.167303978261419?, 1), + (-0.764884433600585? - 0.352471546031727?*I, 1), + (-0.764884433600585? + 0.352471546031727?*I, 1), + (0.181232444469876? - 1.083954101317711?*I, 1), + (0.181232444469876? + 1.083954101317711?*I, 1)] """ #***************************************************************************** @@ -61,9 +65,13 @@ def interval_roots(p, rts, prec): sage: rts = [CC.zeta(3)^i for i in range(0, 3)] sage: from sage.rings.polynomial.complex_roots import interval_roots sage: interval_roots(p, rts, 53) - [1, -0.500000000000000? + 0.866025403784439?*I, -0.500000000000000? - 0.866025403784439?*I] + [1, -0.500000000000000? + 0.866025403784439?*I, + -0.500000000000000? - 0.866025403784439?*I] sage: interval_roots(p, rts, 200) - [1, -0.500000000000000000000000000000000000000000000000000000000000? + 0.866025403784438646763723170752936183471402626905190314027904?*I, -0.500000000000000000000000000000000000000000000000000000000000? - 0.866025403784438646763723170752936183471402626905190314027904?*I] + [1, -0.500000000000000000000000000000000000000000000000000000000000? + + 0.866025403784438646763723170752936183471402626905190314027904?*I, + -0.500000000000000000000000000000000000000000000000000000000000? + - 0.866025403784438646763723170752936183471402626905190314027904?*I] """ CIF = ComplexIntervalField(prec) @@ -172,15 +180,19 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): sage: from sage.rings.polynomial.complex_roots import complex_roots sage: x = polygen(ZZ) sage: complex_roots(x^5 - x - 1) - [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)] - sage: v=complex_roots(x^2 + 27*x + 181) + [(1.167303978261419?, 1), + (-0.764884433600585? - 0.352471546031727?*I, 1), + (-0.764884433600585? + 0.352471546031727?*I, 1), + (0.181232444469876? - 1.083954101317711?*I, 1), + (0.181232444469876? + 1.083954101317711?*I, 1)] + sage: v = complex_roots(x^2 + 27*x + 181) Unfortunately due to numerical noise there can be a small imaginary part to each root depending on CPU, compiler, etc, and that affects the printing order. So we verify the real part of each root and check that the imaginary part is small in both cases:: - sage: v # random + sage: v # random [(-14.61803398874990?..., 1), (-12.3819660112501...? + 0.?e-27*I, 1)] sage: sorted((v[0][0].real(),v[1][0].real())) [-14.61803398874989?, -12.3819660112501...?] @@ -227,11 +239,16 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): ....: if tiny(x.imag()): return x.real() ....: if tiny(x.real()): return CIF(0, x.imag()) sage: rts = complex_roots(p); type(rts[0][0]), sorted(map(smash, rts)) - (, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) + (, + [-1.618033988749895?, -0.618033988749895?*I, + 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic'); type(rts[0][0]), sorted(map(smash, rts)) - (, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) + (, + [-1.618033988749895?, -0.618033988749895?*I, + 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic_real'); type(rts[0][0]), rts - (, [(-1.618033988749895?, 1), (0.618033988749895?, 1)]) + (, + [(-1.618033988749895?, 1), (0.618033988749895?, 1)]) TESTS: diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index d4e2b115e39..f5c1b0e480c 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -2075,8 +2075,9 @@ def factor(self, proof=None): Check that we can factor over the algebraic field (:trac:`25390`):: - sage: R. = PolynomialRing(QQbar) # needs sage.rings.number_field - sage: factor(x^2 + y^2) # needs sage.rings.number_field + sage: # needs sage.libs.singular sage.rings.number_field + sage: R. = PolynomialRing(QQbar) + sage: factor(x^2 + y^2) (x + (-1*I)*y) * (x + 1*I*y) Check that the global proof flag for polynomials is honored:: @@ -2123,7 +2124,7 @@ def factor(self, proof=None): sage: R. = PolynomialRing(CC,1) # needs sage.rings.real_mpfr sage: f = z^4 - 6*z + 3 # needs sage.rings.real_mpfr - sage: f.factor() # needs sage.rings.real_mpfr + sage: f.factor() # needs sage.libs.pari sage.rings.real_mpfr (z - 1.60443920904349) * (z - 0.511399619393097) * (z + 1.05791941421830 - 1.59281852704435*I) * (z + 1.05791941421830 + 1.59281852704435*I) @@ -2252,7 +2253,7 @@ def quo_rem(self, right): ((2*b + 1)*y, (2*b + 1)*y + (-a^2 + 3)*z + a) sage: R. = Qp(5)[] # needs sage.rings.padics - sage: x.quo_rem(y) # needs sage.rings.padics + sage: x.quo_rem(y) # needs sage.libs.singular sage.rings.padics Traceback (most recent call last): ... TypeError: no conversion of this ring to a Singular ring defined @@ -2265,7 +2266,7 @@ def quo_rem(self, right): sage: R. = QQbar[] # needs sage.rings.number_field sage: f = y*x^2 + x + 1 # needs sage.rings.number_field - sage: f.quo_rem(x) # needs sage.rings.number_field + sage: f.quo_rem(x) # needs sage.libs.singular sage.rings.number_field (x*y + 1, 1) """ R = self.parent() @@ -2326,7 +2327,7 @@ def resultant(self, other, variable=None): Check that :trac:`15061` is fixed:: sage: R. = AA[] # needs sage.rings.number_field - sage: (x^2 + 1).resultant(x^2 - y) # needs sage.rings.number_field + sage: (x^2 + 1).resultant(x^2 - y) # needs sage.libs.singular sage.rings.number_field y^2 + 2*y + 1 Test for :trac:`2693`:: @@ -2334,7 +2335,7 @@ def resultant(self, other, variable=None): sage: R. = RR[] sage: p = x + y sage: q = x*y - sage: p.resultant(q) # needs sage.modules + sage: p.resultant(q) # needs sage.libs.singular sage.modules -y^2 Check that this method works over QQbar (:trac:`25351`):: @@ -2343,9 +2344,9 @@ def resultant(self, other, variable=None): sage: P. = QQbar[] sage: a = x + y sage: b = x^3 - y^3 - sage: a.resultant(b) + sage: a.resultant(b) # needs sage.libs.singular sage.modules (-2)*y^3 - sage: a.resultant(b, y) + sage: a.resultant(b, y) # needs sage.libs.singular sage.modules 2*x^3 """ R = self.parent() @@ -2375,7 +2376,7 @@ def subresultants(self, other, variable=None): EXAMPLES:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.singular sage.rings.number_field sage: R. = QQbar[] sage: p = (y^2 + 6)*(x - 1) - y*(x^2 + 1) sage: q = (x^2 + 6)*(y - 1) - x*(y^2 + 1) @@ -2413,9 +2414,9 @@ def reduce(self, I): sage: f3 = -x^2 + y^2 sage: F = Ideal([f1, f2, f3]) sage: g = x*y - 3*x*y^2 - sage: g.reduce(F) + sage: g.reduce(F) # needs sage.libs.singular (-6)*y^2 + 2*y - sage: g.reduce(F.gens()) + sage: g.reduce(F.gens()) # needs sage.libs.singular (-6)*y^2 + 2*y :: diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 561136bac6d..f519400c3c3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -3197,7 +3197,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): INPUT: - ``as_ETuples`` -- (default: ``True``) if ``True`` returns the - result as an list of ETuples, otherwise returns a list of tuples + result as a list of ETuples, otherwise returns a list of tuples EXAMPLES:: diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 44946845b83..dc3bfdc2459 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -41,10 +41,11 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): sage: R. = ZZ['x,y']; R Multivariate Polynomial Ring in x, y over Integer Ring - sage: class CR(CommutativeRing): + sage: cat = Rings().Commutative() + sage: class CR(Parent): ....: def __init__(self): - ....: CommutativeRing.__init__(self,self) - ....: def __call__(self,x): + ....: Parent.__init__(self, self, category=cat) + ....: def __call__(self, x): ....: return None sage: cr = CR() sage: cr.is_commutative() @@ -668,7 +669,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): def repr_long(self): """ - Return structured string representation of self. + Return structured string representation of ``self``. EXAMPLES:: @@ -758,7 +759,6 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): Order: Graded Reverse Lexicographical Variables: T, W - sage: # optional - magma sage: magma(PolynomialRing(GF(7),4, 'x')) Polynomial ring of rank 4 over GF(7) @@ -797,7 +797,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): EXAMPLES:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: F = CyclotomicField(8) sage: P. = F[] sage: gap(P) # indirect doctest @@ -868,12 +868,12 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): def gen(self, n=0): if n < 0 or n >= self._ngens: - raise ValueError("Generator not defined.") + raise ValueError("generator not defined") return self._gens[int(n)] def variable_names_recursive(self, depth=sage.rings.infinity.infinity): r""" - Returns the list of variable names of this and its base rings, as if + Return the list of variable names of this and its base rings, as if it were a single multi-variate polynomial. EXAMPLES:: @@ -883,7 +883,6 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ('x', 'y', 'z', 'w') sage: R.variable_names_recursive(3) ('y', 'z', 'w') - """ if depth <= 0: all = () @@ -901,7 +900,8 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): def _mpoly_base_ring(self, vars=None): """ - Returns the base ring if this is viewed as a polynomial ring over vars. + Return the base ring if this is viewed as a polynomial ring over vars. + See also MPolynomial._mpoly_dict_recursive. """ if vars is None: @@ -1015,10 +1015,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): if not comb: return (d,) monomial = [comb[0]] - res = [] - for j in range(n - 2): - res.append(comb[j + 1] - comb[j] - 1) - monomial += res + monomial.extend(comb[j + 1] - comb[j] - 1 for j in range(n - 2)) monomial.append(n + d - 1 - comb[-1] - 1) return tuple(monomial) @@ -1234,7 +1231,6 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): True sage: R.random_element().parent() == R True - """ k = self.base_ring() n = self.ngens() @@ -1243,7 +1239,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): # Note that 'terms' could be None while 'total' is a # nonnegative integer, so the comparison 'terms > total' could - # fail in Python 3. + # fail if terms and terms > total: terms = total @@ -1526,13 +1522,14 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): def macaulay_resultant(self, *args, **kwds): r""" - This is an implementation of the Macaulay resultant. It computes - the resultant of universal polynomials as well as polynomials - with constant coefficients. This is a project done in - sage days 55. It's based on the implementation in Maple by - Manfred Minimair, which in turn is based on the references listed below: - It calculates the Macaulay resultant for a list of polynomials, - up to sign! + Return the Macaulay resultant. + + This computes the resultant of universal polynomials as well as + polynomials with constant coefficients. This is a project done + in sage days 55. It is based on the implementation in Maple by + Manfred Minimair, which in turn is based on the references + listed below. It calculates the Macaulay resultant for a list + of polynomials, up to sign! REFERENCES: @@ -1562,6 +1559,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): - the Macaulay resultant, an element of the base ring of ``self`` .. TODO:: + Working with sparse matrices should usually give faster results, but with the current implementation it actually works slower. There should be a way to improve performance with regards to this. @@ -1684,7 +1682,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): else: flist = args - if len(flist) <= 0: + if len(flist) == 0: raise TypeError('input list should contain at least 1 polynomial') if not all(f.is_homogeneous() for f in flist): raise TypeError('resultant for non-homogeneous polynomials is not supported') diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index 3afaff91a10..a9574c6d4cb 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -76,6 +76,7 @@ def _repr_(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -85,6 +86,7 @@ def _repr_(self): TESTS:: + sage: # needs sage.rings.finite_rings sage: f = 1/x^3; f x^(-3) sage: f * x^5 @@ -139,6 +141,7 @@ def __hash__(self): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -156,12 +159,12 @@ def _richcmp_(self, other, op): TESTS:: + sage: # needs sage.rings.function_field sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: der = R.derivation(1, twist=sigma) sage: S. = R['delta', der] sage: K = S.fraction_field() - sage: P = K.random_element() sage: Q = K.random_element() sage: D = K.random_element() @@ -197,18 +200,19 @@ def left_denominator(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: s = x + a sage: t = x^2 + a*x + a^2 - sage: f = s^(-1) * t sage: f.left_denominator() x + a In the example below, a simplification occurs:: + sage: # needs sage.rings.finite_rings sage: u = S.random_element(degree=2) sage: g = (u*s)^(-1) * (u*t) sage: g.left_denominator() @@ -222,13 +226,13 @@ def left_denominator(self): sage: S. = R['x', sigma] sage: s = (x + z)^2 sage: t = (x + z) * (x^2 + z^2) - sage: f = s^(-1) * t - sage: f.left_denominator() + sage: f = s^(-1) * t # needs sage.rings.function_field + sage: f.left_denominator() # needs sage.rings.function_field x^2 + (z^2 + z)*x + z^2 However, the following always holds true:: - sage: f == f.left_denominator()^(-1) * f.right_numerator() + sage: f == f.left_denominator()^(-1) * f.right_numerator() # needs sage.rings.function_field True .. SEEALSO:: @@ -258,18 +262,19 @@ def right_numerator(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: s = x + a sage: t = x^2 + a*x + a^2 - sage: f = s^(-1) * t sage: f.right_numerator() x^2 + a*x + a^2 In the example below, a simplification occurs:: + sage: # needs sage.rings.finite_rings sage: u = S.random_element(degree=2) sage: g = (u*s)^(-1) * (u*t) sage: g.right_numerator() @@ -286,15 +291,15 @@ def _reverse_fraction(self): r""" Return the pair `(s,t)` if this element reads `t s^{-1}`. - This is an helper function. Do not call it directly. + This is a helper function. Do not call it directly. TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(11^3) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a+1, twist=Frob) sage: S. = k['x', der] - sage: P = S.random_element(degree=5) sage: Q = S.random_element(degree=5) sage: f = P / Q @@ -330,18 +335,19 @@ def right_denominator(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: s = x + a sage: t = x^2 + a*x + a^2 - sage: f = t/s sage: f.right_denominator() x + a In the example below, a simplification occurs:: + sage: # needs sage.rings.finite_rings sage: u = S.random_element(degree=2) sage: g = (t*u) / (s*u) sage: g.right_denominator() @@ -356,11 +362,12 @@ def right_denominator(self): sage: R. = GF(11)[] sage: sigma = R.hom([z^2]) sage: S. = R['x', sigma] - sage: f = (x + z) / (x - z) - sage: f.right_denominator() + sage: f = (x + z) / (x - z) # needs sage.rings.function_field + sage: f.right_denominator() # needs sage.rings.function_field Traceback (most recent call last): ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in z over Finite Field of size 11 + NotImplementedError: inversion of the twisting morphism Ring endomorphism + of Fraction Field of Univariate Polynomial Ring in z over Finite Field of size 11 Defn: z |--> z^2 """ return self._reverse_fraction()[1] @@ -384,18 +391,19 @@ def left_numerator(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: s = x + a sage: t = x^2 + a*x + a^2 - sage: f = t/s sage: f.left_numerator() x^2 + a*x + a^2 In the example below, a simplification occurs:: + sage: # needs sage.rings.finite_rings sage: u = S.random_element(degree=2) sage: g = (t*u) / (s*u) sage: g.left_numerator() @@ -430,12 +438,12 @@ def _add_(self, other): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = K.random_element() sage: h = K.random_element() @@ -458,12 +466,12 @@ def _sub_(self, other): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = K.random_element() sage: h = K.random_element() @@ -480,12 +488,12 @@ def _neg_(self): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = -f sage: (f+g).is_zero() @@ -503,12 +511,12 @@ def _mul_(self, other): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = K.random_element() sage: h = K.random_element() @@ -538,18 +546,17 @@ def _div_(self, other): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = K.random_element() sage: h = K.random_element() sage: g == 0 or h == 0 or f / (g / h) == f*h / g True - sage: 0/f 0 sage: f/0 @@ -559,7 +566,7 @@ def _div_(self, other): We check that :trac:`32109` is fixed:: - sage: K(0)/K(0) + sage: K(0)/K(0) # needs sage.rings.finite_rings Traceback (most recent call last): ... ZeroDivisionError: cannot divide by zero @@ -579,17 +586,16 @@ def __invert__(self): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) sage: S. = k['x', der] sage: K = S.fraction_field() - sage: f = K.random_element() sage: g = ~f sage: f * g 1 - sage: ~K(0) Traceback (most recent call last): ... @@ -629,23 +635,24 @@ def hilbert_shift(self, s, var=None): When the twisting morphism is not trivial, the output lies in a different Ore polynomial ring:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: f = (x-a)^(-2) sage: g = f.hilbert_shift(a); g x^(-2) - sage: g.parent() - Ore Function Field in x over Finite Field in a of size 5^3 twisted by a |--> a^5 and a*([a |--> a^5] - id) + Ore Function Field in x over Finite Field in a of size 5^3 + twisted by a |--> a^5 and a*([a |--> a^5] - id) sage: g.parent() is S False This behavior ensures that the Hilbert shift by a fixed element - defines an homomorphism of fields:: + defines a homomorphism of fields:: + sage: # needs sage.rings.finite_rings sage: U = K.random_element(degree=5) sage: V = K.random_element(degree=5) sage: s = k.random_element() @@ -669,17 +676,17 @@ class ConstantOreFunctionSection(Map): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: from sage.rings.polynomial.ore_polynomial_element import ConstantOrePolynomialSection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: iota = K.coerce_map_from(k) - sage: sigma = iota.section() - sage: sigma + sage: sigma = iota.section(); sigma Generic map: - From: Ore Function Field in x over Finite Field in a of size 5^3 twisted by a |--> a^5 + From: Ore Function Field in x over Finite Field in a of size 5^3 + twisted by a |--> a^5 To: Finite Field in a of size 5^3 """ def _call_(self, x): @@ -693,14 +700,12 @@ def _call_(self, x): sage: F = R.fraction_field() sage: sigma = R.hom([t^2]) sage: S. = R['x', sigma] - sage: P = S._random_nonzero_element() - sage: f = (t*P) / P - sage: F(f) + sage: f = (t*P) / P # needs sage.rings.function_field + sage: F(f) # needs sage.rings.function_field t - - sage: g = x / (x+t) - sage: F(g) + sage: g = x / (x+t) # needs sage.rings.function_field + sage: F(g) # needs sage.rings.function_field Traceback (most recent call last): ... TypeError: not a constant function @@ -727,9 +732,8 @@ def __init__(self, domain, codomain): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] - sage: K = S.fraction_field() - - sage: K.coerce_map_from(K.base_ring()) # indirect doctest + sage: K = S.fraction_field() # needs sage.rings.function_field + sage: K.coerce_map_from(K.base_ring()) # indirect doctest # needs sage.rings.function_field Ore Function base injection morphism: From: Fraction Field of Univariate Polynomial Ring in t over Rational Field To: Ore Function Field in x over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 @@ -746,6 +750,7 @@ def an_element(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -766,6 +771,7 @@ def _call_(self, x): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -789,6 +795,7 @@ def section(self): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -796,7 +803,8 @@ def section(self): sage: m = K.coerce_map_from(k) sage: m.section() Generic map: - From: Ore Function Field in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + From: Ore Function Field in x over Finite Field in t of size 5^3 + twisted by t |--> t^5 To: Finite Field in t of size 5^3 """ return ConstantOreFunctionSection(self.codomain(), self.domain()) @@ -812,12 +820,12 @@ class OreFunction_with_large_center(OreFunction): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() sage: f = K.random_element() - sage: from sage.rings.polynomial.ore_function_element import OreFunction_with_large_center sage: isinstance(f, OreFunction_with_large_center) True @@ -835,11 +843,11 @@ def reduced_trace(self, var=None): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: a = 1 / (x^2 + t) sage: tr = a.reduced_trace(); tr 3/(z^2 + 2) @@ -847,6 +855,7 @@ def reduced_trace(self, var=None): The reduced trace lies in the center of `S`, which is the fraction field of a univariate polynomial ring in the variable `z = x^3` over `GF(5)`:: + sage: # needs sage.rings.finite_rings sage: tr.parent() Fraction Field of Univariate Polynomial Ring in z over Finite Field of size 5 sage: tr.parent() is K.center() @@ -854,7 +863,7 @@ def reduced_trace(self, var=None): We can use explicit conversion to view ``tr`` as a Ore function:: - sage: K(tr) + sage: K(tr) # needs sage.rings.finite_rings (x^6 + 2)^(-1) * 3 By default, the name of the central variable is usually ``z`` (see @@ -862,13 +871,14 @@ def reduced_trace(self, var=None): for more details about this). However, the user can specify a different variable name if desired:: - sage: a.reduced_trace(var='u') + sage: a.reduced_trace(var='u') # needs sage.rings.finite_rings 3/(u^2 + 2) TESTS: We check that the reduced trace is additive:: + sage: # needs sage.rings.finite_rings sage: a = K.random_element(degree=5) sage: b = K.random_element(degree=7) sage: a.reduced_trace() + b.reduced_trace() == (a+b).reduced_trace() @@ -876,7 +886,7 @@ def reduced_trace(self, var=None): :: - sage: (a*b).reduced_trace() == (b*a).reduced_trace() + sage: (a*b).reduced_trace() == (b*a).reduced_trace() # needs sage.rings.finite_rings True """ ring = self.parent()._ring @@ -896,11 +906,11 @@ def reduced_norm(self, var=None): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: a = (x + t) / (x^2 + t^2) sage: N = a.reduced_norm(); N (z + 2)/(z^2 + 4) @@ -908,6 +918,7 @@ def reduced_norm(self, var=None): The reduced norm lies in the center of `S`, which is the fraction field of a univariate polynomial ring in the variable `z = x^3` over `GF(5)`. :: + sage: # needs sage.rings.finite_rings sage: N.parent() Fraction Field of Univariate Polynomial Ring in z over Finite Field of size 5 sage: N.parent() is K.center() @@ -915,7 +926,7 @@ def reduced_norm(self, var=None): We can use explicit conversion to view ``N`` as a skew polynomial:: - sage: K(N) + sage: K(N) # needs sage.rings.finite_rings (x^6 + 4)^(-1) * (x^3 + 2) By default, the name of the central variable is usually ``z`` (see @@ -923,13 +934,14 @@ def reduced_norm(self, var=None): for more details about this). However, the user can specify a different variable name if desired:: - sage: a.reduced_norm(var='u') + sage: a.reduced_norm(var='u') # needs sage.rings.finite_rings (u + 2)/(u^2 + 4) TESTS: We check that the reduced norm is a multiplicative map:: + sage: # needs sage.rings.finite_rings sage: a = K.random_element() sage: b = K.random_element() sage: a.reduced_norm() * b.reduced_norm() == (a*b).reduced_norm() diff --git a/src/sage/rings/polynomial/ore_function_field.py b/src/sage/rings/polynomial/ore_function_field.py index 5a50e7c22b0..6fd7d5ffc5a 100644 --- a/src/sage/rings/polynomial/ore_function_field.py +++ b/src/sage/rings/polynomial/ore_function_field.py @@ -12,7 +12,8 @@ sage: A. = R['d', der] sage: K = A.fraction_field() sage: K - Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by d/dt + Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t + over Rational Field twisted by d/dt The simplest way to build elements in `K` is to use the division operator over Ore polynomial rings:: @@ -57,6 +58,7 @@ the latter assumption is not fulfilled (or actually if Sage cannot invert the twisting morphism), computing the left numerator and the right denominator fails:: + sage: # needs sage.rings.function_field sage: sigma = R.hom([t^2]) sage: S. = R['x', sigma] sage: F = S.fraction_field() @@ -66,13 +68,15 @@ sage: f.left_numerator() Traceback (most recent call last): ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism + Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 On a related note, fractions are systematically simplified when the twisting morphism is bijective but they are not otherwise. As an example, compare the two following computations:: + sage: # needs sage.rings.function_field sage: P = d^2 + t*d + 1 sage: Q = d + t^2 sage: D = d^3 + t^2 + 1 @@ -83,6 +87,7 @@ sage: g (d^2 + t*d + 1)^(-1) * (d + t^2) + sage: # needs sage.rings.function_field sage: P = x^2 + t*x + 1 sage: Q = x + t^2 sage: D = x^3 + t^2 + 1 @@ -91,7 +96,8 @@ (x^2 + t*x + 1)^(-1) * (x + t^2) sage: g = (D*P)^(-1) * (D*Q) sage: g - (x^5 + t^8*x^4 + x^3 + (t^2 + 1)*x^2 + (t^3 + t)*x + t^2 + 1)^(-1) * (x^4 + t^16*x^3 + (t^2 + 1)*x + t^4 + t^2) + (x^5 + t^8*x^4 + x^3 + (t^2 + 1)*x^2 + (t^3 + t)*x + t^2 + 1)^(-1) + * (x^4 + t^16*x^3 + (t^2 + 1)*x + t^4 + t^2) sage: f == g True @@ -99,6 +105,7 @@ Basic arithmetical operations are available:: + sage: # needs sage.rings.function_field sage: f = 1 / d sage: g = 1 / (d + t) sage: u = f + g; u @@ -115,6 +122,7 @@ Of course, multiplication remains noncommutative:: + sage: # needs sage.rings.function_field sage: g * f (d^2 + t*d + 1)^(-1) sage: g^(-1) * f @@ -171,6 +179,7 @@ def __init__(self, ring, category=None): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(11^3) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(a, twist=Frob) @@ -201,11 +210,11 @@ def _element_constructor_(self, *args, **kwds): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: f = K(x^2 + a, x + a^2) # indirect doctest sage: f (x + a^2)^(-1) * (x^2 + a) @@ -218,6 +227,7 @@ def _coerce_map_from_base_ring(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(11^2) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -225,7 +235,8 @@ def _coerce_map_from_base_ring(self): sage: K.coerce_map_from(k) # indirect doctest Ore Function base injection morphism: From: Finite Field in t of size 11^2 - To: Ore Function Field in x over Finite Field in t of size 11^2 twisted by t |--> t^11 + To: Ore Function Field in x over Finite Field in t of size 11^2 + twisted by t |--> t^11 """ return OreFunctionBaseringInjection(self.base_ring(), self) @@ -261,12 +272,12 @@ def _repr_(self): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = OrePolynomialRing(R, sigma) - sage: S.fraction_field() + sage: S.fraction_field() # needs sage.rings.function_field Ore Function Field in x over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 sage: der = R.derivation() sage: T. = OrePolynomialRing(R, der) - sage: T.fraction_field() + sage: T.fraction_field() # needs sage.rings.function_field Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by d/dt """ s = "Ore Function Field in %s over %s twisted by " % (self.variable_name(), self.base_ring()) @@ -286,6 +297,7 @@ def _latex_(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -321,13 +333,13 @@ def change_var(self, var): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: R. = OrePolynomialRing(k,Frob) sage: K = R.fraction_field() sage: K Ore Function Field in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - sage: Ky = K.change_var('y'); Ky Ore Function Field in y over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: Ky is K.change_var('y') @@ -344,13 +356,14 @@ def characteristic(self): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S = R['x',sigma] - sage: S.fraction_field().characteristic() + sage: S.fraction_field().characteristic() # needs sage.rings.function_field 0 + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S = k['y',Frob] - sage: S.fraction_field().characteristic() + sage: S.fraction_field().characteristic() # needs sage.rings.function_field 5 """ return self.base_ring().characteristic() @@ -369,9 +382,10 @@ def twisting_morphism(self, n=1): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x', sigma] - sage: K = S.fraction_field() - sage: K.twisting_morphism() - Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + sage: K = S.fraction_field() # needs sage.rings.function_field + sage: K.twisting_morphism() # needs sage.rings.function_field + Ring endomorphism of + Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t + 1 When the Ore polynomial ring is only twisted by a derivation, this @@ -379,8 +393,8 @@ def twisting_morphism(self, n=1): sage: der = R.derivation() sage: A. = R['x', der] - sage: F = A.fraction_field() - sage: F.twisting_morphism() + sage: F = A.fraction_field() # needs sage.rings.function_field + sage: F.twisting_morphism() # needs sage.rings.function_field .. SEEALSO:: @@ -404,6 +418,7 @@ def twisting_derivation(self): sage: F.twisting_derivation() d/dt + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -428,6 +443,7 @@ def gen(self, n=0): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^4) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -446,11 +462,11 @@ def gens_dict(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: S. = OrePolynomialRing(R, sigma) sage: K = S.fraction_field() - sage: K.gens_dict() {'x': x} """ @@ -462,6 +478,7 @@ def is_finite(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: k.is_finite() True @@ -480,6 +497,7 @@ def is_exact(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -487,6 +505,7 @@ def is_exact(self): sage: K.is_exact() True + sage: # needs sage.rings.padics sage: k. = Qq(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -508,6 +527,7 @@ def is_sparse(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage.rings.real_mpfr sage: R. = RR[] sage: sigma = R.hom([t+1]) sage: S. = R['x', sigma] @@ -524,6 +544,7 @@ def ngens(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage.rings.real_mpfr sage: R. = RR[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] @@ -550,19 +571,24 @@ def random_element(self, degree=2, monic=False, *args, **kwds): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: K.random_element() # random - (x^2 + (2*t^2 + t + 1)*x + 2*t^2 + 2*t + 3)^(-1) * ((2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2) + (x^2 + (2*t^2 + t + 1)*x + 2*t^2 + 2*t + 3)^(-1) + * ((2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2) sage: K.random_element(monic=True) # random - (x^2 + (4*t^2 + 3*t + 4)*x + 4*t^2 + t)^(-1) * (x^2 + (2*t^2 + t + 3)*x + 3*t^2 + t + 2) + (x^2 + (4*t^2 + 3*t + 4)*x + 4*t^2 + t)^(-1) + * (x^2 + (2*t^2 + t + 3)*x + 3*t^2 + t + 2) sage: K.random_element(degree=3) # random - (x^3 + (2*t^2 + 3)*x^2 + (2*t^2 + 4)*x + t + 3)^(-1) * ((t + 4)*x^3 + (4*t^2 + 2*t + 2)*x^2 + (2*t^2 + 3*t + 3)*x + 3*t^2 + 3*t + 1) + (x^3 + (2*t^2 + 3)*x^2 + (2*t^2 + 4)*x + t + 3)^(-1) + * ((t + 4)*x^3 + (4*t^2 + 2*t + 2)*x^2 + (2*t^2 + 3*t + 3)*x + 3*t^2 + 3*t + 1) sage: K.random_element(degree=[2,5]) # random - (x^2 + (4*t^2 + 2*t + 2)*x + 4*t^2 + t + 2)^(-1) * ((3*t^2 + t + 1)*x^5 + (2*t^2 + 2*t)*x^4 + (t^2 + 2*t + 4)*x^3 + (3*t^2 + 2*t)*x^2 + (t^2 + t + 4)*x) + (x^2 + (4*t^2 + 2*t + 2)*x + 4*t^2 + t + 2)^(-1) + * ((3*t^2 + t + 1)*x^5 + (2*t^2 + 2*t)*x^4 + (t^2 + 2*t + 4)*x^3 + + (3*t^2 + 2*t)*x^2 + (t^2 + t + 4)*x) """ if isinstance(degree, list): degdenom, degnum = degree @@ -579,13 +605,13 @@ def is_commutative(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() sage: K.is_commutative() False - sage: T. = k['y', Frob^3] sage: L = T.fraction_field() sage: L.is_commutative() @@ -599,11 +625,11 @@ def is_field(self, proof=False): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field() - sage: S.is_field() False sage: K.is_field() @@ -613,6 +639,7 @@ def is_field(self, proof=False): We check that :trac:`31470` is fixed:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = k['x', k.frobenius_endomorphism()] sage: K = S.fraction_field() @@ -632,12 +659,14 @@ def fraction_field(self): sage: R. = QQ[] sage: der = R.derivation() sage: A. = R['d', der] - sage: K = A.fraction_field() - - sage: K - Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by d/dt + sage: K = A.fraction_field(); K + Ore Function Field in d + over Fraction Field of Univariate Polynomial Ring in t over Rational Field + twisted by d/dt sage: K.fraction_field() - Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by d/dt + Ore Function Field in d + over Fraction Field of Univariate Polynomial Ring in t over Rational Field + twisted by d/dt sage: K.fraction_field() is K True """ @@ -658,6 +687,7 @@ def __init__(self, embed): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = OrePolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() @@ -676,6 +706,7 @@ def _call_(self, x): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() @@ -706,13 +737,13 @@ def _richcmp_(self, right, op): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() sage: Z = K.center() sage: iota = K.coerce_map_from(Z) sage: sigma = iota.section() - sage: s = loads(dumps(sigma)) sage: s == sigma True @@ -735,6 +766,7 @@ def __init__(self, domain, codomain, ringembed): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() @@ -753,6 +785,7 @@ def _repr_(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() @@ -771,12 +804,12 @@ def _call_(self, x): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() sage: Z. = K.center() sage: iota = K.coerce_map_from(Z) - sage: iota(1/(z+1)) (x^3 + 1)^(-1) """ @@ -790,12 +823,12 @@ def _richcmp_(self, right, op): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() sage: Z = K.center() sage: iota = K.coerce_map_from(Z) - sage: i = loads(dumps(iota)) sage: i == iota True @@ -812,6 +845,7 @@ def section(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: K = S.fraction_field() @@ -834,6 +868,7 @@ def __init__(self, ring, category=None): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -874,31 +909,32 @@ def center(self, name=None, names=None, default=False): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: K = S.fraction_field() - sage: Z = K.center(); Z Fraction Field of Univariate Polynomial Ring in z over Finite Field of size 5 We can pass in another variable name:: - sage: K.center(name='y') + sage: K.center(name='y') # needs sage.rings.finite_rings Fraction Field of Univariate Polynomial Ring in y over Finite Field of size 5 or use the bracket notation:: - sage: Zy. = K.center(); Zy + sage: Zy. = K.center(); Zy # needs sage.rings.finite_rings Fraction Field of Univariate Polynomial Ring in y over Finite Field of size 5 A coercion map from the center to the Ore function field is set:: - sage: K.has_coerce_map_from(Zy) + sage: K.has_coerce_map_from(Zy) # needs sage.rings.finite_rings True and pushout works:: + sage: # needs sage.rings.finite_rings sage: x.parent() Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: y.parent() @@ -910,9 +946,9 @@ def center(self, name=None, names=None, default=False): A conversion map in the reverse direction is also set:: + sage: # needs sage.rings.finite_rings sage: Zy(x^(-6) + 2) (2*y^2 + 1)/y^2 - sage: Zy(1/x^2) Traceback (most recent call last): ... @@ -925,11 +961,11 @@ def center(self, name=None, names=None, default=False): However, a variable name is given the first time this method is called, the given name become the default for the next calls:: + sage: # needs sage.rings.finite_rings sage: k. = GF(11^3) sage: phi = k.frobenius_endomorphism() sage: S. = k['X', phi] sage: K = S.fraction_field() - sage: C. = K.center() # first call sage: C Fraction Field of Univariate Polynomial Ring in u over Finite Field of size 11 @@ -939,6 +975,7 @@ def center(self, name=None, names=None, default=False): We can update the default variable name by passing in the argument ``default=True``:: + sage: # needs sage.rings.finite_rings sage: D. = K.center(default=True) sage: D Fraction Field of Univariate Polynomial Ring in v over Finite Field of size 11 diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 53c14bbc23f..637ba30aa0c 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Univariate Ore polynomials @@ -639,7 +640,6 @@ cdef class OrePolynomial(AlgebraElement): EXAMPLES:: - sage: # needs sage.rings.finite_rings sage: R. = GF(11)[] sage: der = R.derivation() sage: S. = R['x', der] @@ -667,6 +667,7 @@ cdef class OrePolynomial(AlgebraElement): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -680,7 +681,7 @@ cdef class OrePolynomial(AlgebraElement): Divisibility by `0` does not make sense:: - sage: c.is_left_divisible_by(S(0)) + sage: c.is_left_divisible_by(S(0)) # needs sage.rings.finite_rings Traceback (most recent call last): ... ZeroDivisionError: division by zero is not valid @@ -2196,7 +2197,7 @@ cdef void lmul_gen(list A, Morphism m, d) noexcept: replace it by the list of coefficients of ``X*P`` (where ``X`` is the variable in the Ore polynomial ring). - This is an helper function. + This is a helper function. INPUT: @@ -2683,7 +2684,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): Return the list of coefficients of the product of this Ore polynomial by that whose coefficients are given by ``A``. - This is an helper function. + This is a helper function. """ cdef list BA = [self.base_ring().zero()] * (len(self._coeffs) + len(A) - 1) cdef Morphism m = self._parent._morphism @@ -2921,7 +2922,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): False This behavior ensures that the Hilbert shift by a fixed element - defines an homomorphism of rings:: + defines a homomorphism of rings:: sage: # needs sage.rings.finite_rings sage: U = S.random_element(degree=5) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 88db16fd111..c5f32f35a1b 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Univariate Ore polynomial rings @@ -79,10 +80,10 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): We create the Ore ring `\GF{5^3}[x, \text{Frob}]` where Frob is the Frobenius endomorphism:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: S = OrePolynomialRing(k, Frob, 'x') - sage: S + sage: S = OrePolynomialRing(k, Frob, 'x'); S Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 In particular, observe that it is not needed to create and pass in @@ -90,8 +91,8 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): As a shortcut, we can use the square brackets notation as follow:: - sage: T. = k['x', Frob] - sage: T + sage: # needs sage.rings.finite_rings + sage: T. = k['x', Frob]; T Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 sage: T is S True @@ -100,22 +101,23 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): in the right hand side. Indeed, the following fails (it is interpreted by Sage as a classical polynomial ring with variable name ``Frob``):: - sage: T. = k[Frob] + sage: T. = k[Frob] # needs sage.rings.finite_rings Traceback (most recent call last): ... - ValueError: variable name 'Frobenius endomorphism a |--> a^5 on Finite Field in a of size 5^3' is not alphanumeric + ValueError: variable name 'Frobenius endomorphism a |--> a^5 on + Finite Field in a of size 5^3' is not alphanumeric Note moreover that, similarly to the classical case, using the brackets notation also sets the variable:: - sage: x.parent() is S + sage: x.parent() is S # needs sage.rings.finite_rings True We are now ready to carry on computations in the Ore ring:: - sage: x*a + sage: x*a # needs sage.rings.finite_rings (2*a^2 + 4*a + 4)*x - sage: Frob(a)*x + sage: Frob(a)*x # needs sage.rings.finite_rings (2*a^2 + 4*a + 4)*x .. RUBRIC:: The case of a twisting derivation @@ -123,22 +125,23 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): We can similarly create the Ore ring of differential operators over `\QQ[t]`, namely `\QQ[t][d, \frac{d}{dt}]`:: + sage: # needs sage.rings.finite_rings sage: R. = QQ[] sage: der = R.derivation(); der d/dt - sage: A = OrePolynomialRing(R, der, 'd') - sage: A - Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt + sage: A = OrePolynomialRing(R, der, 'd'); A + Ore Polynomial Ring in d over Univariate Polynomial Ring in t + over Rational Field twisted by d/dt Again, the brackets notation is available:: - sage: B. = R['d', der] - sage: A is B + sage: B. = R['d', der] # needs sage.rings.finite_rings + sage: A is B # needs sage.rings.finite_rings True and computations can be carried out:: - sage: d*t + sage: d*t # needs sage.rings.finite_rings t*d + 1 .. RUBRIC:: The combined case @@ -147,15 +150,14 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): `\sigma` and a twisting `\sigma`-derivation can be created as well as follows:: + sage: # needs sage.rings.padics sage: F. = Qq(3^2) sage: sigma = F.frobenius_endomorphism(); sigma Frobenius endomorphism on 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 lifting u |--> u^3 on the residue field sage: der = F.derivation(3, twist=sigma); der (3 + O(3^21))*([Frob] - id) - - sage: M. = F['X', der] - sage: M + sage: M. = F['X', der]; M Ore Polynomial Ring in X over 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 twisted by Frob and (3 + O(3^21))*([Frob] - id) @@ -163,7 +165,7 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): it already contains in it the datum of the twisting endomorphism. Actually, passing in both twisting maps results in an error:: - sage: F['X', sigma, der] + sage: F['X', sigma, der] # needs sage.rings.padics Traceback (most recent call last): ... ValueError: variable name 'Frobenius endomorphism ...' is not alphanumeric @@ -201,6 +203,7 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): In Sage, there is exactly one Ore polynomial ring for each quadruple (base ring, twisting morphism, twisting derivation, name of the variable):: + sage: # needs sage.rings.finite_rings sage: k. = GF(7^3) sage: Frob = k.frobenius_endomorphism() sage: S = k['x', Frob] @@ -210,45 +213,45 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): Rings with different variables names are different:: - sage: S is k['y', Frob] + sage: S is k['y', Frob] # needs sage.rings.finite_rings False Similarly, varying the twisting morphisms yields to different Ore rings (expect when the morphism coincide):: - sage: S is k['x', Frob^2] + sage: S is k['x', Frob^2] # needs sage.rings.finite_rings False - sage: S is k['x', Frob^3] + sage: S is k['x', Frob^3] # needs sage.rings.finite_rings False - sage: S is k['x', Frob^4] + sage: S is k['x', Frob^4] # needs sage.rings.finite_rings True TESTS: You must specify a variable name:: - sage: SkewPolynomialRing(k, Frob) + sage: SkewPolynomialRing(k, Frob) # needs sage.rings.finite_rings Traceback (most recent call last): ... TypeError: you must specify the name of the variable Multivariate Ore polynomial rings are not supported:: - sage: S = OrePolynomialRing(k, Frob,names=['x','y']) + sage: S = OrePolynomialRing(k, Frob,names=['x','y']) # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: multivariate Ore polynomials rings not supported Sparse Ore polynomial rings are not implemented:: - sage: S = SkewPolynomialRing(k, Frob, names='x', sparse=True) + sage: S = SkewPolynomialRing(k, Frob, names='x', sparse=True) # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: sparse Ore polynomial rings are not implemented Saving and loading of polynomial rings works:: - sage: loads(dumps(S)) is S + sage: loads(dumps(S)) is S # needs sage.rings.finite_rings True .. TODO:: @@ -294,6 +297,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False, In certain situations (e.g. when the twisting morphism is the Frobenius over a finite field), even more specialized classes are used:: + sage: # needs sage.rings.finite_rings sage: k. = GF(7^5) sage: Frob = k.frobenius_endomorphism(2) sage: S. = SkewPolynomialRing(k, Frob) @@ -304,6 +308,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False, `None` ot the identity, a regular `PolynomialRing` is created, unless specified otherwise:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^2) sage: Frob = k.frobenius_endomorphism(2) sage: Frob.is_identity() @@ -424,12 +429,12 @@ def __reduce__(self): r""" TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(11^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: loads(dumps(S)) is S True - sage: der = k.derivation(a, twist=Frob) sage: T. = k['y', der] sage: loads(dumps(T)) is T @@ -457,7 +462,7 @@ def _element_constructor_(self, a=None, check=True, construct=False, **kwds): OUTPUT: - An zero-degree Ore polynomial in ``self``, equal to ``a``. + A zero-degree Ore polynomial in ``self``, equal to ``a``. EXAMPLES:: @@ -561,7 +566,7 @@ def _coerce_map_from_(self, P): True sage: S.has_coerce_map_from(ZZ) True - sage: S.has_coerce_map_from(GF(5^3)) + sage: S.has_coerce_map_from(GF(5^3)) # needs sage.rings.finite_rings False sage: S.coerce_map_from(ZZ) @@ -625,6 +630,7 @@ def _latex_(self) -> str: EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -658,6 +664,7 @@ def change_var(self, var): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: R. = OrePolynomialRing(k,Frob); R @@ -685,6 +692,7 @@ def characteristic(self): sage: R['x',sigma].characteristic() 0 + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: k['y',Frob].characteristic() @@ -720,6 +728,7 @@ def twisting_morphism(self, n=1): If ``n`` in negative, Sage tries to compute the inverse of the twisting morphism:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['y',Frob] @@ -735,7 +744,8 @@ def twisting_morphism(self, n=1): sage: T.twisting_morphism(-1) Traceback (most recent call last): ... - NotImplementedError: inverse not implemented for morphisms of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inverse not implemented for morphisms of + Fraction Field of Univariate Polynomial Ring in t over Rational Field When the Ore polynomial ring is only twisted by a derivation, this method returns nothing:: @@ -743,12 +753,14 @@ def twisting_morphism(self, n=1): sage: der = R.derivation() sage: A. = R['x', der] sage: A - Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by d/dt + Ore Polynomial Ring in x over Univariate Polynomial Ring in t + over Rational Field twisted by d/dt sage: A.twisting_morphism() Here is an example where the twisting morphism is automatically inferred from the derivation:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: der = k.derivation(1, twist=Frob) @@ -785,6 +797,7 @@ def twisting_derivation(self): sage: A.twisting_derivation() d/dt + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -811,7 +824,8 @@ def gen(self, n=0): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma]; S - Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + Ore Polynomial Ring in x over Univariate Polynomial Ring in t + over Rational Field twisted by t |--> t + 1 sage: y = S.gen(); y x sage: y == x @@ -861,6 +875,7 @@ def is_finite(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: k.is_finite() True @@ -879,6 +894,7 @@ def is_exact(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -886,9 +902,8 @@ def is_exact(self): True sage: S.base_ring().is_exact() True - sage: R. = k[[]] - sage: sigma = R.hom([u+u^2]) + sage: sigma = R.hom([u + u^2]) sage: T. = R['y', sigma] sage: T.is_exact() False @@ -955,6 +970,7 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -965,6 +981,7 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): Use ``degree`` to obtain polynomials of higher degree:: + sage: # needs sage.rings.finite_rings sage: p = S.random_element(degree=5) # random (t^2 + 3*t)*x^5 + (4*t + 4)*x^3 + (4*t^2 + 4*t)*x^2 + (2*t^2 + 1)*x + 3 sage: p.degree() == 5 @@ -974,7 +991,7 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): integer will be chosen between the first and second element of the tuple as the degree, both inclusive:: - sage: S.random_element(degree=(2,7)) # random + sage: S.random_element(degree=(2,7)) # random # needs sage.rings.finite_rings (3*t^2 + 1)*x^4 + (4*t + 2)*x^3 + (4*t + 1)*x^2 + (t^2 + 3*t + 3)*x + 3*t^2 + 2*t + 2 @@ -983,14 +1000,14 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): If the first tuple element is greater than the second, a ``ValueError`` is raised:: - sage: S.random_element(degree=(5,4)) + sage: S.random_element(degree=(5,4)) # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: first degree argument must be less or equal to the second There is no monic polynomial of negative degree:: - sage: S.random_element(degree=-1, monic=True) + sage: S.random_element(degree=-1, monic=True) # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: there is no monic polynomial with negative degree @@ -1040,6 +1057,7 @@ def random_irreducible(self, degree=2, monic=True, *args, **kwds): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -1068,12 +1086,12 @@ def is_commutative(self) -> bool: EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: S.is_commutative() False - sage: T. = k['y', Frob^3] sage: T.is_commutative() True @@ -1083,7 +1101,6 @@ def is_commutative(self) -> bool: sage: A. = R['d', der] sage: A.is_commutative() False - sage: B. = R['b', 5*der] sage: B.is_commutative() True @@ -1097,6 +1114,7 @@ def is_field(self, proof=False) -> bool: EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] @@ -1107,6 +1125,7 @@ def is_field(self, proof=False) -> bool: We check that :trac:`31470` is fixed:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: S. = k['x', k.frobenius_endomorphism()] sage: zero_matrix(S, 2).row(0) @@ -1121,12 +1140,12 @@ def fraction_field(self): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob] sage: K = S.fraction_field(); K Ore Function Field in x over Finite Field in a of size 5^3 twisted by a |--> a^5 - sage: f = 1/(x + a); f (x + a)^(-1) sage: f.parent() is K @@ -1138,8 +1157,8 @@ def fraction_field(self): sage: der = R.derivation() sage: A. = R['d', der] sage: A.fraction_field() - Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t over Rational Field twisted by d/dt - + Ore Function Field in d over Fraction Field of Univariate Polynomial Ring in t + over Rational Field twisted by d/dt sage: f = t/d; f (d - 1/t)^(-1) * t sage: f*d @@ -1171,6 +1190,7 @@ def _pushout_(self, other): TESTS:: + sage: # needs sage.rings.finite_rings sage: from sage.categories.pushout import pushout sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 94d6aac82b5..de438cb122e 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -5451,6 +5451,8 @@ cdef class Polynomial(CommutativePolynomial): if self.degree() <= 1: return R.fraction_field() + from sage.rings.number_field.number_field import is_NumberField, NumberField + if is_IntegerRing(R): from sage.rings.number_field.number_field import NumberField return NumberField(self, names) @@ -6926,7 +6928,7 @@ cdef class Polynomial(CommutativePolynomial): sage: S. = PolynomialRing(R,'y') sage: p = ((1/b^2*d^2+1/a)*x*y^2+a*b/c*y+e+x^2) sage: q = -4*c^2*y^3+1 - sage: p.resultant(q) # needs sage.libs.pari + sage: p.resultant(q) # needs sage.libs.pari sage.modules (16*c^4)*x^6 + (48*c^4)*e*x^4 + (1/(b^6)*d^6 + 3/(a*b^4)*d^4 + (-12*a^3*b*c + 3)/(a^2*b^2)*d^2 + (-12*a^3*b*c + 1)/(a^3))*x^3 + (48*c^4)*e^2*x^2 + ((-12*a*c)/b*d^2*e + (-12*b*c)*e)*x + (16*c^4)*e^3 + (4*a^3*b^3)/c @@ -6938,7 +6940,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = PolynomialRing(CDF) sage: f = R(1 - I*x + (0.5)*x^2 + (1.7)*x^3) sage: g = f.derivative() - sage: f.resultant(g) + sage: f.resultant(g) # needs sage.modules 133.92599999999996 + 37.56999999999999*I """ variable = self.variable_name() @@ -7730,7 +7732,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f = x^3 - 1 sage: f.roots() [(1, 1)] - sage: f.roots(ring=CC) # ... - low order bits slightly different on ppc # needs sage.rings.real_mpfr + sage: f.roots(ring=CC) # ... - low order bits slightly different on ppc # needs sage.rings.real_mpfr [(1.00000000000000, 1), (-0.500000000000000 - 0.86602540378443...*I, 1), (-0.500000000000000 + 0.86602540378443...*I, 1)] @@ -8151,11 +8153,11 @@ cdef class Polynomial(CommutativePolynomial): sage: x = polygen(QQ) sage: p = x + bigc - sage: p.roots(ring=RR, algorithm='numpy') # needs numpy + sage: p.roots(ring=RR, algorithm='numpy') # needs numpy sage.rings.real_mpfr Traceback (most recent call last): ... LinAlgError: Array must not contain infs or NaNs - sage: p.roots(ring=RR, algorithm='pari') # needs sage.libs.pari + sage: p.roots(ring=RR, algorithm='pari') # needs sage.libs.pari sage.rings.real_mpfr [(-3.50746621104340e451, 1)] sage: p.roots(ring=AA) # needs sage.rings.number_field [(-3.5074662110434039?e451, 1)] diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 0468efd8c68..0713a334afb 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -41,6 +41,7 @@ from . import polynomial_element import sage.rings.rational_field +from sage.arith.misc import crt from sage.rings.ring import Field, IntegralDomain, CommutativeRing from sage.misc.cachefunc import cached_method @@ -1275,7 +1276,7 @@ def random_element(self, *args, **kwds): EXAMPLES:: - sage: # needs sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings sage: F1. = GF(2^7) sage: P1. = F1[] sage: F2 = F1.extension(x^2 + x + 1, 'u') @@ -1539,7 +1540,7 @@ def S_class_group(self, S, proof=True): for ideal_gen in clgp_gen.gens(): rel_ideal_gen = back_to_rel(phi(ideal_gen)) prod_ideal_gen = [0]*i + [rel_ideal_gen.lift()] + [0]*(n - i - 1) - poly_ideal_gen = self(sage.arith.all.crt(prod_ideal_gen, moduli)) + poly_ideal_gen = self(crt(prod_ideal_gen, moduli)) ideal_gens.append(poly_ideal_gen) clgp_gens.append((tuple(ideal_gens), gen_order)) @@ -1755,7 +1756,7 @@ def S_units(self, S, proof=True): mul_order = unit.multiplicative_order() rel_unit = back_to_rel(phi(unit)) prod_unit = [1]*i + [rel_unit.lift()] + [1]*(n - i - 1) - poly_unit = self(sage.arith.all.crt(prod_unit, moduli)) + poly_unit = self(crt(prod_unit, moduli)) units.append((poly_unit, mul_order)) return units @@ -1897,7 +1898,7 @@ def selmer_generators(self, S, m, proof=True): for gen in component_selmer_groups[isos[i][1]]: rel_gen = back_to_rel(phi(gen)) prod_gen = [1]*i + [rel_gen.lift()] + [1]*(n - i - 1) - poly_gen = self(sage.arith.all.crt(prod_gen, moduli)) + poly_gen = self(crt(prod_gen, moduli)) gens.append(poly_gen) return gens diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index d99f0d3b98c..4b752edc8b0 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2109,7 +2109,7 @@ cdef class Polynomial_rational_flint(Polynomial): EXAMPLES:: - sage: # needs sage.libs.pari + sage: # needs sage.groups sage.libs.pari sage: R. = QQ[] sage: f = x^4 - 17*x^3 - 2*x + 1 sage: G = f.galois_group(); G @@ -2130,7 +2130,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: f = x^4 - 17*x^3 - 2*x + 1 sage: G = f.galois_group(pari_group=True); G PARI group [24, -1, 5, "S4"] of degree 4 - sage: PermutationGroup(G) + sage: PermutationGroup(G) # needs sage.groups Transitive group number 5 of degree 4 You can use KASH or GAP to compute Galois groups as well. The advantage is diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 8801ed6709c..858df388caf 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -703,7 +703,7 @@ def _coerce_map_from_base_ring(self): To: Univariate Polynomial Ring in x over Rational Field sage: R.coerce_map_from(GF(7)) """ - from .polynomial_element import PolynomialBaseringInjection + from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection return PolynomialBaseringInjection(self.base_ring(), self) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 3ea7f87bddf..b103ac37508 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Univariate skew polynomials @@ -90,6 +91,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -152,6 +154,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -161,18 +164,17 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): x^5 + (2*t^2 + 4)*x^4 + (t^2 + 2)*x^3 + 2*x^2 + (4*t^2 + 2)*x + 2*t^2 + 4*t + 4 sage: b == a * a * a * a * a True - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 sage: br = a.right_power_mod(5, modulus); br (t + 1)*x^2 + (2*t^2 + t + 1)*x + 2*t^2 + 4*t + 2 sage: br == b % modulus True - sage: a.right_power_mod(100, modulus) (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 Negative exponents are supported: + sage: # needs sage.rings.finite_rings sage: a^(-5) (x^5 + (2*t^2 + 4)*x^4 + (t^2 + 2)*x^3 + 2*x^2 + (4*t^2 + 2)*x + 2*t^2 + 4*t + 4)^(-1) sage: b * a^(-5) @@ -180,7 +182,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): However, they cannot be combined with modulus:: - sage: a.right_power_mod(-10, modulus) + sage: a.right_power_mod(-10, modulus) # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: modulus cannot be combined with negative exponent @@ -245,6 +247,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -317,6 +320,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['x',Frob] @@ -341,6 +345,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['x',Frob] @@ -403,10 +408,12 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: b = a.conjugate(-1) Traceback (most recent call last): ... - NotImplementedError: inverse not implemented for morphisms of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inverse not implemented for morphisms of + Fraction Field of Univariate Polynomial Ring in t over Rational Field Here is a working example:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['y',Frob] @@ -441,6 +448,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -566,6 +574,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -602,6 +611,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): TESTS:: + sage: # needs sage.rings.finite_rings sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 99b3b7e64c9..1a176288395 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat sage.rings.finite_rings r""" Univariate dense skew polynomials over finite fields diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 968c45c8453..367ff8713ba 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.rings.finite_rings +# sage.doctest: needs sage.combinat sage.rings.finite_rings r""" Univariate dense skew polynomials over a field with a finite order automorphism diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 2ae48556880..9d3ea67ee46 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Univariate skew polynomial rings @@ -438,8 +439,6 @@ def _richcmp_(self, right, op): sage: Z = S.center() sage: iota = S.convert_map_from(Z) sage: sigma = iota.section() - - sage: # needs sage.rings.finite_rings sage: s = loads(dumps(sigma)) sage: s == sigma True @@ -517,8 +516,7 @@ def _call_(self, x): sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: Z. = S.center() sage: iota = S.convert_map_from(Z) - - sage: iota(z) # needs sage.rings.finite_rings + sage: iota(z) x^3 """ k = self._codomain.base_ring() @@ -539,8 +537,6 @@ def _richcmp_(self, right, op): sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: Z = S.center() sage: iota = S.convert_map_from(Z) - - sage: # needs sage.rings.finite_rings sage: i = loads(dumps(iota)) sage: i == iota True @@ -596,8 +592,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: S.category() Category of algebras over Finite Field in t of size 5^3 - - sage: TestSuite(S).run() # needs sage.rings.finite_rings + sage: TestSuite(S).run() We check that a call to the method :meth:`sage.rings.polynomial.skew_polynomial_finite_order.SkewPolynomial_finite_order.is_central` diff --git a/src/sage/rings/polynomial/toy_d_basis.py b/src/sage/rings/polynomial/toy_d_basis.py index 00209396614..ec783625140 100644 --- a/src/sage/rings/polynomial/toy_d_basis.py +++ b/src/sage/rings/polynomial/toy_d_basis.py @@ -88,7 +88,7 @@ However, when we compute the Groebner basis of I (defined over `\ZZ`), we note that there is a certain integer in the ideal which is not 1:: - sage: gb = d_basis(I); gb + sage: gb = d_basis(I); gb # needs sage.libs.singular [z ..., y ..., x ..., 282687803443] Now for each prime `p` dividing this integer 282687803443, the Groebner @@ -98,19 +98,19 @@ sage: factor(282687803443) 101 * 103 * 27173681 - sage: I.change_ring(P.change_ring(GF(101))).groebner_basis() + sage: I.change_ring(P.change_ring(GF(101))).groebner_basis() # needs sage.libs.singular [z - 33, y + 48, x + 19] - sage: I.change_ring(P.change_ring(GF(103))).groebner_basis() + sage: I.change_ring(P.change_ring(GF(103))).groebner_basis() # needs sage.libs.singular [z - 18, y + 8, x + 39] - sage: I.change_ring(P.change_ring(GF(27173681))).groebner_basis() # needs sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(27173681))).groebner_basis() # needs sage.libs.singular sage.rings.finite_rings [z + 10380032, y + 3186055, x - 536027] Of course, 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() + sage: I.change_ring(P.change_ring(GF(3))).groebner_basis() # needs sage.libs.singular [1] AUTHOR: diff --git a/src/sage/rings/power_series_mpoly.pyx b/src/sage/rings/power_series_mpoly.pyx index d11eec97269..2846d563a97 100644 --- a/src/sage/rings/power_series_mpoly.pyx +++ b/src/sage/rings/power_series_mpoly.pyx @@ -7,6 +7,12 @@ from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing from sage.rings import power_series_poly +try: + from sage.libs.pari.all import PariError +except ImportError: + PariError = () + + cdef class PowerSeries_mpoly(PowerSeries): def __init__(self, parent, f=0, prec=infinity, int check=1, is_gen=0): diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index c651fe03d02..8f2a86efba9 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -7,7 +7,12 @@ The class ``PowerSeries_poly`` provides additional methods for univariate power from sage.rings.power_series_ring_element cimport PowerSeries from sage.structure.element cimport Element from sage.rings.infinity import infinity -from sage.libs.pari.all import pari_gen, PariError + +try: + from sage.libs.pari.all import pari_gen, PariError +except ImportError: + pari_gen = () + PariError = () cdef class PowerSeries_poly(PowerSeries): diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 0349bfbe373..739525a9974 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -152,7 +152,6 @@ from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.power_series_pari import PowerSeries_pari from sage.structure.category_object import normalize_names from sage.structure.element import Expression, parent from sage.structure.nonexact import Nonexact @@ -168,6 +167,13 @@ from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings +try: + from .laurent_series_ring import LaurentSeriesRing + from .laurent_series_ring_element import LaurentSeries +except ImportError: + LaurentSeriesRing = () + LaurentSeries = () + def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, sparse=False, default_prec=None, order='negdeglex', @@ -537,11 +543,11 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, ValueError: default_prec (= -5) must be non-negative """ - from sage.rings.finite_rings.finite_field_pari_ffelt import ( - FiniteField_pari_ffelt, - ) - if implementation is None: + try: + from sage.rings.finite_rings.finite_field_pari_ffelt import FiniteField_pari_ffelt + except ImportError: + FiniteField_pari_ffelt = () if isinstance(base_ring, FiniteField_pari_ffelt): implementation = 'pari' else: @@ -571,6 +577,7 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, assert is_MPolynomialRing(self.__mpoly_ring) self.Element = power_series_mpoly.PowerSeries_mpoly elif implementation == 'pari': + from .power_series_pari import PowerSeries_pari self.Element = PowerSeries_pari else: raise ValueError('unknown power series implementation: %r' % implementation) @@ -579,7 +586,7 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, category=getattr(self, '_default_category', _CommutativeRings)) Nonexact.__init__(self, default_prec) - if self.Element is PowerSeries_pari: + if implementation == 'pari': self.__generator = self.element_class(self, R.gen().__pari__()) else: self.__generator = self.element_class(self, R.gen(), is_gen=True) @@ -805,7 +812,7 @@ def _element_constructor_(self, f, prec=infinity, check=True): if prec >= f.prec(): return f f = f.truncate(prec) - elif isinstance(f, laurent_series_ring_element.LaurentSeries) and f.parent().power_series_ring() is self: + elif isinstance(f, LaurentSeries) and f.parent().power_series_ring() is self: return self(f.power_series(), prec, check=check) elif isinstance(f, MagmaElement) and str(f.Type()) == 'RngSerPowElt': v = sage_eval(f.Eltseq()) @@ -953,9 +960,8 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): return True # this is allowed. if base_map is None and not codomain.has_coerce_map_from(self.base_ring()): return False - from .laurent_series_ring import is_LaurentSeriesRing v = im_gens[0] - if is_PowerSeriesRing(codomain) or is_LaurentSeriesRing(codomain): + if is_PowerSeriesRing(codomain) or isinstance(codomain, LaurentSeriesRing): try: return v.valuation() > 0 or v.is_nilpotent() except NotImplementedError: @@ -1270,8 +1276,10 @@ def laurent_series_ring(self): try: return self.__laurent_series_ring except AttributeError: - self.__laurent_series_ring = laurent_series_ring.LaurentSeriesRing( - self.base_ring(), self.variable_name(), default_prec=self.default_prec(), sparse=self.is_sparse()) + from .laurent_series_ring import LaurentSeriesRing + + self.__laurent_series_ring = LaurentSeriesRing( + self.base_ring(), self.variable_name(), default_prec=self.default_prec(), sparse=self.is_sparse()) return self.__laurent_series_ring diff --git a/src/sage/rings/puiseux_series_ring_element.pyx b/src/sage/rings/puiseux_series_ring_element.pyx index 39edce35bc9..3f50fb45e2e 100644 --- a/src/sage/rings/puiseux_series_ring_element.pyx +++ b/src/sage/rings/puiseux_series_ring_element.pyx @@ -106,7 +106,6 @@ from sage.arith.functions import lcm from sage.arith.misc import gcd from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.complex_mpfr import ComplexField from sage.rings.infinity import infinity from sage.rings.laurent_series_ring_element cimport LaurentSeries from sage.structure.element cimport (Element, AlgebraElement) @@ -308,6 +307,7 @@ cdef class PuiseuxSeries(AlgebraElement): if isinstance(x, int): x = ZZ(x) elif isinstance(x, float): + from sage.rings.complex_mpfr import ComplexField x = ComplexField()(x) t = x.nth_root(self._e) p = self._l.__u.polynomial() diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 5fc675ffd83..9c286af01ec 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -1525,7 +1525,7 @@ def _factor_univariate_polynomial(self, f): (12) * (x - 0.5773502691896258?) * (x + 0.5773502691896258?) sage: AA._factor_univariate_polynomial(12*x^2 + 4) (12) * (x^2 + 0.3333333333333334?) - sage: AA._factor_univariate_polynomial(EllipticCurve('11a1').change_ring(AA).division_polynomial(5)) # needs sage.schemes + sage: AA._factor_univariate_polynomial(EllipticCurve('11a1').change_ring(AA).division_polynomial(5)) # needs sage.schemes (5) * (x - 16.00000000000000?) * (x - 5.000000000000000?) * (x - 1.959674775249769?) * (x + 2.959674775249769?) * (x^2 - 2.854101966249685?*x + 15.47213595499958?) * (x^2 + 1.909830056250526?*x + 1.660606461254312?) * (x^2 + 3.854101966249685?*x + 6.527864045000421?) * (x^2 + 13.09016994374948?*x + 93.33939353874569?) """ @@ -2901,7 +2901,7 @@ def cmp_elements_with_same_minpoly(a, b, p): Compare the algebraic elements ``a`` and ``b`` knowing that they have the same minimal polynomial ``p``. - This is an helper function for comparison of algebraic elements (i.e. the + This is a helper function for comparison of algebraic elements (i.e. the methods :meth:`AlgebraicNumber._richcmp_` and :meth:`AlgebraicReal._richcmp_`). diff --git a/src/sage/rings/qqbar_decorators.py b/src/sage/rings/qqbar_decorators.py index c0505b11e8b..e1e3b908c8c 100644 --- a/src/sage/rings/qqbar_decorators.py +++ b/src/sage/rings/qqbar_decorators.py @@ -58,6 +58,7 @@ def wrapper(*args, **kwds): Check that :trac:`29468` is fixed:: + sage: # needs sage.libs.singular sage: J = QQbar['x,y'].ideal('x^2 - y') sage: type(J.groebner_basis()) diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 150e4a523bd..b970456ed9f 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -65,30 +65,41 @@ from cysignals.signals cimport sig_on, sig_off import operator import fractions +import sage.rings.rational_field + from sage.arith.long cimport integer_check_long_py +from sage.categories.morphism cimport Morphism +from sage.categories.map cimport Map from sage.cpython.string cimport char_to_str, str_to_bytes - +from sage.libs.gmp.pylong cimport mpz_set_pylong +from sage.rings.integer cimport Integer, smallInteger +from sage.rings.integer_ring import ZZ +from sage.structure.coerce cimport coercion_model, is_numpy_type +from sage.structure.element cimport Element +from sage.structure.parent cimport Parent from sage.structure.richcmp cimport rich_to_bool_sgn -import sage.rings.rational_field -cimport sage.rings.integer as integer -from sage.rings.integer cimport Integer -from sage.rings.integer_ring import ZZ +RealNumber_classes = () -from sage.structure.coerce cimport is_numpy_type +def _register_real_number_class(cls): + r""" + Register ``cls``. -from sage.libs.gmp.pylong cimport mpz_set_pylong + This is called by ``sage.rings.real_mpfr``, to avoid a cyclic import. + """ + global RealNumber_classes + RealNumber_classes += (cls,) -from sage.structure.coerce cimport coercion_model -from sage.structure.element cimport Element -from sage.structure.parent cimport Parent -from sage.categories.morphism cimport Morphism -from sage.categories.map cimport Map + +RealDouble_classes = (float,) +try: + from sage.rings.real_double import RealDoubleElement + RealDouble_classes += (RealDoubleElement,) +except ImportError: + pass -import sage.rings.real_mpfr -import sage.rings.real_double from libc.stdint cimport uint64_t from sage.libs.gmp.binop cimport mpq_add_z, mpq_mul_z, mpq_div_zz @@ -349,7 +360,7 @@ cpdef rational_power_parts(a, Rational b, factor_limit=10**5) noexcept: c = integer_rational_power(a, b) if c is not None: - return c, integer.smallInteger(1) + return c, smallInteger(1) numer, denom = b.numerator(), b.denominator() if a < factor_limit*factor_limit: @@ -357,7 +368,7 @@ cpdef rational_power_parts(a, Rational b, factor_limit=10**5) noexcept: else: from sage.rings.factorint import factor_trial_division f = factor_trial_division(a, factor_limit) - c = integer.smallInteger(1) + c = smallInteger(1) # The sign is not handled by the loop below. We don't want to # simplify (-1)^(2/3) to 1 (see Issue #15605), so we always move # the sign over to d. Note that the case (-1)^2 is already @@ -367,7 +378,7 @@ cpdef rational_power_parts(a, Rational b, factor_limit=10**5) noexcept: d = c else: # d = -1 - d = integer.smallInteger(-1) + d = smallInteger(-1) for p, e in f: c *= p**((e // denom)*numer) d *= p**(e % denom) @@ -1454,7 +1465,7 @@ cdef class Rational(sage.structure.element.FieldElement): True sage: e.norm() 3/5 - sage: 7.is_norm(K) + sage: 7.is_norm(K) # needs sage.groups Traceback (most recent call last): ... NotImplementedError: is_norm is not implemented unconditionally @@ -2755,7 +2766,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: (-1/6).sign() -1 """ - return integer.smallInteger(mpq_sgn(self.value)) + return smallInteger(mpq_sgn(self.value)) def mod_ui(Rational self, unsigned long int n): """ @@ -3107,7 +3118,9 @@ cdef class Rational(sage.structure.element.FieldElement): """ if self.is_zero(): raise ArithmeticError("Support of 0 not defined.") - return sage.arith.all.prime_factors(self) + from sage.arith.misc import prime_factors + + return prime_factors(self) def log(self, m=None, prec=None): r""" diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 341c0d536d0..8e54c459651 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -18,9 +18,9 @@ sage: RealField(9).pi() # needs sage.rings.real_mpfr 3.1 - sage: QQ(RealField(9).pi()) # needs sage.rings.real_mpfr + sage: QQ(RealField(9).pi()) # needs sage.rings.real_interval_field sage.rings.real_mpfr 22/7 - sage: QQ(RealField().pi()) # needs sage.rings.real_mpfr + sage: QQ(RealField().pi()) # needs sage.rings.real_interval_field sage.rings.real_mpfr 245850922/78256779 sage: QQ(35) 35 diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 19c9501bcf8..ba0b77150ad 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -50,6 +50,7 @@ from sage.cpython.python_debug cimport if_Py_TRACE_REFS_then_PyObject_INIT import math +import sage.arith.misc import sage.rings.integer import sage.rings.rational @@ -1955,7 +1956,7 @@ cdef class RealDoubleElement(FieldElement): sage: r.algebraic_dependency(5) # needs sage.libs.pari x^2 - 2 """ - return sage.arith.all.algdep(self,n) + return sage.arith.misc.algdep(self,n) algdep = algebraic_dependency @@ -2079,7 +2080,7 @@ else: # It operates on the following principles: # # - The pool starts out empty. -# - When an new element is needed, one from the pool is returned +# - When a new element is needed, one from the pool is returned # if available, otherwise a new RealDoubleElement object is created # - When an element is collected, it will add it to the pool # if there is room, otherwise it will be deallocated. diff --git a/src/sage/rings/real_field.py b/src/sage/rings/real_field.py index 6f424b90fc8..7b8e17eb5b9 100644 --- a/src/sage/rings/real_field.py +++ b/src/sage/rings/real_field.py @@ -35,11 +35,11 @@ def create_RealField(prec=53, type="MPFR", rnd="RNDN", sci_not=0): sage: from sage.rings.real_field import create_RealField sage: create_RealField(30) Real Field with 30 bits of precision - sage: create_RealField(20, 'RDF') # ignores precision + sage: create_RealField(20, 'RDF') # ignores precision Real Double Field - sage: create_RealField(60, 'Interval') + sage: create_RealField(60, 'Interval') # needs sage.rings.real_interval_field Real Interval Field with 60 bits of precision - sage: create_RealField(40, 'RLF') # ignores precision + sage: create_RealField(40, 'RLF') # ignores precision # needs sage.rings.real_interval_field Real Lazy Field """ if type == "RDF": diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index e765cdfe801..a426a922d6d 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -1,4 +1,4 @@ -# sage.doctest: needs sage.rings.real_mpfr +# sage.doctest: needs sage.rings.real_interval_field sage.rings.real_mpfr """ Lazy real and complex numbers @@ -50,12 +50,16 @@ from sage.rings.integer import Integer cdef QQ, RR, CC, RealField, ComplexField from sage.rings.rational_field import QQ -try: - from sage.rings.real_mpfr import RR, RealField - from sage.rings.complex_mpfr import ComplexField - from sage.rings.cc import CC -except ImportError: - pass +cdef late_import(): + global RR, CC, RealField, ComplexField + if CC is not None: + return + try: + from sage.rings.real_mpfr import RR, RealField + from sage.rings.complex_mpfr import ComplexField + from sage.rings.cc import CC + except ImportError: + pass cdef _QQx = None @@ -1611,6 +1615,9 @@ cdef class LazyAlgebraic(LazyFieldElement): if self._poly.degree() == 2: c, b, a = self._poly.list() self._quadratic_disc = b*b - 4*a*c + + late_import() + if isinstance(parent, RealLazyField_class): if not self._poly.number_of_real_roots(): raise ValueError("%s has no real roots" % self._poly) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 58e3d8de737..47c24f7261b 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -258,6 +258,8 @@ from cpython.object cimport Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT, Py_GE from cysignals.signals cimport sig_on, sig_off +import sage.arith.misc + from sage.libs.gmp.mpz cimport * from sage.libs.mpfr cimport * from sage.libs.mpfi cimport * @@ -5035,7 +5037,7 @@ cdef class RealIntervalFieldElement(RingElement): known_bits = -self.relative_diameter().log2() - return sage.arith.all.algdep(self.center(), n, known_bits=known_bits) + return sage.arith.misc.algdep(self.center(), n, known_bits=known_bits) def factorial(self): """ diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index ba6a1bda129..a91b58d3268 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -117,49 +117,43 @@ Make sure we don't have a new field for every new literal:: # https://www.gnu.org/licenses/ # ***************************************************************************** -import math # for log -import sys +import math # for log +import operator import re +import sys from cpython.object cimport Py_NE, Py_EQ from cysignals.signals cimport sig_on, sig_off +import sage.arith.misc +import sage.misc.weak_dict +import sage.rings.abc +import sage.rings.infinity +import sage.rings.rational_field + +from sage.arith.constants cimport M_LN2_LN10 +from sage.arith.long cimport is_small_python_int +from sage.arith.numerical_approx cimport digits_to_bits +from sage.categories.map cimport Map +from sage.cpython.string cimport char_to_str, str_to_bytes from sage.ext.stdsage cimport PY_NEW -from sage.libs.gmp.pylong cimport mpz_set_pylong from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.pylong cimport mpz_set_pylong from sage.libs.mpfr cimport * +from sage.libs.mpmath.utils cimport mpfr_to_mpfval from sage.misc.randstate cimport randstate, current_randstate -from sage.cpython.string cimport char_to_str, str_to_bytes from sage.misc.superseded import deprecation_cython as deprecation - -from sage.structure.element cimport Element -from sage.structure.element cimport have_same_parent -from sage.structure.richcmp cimport rich_to_bool_sgn -cdef bin_op -from sage.structure.element import bin_op - -import sage.misc.weak_dict - -import operator - -from sage.libs.mpmath.utils cimport mpfr_to_mpfval - from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational - -from sage.categories.map cimport Map - from sage.rings.real_double cimport RealDoubleElement - -import sage.rings.abc -import sage.rings.rational_field - -import sage.rings.infinity from sage.rings.ring import Ring +from sage.structure.element cimport Element +from sage.structure.element cimport have_same_parent +from sage.structure.parent_gens cimport ParentWithGens +from sage.structure.richcmp cimport rich_to_bool_sgn -from sage.arith.numerical_approx cimport digits_to_bits -from sage.arith.constants cimport M_LN2_LN10 -from sage.arith.long cimport is_small_python_int +cdef bin_op +from sage.structure.element import bin_op try: from cypari2 import Gen @@ -1284,6 +1278,7 @@ cdef class RealField_class(sage.rings.abc.RealField): TESTS:: + sage: # needs sage.libs.pari sage: k = RealField(100) sage: R. = k[] sage: k._factor_univariate_polynomial( x ) @@ -5324,7 +5319,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: r.algebraic_dependency(5) x^2 - 2 """ - return sage.arith.all.algdep(self,n) + return sage.arith.misc.algdep(self,n) algdep = algebraic_dependency @@ -6013,6 +6008,11 @@ cdef class int_toRR(Map): return y +# Hook into Rational +from sage.rings.rational import _register_real_number_class +_register_real_number_class(RealNumber) + + # Support Python's numbers abstract base class import numbers numbers.Real.register(RealNumber) diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 0388689a3e2..12892b91287 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1786,7 +1786,7 @@ cdef class RingExtension_generic(CommutativeAlgebra): r""" Return the defining morphism of the fraction field of this extension. - This is an helper function. + This is a helper function. INPUT: diff --git a/src/sage/rings/tate_algebra.py b/src/sage/rings/tate_algebra.py index 839d0e05b18..2905efdb7a8 100644 --- a/src/sage/rings/tate_algebra.py +++ b/src/sage/rings/tate_algebra.py @@ -27,9 +27,9 @@ :func:`sage.rings.tate_algebra.TateAlgebra`:: sage: K = Qp(2, 5, print_mode='digits') - sage: A. = TateAlgebra(K) - sage: A - Tate Algebra in x (val >= 0), y (val >= 0) over 2-adic Field with capped relative precision 5 + sage: A. = TateAlgebra(K); A + Tate Algebra in x (val >= 0), y (val >= 0) + over 2-adic Field with capped relative precision 5 As we observe, the default value for the log radii of convergence is `0` (the series then converge on the closed unit disc). @@ -37,13 +37,15 @@ We can specify different log radii using the following syntax:: sage: B. = TateAlgebra(K, log_radii=[1,2]); B - Tate Algebra in u (val >= -1), v (val >= -2) over 2-adic Field with capped relative precision 5 + Tate Algebra in u (val >= -1), v (val >= -2) + over 2-adic Field with capped relative precision 5 Note that if we pass in the ring of integers of `p`-adic field, the same Tate algebra is returned:: sage: A1. = TateAlgebra(K.integer_ring()); A1 - Tate Algebra in x (val >= 0), y (val >= 0) over 2-adic Field with capped relative precision 5 + Tate Algebra in x (val >= 0), y (val >= 0) + over 2-adic Field with capped relative precision 5 sage: A is A1 True @@ -51,9 +53,9 @@ of a Tate algebra, that is the subring consisting of series bounded by `1` on the domain of convergence:: - sage: Ao = A.integer_ring() - sage: Ao - Integer ring of the Tate Algebra in x (val >= 0), y (val >= 0) over 2-adic Field with capped relative precision 5 + sage: Ao = A.integer_ring(); Ao + Integer ring of the Tate Algebra in x (val >= 0), y (val >= 0) + over 2-adic Field with capped relative precision 5 Now we can build elements:: @@ -67,7 +69,8 @@ sage: f + g ...00001*x^3*y + ...00101 + ...000010*x*y^3 + ...000010*x*y + ...0000100*x^2*y^2 sage: f * g - ...00101*x^3*y + ...000010*x^4*y^4 + ...001010*x*y + ...0000100*x^5*y^3 + ...0000100*x^2*y^4 + ...00001000*x^3*y^3 + ...00101*x^3*y + ...000010*x^4*y^4 + ...001010*x*y + + ...0000100*x^5*y^3 + ...0000100*x^2*y^4 + ...00001000*x^3*y^3 An element in the integer ring is invertible if and only if its reduction modulo `p` is a nonzero constant. In our example, @@ -193,7 +196,8 @@ class TateAlgebraFactory(UniqueFactory): sage: R = Zp(2, 10, print_mode='digits'); R 2-adic Ring with capped relative precision 10 sage: A. = TateAlgebra(R, order='lex'); A - Tate Algebra in x (val >= 0), y (val >= 0) over 2-adic Field with capped relative precision 10 + Tate Algebra in x (val >= 0), y (val >= 0) + over 2-adic Field with capped relative precision 10 We observe that the result is the Tate algebra over the fraction field of `R` and not `R` itself:: @@ -207,7 +211,8 @@ class TateAlgebraFactory(UniqueFactory): we must use the method :meth:`integer_ring`:: sage: Ao = A.integer_ring(); Ao - Integer ring of the Tate Algebra in x (val >= 0), y (val >= 0) over 2-adic Field with capped relative precision 10 + Integer ring of the Tate Algebra in x (val >= 0), y (val >= 0) + over 2-adic Field with capped relative precision 10 sage: Ao.base_ring() 2-adic Ring with capped relative precision 10 sage: Ao.base_ring() is R @@ -847,15 +852,19 @@ def _pushout_(self, R): sage: A. = TateAlgebra(R, log_radii=[1,2]) sage: A1 = pushout(A, R1); A1 - Tate Algebra in u (val >= -1), v (val >= -2) over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 + Tate Algebra in u (val >= -1), v (val >= -2) + over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 sage: A2 = pushout(A, R2); A2 - Tate Algebra in u (val >= -2), v (val >= -4) over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 + Tate Algebra in u (val >= -2), v (val >= -4) + over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 sage: Ao = A.integer_ring() sage: pushout(Ao, R1) - Integer ring of the Tate Algebra in u (val >= -1), v (val >= -2) over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 + Integer ring of the Tate Algebra in u (val >= -1), v (val >= -2) + over 2-adic Unramified Extension Field in a defined by x^2 + x + 1 sage: pushout(Ao, R2.fraction_field()) - Tate Algebra in u (val >= -2), v (val >= -4) over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 + Tate Algebra in u (val >= -2), v (val >= -4) + over 2-adic Eisenstein Extension Field in pi defined by x^2 - 2 TESTS:: @@ -1243,20 +1252,23 @@ def random_element(self, degree=2, terms=5, integral=False, prec=None): sage: R = Zp(2, prec=10, print_mode="digits") sage: A. = TateAlgebra(R) sage: A.random_element() # random - (...00101000.01)*y + ...1111011111*x^2 + ...0010010001*x*y + ...110000011 + ...010100100*y^2 + (...00101000.01)*y + ...1111011111*x^2 + ...0010010001*x*y + + ...110000011 + ...010100100*y^2 sage: A.random_element(degree=5, terms=3) # random (...0101100.01)*x^2*y + (...01000011.11)*y^2 + ...00111011*x*y sage: A.random_element(integral=True) # random - ...0001111101*x + ...1101110101 + ...00010010110*y + ...101110001100*x*y + ...000001100100*y^2 + ...0001111101*x + ...1101110101 + ...00010010110*y + + ...101110001100*x*y + ...000001100100*y^2 Note that if we are already working on the ring of integers, specifying ``integral=False`` has no effect:: sage: Ao = A.integer_ring() sage: f = Ao.random_element(integral=False); f # random - ...1100111011*x^2 + ...1110100101*x + ...1100001101*y + ...1110110001 + ...01011010110*y^2 + ...1100111011*x^2 + ...1110100101*x + ...1100001101*y + + ...1110110001 + ...01011010110*y^2 sage: f in Ao True @@ -1265,7 +1277,9 @@ def random_element(self, degree=2, terms=5, integral=False, prec=None): sage: B. = TateAlgebra(R, log_radii=[-1,-2]) sage: B.random_element(integral=True) # random - (...1111111.001)*x*y + (...111000101.1)*x + (...11010111.01)*y^2 + ...0010011011*y + ...0010100011000 + (...1111111.001)*x*y + (...111000101.1)*x + (...11010111.01)*y^2 + + ...0010011011*y + ...0010100011000 + """ if integral or self._integral: polring = self._polynomial_ring.change_ring(self._field.integer_ring()) diff --git a/src/sage/rings/valuation/gauss_valuation.py b/src/sage/rings/valuation/gauss_valuation.py index 759d2a4e03e..a69977ae4b3 100644 --- a/src/sage/rings/valuation/gauss_valuation.py +++ b/src/sage/rings/valuation/gauss_valuation.py @@ -788,7 +788,7 @@ def simplify(self, f, error=None, force=False, size_heuristic_bound=32, effectiv def lower_bound(self, f): r""" - Return an lower bound of this valuation at ``f``. + Return a lower bound of this valuation at ``f``. Use this method to get an approximation of the valuation of ``f`` when speed is more important than accuracy. diff --git a/src/sage/rings/valuation/mapped_valuation.py b/src/sage/rings/valuation/mapped_valuation.py index d5b3248ef1d..2c4aa42799d 100644 --- a/src/sage/rings/valuation/mapped_valuation.py +++ b/src/sage/rings/valuation/mapped_valuation.py @@ -560,7 +560,7 @@ def simplify(self, x, error=None, force=False): def lower_bound(self, x): r""" - Return an lower bound of this valuation at ``x``. + Return a lower bound of this valuation at ``x``. Use this method to get an approximation of the valuation of ``x`` when speed is more important than accuracy. diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index 62c5dac33d7..0121f6a6b25 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -481,7 +481,7 @@ def mac_lane_approximants(self, G, assume_squarefree=False, require_final_EF=Tru [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 7/2 ], [ Gauss valuation induced by (x + 1)-adic valuation, v(y^13 + y^12 + y^10 + y^7 + y^6 + y^3 + 1) = 1 ]] sage: v0 = valuations.FunctionFieldValuation(K, GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x^3+x^2+1,1)) - sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed # needs sage.rings.function_field + sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed [[ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ], [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^2 + (x^2 + x)*y + 1) = 1 ], [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + (x + 1)*y^2 + (x + 1)*y + x^2 + x + 1) = 1 ], diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index b15323532b7..430532461f2 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -3223,7 +3223,7 @@ def __neg__(self): # recurrent addition or multiplication on the right by an integer def __mul__(self, other): r""" - If ``other`` is an configuration, the recurrent element equivalent + If ``other`` is a configuration, the recurrent element equivalent to the sum. If ``other`` is an integer, the sum of configuration with itself ``other`` times. diff --git a/src/sage/schemes/affine/affine_space.py b/src/sage/schemes/affine/affine_space.py index ef1ae326a43..fdf38862068 100644 --- a/src/sage/schemes/affine/affine_space.py +++ b/src/sage/schemes/affine/affine_space.py @@ -270,10 +270,10 @@ def rational_points(self, F=None): if F is None: if not isinstance(self.base_ring(), FiniteField): raise TypeError("base ring (= %s) must be a finite field" % self.base_ring()) - return [P for P in self] + return list(self) elif not isinstance(F, FiniteField): raise TypeError("second argument (= %s) must be a finite field" % F) - return [P for P in self.base_extend(F)] + return list(self.base_extend(F)) def __eq__(self, right): """ @@ -1226,7 +1226,7 @@ def translation(self, p, q=None): if q is not None: v = [cp - cq for cp, cq in zip(p, q)] else: - v = [cp for cp in p] + v = list(p) return self._morphism(self.Hom(self), [x - c for x, c in zip(gens, v)]) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index e1032861b2f..d5d97e5e044 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -684,13 +684,13 @@ def tangents(self, P, factor=True): if t > 0: fact.append(vars[0]) # divide T by that power of vars[0] - T = self.ambient_space().coordinate_ring()(dict([((v[0] - t, v[1]), h) for (v, h) in T.dict().items()])) + T = self.ambient_space().coordinate_ring()({(v[0] - t, v[1]): h for (v, h) in T.dict().items()}) t = min([e[1] for e in T.exponents()]) # vars[1] divides T if t > 0: fact.append(vars[1]) # divide T by that power of vars[1] - T = self.ambient_space().coordinate_ring()(dict([((v[0], v[1] - t), h) for (v, h) in T.dict().items()])) + T = self.ambient_space().coordinate_ring()({(v[0], v[1] - t): h for (v, h) in T.dict().items()}) # T is homogeneous in var[0], var[1] if nonconstant, so dehomogenize if T not in self.base_ring(): if T.degree(vars[0]) > 0: @@ -1032,7 +1032,7 @@ def projection(self, indices, AS=None): C = AA2.curve(G) except (TypeError, ValueError): C = AA2.subscheme(G) - return tuple([psi, C]) + return (psi, C) def plane_projection(self, AP=None): r""" @@ -1397,7 +1397,7 @@ def blowup(self, P=None): homvars.insert(i, 1) coords = [(p_A.gens()[0] - P[i])*homvars[j] + P[j] for j in range(n)] proj_maps.append(H(coords)) - return tuple([tuple(patches), tuple(t_maps), tuple(proj_maps)]) + return (tuple(patches), tuple(t_maps), tuple(proj_maps)) def resolution_of_singularities(self, extend=False): r""" @@ -1688,7 +1688,7 @@ def extension(self): patches = [res[i][0] for i in range(len(res))] t_maps = [tuple(res[i][1]) for i in range(len(res))] p_maps = [res[i][2] for i in range(len(res))] - return tuple([tuple(patches), tuple(t_maps), tuple(p_maps)]) + return (tuple(patches), tuple(t_maps), tuple(p_maps)) def tangent_line(self, p): """ @@ -2600,7 +2600,7 @@ def places_at_infinity(self): sage: C.places_at_infinity() [Place (1/x, 1/x*z^2)] """ - return list(set(p for f in self._coordinate_functions if f for p in f.poles())) + return list({p for f in self._coordinate_functions if f for p in f.poles()}) def places_on(self, point): """ diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 7c82026b3c1..9480d761f61 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -495,7 +495,7 @@ def projection(self, P=None, PS=None): phi = K(l) G = [phi(f) for f in J.gens()] C = PP2.curve(G) - return tuple([psi, C]) + return (psi, C) def plane_projection(self, PP=None): r""" @@ -577,7 +577,7 @@ def plane_projection(self, PP=None): psi = K(phi.defining_polynomials()) H = Hom(self, L[1].ambient_space()) phi = H([psi(L[0].defining_polynomials()[i]) for i in range(len(L[0].defining_polynomials()))]) - return tuple([phi, C]) + return (phi, C) class ProjectivePlaneCurve(ProjectiveCurve): @@ -1104,7 +1104,7 @@ def quadratic_transform(self): T = [] for item in G.dict().items(): tup = tuple([item[0][i] - degs[i] for i in range(len(L))]) - T.append(tuple([tup, item[1]])) + T.append((tup, item[1])) G = R(dict(T)) H = Hom(self, PP.curve(G)) phi = H(coords) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0940825b9a5..fa30e97085b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -68,7 +68,7 @@ from sage.rings.real_mpfr import RealField # from sage.sets.set import Set -roots_interval_cache = dict() +roots_interval_cache = {} def braid_from_piecewise(strands): @@ -793,7 +793,7 @@ def populate_roots_interval_cache(inputs): @parallel -def braid_in_segment(glist, x0, x1, precision=dict()): +def braid_in_segment(glist, x0, x1, precision={}): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. @@ -1711,7 +1711,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis f = prod(flist1) if len(flist1) == 0: bm = [] - dic = dict() + dic = {} else: bm, dic = braid_monodromy(f, flist1) g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) @@ -1721,7 +1721,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis hom = g.hom(codomain=g, im_gens=list(g.gens()), check=False) g1 = hom.codomain() if len(flist) == 0: - return (g1, dict()) + return (g1, {}) dic1 = {} for i in range(len(flist1)): L = [j1 for j1 in dic.keys() if dic[j1] == i] diff --git a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py index 82504873715..f2d753baf8f 100644 --- a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py +++ b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py @@ -921,7 +921,7 @@ def _initialize_fat_vertical(self, s0, max_upper_target): L = floor((max_upper_target - self._epsilon) / self._p) + 1 if s0 not in self._vertical_fat_s: (m0, m1), (M0, M1) = self._vertical_matrix_reduction(s0) - D0, D1 = map(lambda y: matrix(self._Zq, [y]), [m0, m1]) + D0, D1 = (matrix(self._Zq, [y]) for y in [m0, m1]) targets = [0] * (2 * L) for l in reversed(range(L)): targets[2 * l] = max_upper_target - self._p * (L - l) @@ -1268,7 +1268,7 @@ def _denominator(): f = x ** self._delta - lc L = f.splitting_field("a") roots = [r for r, _ in f.change_ring(L).roots()] - roots_dict = dict([(r, i) for i, r in enumerate(roots)]) + roots_dict = {r: i for i, r in enumerate(roots)} rootsfrob = [L.frobenius_endomorphism(self._Fq.degree())(r) for r in roots] m = zero_matrix(len(roots)) for i, r in enumerate(roots): diff --git a/src/sage/schemes/elliptic_curves/BSD.py b/src/sage/schemes/elliptic_curves/BSD.py index 85130f0a5f9..bf3f302b978 100644 --- a/src/sage/schemes/elliptic_curves/BSD.py +++ b/src/sage/schemes/elliptic_curves/BSD.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- "Birch and Swinnerton-Dyer formulas" from sage.arith.misc import prime_divisors diff --git a/src/sage/schemes/elliptic_curves/Qcurves.py b/src/sage/schemes/elliptic_curves/Qcurves.py index 45fb5c51293..c7d3da73691 100644 --- a/src/sage/schemes/elliptic_curves/Qcurves.py +++ b/src/sage/schemes/elliptic_curves/Qcurves.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Testing whether elliptic curves over number fields are `\QQ`-curves diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 03acc69e732..58bed0a67a4 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -973,7 +973,7 @@ def is_cm_j_invariant(j, algorithm='CremonaSutherland', method=None): if j in ZZ: j = ZZ(j) - table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)]) + table = {jj: (d,f) for d,f,jj in cm_j_invariants_and_orders(QQ)} if j in table: return True, table[j] return False, None diff --git a/src/sage/schemes/elliptic_curves/ec_database.py b/src/sage/schemes/elliptic_curves/ec_database.py index 043c287ec7e..f66ee2d1d31 100644 --- a/src/sage/schemes/elliptic_curves/ec_database.py +++ b/src/sage/schemes/elliptic_curves/ec_database.py @@ -136,7 +136,7 @@ def rank(self, rank, tors=0, n=10, labels=False): data = os.path.join(ELLCURVE_DATA_DIR, 'rank%s' % rank) try: f = open(data) - except IOError: + except OSError: return [] v = [] tors = int(tors) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 59bf8882f1f..68e9c3fc79d 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Isogenies @@ -1953,7 +1952,7 @@ def __sort_kernel_list(self): """ a1, a2, a3, a4, _ = self._domain.a_invariants() - self.__kernel_mod_sign = dict() + self.__kernel_mod_sign = {} v = w = 0 for Q in self.__kernel_list: @@ -3464,6 +3463,15 @@ def compute_isogeny_stark(E1, E2, ell): sage: E2 = phi.codomain() sage: compute_isogeny_stark(E, E2, 2) x + + TESTS: + + Check for :issue:`21883`:: + + sage: E1 = EllipticCurve([0,1]) + sage: E2 = EllipticCurve([0,-27]) + sage: E1.isogeny(None, E2, degree=3) + Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 27 over Rational Field """ K = E1.base_field() R, x = PolynomialRing(K, 'x').objgen() @@ -3477,8 +3485,8 @@ def compute_isogeny_stark(E1, E2, ell): for i in range(2*ell + 1): pe1 += wp1[2*i] * Z**i pe2 += wp2[2*i] * Z**i - pe1 = pe1.add_bigoh(2*ell+2) - pe2 = pe2.add_bigoh(2*ell+2) + pe1 = pe1.add_bigoh(2*ell+3) + pe2 = pe2.add_bigoh(2*ell+3) n = 1 q = [R.one(), R.zero()] diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 5b569ae54e3..57003863b1b 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -169,7 +169,7 @@ def __init__(self, K, ainvs): - (x**3 + a2*x**2*z + a4*x*z**2 + a6*z**3) plane_curve.ProjectivePlaneCurve.__init__(self, PP, f) - self.__divpolys = (dict(), dict(), dict()) + self.__divpolys = ({}, {}, {}) # See #1975: we deliberately set the class to # EllipticCurvePoint_finite_field for finite rings, so that we @@ -1732,7 +1732,7 @@ def division_polynomial_0(self, n, x=None): x = polygen(self.base_ring()) else: # For other inputs, we use a temporary cache. - cache = dict() + cache = {} b2, b4, b6, b8 = self.b_invariants() @@ -2119,7 +2119,7 @@ def _multiple_x_numerator(self, n, x=None): try: cache = self.__mulxnums except AttributeError: - cache = self.__mulxnums = dict() + cache = self.__mulxnums = {} try: return cache[n] except KeyError: @@ -2216,7 +2216,7 @@ def _multiple_x_denominator(self, n, x=None): try: cache = self.__mulxdens except AttributeError: - cache = self.__mulxdens = dict() + cache = self.__mulxdens = {} try: return cache[n] except KeyError: diff --git a/src/sage/schemes/elliptic_curves/ell_local_data.py b/src/sage/schemes/elliptic_curves/ell_local_data.py index 11a0e9db661..a71bc7fd25f 100644 --- a/src/sage/schemes/elliptic_curves/ell_local_data.py +++ b/src/sage/schemes/elliptic_curves/ell_local_data.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Local data for elliptic curves over number fields @@ -1199,7 +1198,7 @@ def check_prime(K, P): raise TypeError("%s is not a number field" % (K,)) if is_NumberFieldFractionalIdeal(P) or P in K: - # if P is an ideal, making sure it is an fractional ideal of K + # if P is an ideal, making sure it is a fractional ideal of K P = K.fractional_ideal(P) if P.is_prime(): return P diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 243df1d23f0..055c7eaec45 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Modular symbols attached to elliptic curves over `\QQ` diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 8e2993b3375..5e7a5731ea8 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -672,9 +672,8 @@ def global_integral_model(self): """ K = self.base_field() ai = self.a_invariants() - Ps = set(ff[0] - for a in ai if not a.is_integral() - for ff in a.denominator_ideal().factor()) + Ps = {ff[0] for a in ai if not a.is_integral() + for ff in a.denominator_ideal().factor()} for P in Ps: pi = K.uniformizer(P, 'positive') e = min((ai[i].valuation(P)/[1,2,3,4,6][i]) @@ -2919,6 +2918,18 @@ class number is only `3` is that the class also contains three sage: E = EllipticCurve([a+1, 1, 1, 0, 0]) sage: C = E.isogeny_class(); len(C) # long time 4 + + Check that :issue:`36780` is fixed:: + + sage: L5. = NumberField(x^2-5) + sage: F = EllipticCurve(L5,[0,-4325477943600 *r5-4195572876000]) + sage: F.isogeny_class().matrix() + [ 1 25 75 3 5 15] + [25 1 3 75 5 15] + [75 3 1 25 15 5] + [ 3 75 25 1 15 5] + [ 5 5 15 15 1 3] + [15 15 5 5 3 1] """ try: return self._isoclass @@ -3283,10 +3294,10 @@ def reducible_primes(self, algorithm='Billerey', max_l=None, For curves without CM the list returned is exactly the finite set of primes `\ell` for which the mod-`\ell` Galois representation is reducible. For curves with CM this set is - infinite; we return a finite list of primes `\ell` such that - every curve isogenous to this curve can be obtained by a - finite sequence of isogenies of degree one of the primes in - the list. + infinite; we return a (not necessarily minimal) finite list + of primes `\ell` such that every curve isogenous to this curve + can be obtained by a finite sequence of isogenies of degree one + of the primes in the list. INPUT: @@ -3328,7 +3339,7 @@ def reducible_primes(self, algorithm='Billerey', max_l=None, sage: rho.reducible_primes() # CM curves always return [0] [0] sage: E.reducible_primes() - [2] + [2, 5] sage: E = EllipticCurve_from_j(K(0)) # CM but NOT over K sage: rho = E.galois_representation() sage: rho.reducible_primes() # long time diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index d35790a13f3..1bf84e9a766 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Points on elliptic curves @@ -1457,7 +1456,7 @@ def _miller_(self, Q, n): - ``Q`` -- a nonzero point on self.curve(). - - ``n`` -- an nonzero integer. If `n<0` then return `Q` + - ``n`` -- a nonzero integer. If `n<0` then return `Q` evaluated at `1/(v_{nP}*f_{n,P})` (used in the ate pairing). OUTPUT: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 27e52973afd..130eaec9e37 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Elliptic curves over the rational numbers @@ -1559,10 +1558,11 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): return self.analytic_rank_upper_bound() elif algorithm == 'all': if leading_coefficient: - S = set([self.analytic_rank('pari', True)]) + S = {self.analytic_rank('pari', True)} else: - S = set([self.analytic_rank('pari'), - self.analytic_rank('rubinstein'), self.analytic_rank('sympow')]) + S = {self.analytic_rank('pari'), + self.analytic_rank('rubinstein'), + self.analytic_rank('sympow')} if len(S) != 1: raise RuntimeError("Bug in analytic_rank; algorithms don't agree! (E=%s)" % self) return list(S)[0] @@ -5347,10 +5347,10 @@ def _shortest_paths(self): # Take logs here since shortest path minimizes the *sum* of the weights -- not the product. M = M.parent()([a.log() if a else 0 for a in M.list()]) G = Graph(M, format='weighted_adjacency_matrix') - G.set_vertices(dict([(v,isocls[v]) for v in G.vertices(sort=False)])) + G.set_vertices({v: isocls[v] for v in G.vertices(sort=False)}) v = G.shortest_path_lengths(0, by_weight=True) # Now exponentiate and round to get degrees of isogenies - v = dict([(i, j.exp().round() if j else 0) for i,j in v.items()]) + v = {i: j.exp().round() if j else 0 for i,j in v.items()} return isocls.curves, v def _multiple_of_degree_of_isogeny_to_optimal_curve(self): @@ -6179,7 +6179,7 @@ def point_preprocessing(free,tor): subgroup of index 2. """ r = len(free) - newfree = [Q for Q in free] # copy + newfree = list(free) # copy tor_egg = [T for T in tor if not T.is_on_identity_component()] free_id = [P.is_on_identity_component() for P in free] if any(tor_egg): @@ -6207,7 +6207,7 @@ def point_preprocessing(free,tor): int_points = [P for P in tors_points if not P.is_zero()] int_points = [P for P in int_points if P[0].is_integral()] if not both_signs: - xlist = set([P[0] for P in int_points]) + xlist = {P[0] for P in int_points} int_points = [self.lift_x(x) for x in xlist] int_points.sort() if verbose: @@ -6800,8 +6800,8 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): alpha = [(log_ab/R(log(p,e))).floor() for p in S] if all(alpha_i <= 1 for alpha_i in alpha): # so alpha_i must be 0 to satisfy that denominator is a square int_abs_bound = abs_bound.floor() - return set(x for x in range(-int_abs_bound, int_abs_bound) - if E.is_x_coord(x)) + return {x for x in range(-int_abs_bound, int_abs_bound) + if E.is_x_coord(x)} else: xs = [] alpha_max_even = [y - y % 2 for y in alpha] @@ -6849,7 +6849,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): int_points = [P for P in tors_points if not P.is_zero()] int_points = [P for P in int_points if P[0].is_S_integral(S)] if not both_signs: - xlist = set([P[0] for P in int_points]) + xlist = {P[0] for P in int_points} int_points = [E.lift_x(x) for x in xlist] int_points.sort() if verbose: diff --git a/src/sage/schemes/elliptic_curves/ell_tate_curve.py b/src/sage/schemes/elliptic_curves/ell_tate_curve.py index 7b616671e24..6f8d861db93 100644 --- a/src/sage/schemes/elliptic_curves/ell_tate_curve.py +++ b/src/sage/schemes/elliptic_curves/ell_tate_curve.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Tate's parametrisation of `p`-adic curves with multiplicative reduction diff --git a/src/sage/schemes/elliptic_curves/ell_torsion.py b/src/sage/schemes/elliptic_curves/ell_torsion.py index 9a39ead60e5..62b75c79fea 100644 --- a/src/sage/schemes/elliptic_curves/ell_torsion.py +++ b/src/sage/schemes/elliptic_curves/ell_torsion.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Torsion subgroups of elliptic curves over number fields (including `\QQ`) diff --git a/src/sage/schemes/elliptic_curves/ell_wp.py b/src/sage/schemes/elliptic_curves/ell_wp.py index 8785976dec3..b7dd052a701 100644 --- a/src/sage/schemes/elliptic_curves/ell_wp.py +++ b/src/sage/schemes/elliptic_curves/ell_wp.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Weierstrass `\wp`-function for elliptic curves diff --git a/src/sage/schemes/elliptic_curves/formal_group.py b/src/sage/schemes/elliptic_curves/formal_group.py index dbd2db5f778..a7dcb48d36c 100644 --- a/src/sage/schemes/elliptic_curves/formal_group.py +++ b/src/sage/schemes/elliptic_curves/formal_group.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Formal groups of elliptic curves diff --git a/src/sage/schemes/elliptic_curves/gal_reps.py b/src/sage/schemes/elliptic_curves/gal_reps.py index c28b4f76d2c..9543215ee24 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps.py +++ b/src/sage/schemes/elliptic_curves/gal_reps.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Galois representations attached to elliptic curves diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py index 48f742a6cd0..33ada435181 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -740,10 +740,11 @@ def _exceptionals(E, L, patience=1000): def _over_numberfield(E): r""" - Return `E`, defined over a ``NumberField`` object. + Return `E`, defined over an absolute ``NumberField``. This is necessary since if `E` is defined over `\QQ`, then we - cannot use Sage commands available for number fields. + cannot use Sage commands available for number fields, and if the + base field is relative then other problems result. INPUT: @@ -751,26 +752,27 @@ def _over_numberfield(E): OUTPUT: - - If `E` is defined over a NumberField, returns E. + - If `E` is defined over an absolute number field, returns `E`. - - If `E` is defined over QQ, returns E defined over the NumberField QQ. + - If `E` is defined over a relative number field, returns `E` defined over the absolute base field. + + - If `E` is defined over `\QQ`, returns `E` defined over the number field `\QQ`. EXAMPLES:: sage: E = EllipticCurve([1, 2]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._over_numberfield(E) Elliptic Curve defined by y^2 = x^3 + x + 2 over Number Field in a with defining polynomial x - """ + """ K = E.base_field() if K == QQ: - x = QQ['x'].gen() - K = NumberField(x, 'a') - E = E.change_ring(K) - - return E - + from sage.rings.polynomial.polynomial_ring import polygen + K = NumberField(polygen(QQ), 'a') + else: + K = K.absolute_field('a') + return E.change_ring(K) def deg_one_primes_iter(K, principal_only=False): r""" @@ -858,7 +860,7 @@ def _semistable_reducible_primes(E, verbose=False): deg_one_primes = deg_one_primes_iter(K, principal_only=True) - bad_primes = set([]) # This will store the output. + bad_primes = set() # This will store the output. # We find two primes (of distinct residue characteristics) which are # of degree 1, unramified in K/Q, and at which E has good reduction. @@ -1096,7 +1098,7 @@ def _possible_normalizers(E, SA): W = W + V.span([splitting_vector]) - bad_primes = set([]) + bad_primes = set() for i in traces_list: for p in i.prime_factors(): diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index a377c02d5cb..1b5536cc515 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -844,7 +844,7 @@ def kolyvagin_generators(self): # so we just find a generator. for sigma in self: if sigma.order() == self.cardinality(): - return tuple([sigma]) + return (sigma,) raise NotImplementedError @@ -2568,7 +2568,7 @@ def betas(self): N = self.level() R = Integers(4*N) m = 2*N - return tuple(sorted( set([a % m for a in R(D).sqrt(all=True)]) )) + return tuple(sorted( {a % m for a in R(D).sqrt(all=True)} )) @cached_method def points(self, beta=None): @@ -3776,7 +3776,7 @@ def _square_roots_mod_2N_of_D_mod_4N(self): N = self.__E.conductor() R = Integers(4*N) m = 2*N - return sorted( set([a % m for a in R(self.discriminant()).sqrt(all=True)]) ) + return sorted( {a % m for a in R(self.discriminant()).sqrt(all=True)} ) def _trace_numerical_conductor_1(self, prec=53): """ diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index 30709dccf96..78736209ee5 100644 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -155,7 +155,7 @@ def is_empty(self): def __add__(left, right): r""" - If both left an right are unions of intervals, take their union, + If both left and right are unions of intervals, take their union, otherwise treat the non-union of intervals as a scalar and shift. EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index ed05e1760fa..1109fd9b93b 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -410,7 +410,7 @@ def graph(self): M = self.matrix(fill=False) n = len(self) G = Graph(M, format='weighted_adjacency_matrix') - D = dict([(v,self.curves[v]) for v in G.vertices(sort=False)]) + D = {v: self.curves[v] for v in G.vertices(sort=False)} G.set_vertices(D) if self._qfmat: # i.e. self.E.has_rational_cm(): for i in range(n): @@ -424,7 +424,7 @@ def graph(self): n = M.nrows() # = M.ncols() G = Graph(M, format='weighted_adjacency_matrix') N = self.matrix(fill=True) - D = dict([(v,self.curves[v]) for v in G.vertices(sort=False)]) + D = {v: self.curves[v] for v in G.vertices(sort=False)} # The maximum degree classifies the shape of the isogeny # graph, though the number of vertices is often enough. # This only holds over Q, so this code will need to change @@ -876,17 +876,17 @@ def add_tup(t): else: key_function = lambda E: flatten([list(ai) for ai in E.ainvs()]) - self.curves = sorted(curves,key=key_function) - perm = dict([(ind, self.curves.index(Ei)) - for ind, Ei in enumerate(curves)]) + self.curves = sorted(curves, key=key_function) + perm = {ind: self.curves.index(Ei) + for ind, Ei in enumerate(curves)} if verbose: print("Sorting permutation = %s" % perm) mat = MatrixSpace(ZZ, ncurves)(0) self._maps = [[0] * ncurves for _ in range(ncurves)] - for i,j,l,phi in tuples: + for i, j, l, phi in tuples: if phi != 0: - mat[perm[i],perm[j]] = l + mat[perm[i], perm[j]] = l self._maps[perm[i]][perm[j]] = phi self._mat = fill_isogeny_matrix(mat) if verbose: @@ -1109,7 +1109,7 @@ def _compute(self): curves.append(Edash) ijl_triples.append((i,j,l,phi)) if l_list is None: - l_list = [d for d in set([ZZ(f.degree()) for f in isogs])] + l_list = list({ZZ(f.degree()) for f in isogs}) i += 1 self.curves = tuple(curves) ncurves = len(curves) @@ -1135,7 +1135,8 @@ def isogeny_degrees_cm(E, verbose=False): A finite list of primes `\ell` such that every curve isogenous to this curve can be obtained by a finite sequence of isogenies of - degree one of the primes in the list. + degree one of the primes in the list. This list is not + necessarily minimal. ALGORITHM: @@ -1188,8 +1189,20 @@ def isogeny_degrees_cm(E, verbose=False): downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] - Complete set of primes: {2, 3, 5} - [2, 3, 5] + Set of primes before filtering: {2, 3, 5} + List of primes after filtering: [2, 3] + [2, 3] + + TESTS: + + Check that :issue:`36780` is fixed:: + + sage: L5. = NumberField(x^2-5) + sage: E = EllipticCurve(L5,[0,-4325477943600 *r5-4195572876000]) + sage: from sage.schemes.elliptic_curves.isogeny_class import isogeny_degrees_cm + sage: isogeny_degrees_cm(E) + [3, 5] + """ if not E.has_cm(): raise ValueError("possible_isogeny_degrees_cm(E) requires E to be an elliptic curve with CM") @@ -1205,6 +1218,11 @@ def isogeny_degrees_cm(E, verbose=False): n = E.base_field().absolute_degree() if not E.has_rational_cm(): n *= 2 + # For discriminants with extra units there's an extra factor in the class number formula: + if d == -4: + n *= 2 + if d == -3: + n *= 3 divs = n.divisors() data = pari(d).quadclassunit() @@ -1232,7 +1250,7 @@ def isogeny_degrees_cm(E, verbose=False): # of the order O of discriminant d. The latter case can only # happen when l^2 divides d. - # Compute the ramified primes + # (a) ramified primes ram_l = d.odd_part().prime_factors() @@ -1247,25 +1265,22 @@ def isogeny_degrees_cm(E, verbose=False): else: - # Find the "upward" primes (index divided by l): + # "Upward" primes (index divided by l): L1 = Set([l for l in ram_l if d.valuation(l) > 1]) L += L1 if verbose: print("upward primes: %s" % L1) - # Find the "downward" primes (index multiplied by l, class - # number multiplied by l-kronecker_symbol(d,l)): - - # (a) ramified primes; the suborder has class number l*h, so l - # must divide n/2h: + # "Downward" ramified primes; index multiplied by l, class + # number multiplied by l, so l must divide n/2h: L1 = Set([l for l in ram_l if l.divides(n_over_2h)]) L += L1 if verbose: print("downward ramified primes: %s" % L1) - # (b) split primes; the suborder has class number (l-1)*h, so + # (b) Downward split primes; the suborder has class number (l-1)*h, so # l-1 must divide n/2h: L1 = Set([lm1+1 for lm1 in divs @@ -1274,7 +1289,7 @@ def isogeny_degrees_cm(E, verbose=False): if verbose: print("downward split primes: %s" % L1) - # (c) inert primes; the suborder has class number (l+1)*h, so + # (c) Downward inert primes; the suborder has class number (l+1)*h, so # l+1 must divide n/2h: L1 = Set([lp1-1 for lp1 in divs @@ -1283,10 +1298,9 @@ def isogeny_degrees_cm(E, verbose=False): if verbose: print("downward inert primes: %s" % L1) - # Now find primes represented by each form of discriminant d. - # In the rational CM case, we use all forms associated to - # generators of the class group, otherwise only forms of order - # 2: + # Horizontal primes (rational CM only): same order, degrees are + # all integers represented by some binary quadratic form of + # discriminant d, so we find a prime represented by each form. if E.has_rational_cm(): from sage.quadratic_forms.binary_qf import BinaryQF @@ -1300,10 +1314,14 @@ def isogeny_degrees_cm(E, verbose=False): # Return sorted list if verbose: - print("Complete set of primes: %s" % L) - - return sorted(L) + print("Set of primes before filtering: %s" % L) + # This filter will quickly eliminate most false entries in the set + from .gal_reps_number_field import Frobenius_filter + L = Frobenius_filter(E, sorted(L)) + if verbose: + print("List of primes after filtering: %s" % L) + return L def possible_isogeny_degrees(E, algorithm='Billerey', max_l=None, num_l=None, exact=True, verbose=False): @@ -1434,8 +1452,9 @@ def possible_isogeny_degrees(E, algorithm='Billerey', max_l=None, downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] - Complete set of primes: {2, 3, 5} - [2, 3, 5] + Set of primes before filtering: {2, 3, 5} + List of primes after filtering: [2, 3] + [2, 3] """ if E.has_cm(): return isogeny_degrees_cm(E, verbose) diff --git a/src/sage/schemes/elliptic_curves/jacobian.py b/src/sage/schemes/elliptic_curves/jacobian.py index 1a33c7e4726..23151109453 100644 --- a/src/sage/schemes/elliptic_curves/jacobian.py +++ b/src/sage/schemes/elliptic_curves/jacobian.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Construct elliptic curves as Jacobians diff --git a/src/sage/schemes/elliptic_curves/kraus.py b/src/sage/schemes/elliptic_curves/kraus.py index 8565cc5e3be..92a74f4e718 100644 --- a/src/sage/schemes/elliptic_curves/kraus.py +++ b/src/sage/schemes/elliptic_curves/kraus.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Global and semi-global minimal models for elliptic curves over number fields diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 6e1dfa18c52..f3f1533f7d0 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ `L`-series for elliptic curves diff --git a/src/sage/schemes/elliptic_curves/mod_poly.py b/src/sage/schemes/elliptic_curves/mod_poly.py index 21926e14c8e..2482bb0da5a 100644 --- a/src/sage/schemes/elliptic_curves/mod_poly.py +++ b/src/sage/schemes/elliptic_curves/mod_poly.py @@ -22,7 +22,7 @@ _db = ClassicalModularPolynomialDatabase() _cache_bound = 100 -_cache = dict() +_cache = {} def classical_modular_polynomial(l, j=None): diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 33e509dca96..7cf14a0028b 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -1315,7 +1315,7 @@ cdef class ModularSymbolNumerical: if self._ans is NULL or self._ans_num is NULL: if self._ans is not NULL: sig_free(self._ans) if self._ans_num is not NULL: sig_free(self._ans_num) - raise MemoryError("Memory error with an coefficients.") + raise MemoryError("Memory error with coefficients.") verbose(" not enough precomputed coefficients, " "adding %s"%(T - self._lans), level=3) diff --git a/src/sage/schemes/elliptic_curves/modular_parametrization.py b/src/sage/schemes/elliptic_curves/modular_parametrization.py index 501c17cd571..2ed663e31cb 100644 --- a/src/sage/schemes/elliptic_curves/modular_parametrization.py +++ b/src/sage/schemes/elliptic_curves/modular_parametrization.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Modular parametrization of elliptic curves over `\QQ` diff --git a/src/sage/schemes/elliptic_curves/saturation.py b/src/sage/schemes/elliptic_curves/saturation.py index 095b5b1be12..5e4c66ad78a 100644 --- a/src/sage/schemes/elliptic_curves/saturation.py +++ b/src/sage/schemes/elliptic_curves/saturation.py @@ -128,10 +128,10 @@ def __init__(self, E, verbose=False): else: self._Kpol = K.defining_polynomial() self._D = self._Kpol.discriminant() - self._reductions = dict() - self._lincombs = dict() + self._reductions = {} + self._lincombs = {} self._torsion_gens = [t.element() for t in E.torsion_subgroup().gens()] - self._reductions = dict() + self._reductions = {} # This will hold a dictionary with keys (q,aq) with q prime # and aq a root of K's defining polynomial mod q, and values # (n,gens) where n is the cardinality of the reduction of E @@ -198,7 +198,7 @@ def add_reductions(self, q): """ if q in self._reductions: return - self._reductions[q] = redmodq = dict() + self._reductions[q] = redmodq = {} if q.divides(self._N) or q.divides(self._D): return from sage.schemes.elliptic_curves.constructor import EllipticCurve diff --git a/src/sage/schemes/elliptic_curves/sha_tate.py b/src/sage/schemes/elliptic_curves/sha_tate.py index 33818051b8f..b5a7b48bf0a 100644 --- a/src/sage/schemes/elliptic_curves/sha_tate.py +++ b/src/sage/schemes/elliptic_curves/sha_tate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Tate-Shafarevich group @@ -1065,7 +1064,7 @@ def bound_kolyvagin(self, D=0, regulator=None, if not ignore_nonsurj_hypothesis: for p in E.galois_representation().non_surjective(): B.append(p) - B = sorted(set(int(x) for x in B)) + B = sorted({int(x) for x in B}) return B, n def bound_kato(self): diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 0648ad2a9f4..2b3c61b5017 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -371,7 +371,7 @@ def embedding_morphism(self): or neighborhood of a point then the embedding is the embedding into the original scheme. - * A ``NotImplementedError`` is raised if the construction of + * A :class:`NotImplementedError` is raised if the construction of the embedding morphism is not implemented yet. EXAMPLES:: @@ -465,9 +465,8 @@ def embedding_center(self): OUTPUT: - A point of ``self``. Raises ``AttributeError`` if there is no - distinguished point, depending on how ``self`` was - constructed. + A point of ``self``. This raises :class:`AttributeError` if there + is no distinguished point, depending on how ``self`` was constructed. EXAMPLES:: @@ -1133,7 +1132,7 @@ def normalize_defining_polynomials(self): mult = lcm([c.denominator() for c in P.coefficients()]) P = mult*P # stores the common factor from all coefficients - div = gcd([_ for _ in P.coefficients()]) + div = gcd(list(P.coefficients())) poly_ring = P.parent() # need to coerce, since division might change base ring P = poly_ring((BR.one()/div)*P) normalized_polys.append(P) diff --git a/src/sage/schemes/generic/ambient_space.py b/src/sage/schemes/generic/ambient_space.py index d22ea07d149..09d26860af0 100644 --- a/src/sage/schemes/generic/ambient_space.py +++ b/src/sage/schemes/generic/ambient_space.py @@ -219,8 +219,8 @@ def base_extend(self, R): .. NOTE:: - A ``ValueError`` is raised if there is no such natural map. If - you need to drop this condition, use ``self.change_ring(R)``. + A :class:`ValueError` is raised if there is no such natural map. + If you need to drop this condition, use ``self.change_ring(R)``. EXAMPLES:: diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index 60e840619ac..a9a0f0735df 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -175,7 +175,7 @@ def create_key_and_extra_args(self, X, Y, category=None, base=None, if not category: from sage.categories.schemes import Schemes category = Schemes(base_spec) - key = tuple([id(X), id(Y), category, as_point_homset]) + key = (id(X), id(Y), category, as_point_homset) extra = {'X':X, 'Y':Y, 'base_ring':base_ring, 'check':check} return key, extra @@ -313,7 +313,7 @@ def natural_map(self): OUTPUT: A :class:`SchemeMorphism` if there is a natural map from - domain to codomain. Otherwise, a ``NotImplementedError`` is + domain to codomain. Otherwise, a :class:`NotImplementedError` is raised. EXAMPLES:: diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index da188ac63b3..88e7e6a61c2 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -498,7 +498,7 @@ def coordinate_ring(self): OUTPUT: The global coordinate ring of this scheme, if - defined. Otherwise raise a ``ValueError``. + defined. Otherwise this raises a :class:`ValueError`. EXAMPLES:: diff --git a/src/sage/schemes/generic/spec.py b/src/sage/schemes/generic/spec.py index c9a07313bb3..92453a86bf4 100644 --- a/src/sage/schemes/generic/spec.py +++ b/src/sage/schemes/generic/spec.py @@ -54,7 +54,7 @@ def Spec(R, S=None): sage: A is B True - A ``TypeError`` is raised if the input is not a commutative ring:: + A :class:`TypeError` is raised if the input is not a commutative ring:: sage: Spec(5) Traceback (most recent call last): diff --git a/src/sage/schemes/hyperelliptic_curves/invariants.py b/src/sage/schemes/hyperelliptic_curves/invariants.py index 05bffb5c113..a0101f60a3c 100644 --- a/src/sage/schemes/hyperelliptic_curves/invariants.py +++ b/src/sage/schemes/hyperelliptic_curves/invariants.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Compute invariants of quintics and sextics via 'Ueberschiebung' diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py b/src/sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py index 8d8d89896db..2ceb709b646 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Some functions regarding geometric endomorphism rings of Jacobians of hyperelliptic curves. diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py b/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py index 1d3b0fd2763..2e91a96e02a 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py @@ -41,7 +41,7 @@ class HyperellipticJacobian_generic(Jacobian_generic): sage: D == D + J(0) True - An more extended example, demonstrating arithmetic in J(QQ) and + A more extended example, demonstrating arithmetic in J(QQ) and J(K) for a number field K/QQ. :: diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index f8c8f847350..fb3b8bd8d4b 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -1683,7 +1683,7 @@ def matrix_of_frobenius(Q, p, M, trace=None, compute_exact_forms=False): F1_coeffs = transpose_list(F1.coeffs()) F1_modp_coeffs = F1_coeffs[int((M-2)*p):] # make a copy, because reduce_all will destroy the coefficients: - F1_modp_coeffs = [[cell for cell in row] for row in F1_modp_coeffs] + F1_modp_coeffs = [list(row) for row in F1_modp_coeffs] F1_modp_offset = offset - (M-2)*p F1_modp_reduced = reduce_all(Q, p, F1_modp_coeffs, F1_modp_offset) diff --git a/src/sage/schemes/jacobians/abstract_jacobian.py b/src/sage/schemes/jacobians/abstract_jacobian.py index 8fece57a242..9ab2ef283ae 100644 --- a/src/sage/schemes/jacobians/abstract_jacobian.py +++ b/src/sage/schemes/jacobians/abstract_jacobian.py @@ -177,7 +177,7 @@ def _point(self): OUTPUT: - This method always raises a ``NotImplementedError``; it is + This method always raises a :class:`NotImplementedError`; it is only abstract. EXAMPLES:: diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 97bd54408ac..26bea07db90 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -1050,7 +1050,7 @@ def rational_point(self, algorithm='default', read_cache=True): r""" Return a point on ``self`` defined over the base field. - Raises ``ValueError`` if no rational point exists. + This raises a :class:`ValueError` if no rational point exists. See ``self.has_rational_point`` for the algorithm used and for the use of the parameters ``algorithm`` and ``read_cache``. @@ -1202,7 +1202,8 @@ def singular_point(self): sage: Conic(GF(2), [1,1,1,1,1,1]).singular_point() (1 : 1 : 1) - ``ValueError`` is raised if the conic has no rational singular point + :class:`ValueError` is raised if the conic has no rational + singular point :: diff --git a/src/sage/schemes/plane_conics/con_rational_function_field.py b/src/sage/schemes/plane_conics/con_rational_function_field.py index 581102b5c5b..a5dcfb9bf1b 100644 --- a/src/sage/schemes/plane_conics/con_rational_function_field.py +++ b/src/sage/schemes/plane_conics/con_rational_function_field.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Projective plane conics over a rational function field diff --git a/src/sage/schemes/plane_conics/constructor.py b/src/sage/schemes/plane_conics/constructor.py index 20bc5630ac1..7bb681a4268 100644 --- a/src/sage/schemes/plane_conics/constructor.py +++ b/src/sage/schemes/plane_conics/constructor.py @@ -92,7 +92,7 @@ def Conic(base_field, F=None, names=None, unique=True): - ``unique`` -- Used only if ``F`` is a list of points in the plane. If the conic through the points is not unique, then - raise ``ValueError`` if and only if ``unique`` is True + raise :class:`ValueError` if and only if ``unique`` is ``True`` OUTPUT: diff --git a/src/sage/schemes/product_projective/point.py b/src/sage/schemes/product_projective/point.py index 70cc344806f..6de7cf74d7d 100644 --- a/src/sage/schemes/product_projective/point.py +++ b/src/sage/schemes/product_projective/point.py @@ -368,8 +368,8 @@ def scale_by(self, t): r""" Scale the coordinates of the point by ``t``, done componentwise. - A ``TypeError`` occurs if the point is not in the base ring of the - codomain after scaling. + A :class:`TypeError` occurs if the point is not in the base ring + of the codomain after scaling. INPUT: diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index c9a2cf6b25f..a4488647063 100644 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -393,7 +393,7 @@ def good_primes(B): of the ambient space. """ - M = dict() # stores optimal list of primes, corresponding to list size + M = {} # stores optimal list of primes, corresponding to list size small_primes = sufficient_primes(B) max_length = len(small_primes) M[max_length] = small_primes diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index 0293e666cd6..3fee46cc15a 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -624,7 +624,7 @@ def _degree(self, polynomial): - ``polynomial`` -- a polynomial in the coordinate_ring OUTPUT: A tuple of integers, one for each projective space component. A - ``ValueError`` is raised if the polynomial is not multihomogeneous. + :class:`ValueError` is raised if the polynomial is not multihomogeneous. EXAMPLES:: @@ -694,7 +694,7 @@ def _point_homset(self, *args, **kwds): def _validate(self, polynomials): r""" If ``polynomials`` is a tuple of valid polynomial functions on this space, - return ``polynomials``, otherwise raise a ``TypeError``. + return ``polynomials``, otherwise raise a :class:`TypeError`. Since this is a product of projective spaces, the polynomials must be multi-homogeneous. diff --git a/src/sage/schemes/projective/proj_bdd_height.py b/src/sage/schemes/projective/proj_bdd_height.py index 744313fc77f..86100cf832a 100644 --- a/src/sage/schemes/projective/proj_bdd_height.py +++ b/src/sage/schemes/projective/proj_bdd_height.py @@ -78,9 +78,9 @@ def ZZ_points_of_bounded_height(PS, dim, bound): [] """ if bound < 1: - return iter(set([])) + return iter(set()) - points_of_bounded_height = set([]) + points_of_bounded_height = set() for t in itertools.product(range(-bound, bound+1), repeat=dim+1): if gcd(t) == 1: @@ -133,10 +133,10 @@ def QQ_points_of_bounded_height(PS, dim, bound, normalize=False): [] """ if bound < 1: - return iter(set([])) + return iter(set()) unit_tuples = list(itertools.product([-1, 1], repeat=dim)) - points_of_bounded_height = set([]) + points_of_bounded_height = set() increasing_tuples = itertools.combinations_with_replacement(range(floor(bound + 1)), dim + 1) for t in increasing_tuples: if gcd(t) == 1: @@ -190,12 +190,12 @@ def IQ_points_of_bounded_height(PS, K, dim, bound): class_group_ideal_norms = [i.norm() for i in class_group_ideals] class_number = len(class_group_ideals) - possible_norm_set = set([]) + possible_norm_set = set() for i in range(class_number): for k in range(1, floor(bound + 1)): possible_norm_set.add(k*class_group_ideal_norms[i]) - coordinate_space = dict() + coordinate_space = {} coordinate_space[0] = [K(0)] for m in possible_norm_set: coordinate_space[m] = K.elements_of_norm(m) @@ -212,7 +212,7 @@ def IQ_points_of_bounded_height(PS, K, dim, bound): if x in a: a_coordinates.append(x) - points_in_class_a = set([]) + points_in_class_a = set() t = len(a_coordinates) - 1 increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) for index_tuple in increasing_tuples: @@ -345,12 +345,12 @@ def points_of_bounded_height(PS, K, dim, bound, prec=53): fundamental_units = lll_fund_units fund_unit_logs = list(map(log_embed, fundamental_units)) - possible_norm_set = set([]) + possible_norm_set = set() for i in range(class_number): for k in range(1, floor(bound + 1)): possible_norm_set.add(k*class_group_ideal_norms[i]) - principal_ideal_gens = dict() + principal_ideal_gens = {} negative_norm_units = K.elements_of_norm(-1) if len(negative_norm_units) == 0: for m in possible_norm_set: @@ -359,7 +359,7 @@ def points_of_bounded_height(PS, K, dim, bound, prec=53): for m in possible_norm_set: principal_ideal_gens[m] = K.elements_of_norm(m) - pr_ideal_gen_logs = dict() + pr_ideal_gen_logs = {} for key in principal_ideal_gens: for y in principal_ideal_gens[key]: pr_ideal_gen_logs[y] = log_embed(y) @@ -398,11 +398,11 @@ def points_of_bounded_height(PS, K, dim, bound, prec=53): T_it = T.inverse().transpose() unit_polytope = Polyhedron([v*T_it for v in vertices]) - coordinate_space = dict() + coordinate_space = {} coordinate_space[0] = [[K(0), log_embed(0)]] int_points = unit_polytope.integral_points() - units_with_logs = dict() + units_with_logs = {} for n in int_points: new_unit = 1 for j in range(r): @@ -444,7 +444,7 @@ def points_of_bounded_height(PS, K, dim, bound, prec=53): a_coordinates.append(pair) t = len(a_coordinates) - 1 - points_in_class_a = set([]) + points_in_class_a = set() increasing_tuples = itertools.combinations_with_replacement(range(t + 1), dim + 1) log_arch_height_bound = logB + log_a_norm for index_tuple in increasing_tuples: diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 2983b03796e..fe408bd1d42 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Morphisms on projective schemes @@ -735,9 +734,9 @@ def as_dynamical_system(self): def scale_by(self, t): """ - Scales each coordinate by a factor of ``t``. + Scale each coordinate by a factor of ``t``. - A ``TypeError`` occurs if the point is not in the coordinate ring + A :class:`TypeError` occurs if the point is not in the coordinate ring of the parent after scaling. INPUT: diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index e22e0a858b1..55bd5354976 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -488,11 +488,11 @@ def _matrix_times_point_(self, mat, dom): X = mat * vector(list(self)) return dom.codomain()(list(X)) - def scale_by(self,t): + def scale_by(self, t): """ Scale the coordinates of the point by ``t``. - A ``TypeError`` occurs if the point is not in the + A :class:`TypeError` occurs if the point is not in the base_ring of the codomain after scaling. INPUT: diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index cc99ba389ac..6e08612f88b 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -412,7 +412,7 @@ def good_primes(B): where alpha is product of all primes, and P_max is largest prime in list. """ - M = dict() # stores optimal list of primes, corresponding to list size + M = {} # stores optimal list of primes, corresponding to list size small_primes = sufficient_primes(B) max_length = len(small_primes) M[max_length] = small_primes diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index c1402654a87..5c55a62106e 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -180,7 +180,7 @@ def ProjectiveSpace(n, R=None, names=None): sage: ProjectiveSpace(5) Projective Space of dimension 5 over Integer Ring - There is also an projective space associated each polynomial ring. + There is also a projective space associated each polynomial ring. :: @@ -1951,7 +1951,7 @@ def hyperplane_transformation_matrix(self, plane_1, plane_2): base_list = [list(s) for s in source_points] elif len(source_points) == N + 1: Ms = matrix(base_list + [point.change_ring(self.base_ring())]) - if not any([m == 0 for m in Ms.minors(N + 1)]): + if not any(m == 0 for m in Ms.minors(N + 1)): source_points.append(self(point)) break if len(source_points) != N+2: @@ -2371,10 +2371,10 @@ def rational_points(self, F=None): (b + 1 : 1), (b + 2 : 1), (b : 1)] """ if F is None: - return [P for P in self] + return list(self) elif not isinstance(F, FiniteField): raise TypeError("second argument (= %s) must be a finite field" % F) - return [P for P in self.base_extend(F)] + return list(self.base_extend(F)) def rational_points_dictionary(self): r""" diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index b7e3f1ac8f6..a7760fa7101 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -1688,10 +1688,10 @@ def direction(center, neighbour): # that entry, and the corresponding cycle. (also, forms it # into a loop) if P[i][j] != 0: - acycles[i] += [(P[i][j], [x for x in cycles[j]] + [cycles[j][0]])] + acycles[i] += [(P[i][j], list(cycles[j]) + [cycles[j][0]])] if P[self.genus + i][j] != 0: bcycles[i] += [ - (P[self.genus + i][j], [x for x in cycles[j]] + [cycles[j][0]]) + (P[self.genus + i][j], list(cycles[j]) + [cycles[j][0]]) ] return acycles + bcycles @@ -2326,7 +2326,7 @@ def normalize_pairs(L): integral_dict = self._integral_dict else: fcd = [fast_callable(omega, domain=self._CC) for omega in differentials] - integral_dict = dict() + integral_dict = {} if integration_method == "heuristic": line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) diff --git a/src/sage/schemes/toric/chow_group.py b/src/sage/schemes/toric/chow_group.py index dac091a4f6f..9b612ece0eb 100644 --- a/src/sage/schemes/toric/chow_group.py +++ b/src/sage/schemes/toric/chow_group.py @@ -1,5 +1,4 @@ # sage.doctest: needs sage.geometry.polyhedron sage.graphs -# -*- coding: utf-8 -*- r""" The Chow group of a toric variety @@ -553,7 +552,7 @@ def create_key_and_extra_args(self, toric_variety, base_ring=ZZ, check=True): if base_ring not in [ZZ, QQ]: raise ValueError('base ring must be either ZZ or QQ') - key = tuple([toric_variety, base_ring]) + key = (toric_variety, base_ring) extra = {'check': check} return key, extra diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index d70a3e95d5d..c09c404d64a 100644 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -868,7 +868,7 @@ def Cube_nonpolyhedral(self, names='z+', base_ring=QQ): .. NOTE:: - * This is an example of an non-polyhedral fan. + * This is an example of a non-polyhedral fan. * Its Chow group has torsion: `A_2(X)=\ZZ^5 \oplus \ZZ_2` diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py index 4a40aa82ef7..20507e5c0af 100644 --- a/src/sage/schemes/toric/points.py +++ b/src/sage/schemes/toric/points.py @@ -96,7 +96,7 @@ def __iter__(self): rays = self.fan().rays() + self.fan().virtual_rays() n = len(rays) if n == 0: - yield tuple() + yield () else: R = self.ring p = [R.one() for k in range(n)] @@ -242,7 +242,7 @@ def _Chow_group_torsion(self): ((1, 2, 4), (1, 4, 2)) """ if self.fan.is_smooth(): - return tuple() + return () image = self.rays().column_matrix().image() torsion = image.saturation().quotient(image) result = set() @@ -539,7 +539,7 @@ def _Chow_group_torsion_generators(self): ((1, 2, 4),) """ if self.fan.is_smooth(): - return tuple() + return () image = self.rays().column_matrix().image() torsion = image.saturation().quotient(image) result = set() diff --git a/src/sage/schemes/toric/sheaf/constructor.py b/src/sage/schemes/toric/sheaf/constructor.py index 52f00d99815..72ed52a0115 100644 --- a/src/sage/schemes/toric/sheaf/constructor.py +++ b/src/sage/schemes/toric/sheaf/constructor.py @@ -40,7 +40,7 @@ def TangentBundle(X): raise ValueError('not a toric variety') fan = X.fan() - filtrations = dict() + filtrations = {} from sage.modules.filtered_vector_space import FilteredVectorSpace for i, ray in enumerate(fan.rays()): F = FilteredVectorSpace(fan.rays(), {0: range(fan.nrays()), 1: [i]}) diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py index 08cea1487f9..4325e77f186 100644 --- a/src/sage/schemes/toric/sheaf/klyachko.py +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -685,7 +685,7 @@ def cohomology(self, degree=None, weight=None, dim=False): C = self.cohomology_complex(weight) space_dim = self._variety.dimension() C_homology = C.homology() - HH = dict() + HH = {} for d in range(space_dim+1): try: HH[d] = C_homology[d] diff --git a/src/sage/schemes/toric/toric_subscheme.py b/src/sage/schemes/toric/toric_subscheme.py index 6b3645d0e4a..3d481846b88 100644 --- a/src/sage/schemes/toric/toric_subscheme.py +++ b/src/sage/schemes/toric/toric_subscheme.py @@ -220,7 +220,7 @@ def affine_patch(self, i): try: return self._affine_patches[i] except AttributeError: - self._affine_patches = dict() + self._affine_patches = {} except KeyError: pass ambient_patch = self.ambient_space().affine_patch(i) @@ -315,9 +315,9 @@ def affine_algebraic_patch(self, cone=None, names=None): R, I, dualcone = ambient._semigroup_ring(cone, names) # inhomogenize the Cox homogeneous polynomial with respect to the given cone - inhomogenize = dict( (ambient.coordinate_ring().gen(i), 1) - for i in range(0,fan.nrays()) - if i not in cone.ambient_ray_indices() ) + inhomogenize = {ambient.coordinate_ring().gen(i): 1 + for i in range(fan.nrays()) + if i not in cone.ambient_ray_indices()} polynomials = [p.subs(inhomogenize) for p in self.defining_polynomials()] # map the monomial x^{D_m} to m, see reference. @@ -402,7 +402,7 @@ def _best_affine_patch(self, point): # that it is numerically stable to dehomogenize, see the # corresponding method for projective varieties. point = list(point) - zeros = set(i for i, coord in enumerate(point) if coord == 0) + zeros = {i for i, coord in enumerate(point) if coord == 0} for cone_idx, cone in enumerate(self.ambient_space().fan().generating_cones()): if zeros.issubset(cone.ambient_ray_indices()): return cone_idx @@ -410,7 +410,7 @@ def _best_affine_patch(self, point): def neighborhood(self, point): r""" - Return an toric algebraic scheme isomorphic to neighborhood of + Return a toric algebraic scheme isomorphic to neighborhood of the ``point``. INPUT: @@ -677,8 +677,8 @@ def is_nondegenerate(self): SR = SR.change_ring(R) def restrict(cone): - patch = dict() - divide = dict() + patch = {} + divide = {} for i in cone.ambient_ray_indices(): patch[R.gen(i)] = R.zero() # restrict to torus orbit # divide out highest power of R.gen(i) diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 3635e7abbd4..9b06bd0223c 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -1,5 +1,4 @@ # sage.doctest: needs sage.geometry.polyhedron sage.graphs -# -*- coding: utf-8 -*- r""" Toric varieties @@ -709,9 +708,9 @@ def _check_satisfies_equations(self, coordinates): if coordinate not in base_field: raise TypeError("coordinate %s is not an element of %s" % (coordinate, base_field)) - zero_positions = set(position - for position, coordinate in enumerate(coordinates) - if coordinate == 0) + zero_positions = {position + for position, coordinate in enumerate(coordinates) + if coordinate == 0} if not zero_positions: return True for i in range(n - self._torus_factor_dim, n): @@ -2731,7 +2730,7 @@ def orbit_closure(self, cone): star_rays = set() for star_cone in cone.star_generators(): star_rays.update(star_cone.rays()) - ray_map = dict( (ray, self._orbit_closure_projection(cone, ray)) for ray in star_rays) + ray_map = {ray: self._orbit_closure_projection(cone, ray) for ray in star_rays} from sage.schemes.toric.morphism import SchemeMorphism_orbit_closure_toric_variety orbit_closure._embedding_morphism = \ SchemeMorphism_orbit_closure_toric_variety(orbit_closure.Hom(self), cone, ray_map) diff --git a/src/sage/schemes/toric/weierstrass.py b/src/sage/schemes/toric/weierstrass.py index 137dfd3e042..b727e586141 100644 --- a/src/sage/schemes/toric/weierstrass.py +++ b/src/sage/schemes/toric/weierstrass.py @@ -281,7 +281,7 @@ def Newton_polytope_vars_coeffs(polynomial, variables): """ R = polynomial.parent() var_indices = [R.gens().index(x) for x in variables] - result = dict() + result = {} for c, m in polynomial: e = m.exponents()[0] v = tuple([e[i] for i in var_indices]) @@ -596,7 +596,7 @@ def index(monomial): return tuple(0 for i in indices) e = monomial.exponents()[0] return tuple(e[i] for i in indices) - coeffs = dict() + coeffs = {} for c, m in polynomial: i = index(m) coeffs[i] = c*m + coeffs.pop(i, R.zero()) @@ -992,7 +992,7 @@ def _check_polynomial_P2_112(polynomial, variables): _check_homogeneity(polynomial, variables, (1, 0, 1, -2), 0) _check_homogeneity(polynomial, variables, (0, 1, 0, 1), 2) elif len(variables) == 2: - variables = tuple([variables[0], variables[1], None, None]) + variables = (variables[0], variables[1], None, None) else: raise ValueError(f'need two or four variables, got {variables}') return variables diff --git a/src/sage/schemes/toric/weierstrass_covering.py b/src/sage/schemes/toric/weierstrass_covering.py index cc9caeeae4d..128c30d33a8 100644 --- a/src/sage/schemes/toric/weierstrass_covering.py +++ b/src/sage/schemes/toric/weierstrass_covering.py @@ -255,9 +255,9 @@ def homogenize(inhomog, degree): result = vector(ZZ, result) result.set_immutable() return result - X_dict = dict((homogenize(e, 2), v) for e, v in X.dict().items()) - Y_dict = dict((homogenize(e, 3), v) for e, v in Y.dict().items()) - Z_dict = dict((homogenize(e, 1), v) for e, v in Z.dict().items()) + X_dict = {homogenize(e, 2): v for e, v in X.dict().items()} + Y_dict = {homogenize(e, 3): v for e, v in Y.dict().items()} + Z_dict = {homogenize(e, 1): v for e, v in Z.dict().items()} # shift to non-negative exponents if necessary min_deg = [0] * R.ngens() for var in variables: @@ -267,9 +267,9 @@ def homogenize(inhomog, degree): min_Z = min([e[i] for e in Z_dict]) if Z_dict else 0 min_deg[i] = min(min_X / 2, min_Y / 3, min_Z) min_deg = vector(min_deg) - X_dict = dict((tuple(e - 2 * min_deg), v) for e, v in X_dict.items()) - Y_dict = dict((tuple(e - 3 * min_deg), v) for e, v in Y_dict.items()) - Z_dict = dict((tuple(e - 1 * min_deg), v) for e, v in Z_dict.items()) + X_dict = {tuple(e - 2 * min_deg): v for e, v in X_dict.items()} + Y_dict = {tuple(e - 3 * min_deg): v for e, v in Y_dict.items()} + Z_dict = {tuple(e - 1 * min_deg): v for e, v in Z_dict.items()} return (R(X_dict), R(Y_dict), R(Z_dict)) diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd new file mode 100644 index 00000000000..f5d8f755ecc --- /dev/null +++ b/src/sage/sets/family.pxd @@ -0,0 +1,11 @@ +from sage.structure.parent cimport Parent + + +cdef class AbstractFamily(Parent): + cdef public __custom_name + cdef dict __dict__ # enables Python attributes as needed for EnumeratedSets() + + +cdef class FiniteFamily(AbstractFamily): + cdef public dict _dictionary + cdef public object _keys diff --git a/src/sage/sets/family.py b/src/sage/sets/family.pyx similarity index 96% rename from src/sage/sets/family.py rename to src/sage/sets/family.pyx index f3c3d0a7556..d1c102261a8 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.pyx @@ -22,10 +22,16 @@ Category of finite enumerated sets """ -# **************************************************************************** -# Copyright (C) 2008 Nicolas Thiery , -# Mike Hansen , -# Florent Hivert +# ***************************************************************************** +# Copyright (C) 2008-2017 Nicolas Thiery +# 2008-2009 Mike Hansen +# 2008-2010 Florent Hivert +# 2013-2021 Travis Scrimshaw +# 2014 Nathann Cohen +# 2017 Erik M. Bray +# 2018 Frédéric Chapoton +# 2019 Markus Wageringel +# 2022-2023 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 @@ -38,19 +44,18 @@ from pprint import pformat, saferepr from collections.abc import Iterable -from sage.misc.abstract_method import abstract_method -from sage.misc.cachefunc import cached_method -from sage.structure.parent import Parent from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet -from sage.misc.lazy_import import lazy_import -from sage.rings.integer import Integer +from sage.misc.cachefunc import cached_method from sage.misc.call import AttrCallObject -from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.misc.lazy_import import LazyImport from sage.rings.infinity import Infinity -lazy_import('sage.combinat.combinat', 'CombinatorialClass') +from sage.rings.integer import Integer +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.sets.non_negative_integers import NonNegativeIntegers + +CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass') def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None): @@ -396,8 +401,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa return TrivialFamily(indices) if isinstance(indices, (FiniteFamily, LazyFamily, TrivialFamily)): return indices - if (indices in EnumeratedSets() - or isinstance(indices, CombinatorialClass)): + if indices in EnumeratedSets(): return EnumeratedFamily(indices) if isinstance(indices, Iterable): return TrivialFamily(indices) @@ -418,7 +422,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa keys=indices) -class AbstractFamily(Parent): +cdef class AbstractFamily(Parent): """ The abstract class for family @@ -436,7 +440,6 @@ def hidden_keys(self): """ return [] - @abstract_method def keys(self): """ Return the keys of the family. @@ -447,8 +450,8 @@ def keys(self): sage: sorted(f.keys()) [3, 4, 7] """ + raise NotImplementedError - @abstract_method(optional=True) def values(self): """ Return the elements (values) of this family. @@ -459,6 +462,7 @@ def values(self): sage: sorted(f.values()) ['aa', 'bb', 'cc'] """ + raise NotImplementedError def items(self): """ @@ -535,7 +539,8 @@ def inverse_family(self): return Family({self[k]: k for k in self.keys()}) -class FiniteFamily(AbstractFamily): + +cdef class FiniteFamily(AbstractFamily): r""" A :class:`FiniteFamily` is an associative container which models a finite family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its @@ -712,8 +717,8 @@ def __eq__(self, other): False """ return (isinstance(other, self.__class__) and - self._keys == other._keys and - self._dictionary == other._dictionary) + self._keys == ( other)._keys and + self._dictionary == ( other)._dictionary) def _repr_(self): """ @@ -869,15 +874,17 @@ def __getitem__(self, i): ... KeyError """ - if i in self._dictionary: - return self._dictionary[i] - - if i not in self.hidden_dictionary: - if i not in self._hidden_keys: - raise KeyError - self.hidden_dictionary[i] = self.hidden_function(i) - - return self.hidden_dictionary[i] + try: + return FiniteFamily.__getitem__(self, i) + except KeyError: + try: + return self.hidden_dictionary[i] + except KeyError: + if i not in self._hidden_keys: + raise KeyError + v = self.hidden_function(i) + self.hidden_dictionary[i] = v + return v def hidden_keys(self): """ @@ -902,11 +909,11 @@ def __getstate__(self): """ from sage.misc.fpickle import pickle_function f = pickle_function(self.hidden_function) - return {'dictionary': self._dictionary, - 'hidden_keys': self._hidden_keys, - 'hidden_dictionary': self.hidden_dictionary, - 'hidden_function': f, - 'keys': self._keys} + state = super().__getstate__() + state.update({'hidden_keys': self._hidden_keys, + 'hidden_dictionary': self.hidden_dictionary, + 'hidden_function': f}) + return state def __setstate__(self, d): """ @@ -922,7 +929,7 @@ def __setstate__(self, d): 6 """ hidden_function = d['hidden_function'] - if isinstance(hidden_function, str): + if isinstance(hidden_function, (str, bytes)): # Let's assume that hidden_function is an unpickled function. from sage.misc.fpickle import unpickle_function hidden_function = unpickle_function(hidden_function) @@ -1074,7 +1081,7 @@ def _repr_(self): """ if self.function_name is not None: name = self.function_name + "(i)" - elif isinstance(self.function, type(lambda x: 1)): + elif isinstance(self.function, types.LambdaType): name = self.function.__name__ name = name + "(i)" else: diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 45394cba23c..2b431284e4d 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1860,7 +1860,7 @@ def open_closed(lower, upper, **kwds): @staticmethod def closed_open(lower, upper, **kwds): r""" - Construct an half-open interval + Construct a half-open interval INPUT: @@ -1872,7 +1872,7 @@ def closed_open(lower, upper, **kwds): OUTPUT: A new :class:`RealSet` that is closed at the lower bound and - open an the upper bound. + open at the upper bound. EXAMPLES:: diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx index d79e104704f..66720d924e5 100644 --- a/src/sage/sets/recursively_enumerated_set.pyx +++ b/src/sage/sets/recursively_enumerated_set.pyx @@ -960,7 +960,7 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): sage: R.to_digraph() # needs sage.graphs Looped multi-digraph on 10 vertices - Digraph of an recursively enumerated set with a symmetric structure of + Digraph of a recursively enumerated set with a symmetric structure of infinite cardinality using ``max_depth`` argument:: sage: succ = lambda a: [(a[0]-1,a[1]), (a[0],a[1]-1), (a[0]+1,a[1]), (a[0],a[1]+1)] @@ -976,7 +976,7 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): sage: C.to_digraph() # needs sage.graphs Looped multi-digraph on 25 vertices - Digraph of an recursively enumerated set with a graded structure:: + Digraph of a recursively enumerated set with a graded structure:: sage: f = lambda a: [a+1, a+I] sage: C = RecursivelyEnumeratedSet([0], f, structure='graded') diff --git a/src/sage/symbolic/expression_conversion_sympy.py b/src/sage/symbolic/expression_conversion_sympy.py index 4a3b1556ce1..c2c1b3e2634 100644 --- a/src/sage/symbolic/expression_conversion_sympy.py +++ b/src/sage/symbolic/expression_conversion_sympy.py @@ -129,7 +129,6 @@ def symbol(self, ex): """ EXAMPLES:: - sage: sage: from sage.symbolic.expression_conversions import SympyConverter sage: s = SympyConverter() sage: s.symbol(x) @@ -144,7 +143,6 @@ def relation(self, ex, op): """ EXAMPLES:: - sage: sage: import operator sage: from sage.symbolic.expression_conversions import SympyConverter sage: s = SympyConverter() diff --git a/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py b/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py index e54352bafe9..5f8cd284b52 100644 --- a/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py @@ -252,19 +252,19 @@ ~~~~~~~~~~~~~~~~~~~~~~ :: - sage: H = G.subgroup([a^2]) + sage: H = G.subgroup([a^2]) # optional - gap_package_polycyclic sage: H.order() 7 ~~~~~~~~~~~~~~~~~~~~~~ :: - sage: K = G.subgroup([a^12]) + sage: K = G.subgroup([a^12]) # optional - gap_package_polycyclic sage: K.order() 7 ~~~~~~~~~~~~~~~~~~~~~~ :: - sage: allsg = G.subgroups(); allsg + sage: allsg = G.subgroups(); allsg # optional - gap_package_polycyclic [Multiplicative Abelian subgroup isomorphic to C2 x C7 generated by {a}, Multiplicative Abelian subgroup isomorphic to C7 generated by {a^2}, Multiplicative Abelian subgroup isomorphic to C2 generated by {a^7}, @@ -272,8 +272,8 @@ ~~~~~~~~~~~~~~~~~~~~~~ :: - sage: sub = allsg[2] - sage: sub.order() + sage: sub = allsg[2] # optional - gap_package_polycyclic + sage: sub.order() # optional - gap_package_polycyclic 2 ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/tests/gap_packages.py b/src/sage/tests/gap_packages.py index b13bba24a83..249d1b03d23 100644 --- a/src/sage/tests/gap_packages.py +++ b/src/sage/tests/gap_packages.py @@ -10,11 +10,11 @@ Status Package GAP Output +--------+---------+------------+ - sage: test_packages(['atlasrep', 'tomlib']) + sage: test_packages(['primgrp', 'smallgrp']) Status Package GAP Output +--------+----------+------------+ - atlasrep true - tomlib true + primgrp true + smallgrp true """ import os @@ -103,8 +103,8 @@ def all_installed_packages(ignore_dot_gap=False, gap=None): EXAMPLES:: sage: from sage.tests.gap_packages import all_installed_packages - sage: all_installed_packages() - (...'gapdoc'...) + sage: [p.lower() for p in all_installed_packages()] + [...'gapdoc'...] sage: all_installed_packages(ignore_dot_gap=True) == all_installed_packages(gap=gap, ignore_dot_gap=True) True """ @@ -116,6 +116,16 @@ def all_installed_packages(ignore_dot_gap=False, gap=None): else: paths = [str(p) for p in gap('GAPInfo.RootPaths')] + # When GAP_ROOT_PATHS begins or ends with a semicolon (to append + # or prepend to the default list), the list of "gap" root paths + # will sometimes contain duplicates while the list for libgap will + # not. I don't know why this is: the appending/prepending does + # work as intended, even for libgap, so the issue is not that + # appending/prepending don't work at all for libgap. For lack of a + # better idea, we deduplicate here to avoid listing the same + # packages twice for the non-lib "gap" interface. + paths = set(paths) + packages = [] for path in paths: if ignore_dot_gap and path.endswith('/.gap/'): diff --git a/src/sage/topology/simplicial_complex_examples.py b/src/sage/topology/simplicial_complex_examples.py index 304ea7ec8aa..2b1af7cbbe4 100644 --- a/src/sage/topology/simplicial_complex_examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -1423,7 +1423,6 @@ def RandomTwoSphere(n): EXAMPLES:: - sage: sage: G = simplicial_complexes.RandomTwoSphere(6); G Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and 8 facets sage: G.homology() # needs sage.modules diff --git a/src/sage/topology/simplicial_complex_morphism.py b/src/sage/topology/simplicial_complex_morphism.py index 3d4fc063198..0030c4edee8 100644 --- a/src/sage/topology/simplicial_complex_morphism.py +++ b/src/sage/topology/simplicial_complex_morphism.py @@ -244,20 +244,43 @@ def __call__(self, x, orientation=False): (0, 1) sage: g(Simplex([0,1]), orientation=True) # needs sage.modules ((0, 1), -1) + + TESTS: + + Test that the problem in :issue:`36849` has been fixed:: + + sage: S = SimplicialComplex([[1,2]],is_mutable=False).barycentric_subdivision() + sage: T = SimplicialComplex([[1,2],[2,3],[1,3]],is_mutable=False).barycentric_subdivision() + sage: f = {x[0]:x[0] for x in S.cells()[0]} + sage: H = Hom(S,T) + sage: z = H(f) + sage: z.associated_chain_complex_morphism() + Chain complex morphism: + From: Chain complex with at most 2 nonzero terms over Integer Ring + To: Chain complex with at most 2 nonzero terms over Integer Ring """ dim = self.domain().dimension() if not isinstance(x, Simplex) or x.dimension() > dim or x not in self.domain().faces()[x.dimension()]: raise ValueError("x must be a simplex of the source of f") tup = x.tuple() - fx = [] - for j in tup: - fx.append(self._vertex_dictionary[j]) + fx = [self._vertex_dictionary[j] for j in tup] if orientation: from sage.algebras.steenrod.steenrod_algebra_misc import convert_perm from sage.combinat.permutation import Permutation if len(set(fx)) == len(tup): - oriented = Permutation(convert_perm(fx)).signature() + # We need to compare the image simplex, as given in + # the order specified by self, with its orientation in + # the codomain. + image = Simplex(set(fx)) + Y_faces = self.codomain()._n_cells_sorted(image.dimension()) + idx = Y_faces.index(image) + actual_image = Y_faces[idx] + # The signature of the permutation specified by self: + sign_image = Permutation(convert_perm(fx)).signature() + # The signature of the permutation of the simplex in the domain: + sign_simplex = Permutation(convert_perm(actual_image)).signature() + oriented = sign_image * sign_simplex else: oriented = 1 return (Simplex(set(fx)), oriented) diff --git a/src/sage/topology/simplicial_set.py b/src/sage/topology/simplicial_set.py index e33465f1153..9b707a62916 100644 --- a/src/sage/topology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -2382,7 +2382,6 @@ def coproduct(self, *others): sage: Y = S2.unset_base_point() sage: Z = K.unset_base_point() - sage: sage: S2.coproduct(K).is_pointed() True sage: S2.coproduct(K) diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 032b79322da..395245c0df5 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -4,7 +4,7 @@ This file contains: -- :class:`AsciiArt` an simple implementation of an ASCII art object, +- :class:`AsciiArt` a simple implementation of an ASCII art object, - :func:`ascii_art` a function to get the ASCII art representation of any object in Sage, diff --git a/src/sage/version.py b/src/sage/version.py index 92ef577e70a..ae557017ab3 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.3.beta2' -date = '2023-12-13' -banner = 'SageMath version 10.3.beta2, Release Date: 2023-12-13' +version = '10.3.beta4' +date = '2023-12-26' +banner = 'SageMath version 10.3.beta4, Release Date: 2023-12-26' diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 8602016d042..0ab862c7a13 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -198,6 +198,10 @@ def sphinx_plot(graphics, **kwds): # for a list of supported languages. #language = None +# The LaTeX engine to build the docs. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-latex_engine +latex_engine = 'lualatex' + # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' @@ -502,182 +506,6 @@ def set_intersphinx_mappings(app, config): \usepackage{mathrsfs} \usepackage{iftex} -% Only declare unicode characters when compiling with pdftex; E.g. japanese -% tutorial does not use pdftex -\ifPDFTeX - \DeclareUnicodeCharacter{01CE}{\capitalcaron a} - \DeclareUnicodeCharacter{0428}{cyrillic Sha} - \DeclareUnicodeCharacter{250C}{+} - \DeclareUnicodeCharacter{2510}{+} - \DeclareUnicodeCharacter{2514}{+} - \DeclareUnicodeCharacter{2518}{+} - \DeclareUnicodeCharacter{253C}{+} - - \DeclareUnicodeCharacter{03B1}{\ensuremath{\alpha}} - \DeclareUnicodeCharacter{03B2}{\ensuremath{\beta}} - \DeclareUnicodeCharacter{03B3}{\ensuremath{\gamma}} - \DeclareUnicodeCharacter{0393}{\ensuremath{\Gamma}} - \DeclareUnicodeCharacter{03B4}{\ensuremath{\delta}} - \DeclareUnicodeCharacter{0394}{\ensuremath{\Delta}} - \DeclareUnicodeCharacter{03B5}{\ensuremath{\varepsilon}} - \DeclareUnicodeCharacter{03B6}{\ensuremath{\zeta}} - \DeclareUnicodeCharacter{03B7}{\ensuremath{\eta}} - \DeclareUnicodeCharacter{03B8}{\ensuremath{\vartheta}} - \DeclareUnicodeCharacter{0398}{\ensuremath{\Theta}} - \DeclareUnicodeCharacter{03BA}{\ensuremath{\kappa}} - \DeclareUnicodeCharacter{03BB}{\ensuremath{\lambda}} - \DeclareUnicodeCharacter{039B}{\ensuremath{\Lambda}} - \DeclareUnicodeCharacter{00B5}{\ensuremath{\mu}} % micron sign - \DeclareUnicodeCharacter{03BC}{\ensuremath{\mu}} - \DeclareUnicodeCharacter{03BD}{\ensuremath{\nu}} - \DeclareUnicodeCharacter{03BE}{\ensuremath{\xi}} - \DeclareUnicodeCharacter{039E}{\ensuremath{\Xi}} - \DeclareUnicodeCharacter{03B9}{\ensuremath{\iota}} - \DeclareUnicodeCharacter{03C0}{\ensuremath{\pi}} - \DeclareUnicodeCharacter{03A0}{\ensuremath{\Pi}} - \DeclareUnicodeCharacter{03C1}{\ensuremath{\rho}} - \DeclareUnicodeCharacter{03C3}{\ensuremath{\sigma}} - \DeclareUnicodeCharacter{03A3}{\ensuremath{\Sigma}} - \DeclareUnicodeCharacter{03C4}{\ensuremath{\tau}} - \DeclareUnicodeCharacter{03C6}{\ensuremath{\varphi}} - \DeclareUnicodeCharacter{03A6}{\ensuremath{\Phi}} - \DeclareUnicodeCharacter{03C7}{\ensuremath{\chi}} - \DeclareUnicodeCharacter{03C8}{\ensuremath{\psi}} - \DeclareUnicodeCharacter{03A8}{\ensuremath{\Psi}} - \DeclareUnicodeCharacter{03C9}{\ensuremath{\omega}} - \DeclareUnicodeCharacter{03A9}{\ensuremath{\Omega}} - \DeclareUnicodeCharacter{03C5}{\ensuremath{\upsilon}} - \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}} - \DeclareUnicodeCharacter{2113}{\ell} - - \DeclareUnicodeCharacter{2148}{\ensuremath{\id}} - \DeclareUnicodeCharacter{2202}{\ensuremath{\partial}} - \DeclareUnicodeCharacter{2205}{\ensuremath{\emptyset}} - \DeclareUnicodeCharacter{2208}{\ensuremath{\in}} - \DeclareUnicodeCharacter{2209}{\ensuremath{\notin}} - \DeclareUnicodeCharacter{2211}{\ensuremath{\sum}} - \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} - \DeclareUnicodeCharacter{221E}{\ensuremath{\infty}} - \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} - \DeclareUnicodeCharacter{2228}{\ensuremath{\vee}} - \DeclareUnicodeCharacter{2229}{\ensuremath{\cap}} - \DeclareUnicodeCharacter{222A}{\ensuremath{\cup}} - \DeclareUnicodeCharacter{222B}{\ensuremath{\int}} - \DeclareUnicodeCharacter{2248}{\ensuremath{\approx}} - \DeclareUnicodeCharacter{2260}{\ensuremath{\neq}} - \DeclareUnicodeCharacter{2264}{\ensuremath{\leq}} - \DeclareUnicodeCharacter{2265}{\ensuremath{\geq}} - \DeclareUnicodeCharacter{2293}{\ensuremath{\sqcap}} - \DeclareUnicodeCharacter{2294}{\ensuremath{\sqcup}} - \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} - \DeclareUnicodeCharacter{22C1}{\ensuremath{\bigvee}} - \DeclareUnicodeCharacter{22C2}{\ensuremath{\bigcap}} - \DeclareUnicodeCharacter{22C3}{\ensuremath{\bigcup}} - \DeclareUnicodeCharacter{2323}{\ensuremath{\smile}} % cup product - \DeclareUnicodeCharacter{00B1}{\ensuremath{\pm}} - \DeclareUnicodeCharacter{2A02}{\ensuremath{\bigotimes}} - \DeclareUnicodeCharacter{2295}{\ensuremath{\oplus}} - \DeclareUnicodeCharacter{2297}{\ensuremath{\otimes}} - \DeclareUnicodeCharacter{2A01}{\ensuremath{\oplus}} - \DeclareUnicodeCharacter{00BD}{\ensuremath{\nicefrac{1}{2}}} - \DeclareUnicodeCharacter{00D7}{\ensuremath{\times}} - \DeclareUnicodeCharacter{00B7}{\ensuremath{\cdot}} - \DeclareUnicodeCharacter{230A}{\ensuremath{\lfloor}} - \DeclareUnicodeCharacter{230B}{\ensuremath{\rfloor}} - \DeclareUnicodeCharacter{2308}{\ensuremath{\lceil}} - \DeclareUnicodeCharacter{2309}{\ensuremath{\rceil}} - \DeclareUnicodeCharacter{22C5}{\ensuremath{\cdot}} - \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} - \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} - \DeclareUnicodeCharacter{2192}{\ensuremath{\to}} - \DeclareUnicodeCharacter{21A6}{\ensuremath{\mapsto}} - \DeclareUnicodeCharacter{2102}{\ensuremath{\mathbb{C}}} - \DeclareUnicodeCharacter{211A}{\ensuremath{\mathbb{Q}}} - \DeclareUnicodeCharacter{211D}{\ensuremath{\mathbb{R}}} - \DeclareUnicodeCharacter{2124}{\ensuremath{\mathbb{Z}}} - \DeclareUnicodeCharacter{2202}{\ensuremath{\partial}} - - \DeclareUnicodeCharacter{2070}{\ensuremath{{}^0}} - \DeclareUnicodeCharacter{00B9}{\ensuremath{{}^1}} - \DeclareUnicodeCharacter{00B2}{\ensuremath{{}^2}} - \DeclareUnicodeCharacter{00B3}{\ensuremath{{}^3}} - \DeclareUnicodeCharacter{2074}{\ensuremath{{}^4}} - \DeclareUnicodeCharacter{2075}{\ensuremath{{}^5}} - \DeclareUnicodeCharacter{2076}{\ensuremath{{}^6}} - \DeclareUnicodeCharacter{2077}{\ensuremath{{}^7}} - \DeclareUnicodeCharacter{2078}{\ensuremath{{}^8}} - \DeclareUnicodeCharacter{2079}{\ensuremath{{}^9}} - \DeclareUnicodeCharacter{207A}{\ensuremath{{}^+}} - \DeclareUnicodeCharacter{207B}{\ensuremath{{}^-}} - \DeclareUnicodeCharacter{141F}{\ensuremath{{}^/}} - \DeclareUnicodeCharacter{2080}{\ensuremath{{}_0}} - \DeclareUnicodeCharacter{2081}{\ensuremath{{}_1}} - \DeclareUnicodeCharacter{2082}{\ensuremath{{}_2}} - \DeclareUnicodeCharacter{2083}{\ensuremath{{}_3}} - \DeclareUnicodeCharacter{2084}{\ensuremath{{}_4}} - \DeclareUnicodeCharacter{2085}{\ensuremath{{}_5}} - \DeclareUnicodeCharacter{2086}{\ensuremath{{}_6}} - \DeclareUnicodeCharacter{2087}{\ensuremath{{}_7}} - \DeclareUnicodeCharacter{2088}{\ensuremath{{}_8}} - \DeclareUnicodeCharacter{2089}{\ensuremath{{}_9}} - \DeclareUnicodeCharacter{208A}{\ensuremath{{}_+}} - \DeclareUnicodeCharacter{208B}{\ensuremath{{}_-}} - \DeclareUnicodeCharacter{1D62}{\ensuremath{{}_i}} - \DeclareUnicodeCharacter{2C7C}{\ensuremath{{}_j}} - - \newcommand{\sageMexSymbol}[1] - {{\fontencoding{OMX}\fontfamily{cmex}\selectfont\raisebox{0.75em}{\symbol{#1}}}} - \DeclareUnicodeCharacter{239B}{\sageMexSymbol{"30}} % parenlefttp - \DeclareUnicodeCharacter{239C}{\sageMexSymbol{"42}} % parenleftex - \DeclareUnicodeCharacter{239D}{\sageMexSymbol{"40}} % parenleftbt - \DeclareUnicodeCharacter{239E}{\sageMexSymbol{"31}} % parenrighttp - \DeclareUnicodeCharacter{239F}{\sageMexSymbol{"43}} % parenrightex - \DeclareUnicodeCharacter{23A0}{\sageMexSymbol{"41}} % parenrightbt - \DeclareUnicodeCharacter{23A1}{\sageMexSymbol{"32}} % bracketlefttp - \DeclareUnicodeCharacter{23A2}{\sageMexSymbol{"36}} % bracketleftex - \DeclareUnicodeCharacter{23A3}{\sageMexSymbol{"34}} % bracketleftbt - \DeclareUnicodeCharacter{23A4}{\sageMexSymbol{"33}} % bracketrighttp - \DeclareUnicodeCharacter{23A5}{\sageMexSymbol{"37}} % bracketrightex - \DeclareUnicodeCharacter{23A6}{\sageMexSymbol{"35}} % bracketrightbt - - \DeclareUnicodeCharacter{23A7}{\sageMexSymbol{"38}} % curly brace left top - \DeclareUnicodeCharacter{23A8}{\sageMexSymbol{"3C}} % curly brace left middle - \DeclareUnicodeCharacter{23A9}{\sageMexSymbol{"3A}} % curly brace left bottom - \DeclareUnicodeCharacter{23AA}{\sageMexSymbol{"3E}} % curly brace extension - \DeclareUnicodeCharacter{23AB}{\sageMexSymbol{"39}} % curly brace right top - \DeclareUnicodeCharacter{23AC}{\sageMexSymbol{"3D}} % curly brace right middle - \DeclareUnicodeCharacter{23AD}{\sageMexSymbol{"3B}} % curly brace right bottom - \DeclareUnicodeCharacter{23B0}{\{} % 2-line curly brace left top half (not in cmex) - \DeclareUnicodeCharacter{23B1}{\}} % 2-line curly brace right top half (not in cmex) - - \DeclareUnicodeCharacter{2320}{\ensuremath{\int}} % top half integral - \DeclareUnicodeCharacter{2321}{\ensuremath{\int}} % bottom half integral - \DeclareUnicodeCharacter{23AE}{\ensuremath{\|}} % integral extenison - - % Box drawings light - \DeclareUnicodeCharacter{2500}{-} % h - \DeclareUnicodeCharacter{2502}{|} % v - \DeclareUnicodeCharacter{250C}{+} % dr - \DeclareUnicodeCharacter{2510}{+} % dl - \DeclareUnicodeCharacter{2514}{+} % ur - \DeclareUnicodeCharacter{2518}{+} % ul - \DeclareUnicodeCharacter{251C}{+} % vr - \DeclareUnicodeCharacter{2524}{+} % vl - \DeclareUnicodeCharacter{252C}{+} % dh - \DeclareUnicodeCharacter{2534}{+} % uh - \DeclareUnicodeCharacter{253C}{+} % vh - \DeclareUnicodeCharacter{2571}{/} % upper right to lower left - \DeclareUnicodeCharacter{2572}{\ensuremath{\setminus}} % upper left to lower right - \DeclareUnicodeCharacter{2573}{X} % diagonal cross - - \DeclareUnicodeCharacter{25CF}{\ensuremath{\bullet}} % medium black circle - \DeclareUnicodeCharacter{26AC}{\ensuremath{\circ}} % medium small white circle - \DeclareUnicodeCharacter{256D}{+} - \DeclareUnicodeCharacter{256E}{+} - \DeclareUnicodeCharacter{256F}{+} - \DeclareUnicodeCharacter{2570}{+} -\fi - \let\textLaTeX\LaTeX \AtBeginDocument{\renewcommand*{\LaTeX}{\hbox{\textLaTeX}}} diff --git a/tox.ini b/tox.ini index 6c928aa2623..eac4cdce808 100644 --- a/tox.ini +++ b/tox.ini @@ -206,6 +206,7 @@ setenv = # # https://hub.docker.com/_/ubuntu?tab=description # as of 2023-05, latest=jammy=22.04, rolling=lunar=23.04, devel=mantic=23.10 + # ubuntu-focal does not have libgap-dev # ubuntu: SYSTEM=debian ubuntu: BASE_IMAGE=ubuntu @@ -221,6 +222,7 @@ setenv = ubuntu-bionic: BASE_TAG=bionic ubuntu-bionic: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-focal: BASE_TAG=focal + ubuntu-focal: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-jammy: BASE_TAG=jammy ubuntu-lunar: BASE_TAG=lunar ubuntu-mantic: BASE_TAG=mantic @@ -498,7 +500,6 @@ setenv = ### ### "local" envs ### - homebrew: SYSTEM=homebrew local: SHARED_CACHE_DIR={toxworkdir}/Caches local: SETENV=: local: SETENV_CONFIGURE=: @@ -507,6 +508,7 @@ setenv = local-sudo: __SUDO=--sudo local-root: CONFIG_CONFIGURE_ARGS_ROOT=--enable-build-as-root # brew caches downloaded files in ${HOME}/Library/Caches. We share it between different toxenvs. + homebrew: SYSTEM=homebrew local-homebrew: HOMEBREW={envdir}/homebrew local-{homebrew-usrlocal,nohomebrew}: HOMEBREW=/usr/local # local-macos-nohomebrew: "best effort" isolation to avoid using a homebrew installation in /usr/local @@ -519,6 +521,16 @@ setenv = local-{homebrew-nokegonly,nohomebrew}: BOOTSTRAP=ACLOCAL_PATH="$HOMEBREW/opt/gettext/share/aclocal:$ACLOCAL_PATH" PATH="$HOMEBREW/opt/gettext/bin/:$HOMEBREW/bin:$PATH" ./bootstrap local-homebrew-!nokegonly: SETENV=. .homebrew-build-env local-homebrew: IGNORE_MISSING_SYSTEM_PACKAGES=no + # macports + macports: SYSTEM=macports + local-macports: MP_PREFIX={envdir}/macports + local-macports-optlocal: MP_PREFIX=/opt/local + local-macports-optlocal: SUDO=sudo + local-macports-optlocal: __SUDO=--sudo + local-macports: PATH={env:MP_PREFIX}/bin:/usr/bin:/bin:/usr/sbin:/sbin + local-macports: CPATH={env:MP_PREFIX}/include + local-macports: LIBRARY_PATH={env:MP_PREFIX}/lib + local-macports: SETENV=eval $(build/bin/sage-print-system-package-command {env:SYSTEM} setup-build-env) # conda local-conda: CONDA_PREFIX={envdir}/conda local-conda: PATH={env:CONDA_PREFIX}/bin:/usr/bin:/bin:/usr/sbin:/sbin @@ -566,6 +578,8 @@ setenv = homebrew-{python3.9,python3.10,python3.11,python3.12}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python={env:HOMEBREW}/opt/python@{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # Installers from https://www.python.org/downloads/macos/ (must manually download and install) macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Frameworks/Python.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 + # MacPorts + macports-{python3.8,python3.9,python3.10,python3.11,python3.12}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python={env:MP_PREFIX}/bin/python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}-cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}/bin/python3 conda: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python3 @@ -611,7 +625,15 @@ setenv = macos-nohomebrew: FREETYPE_CONFIGURE=--without-harfbuzz macos-nohomebrew: PILLOW_BUILD_EXT=--disable-platform-guessing --disable-jpeg2000 --disable-imagequant --disable-tiff --disable-lcms --disable-webp --disable-webpmux --disable-xcb macos-nohomebrew: ZLIB_ROOT={env:MACOS_SDK}/usr - macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk + macports: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" + macports: CONFIGURED_CC=gcc -isysroot {env:MACOS_SDK} + macports: CONFIGURED_CXX=g++ -isysroot {env:MACOS_SDK} + macports-clang_13: CONFIGURED_CC=clang-mp-13 -isysroot {env:MACOS_SDK} + macports-clang_13: CONFIGURED_CXX=clang-mp-13 -isysroot {env:MACOS_SDK} + # + # macOS SDK/deployment target settings + # + {macos,macports}: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk # python3 from XCode 12 has MACOSX_DEPLOYMENT_TARGET=10.14.6. Selecting a lower target would cause /usr/bin/python3 to be rejected by configure. macos-10.14: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk macos-10.14: MACOSX_DEPLOYMENT_TARGET=10.14.6 @@ -686,6 +708,12 @@ commands = homebrew: bash -c 'if [ ! -x {env:HOMEBREW}/bin/brew ]; then mkdir -p {env:HOMEBREW} && cd {env:HOMEBREW} && curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 ; fi' homebrew: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) export PATH="build/bin:$PATH" && PACKAGES=$(sage-get-system-packages homebrew $(sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) {env:ALL_EXTRA_SAGE_PACKAGES}); {env:HOMEBREW}/bin/brew install $PACKAGES; {env:HOMEBREW}/bin/brew upgrade $PACKAGES;; esac' # + # local-macports + # + # https://guide.macports.org/#installing.macports.source.multiple + local-macports: bash -c 'if [ ! -x "{env:MP_PREFIX}/bin/port" ]; then (mkdir -p {envdir}/macports-src && cd {envdir}/macports-src && curl -L https://distfiles.macports.org/MacPorts/MacPorts-2.7.2.tar.gz | tar xz --strip 1 && ./configure --prefix="{env:MP_PREFIX}" --with-applications-dir=$MP_PREFIX/Applications --with-no-root-privileges --without-startupitems && make && {env:SUDO:} make install); fi' + local-macports: bash -c 'case "{env:SKIP_SYSTEM_PKG_INSTALL:}" in 1|y*|Y*);; *) {env:SUDO:} port selfupdate && {env:SUDO:} port upgrade outdated && PACKAGES=$(build/bin/sage-get-system-packages {env:SYSTEM} $(PATH=build/bin:$PATH build/bin/sage-package list {env:SAGE_PACKAGE_LIST_ARGS}) {env:ALL_EXTRA_SAGE_PACKAGES}); eval $(build/bin/sage-print-system-package-command {env:SYSTEM} {env:__SUDO:} --yes --no-install-recommends install $PACKAGES) || [ "$IGNORE_MISSING_SYSTEM_PACKAGES" = yes ] && echo "(ignoring errors)" ;; esac' + # # local-conda # # https://docs.anaconda.com/anaconda/install/silent-mode/