diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 451560ecd8c..82b14472c55 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -17,13 +17,6 @@ name: CI Linux incremental on: pull_request: - types: - # Defaults - - opened - - synchronize - - reopened - # When a CI label is added - - labeled paths: - 'build/pkgs/**' - 'pkgs/**' @@ -49,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - name: Get all packages that have changed id: changed-packages - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@v42 with: files_yaml: | configures: @@ -81,14 +74,6 @@ jobs: test: needs: [changed_files] - if: | - github.event_name != 'pull_request' || - ((github.event.action != 'labeled' && - (contains(github.event.pull_request.labels.*.name, 'c: packages: standard') || - contains(github.event.pull_request.labels.*.name, 'c: packages: optional'))) || - (github.event.action == 'labeled' && - (github.event.label.name == 'c: packages: optional' || - github.event.label.name == 'c: packages: standard'))) uses: ./.github/workflows/docker.yml with: # Build incrementally from published Docker image @@ -139,7 +124,7 @@ jobs: # with only a small number of test failures as of 10.2.rc0 tox_system_factors: >- ["gentoo-python3.11", - "archlinux"] + "archlinux-latest"] tox_packages_factors: >- ["standard-sitepackages"] docker_push_repository: ghcr.io/${{ github.repository }}/ diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9c9c033441f..1ee938339b3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -152,15 +152,16 @@ jobs: EXTRA_SAGE_PACKAGES: ${{ inputs.extra_sage_packages }} steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images - root-reserve-mb: 40000 + root-reserve-mb: 30000 remove-dotnet: true remove-android: true remove-haskell: true remove-codeql: true remove-docker-images: true + continue-on-error: true if: inputs.free_disk_space - name: Check out SageMath uses: actions/checkout@v4 diff --git a/.github/workflows/docker_hub.yml b/.github/workflows/docker_hub.yml index 694cd51762f..b1c5f42b77d 100644 --- a/.github/workflows/docker_hub.yml +++ b/.github/workflows/docker_hub.yml @@ -16,15 +16,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images - root-reserve-mb: 40000 + root-reserve-mb: 30000 remove-dotnet: true remove-android: true remove-haskell: true remove-codeql: true remove-docker-images: true + continue-on-error: true - name: Checkout uses: actions/checkout@v4 diff --git a/CITATION.cff b/CITATION.cff index 4ea68a60cc9..5cf526828d0 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.beta7 +version: 10.3.beta8 doi: 10.5281/zenodo.593563 -date-released: 2024-02-02 +date-released: 2024-02-13 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index 1e25d75be11..dc3b1773876 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.3.beta7, Release Date: 2024-02-02 +SageMath version 10.3.beta8, Release Date: 2024-02-13 diff --git a/build/bin/testcc.sh b/build/bin/testcc.sh deleted file mode 100755 index 05c8bf08fc3..00000000000 --- a/build/bin/testcc.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh -# Determine the type of C compiler, which can later -# be used to determine the flags the C compiler -# will want. This is done by testing what the -# C compiler's pre-processor defines. For example, -# gcc will always define __GNUC__ -# -# This will typically be called as (don't quote $CC): -# testcc.sh $CC - -# Copyright Dr. David Kirkby -# Released under the GPL version 2, or any later version -# the user wishes to use. -# Considerably cleaned up by Peter Jeremy. -# Further clean-up by Jeroen Demeyer (#12821). -# Helpful comments by William Stein. - -# Some of the compilers have been tested, though some have -# not, due to a lack of hardware or software. - -# Documentation on the compilers is taken from many source -# in particular, for the commercial Unix compilers: - -# HP-UX C and C++ compiler. -# http://docs.hp.com/en/7730/newhelp0610/preprocess.htm - -# IBM Compiler Reference - XL C/C++ for AIX, V10.1 -# http://www-01.ibm.com/support/docview.wss?uid=swg27012860&aid=1 - -# Using HP C++ for Tru64 UNIX and Linux Alpha -# http://h30097.www3.hp.com/cplus/ugu_impl.html#implem_chap - - -# Define a function to display usage information. -usage() -{ -cat <&2 -Usage: $0 name_or_path_to_the_C_compiler [optional arguments] - -Typically, this will be called as -$0 \$CC - - The script will print one of the following to indicate the C compiler - - GCC - For gcc or a gcc-like C compiler on any platform - Sun_Studio - For Sun Studio or earlier Sun C compiler - HP_on_Tru64 - For a C compiler produced by HP/Compaq for Tru64 - HP_on_HP-UX - For a C compiler produced by HP/Compaq for HP-UX - IBM_on_AIX - For a C compiler produced by IBM for AIX - HP_on_Alpha_Linux - For a C compiler produced by HP for Alpha linux - (This script has not been tested on Alpha Linux, - but is based on HP's documentation) - Unknown - If the C compiler type is unknown - -EOF -} - -# Exit if the user does not supply any command line arguments. -if [ $# -lt 1 ] ; then - usage - exit 2 -fi - -# Create a test file. It does not need to be a complete -# C file, as it is only pre-processed. So there is no -# need for a 'main' - -if [ -z "$SAGE_ROOT" ]; then - echo "The SAGE_ROOT environment variable must be set to" - echo "the root of the Sage installation" - exit 1 -fi - -mkdir -p "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to create the build directory." - exit 1 -fi - -cd "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to change into the build directory." - exit 1 -fi -TESTFILE=sage-testcc-$$.c - -cat >$TESTFILE <<"E*O*F" -#ifdef __GNUC__ -GCC -#define KNOWN_CC -#endif - -#ifdef __SUNPRO_C -Sun_Studio -#define KNOWN_CC -#endif - -/* - * I've not found any official documentation to suggest __DECC - * would be defined, but __DECCC is defined for C++, - * and a Google on __DECC shows many hits. I do not have - * easy access to a Tru64 system. - */ -#ifdef __digital__ -#ifdef __DECC -HP_on_Tru64 -#define KNOWN_CC -#endif -#endif - -/* Untested, as I have no access to a Alpha Linux system. */ -#ifdef __linux__ -#ifdef __DECCC -HP_on_Alpha_Linux -#define KNOWN_CC -#endif -#endif - -#ifdef __HP_cc -HP_on_HP-UX -#define KNOWN_CC -#endif - -#ifdef __xlC__ -IBM_on_AIX -#define KNOWN_CC -#endif - -#ifndef KNOWN_CC -Unknown -#endif -E*O*F - -"$@" -E $TESTFILE | sed -n '/^[A-Z]/{s/ //g;p;}' -rm -f $TESTFILE -exit 0 diff --git a/build/bin/testcxx.sh b/build/bin/testcxx.sh deleted file mode 100755 index 0046bca13ac..00000000000 --- a/build/bin/testcxx.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/sh -# Determine the type of C++ compiler, which can later -# be used to determine the flags the compiler -# will want. This is done by testing what the -# C++ compiler's pre-processor defines. For example, -# g++ will always define __GNUC__ (as does gcc). -# -# This will typically be called as (don't quote $CXX): -# testcxx.sh $CXX - -# Copyright Dr. David Kirkby -# Released under the GPL version 2, or any later version -# the user wishes to use. -# Considerably cleaned up by Peter Jeremy. -# Further clean-up by Jeroen Demeyer (#12821). -# Helpful comments by William Stein. - -# Some of the compilers have been tested, though some have -# not, due to a lack of hardware or software. - -# Documentation on the compilers is taken from many source -# in particular, for the commercial Unix compilers: - -# HP-UX C and C++ compiler. -# http://docs.hp.com/en/7730/newhelp0610/preprocess.htm - -# IBM Compiler Reference - XL C/C++ for AIX, V10.1 -# http://www-01.ibm.com/support/docview.wss?uid=swg27012860&aid=1 - -# Using HP C++ for Tru64 UNIX and Linux Alpha -# http://h30097.www3.hp.com/cplus/ugu_impl.html#implem_chap - - -# Define a function to display usage information. -usage() -{ -cat <&2 -Usage: $0 name_or_path_to_the_C++_compiler [optional arguments] - -Typically, this will be called as -$0 \$CXX - - The script will print one of the following to indicate the C++ compiler - - GCC - For g++ or a g++ like C++ compiler on any platform - Sun_Studio - For Sun Studio or earlier Sun C++ compiler - HP_on_Tru64 - For a C++ compiler produced by HP/Compaq for Tru64 - HP_on_HP-UX - For a C++ compiler produced by HP/Compaq for HP-UX - IBM_on_AIX - For a C++ compiler produced by IBM for AIX - HP_on_Alpha_Linux - For a C++ compiler produced by HP for Alpha linux - (This script has not been tested on Alpha Linux, - but is based on HP's documentation) - Unknown - If the C++ compiler type is unknown - -EOF -} - -# Exit if the user does not supply any command line arguments. -if [ $# -lt 1 ] ; then - usage - exit 2 -fi - -# Create a test file. It does not need to be a complete -# C++ file, as it is only pre-processed. So there is no -# need for a 'main' - -if [ -z "$SAGE_ROOT" ]; then - echo "The SAGE_ROOT environment variable must be set to" - echo "the root of the Sage installation" - exit 1 -fi - -mkdir -p "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to create the build directory." - exit 1 -fi - -cd "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to change into the build directory." - exit 1 -fi -TESTFILE=sage-testcxx-$$.cpp - -cat >$TESTFILE <<"E*O*F" -#ifdef __GNUC__ -GCC -#define KNOWN_CXX -#endif - -#ifdef __SUNPRO_CC -Sun_Studio -#define KNOWN_CXX -#endif - -#ifdef __digital__ -#ifdef __DECCXX -HP_on_Tru64 -#define KNOWN_CXX -#endif -#endif - -#ifdef __linux__ -#ifdef __DECCXX -HP_on_Alpha_Linux -#define KNOWN_CXX -#endif -#endif - -#ifdef __HP_aCC -HP_on_HP-UX -#define KNOWN_CXX -#endif - -#ifdef __xlC__ -IBM_on_AIX -#define KNOWN_CXX -#endif - -#ifndef KNOWN_CXX -Unknown -#endif -E*O*F - -"$@" -E $TESTFILE | sed -n '/^[A-Z]/{s/ //g;p;}' -rm -f $TESTFILE -exit 0 diff --git a/build/pkgs/_python3.12/distros/debian.txt b/build/pkgs/_python3.12/distros/debian.txt index 20ac50641bf..04a63409a04 100644 --- a/build/pkgs/_python3.12/distros/debian.txt +++ b/build/pkgs/_python3.12/distros/debian.txt @@ -1,4 +1,3 @@ python3.12 python3.12-dev -python3.12-distutils python3.12-venv diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 9e771da370e..753442bc6aa 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=84c8bfc8a84399d50eac5f0e75672da0e36216ae -md5=1171b0a7cd69002c426fb7b48e9026e4 -cksum=2998629640 +sha1=0c93ffbc40ee55c429b82c6070817c0133a05013 +md5=96e8d0c3aa48cab18b60499c4d303a73 +cksum=2523075193 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 14d9ba1a4be..67d54a29923 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -128d437fcc7f3917b314662fdee34aeaab968b67 +81a374dc4dacba71cf071f981d496ff63b2b2acb diff --git a/build/pkgs/database_knotinfo/checksums.ini b/build/pkgs/database_knotinfo/checksums.ini index fe398cb7354..6f174a26738 100644 --- a/build/pkgs/database_knotinfo/checksums.ini +++ b/build/pkgs/database_knotinfo/checksums.ini @@ -1,5 +1,5 @@ tarball=database_knotinfo-VERSION.tar.gz -sha1=1c6746a2742b5fb5c6a2803249dd6988d6ec1890 -md5=b2cd3295f179fbbde2a970ba7f0c3db4 -cksum=1079889953 +sha1=d32a5640c59c25d49ee72770aedfc6daac7c3d0e +md5=2d4104feee05547b542b1d86dd0f7675 +cksum=2620016485 upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz diff --git a/build/pkgs/database_knotinfo/package-version.txt b/build/pkgs/database_knotinfo/package-version.txt index 603f3a6d51a..72125ba3ec8 100644 --- a/build/pkgs/database_knotinfo/package-version.txt +++ b/build/pkgs/database_knotinfo/package-version.txt @@ -1 +1 @@ -2023.6.1 +2024.2.1 diff --git a/build/pkgs/fflas_ffpack/spkg-configure.m4 b/build/pkgs/fflas_ffpack/spkg-configure.m4 index 01a04713f07..646cfb527e4 100644 --- a/build/pkgs/fflas_ffpack/spkg-configure.m4 +++ b/build/pkgs/fflas_ffpack/spkg-configure.m4 @@ -4,7 +4,7 @@ SAGE_SPKG_CONFIGURE([fflas_ffpack], [ # the system fflas-ffpack, too. Use pkg-config to find a # recentish version, if there is one. PKG_CHECK_MODULES([FFLAS_FFPACK], - [fflas-ffpack >= 2.4.0], + [fflas-ffpack >= 2.4.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_fflas_ffpack=no], [sage_spkg_install_fflas_ffpack=yes]) ]) diff --git a/build/pkgs/free_fonts/distros/fedora.txt b/build/pkgs/free_fonts/distros/fedora.txt index b03a4525c08..435c13a9578 100644 --- a/build/pkgs/free_fonts/distros/fedora.txt +++ b/build/pkgs/free_fonts/distros/fedora.txt @@ -1 +1,3 @@ -gnu-free-fonts +gnu-free-mono-fonts +gnu-free-sans-fonts +gnu-free-serif-fonts diff --git a/build/pkgs/free_fonts/distros/nix.txt b/build/pkgs/free_fonts/distros/nix.txt new file mode 100644 index 00000000000..d5a8b479117 --- /dev/null +++ b/build/pkgs/free_fonts/distros/nix.txt @@ -0,0 +1 @@ +freefont-ttf diff --git a/build/pkgs/free_fonts/distros/openbsd.txt b/build/pkgs/free_fonts/distros/openbsd.txt new file mode 100644 index 00000000000..05a692834fe --- /dev/null +++ b/build/pkgs/free_fonts/distros/openbsd.txt @@ -0,0 +1 @@ +fonts/freefont-ttf diff --git a/build/pkgs/free_fonts/distros/repology.txt b/build/pkgs/free_fonts/distros/repology.txt index 0870af23017..30e8a613e7e 100644 --- a/build/pkgs/free_fonts/distros/repology.txt +++ b/build/pkgs/free_fonts/distros/repology.txt @@ -1 +1,4 @@ -gnu-freefont +font-freefont +fonts:gnu-freefont +texlive:gnu-freefont +# https://repology.org/project/freefonts/versions is something else. diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index d2841f740aa..0efcffc025e 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -93,10 +93,4 @@ SAGE_SPKG_CONFIGURE([gfortran], [ ]) ]) fi - AS_CASE([$host], - [*-*-cygwin*], [AS_VAR_IF([sage_spkg_install_gfortran], [yes], [ - AS_VAR_APPEND([SAGE_SPKG_ERRORS], [" - On Cygwin, gfortran must be installed as a system package. This is an error."]) - ]) - ]) ]) diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4 index 84c4af52087..36cfab37081 100644 --- a/build/pkgs/givaro/spkg-configure.m4 +++ b/build/pkgs/givaro/spkg-configure.m4 @@ -1,25 +1,6 @@ SAGE_SPKG_CONFIGURE([givaro], [ - m4_pushdef([SAGE_GIVARO_MINVER],["40101"]) - m4_pushdef([SAGE_GIVARO_LTVER],["40200"]) - SAGE_SPKG_DEPCHECK([gmp], [ - AC_PATH_PROG([GIVAROCONFIG], [givaro-config]) - AS_IF([test x$GIVAROCONFIG = x], [ - AC_MSG_NOTICE([givaro-config not found. Installing givaro]) - sage_spkg_install_givaro=yes], [ - AC_MSG_CHECKING([is givaro's version acceptable? ]) - givaro_ver=`$GIVAROCONFIG --decimal-version 2>> config.log` - AX_COMPARE_VERSION([$givaro_ver], [ge], SAGE_GIVARO_MINVER, [ - AX_COMPARE_VERSION([$givaro_ver], [lt], SAGE_GIVARO_LTVER, [ - AC_MSG_RESULT([yes])], [ - AC_MSG_RESULT([no, too new]) - sage_spkg_install_givaro=yes - ]) - ], [ - AC_MSG_RESULT([no, too old]) - sage_spkg_install_givaro=yes - ]) - ]) - ]) - m4_popdef([SAGE_GIVARO_LTVER]) - m4_popdef([SAGE_GIVARO_MINVER]) + PKG_CHECK_MODULES([GIVARO], + [givaro >= 4.1.1],dnl The version test is refined in linbox/spkg-configure.m4 + [sage_spkg_install_givaro=no], + [sage_spkg_install_givaro=yes]) ]) diff --git a/build/pkgs/gmp/spkg-install.in b/build/pkgs/gmp/spkg-install.in index 27ebbd88c6b..baacc5132b1 100644 --- a/build/pkgs/gmp/spkg-install.in +++ b/build/pkgs/gmp/spkg-install.in @@ -28,37 +28,6 @@ fi case "$UNAME" in - SunOS) - true;; # Auto-detect ABI - Darwin) - # In some cases (see SAGE_ROOT/spkg/bin/sage-env), on Darwin, - # CC might be set to clang, but GMP doesn't seem to build - # with clang. - CLANG=`command -v clang` - GCC=`command -v gcc` - if [ -n "$CC" ] && [ "$CC" = "$CLANG" ] && [ -n "$GCC" ] ; then - export CC="$GCC" - fi - # Do not set ABI=32 on MacOS X 10.6 (Darwin 10) and later, since - # there everything defaults to 64-bit: - if [ "`uname -r | sed 's/\..*//'`" -lt 10 ]; then - # Assume MacOS X 10.4 or 10.5 (Darwin 8 or 9); also, PPC CPUs - # are only supported by these, not later versions. - echo "Building a 32-bit version of GMP, which is the only supported option." - ABI=32 - case "`uname -m`" in - ppc|ppc64|[Pp]ower*) # Apple's 'uname' returns strange strings - # The Darwin assembler rejects code using an - # extended instruction set by default (cf. #8664): - required_cflags="$required_cflags -Wa,-force_cpusubtype_ALL" - ;; - esac - else - # Darwin 10 (MacOS X 10.6) or later. - # We don't have to set ABI here. - echo "Building a 64-bit version of GMP, which is the default." - fi - ;; # Darwin Linux) # GMP fails to build on 32-bit operating systems running on # 64-bit CPUs if CFLAGS happen to contain '-m32' and ABI is @@ -92,26 +61,8 @@ case "$UNAME" in rm -f foo foo.c fi ;; # Linux - *) # e.g. AIX or HP-UX - echo >&2 "Warning: Your platform ($UNAME) isn't yet explicitly supported" \ - "by this GMP spkg, i.e., by Sage's part of it." esac -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs. -# See #12765, #12751, and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496 -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ] ; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.0) - required_cflags="$required_cflags -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in GCC 4.7.0" - echo >&2 " on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - ;; - esac -fi - export ABI CFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe(r). # We don't export CPPFLAGS here, since we don't (have to) modify them. diff --git a/build/pkgs/lie/spkg-install.in b/build/pkgs/lie/spkg-install.in index 04f6b9372e6..bfaf9d5f17c 100644 --- a/build/pkgs/lie/spkg-install.in +++ b/build/pkgs/lie/spkg-install.in @@ -7,9 +7,6 @@ die () { cd src chmod -R a+rX . -if [ $UNAME = "SunOS" ]; then - sage-apply-patches -d solaris || die "Error patching LiE." -fi # Building LiE in parallel is broken export MAKE="$MAKE -j1" diff --git a/build/pkgs/linbox/spkg-configure.m4 b/build/pkgs/linbox/spkg-configure.m4 index 1571c9de790..8c5b85ef7fd 100644 --- a/build/pkgs/linbox/spkg-configure.m4 +++ b/build/pkgs/linbox/spkg-configure.m4 @@ -1,8 +1,16 @@ SAGE_SPKG_CONFIGURE([linbox], [ SAGE_SPKG_DEPCHECK([fflas_ffpack flint fplll givaro gmp iml m4ri m4rie mpfr ntl], [ - PKG_CHECK_MODULES([LINBOX], - [linbox >= 1.6.3 linbox <= 1.6.4], - [sage_spkg_install_linbox=no], - [sage_spkg_install_linbox=yes]) + PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching old versions + [linbox >= 1.6.3 linbox <= 1.6.4 fflas-ffpack >= 2.4.0 fflas-ffpack < 2.5.0 givaro >= 4.1.1 givaro < 4.2.0], + [sage_spkg_install_linbox=no], + [PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching new versions + [linbox >= 1.7.0 linbox <= 1.7.0 fflas-ffpack >= 2.5.0 givaro >= 4.2.0 givaro < 4.3.0], + [sage_spkg_install_linbox=no], + [sage_spkg_install_linbox=yes])]) ]) +], [dnl REQUIRED_CHECK +], [dnl PRE +], [dnl POST + sage_spkg_install_fflas_ffpack=$sage_spkg_install_linbox + sage_spkg_install_givaro=$sage_spkg_install_linbox ]) diff --git a/build/pkgs/m4ri/spkg-install.in b/build/pkgs/m4ri/spkg-install.in index 0af3234ed16..392265fc116 100644 --- a/build/pkgs/m4ri/spkg-install.in +++ b/build/pkgs/m4ri/spkg-install.in @@ -1,14 +1,5 @@ CFLAGS="$CFLAGS -I$SAGE_LOCAL/include -g" - -COMPILER=`testcc.sh $CC` - -if [ "$COMPILER" = "GCC" ] ; then - CFLAGS="$CFLAGS -fPIC -Wall -pedantic" -elif [ "$COMPILER" = "Sun_Studio" ] ; then - CFLAGS="$CFLAGS -Kpic" -elif [ "$COMPILER" = "HP_on_HP-UX" ] ; then - CFLAGS="$CFLAGS + z" -fi +CFLAGS="$CFLAGS -fPIC -Wall -pedantic" if [ "x$SAGE_DEBUG" = "xyes" ]; then ENABLE_DEBUG="--enable-debug" diff --git a/build/pkgs/m4rie/spkg-install.in b/build/pkgs/m4rie/spkg-install.in index bc0c13ffe1e..4d242bc52cf 100644 --- a/build/pkgs/m4rie/spkg-install.in +++ b/build/pkgs/m4rie/spkg-install.in @@ -5,16 +5,9 @@ ROOT_DIR="`pwd`" INCLUDES="-I$SAGE_LOCAL/include" LIBDIRS="-L$SAGE_LOCAL/lib" -CFLAGS="`testcflags.sh -fPIC -Wall -pedantic -g` $CFLAGS $INCLUDES" +CFLAGS="-fPIC -Wall -pedantic -g $CFLAGS $INCLUDES" LDFLAGS="$LIBDIRS $LDFLAGS" -COMPILER=`testcc.sh $CC` -if [ "$COMPILER" = "Sun_Studio" ] ; then - CFLAGS="$CFLAGS -Kpic" -elif [ "$COMPILER" = "HP_on_HP-UX" ] ; then - CFLAGS="$CFLAGS + z" -fi - CPPFLAGS="$INCLUDES" if [ "x$SAGE_DEBUG" = "xyes" ]; then diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 06affdacff8..94175da210b 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -1,143 +1,131 @@ -SAGE_SPKG_CONFIGURE([openblas], [ - dnl CHECK - SAGE_SPKG_DEPCHECK([gfortran], [ - SAVE_LIBS="$LIBS" - SAVE_CFLAGS="$CFLAGS" - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MAJOR], [0]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MINOR], [2]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MICRO], [20]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION], [SAGE_OPENBLAS_MIN_VERSION_MAJOR.SAGE_OPENBLAS_MIN_VERSION_MINOR.SAGE_OPENBLAS_MIN_VERSION_MICRO]) - dnl Reject openblas 0.3.22 - https://github.com/sagemath/sage/pull/35371 - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MAJOR], [0]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MINOR], [3]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MICRO], [99]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION], [SAGE_OPENBLAS_LT_VERSION_MAJOR.SAGE_OPENBLAS_LT_VERSION_MINOR.SAGE_OPENBLAS_LT_VERSION_MICRO]) - PKG_CHECK_MODULES([OPENBLAS], [openblas >= ]SAGE_OPENBLAS_MIN_VERSION [openblas < ]SAGE_OPENBLAS_LT_VERSION, [ - LIBS="$OPENBLAS_LIBS $LIBS" - CFLAGS="$OPENBLAS_CFLAGS $CFLAGS" - PKG_CHECK_VAR([OPENBLASPCDIR], [openblas], [pcfiledir], [ - sage_install_blas_pc=yes - AC_CHECK_FUNC([cblas_dgemm], [dnl openblas works as cblas - sage_install_cblas_pc=yes - ], [ - dnl openblas does not work as cblas; try to use system cblas as is - PKG_CHECK_MODULES([CBLAS], [cblas], [], [sage_spkg_install_openblas=yes]) - ]) - dnl Check all name manglings that AC_FC_FUNC could check based on the - dnl characteristics of the Fortran compiler - m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ - AC_CHECK_FUNC(dgeqrf_mangled, [ - AS_VAR_SET([HAVE_DGEQRF], [yes]) - ]) - ]) - AS_IF([test x$HAVE_DGEQRF = xyes], [dnl openblas works as lapack - sage_install_lapack_pc=yes - ], [ - dnl openblas does not work as lapack; try to use system lapack as is - PKG_CHECK_MODULES([LAPACK], [lapack], [], [sage_spkg_install_openblas=yes]) +SAGE_SPKG_CONFIGURE([openblas], [dnl CHECK + SAGE_SPKG_DEPCHECK([gfortran], [dnl + SAVE_LIBS="$LIBS" + SAVE_CFLAGS="$CFLAGS" + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MAJOR], [0]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MINOR], [2]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MICRO], [20]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION], [SAGE_OPENBLAS_MIN_VERSION_MAJOR.SAGE_OPENBLAS_MIN_VERSION_MINOR.SAGE_OPENBLAS_MIN_VERSION_MICRO]) + dnl Reject openblas 0.3.22 - https://github.com/sagemath/sage/pull/35371 + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MAJOR], [0]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MINOR], [3]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MICRO], [99]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION], [SAGE_OPENBLAS_LT_VERSION_MAJOR.SAGE_OPENBLAS_LT_VERSION_MINOR.SAGE_OPENBLAS_LT_VERSION_MICRO]) + PKG_CHECK_MODULES([OPENBLAS], [openblas >= ]SAGE_OPENBLAS_MIN_VERSION [openblas < ]SAGE_OPENBLAS_LT_VERSION, [dnl Have openblas.pc + LIBS="$OPENBLAS_LIBS $LIBS" + CFLAGS="$OPENBLAS_CFLAGS $CFLAGS" + PKG_CHECK_VAR([OPENBLASPCDIR], [openblas], [pcfiledir], [dnl + sage_install_blas_pc=yes + AC_CHECK_FUNC([cblas_dgemm], [dnl openblas works as cblas + sage_install_cblas_pc=yes + ], [dnl openblas does not work as cblas; try to use system cblas as is + PKG_CHECK_MODULES([CBLAS], [cblas], [], [sage_spkg_install_openblas=yes]) + ]) + dnl Check all name manglings that AC_FC_FUNC could check based on the + dnl characteristics of the Fortran compiler + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [dnl + AC_CHECK_FUNC(dgeqrf_mangled, [dnl + AS_VAR_SET([HAVE_DGEQRF], [yes]) ]) - ], [ - AC_MSG_WARN([Unable to locate the directory of openblas.pc. This should not happen!]) - sage_spkg_install_openblas=yes - ]) - AS_IF([test x$sage_spkg_install_openblas != xyes], [ - AC_MSG_CHECKING([the OpenBLAS version using openblas_get_config]) - AC_LANG_PUSH([C]) - AC_RUN_IFELSE([ - dnl Reject 0.3.22 - see https://github.com/sagemath/sage/pull/35377 - AC_LANG_PROGRAM([[#include - char *openblas_get_config(void); ]], - [[if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) - ], [ - AC_MSG_RESULT([good]) - ], [ - AC_MSG_RESULT([known bad version]) - sage_spkg_install_openblas=yes]) - AC_LANG_POP([C]) - ]) - AS_IF([test x$sage_spkg_install_openblas != xyes], [ + ]) + AS_IF([test x$HAVE_DGEQRF = xyes], [dnl openblas works as lapack + sage_install_lapack_pc=yes + ], [dnl openblas does not work as lapack; try to use system lapack as is + PKG_CHECK_MODULES([LAPACK], [lapack], [], [sage_spkg_install_openblas=yes]) + ]) + ], [dnl + AC_MSG_WARN([Unable to locate the directory of openblas.pc. This should not happen!]) + sage_spkg_install_openblas=yes + ]) + AS_IF([test x$sage_spkg_install_openblas != xyes], [dnl + AC_MSG_CHECKING([the OpenBLAS version using openblas_get_config]) + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([dnl Reject 0.3.22 - see https://github.com/sagemath/sage/pull/35377 + AC_LANG_PROGRAM([[#include + char *openblas_get_config(void); ]], + [[if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) + ], [dnl + AC_MSG_RESULT([good]) + ], [dnl + AC_MSG_RESULT([known bad version]) + sage_spkg_install_openblas=yes]) + AC_LANG_POP([C]) + ]) + AS_IF([test x$sage_spkg_install_openblas != xyes], [dnl AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], ["\$(LN) -sf \"$OPENBLASPCDIR/openblas.pc\" \"\$(@)\""]) - m4_foreach([blaslibnam], [blas, cblas, lapack], [ - AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [ - AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) - ]) + m4_foreach([blaslibnam], [blas, cblas, lapack], [dnl + AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [dnl + AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) + ]) + ]) + ]) + ], [dnl No openblas.pc + dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as + dnl part of openblas_get_config. We reject all older versions. + AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [dnl + AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [dnl + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_openblas_get_config "]) ]) - ]) - ], [ - dnl No openblas.pc - AS_CASE([$host], - [*-*-cygwin*], [dnl #29538 - workaround failing build of matplotlib etc. - AS_VAR_SET([HAVE_OPENBLAS], [no]) - AC_MSG_RESULT([$HAVE_OPENBLAS, test for OpenBLAS disabled on Cygwin]) - ], - [dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as - dnl part of openblas_get_config. We reject all older versions. - AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [ - AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [ - AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_openblas_get_config "]) - ]) - AC_MSG_CHECKING([whether openblas_get_config indicates version >= ]SAGE_OPENBLAS_MIN_VERSION) - AC_LANG_PUSH([C]) - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[#include - #include - char *openblas_get_config(void); - int version[3]; ]], - [[version[0] = version[1] = version[2] = 0; - /*printf("%s", openblas_get_config());*/ - if (sscanf(openblas_get_config(), "OpenBLAS %d.%d.%d", - version, version+1, version+2) < 1) - return 1; - if ( 10000 * version[0] - + 100 * version[1] - + version[2] - < 10000 * ]]SAGE_OPENBLAS_MIN_VERSION_MAJOR[[ - + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ - + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) - return 1; - if ( 10000 * version[0] - + 100 * version[1] - + version[2] - >=10000 * ]]SAGE_OPENBLAS_LT_VERSION_MAJOR[[ - + 100 * ]]SAGE_OPENBLAS_LT_VERSION_MINOR[[ - + ]]SAGE_OPENBLAS_LT_VERSION_MICRO[[) - return 1; - if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) - ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])], - [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) - AC_LANG_POP([C]) - AC_MSG_RESULT([$HAVE_OPENBLAS]) - ]) - ]) - AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [ + AC_MSG_CHECKING([whether openblas_get_config indicates version >= ]SAGE_OPENBLAS_MIN_VERSION) + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([dnl + AC_LANG_PROGRAM([[#include + #include + char *openblas_get_config(void); + int version[3]; ]], + [[version[0] = version[1] = version[2] = 0; + /*printf("%s", openblas_get_config());*/ + if (sscanf(openblas_get_config(), "OpenBLAS %d.%d.%d", + version, version+1, version+2) < 1) + return 1; + if ( 10000 * version[0] + + 100 * version[1] + + version[2] + < 10000 * ]]SAGE_OPENBLAS_MIN_VERSION_MAJOR[[ + + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ + + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) + return 1; + if ( 10000 * version[0] + + 100 * version[1] + + version[2] + >=10000 * ]]SAGE_OPENBLAS_LT_VERSION_MAJOR[[ + + 100 * ]]SAGE_OPENBLAS_LT_VERSION_MINOR[[ + + ]]SAGE_OPENBLAS_LT_VERSION_MICRO[[) + return 1; + if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) + ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], + [AS_VAR_SET([HAVE_OPENBLAS], [no])], + [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) + AC_LANG_POP([C]) + AC_MSG_RESULT([$HAVE_OPENBLAS]) + ]) + AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [dnl AS_VAR_SET([HAVE_CBLAS_DGEMM], [yes]) - AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [ + AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [dnl AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_cblas_dgemm "]) ]) ], [], [-lgfortran]) - m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ - AC_SEARCH_LIBS(dgeqrf_mangled, [openblas lapack], [ - AS_VAR_SET([HAVE_DGEQRF], [yes]) - AS_IF([test x"$ac_cv_search_]dgeqrf_mangled[" != x"none required"], [ - AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_]dgeqrf_mangled[ "]) - ]) - ], [], [-lgfortran]) + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [dnl + AC_SEARCH_LIBS(dgeqrf_mangled, [openblas lapack], [dnl + AS_VAR_SET([HAVE_DGEQRF], [yes]) + AS_IF([test x"$ac_cv_search_]dgeqrf_mangled[" != x"none required"], [dnl + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_]dgeqrf_mangled[ "]) + ]) + ], [], [-lgfortran]) ]) - AS_IF([test x"$HAVE_OPENBLAS" = xyes -a x"$HAVE_CBLAS_DGEMM" = xyes -a x"$HAVE_DGEQRF" = xyes], [ + AS_IF([test x"$HAVE_OPENBLAS" = xyes -a x"$HAVE_CBLAS_DGEMM" = xyes -a x"$HAVE_DGEQRF" = xyes], [dnl AC_SUBST([OPENBLAS_LIBS]) AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], [" (echo \"Name: openblas\"; echo \"Description: OpenBLAS\"; echo \"Version: 0.3\"; echo \"Libs: $OPENBLAS_LIBS\") > \"\$(@)\""]) - m4_foreach([blaslibnam], [openblas, blas, cblas, lapack], [ + m4_foreach([blaslibnam], [openblas, blas, cblas, lapack], [dnl AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) ]) - ], [ - dnl No system BLAS found + ], [dnl No system BLAS found sage_spkg_install_openblas=yes ]) ]) - LIBS="$SAVE_LIBS" - CFLAGS="$SAVE_CFLAGS" - ]) - ] -) + LIBS="$SAVE_LIBS" + CFLAGS="$SAVE_CFLAGS" + ]) +]) diff --git a/build/pkgs/openssl/spkg-configure.m4 b/build/pkgs/openssl/spkg-configure.m4 index 51dcb7de779..70270ea9296 100644 --- a/build/pkgs/openssl/spkg-configure.m4 +++ b/build/pkgs/openssl/spkg-configure.m4 @@ -48,12 +48,6 @@ SAGE_SPKG_CONFIGURE([openssl], [ ], [dnl No openssl found sage_spkg_install_openssl=yes ]) - AS_CASE([$host], - [*-*-cygwin*], [AS_VAR_IF([sage_spkg_install_openssl], [yes], [ - AS_VAR_APPEND([SAGE_SPKG_ERRORS], [" -On Cygwin, openssl must be installed as a system package. This is an error."]) - ]) - ]) ], [dnl REQUIRED-CHECK AC_REQUIRE([SAGE_SPKG_CONFIGURE_PYTHON3]) AC_REQUIRE([SAGE_SPKG_CONFIGURE_CURL]) diff --git a/build/pkgs/python3/SPKG.rst b/build/pkgs/python3/SPKG.rst index 6d9c7de71e7..55757b972e6 100644 --- a/build/pkgs/python3/SPKG.rst +++ b/build/pkgs/python3/SPKG.rst @@ -8,16 +8,15 @@ By default, Sage will try to use system's ``python3`` to set up a virtual environment, a.k.a. `venv `_ rather than building a Python 3 installation from scratch. -Sage will accept versions 3.9.x to 3.10.x. +Sage will accept versions 3.9.x to 3.12.x. You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use ``/path/to/python3_binary`` to set up the venv. Note that setting up the venv requires a number of Python modules to be available within the Python in question. Currently, -as of Sage 9.7, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, -``hashlib``, ``socket``, ``zlib``, ``distutils.core``, ``ssl`` - -they will be checked for by the ``configure`` script. +as of Sage 10.3, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, +``hashlib``, ``socket``, ``zlib``, ``ssl``, ``ensurepip``. -Use the ``configure`` option ``--without-system-python3`` in case you want Python 3 +Use the ``configure`` option ``--without-system-python3`` if you want Python 3 built from scratch. diff --git a/build/pkgs/python3/distros/alpine.txt b/build/pkgs/python3/distros/alpine.txt index 596ce366935..c4fe4764a15 100644 --- a/build/pkgs/python3/distros/alpine.txt +++ b/build/pkgs/python3/distros/alpine.txt @@ -1 +1,2 @@ python3-dev +py3-setuptools diff --git a/build/pkgs/python3/distros/debian.txt b/build/pkgs/python3/distros/debian.txt index 6b3ebf766dc..71f8637f37e 100644 --- a/build/pkgs/python3/distros/debian.txt +++ b/build/pkgs/python3/distros/debian.txt @@ -1,4 +1,4 @@ python3 libpython3-dev -python3-distutils +python3-setuptools python3-venv diff --git a/build/pkgs/python3/distros/fedora.txt b/build/pkgs/python3/distros/fedora.txt index 07358a92e89..ba7b5ea9b21 100644 --- a/build/pkgs/python3/distros/fedora.txt +++ b/build/pkgs/python3/distros/fedora.txt @@ -1 +1,2 @@ python3-devel +python-setuptools diff --git a/build/pkgs/python3/distros/freebsd.txt b/build/pkgs/python3/distros/freebsd.txt index e650785f225..d7bf687a0a3 100644 --- a/build/pkgs/python3/distros/freebsd.txt +++ b/build/pkgs/python3/distros/freebsd.txt @@ -1 +1,2 @@ lang/python +devel/py-setuptools diff --git a/build/pkgs/python3/distros/homebrew.txt b/build/pkgs/python3/distros/homebrew.txt index 1054bbddaff..3ae3509605d 100644 --- a/build/pkgs/python3/distros/homebrew.txt +++ b/build/pkgs/python3/distros/homebrew.txt @@ -1,2 +1,3 @@ # This installs /usr/local/bin/python3 -> python3.9 python3 +python-setuptools diff --git a/build/pkgs/python3/distros/macports.txt b/build/pkgs/python3/distros/macports.txt index 92826c681b4..60bf435e3ce 100644 --- a/build/pkgs/python3/distros/macports.txt +++ b/build/pkgs/python3/distros/macports.txt @@ -1 +1,2 @@ python310 +py-setuptools diff --git a/build/pkgs/python3/distros/opensuse.txt b/build/pkgs/python3/distros/opensuse.txt index 760ddd76f2c..de3b7dcc464 100644 --- a/build/pkgs/python3/distros/opensuse.txt +++ b/build/pkgs/python3/distros/opensuse.txt @@ -1 +1,2 @@ python3${PYTHON_MINOR}-devel +python3${PYTHON_MINOR}-setuptools diff --git a/build/pkgs/python3/distros/void.txt b/build/pkgs/python3/distros/void.txt index 07358a92e89..ab50f2d9b07 100644 --- a/build/pkgs/python3/distros/void.txt +++ b/build/pkgs/python3/distros/void.txt @@ -1 +1,2 @@ python3-devel +python3-setuptools diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 4cd421bf4b8..00d073802d4 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -48,12 +48,6 @@ if [ "$UNAME" = Darwin ]; then echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." CC=clang fi -elif [ "$UNAME" = SunOS ]; then - # Enable some C99 features on Solaris. This in particular enables - # the isinf() and isfinite() functions. It works both for C and - # C++ code (which is not true for -std=c99). See - # https://github.com/sagemath/sage/issues/14265 - export CFLAGS="-D__C99FEATURES__ $CFLAGS" fi # Use EXTRA_CFLAGS for user-defined CFLAGS since Python puts its own diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 9e3d51b2e98..bc5baadb94f 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -2,7 +2,7 @@ SAGE_SPKG_CONFIGURE([python3], [ m4_pushdef([MIN_VERSION], [3.9.0]) m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.9.0]) m4_pushdef([LT_STABLE_VERSION], [3.12.0]) - m4_pushdef([LT_VERSION], [3.12.0]) + m4_pushdef([LT_VERSION], [3.13.0]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python=PYTHON3], [Python 3 executable to use for the Sage venv; default: python3])]) @@ -24,8 +24,8 @@ SAGE_SPKG_CONFIGURE([python3], [ dnl Check if we can do venv with a system python3 dnl instead of building our own copy. dnl Trac #31160: We no longer check for readline here. - check_modules="sqlite3, ctypes, math, hashlib, socket, zlib, distutils.core, ssl, ensurepip" - AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ + check_modules="sqlite3, ctypes, math, hashlib, socket, zlib, ssl, ensurepip" + AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules and setuptools/distutils], [ac_cv_path_PYTHON3], [ AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python AC_MSG_RESULT([]) AC_PATH_PROG([ac_path_PYTHON3], [$ac_path_PYTHON3]) @@ -40,7 +40,7 @@ SAGE_SPKG_CONFIGURE([python3], [ ac_path_PYTHON3_found=: AC_MSG_RESULT([yes]) dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK - AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules]) + AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules and setuptools/distutils]) ]) AS_IF([test -z "$ac_cv_path_PYTHON3"], [ AC_MSG_ERROR([the python3 selected using --with-python=$with_python is not suitable]) diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 index b31f6bef2ec..0552d0c56ce 100644 --- a/build/pkgs/r/spkg-configure.m4 +++ b/build/pkgs/r/spkg-configure.m4 @@ -1,21 +1,14 @@ -SAGE_SPKG_CONFIGURE([r], [ - dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements - m4_pushdef([SAGE_R_MINVER],["3.5"]) - AS_CASE([$host], - [*-*-cygwin*], [ - dnl #29486: rpy2 2.8.x does not build against system R on cygwin. - sage_spkg_install_r=yes - ], [ - PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [ - AC_PATH_PROG([R], [R]) - AS_IF([test "x$R" = x], [ - AC_MSG_NOTICE([R is not found]) - sage_spkg_install_r=yes - ], [ - dnl TODO: check that versions of R and libR match - sage_spkg_install_r=no - ]) - ], [sage_spkg_install_r=yes]) +SAGE_SPKG_CONFIGURE([r], [dnl + dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements + m4_pushdef([SAGE_R_MINVER], ["3.5"]) + PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [dnl + AC_PATH_PROG([R], [R]) + AS_IF([test "x$R" = x], [dnl + AC_MSG_NOTICE([R is not found]) + sage_spkg_install_r=yes + ], [dnl TODO: check that versions of R and libR match + sage_spkg_install_r=no ]) - m4_popdef([SAGE_R_MINVER]) + ], [sage_spkg_install_r=yes]) + m4_popdef([SAGE_R_MINVER]) ]) diff --git a/build/pkgs/rubiks/spkg-install.in b/build/pkgs/rubiks/spkg-install.in index 893485ea0bd..5c882a0bfd5 100644 --- a/build/pkgs/rubiks/spkg-install.in +++ b/build/pkgs/rubiks/spkg-install.in @@ -36,11 +36,7 @@ echo " " # End of pretty general spkg-install file. # Now do the specific things needed for this package (rubiks) -if [ $UNAME = "SunOS" ]; then - INSTALL=cp; export INSTALL -else - INSTALL=install; export INSTALL -fi +INSTALL=install; export INSTALL if [ $UNAME = "Darwin" ]; then # #34293: Work around compiler hang diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 6e88cf9d1a0..0ec273543e2 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.3b7 +sage-conf ~= 10.3b8 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index db8cdd83e08..56e6e3f3495 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.3b7 +sage-docbuild ~= 10.3b8 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index e06c5137c37..f9408e28398 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.3b7 +sage-setup ~= 10.3b8 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 55539104399..9c0f33a7153 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.3b7 +sage-sws2rst ~= 10.3b8 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 9e9032a0594..86712dcb467 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.3b7 +sagemath-standard ~= 10.3b8 diff --git a/build/pkgs/sagemath_bliss/install-requires.txt b/build/pkgs/sagemath_bliss/install-requires.txt index d68ee5fa72e..8da9922be3a 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.3b7 +sagemath-bliss ~= 10.3b8 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 9e721757460..e3ee3bb17c6 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.3b7 +sagemath-categories ~= 10.3b8 diff --git a/build/pkgs/sagemath_coxeter3/install-requires.txt b/build/pkgs/sagemath_coxeter3/install-requires.txt index c24c8e93a3e..a41ca7949f5 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.3b7 +sagemath-coxeter3 ~= 10.3b8 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 6eef4df1e21..738c3d6f68c 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.3b7 +sagemath-environment ~= 10.3b8 diff --git a/build/pkgs/sagemath_mcqd/install-requires.txt b/build/pkgs/sagemath_mcqd/install-requires.txt index 120f5137fc8..fb6b95fb1bb 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.3b7 +sagemath-mcqd ~= 10.3b8 diff --git a/build/pkgs/sagemath_meataxe/install-requires.txt b/build/pkgs/sagemath_meataxe/install-requires.txt index af57d27f60e..12843b536a9 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.3b7 +sagemath-meataxe ~= 10.3b8 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index c90f5dfbded..8cbc66818b8 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.3b7 +sagemath-objects ~= 10.3b8 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index 1c63ba6a4e1..57ee651b95f 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.3b7 +sagemath-repl ~= 10.3b8 diff --git a/build/pkgs/sagemath_sirocco/install-requires.txt b/build/pkgs/sagemath_sirocco/install-requires.txt index 168177f8995..cdda9fc7b51 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.3b7 +sagemath-sirocco ~= 10.3b8 diff --git a/build/pkgs/sagemath_tdlib/install-requires.txt b/build/pkgs/sagemath_tdlib/install-requires.txt index baddf839ed1..74ad7694e5f 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.3b7 +sagemath-tdlib ~= 10.3b8 diff --git a/build/pkgs/scipy/install-requires.txt b/build/pkgs/scipy/install-requires.txt index dd975a870e3..8f07730e208 100644 --- a/build/pkgs/scipy/install-requires.txt +++ b/build/pkgs/scipy/install-requires.txt @@ -4,4 +4,4 @@ # deprecations cannot be introduced in micro releases. # SciPy devs wait "at least 6 months", "in practice two (minor) releases" # from deprecation to removal of a feature. -scipy >=1.5, <1.12 +scipy >=1.5 diff --git a/configure.ac b/configure.ac index ce6c5ea4a71..78a91e036c0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,14 @@ #***************************************************************************** # Copyright (C) 2005-2007 William Stein -# Copyright (C) 2009-2011 David Kirkby -# Copyright (C) 2012-2016 Jeroen Demeyer +# 2009-2011 David Kirkby +# 2012-2015 Volker Braun +# 2012-2019 Jeroen Demeyer +# 2014-2017 François Bissey +# 2016-2022 Matthias Koeppe +# 2017-2018 Erik M. Bray +# 2018 Dima Pasechnik +# 2020 Jonathan Kliem +# 2021-2023 Michael Orlitzky # # 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 @@ -57,13 +64,6 @@ dnl avoid involving libtool by using it to get the shared library extension. AC_LIB_RPATH AC_SUBST(SHLIBEXT, "${acl_shlibext}") -#--------------------------------------------------------- -# -# This is essentially the configure part of the old "install" file. -# Over time, this should be changed to proper autoconf style with -# configure options. -# - ######################################################################## # Set various environment variables (needed at configure time) ######################################################################## @@ -143,79 +143,16 @@ AC_CANONICAL_BUILD() AC_CANONICAL_HOST() case $host in -*-*-sunos*|*-*-solaris2.[1-9]) -AC_MSG_ERROR([[ -Sage is not supported on any version of Solaris earlier than 10. -Sage has been tested on the first release of Solaris 10 -(03/2005) and works on that. Sage may or may not work with -your version of Solaris. - -More information can be found about Sage on Solaris -on the Wiki at http://wiki.sagemath.org/solaris]]);; - -*-*-darwin[1-7].*) -AC_MSG_ERROR([[ -Sage has never been built on OS X 10.3 (Panther) -or earlier. The oldest version of OS X successfully used -is OS X version 10.4 (Tiger). You might consider updating -your version of OS X if your hardware permits this, but -Apple charges for upgrades of OS X]]);; - -*-*-hpux*) -AC_MSG_ERROR([[ -You are attempting to build Sage on HP's HP-UX operating system, -which is not a supported platform for Sage yet though -some work has been done on HP-UX. A port does not look to -be particularly difficult. Some information can be -found on the Sage Wiki at http://wiki.sagemath.org/HP-UX - -If you would like to help port Sage to HP-UX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-aix*) -AC_MSG_ERROR([[ -You are attempting to build Sage on IBM's AIX operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to AIX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-irix*) -AC_MSG_ERROR([[ -You are attempting to build Sage on SGI's IRIX operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to IRIX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-osf*) -AC_MSG_ERROR([[ -You are attempting to build Sage on HP's Tru64 operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to Tru64, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -# The following are all supported platforms. +dnl The following are all supported platforms. *-*-freebsd*);; *-*-linux*);; *-*-darwin*);; -*-*-solaris*);; -# Wildcard for other unsupported platforms +dnl Wildcard for unsupported platforms *) AC_MSG_ERROR([[ You are attempting to build Sage on $host, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to $host, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; +which is not a supported platform for Sage]]);; esac @@ -328,17 +265,6 @@ else AC_MSG_ERROR([You do not have a suitable version of Python installed]) fi -# Check for Latex, the use of which is less important in Sage than -# it used to be, as it was at one time required to build any documentation -# but this is no longer so. -AC_CHECK_PROG(found_latex, latex, yes, no) -if test x$found_latex != xyes -then - AC_MSG_NOTICE([You do not have 'latex', which is recommended, but not]) - AC_MSG_NOTICE([required. Latex is only really used for building pdf]) - AC_MSG_NOTICE([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) -fi - # Check that perl is available, with version 5.8.0 or later. # Some packages need perl, however it is not clear whether Sage really # requires version >= 5.8.0. The R package *used* to require it, but @@ -403,28 +329,14 @@ AC_CHECK_HEADER([complex.h],[],[ # First check for something that should be in any maths library (sqrt). AC_LANG(C++) AC_CHECK_LIB(m,sqrt,[],[ - AC_MSG_NOTICE([This system has no maths library installed.]) - # On AIX this is not installed by default - strange as that might seem. - # but is in a fileset bos.adt.libm. However, the fileset bos.adt - # includes other things that are probably useful. - if test "x`uname`" = 'xAIX' - then - AC_MSG_NOTICE([On AIX, libm is contained in the bos.adt.libm fileset.]) - AC_MSG_NOTICE([Actually, we recommend to install the complete bos.adt fileset.]) - AC_MSG_NOTICE([This needs to be performed by a system administrator.]) - fi AC_MSG_ERROR([Exiting, since a maths library was not found.]) ]) -# Check for system services - # Check that we are not building in a directory containing spaces AS_IF([echo "$ac_pwd" |grep " " >/dev/null], AC_MSG_ERROR([the path to the Sage root directory ($ac_pwd) contains a space. Sage will not build correctly in this case]) ) -SAGE_CHECK_OSX_SUPPORTED() - ############################################################################### # Collect substitutions for build/make/Makefile.in ############################################################################### @@ -567,7 +479,6 @@ AS_VAR_SET([sage_use_system_gcc], [force]) SAGE_SPKG_COLLECT() -dnl AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile]) AC_CONFIG_FILES([src/bin/sage-env-config src/bin/sage-src-env-config build/bin/sage-build-env-config]) diff --git a/m4/pyproject_toml_metadata.m4 b/m4/pyproject_toml_metadata.m4 index 2511fac45f7..139af7e43e4 100644 --- a/m4/pyproject_toml_metadata.m4 +++ b/m4/pyproject_toml_metadata.m4 @@ -17,4 +17,4 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", ] urls = {Homepage = "https://www.sagemath.org"} -requires-python = ">=3.9, <3.12" +requires-python = ">=3.9, <3.13" diff --git a/m4/sage_check_osx_supported.m4 b/m4/sage_check_osx_supported.m4 deleted file mode 100644 index 36984469c24..00000000000 --- a/m4/sage_check_osx_supported.m4 +++ /dev/null @@ -1,44 +0,0 @@ -AC_DEFUN([SAGE_CHECK_OSX_SUPPORTED], [ - AC_REQUIRE([AC_CANONICAL_HOST]) - AS_CASE([$host], - [*-apple-darwin*], [ - # Warning: xcodebuild does not seem to be maintained in Xcode 4.3 - # or later, so do not rely on the variable XCODE_VERS with OS X - # 10.7 or later. - changequote(<,>) - XCODE_VERS=`xcodebuild -version 2> /dev/null | grep Xcode | sed -e 's/[A-Za-z ]//g'` - changequote([,]) - if test -z $XCODE_VERS; then - XCODE_VERS="2" - fi - XCODE_VERS_MAJOR=`echo $XCODE_VERS | cut '-d.' -f1` - DARWIN_VERSION=`uname -r | cut '-d.' -f1` - if test $DARWIN_VERSION -gt 10; then - echo "You are using OS X Lion (or later)." - echo "You are strongly advised to install Apple's latest Xcode" - echo "unless you already have it. You can install this using" - echo "the App Store. Also, make sure you install Xcode's" - echo "Command Line Tools -- see Sage's README.txt." - elif test $XCODE_VERS_MAJOR -gt 2; then - echo "You are using Xcode version $XCODE_VERS." - echo "You are strongly advised to install Apple's latest Xcode" - echo "unless you already have it. You can download this from" - echo "http://developer.apple.com/downloads/." - echo "If using Xcode 4.3 or later, make sure you install Xcode's" - echo "Command Line Tools -- see Sage's README.txt." - else - echo "You are using Xcode version 1 or 2" - echo "WARNING: You are strongly advised to install the" - echo "latest version of Apple's Xcode for your platform," - echo "unless you already have it." - if test $DARWIN_VERSION -eq 10; then - echo "Probably you need Xcode 3.2.6" - elif test $DARWIN_VERSION -eq 9; then - echo "Probably you need Xcode 3.1.4" - elif test $DARWIN_VERSION -lt 9; then - echo "Probably you need Xcode 2.5" - fi - fi >& AS_MESSAGE_FD - - ]) -]) diff --git a/m4/sage_check_python_for_venv.m4 b/m4/sage_check_python_for_venv.m4 index da89e93a7ed..363f08c1138 100644 --- a/m4/sage_check_python_for_venv.m4 +++ b/m4/sage_check_python_for_venv.m4 @@ -16,11 +16,23 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ AX_COMPARE_VERSION([$python3_version], [ge], MIN_VERSION, [ AX_COMPARE_VERSION([$python3_version], [lt], LT_VERSION, [ dnl Because the system python is not used directly but rather in a venv without site-packages, - dnl we test whether the module will be available in a venv. + dnl we should test whether the module will be available in a venv. dnl Otherwise, some system site-package may be providing this module to the system python. + dnl However, on Python >= 3.12, we need setuptools to run our extension compilation tests + dnl because distutils has been removed from the standard library. + AX_COMPARE_VERSION([$python3_version], [ge], [3.12.0], [ + conftest_venv_options="--system-site-packages" + distutils_core="setuptools" + distutils_extension="setuptools.extension" + ], [ + conftest_venv_options= + distutils_core="distutils.core" + distutils_extension="distutils.extension" + ]) + all_required_modules="]REQUIRED_MODULES[, $distutils_core, $distutils_extension" dnl m4_define([conftest_venv], [config-venv]) .... for debugging only rm -rf conftest_venv - AS_IF(["]PYTHON_EXE[" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import ]REQUIRED_MODULES[" 2>& ]AS_MESSAGE_LOG_FD, [ + AS_IF(["]PYTHON_EXE[" build/bin/sage-venv $conftest_venv_options conftest_venv && conftest_venv/bin/python3 -c "import $all_required_modules" 2>& ]AS_MESSAGE_LOG_FD, [ AS_VAR_SET([python3_result], [yes]) SAGE_PYTHON_CHECK_DISTUTILS([CC="$CC" CXX="$CXX" conftest_venv/bin/python3], [ SAGE_ARCHFLAGS="unset" @@ -46,7 +58,7 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ AC_MSG_RESULT([$python3_result]) ]) ], [ - AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: ]REQUIRED_MODULES) + AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: $all_required_modules]) ]) ], [ AC_MSG_RESULT([no, $python3_version is too recent]) @@ -123,8 +135,9 @@ PyInit_spam(void) ]) AC_LANG_POP([C]) cat > conftest.py < conftest.py <`_. Be - sure to do the steps to install WSL2 and set it as default. - Make sure to allocate enough RAM to WSL: 5GB is known to be enough, - 2GB might not allow you to build some packages. - Then go to the Microsoft Store and install Ubuntu (or another - Linux distribution). Start Ubuntu from the start menu. - - On the Linux running on WSL, you always have root access, so you - can use any of the installation methods described below for - Linux. + Enable Windows Subsystem for Linux (WSL) by following the + `official WSL setup guide + `_. Be + sure to do the steps to install WSL2 and set it as default. + Make sure to allocate enough RAM to WSL: 5GB is known to be enough, + 2GB might not allow you to build some packages. + Then go to the Microsoft Store and install Ubuntu (or another + Linux distribution). Start Ubuntu from the start menu. + + On the Linux running on WSL, you always have root access, so you + can use any of the installation methods described below for + Linux. Linux ===== diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 7aacaef6439..26454e4d6d3 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -638,29 +638,18 @@ Most users won't need to set any of these: the build process just works on many platforms. (Note though that setting :envvar:`MAKE`, as described below, can significantly speed up the process.) -Building Sage involves building about 100 packages, each of which has its own +Building Sage involves building many packages, each of which has its own compilation instructions. -The Sage source tarball already includes the sources for all standard -packages, that is, it allows you to build Sage without internet -connection. The git repository, however, does not contain the source -code for third-party packages. Instead, it will be downloaded as -needed (Note: you can run ``make download`` to force downloading -packages before building). Package downloads use the Sage mirror -network, the nearest mirror will be determined automatically for -you. This is influenced by the following environment variable: -- :envvar:`SAGE_SERVER` - Try the specified mirror first, before - falling back to the official Sage mirror list. Note that Sage will - search the directory - - - ``SAGE_SERVER/spkg/upstream`` - - for upstream tarballs. +Standard environment controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here are some of the more commonly used variables affecting the build process: -- :envvar:`MAKE` - one useful setting for this variable when building Sage is +.. envvar:: MAKE + + One useful setting for this variable when building Sage is ``MAKE='make -jNUM'`` to tell the ``make`` program to run ``NUM`` jobs in parallel when building. Note that some Sage packages may not support this variable. @@ -676,14 +665,99 @@ Here are some of the more commonly used variables affecting the build process: and `Parallel building `_. - .. warning:: +.. envvar:: V + + If set to ``0``, silence the build. Instead of showing a detailed + compilation log, only one line of output is shown at the beginning + and at the end of the installation of each Sage package. To see + even less output, use:: + + $ make -s V=0 + + (Note that the above uses the syntax of setting a Makefile variable.) + +.. envvar:: CC + + While some programs allow you to use this to specify your C + compiler, **not every Sage package recognizes this**. + If GCC is installed within Sage, :envvar:`CC` is ignored and Sage's ``gcc`` + is used instead. - Some users on single-core macOS machines have reported problems when - building Sage with ``MAKE='make -jNUM'`` with ``NUM`` greater than one. +.. envvar:: CPP -- :envvar:`SAGE_NUM_THREADS` - if set to a number, then when rebuilding with - ``sage -b`` or parallel doctesting with ``sage -t -p 0``, use at most this - many threads. + Similarly, this will set the C preprocessor for some Sage + packages, and similarly, using it is likely quite risky. + If GCC is installed within Sage, :envvar:`CPP` is ignored and Sage's ``cpp`` + is used instead. + +.. envvar:: CXX + + Similarly, this will set the C++ compiler for some Sage + packages, and similarly, using it is likely quite risky. + If GCC is installed within Sage, :envvar:`CXX` is ignored and Sage's ``g++`` + is used instead. + +.. envvar:: FC + + Similarly, this will set the Fortran compiler. + This is supported by all Sage packages which have Fortran code. + However, for historical reasons, the value is hardcoded during the initial + ``make`` and subsequent changes to ``$FC`` might be ignored (in which case, + the original value will be used instead). + If GCC is installed within Sage, :envvar:`FC` is ignored and Sage's + ``gfortran`` is used instead. + +.. envvar:: CFLAGS +.. envvar:: CXXFLAGS +.. envvar:: FCFLAGS + + The flags for + the C compiler, the C++ compiler and the Fortran compiler, respectively. + The same comments apply to these: setting them may cause problems, because + they are not universally respected among the Sage packages. Note + also that ``export CFLAGS=""`` does not have the same effect as + ``unset CFLAGS``. The latter is preferable. + +.. envvar:: CPPFLAGS +.. envvar:: LDFLAGS +.. envvar:: CXXFLAG64 +.. envvar:: LDFLAG64 +.. envvar:: LD + + Similar comments apply to these compiler and linker flags. + + +Sage-specific environment variables controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. envvar:: SAGE_SERVER + + The Sage source tarball already includes the sources for all standard + packages, that is, it allows you to build Sage without internet + connection. The git repository, however, does not contain the source + code for third-party packages. Instead, it will be downloaded as + needed (note: you can run ``make download`` to force downloading + packages before building). + + If :envvar:`SAGE_SERVER` is set, the specified Sage mirror is contacted + first. Note that Sage will search the directory + ``SAGE_SERVER/spkg/upstream`` for upstream tarballs. + + If downloading a file from there fails or :envvar:`SAGE_SERVER` is not set, + files will be attempted to download from release assets of the + Sage GitHub repository. + + If that fails too, the Sage mirror network is contacted to determine + the nearest mirrors. + + This sequence of operations is defined by the files in the directory + :file:`$SAGE_ROOT/.upstream.d`. + +.. envvar:: SAGE_NUM_THREADS + + If set to a number, then when rebuilding with ``sage -b`` or + parallel doctesting with ``sage -t -p 0``, use at most this many + threads. If this is not set, then determine the number of threads using the value of the :envvar:`MAKE` (see above) or :envvar:`MAKEFLAGS` environment variables. @@ -697,23 +771,18 @@ Here are some of the more commonly used variables affecting the build process: When ``sage -t -p`` runs under the control of the GNU ``make`` jobserver, then Sage will request as most this number of job slots. -- :envvar:`V` - if set to ``0``, silence the build. Instead of - showing a detailed compilation log, only one line of output is shown - at the beginning and at the end of the installation of each Sage - package. To see even less output, use:: - - $ make -s V=0 - - (Note that the above uses the syntax of setting a Makefile variable.) +.. envvar:: SAGE_CHECK -- :envvar:`SAGE_CHECK` - if set to ``yes``, then during the build process, + If set to ``yes``, then during the build process, or when installing packages manually, run the test suite for each package which has one, and stop with an error if tests are failing. If set to ``warn``, then only a warning is printed in this case. See also :envvar:`SAGE_CHECK_PACKAGES`. -- :envvar:`SAGE_CHECK_PACKAGES` - if :envvar:`SAGE_CHECK` is set to ``yes``, +.. envvar:: SAGE_CHECK_PACKAGES + + If :envvar:`SAGE_CHECK` is set to ``yes``, then the default behavior is to run test suites for all spkgs which contain them. If :envvar:`SAGE_CHECK_PACKAGES` is set, it should be a comma-separated list @@ -730,9 +799,13 @@ Here are some of the more commonly used variables affecting the build process: on most platforms. So when this variable is empty or unset, Sage uses a default of ``!python2,!python3``. -- :envvar:`SAGE_INSTALL_GCC` - **Obsolete, do not use, to be removed** +.. envvar:: SAGE_INSTALL_GCC + + **Obsolete, do not use, to be removed** -- :envvar:`SAGE_INSTALL_CCACHE` - by default Sage doesn't install ccache, +.. envvar:: SAGE_INSTALL_CCACHE + + By default Sage doesn't install :ref:`ccache `, however by setting ``SAGE_INSTALL_CCACHE=yes`` Sage will install ccache. Because the Sage distribution is quite large, the maximum cache is set to 4G. This can be changed by running ``sage -sh -c "ccache --max-size=SIZE"``, @@ -744,10 +817,9 @@ Here are some of the more commonly used variables affecting the build process: building ccache for Sage, so that Sage can pull down the necessary sources. -- .. _sage_debug: +.. envvar:: SAGE_DEBUG - :envvar:`SAGE_DEBUG` - controls debugging support. - There are three different possible values: + Controls debugging support. There are three different possible values: * Not set (or set to anything else than "yes" or "no"): build binaries with debugging symbols, but no special debug builds. @@ -766,12 +838,16 @@ Here are some of the more commonly used variables affecting the build process: Instead of using :envvar:`SAGE_DEBUG` one can configure with ``--enable-debug={no|symbols|yes}``. -- :envvar:`SAGE_PROFILE` - controls profiling support. If this is set +.. envvar:: SAGE_PROFILE + + Controls profiling support. If this is set to ``yes``, profiling support is enabled where possible. Note that - Python-level profiling is always available; This option enables + Python-level profiling is always available; this option enables profiling in Cython modules. -- :envvar:`SAGE_BUILD_DIR` - the default behavior is to build each spkg in a +.. envvar:: SAGE_BUILD_DIR + + The default behavior is to build each spkg in a subdirectory of :file:`$SAGE_ROOT/local/var/tmp/sage/build/`; for example, build version 7.27.0 of :file:`ipython` in the directory @@ -794,7 +870,9 @@ Here are some of the more commonly used variables affecting the build process: permission to create. The path name must contain **no spaces**. -- :envvar:`SAGE_KEEP_BUILT_SPKGS` - the default behavior is to delete each +.. envvar:: SAGE_KEEP_BUILT_SPKGS + + The default behavior is to delete each build directory -- the appropriate subdirectory of :file:`$SAGE_ROOT/local/var/tmp/sage/build` or :file:`$SAGE_BUILD_DIR` -- after each spkg @@ -829,9 +907,9 @@ Here are some of the more commonly used variables affecting the build process: So you can set this variable to ``yes`` instead of using the ``-s`` flag for ``sage -i`` and ``sage -f``. -- .. _sage_fat_binary: +.. envvar:: SAGE_FAT_BINARY - :envvar:`SAGE_FAT_BINARY` - to build binaries that will run on the + To build binaries that will run on the widest range of target CPUs set this variable to ``yes`` before building Sage or configure with ``--enable-fat-binary``. This does not make the binaries relocatable, it only @@ -839,7 +917,9 @@ Here are some of the more commonly used variables affecting the build process: be moved to a different directory) binaries, you must use https://github.com/sagemath/binary-pkg -- :envvar:`SAGE_SUDO` - set this to ``sudo -E`` or to any other +.. envvar:: SAGE_SUDO + + Set this to ``sudo -E`` or to any other command prefix that is necessary to write into a installation hierarchy (:envvar:`SAGE_LOCAL`) owned by root or another user. Note that this command needs to preserve environment variable @@ -852,9 +932,13 @@ Here are some of the more commonly used variables affecting the build process: supports :envvar:`SAGE_SUDO`, into a root-owned installation hierarchy (:envvar:`SAGE_LOCAL`). -Environment variables for documentation build: -- :envvar:`SAGE_DOCBUILD_OPTS` - the value of this variable is passed as an +Environment variables controlling the documentation build +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. envvar:: SAGE_DOCBUILD_OPTS + + The value of this variable is passed as an argument to ``sage --docbuild all html`` or ``sage --docbuild all pdf`` when you run ``make``, ``make doc``, or ``make doc-pdf``. For example, you can add ``--no-plot`` to this variable to avoid building the graphics coming from @@ -862,18 +946,24 @@ Environment variables for documentation build: ``--include-tests-blocks`` to include all "TESTS" blocks in the reference manual. Run ``sage --docbuild help`` to see the full list of options. -- :envvar:`SAGE_SPKG_INSTALL_DOCS` - if set to ``yes``, then install +.. envvar:: SAGE_SPKG_INSTALL_DOCS + + If set to ``yes``, then install package-specific documentation to :file:`$SAGE_ROOT/local/share/doc/PACKAGE_NAME/` when an spkg is installed. This option may not be supported by all spkgs. Some spkgs might also assume that certain programs are available on the system (for example, ``latex`` or ``pdflatex``). -- :envvar:`SAGE_USE_CDNS` -- if set to ``yes``, then build the documentation +.. envvar:: SAGE_USE_CDNS + + If set to ``yes``, then build the documentation using CDNs (Content Distribution Networks) for scripts necessary for HTML documentation, such as `MathJax `_. -- :envvar:`SAGE_LIVE_DOC` -- if set to ``yes``, then build live Sage +.. envvar:: SAGE_LIVE_DOC + + If set to ``yes``, then build live Sage documentation. If the ``Make live`` button on any webpage of the live doc is clicked, every example code gets a `CodeMirror `_ code cell runnable via `Thebe `_. @@ -883,9 +973,10 @@ Environment variables for documentation build: local Jupyter server. The environment variable :envvar:`SAGE_JUPYTER_SERVER` is used for this purpose. - :envvar:`SAGE_JUPYTER_SERVER` - set this to either ``binder``, - ``binder:repo`` with ``repo`` specifying a Binder repo or the URL to a local - Jupyter server. +.. envvar:: SAGE_JUPYTER_SERVER + + Set this to either ``binder``, ``binder:repo`` with ``repo`` + specifying a Binder repo or the URL to a local Jupyter server. - ``binder`` refers to `Sage's official Binder repo `_. This is assumed if the @@ -913,84 +1004,72 @@ Environment variables for documentation build: before opening the Sage documentation webpage. -Environment variables dealing with specific Sage packages: +Environment variables dealing with specific Sage packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- :envvar:`SAGE_MATPLOTLIB_GUI` - if set to anything non-empty except ``no``, +.. envvar:: SAGE_MATPLOTLIB_GUI + + If set to anything non-empty except ``no``, then Sage will attempt to build the graphical backend when it builds the matplotlib package. -- :envvar:`PARI_CONFIGURE` - use this to pass extra parameters to +.. envvar:: OPENBLAS_CONFIGURE + + Adds additional configuration flags for + the OpenBLAS package that gets added to the ``make`` command. (see :trac:`23272`) + +.. envvar:: PARI_CONFIGURE + + Use this to pass extra parameters to PARI's ``Configure`` script, for example to specify graphics support (which is disabled by default). See the file :file:`build/pkgs/pari/spkg-install.in` for more information. -- :envvar:`SAGE_TUNE_PARI` - if yes, enable PARI self-tuning. Note that +.. envvar:: SAGE_TUNE_PARI + + If yes, enable PARI self-tuning. Note that this can be time-consuming. If you set this variable to "yes", you will also see this: ``WARNING: Tuning PARI/GP is unreliable. You may find your build of PARI fails, or PARI/GP does not work properly once built. We recommend to build this package with SAGE_CHECK="yes".`` -- :envvar:`PARI_MAKEFLAGS` - The value of this variable is passed as an - argument to the ``$MAKE`` command when compiling PARI. - -Some standard environment variables which are used by Sage: - -- :envvar:`CC` - while some programs allow you to use this to specify your C - compiler, **not every Sage package recognizes this**. - If GCC is installed within Sage, :envvar:`CC` is ignored and Sage's ``gcc`` - is used instead. +.. envvar:: PARI_MAKEFLAGS -- :envvar:`CPP` - similarly, this will set the C preprocessor for some Sage - packages, and similarly, using it is likely quite risky. - If GCC is installed within Sage, :envvar:`CPP` is ignored and Sage's ``cpp`` - is used instead. - -- :envvar:`CXX` - similarly, this will set the C++ compiler for some Sage - packages, and similarly, using it is likely quite risky. - If GCC is installed within Sage, :envvar:`CXX` is ignored and Sage's ``g++`` - is used instead. - -- :envvar:`FC` - similarly, this will set the Fortran compiler. - This is supported by all Sage packages which have Fortran code. - However, for historical reasons, the value is hardcoded during the initial - ``make`` and subsequent changes to ``$FC`` might be ignored (in which case, - the original value will be used instead). - If GCC is installed within Sage, :envvar:`FC` is ignored and Sage's - ``gfortran`` is used instead. - -- :envvar:`CFLAGS`, :envvar:`CXXFLAGS` and :envvar:`FCFLAGS` - the flags for - the C compiler, the C++ compiler and the Fortran compiler, respectively. - The same comments apply to these: setting them may cause problems, because - they are not universally respected among the Sage packages. Note - also that ``export CFLAGS=""`` does not have the same effect as - ``unset CFLAGS``. The latter is preferable. + The value of this variable is passed as an + argument to the ``$MAKE`` command when compiling PARI. -- Similar comments apply to other compiler and linker flags like - :envvar:`CPPFLAGS`, :envvar:`LDFLAGS`, :envvar:`CXXFLAG64`, - :envvar:`LDFLAG64`, and :envvar:`LD`. -- :envvar:`OPENBLAS_CONFIGURE` - adds additional configuration flags for - the OpenBLAS package that gets added to the make command. (see :trac:`23272`) +Environment variables dealing with doctesting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Environment variables dealing with doctesting: +.. envvar:: SAGE_TIMEOUT -- :envvar:`SAGE_TIMEOUT` - used for Sage's doctesting: the number of seconds + Used for Sage's doctesting: the number of seconds to allow a doctest before timing it out. If this isn't set, the default is 300 seconds (5 minutes). -- :envvar:`SAGE_TIMEOUT_LONG` - used for Sage's doctesting: the number of +.. envvar:: SAGE_TIMEOUT_LONG + + Used for Sage's doctesting: the number of seconds to allow a doctest before timing it out, if tests are run using ``sage -t --long``. If this isn't set, the default is 1800 seconds (30 minutes). -- :envvar:`SAGE_TEST_GLOBAL_ITER`, :envvar:`SAGE_TEST_ITER` - these can +.. envvar:: SAGE_TEST_GLOBAL_ITER +.. envvar:: SAGE_TEST_ITER + + These can be used instead of passing the flags ``--global-iterations`` and ``--file-iterations``, respectively, to ``sage -t``. Indeed, these variables are only used if the flags are unset. Run ``sage -t -h`` for more information on the effects of these flags (and therefore these variables). + +Environment variables set within Sage environments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Sage sets some other environment variables. The most accurate way to see what Sage does is to first run ``env`` from a shell prompt to see what environment variables you have set. Then run ``sage --sh -c diff --git a/src/doc/en/reference/matroids/index.rst b/src/doc/en/reference/matroids/index.rst index 4b854c47573..692d798d222 100644 --- a/src/doc/en/reference/matroids/index.rst +++ b/src/doc/en/reference/matroids/index.rst @@ -27,6 +27,7 @@ Concrete implementations :maxdepth: 1 sage/matroids/basis_matroid + sage/matroids/circuits_matroid sage/matroids/circuit_closures_matroid sage/matroids/linear_matroid sage/matroids/rank_matroid diff --git a/src/doc/en/reference/quivers/index.rst b/src/doc/en/reference/quivers/index.rst index 98bf561c4dc..3650919b338 100644 --- a/src/doc/en/reference/quivers/index.rst +++ b/src/doc/en/reference/quivers/index.rst @@ -6,6 +6,7 @@ Quivers sage/quivers/algebra sage/quivers/algebra_elements + sage/quivers/ar_quiver sage/quivers/homspace sage/quivers/morphism sage/quivers/path_semigroup diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 7921aea3bf5..e68bdbc2d57 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -508,6 +508,11 @@ REFERENCES: Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf +.. [BMFPR2011] \M. Bousquet-Melou, É. Fusy, L.-F. Préville-Ratelle, + *The number of intervals in the m-Tamari lattices*. + Electronic Journal of Combinatorics 18(2), 2011. + :doi:`10.37236/2027` + .. [BPPSST2017] Banik, Pandey, Peyrin, Sasaki, Sim, and Todo, GIFT : A Small Present Towards Reaching the Limit of Lightweight Encryption. *Cryptographic Hardware and Embedded Systems - CHES 2017*, @@ -1426,6 +1431,9 @@ REFERENCES: *Omitting parentheses from the cyclic notation*. (2013). :arxiv:`1308.0936v2`. +.. [CC2023] \C. Ceballos and C. Chenevière. + *On linear intervals in the alt `\nu`-Tamari lattices* :arxiv:`2305.02250` + .. [CCL2015] \N. Cohen, D. Coudert, and A. Lancin. *On computing the Gromov hyperbolicity*. ACM Journal of Experimental Algorithmics, 20(1.6):1-18, 2015. :doi:`10.1145/2780652` or @@ -3709,6 +3717,10 @@ REFERENCES: class number*, Habilitationsschrift, RWTH Aachen University, 2016. http://www.math.rwth-aachen.de/~Markus.Kirschmer/papers/herm.pdf +.. [KV2010] \M. Kirschmer, J. Voight: *Algorithmic enumeration of ideal classes + for quaternion orders*. SIAM J. Comput. 39(5): 1714-1747 (2010) + https://math.dartmouth.edu/~jvoight/articles/73446.pdf + .. [KB1983] \W. Kühnel and T. F. Banchoff, "The 9-vertex complex projective plane", Math. Intelligencer 5 (1983), no. 3, 11-22. diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 65b28fa0968..9a3cb8e8ee7 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -116,7 +116,8 @@ implemented in Sage as well: sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 9d666684313..41ec9264ae6 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -117,7 +117,8 @@ par ailleurs les catégories en tant que telles : sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst index fd125a81987..6aaf7aa2911 100644 --- a/src/doc/ja/tutorial/tour_coercion.rst +++ b/src/doc/ja/tutorial/tour_coercion.rst @@ -99,7 +99,8 @@ Sageのクラス階層と圏の階層構造にはそれなりに類似が見ら sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/pt/tutorial/tour_coercion.rst b/src/doc/pt/tutorial/tour_coercion.rst index 60589243901..b5eeaa85a9f 100644 --- a/src/doc/pt/tutorial/tour_coercion.rst +++ b/src/doc/pt/tutorial/tour_coercion.rst @@ -123,10 +123,10 @@ categorias matemáticas também são implementadas no Sage: sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces - sage: ZZ.category().is_subcategory(Rings()) True sage: ZZ in Rings() diff --git a/src/sage/algebras/algebra.py b/src/sage/algebras/algebra.py index e69ea7da530..a63aae4e217 100644 --- a/src/sage/algebras/algebra.py +++ b/src/sage/algebras/algebra.py @@ -18,9 +18,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.ring import Algebra from sage.categories.algebras import Algebras + def is_Algebra(x): r""" Return True if x is an Algebra. @@ -37,7 +37,4 @@ def is_Algebra(x): """ from sage.misc.superseded import deprecation deprecation(35253, "the function is_Algebra is deprecated; use '... in Algebras(base_ring)' instead") - try: - return isinstance(x, Algebra) or x in Algebras(x.base_ring()) - except Exception: - return False + return x in Algebras(x.base_ring()) diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 14be60e8f56..a98f5284eca 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -1,8 +1,7 @@ """ Algebras """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -14,10 +13,13 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.lazy_import import lazy_import +# old-style class for associative algebras, use Parent instead +from sage.rings.ring import Algebra + import sage.algebras.catalog as algebras from .quatalg.all import * @@ -28,11 +30,9 @@ from .lie_conformal_algebras.all import * # Algebra base classes -from .algebra import Algebra from .free_algebra import FreeAlgebra from .free_algebra_quotient import FreeAlgebraQuotient - from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra lazy_import('sage.algebras.group_algebra', 'GroupAlgebra') diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 3b9b8ec9db6..64ba03acf87 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -416,7 +416,8 @@ def __init__(self, Q, names, category=None): sage: Cl = CliffordAlgebra(Q) sage: Cl.category() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: TestSuite(Cl).run() TESTS: @@ -994,7 +995,8 @@ def lift_module_morphism(self, m, names=None): sage: phi = Cl.lift_module_morphism(m, 'abc') sage: phi.category_for() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 7 -3 -7 0] [ 0 1 -1 -1 0 0 0 -17] @@ -1077,7 +1079,8 @@ def lift_isometry(self, m, names=None): sage: phi = Cl.lift_isometry(m, 'abc') sage: phi.category_for() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 1 2 5 0] [ 0 1 1 2 0 0 0 5] diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index f8c91675c97..2ec097d8a82 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -163,11 +163,13 @@ from sage.misc.lazy_import import lazy_import from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.ring import Algebra +from sage.rings.integer_ring import ZZ from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.word import Word from sage.structure.category_object import normalize_names + lazy_import('sage.algebras.letterplace.free_algebra_letterplace', 'FreeAlgebra_letterplace') @@ -330,7 +332,11 @@ def create_key(self, base_ring, arg1=None, arg2=None, if arg2 is None: arg2 = len(arg1) names = normalize_names(arg2, arg1) - return base_ring, names + if degrees is None: + return base_ring, names + if degrees in ZZ: + return base_ring, names, (degrees,) * len(names) + return base_ring, names, tuple(degrees) def create_object(self, version, key): """ @@ -354,7 +360,9 @@ def create_object(self, version, key): if isinstance(key[0], tuple): from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace return FreeAlgebra_letterplace(key[1], degrees=key[0]) - return FreeAlgebra_generic(key[0], len(key[1]), key[1]) + if len(key) == 2: + return FreeAlgebra_generic(key[0], len(key[1]), key[1]) + return FreeAlgebra_generic(key[0], len(key[1]), key[1], key[2]) FreeAlgebra = FreeAlgebraFactory('FreeAlgebra') @@ -391,6 +399,9 @@ class FreeAlgebra_generic(CombinatorialFreeModule, Algebra): - ``R`` -- a ring - ``n`` -- an integer - ``names`` -- the generator names + - ``degrees`` -- (optional) a tuple or list specifying the + degrees of all the generators, if omitted, the algebra is not + graded EXAMPLES:: @@ -445,7 +456,7 @@ class FreeAlgebra_generic(CombinatorialFreeModule, Algebra): """ Element = FreeAlgebraElement - def __init__(self, R, n, names): + def __init__(self, R, n, names, degrees=None): """ The free algebra on `n` generators over a base ring. @@ -468,9 +479,18 @@ def __init__(self, R, n, names): self.__ngens = n indices = FreeMonoid(n, names=names) cat = AlgebrasWithBasis(R) + if degrees is not None: + if len(degrees) != len(names) or not all(d in ZZ for d in degrees): + raise ValueError("argument degrees must specify an integer for each generator") + cat = cat.Graded() + CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) self._assign_names(indices.variable_names()) + if degrees is None: + self._degrees = None + else: + self._degrees = {g: ZZ(d) for g, d in zip(self.monoid().gens(), degrees)} def one_basis(self): """ @@ -527,17 +547,23 @@ def _repr_(self) -> str: EXAMPLES:: - sage: F = FreeAlgebra(QQ,3,'x') - sage: F # indirect doctest + sage: F = FreeAlgebra(QQ, 3, 'x') + sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<>') - sage: F #indirect doctest + sage: F # indirect doctest QQ<> - sage: FreeAlgebra(ZZ,1,['a']) + sage: FreeAlgebra(ZZ, 1, ['a']) Free Algebra on 1 generators (a,) over Integer Ring + + sage: FreeAlgebra(QQ, 2, ['x', 'y'], degrees=(2,1)) + Free Algebra on 2 generators (x, y) with degrees (2, 1) over Rational Field """ - return "Free Algebra on {} generators {} over {}".format( - self.__ngens, self.gens(), self.base_ring()) + if self._degrees is None: + return "Free Algebra on {} generators {} over {}".format( + self.__ngens, self.gens(), self.base_ring()) + return "Free Algebra on {} generators {} with degrees {} over {}".format( + self.__ngens, self.gens(), tuple(self._degrees.values()), self.base_ring()) def _latex_(self) -> str: r""" @@ -774,6 +800,23 @@ def gens(self): """ return tuple(self.gen(i) for i in range(self.__ngens)) + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: A. = FreeAlgebra(QQ, degrees=(1, -1)) + sage: m = A.basis().keys()[42] + sage: m + a*b*a*b^2 + sage: A.degree_on_basis(m) + -1 + sage: (a*b*a*b^2).degree() + -1 + """ + return ZZ.sum(self._degrees[g] * e for g, e in m) + def product_on_basis(self, x, y): """ Return the product of the basis elements indexed by ``x`` and ``y``. diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index 1854414e2d0..b2e0df0e15b 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -203,6 +203,56 @@ def _mul_(self, y): del z_elt[key] return A._from_dict(z_elt) + def is_unit(self): + r""" + Return ``True`` if ``self`` is invertible. + + EXAMPLES:: + + sage: A. = FreeAlgebra(ZZ) + sage: A(-1).is_unit() + True + sage: A(2).is_unit() + False + sage: A(1 + x).is_unit() + False + sage: A. = FreeAlgebra(QQ, degrees=(1,-1)) + sage: A(x * y).is_unit() + False + sage: A(2).is_unit() + True + """ + mc = self._monomial_coefficients + if not mc or len(mc) > 1: + return False + m, c = next(iter(mc.items())) + return m.is_one() and c.is_unit() + + def __invert__(self): + """ + EXAMPLES:: + + sage: A. = FreeAlgebra(QQ) + sage: ~A(1) + 1 + + TESTS:: + + sage: ~A(0) + Traceback (most recent call last): + ... + ArithmeticError: element is not invertible + + sage: ~A(1 + x) + Traceback (most recent call last): + ... + ArithmeticError: element is not invertible + """ + if self.is_unit(): + m, c = next(iter(self._monomial_coefficients.items())) + return type(self)(self.parent(), {m: c.inverse_of_unit()}) + raise ArithmeticError("element is not invertible") + def _acted_upon_(self, scalar, self_on_left=False): """ Return the action of a scalar on ``self``. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index f45399ae288..e48e2f83775 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -11,6 +11,9 @@ - Peter Bruin (2021): do not require the base ring to be a field +- Lorenz Panny (2022): :meth:`QuaternionOrder.isomorphism_to`, + :meth:`QuaternionFractionalIdeal_rational.minimal_element` + This code is partly based on Sage code by David Kohel from 2005. TESTS: @@ -63,6 +66,7 @@ from sage.structure.factory import UniqueFactory from sage.modules.free_module import FreeModule from sage.modules.free_module_element import vector +from sage.quadratic_forms.quadratic_form import QuadraticForm from operator import itemgetter @@ -2081,13 +2085,177 @@ def ternary_quadratic_form(self, include_basis=False): G = tr0.intersection(S) B = [Q(a) for a in G.basis()] m = matrix(QQ, [[x.pair(y) for x in B] for y in B]) - from sage.quadratic_forms.quadratic_form import QuadraticForm Q = QuadraticForm(m) if include_basis: return Q, B else: return Q + def isomorphism_to(self, other, *, conjugator=False): + r""" + Compute an isomorphism from this quaternion order `O` + to another order `O'` in the same quaternion algebra. + + If the optional keyword argument ``conjugator`` is set + to ``True``, this method returns a single quaternion + `\gamma \in O \cap O'` of minimal norm such that + `O' = \gamma^{-1} O \gamma`, + rather than the ring isomorphism it defines. + + .. NOTE:: + + This method is currently only implemented for maximal orders in + definite quaternion orders over `\QQ`. For a general algorithm, + see [KV2010]_ (Problem ``IsConjugate``). + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-1, -19) + sage: O0 = Quat.quaternion_order([1, i, (i+j)/2, (1+k)/2]) + sage: O1 = Quat.quaternion_order([1, 667*i, 1/2+j/2+9*i, (222075/2*i+333*j+k/2)/667]) + sage: iso = O0.isomorphism_to(O1) + sage: iso + Ring morphism: + From: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, i, 1/2*i + 1/2*j, 1/2 + 1/2*k) + To: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, 667*i, 1/2 + 9*i + 1/2*j, 222075/1334*i + 333/667*j + 1/1334*k) + Defn: i |--> 629/667*i + 36/667*j - 36/667*k + j |--> 684/667*i - 648/667*j - 19/667*k + k |--> -684/667*i - 19/667*j - 648/667*k + sage: iso(1) + 1 + sage: iso(i) + 629/667*i + 36/667*j - 36/667*k + sage: iso(i/3) + Traceback (most recent call last): + ... + TypeError: 1/3*i fails to convert into the map's domain ... + + :: + + sage: gamma = O0.isomorphism_to(O1, conjugator=True); gamma + -36*i - j + k + sage: gamma in O0 + True + sage: gamma in O1 + True + sage: O1.unit_ideal() == ~gamma * O0 * gamma + True + + TESTS: + + Some random testing:: + + sage: q = randrange(1,1000) + sage: p = randrange(1,1000) + sage: Quat. = QuaternionAlgebra(-q, -p) + sage: O0 = Quat.maximal_order() + sage: while True: + ....: b = Quat.random_element() + ....: if gcd(b.reduced_norm(), Quat.discriminant()) == 1: + ....: break + sage: O1 = (b * O0).left_order() + sage: iso = O0.isomorphism_to(O1); iso + Ring morphism: ... + sage: iso.domain() == O0 + True + sage: iso.codomain() == O1 + True + sage: iso(O0.random_element()) in O1 + True + sage: iso(1) == 1 + True + sage: els = list(O0.basis()) + sage: els += [O0.random_element() for _ in range(5)] + sage: {iso(g).reduced_norm() == g.reduced_norm() for g in els} + {True} + sage: {iso(g).reduced_trace() == g.reduced_trace() for g in els} + {True} + sage: {iso(g * h) == iso(g) * iso(h) for g in els for h in els} + {True} + + Test error cases:: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O = Quat.maximal_order() + sage: O.isomorphism_to('potato') + Traceback (most recent call last): + ... + TypeError: not a quaternion order + + :: + + sage: Quat1. = QuaternionAlgebra(-1,-11) + sage: Quat2. = QuaternionAlgebra(-3,-11) + sage: Quat1.discriminant() == Quat2.discriminant() # isomorphic + True + sage: O1 = Quat1.maximal_order() + sage: O2 = Quat2.maximal_order() + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + TypeError: not an order in the same quaternion algebra + + :: + + sage: Quat. = QuaternionAlgebra(7,11) + sage: O1 = Quat.maximal_order() + sage: O2 = (O1 * (i+j)).right_order() + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + NotImplementedError: only implemented for definite quaternion orders + + :: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O1 = Quat.quaternion_order([1, i, j, k]) + sage: O2 = Quat.quaternion_order([1,-i, j,-k]) + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + NotImplementedError: only implemented for maximal orders + + :: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O1 = Quat.quaternion_order([1, i, (i+j)/2, (1+k)/2]) + sage: O2 = Quat.quaternion_order([1, (2+i+k)/4, (-11*i+2*j+k)/4, (-5*i+k)/3]) + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + ValueError: quaternion orders not isomorphic + + ALGORITHM: + + Find a generator of the principal lattice `N\cdot O\cdot O'` + where `N = [O : O cap O']` using + :meth:`QuaternionFractionalIdeal_rational.minimal_element()`. + An isomorphism is given by conjugation by such an element. + """ + if not isinstance(other, QuaternionOrder): + raise TypeError('not a quaternion order') + Q = self.quaternion_algebra() + if other.quaternion_algebra() != Q: + raise TypeError('not an order in the same quaternion algebra') + + if not self.quadratic_form().is_positive_definite(): + raise NotImplementedError('only implemented for definite quaternion orders') + if not (self.discriminant() == Q.discriminant() == other.discriminant()): + raise NotImplementedError('only implemented for maximal orders') + + N = self.intersection(other).free_module().index_in(self.free_module()) + I = N * self * other + gamma = I.minimal_element() + if self*gamma != I: + raise ValueError('quaternion orders not isomorphic') + assert gamma*other == I + + if conjugator: + return gamma + + ims = [~gamma * gen * gamma for gen in Q.gens()] + return self.hom(ims, other, check=False) + class QuaternionFractionalIdeal(Ideal_fractional): pass @@ -2417,8 +2585,35 @@ def _richcmp_(self, right, op): sage: I != I # indirect doctest False + + TESTS:: + + sage: B = QuaternionAlgebra(QQ,-1,-11) + sage: i,j,k = B.gens() + sage: I = B.ideal([1,i,j,i*j]) + sage: I == I + True + sage: O = B.ideal([1,i,(i+j)/2,(1+i*j)/2]) + sage: I <= O + True + sage: I >= O + False + sage: I != O + True + sage: I == O + False + sage: I != I + False + sage: I < I + False + sage: I < O + True + sage: I <= I + True + sage: O >= O + True """ - return self.basis_matrix()._richcmp_(right.basis_matrix(), op) + return self.free_module().__richcmp__(right.free_module(), op) def __hash__(self): """ @@ -2519,7 +2714,6 @@ def quadratic_form(self): sage: I.theta_series(10) 1 + 12*q^2 + 12*q^3 + 12*q^4 + 12*q^5 + 24*q^6 + 24*q^7 + 36*q^8 + 36*q^9 + O(q^10) """ - from sage.quadratic_forms.quadratic_form import QuadraticForm # first get the gram matrix gram_matrix = self.gram_matrix() # rescale so that there are no denominators @@ -2531,6 +2725,35 @@ def quadratic_form(self): # now get the quadratic form return QuadraticForm(gram_matrix) + def minimal_element(self): + r""" + Return an element in this quaternion ideal of minimal norm. + + If the ideal is a principal lattice, this method can be used + to find a generator; see [Piz1980]_, Corollary 1.20. + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-3,-101) + sage: O = Quat.maximal_order(); O + Order of Quaternion Algebra (-3, -101) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) + sage: (O * 5).minimal_element() + 5/2 + 5/2*i + sage: alpha = 1/2 + 1/6*i + j + 55/3*k + sage: I = O*141 + O*alpha; I.norm() + 141 + sage: el = I.minimal_element(); el + 13/2 - 7/6*i + j + 2/3*k + sage: el.reduced_norm() + 282 + """ + qf = self.quadratic_form() + if not qf.is_positive_definite(): + raise ValueError('quaternion algebra must be definite') + pariqf = qf.__pari__() + _,v = pariqf.qfminim(None, None, 1) + return sum(ZZ(c)*g for c,g in zip(v, self.basis())) + def theta_series(self, B, var='q'): r""" Return normalized theta series of ``self``, as a power series over diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 73e88f40d67..bebe00e26e5 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -684,6 +684,30 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): """ return (self.conjugate() * right).reduced_trace() + def _im_gens_(self, codomain, im_gens, base_map): + r""" + Return the image of this quaternion under the morphism + defined by ``im_gens`` in ``codomain``, where elements + of the base ring are mapped by ``base_map``. + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-1, -19) + sage: b = 5 + 6*i + 7*j + 8*k + sage: ims = [~b * g * b for g in Quat.gens()] + sage: Quat(1)._im_gens_(Quat, ims, None) == 1 + True + sage: i._im_gens_(Quat, ims, None) == ims[0] + True + sage: j._im_gens_(Quat, ims, None) == ims[1] + True + sage: k._im_gens_(Quat, ims, None) == ims[2] + True + """ + if base_map is None: + base_map = lambda v: v + return sum(base_map(c)*g for c,g in zip(self, [1] + list(im_gens))) + cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): """ diff --git a/src/sage/categories/algebras.py b/src/sage/categories/algebras.py index d93a51d28bd..5f7dcaff05b 100644 --- a/src/sage/categories/algebras.py +++ b/src/sage/categories/algebras.py @@ -73,10 +73,7 @@ def __contains__(self, x): sage: QQ['x'] in Algebras(CDF) # needs sage.rings.complex_double False """ - if super().__contains__(x): - return True - from sage.rings.ring import Algebra - return isinstance(x, Algebra) and x.base_ring() == self.base_ring() + return super().__contains__(x) # def extra_super_categories(self): # """ diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index ca2cb563f88..a344ed2a296 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -81,7 +81,7 @@ RingModules = Modules from sage.categories.vector_spaces import VectorSpaces -# (hopf) algebra structures +# (Hopf) algebra structures from sage.categories.algebras import Algebras from sage.categories.commutative_algebras import CommutativeAlgebras from sage.categories.coalgebras import Coalgebras diff --git a/src/sage/categories/basic.py b/src/sage/categories/basic.py index f3574db469d..568c9b7c114 100644 --- a/src/sage/categories/basic.py +++ b/src/sage/categories/basic.py @@ -42,6 +42,7 @@ from sage.categories.commutative_rings import CommutativeRings from sage.categories.integral_domains import IntegralDomains from sage.categories.gcd_domains import GcdDomains +from sage.categories.dedekind_domains import DedekindDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.unique_factorization_domains import UniqueFactorizationDomains diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index eec2f674aa7..492a1bf9ebf 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -75,14 +75,16 @@ def _make_named_class_key(self, name): (Join of Category of number fields and Category of quotient fields and Category of metric spaces, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) sage: Bimodules(Fields(), ZZ)._make_named_class_key('element_class') (Category of fields, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 9164bba6a57..aa99f9841db 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -119,7 +119,7 @@ _join_cache = WeakValueDictionary() -HALL_OF_FAME = ['Coxeter', 'Hopf', 'Weyl', 'Lie', 'Hecke'] +HALL_OF_FAME = ['Coxeter', 'Hopf', 'Weyl', 'Lie', 'Hecke', 'Dedekind'] class Category(UniqueRepresentation, SageObject): @@ -2592,6 +2592,7 @@ def category_sample(): sage: from sage.categories.category import category_sample sage: sorted(category_sample(), key=str) # needs sage.groups [Category of Coxeter groups, + Category of Dedekind domains, Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, Category of Hopf algebras over Rational Field, @@ -2836,7 +2837,8 @@ def _make_named_class_key(self, name): The parent class of an algebra depends only on the category of the base ring:: sage: Algebras(ZZ)._make_named_class_key("parent_class") - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces @@ -2847,7 +2849,8 @@ def _make_named_class_key(self, name): (Join of Category of number fields and Category of quotient fields and Category of metric spaces, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) @@ -2973,7 +2976,8 @@ def _make_named_class_key(self, name): EXAMPLES:: sage: Modules(ZZ)._make_named_class_key('element_class') - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index 4ead4f23530..7357dc5e8e4 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -225,7 +225,8 @@ def _make_named_class_key(self, name): EXAMPLES:: sage: Modules(ZZ)._make_named_class_key('element_class') - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/dedekind_domains.py b/src/sage/categories/dedekind_domains.py new file mode 100644 index 00000000000..d02c4bb996e --- /dev/null +++ b/src/sage/categories/dedekind_domains.py @@ -0,0 +1,155 @@ +r""" +Dedekind Domains +""" +# **************************************************************************** +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from sage.categories.category import Category +from sage.categories.integral_domains import IntegralDomains + + +class DedekindDomains(Category): + """ + The category of Dedekind domains. + + A Dedekind domain is a Noetherian integral domain of Krull + dimension one that is integrally closed in its field of fractions. + + EXAMPLES:: + + sage: C = DedekindDomains(); C + Category of Dedekind domains + sage: C.super_categories() + [Category of integral domains] + + TESTS:: + + sage: TestSuite(C).run() + """ + def super_categories(self): + """ + EXAMPLES:: + + sage: DedekindDomains().super_categories() + [Category of integral domains] + """ + return [IntegralDomains()] + + class ParentMethods: + def krull_dimension(self): + """ + Return 1 since Dedekind domains have Krull dimension 1. + + EXAMPLES: + + The following are examples of Dedekind domains:: + + sage: ZZ.krull_dimension() + 1 + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.krull_dimension() # needs sage.rings.number_field + 1 + + The following are not Dedekind domains but have + a ``krull_dimension`` function:: + + sage: QQ.krull_dimension() + 0 + sage: T. = PolynomialRing(QQ,2); T + Multivariate Polynomial Ring in x, y over Rational Field + sage: T.krull_dimension() + 2 + sage: U. = PolynomialRing(ZZ,3); U + Multivariate Polynomial Ring in x, y, z over Integer Ring + sage: U.krull_dimension() + 4 + + sage: # needs sage.rings.number_field + sage: K. = QuadraticField(-1) + sage: R = K.order(2*i); R + Order of conductor 2 generated by 2*i + in Number Field in i with defining polynomial x^2 + 1 with i = 1*I + sage: R.is_maximal() + False + sage: R.krull_dimension() + 1 + """ + from sage.rings.integer_ring import ZZ + return ZZ.one() + + def is_integrally_closed(self) -> bool: + """ + Return ``True`` since Dedekind domains are integrally closed. + + EXAMPLES: + + The following are examples of Dedekind domains:: + + sage: ZZ.is_integrally_closed() + True + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.is_integrally_closed() # needs sage.rings.number_field + True + + These, however, are not Dedekind domains:: + + sage: QQ.is_integrally_closed() + True + sage: S = ZZ[sqrt(5)]; S.is_integrally_closed() # needs sage.rings.number_field sage.symbolic + False + sage: T. = PolynomialRing(QQ, 2); T + Multivariate Polynomial Ring in x, y over Rational Field + sage: T.is_integral_domain() + True + """ + return True + + def integral_closure(self): + r""" + Return ``self`` since Dedekind domains are integrally closed. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') + sage: OK = K.ring_of_integers() + sage: OK.integral_closure() + Gaussian Integers generated by s in Number Field in s + with defining polynomial x^2 + 1 + sage: OK.integral_closure() == OK + True + + sage: QQ.integral_closure() == QQ + True + """ + return self + + def is_noetherian(self) -> bool: + r""" + Return ``True`` since Dedekind domains are Noetherian. + + EXAMPLES: + + The integers, `\ZZ`, and rings of integers of number + fields are Dedekind domains:: + + sage: ZZ.is_noetherian() + True + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.is_noetherian() # needs sage.rings.number_field + True + sage: QQ.is_noetherian() + True + """ + return True + + class ElementMethods: + pass diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py index 9f221f1432e..e8db64d11a9 100644 --- a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py @@ -242,7 +242,7 @@ def basis(self): lie_algebra_generators = basis - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 4f8bb141467..82769322ab9 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -118,9 +118,9 @@ def __contains__(self, x): 0 """ - import sage.rings.ring + from sage.rings.ring import _is_Field try: - return self._contains_helper(x) or sage.rings.ring._is_Field(x) + return self._contains_helper(x) or _is_Field(x) except Exception: return False diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 013b30dc281..ee3c14948aa 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -27,6 +27,31 @@ from sage.sets.family import Family +def _ce_complex_key(self, M, d, s, n): + """ + The key for caching the Chevalley-Eilenberg complex. + + TESTS:: + + sage: from sage.categories.finite_dimensional_lie_algebras_with_basis import _ce_complex_key + sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}}) + sage: _ce_complex_key(L, None, False, True, 5) + (None, False, True) + sage: f = ({x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}) + sage: _ce_complex_key(L, f, False, True, 5) + (Representation of Lie algebra on 2 generators (x, y) over Rational Field defined by: + [1 0] + x |--> [0 0] + [0 1] + y |--> [0 0], + False, + True) + """ + if isinstance(M, dict): + M = self.representation(M) + return (M, d, s) + + class FiniteDimensionalLieAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ Category of finite dimensional Lie algebras with a basis. @@ -1103,7 +1128,7 @@ def is_semisimple(self): """ return not self.killing_form_matrix().is_singular() - @cached_method(key=lambda self,M,d,s,n: (M,d,s)) + @cached_method(key=_ce_complex_key) def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=None): r""" Return the Chevalley-Eilenberg complex of ``self``. @@ -1134,7 +1159,13 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non INPUT: - ``M`` -- (default: the trivial 1-dimensional module) - the module `M` + one of the following: + + * a module `M` with an action of ``self`` + * a dictionary whose keys are basis elements and values + are matrices representing a Lie algebra homomorphism + defining the representation + - ``dual`` -- (default: ``False``) if ``True``, causes the dual of the complex to be computed - ``sparse`` -- (default: ``True``) whether to use sparse @@ -1148,15 +1179,15 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: C = L.chevalley_eilenberg_complex(); C Chain complex with at most 4 nonzero terms over Integer Ring sage: ascii_art(C) - [ 2 0 0] [0] - [ 0 -1 0] [0] - [0 0 0] [ 0 0 2] [0] + [-2 0 0] [0] + [ 0 1 0] [0] + [0 0 0] [ 0 0 -2] [0] 0 <-- C_0 <-------- C_1 <----------- C_2 <---- C_3 <-- 0 - sage: # long time, needs sage.combinat sage.modules + sage: # needs sage.combinat sage.modules sage: L = LieAlgebra(QQ, cartan_type=['C',2]) - sage: C = L.chevalley_eilenberg_complex() - sage: [C.free_module_rank(i) for i in range(11)] + sage: C = L.chevalley_eilenberg_complex() # long time + sage: [C.free_module_rank(i) for i in range(11)] # long time [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1] sage: # needs sage.combinat sage.modules @@ -1164,47 +1195,63 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: E, F, H = g.basis() sage: n = g.subalgebra([F, H]) sage: ascii_art(n.chevalley_eilenberg_complex()) - [0] - [0 0] [2] - 0 <-- C_0 <------ C_1 <---- C_2 <-- 0 + [ 0] + [0 0] [-2] + 0 <-- C_0 <------ C_1 <----- C_2 <-- 0 + + sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}}) + sage: f = ({x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}) + sage: C = L.chevalley_eilenberg_complex(f); C + Chain complex with at most 3 nonzero terms over Rational Field + sage: ascii_art(C) + [ 0 -1] + [ 2 0] + [1 0 0 1] [ 0 0] + [0 0 0 0] [ 0 1] + 0 <-- C_0 <---------- C_1 <-------- C_2 <-- 0 + + sage: ascii_art(L.chevalley_eilenberg_complex(f, sparse=False)) + [ 0 -1] + [ 2 0] + [1 0 0 1] [ 0 0] + [0 0 0 0] [ 0 1] + 0 <-- C_0 <---------- C_1 <-------- C_2 <-- 0 REFERENCES: - :wikipedia:`Lie_algebra_cohomology#Chevalley-Eilenberg_complex` - [Wei1994]_ Chapter 7 - - .. TODO:: - - Currently this is only implemented for coefficients - given by the trivial module `R`, where `R` is the - base ring and `g R = 0` for all `g \in \mathfrak{g}`. - Allow generic coefficient modules `M`. """ if dual: return self.chevalley_eilenberg_complex(M, dual=False, sparse=sparse, ncpus=ncpus).dual() - if M is not None: - raise NotImplementedError("only implemented for the default" - " (the trivial module)") - - from itertools import combinations + import itertools + from itertools import combinations, product from sage.arith.misc import binomial from sage.matrix.matrix_space import MatrixSpace + from sage.algebras.lie_algebras.representation import Representation_abstract R = self.base_ring() zero = R.zero() mone = -R.one() - if M is not None: - raise NotImplementedError("coefficient module M cannot be passed") # Make sure we specify the ordering of the basis - B = self.basis() - K = list(B.keys()) - B = [B[k] for k in K] - Ind = list(range(len(K))) - M = self.module() - ambient = M.is_ambient() + LB = self.basis() + LK = list(LB.keys()) + LB = [LB[k] for k in LK] + LI = list(range(len(LK))) + Lmod = self.module() + ambient = Lmod.is_ambient() + + if M is not None: + if not isinstance(M, Representation_abstract): + M = self.representation(M) + + MB = M.basis() + MK = list(MB.keys()) + MB = [MB[k] for k in MK] + MI = list(range(len(MK))) def sgn(k, X): """ @@ -1231,27 +1278,30 @@ def sgn(k, X): return R.one(), tuple(Y) from sage.parallel.decorate import parallel + from sage.matrix.constructor import matrix @parallel(ncpus=ncpus) def compute_diff(k): """ Build the ``k``-th differential (in parallel). """ - indices = {tuple(X): i for i,X in enumerate(combinations(Ind, k-1))} + # The indices for the exterior algebra + ext_ind = {tuple(X): i for i, X in enumerate(combinations(LI, k-1))} + + # Compute the part independent of the module first ("part 2" of the computation) if sparse: - data = {} + p2_data = {} row = 0 else: - data = [] + p2_data = [] if not sparse: - zero = [zero] * len(indices) - for X in combinations(Ind, k): + zv = [zero] * len(ext_ind) + for X in combinations(LI, k): if not sparse: - ret = list(zero) + ret = list(zv) for i in range(k): Y = list(X) Y.pop(i) - # We do mone**i because we are 0-based # This is where we would do the action on # the coefficients module #ret[indices[tuple(Y)]] += mone**i * zero @@ -1259,41 +1309,89 @@ def compute_diff(k): # We shift j by 1 because we already removed # an earlier element from X. Z = tuple(Y[:j-1] + Y[j:]) - elt = mone**(i+j) * B[X[i]].bracket(B[X[j]]) + elt = mone**(i+j+1) * LB[X[i]].bracket(LB[X[j]]) + if not elt: + continue if ambient: vec = elt.to_vector() else: - vec = M.coordinate_vector(elt.to_vector()) + vec = Lmod.coordinate_vector(elt.to_vector()) for key, coeff in vec.iteritems(): + if not coeff: + continue s, A = sgn(key, Z) if A is None: continue if sparse: - coords = (row, indices[A]) - if coords in data: - data[coords] += s * coeff + coords = (row, ext_ind[A]) + if coords in p2_data: + p2_data[coords] += s * coeff else: - data[coords] = s * coeff + p2_data[coords] = s * coeff + else: + ret[ext_ind[A]] += s * coeff + if sparse: + row += 1 + else: + p2_data.append(ret) + + nrows = binomial(len(LI), k) + ncols = binomial(len(LI), k-1) + MS = MatrixSpace(R, nrows, ncols, sparse=sparse) + if M is None: + p2 = MS(p2_data).transpose() + p2.set_immutable() + return p2 + p2 = matrix.identity(len(MI)).tensor_product(MS(p2_data)).transpose() + + ten_ind = {tuple(Y): i for i, Y in enumerate(product(MI, ext_ind))} + + # Now compute the part from the module ("part 1") + if sparse: + p1_data = {} + row = 0 + else: + p1_data = [] + if not sparse: + zv = [zero] * len(ten_ind) + for v, X in product(MI, combinations(LI, k)): + if not sparse: + ret = list(zv) + for i in range(k): + # We do mone**i because we are 0-based + elt = mone**i * LB[X[i]] * MB[v] + if not elt: + continue + Y = X[:i] + X[i+1:] + for j in MI: + coeff = elt[MK[j]] + if not coeff: + continue + if sparse: + coords = (row, ten_ind[j, Y]) + if coords in p1_data: + p1_data[coords] += coeff else: - ret[indices[A]] += s * coeff + p1_data[coords] = coeff + else: + ret[ten_ind[j, Y]] += coeff if sparse: row += 1 else: - data.append(ret) - nrows = binomial(len(Ind), k) - ncols = binomial(len(Ind), k-1) + p1_data.append(ret) + + nrows = len(MI) * binomial(len(LI), k) + ncols = len(ten_ind) MS = MatrixSpace(R, nrows, ncols, sparse=sparse) - ret = MS(data).transpose() + ret = MS(p1_data).transpose() + p2 ret.set_immutable() return ret - chain_data = {X[0][0]: M for X, M in compute_diff(list( range(1,len(Ind)+1) ))} - from sage.homology.chain_complex import ChainComplex - try: - return ChainComplex(chain_data, degree_of_differential=-1) - except TypeError: - return chain_data + ind = list(range(1, len(LI) + 1)) + chain_data = {X[0][0]: M for X, M in compute_diff(ind)} + C = ChainComplex(chain_data, degree_of_differential=-1) + return C def homology(self, deg=None, M=None, sparse=True, ncpus=None): r""" diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 1462073c5f2..a4d012f89b7 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -36,7 +36,7 @@ class FiniteDimensionalModulesWithBasis(CategoryWithAxiom_over_base_ring): class ParentMethods: - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 9aa5e0e5fd5..f6866485e93 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -101,12 +101,9 @@ def cardinality(self): sage: G.cardinality() 384 """ - try: - o = self.order - except AttributeError: - return self._cardinality_from_iterator() - else: - return o() + if hasattr(self, 'order'): + return self.order() + return self._cardinality_from_iterator() def some_elements(self): """ diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 5bdcea35419..9925ecf45b0 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -1244,16 +1244,18 @@ def reversed(self): Set of Morphisms from Ambient free module of rank 2 over the principal ideal domain Integer Ring to Ambient free module of rank 3 over the principal ideal domain Integer Ring in - Category of finite dimensional modules with basis over (euclidean - domains and infinite enumerated sets and metric spaces) + Category of finite dimensional modules with basis over (Dedekind + domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: type(H) sage: H.reversed() Set of Morphisms from Ambient free module of rank 3 over the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in - Category of finite dimensional modules with basis over (euclidean - domains and infinite enumerated sets and metric spaces) + Category of finite dimensional modules with basis over (Dedekind + domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: type(H.reversed()) """ diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index c4f5127306f..6927e3d4936 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -884,7 +884,8 @@ def __init_extra__(self): Ambient free module of rank 3 over the principal ideal domain Integer Ring) sage: M.category() # needs sage.modules Category of Cartesian products of modules with basis - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: M.base_ring() # needs sage.modules Integer Ring diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index a30cf52e5c9..8579ca89786 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -178,8 +178,9 @@ cdef class Morphism(Map): sage: f = R.hom([t**2]) sage: f.category() Category of endsets of unital magmas and right modules over - (euclidean domains and infinite enumerated sets and metric spaces) - and left modules over (euclidean domains + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) + and left modules over (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) sage: # needs sage.rings.number_field diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 0aff713bd94..178477bce7a 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -350,14 +350,17 @@ Integer Ring sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.categories() - [Join of Category of euclidean domains + [Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces, + Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, Category of gcd domains, Category of integral domains, Category of domains, diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index aa2e5dc9f4f..55cd22d87dd 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3797,7 +3797,7 @@ def __call__(self, R): return PermutationGroup([g for g in (R.gens() + self.gens()) if not g.is_one()], domain=self._domain) - def gens(self): + def gens(self) -> tuple: """ EXAMPLES:: diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 8f2c57feeb9..7c894a03112 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -906,7 +906,7 @@ def algebra_generators(self): # gens / monoid/group/*_generators, these methods could possibly # be removed in favor of aliases gens -> xxx_generators in # the Algebras.FinitelyGenerated hierarchy - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index ed84dc12517..7e58422d727 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -128,7 +128,7 @@ def _explain_constructor(cl): var = "It accepts unspecified arguments as well.\n" else: var = "" - return("{}\n{}\n{}See the documentation of {}.{} for more details." + return ("{}\n{}\n{}See the documentation of {}.{} for more details." .format(reqs, opts, var, cl.__module__, cl.__name__)) diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index b192792f11f..566add655a3 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -221,7 +221,7 @@ def _check_n_q_d(n, q, d, field_based=True): raise ValueError("The alphabet size must be an integer >1") if field_based and not is_prime_power(q): raise ValueError("The alphabet size does not make sense for a code over a field") - if not(0 < d <= n and n in ZZ and d in ZZ): + if not (0 < d <= n and n in ZZ and d in ZZ): raise ValueError("The length or minimum distance does not make sense") return True diff --git a/src/sage/coding/cyclic_code.py b/src/sage/coding/cyclic_code.py index 342b40d45c9..8cab59c2504 100644 --- a/src/sage/coding/cyclic_code.py +++ b/src/sage/coding/cyclic_code.py @@ -577,16 +577,17 @@ def field_embedding(self): To: Finite Field in z3 of size 2^3 Defn: 1 |--> 1 """ - if not(hasattr(self, "_field_embedding")): + if not hasattr(self, "_field_embedding"): self.defining_set() return self._field_embedding def defining_set(self, primitive_root=None): r""" Return the set of exponents of the roots of ``self``'s generator - polynomial over the extension field. Of course, it depends on the - choice of the primitive root of the splitting field. + polynomial over the extension field. + Of course, it depends on the choice of the primitive root of + the splitting field. INPUT: diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 82a0a5cfe72..56487aa6da1 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -258,11 +258,11 @@ def __eq__(self, other): True """ return isinstance(other, GeneralizedReedSolomonCode) \ - and self.base_field() == other.base_field() \ - and self.length() == other.length() \ - and self.dimension() == other.dimension() \ - and self.evaluation_points() == other.evaluation_points() \ - and self.column_multipliers() == other.column_multipliers() + and self.base_field() == other.base_field() \ + and self.length() == other.length() \ + and self.dimension() == other.dimension() \ + and self.evaluation_points() == other.evaluation_points() \ + and self.column_multipliers() == other.column_multipliers() def __hash__(self): """ @@ -298,9 +298,9 @@ def _repr_(self): [40, 12, 29] Generalized Reed-Solomon Code over GF(59) """ return "[%s, %s, %s] %sReed-Solomon Code over GF(%s)"\ - % (self.length(), self.dimension(), self.minimum_distance(), - "Generalized " if self.is_generalized() else "", - self.base_field().cardinality()) + % (self.length(), self.dimension(), self.minimum_distance(), + "Generalized " if self.is_generalized() else "", + self.base_field().cardinality()) def _latex_(self): r""" @@ -319,9 +319,9 @@ def _latex_(self): [40, 12, 29] \textnormal{ Generalized Reed-Solomon Code over } \Bold{F}_{59} """ return "[%s, %s, %s] \\textnormal{ %sReed-Solomon Code over } %s"\ - % (self.length(), self.dimension(), self.minimum_distance(), - "Generalized " if self.is_generalized() else "", - self.base_field()._latex_()) + % (self.length(), self.dimension(), self.minimum_distance(), + "Generalized " if self.is_generalized() else "", + self.base_field()._latex_()) def minimum_distance(self): r""" @@ -390,7 +390,7 @@ def is_generalized(self): sage: C2.is_generalized() True """ - return not all( beta.is_one() for beta in self.column_multipliers() ) + return not all(beta.is_one() for beta in self.column_multipliers()) @cached_method def multipliers_product(self): @@ -538,7 +538,7 @@ def weight_distribution(self): q = self.base_ring().order() s = SR.var('s') wd = [1] + [0] * (d - 1) - for i in range(d, n+1): + for i in range(d, n + 1): tmp = binomial(n, i) * (q - 1) wd.append(tmp * symbolic_sum(binomial(i-1, s) * (-1)**s * q**(i - d - s), s, 0, i-d)) return wd @@ -647,9 +647,9 @@ def ReedSolomonCode(base_field, length, dimension, primitive_root=None): else: if primitive_root.multiplicative_order() != length: raise ValueError("Supplied primitive_root is not a primitive n'th root of unity") - return GeneralizedReedSolomonCode([ primitive_root**i for i in range(length) ], dimension) + return GeneralizedReedSolomonCode([primitive_root**i for i in range(length)], dimension) -####################### encoders ############################### +# ###################### encoders ############################### class GRSEvaluationVectorEncoder(Encoder): @@ -723,7 +723,7 @@ def __eq__(self, other): False """ return isinstance(other, GRSEvaluationVectorEncoder) \ - and self.code() == other.code() + and self.code() == other.code() def _repr_(self): r""" @@ -788,7 +788,7 @@ def generator_matrix(self): C = self.code() alphas = C.evaluation_points() col_mults = C.column_multipliers() - g = matrix(C.base_field(), C.dimension(), C.length(), lambda i,j: col_mults[j] * alphas[j]**i) + g = matrix(C.base_field(), C.dimension(), C.length(), lambda i, j: col_mults[j] * alphas[j]**i) g.set_immutable() return g @@ -1086,7 +1086,7 @@ def message_space(self): polynomial_ring = message_space -####################### decoders ############################### +# ###################### decoders ############################### class GRSBerlekampWelchDecoder(Decoder): @@ -1184,7 +1184,7 @@ def _latex_(self): \textnormal{ Reed-Solomon Code over } \Bold{F}_{59} """ return "\\textnormal{Berlekamp Welch decoder for }%s"\ - % self.code()._latex_() + % self.code()._latex_() def _decode_to_code_and_message(self, r): r""" @@ -1229,14 +1229,15 @@ def _decode_to_code_and_message(self, r): col_mults = C.column_multipliers() r_list = copy(r) - r_list = [r[i]/col_mults[i] for i in range(0, C.length())] - - t = (C.minimum_distance()-1) // 2 - l0 = n-1-t - l1 = n-1-t-(k-1) - S = matrix(C.base_field(), n, l0+l1+2, - lambda i, j: (C.evaluation_points()[i])**j if j < (l0+1) - else r_list[i]*(C.evaluation_points()[i])**(j-(l0+1))) + r_list = [r[i] / col_mults[i] for i in range(C.length())] + + t = (C.minimum_distance() - 1) // 2 + l0 = n - 1 - t + l1 = n - t - k + pts = C.evaluation_points() + S = matrix(C.base_field(), n, l0 + l1 + 2, + lambda i, j: (pts[i]**j if j < (l0 + 1) + else r_list[i] * pts[i]**(j - (l0 + 1)))) S = S.right_kernel() S = S.basis_matrix().row(0) R = C.base_field()['x'] @@ -1574,7 +1575,7 @@ def _partial_xgcd(self, a, b, PolRing): r = b prev_r = a - while(r.degree() >= stop): + while r.degree() >= stop: q = prev_r.quo_rem(r)[0] (prev_r, r) = (r, prev_r - q * r) (prev_s, s) = (s, prev_s - q * s) @@ -1858,9 +1859,9 @@ def __eq__(self, other): False """ return isinstance(other, GRSErrorErasureDecoder) \ - and self.code() == other.code() + and self.code() == other.code() - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -1890,7 +1891,7 @@ def _latex_(self): \textnormal{ Reed-Solomon Code over } \Bold{F}_{59} """ return "\\textnormal{Error-Erasure decoder for }%s"\ - % self.code()._latex_() + % self.code()._latex_() def decode_to_message(self, word_and_erasure_vector): r""" @@ -2095,8 +2096,8 @@ def __eq__(self, other): False """ return isinstance(other, GRSKeyEquationSyndromeDecoder) \ - and self.code() == other.code()\ - and self.input_space() == other.input_space() + and self.code() == other.code()\ + and self.input_space() == other.input_space() def _repr_(self): r""" @@ -2165,7 +2166,7 @@ def _partial_xgcd(self, a, b, PolRing): prev_r = a r = b - while(r.degree() >= t.degree()): + while r.degree() >= t.degree(): q = prev_r.quo_rem(r)[0] prev_r, r = r, prev_r - q * r prev_t, t = t, prev_t - q * t @@ -2376,10 +2377,10 @@ def decoding_radius(self): sage: D.decoding_radius() 14 """ - return (self.code().minimum_distance()-1) // 2 + return (self.code().minimum_distance() - 1) // 2 -####################### registration ############################### +# ###################### registration ############################### GeneralizedReedSolomonCode._registered_encoders["EvaluationVector"] = GRSEvaluationVectorEncoder GeneralizedReedSolomonCode._registered_encoders["EvaluationPolynomial"] = GRSEvaluationPolynomialEncoder diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index f8f051a00d9..885fd02fa95 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -277,11 +277,11 @@ def _dump_code_in_leon_format(C): from sage.misc.temporary_file import tmp_filename F = C.base_ring() p = F.order() # must be prime and <11 - s = "LIBRARY code;\n"+"code=seq(%s,%s,%s,seq(\n" % (p,C.dimension(),C.length()) - Gr = [str(r)[1:-1].replace(" ","") for r in C.generator_matrix().rows()] + s = "LIBRARY code;\n" + "code=seq(%s,%s,%s,seq(\n" % (p, C.dimension(), C.length()) + Gr = [str(r)[1:-1].replace(" ", "") for r in C.generator_matrix().rows()] s += ",\n".join(Gr) + "\n));\nFINISH;" file_loc = tmp_filename() - f = open(file_loc,"w") + f = open(file_loc, "w") f.write(s) f.close() @@ -523,7 +523,7 @@ def automorphism_group_gens(self, equivalence="semilinear"): """ aut_group_can_label = self._canonize(equivalence) return aut_group_can_label.get_autom_gens(), \ - aut_group_can_label.get_autom_order() + aut_group_can_label.get_autom_order() def assmus_mattson_designs(self, t, mode=None): r""" @@ -605,29 +605,29 @@ def assmus_mattson_designs(self, t, mode=None): n = len(G.columns()) Cp = C.dual_code() wts = C.weight_distribution() - d = min([i for i in range(1,len(wts)) if wts[i] != 0]) + d = min([i for i in range(1, len(wts)) if wts[i] != 0]) if t >= d: return 0 - nonzerowts = [i for i in range(len(wts)) if wts[i] != 0 and i <= n and i >= d] + nonzerowts = [i for i in range(len(wts)) if wts[i] != 0 and d <= i <= n] if mode == "verbose": for w in nonzerowts: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w, t, n, w, wts[w] * binomial(w, t) // binomial(n, t), wts[w])) wtsp = Cp.weight_distribution() - dp = min([i for i in range(1,len(wtsp)) if wtsp[i] != 0]) + dp = min([i for i in range(1, len(wtsp)) if wtsp[i] != 0]) nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i] != 0 and i <= n-t and i >= dp] - s = len([i for i in range(1,n) if wtsp[i] != 0 and i <= n-t and i > 0]) + s = len([i for i in range(1, n) if wtsp[i] != 0 and 0 < i <= n-t]) if mode == "verbose": for w in nonzerowtsp: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w, t, n, w, wts[w] * binomial(w, t) // binomial(n, t), wts[w])) if s <= d-t: - des = [[t,(n,w,wts[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowts] - ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des] - desp = [[t,(n,w,wtsp[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowtsp] - ans = ans + ["weights from C*: ",nonzerowtsp,"designs from C*: ",desp] + des = [[t, (n, w, wts[w] * binomial(w, t) // binomial(n, t))] for w in nonzerowts] + ans = ans + ["weights from C: ", nonzerowts, "designs from C: ", des] + desp = [[t, (n, w, wtsp[w] * binomial(w, t) // binomial(n, t))] for w in nonzerowtsp] + ans = ans + ["weights from C*: ", nonzerowtsp, "designs from C*: ", desp] return ans return 0 @@ -669,15 +669,15 @@ def binomial_moment(self, i): d = self.minimum_distance() F = self.base_ring() q = F.order() - J = range(1,n+1) + J = range(1, n+1) Cp = self.dual_code() dp = Cp.minimum_distance() if i < d: return 0 - if i > n-dp and i <= n: - return binomial(n,i)*(q**(i+k-n) - 1)//(q-1) + if n - dp < i <= n: + return binomial(n, i)*(q**(i+k-n) - 1)//(q-1) from sage.combinat.set_partition import SetPartitions - P = SetPartitions(J,2).list() + P = SetPartitions(J, 2).list() b = QQ(0) for p in P: p = list(p) @@ -794,7 +794,7 @@ def canonical_representative(self, equivalence="semilinear"): """ aut_group_can_label = self._canonize(equivalence) return aut_group_can_label.get_canonical_form(), \ - aut_group_can_label.get_transporter() + aut_group_can_label.get_transporter() def characteristic(self): r""" @@ -877,7 +877,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial())*T**(d - dperp) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not(is_even(n)): + if not is_even(n): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -886,7 +886,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial()) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not(is_even(n)): + if not is_even(n): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -1660,13 +1660,13 @@ def permutation_automorphism_group(self, algorithm="partition"): 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 + Gp = A.Intersection2(Gp) # bottleneck 3 if Gp.Size() == 1: return PermutationGroup([()]) gens = Gp.GeneratorsOfGroup() stop = 1 # get ready to stop for x in gens: # if one of these gens is not an auto then don't stop - if not(self.is_permutation_automorphism(Sn_sage(x))): + if not self.is_permutation_automorphism(Sn_sage(x)): stop = 0 break G = PermutationGroup(list(map(Sn_sage, gens))) @@ -1877,7 +1877,7 @@ def weight_distribution(self, algorithm=None): from sage.coding.binary_code import weight_dist return weight_dist(self.generator_matrix()) elif algorithm == "leon": - if not(F.order() in [2,3,5,7]): + if F.order() not in [2, 3, 5, 7]: raise NotImplementedError("The algorithm 'leon' is only implemented for q = 2,3,5,7.") # The GAP command DirectoriesPackageLibrary tells the location of the latest # version of the Guava libraries, so gives us the location of the Guava binaries too. @@ -2030,16 +2030,16 @@ def zeta_polynomial(self, name="T"): if d == 1 or dperp == 1: print("\n WARNING: There is no guarantee this function works when the minimum distance") print(" of the code or of the dual code equals 1.\n") - RT = PolynomialRing(QQ,"%s" % name) - R = PolynomialRing(QQ,3,"xy%s" % name) - x,y,T = R.gens() + RT = PolynomialRing(QQ, "%s" % name) + R = PolynomialRing(QQ, 3, "xy%s" % name) + x, y, T = R.gens() we = self.weight_enumerator() A = R(we) - #B = A(x+y,y,T)-(x+y)**n - B = A(x,x+y,T)-(x+y)**n + # B = A(x+y,y,T)-(x+y)**n + B = A(x, x+y, T)-(x+y)**n Bs = B.coefficients() Bs.reverse() - b = [Bs[i]/binomial(n,i+d) for i in range(len(Bs))] + b = [Bs[i]/binomial(n, i+d) for i in range(len(Bs))] r = n-d-dperp+2 P_coeffs = [] for i in range(len(b)): @@ -2397,7 +2397,7 @@ def _latex_(self): [7, 4]\textnormal{ Linear code over }\Bold{F}_{2} """ return "[%s, %s]\\textnormal{ Linear code over }%s"\ - % (self.length(), self.dimension(), self.base_ring()._latex_()) + % (self.length(), self.dimension(), self.base_ring()._latex_()) def generator_matrix(self, encoder_name=None, **kwargs): r""" @@ -2428,7 +2428,7 @@ def generator_matrix(self, encoder_name=None, **kwargs): return g -####################### encoders ############################### +# ###################### encoders ############################### class LinearCodeGeneratorMatrixEncoder(Encoder): r""" @@ -2467,7 +2467,7 @@ def __eq__(self, other): True """ return isinstance(other, LinearCodeGeneratorMatrixEncoder)\ - and self.code() == other.code() + and self.code() == other.code() def _repr_(self): r""" @@ -2519,7 +2519,7 @@ def generator_matrix(self): return g -####################### decoders ############################### +# ###################### decoders ############################### class LinearCodeSyndromeDecoder(Decoder): r""" @@ -2812,18 +2812,18 @@ def _build_lookup_table(self): F = C.base_ring() l = list(F) zero = F.zero() - #Builds a list of generators of all error positions for all - #possible error weights + # Builds a list of generators of all error positions for all + # possible error weights if zero in l: l.remove(zero) # Remember to include the no-error-vector to handle codes of minimum # distance 1 gracefully - zero_syndrome = vector(F,[F.zero()]*(n-k)) + zero_syndrome = vector(F, [F.zero()]*(n-k)) zero_syndrome.set_immutable() - lookup = {zero_syndrome: vector(F,[F.zero()]*n)} + lookup = {zero_syndrome: vector(F, [F.zero()]*n)} error_position_tables = [cartesian_product([l]*i) for i in range(1, t+1)] first_collision = True - #Filling the lookup table + # Filling the lookup table for i in range(1, t+1): stop = True patterns = Subsets(range(n), i) @@ -2839,16 +2839,16 @@ def _build_lookup_table(self): s.set_immutable() try: e_cur = lookup[s] - #if this is the first time we see a collision - #we learn the minimum distance of the code + # if this is the first time we see a collision + # we learn the minimum distance of the code if first_collision: self._code_minimum_distance = e.hamming_weight() + e_cur.hamming_weight() first_collision = False except KeyError: stop = False lookup[s] = copy(e) - #if we reached the early termination condition - #we learn the covering radius of the code + # if we reached the early termination condition + # we learn the covering radius of the code if stop: self._code_covering_radius = i - 1 self._maximum_error_weight = self._code_covering_radius @@ -3001,9 +3001,9 @@ def __eq__(self, other): True """ return isinstance(other, LinearCodeNearestNeighborDecoder)\ - and self.code() == other.code() + and self.code() == other.code() - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -3079,7 +3079,7 @@ def decoding_radius(self): return (self.code().minimum_distance()-1) // 2 -####################### registration ############################### +# ###################### registration ############################### LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 2ae0879fa74..2a422a2d19d 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -373,7 +373,7 @@ def rate(self): return self.dimension() / self.length() @cached_method - def gens(self): + def gens(self) -> list: r""" Return the generators of this code as a list of vectors. @@ -891,12 +891,9 @@ def is_subcode(self, other): True """ G = self.generator_matrix() - for r in G.rows(): - if not(r in other): - return False - return True + return all(r in other for r in G.rows()) - def is_permutation_automorphism(self,g): + def is_permutation_automorphism(self, g): r""" Return `1` if `g` is an element of `S_n` (`n` = length of ``self``) and if `g` is an automorphism of ``self``. diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 7aafa4f3f27..3caab922b1b 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -328,7 +328,7 @@ def structured_representation(self): C = self.original_code() pts = copy(self.punctured_positions()) list_pts = list(pts) - while(isinstance(C, PuncturedCode)): + while isinstance(C, PuncturedCode): cur_pts = list(C.punctured_positions()) list_len = len(list_pts) for p in cur_pts: diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 6ea8a904722..1d55ba6fa38 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -258,9 +258,9 @@ def __init__(self, base_field, order, num_of_var): # input sanitization if base_field not in FiniteFields(): raise ValueError("the input `base_field` must be a FiniteField") - if not(isinstance(order, (Integer, int))): + if not isinstance(order, (Integer, int)): raise ValueError("The order of the code must be an integer") - if not(isinstance(num_of_var, (Integer, int))): + if not isinstance(num_of_var, (Integer, int)): raise ValueError("The number of variables must be an integer") q = base_field.cardinality() if order >= q: @@ -425,9 +425,9 @@ def __init__(self, order, num_of_var): ValueError: The order of the code must be an integer """ # input sanitization - if not(isinstance(order, (Integer, int))): + if not isinstance(order, (Integer, int)): raise ValueError("The order of the code must be an integer") - if not(isinstance(num_of_var, (Integer, int))): + if not isinstance(num_of_var, (Integer, int)): raise ValueError("The number of variables must be an integer") if (num_of_var < order): raise ValueError( @@ -836,7 +836,7 @@ def __eq__(self, other): False """ return isinstance(other, ReedMullerPolynomialEncoder) \ - and self.code() == other.code() + and self.code() == other.code() def encode(self, p): r""" @@ -984,7 +984,7 @@ def points(self): return ((code.base_field())**code.number_of_variables()).list() -####################### registration ############################### +# --------------- registration -------------- QAryReedMullerCode._registered_encoders["EvaluationVector"] = ReedMullerVectorEncoder QAryReedMullerCode._registered_encoders["EvaluationPolynomial"] = ReedMullerPolynomialEncoder diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 32f9ccc52d7..e49bc3d25e1 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1045,7 +1045,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): # if one edge appearing in exactly two oriented triangles, test that it is not a double-edge and then # test that either the third or fourth vertices (from the oriented triangles) is of degree 2. # Then initializes the long_cycle as this triangle including the degree 2 vertex, as long as no other long_cycles. - elif count == 1 and not dg.has_multiple_edges() and not multiple_trian_edges[0] in dg.multiple_edges(): + elif count == 1 and not dg.has_multiple_edges() and multiple_trian_edges[0] not in dg.multiple_edges(): multiple_trian_edge = multiple_trian_edges[0] neighbors = list(set(dg.neighbors( multiple_trian_edge[0] )).intersection(dg.neighbors( multiple_trian_edge[1] ))) if dg.degree( neighbors[0] ) == 2: diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 2d6a6c31aaa..6a9e5598381 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -651,7 +651,7 @@ def _inverse_simple_reflections(self): return {i: ~s[i] for i in self.index_set()} @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 7363fa05f90..695e31ab70e 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -879,7 +879,7 @@ def BIBD_from_PBD(PBD, v, k, check=True, base_cases=None): for X in PBD: n = len(X) N = (k-1)*n+1 - if not (n,k) in base_cases: + if (n,k) not in base_cases: base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k), N) for XX in base_cases[n,k]: diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 5eb2b90802a..f3e1ef73350 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2469,7 +2469,7 @@ def QDM_57_9_1_1_8(): ((45,7,1,1,9), QDM_45_7_1_1_9), ((54,7,1,1,8), QDM_54_7_1_1_8), ((57,9,1,1,8), QDM_57_9_1_1_8)]: - if not (n+u,lmbda) in QDM: + if (n+u,lmbda) not in QDM: QDM[n+u,lmbda] = {} QDM[n+u,lmbda][n,lmbda,mu,u] = (k,f) @@ -2701,7 +2701,7 @@ def QDM_57_9_1_1_8(): # Translate all V(m,t) into (mt+1,m+2;1,0;t)-QDM constructors for (m,t),(vec,source) in Vmt_vectors.items(): n,k,lmbda,mu,u = (m*t+1,m+2,1,0,t) - if not (n+u,lmbda) in QDM: + if (n+u,lmbda) not in QDM: QDM[n+u,lmbda] = {} QDM[n+u,lmbda][n,lmbda,mu,u] = (k,lambda m=m,t=t,vec=vec:QDM_from_Vmt(m,t,vec)) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 393ea4b9b47..357a94fbb44 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -3138,8 +3138,9 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch See also :wikipedia:`Difference_set`. - If there is no such difference family, an ``EmptySetError`` is raised and if - there is no construction at the moment ``NotImplementedError`` is raised. + If there is no such difference family, an ``EmptySetError`` is raised and + if there is no construction at the moment :class:`NotImplementedError` + is raised. INPUT: diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 27452495862..58662fbeb88 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -299,7 +299,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): Unknown If you ask for such a MOLS then you will respectively get an informative - ``EmptySetError`` or ``NotImplementedError``:: + ``EmptySetError`` or :class:`NotImplementedError`:: sage: designs.mutually_orthogonal_latin_squares(5, 5) Traceback (most recent call last): diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 312f6a4b375..b4a14b0e7b6 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -190,7 +190,7 @@ def transversal_design(k, n, resolvable=False, check=True, existence=False): Unknown If you ask for a transversal design that Sage is not able to build then an - ``EmptySetError`` or a ``NotImplementedError`` is raised:: + ``EmptySetError`` or a :class:`NotImplementedError` is raised:: sage: designs.transversal_design(47, 100) Traceback (most recent call last): @@ -1359,7 +1359,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): return True independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) - elif max_hole == 1 and not orthogonal_array(k,n,existence=True) is True: + elif max_hole == 1 and orthogonal_array(k,n,existence=True) is not True: return orthogonal_array(k,n,existence=existence) # From a quasi-difference matrix @@ -2117,8 +2117,8 @@ class OAMainFunctions: 6 If you ask for an orthogonal array that does not exist, then you will - either obtain an ``EmptySetError`` (if it knows that such an orthogonal array - does not exist) or a ``NotImplementedError``:: + either obtain an ``EmptySetError`` (if it knows that such an orthogonal + array does not exist) or a :class:`NotImplementedError`:: sage: designs.orthogonal_arrays.build(4,2) Traceback (most recent call last): diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 347a6a4d27b..e79e996dcb5 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -738,7 +738,7 @@ def PBD_4_7_from_Y(gdd,check=True): "but there are other: {}".format(txt)) for gs in group_sizes: - if not PBD_4_7(3*gs+1,existence=True) is True: + if PBD_4_7(3*gs+1,existence=True) is not True: raise RuntimeError("A group has size {} but I do not know how to " "build a ({},[4,7])-PBD".format(gs,3*gs+1)) diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 8312f2f92c8..7c7819c4138 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -709,7 +709,7 @@ def steiner_quadruple_system(n, check=False): ....: sqs = designs.steiner_quadruple_system(n, check=True) """ n = int(n) - if not ((n % 6) in [2, 4]): + if (n % 6) not in [2, 4]: raise ValueError("n mod 6 must be equal to 2 or 4") elif n == 4: sqs = IncidenceStructure(4, [[0,1,2,3]], copy=False, check=False) diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index 267ff3b2fe0..c31e8dcd229 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -295,7 +295,7 @@ cdef int is_subhypergraph_admissible(hypergraph h1,hypergraph * h2_trace,int n,h return 1 -cdef int cmp_128_bits(void * a, void * b) noexcept nogil: +cdef int cmp_128_bits(const void * a, const void * b) noexcept nogil: r""" Lexicographic order on 128-bits words """ diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 057eee403dc..13e523df4e6 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -591,8 +591,7 @@ ... ValueError: Invalid input sequence. -The raised ``ValueError`` -means `13` is not divisible by `3`. +The raised :class:`ValueError` means `13` is not divisible by `3`. .. _finite_state_machine_gray_code_example: @@ -2120,7 +2119,7 @@ def _epsilon_cycle_output_empty_(self, fsm=None): ``True`` or ``False`` - A ``ValueError`` is raised when ``self`` is not in an epsilon + A :class:`ValueError` is raised when ``self`` is not in an epsilon cycle. TESTS:: @@ -2537,7 +2536,9 @@ def duplicate_transition_ignore(old_transition, new_transition): def duplicate_transition_raise_error(old_transition, new_transition): """ Alternative function for handling duplicate transitions in finite - state machines. This implementation raises a ``ValueError``. + state machines. + + This implementation raises a :class:`ValueError`. See the documentation of the ``on_duplicate_transition`` parameter of :class:`FiniteStateMachine`. @@ -2551,7 +2552,7 @@ def duplicate_transition_raise_error(old_transition, new_transition): OUTPUT: - Nothing. A ``ValueError`` is raised. + Nothing. A :class:`ValueError` is raised. EXAMPLES:: @@ -2812,7 +2813,7 @@ class FiniteStateMachine(SageObject): non-deterministic). If the transition does not exist, the function should raise a - ``LookupError`` or return an empty list. + :class:`LookupError` or return an empty list. When constructing a finite state machine in this way, some initial states and an input alphabet have to be specified. @@ -5507,7 +5508,7 @@ def state(self, state): The state of the finite state machine corresponding to ``state``. - If no state is found, then a ``LookupError`` is thrown. + If no state is found, then a :class:`LookupError` is thrown. EXAMPLES:: @@ -5553,7 +5554,7 @@ def transition(self, transition): The transition of the finite state machine corresponding to ``transition``. - If no transition is found, then a ``LookupError`` is thrown. + If no transition is found, then a :class:`LookupError` is thrown. EXAMPLES:: @@ -7622,7 +7623,7 @@ def product_FiniteStateMachine(self, other, function, for `j\in\{1, \ldots, d\}` and returns a pair ``(word_in, word_out)`` which is the label of the transition `A=(A_1, \ldots, A_d)` to `B=(B_1, \ldots, B_d)`. If there is no transition from `A` to `B`, - then ``function`` should raise a ``LookupError``. + then ``function`` should raise a :class:`LookupError`. - ``new_input_alphabet`` (optional) -- the new input alphabet as a list. @@ -7637,7 +7638,7 @@ def product_FiniteStateMachine(self, other, function, the corresponding state in the new finite state machine. By default, the final output is the empty word if both final outputs of the constituent states are empty; otherwise, a - ``ValueError`` is raised. + :class:`ValueError` is raised. - ``new_class`` -- Class of the new finite state machine. By default (``None``), the class of ``self`` is used. @@ -12685,7 +12686,7 @@ def process(self, *args, **kwargs): - ``full_output`` -- (default: ``True``) a boolean. If set, then the full output is given, otherwise only the generated output (the third entry below only). If the input is not - accepted, a ``ValueError`` is raised. + accepted, a :class:`ValueError` is raised. - ``always_include_output`` -- if set (not by default), always include the output. This is inconsequential for a diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index fb1cb498c79..2b3ce0ae216 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -285,7 +285,7 @@ def change_ring(self, R): """ return FreeDendriformAlgebra(R, names=self.variable_names()) - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` (as an algebra). diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index cb81e811ade..88d54b1f443 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -348,7 +348,7 @@ def change_ring(self, R): """ return FreePreLieAlgebra(R, names=self.variable_names()) - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` (as an algebra). diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 3f1476fb15c..6cff509724d 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -103,6 +103,13 @@ def nesting(la, nu): sage: nesting(set(mu).difference(nu), nu) 1 + sage: A = SetPartition([[1], [2,5], [3,4]]) + sage: B = SetPartition([[1,3,4], [2,5]]) + sage: nesting(A, B) + 1 + sage: nesting(B, A) + 1 + :: sage: lst = list(SetPartitions(4)) @@ -135,11 +142,13 @@ def nesting(la, nu): nst = 0 for p in la: p = sorted(p) - for i in range(len(p)-1): - for a in arcs: - if a[0] >= p[i]: + for a in arcs: + if p[-1] < a[0]: + continue + for i in range(len(p)-1): + if a[1] <= p[i+1]: break - if p[i+1] < a[1]: + if a[0] < p[i]: nst += 1 return nst diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 3b8cb966702..cec67892f4c 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -3017,7 +3017,7 @@ def arm_length(self, i, j): OUTPUT: - An integer or a ``ValueError`` + An integer or a :class:`ValueError` EXAMPLES:: @@ -3111,7 +3111,7 @@ def leg_length(self, i, j): OUTPUT: - An integer or a ``ValueError`` + An integer or a :class:`ValueError` EXAMPLES:: @@ -6201,8 +6201,9 @@ def __contains__(self, x): def subset(self, *args, **kwargs): r""" - Return ``self`` if no arguments are given, otherwise raises a - ``ValueError``. + Return ``self`` if no arguments are given. + + Otherwise, it raises a :class:`ValueError`. EXAMPLES:: @@ -9017,7 +9018,7 @@ def number_of_partitions(n, algorithm='default'): sage: len(v) 7 - The input must be a nonnegative integer or a ``ValueError`` is raised. + The input must be a nonnegative integer or a :class:`ValueError` is raised. :: diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index b5caf31d84b..84c77b0670e 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -36,7 +36,7 @@ def _int_or_half_int(k): OUTPUT: - If ``k`` is not in `1/2 \ZZ`, then this raises a ``ValueError``. + If ``k`` is not in `1/2 \ZZ`, then this raises a :class:`ValueError`. Otherwise, we return the pair: - boolean; ``True`` if ``k`` is an integer and ``False`` if a half integer diff --git a/src/sage/combinat/partition_shifting_algebras.py b/src/sage/combinat/partition_shifting_algebras.py index e8f22589eb3..90f1f10d59f 100644 --- a/src/sage/combinat/partition_shifting_algebras.py +++ b/src/sage/combinat/partition_shifting_algebras.py @@ -92,7 +92,7 @@ def check(self, seq): r""" Verify that ``seq`` is a valid shifting sequence. - If it is not, raise a ``ValueError``. + If it is not, raise a :class:`ValueError`. EXAMPLES:: diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 43191540d71..b84b587e1b3 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -3,7 +3,7 @@ Generalized Tamari lattices These lattices depend on three parameters `a`, `b` and `m`, where `a` -and `b` are coprime positive integers and `m` is a nonnegative +and `b` are positive integers and `m` is a nonnegative integer. The elements are :func:`Dyck paths` @@ -48,8 +48,6 @@ # **************************************************************************** from __future__ import annotations from sage.combinat.posets.lattices import LatticePoset, MeetSemilattice -from sage.arith.misc import gcd - def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: r""" @@ -63,10 +61,10 @@ def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: INPUT: - - `a` and `b` -- coprime integers with `a \geq b` + - `a` and `b` -- integers with `a \geq b` - `i` and `j` -- nonnegative integers with `1 \geq \frac{j}{b} \geq - \frac{bi}{a} \geq 0` + \frac{i}{a} \geq 0` OUTPUT: @@ -103,7 +101,7 @@ def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: def swap(p, i, m=1) -> tuple[int, ...]: r""" - Perform a covering move in the `(a,b)`-Tamari lattice of parameter `m`. + Perform a covering move in the `(a,b)`-Tamari lattice of slope parameter `m`. The letter at position `i` in `p` must be a `0`, followed by at least one `1`. @@ -125,6 +123,11 @@ def swap(p, i, m=1) -> tuple[int, ...]: (1, 1, 0, 0, 0) sage: swap((1,1,0,0,1,1,0,0,0),3) (1, 1, 0, 1, 1, 0, 0, 0, 0) + sage: swap((1,0,1,0,1,0,0,0), 1, 1) + (1, 1, 0, 0, 1, 0, 0, 0) + sage: swap((1,0,1,0,1,0,0,0), 1, 5/3) + (1, 1, 0, 1, 0, 0, 0, 0) + TESTS:: @@ -151,7 +154,7 @@ def swap(p, i, m=1) -> tuple[int, ...]: height += m else: height -= 1 - if height == 0: + if height <= 0: found = True q = [k for k in p] for k in range(i, j): @@ -160,19 +163,19 @@ def swap(p, i, m=1) -> tuple[int, ...]: return tuple(q) -def GeneralizedTamariLattice(a, b, m=1, check=True): +def GeneralizedTamariLattice(a, b, m=1): r""" Return the `(a,b)`-Tamari lattice of parameter `m`. INPUT: - - `a` and `b` -- coprime integers with `a \geq b` + - `a` and `b` -- integers with `a \geq b` - - `m` -- a nonnegative integer such that `a \geq b m` + - `m` -- a nonnegative rational number such that `a \geq b m` OUTPUT: - - a finite lattice (the lattice property is only conjectural in general) + - a finite lattice (special case of the alt `\nu`-Tamari lattices in [CC2023]_) The elements of the lattice are :func:`Dyck paths` in the @@ -185,7 +188,8 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): The usual :wikipedia:`Tamari lattice` of index `b` is the special case `a=b+1` and `m=1`. - Other special cases give the `m`-Tamari lattices studied in [BMFPR]_. + Other special cases give the `m`-Tamari lattices studied in [BMFPR2011]_, + or the rational Tamari lattices when a and b are coprime and m = a/b (see [PRV2017]_). EXAMPLES:: @@ -194,16 +198,15 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): Finite lattice containing 2 elements sage: GeneralizedTamariLattice(4,3) Finite lattice containing 5 elements - sage: GeneralizedTamariLattice(4,4) - Traceback (most recent call last): - ... - ValueError: the numbers a and b must be coprime with a>=b sage: GeneralizedTamariLattice(7,5,2) Traceback (most recent call last): ... ValueError: the condition a>=b*m does not hold - sage: P = GeneralizedTamariLattice(5,3);P + sage: P = GeneralizedTamariLattice(5,3); P Finite lattice containing 7 elements + sage: P = GeneralizedTamariLattice(5, 3, m=5/3); P + Finite lattice containing 7 elements + TESTS:: @@ -212,11 +215,12 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): REFERENCES: - .. [BMFPR] \M. Bousquet-Melou, E. Fusy, L.-F. Preville Ratelle. - *The number of intervals in the m-Tamari lattices*. :arxiv:`1106.1498` + - [BMFPR2011]_ + + - [PRV2017]_ + + - [CC2023]_ """ - if not (gcd(a, b) == 1 and a >= b): - raise ValueError("the numbers a and b must be coprime with a>=b") if a < b * m: raise ValueError("the condition a>=b*m does not hold") @@ -224,7 +228,7 @@ def covers(p): return [swap(p, i, m) for i in range(len(p) - 1) if not p[i] and p[i + 1]] return LatticePoset({p: covers(p) - for p in paths_in_triangle(a, b, a, b)}, check=check) + for p in paths_in_triangle(a, b, a, b)}, check=False) def TamariLattice(n, m=1): @@ -261,9 +265,9 @@ def TamariLattice(n, m=1): REFERENCES: - - [BMFPR]_ + - [BMFPR2011]_ """ - return GeneralizedTamariLattice(m * n + 1, n, m, check=False) + return GeneralizedTamariLattice(m * n + 1, n, m) # a variation : the Dexter meet-semilattices diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 5106615ae9b..f5506152d44 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -18,7 +18,7 @@ - Arnaud Bergeron (2008) : Initial version, path on the square grid -- Sebastien Labbe (2009-01-14) : New classes and hierarchy, doc and functions. +- Sébastien Labbé (2009-01-14) : New classes and hierarchy, doc and functions. EXAMPLES: @@ -185,22 +185,22 @@ from sage.combinat.words.word import FiniteWord_class from sage.combinat.words.alphabet import build_alphabet from sage.misc.lazy_import import lazy_import -lazy_import("sage.plot.all", ["arrow", "line", "polygon", "point", "Graphics"]) from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from .word_datatypes import (WordDatatype_str, - WordDatatype_list, - WordDatatype_tuple) - #WordDatatype_cpp_basic_string) + WordDatatype_list, + WordDatatype_tuple) +# WordDatatype_cpp_basic_string) from .word_infinite_datatypes import ( - WordDatatype_iter_with_caching, - WordDatatype_iter, - WordDatatype_callable_with_caching, - WordDatatype_callable) + WordDatatype_iter_with_caching, + WordDatatype_iter, + WordDatatype_callable_with_caching, + WordDatatype_callable) from sage.matrix.constructor import vector_on_axis_rotation_matrix +lazy_import("sage.plot.all", ["arrow", "line", "polygon", "point", "Graphics"]) lazy_import('sage.rings.number_field.number_field', 'QuadraticField') @@ -212,13 +212,13 @@ def WordPaths(alphabet, steps=None): r""" - Returns the combinatorial class of paths of the given type of steps. + Return the combinatorial class of paths of the given type of steps. INPUT: - ``alphabet`` - ordered alphabet - - ``steps`` - (default is None). It can be one of the following: + - ``steps`` - (default is ``None``). It can be one of the following: - an iterable ordered container of as many vectors as there are letters in the alphabet. The vectors are associated to the letters @@ -248,7 +248,7 @@ def WordPaths(alphabet, steps=None): OUTPUT: - - The combinatorial class of all paths of the given type. + The combinatorial class of all paths of the given type. EXAMPLES: @@ -321,10 +321,10 @@ def WordPaths(alphabet, steps=None): ... TypeError: Unknown type of steps : square_gridd """ - #Construction of the alphabet + # Construction of the alphabet alphabet = build_alphabet(alphabet) - #If no steps are given, they are guessed from the alphabet + # If no steps are given, they are guessed from the alphabet if steps is None: if alphabet.cardinality() == 2: steps = 'north_east' @@ -335,7 +335,7 @@ def WordPaths(alphabet, steps=None): else: raise TypeError("Unable to make a class WordPaths from %s" % alphabet) - #Returns the class of WordPaths according to the given type of paths + # Return the class of WordPaths according to the given type of paths if isinstance(steps, str): if steps in ('square_grid', 'square'): return WordPaths_square_grid(alphabet=alphabet) @@ -405,25 +405,25 @@ def __init__(self, alphabet, steps): sage: WordPaths_all('ab', d) Traceback (most recent call last): ... - ValueError: Can't make vectors from steps + ValueError: cannot make vectors from steps sage: d = ((1,1), (-1,1,0)) sage: WordPaths_all('ab', d) Traceback (most recent call last): ... - ValueError: Can't make summable vectors from steps + ValueError: cannot make summable vectors from steps """ - #Construction of the words class + # Construction of the words class FiniteWords.__init__(self, alphabet) alphabet = self.alphabet() - #Checking the size of alphabet and steps + # Checking the size of alphabet and steps ls = len(steps) la = alphabet.cardinality() - if la != ls and la != 2*ls: + if la != ls and la != 2 * ls: raise TypeError("size of steps (=%s) must equal the size of alphabet (=%s) or half the size of alphabet" % (len(steps), alphabet.cardinality())) - #Construction of the steps + # Construction of the steps from sage.structure.element import Vector if all(isinstance(x, Vector) for x in steps): vsteps = steps @@ -431,13 +431,13 @@ def __init__(self, alphabet, steps): try: vsteps = [vector(s) for s in steps] except (TypeError): - raise ValueError("Can't make vectors from steps") + raise ValueError("cannot make vectors from steps") try: s = sum(vsteps) except (TypeError, AttributeError): - raise ValueError("Can't make summable vectors from steps") + raise ValueError("cannot make summable vectors from steps") - #Complete vsteps with the opposite vectors if needed + # Complete vsteps with the opposite vectors if needed if la == 2 * ls: vsteps += [-v for v in vsteps] @@ -457,9 +457,9 @@ def __eq__(self, other): False """ return self is other or (type(self) is type(other) and - self.alphabet() == other.alphabet() and - self.vector_space() == other.vector_space() and - self.letters_to_steps() == other.letters_to_steps()) + self.alphabet() == other.alphabet() and + self.vector_space() == other.vector_space() and + self.letters_to_steps() == other.letters_to_steps()) def __ne__(self, other): r""" @@ -478,7 +478,7 @@ def __ne__(self, other): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -524,38 +524,38 @@ def _element_classes(self): dimension = self._vector_space.dimension() if dimension == 2: return { - 'list': FiniteWordPath_2d_list, - 'str': FiniteWordPath_2d_str, - 'tuple': FiniteWordPath_2d_tuple, - 'callable_with_caching': FiniteWordPath_2d_callable_with_caching, - 'callable': FiniteWordPath_2d_callable, - 'iter_with_caching': FiniteWordPath_2d_iter_with_caching, - 'iter': FiniteWordPath_2d_iter, + 'list': FiniteWordPath_2d_list, + 'str': FiniteWordPath_2d_str, + 'tuple': FiniteWordPath_2d_tuple, + 'callable_with_caching': FiniteWordPath_2d_callable_with_caching, + 'callable': FiniteWordPath_2d_callable, + 'iter_with_caching': FiniteWordPath_2d_iter_with_caching, + 'iter': FiniteWordPath_2d_iter, } elif dimension == 3: return { - 'list': FiniteWordPath_3d_list, - 'str': FiniteWordPath_3d_str, - 'tuple': FiniteWordPath_3d_tuple, - 'callable_with_caching': FiniteWordPath_3d_callable_with_caching, - 'callable': FiniteWordPath_3d_callable, - 'iter_with_caching': FiniteWordPath_3d_iter_with_caching, - 'iter': FiniteWordPath_3d_iter, + 'list': FiniteWordPath_3d_list, + 'str': FiniteWordPath_3d_str, + 'tuple': FiniteWordPath_3d_tuple, + 'callable_with_caching': FiniteWordPath_3d_callable_with_caching, + 'callable': FiniteWordPath_3d_callable, + 'iter_with_caching': FiniteWordPath_3d_iter_with_caching, + 'iter': FiniteWordPath_3d_iter, } else: return { - 'list': FiniteWordPath_all_list, - 'str': FiniteWordPath_all_str, - 'tuple': FiniteWordPath_all_tuple, - 'callable_with_caching': FiniteWordPath_all_callable_with_caching, - 'callable': FiniteWordPath_all_callable, - 'iter_with_caching': FiniteWordPath_all_iter_with_caching, - 'iter': FiniteWordPath_all_iter, + 'list': FiniteWordPath_all_list, + 'str': FiniteWordPath_all_str, + 'tuple': FiniteWordPath_all_tuple, + 'callable_with_caching': FiniteWordPath_all_callable_with_caching, + 'callable': FiniteWordPath_all_callable, + 'iter_with_caching': FiniteWordPath_all_iter_with_caching, + 'iter': FiniteWordPath_all_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" - Returns a string representation of self. + Return a string representation of ``self``. EXAMPLES:: @@ -566,9 +566,9 @@ def __repr__(self): """ return "Word Paths over %s steps" % self.alphabet().cardinality() - def letters_to_steps(self): + def letters_to_steps(self) -> dict: r""" - Returns the dictionary mapping letters to vectors (steps). + Return the dictionary mapping letters to vectors (steps). EXAMPLES:: @@ -632,18 +632,17 @@ def __init__(self, alphabet): Word Paths on the square grid sage: P == loads(dumps(P)) True - """ - #Construction of the steps - d = [(1 ,0), (0,1), (-1,0), (0,-1)] + # Construction of the steps + d = [(1, 0), (0, 1), (-1, 0), (0, -1)] - #Construction of the class + # Construction of the class super().__init__(alphabet, steps=d) @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -661,16 +660,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_square_grid_list, - 'str': FiniteWordPath_square_grid_str, - 'tuple': FiniteWordPath_square_grid_tuple, - 'callable_with_caching': FiniteWordPath_square_grid_callable_with_caching, - 'callable': FiniteWordPath_square_grid_callable, - 'iter_with_caching': FiniteWordPath_square_grid_iter_with_caching, - 'iter': FiniteWordPath_square_grid_iter, + 'list': FiniteWordPath_square_grid_list, + 'str': FiniteWordPath_square_grid_str, + 'tuple': FiniteWordPath_square_grid_tuple, + 'callable_with_caching': FiniteWordPath_square_grid_callable_with_caching, + 'callable': FiniteWordPath_square_grid_callable, + 'iter_with_caching': FiniteWordPath_square_grid_iter_with_caching, + 'iter': FiniteWordPath_square_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -701,20 +700,19 @@ def __init__(self, alphabet): Word Paths on the triangle grid sage: P == loads(dumps(P)) True - """ K = QuadraticField(3, 'sqrt3') sqrt3 = K.gen() - #Construction of the steps - d = (vector(K, (1 ,0 )), - vector(K, (ZZ(1)/ZZ(2), sqrt3/2)), - vector(K, (ZZ(-1)/ZZ(2), sqrt3/2)), - vector(K, (-1 , 0 )), - vector(K, (ZZ(-1)/ZZ(2), -sqrt3/2 )), - vector(K, (ZZ(1)/ZZ(2), -sqrt3/2 ))) + # Construction of the steps + d = (vector(K, (1, 0)), + vector(K, (ZZ(1) / ZZ(2), sqrt3 / 2)), + vector(K, (ZZ(-1) / ZZ(2), sqrt3 / 2)), + vector(K, (-1, 0)), + vector(K, (ZZ(-1) / ZZ(2), -sqrt3 / 2)), + vector(K, (ZZ(1) / ZZ(2), -sqrt3 / 2))) - #Construction of the class + # Construction of the class super().__init__(alphabet, steps=d) self._infinite_word_class = None @@ -723,7 +721,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -741,16 +739,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_triangle_grid_list, - 'str': FiniteWordPath_triangle_grid_str, - 'tuple': FiniteWordPath_triangle_grid_tuple, - 'callable_with_caching': FiniteWordPath_triangle_grid_callable_with_caching, - 'callable': FiniteWordPath_triangle_grid_callable, - 'iter_with_caching': FiniteWordPath_triangle_grid_iter_with_caching, - 'iter': FiniteWordPath_triangle_grid_iter, + 'list': FiniteWordPath_triangle_grid_list, + 'str': FiniteWordPath_triangle_grid_str, + 'tuple': FiniteWordPath_triangle_grid_tuple, + 'callable_with_caching': FiniteWordPath_triangle_grid_callable_with_caching, + 'callable': FiniteWordPath_triangle_grid_callable, + 'iter_with_caching': FiniteWordPath_triangle_grid_iter_with_caching, + 'iter': FiniteWordPath_triangle_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -781,9 +779,8 @@ def __init__(self, alphabet): Word Paths on the hexagonal grid sage: P == loads(dumps(P)) True - """ - #Construction of the class + # Construction of the class super().__init__(alphabet) self._infinite_word_class = None @@ -792,7 +789,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -810,16 +807,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_hexagonal_grid_list, - 'str': FiniteWordPath_hexagonal_grid_str, - 'tuple': FiniteWordPath_hexagonal_grid_tuple, - 'callable_with_caching': FiniteWordPath_hexagonal_grid_callable_with_caching, - 'callable': FiniteWordPath_hexagonal_grid_callable, - 'iter_with_caching': FiniteWordPath_hexagonal_grid_iter_with_caching, - 'iter': FiniteWordPath_hexagonal_grid_iter, + 'list': FiniteWordPath_hexagonal_grid_list, + 'str': FiniteWordPath_hexagonal_grid_str, + 'tuple': FiniteWordPath_hexagonal_grid_tuple, + 'callable_with_caching': FiniteWordPath_hexagonal_grid_callable_with_caching, + 'callable': FiniteWordPath_hexagonal_grid_callable, + 'iter_with_caching': FiniteWordPath_hexagonal_grid_iter_with_caching, + 'iter': FiniteWordPath_hexagonal_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -852,8 +849,9 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(1,0,0), (0,1,0), (0,0,1), (-1,0,0), (0,-1,0), (0,0,-1)] + # Construction of the class + d = [(1, 0, 0), (0, 1, 0), (0, 0, 1), + (-1, 0, 0), (0, -1, 0), (0, 0, -1)] super().__init__(alphabet, steps=d) self._infinite_word_class = None self._finite_word_class = FiniteWordPath_cube_grid @@ -861,7 +859,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -879,15 +877,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_cube_grid_list, - 'str': FiniteWordPath_cube_grid_str, - 'tuple': FiniteWordPath_cube_grid_tuple, - 'callable_with_caching': FiniteWordPath_cube_grid_callable_with_caching, - 'callable': FiniteWordPath_cube_grid_callable, - 'iter_with_caching': FiniteWordPath_cube_grid_iter_with_caching, - 'iter': FiniteWordPath_cube_grid_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_cube_grid_str, + 'tuple': FiniteWordPath_cube_grid_tuple, + 'callable_with_caching': FiniteWordPath_cube_grid_callable_with_caching, + 'callable': FiniteWordPath_cube_grid_callable, + 'iter_with_caching': FiniteWordPath_cube_grid_iter_with_caching, + 'iter': FiniteWordPath_cube_grid_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -919,8 +917,8 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(1,1), (1,-1)] + # Construction of the class + d = [(1, 1), (1, -1)] super().__init__(alphabet, steps=d) self._infinite_word_class = None @@ -929,7 +927,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -947,15 +945,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_dyck_list, - 'str': FiniteWordPath_dyck_str, - 'tuple': FiniteWordPath_dyck_tuple, - 'callable_with_caching': FiniteWordPath_dyck_callable_with_caching, - 'callable': FiniteWordPath_dyck_callable, - 'iter_with_caching': FiniteWordPath_dyck_iter_with_caching, - 'iter': FiniteWordPath_dyck_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_dyck_str, + 'tuple': FiniteWordPath_dyck_tuple, + 'callable_with_caching': FiniteWordPath_dyck_callable_with_caching, + 'callable': FiniteWordPath_dyck_callable, + 'iter_with_caching': FiniteWordPath_dyck_iter_with_caching, + 'iter': FiniteWordPath_dyck_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -988,8 +986,8 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(0,1), (1,0)] + # Construction of the class + d = [(0, 1), (1, 0)] super().__init__(alphabet, steps=d) self._infinite_word_class = None self._finite_word_class = FiniteWordPath_north_east @@ -997,7 +995,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -1015,15 +1013,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_north_east_list, - 'str': FiniteWordPath_north_east_str, - 'tuple': FiniteWordPath_north_east_tuple, - 'callable_with_caching': FiniteWordPath_north_east_callable_with_caching, - 'callable': FiniteWordPath_north_east_callable, - 'iter_with_caching': FiniteWordPath_north_east_iter_with_caching, - 'iter': FiniteWordPath_north_east_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_north_east_str, + 'tuple': FiniteWordPath_north_east_tuple, + 'callable_with_caching': FiniteWordPath_north_east_callable_with_caching, + 'callable': FiniteWordPath_north_east_callable, + 'iter_with_caching': FiniteWordPath_north_east_iter_with_caching, + 'iter': FiniteWordPath_north_east_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -1042,9 +1040,9 @@ def __repr__(self): ####################################################################### class FiniteWordPath_all(SageObject): - def _repr_(self): + def _repr_(self) -> str: r""" - Returns a string representation of this path. + Return a string representation of this path. EXAMPLES:: @@ -1058,7 +1056,7 @@ def _repr_(self): def points(self, include_last=True): r""" - Returns an iterator yielding a list of points used to draw the path + Return an iterator yielding a list of points used to draw the path represented by this word. INPUT: @@ -1093,11 +1091,11 @@ def points(self, include_last=True): def start_point(self): r""" - Return the starting point of self. + Return the starting point of ``self``. OUTPUT: - vector + vector EXAMPLES:: @@ -1114,7 +1112,7 @@ def start_point(self): @cached_method def end_point(self): r""" - Returns the end point of the path. + Return the end point of the path. EXAMPLES:: @@ -1138,10 +1136,10 @@ def end_point(self): def directive_vector(self): r""" - Returns the directive vector of self. + Return the directive vector of ``self``. The directive vector is the vector starting at the start point - and ending at the end point of the path self. + and ending at the end point of the path ``self``. EXAMPLES:: @@ -1160,9 +1158,11 @@ def directive_vector(self): """ return self.end_point() - self.start_point() - def is_closed(self): + def is_closed(self) -> bool: r""" - Returns True if the path is closed, i.e. if the origin and the end of + Return ``True`` if the path is closed. + + A path is closed if the origin and the end of the path are equal. EXAMPLES:: @@ -1179,10 +1179,12 @@ def is_closed(self): """ return self.start_point() == self.end_point() - def is_simple(self): + def is_simple(self) -> bool: r""" - Returns True if the path is simple, i.e. if all its points are - distincts. + Return ``True`` if the path is simple. + + A path is simple if all its points are + distinct. If the path is closed, the last point is not considered. @@ -1215,9 +1217,9 @@ def is_simple(self): return False return True - def tikz_trajectory(self): + def tikz_trajectory(self) -> str: r""" - Returns the trajectory of self as a tikz str. + Return the trajectory of ``self`` as a ``tikz`` string. EXAMPLES:: @@ -1228,8 +1230,7 @@ def tikz_trajectory(self): """ from sage.misc.functional import N as n - f = lambda x: n(x,digits=3) - l = [str(tuple(map(f, pt))) for pt in self.points()] + l = (str(tuple(n(x, digits=3) for x in pt)) for pt in self.points()) return ' -- '.join(l) def projected_point_iterator(self, v=None, ring=None): @@ -1292,7 +1293,7 @@ def projected_point_iterator(self, v=None, ring=None): yield R * q def plot_projection(self, v=None, letters=None, color=None, ring=None, - size=12, kind='right'): + size=12, kind='right'): r""" Return an image of the projection of the successive points of the path into the space orthogonal to the given vector. @@ -1405,7 +1406,7 @@ def plot_projection(self, v=None, letters=None, color=None, ring=None, if color is None: from sage.plot.all import hue A = self.parent().alphabet() - color = {a: hue(A.rank(a)/float(A.cardinality())) for a in A} + color = {a: hue(A.rank(a) / float(A.cardinality())) for a in A} it = self.projected_point_iterator(v, ring=ring) if kind == 'right': next(it) @@ -1464,7 +1465,7 @@ def projected_path(self, v=None, ring=None): R = vector_on_axis_rotation_matrix(v, 0, ring=ring)[1:] d = self.parent().letters_to_steps() A = self.parent().alphabet() - nvvectors = [R*d[a] for a in A] + nvvectors = [R * d[a] for a in A] projected_parent = WordPaths(A, nvvectors) return projected_parent(self) @@ -1488,13 +1489,13 @@ def is_tangent(self): class FiniteWordPath_2d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), - fill=True, filloptions=dict(rgbcolor='red',alpha=0.2), - startpoint=True, startoptions=dict(rgbcolor='red',pointsize=100), - endarrow=True, arrowoptions=dict(rgbcolor='red',arrowsize=20,width=3), - gridlines=False, gridoptions=dict()): + def plot(self, pathoptions=dict(rgbcolor='red', thickness=3), + fill=True, filloptions=dict(rgbcolor='red', alpha=0.2), + startpoint=True, startoptions=dict(rgbcolor='red', pointsize=100), + endarrow=True, arrowoptions=dict(rgbcolor='red', arrowsize=20, width=3), + gridlines=False, gridoptions=dict()): r""" - Returns a 2d Graphics illustrating the path. + Return a 2d Graphics illustrating the path. INPUT: @@ -1579,15 +1580,15 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), #################### pts = [[RR(i) for i in x] for x in pts] - #Inside + # Inside if fill and self.is_closed(): G += polygon(pts, **filloptions) - #Startpoint + # Startpoint if startpoint: G += point(pts[0], **startoptions) - #The path itself + # The path itself if endarrow and not self.is_empty(): G += line(pts[:-1], **pathoptions) G += arrow(pts[-2], pts[-1], **arrowoptions) @@ -1597,11 +1598,11 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), G.axes(False) G.set_aspect_ratio(1) - #gridlines - ###############BUG############## - #Gridlines doesn't work fine. - #It should be gridlines="integers" - ###############BUG############## + # gridlines + # ############## BUG ############## + # Gridlines doesn't work fine. + # It should be gridlines="integers" + # ############## BUG ############## if gridlines: G = G.show(gridlines=True, **gridoptions) @@ -1609,7 +1610,7 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), def animate(self): r""" - Returns an animation object illustrating the path growing step by step. + Return an animation object illustrating the path growing step by step. EXAMPLES:: @@ -1675,17 +1676,17 @@ def animate(self): #################### #################### - #Bug: plot needs float for coordinates + # Bug: plot needs float for coordinates #################### #################### pts = [[RR(i) for i in x] for x in pts] - images = [line(pts[:i]) for i in range(1,len(pts)+1)] + images = [line(pts[:i]) for i in range(1, len(pts) + 1)] if self.is_closed(): images.append(polygon(pts)) - #Get the window of the last image + # Get the window of the last image last_image = images[-1] kwds = {} kwds['xmin'] = last_image.xmin() @@ -1699,7 +1700,7 @@ def animate(self): def plot_directive_vector(self, options=dict(rgbcolor='blue')): r""" - Returns an arrow 2d graphics that goes from the start of the path + Return an arrow 2d graphics that goes from the start of the path to the end. INPUT: @@ -1740,7 +1741,7 @@ def plot_directive_vector(self, options=dict(rgbcolor='blue')): def area(self): r""" - Returns the area of a closed path. + Return the area of a closed path. INPUT: @@ -1760,7 +1761,7 @@ def area(self): def height(self): r""" - Returns the height of self. + Return the height of ``self``. The height of a `2d`-path is merely the difference between the highest and the lowest `y`-coordinate of each @@ -1827,7 +1828,7 @@ def height_vector(self): def width(self): r""" - Returns the width of self. + Return the width of ``self``. The height of a `2d`-path is merely the difference between the rightmost and the leftmost `x`-coordinate of each @@ -1835,7 +1836,7 @@ def width(self): OUTPUT: - non negative real number + non negative real number EXAMPLES:: @@ -1894,7 +1895,7 @@ def width_vector(self): def xmin(self): r""" - Returns the minimum of the x-coordinates of the path. + Return the minimum of the x-coordinates of the path. EXAMPLES:: @@ -1917,11 +1918,11 @@ def xmin(self): sage: w.xmin() 0.000000000000000 """ - return min(x for (x,_) in self.points()) + return min(x for (x, _) in self.points()) def ymin(self): r""" - Returns the minimum of the y-coordinates of the path. + Return the minimum of the y-coordinates of the path. EXAMPLES:: @@ -1944,11 +1945,11 @@ def ymin(self): sage: w.ymin() 0.000000000000000 """ - return min(y for (_,y) in self.points()) + return min(y for (_, y) in self.points()) def xmax(self): r""" - Returns the maximum of the x-coordinates of the path. + Return the maximum of the x-coordinates of the path. EXAMPLES:: @@ -1971,11 +1972,11 @@ def xmax(self): sage: w.xmax() 4.50000000000000 """ - return max(x for (x,_) in self.points()) + return max(x for (x, _) in self.points()) def ymax(self): r""" - Returns the maximum of the y-coordinates of the path. + Return the maximum of the y-coordinates of the path. EXAMPLES:: @@ -1998,12 +1999,12 @@ def ymax(self): sage: w.ymax() 2.59807621135332 """ - return max(y for (_,y) in self.points()) + return max(y for (_, y) in self.points()) class FiniteWordPath_3d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), - startpoint=True, startoptions=dict(rgbcolor='red',size=10)): + def plot(self, pathoptions=dict(rgbcolor='red', arrow_head=True, thickness=3), + startpoint=True, startoptions=dict(rgbcolor='red', size=10)): r""" INPUT: @@ -2031,9 +2032,9 @@ def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), Graphics3d Object """ - #The following line seems not to work for 3d - #G = Graphics() - #so we draw to start a small almost invisible point instead: + # The following line seems not to work for 3d + # G = Graphics() + # so we draw to start a small almost invisible point instead: G = point([self.start_point()], size=1) pts = list(self.points()) if startpoint: @@ -2050,9 +2051,9 @@ def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), ####################################################################### class FiniteWordPath_square_grid(FiniteWordPath_2d): - def is_closed(self): + def is_closed(self) -> bool: r""" - Returns True if self represents a closed path and False otherwise. + Return whether ``self`` represents a closed path. EXAMPLES:: @@ -2073,7 +2074,7 @@ def is_closed(self): def area(self): r""" - Returns the area of a closed path. + Return the area of a closed path. INPUT: @@ -2177,10 +2178,12 @@ def _area_vh(self, x=0, y=0): x -= 1 return area // 2 - def is_simple(self): + def is_simple(self) -> bool: r""" - Returns True if the path is simple, i.e. if all its points are - distincts. + Return whether the path is simple. + + A path is simple if all its points are + distinct. If the path is closed, the last point is not considered. @@ -2217,9 +2220,9 @@ def is_simple(self): """ return super().is_simple() - def tikz_trajectory(self): + def tikz_trajectory(self) -> str: r""" - Returns the trajectory of self as a tikz str. + Return the trajectory of ``self`` as a ``tikz`` string. EXAMPLES:: @@ -2246,7 +2249,7 @@ class FiniteWordPath_triangle_grid(FiniteWordPath_2d): # redefined here with conversion to RR in order to avoid this problem def xmin(self): r""" - Returns the minimum of the x-coordinates of the path. + Return the minimum of the x-coordinates of the path. EXAMPLES:: @@ -2257,11 +2260,11 @@ def xmin(self): sage: w.xmin() -3.00000000000000 """ - return min(RR(x) for (x,_) in self.points()) + return min(RR(x) for (x, _) in self.points()) def ymin(self): r""" - Returns the minimum of the y-coordinates of the path. + Return the minimum of the y-coordinates of the path. EXAMPLES:: @@ -2272,11 +2275,11 @@ def ymin(self): sage: w.ymin() -0.866025403784439 """ - return min(RR(y) for (_,y) in self.points()) + return min(RR(y) for (_, y) in self.points()) def xmax(self): r""" - Returns the maximum of the x-coordinates of the path. + Return the maximum of the x-coordinates of the path. EXAMPLES:: @@ -2287,11 +2290,11 @@ def xmax(self): sage: w.xmax() 4.00000000000000 """ - return max(RR(x) for (x,_) in self.points()) + return max(RR(x) for (x, _) in self.points()) def ymax(self): r""" - Returns the maximum of the y-coordinates of the path. + Return the maximum of the y-coordinates of the path. EXAMPLES:: @@ -2302,7 +2305,7 @@ def ymax(self): sage: w.ymax() 8.66025403784439 """ - return max(RR(y) for (_,y) in self.points()) + return max(RR(y) for (_, y) in self.points()) # TODO: faire une verification du mot pour etre sur hexagonal grid @@ -2353,7 +2356,7 @@ class FiniteWordPath_dyck(FiniteWordPath_2d): # # ####################################################################### -##### Finite paths ##### +# #### Finite paths #### class FiniteWordPath_all_list(WordDatatype_list, FiniteWordPath_all, FiniteWord_class): r""" @@ -2416,7 +2419,7 @@ class FiniteWordPath_all_callable(WordDatatype_callable, FiniteWordPath_all, Fin pass -##### Finite paths on 2d ##### +# #### Finite paths on 2d #### class FiniteWordPath_2d_list(WordDatatype_list, FiniteWordPath_2d, FiniteWord_class): r""" @@ -2479,7 +2482,7 @@ class FiniteWordPath_2d_callable(WordDatatype_callable, FiniteWordPath_2d, Finit pass -##### Finite paths on 3d ##### +# #### Finite paths on 3d #### class FiniteWordPath_3d_list(WordDatatype_list, FiniteWordPath_3d, FiniteWord_class): r""" @@ -2542,7 +2545,7 @@ class FiniteWordPath_3d_callable(WordDatatype_callable, FiniteWordPath_3d, Finit pass -##### Finite paths on square grid ##### +# #### Finite paths on square grid #### class FiniteWordPath_square_grid_list(WordDatatype_list, FiniteWordPath_square_grid, FiniteWord_class): r""" @@ -2605,12 +2608,12 @@ class FiniteWordPath_square_grid_callable(WordDatatype_callable, FiniteWordPath_ pass -##### Unknown length paths on square grid (experimental) ##### +# #### Unknown length paths on square grid (experimental) #### -#class WordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, Word_class): +# class WordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, Word_class): # pass -##### Finite paths on triangle grid ##### +# #### Finite paths on triangle grid #### class FiniteWordPath_triangle_grid_list(WordDatatype_list, FiniteWordPath_triangle_grid, FiniteWord_class): r""" @@ -2673,7 +2676,7 @@ class FiniteWordPath_triangle_grid_callable(WordDatatype_callable, FiniteWordPat pass -##### Finite paths on hexagonal grid ##### +# #### Finite paths on hexagonal grid #### class FiniteWordPath_hexagonal_grid_list(WordDatatype_list, FiniteWordPath_hexagonal_grid, FiniteWord_class): r""" @@ -2736,7 +2739,7 @@ class FiniteWordPath_hexagonal_grid_callable(WordDatatype_callable, FiniteWordPa pass -##### Finite paths on cube grid ##### +# #### Finite paths on cube grid #### class FiniteWordPath_cube_grid_list(WordDatatype_list, FiniteWordPath_cube_grid, FiniteWord_class): r""" @@ -2799,7 +2802,7 @@ class FiniteWordPath_cube_grid_callable(WordDatatype_callable, FiniteWordPath_cu pass -##### Finite paths on north_east ##### +# #### Finite paths on north_east #### class FiniteWordPath_north_east_list(WordDatatype_list, FiniteWordPath_north_east, FiniteWord_class): r""" @@ -2862,7 +2865,7 @@ class FiniteWordPath_north_east_callable(WordDatatype_callable, FiniteWordPath_n pass -##### Finite paths on dyck ##### +# #### Finite paths on dyck #### class FiniteWordPath_dyck_list(WordDatatype_list, FiniteWordPath_dyck, FiniteWord_class): r""" diff --git a/src/sage/combinat/words/word_infinite_datatypes.py b/src/sage/combinat/words/word_infinite_datatypes.py index 9998d3487d5..604acfb3c1b 100644 --- a/src/sage/combinat/words/word_infinite_datatypes.py +++ b/src/sage/combinat/words/word_infinite_datatypes.py @@ -862,7 +862,7 @@ def __getitem__(self, key): step = 1 if key.step is None else int(key.step) # If either key.start or key.stop is negative, # then we need to expand the word. - if start < 0 or (not (stop is None) and stop < 0): + if start < 0 or (stop is not None and stop < 0): data = list(self)[key] length = None # If key.step is negative, then we need to expand a prefix. diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 72e39796965..8817a304d85 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -43,6 +43,8 @@ from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method +columns_white_list = ['knot_atlas_anon', 'knotilus_page_anon'] +columns_black_list = ['homfly_polynomial_old'] class KnotInfoColumnTypes(Enum): r""" @@ -92,6 +94,7 @@ class KnotInfoColumns(Enum): 'Quasipositive Braid', 'Multivariable Alexander Polynomial', 'HOMFLYPT Polynomial', + 'Khovanov Polynomial', 'Unoriented', 'Arc Notation', 'Linking Matrix', @@ -536,7 +539,11 @@ def _create_col_dict_sobj(self, sobj_path=None): for col in knot_column_names: name = knot_column_names[col] - if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']: + if not name and col not in columns_white_list: + # not of interest + continue + + if col in columns_black_list: # not of interest continue @@ -551,7 +558,11 @@ def _create_col_dict_sobj(self, sobj_path=None): for col in link_column_names: name = link_column_names[col] - if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']: + if not name and col not in columns_white_list: + # not of interest + continue + + if col in columns_black_list: # not of interest continue @@ -629,7 +640,7 @@ def _create_data_sobj(self, sobj_path=None): @cached_method def columns(self): r""" - Return the columns ot the databese table as instances of enum class + Return the columns ot the database table as instances of enum class :class:`KnotInfoColumns`. EXAMPLES:: @@ -737,7 +748,7 @@ def read_num_knots(self): sage: from sage.databases.knotinfo_db import KnotInfoDataBase sage: ki_db = KnotInfoDataBase() sage: ki_db.read_num_knots() # optional - database_knotinfo - 2978 + 12966 """ if not self._num_knots: self.demo_version() @@ -823,8 +834,7 @@ def _test_database(self, **options): 'homfly_polynomial': ['HOMFLY', KnotInfoColumnTypes.OnlyKnots], 'homflypt_polynomial': ['HOMFLYPT Polynomial', KnotInfoColumnTypes.OnlyLinks], 'kauffman_polynomial': ['Kauffman', KnotInfoColumnTypes.KnotsAndLinks], - 'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.KnotsAndLinks], - 'khovanov_torsion_polynomial': ['Khovanov Torsion', KnotInfoColumnTypes.OnlyKnots], + 'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.OnlyLinks], 'khovanov_unreduced_integral_polynomial': ['KH Unred Z Poly', KnotInfoColumnTypes.OnlyKnots], 'khovanov_reduced_integral_polynomial': ['KH Red Z Poly', KnotInfoColumnTypes.OnlyKnots], 'khovanov_reduced_rational_polynomial': ['KH Red Q Poly', KnotInfoColumnTypes.OnlyKnots], @@ -1020,15 +1030,15 @@ def _test_database(self, **options): ], dc.homfly_polynomial: [ '', - '(2*v^2-v^4)+(v^2)*z^2', - '(v^(-2)-1+ v^2)+ (-1)*z^2', - '(3*v^4-2*v^6)+ (4*v^4-v^6)*z^2+ (v^4)*z^4', - '(v^2+ v^4-v^6)+ (v^2+ v^4)*z^2', - '(v^(-2)-v^2+ v^4)+ (-1-v^2)*z^2', - '(2-2*v^2+ v^4)+ (1-3*v^2+ v^4)*z^2+ (-v^2)*z^4', - '(-v^(-2)+ 3-v^2)+ (-v^(-2)+ 3-v^2)*z^2+ (1)*z^4', - '(4*v^6-3*v^8)+ (10*v^6-4*v^8)*z^2+ (6*v^6-v^8)*z^4+ (v^6)*z^6', - '(v^2+ v^6-v^8)+ (v^2+ v^4+ v^6)*z^2' + '(2*v^2-v^4)+v^2*z^2', + '(v^(-2)-1+v^2)-z^2', + '(3*v^4-2*v^6)+(4*v^4-v^6)*z^2+v^4*z^4', + '(v^2+v^4-v^6)+(v^2+v^4)*z^2', + '(v^(-2)-v^2+v^4)+(-1-v^2)*z^2', + '(2-2*v^2+v^4)+(1-3*v^2+v^4)*z^2-v^2*z^4', + '(-v^(-2)+3-v^2)+(-v^(-2)+3-v^2)*z^2+z^4', + '(4*v^6-3*v^8)+(10*v^6-4*v^8)*z^2+(6*v^6-v^8)*z^4+v^6*z^6', + '(v^2+v^6-v^8)+(v^2+v^4+v^6)*z^2' ], dc.homflypt_polynomial: [ '1/(v^3*z)-1/(v*z)-z/v', @@ -1123,16 +1133,6 @@ def _test_database(self, **options): '3*z + 2*z^3', '-3*z-4*z^3-z^5'], dc.khovanov_polynomial: [ - '', - 'q^(-9)t^(-3)+q^(-5)t^(-2)+q^(-3)+q^(-1)', - 'q^(-5)t^(-2)+q^(-1)t^(-1)+q+q^(-1)+qt+q^5t^2', - 'q^(-15)t^(-5)+q^(-11)t^(-4)+q^(-11)t^(-3)+q^(-7)t^(-2)+q^(-5)+q^(-3)', - 'q^(-13)t^(-5)+q^(-9)t^(-4)+q^(-9)t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', - 'q^(-9)t^(-4)+q^(-5)t^(-3)+q^(-5)t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+q^(-1)+qt+q^5t^2', - 'q^(-11)t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+(q^(-5)+q^(-3))t^(-1)+q^(-3)+2q^(-1)+tq^(-1)+q^3t^2', - 'q^(-7)t^(-3)+(q^(-5)+q^(-3))t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+2q^(-1)+t(q+q^3)+(q^3+q^5)t^2+q^7t^3', - 'q^(-21)t^(-7)+q^(-17)t^(-6)+q^(-17)t^(-5)+q^(-13)t^(-4)+q^(-13)t^(-3)+q^(-9)t^(-2)+q^(-7)+q^(-5)', - 'q^(-17)t^(-7)+q^(-13)t^(-6)+q^(-13)t^(-5)+(q^(-11)+q^(-9))t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', '1 + q^(-2) + 1/(q^6*t^2) + 1/(q^4*t^2)', '1 + q^2 + q^4*t^2 + q^6*t^2', '1 + q^(-2) + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^6*t^2) + 1/(q^2*t)', @@ -1144,17 +1144,6 @@ def _test_database(self, **options): 'q^(-4) + q^(-2) + 1/(q^16*t^6) + 1/(q^14*t^6) + 1/(q^14*t^5) + 1/(q^12*t^4) + 1/(q^10*t^4) + 1/(q^10*t^3) + 1/(q^8*t^3) + 1/(q^8*t^2) + 1/(q^6*t^2) + 1/(q^4*t)', 'q^2 + q^4 + q^4*t + q^6*t^2 + q^8*t^2 + q^8*t^3 + q^10*t^3 + q^10*t^4 + q^12*t^4 + q^14*t^5 + q^14*t^6 + q^16*t^6', 'q^(-6) + q^(-4) + 1/(q^18*t^6) + 1/(q^16*t^6) + 1/(q^16*t^5) + 1/(q^12*t^4) + 1/(q^12*t^3) + 1/(q^8*t^2)'], - dc.khovanov_torsion_polynomial: [ - '', - 'Q^(-7)t^(-2)', - 'Q^(-3)t^(-1)+Q^3t^2', - 'Q^(-13)t^(-4)+Q^(-9)t^(-2)', - 'Q^(-11)t^(-4)+Q^(-7)t^(-2)+Q^(-5)t^(-1)', - 'Q^(-7)t^(-3)+Q^(-3)t^(-1)+Q^(-1)+Q^3t^2', - 'Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)+Q^(-3)+Qt^2', - 'Q^(-5)t^(-2)+Q^(-3)t^(-1)+Q^(-1)+Qt+Q^3t^2+Q^5t^3', - 'Q^(-19)t^(-6)+Q^(-15)t^(-4)+Q^(-11)t^(-2)', - 'Q^(-15)t^(-6)+Q^(-11)t^(-4)+Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)'], dc.khovanov_unreduced_integral_polynomial: [ '', 'q + q^(3) + t^(2) q^(5) + t^(3) q^(9) + t^(3) q^(7) T^(2)', diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 0242f456959..740d25358d8 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -1081,8 +1081,7 @@ def nth_iterate(self, P, n, **kwds): sage: f.nth_iterate(P(0, 1), 3) Traceback (most recent call last): ... - ValueError: [0, 0] does not define a point in Projective Space of - dimension 1 over Rational Field since all entries are zero + ValueError: [0, 0] does not define a valid projective point since all entries are zero :: @@ -1597,8 +1596,7 @@ def orbit(self, P, N, **kwds): sage: f.orbit(P(0, 1), 3) Traceback (most recent call last): ... - ValueError: [0, 0] does not define a point in Projective Space of - dimension 1 over Rational Field since all entries are zero + ValueError: [0, 0] does not define a valid projective point since all entries are zero sage: f.orbit(P(0, 1), 3, check=False) [(0 : 1), (0 : 0), (0 : 0), (0 : 0)] diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 7f6ad9b6843..48b77950153 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3851,7 +3851,7 @@ def ngens(self): return len(self._names) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the coordinate hyperplanes. diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index ad2f4e83c44..547d2eeb5a3 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -534,7 +534,7 @@ def ngens(self): return len(self._names) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/geometry/polyhedron/backend_field.py b/src/sage/geometry/polyhedron/backend_field.py index ca89b53b8b0..feda80cb697 100644 --- a/src/sage/geometry/polyhedron/backend_field.py +++ b/src/sage/geometry/polyhedron/backend_field.py @@ -90,7 +90,7 @@ def _is_zero(self, x): sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic sage: p._is_zero(0) # needs sage.rings.number_field sage.symbolic True - sage: p._is_zero(1/100000) # needs sage.rings.number_field + sage: p._is_zero(1/100000) # needs sage.rings.number_field sage.symbolic False """ return x == 0 diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index 4a34a6a510e..d2816aa7287 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -1511,7 +1511,7 @@ def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=No raise ValueError('You may not specify both positive_point and positive_dual_point.') self._flip_sign_of_generator = (scalar_product < 0) - def gens(self): + def gens(self) -> tuple: """ Return the generators of the quotient. diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 0c6c309721f..929fa9f31a7 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -70,6 +70,7 @@ :delim: | :meth:`~DiGraph.path_semigroup` | Return the (partial) semigroup formed by the paths of the digraph. + :meth:`~DiGraph.auslander_reiten_quiver` | Return the Auslander-Reiten quiver of ``self``. **Connectivity:** @@ -3180,6 +3181,23 @@ def path_semigroup(self): from sage.quivers.path_semigroup import PathSemigroup return PathSemigroup(self) + def auslander_reiten_quiver(self): + r""" + Return the Auslander-Reiten quiver of ``self``. + + .. SEEALSO:: + + :class:`~sage.quivers.ar_quiver.AuslanderReitenQuiver` + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: D.auslander_reiten_quiver() + Auslander-Reiten quiver of Multi-digraph on 2 vertices + """ + from sage.quivers.ar_quiver import AuslanderReitenQuiver + return AuslanderReitenQuiver(self) + # Directed Acyclic Graphs (DAGs) def topological_sort(self, implementation="default"): diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index d182f49afb1..1ddad92806c 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -649,14 +649,16 @@ def nauty_directg(self, graphs, options="", debug=False): INPUT: - - ``graphs`` -- a :class:`Graph` or an iterable containing :class:`Graph` - the graph6 string of these graphs is used as an input for ``directg``. + - ``graphs`` -- a :class:`Graph` or an iterable containing + :class:`Graph`. The graph6 string of these graphs is used as an input + for ``directg``. - ``options`` (str) -- a string passed to directg as if it was run at a system command line. Available options from directg --help:: -e | -e: specify a value or range of the total number of arcs -o orient each edge in only one direction, never both + -a only make acyclic orientations (implies -o) -f Use only the subgroup that fixes the first vertices setwise -V only output graphs with nontrivial groups (including exchange of isolated vertices). The -f option is respected. @@ -680,6 +682,19 @@ def nauty_directg(self, graphs, options="", debug=False): sage: len(list(digraphs.nauty_directg(graphs.PetersenGraph(), options="-o"))) 324 + Generate non-isomorphic acyclic orientations:: + + sage: K = graphs.CompleteGraph(4) + sage: all(d.is_directed_acyclic() for d in digraphs.nauty_directg(K, options='-a')) + True + sage: sum(1 for _ in digraphs.nauty_directg(K, options='-a')) + 1 + sage: S = graphs.StarGraph(4) + sage: all(d.is_directed_acyclic() for d in digraphs.nauty_directg(S, options='-a')) + True + sage: sum(1 for _ in digraphs.nauty_directg(S, options='-a')) + 5 + TESTS:: sage: g = digraphs.nauty_directg(graphs.PetersenGraph(), options="-o -G") diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 97c94e26574..bbe793b3625 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -928,7 +928,7 @@ def gen(self, i=0): x[i] = 1 return self.element_class(self, x) - def gens(self): + def gens(self) -> tuple: """ Return the generators of the group. @@ -1875,7 +1875,7 @@ def _repr_(self): s += '{' + ', '.join(map(str, self.gens())) + '}' return s - def gens(self): + def gens(self) -> tuple: """ Return the generators for this subgroup. diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index f1a0bf1abac..4aecfd69816 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -267,7 +267,7 @@ def gen(self, i=0): x[i] = 1 return self.element_class(self, x) - def gens(self): + def gens(self) -> tuple: """ Return the generators for the group. diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index a856f2120fe..c0a90f0c110 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -416,7 +416,7 @@ def __init__(self, cover, rels, gens): AdditiveAbelianGroup_class.__init__(self, cover, rels) self._orig_gens = tuple(self(x) for x in gens) - def gens(self): + def gens(self) -> tuple: r""" Return the specified generators for self (as a tuple). Compare ``self.smithform_gens()``. diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 4361403dccc..5fa9ccfac39 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -8,7 +8,7 @@ from .perm_gps.all import * from .generic import (discrete_log, discrete_log_rho, discrete_log_lambda, - linear_relation, multiple, multiples) + linear_relation, multiple, multiples, order_from_multiple) lazy_import('sage.groups.class_function', 'ClassFunction') diff --git a/src/sage/groups/cactus_group.py b/src/sage/groups/cactus_group.py index a12fbb88563..a4a6fe629b0 100644 --- a/src/sage/groups/cactus_group.py +++ b/src/sage/groups/cactus_group.py @@ -218,7 +218,7 @@ def group_generators(self): return Family(l, lambda x: self.element_class(self, [x])) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` as a tuple. @@ -943,7 +943,7 @@ def gen(self, i): return self.gens()[i] @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/groups/kernel_subgroup.py b/src/sage/groups/kernel_subgroup.py index 605778784e4..dec7a0be573 100644 --- a/src/sage/groups/kernel_subgroup.py +++ b/src/sage/groups/kernel_subgroup.py @@ -69,7 +69,7 @@ def _repr_(self): """ return "Kernel subgroup defined by {}".format(self._morphism) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index d2305a655b4..354882f6829 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -347,7 +347,7 @@ class ParentLibGAP(SageObject): def minimal_normal_subgroups(self): """ - Return the nontrivial minimal normal subgroups ``self``. + Return the nontrivial minimal normal subgroups of ``self``. EXAMPLES:: diff --git a/src/sage/groups/lie_gps/nilpotent_lie_group.py b/src/sage/groups/lie_gps/nilpotent_lie_group.py index 19bfb0e2288..1164f28e056 100644 --- a/src/sage/groups/lie_gps/nilpotent_lie_group.py +++ b/src/sage/groups/lie_gps/nilpotent_lie_group.py @@ -310,7 +310,7 @@ def _dRx(self): return R_a.differential(self.one()).matrix().subs(asubs) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return a tuple of elements whose one-parameter subgroups generate the Lie group. diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index ef6ba42a105..afcadc9123a 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -377,7 +377,7 @@ def __init__(self, degree, base_ring, generator_matrices, category=None): MatrixGroup_generic.__init__(self, degree, base_ring, category=category) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of the matrix group. diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 822bd576afd..70caaf80e26 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -66,6 +66,8 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, NamedMatrixGroup_generic) from sage.misc.latex import latex +from sage.misc.misc_c import prod +from sage.rings.infinity import Infinity ############################################################################### @@ -307,3 +309,49 @@ def _check_matrix(self, x, *args): else: if x.determinant() == 0: raise TypeError('matrix must non-zero determinant') + + def order(self): + """ + Return the order of ``self``. + + EXAMPLES:: + + sage: G = SL(3, GF(5)) + sage: G.order() + 372000 + + TESTS: + + Check if :trac:`36876` is fixed:: + + sage: SL(1, QQ).order() + 1 + sage: SL(2, ZZ).cardinality() + +Infinity + + Check if :trac:`35490` is fixed:: + + sage: q = 7 + sage: FqT. = GF(q)[] + sage: N = T^2+1 + sage: FqTN = QuotientRing(FqT, N*FqT) + sage: S = SL(2, FqTN) + sage: S.is_finite() + True + sage: S.order() + 117600 + """ + n = self.degree() + + if self.base_ring().is_finite(): + q = self.base_ring().order() + ord = prod(q**n - q**i for i in range(n)) + if self._special: + return ord / (q-1) + return ord + + if self._special and n == 1: + return 1 + return Infinity + + cardinality = order diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index ff7aa0ae9c2..22c44cb2312 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -817,7 +817,7 @@ def algebra_generators(self): from sage.sets.family import Family return Family(V, lambda x: d[x]) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` (as an algebra). diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index fde9556858d..0125a2273db 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -1241,7 +1241,7 @@ def structure_sheaf(self): deprecation(27848, 'The function `structure_sheaf` is deprecated. Use `self.sheaf()` instead.') return self.parent()('OO_%s' % self.name()) - def substitute(self, *args, **kwds): + def subs(self, *args, **kwds): """ Note that we have to override the substitute method so that we get the default one from Macaulay2 instead of the one provided by Element. @@ -1260,8 +1260,6 @@ def substitute(self, *args, **kwds): """ return self.__getattr__("substitute")(*args, **kwds) - subs = substitute - def _tab_completion(self): """ Return a list of tab completions for ``self``. diff --git a/src/sage/knots/all.py b/src/sage/knots/all.py index 02223efef57..d25acbda1a3 100644 --- a/src/sage/knots/all.py +++ b/src/sage/knots/all.py @@ -3,5 +3,4 @@ lazy_import('sage.knots.knot', ['Knot', 'Knots']) lazy_import('sage.knots.link', 'Link') -if DatabaseKnotInfo().is_present(): - lazy_import('sage.knots.knotinfo', ['KnotInfo', 'KnotInfoSeries']) +lazy_import('sage.knots.knotinfo', ['KnotInfo', 'KnotInfoSeries']) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index 0ef601e124c..2cc94c795fb 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -44,14 +44,12 @@ :arxiv:`Gittings, T., "Minimum Braids: A Complete Invariant of Knots and Links" `). Furthermore, note that not all columns available in the database are visible on the web -pages (see also the related note under :meth:`KnotInfoBase.khovanov_polynomial`). -It is planned to remove non-visible columns from the database in the future (see +pages. It is planned to remove non-visible columns from the database in the future (see the `Python Wrapper `__ for updated information). EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.pd_notation() [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]] @@ -103,7 +101,7 @@ sage: # optional - snappy sage: L6 = KnotInfo.L6a1_0 sage: l6s = L6.link(snappy=True); l6s - Plink failed to import tkinter. + ... sage: type(l6s) @@ -260,13 +258,21 @@ def eval_knotinfo(string, locals={}, to_tuple=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo, eval_knotinfo + sage: from sage.knots.knotinfo import eval_knotinfo sage: L = KnotInfo.L4a1_0 sage: L.braid_notation(original=True) '{3, {-2, -2, -1, 2, -1}}' sage: eval_knotinfo(_) (3, (-2, -2, -1, 2, -1)) + sage: KnotInfo.K13a_1.kauffman_polynomial() # optional - database_knotinfo # indirect doctest + Traceback (most recent call last): + ... + NotImplementedError: this value is not provided by the database """ + if not string: + # An empty string in the database Excel spreadsheet indicates that + # the property is not provided for that particular knot or link. + raise NotImplementedError('this value is not provided by the database') if to_tuple: new_string = string.replace('{', '(') new_string = new_string.replace('}', ')') @@ -276,6 +282,31 @@ def eval_knotinfo(string, locals={}, to_tuple=True): new_string = new_string.replace(';', ',') return sage_eval(new_string, locals=locals) +def knotinfo_int(string): + r""" + Preparse a string from the KnotInfo database representing an integer. + + INPUT: + + - ``string`` -- string that gives a value of some database entry + + EXAMPLES:: + + sage: from sage.knots.knotinfo import knotinfo_int + sage: knotinfo_int('7') + 7 + sage: KnotInfo.K13a_1.braid_index() # optional - database_knotinfo # indirect doctest + Traceback (most recent call last): + ... + NotImplementedError: this integer is not provided by the database + """ + if not string: + # an empty string in the Excel sheet of the database indicates that + # the property is not provided for this special knot or link. + raise NotImplementedError('this integer is not provided by the database') + else: + return int(string) + def knotinfo_bool(string): r""" Preparse a string from the KnotInfo database representing a boolean. @@ -289,14 +320,21 @@ def knotinfo_bool(string): sage: from sage.knots.knotinfo import knotinfo_bool sage: knotinfo_bool('Y') True + sage: KnotInfo.K13a_1.is_almost_alternating() # optional - database_knotinfo # indirect doctest + Traceback (most recent call last): + ... + NotImplementedError: this boolean is not provided by the database """ + if not string: + # an empty string in the Excel sheet of the database indicates that + # the property is not provided for this special knot or link. + raise NotImplementedError('this boolean is not provided by the database') if string == 'Y': return True elif string == 'N': return False raise ValueError('%s is not a KnotInfo boolean') - # --------------------------------------------------------------------------------- # KnotInfoBase # --------------------------------------------------------------------------------- @@ -307,7 +345,6 @@ class KnotInfoBase(Enum): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: [knot.name for knot in KnotInfo if knot.crossing_number() < 5] ['K0_1', 'K3_1', 'K4_1', 'L2a1_0', 'L2a1_1', 'L4a1_0', 'L4a1_1'] @@ -330,7 +367,6 @@ def __gt__(self, other): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0 < KnotInfo.L4a1_1 # indirect doctest True sage: KnotInfo.L2a1_0 < KnotInfo.K3_1 # indirect doctest @@ -351,7 +387,6 @@ def items(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: it = L.items sage: [i.name for i in it if i.name.startswith('braid')] @@ -378,7 +413,6 @@ def __getitem__(self, item): r""" EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L[L.items.alternating] 'Y' @@ -389,14 +423,14 @@ def __getitem__(self, item): sage: L[0] Traceback (most recent call last): ... - KeyError: "Item must be an instance of " + KeyError: "item must be an instance of " """ if not isinstance(item, KnotInfoColumns): - raise KeyError('Item must be an instance of %s' % (KnotInfoColumns)) + raise KeyError('item must be an instance of %s' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyLinks and self.is_knot(): - raise KeyError('Item not available for knots' % (KnotInfoColumns)) + raise KeyError('item not available for knots' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyKnots and not self.is_knot(): - raise KeyError('Item not available for links' % (KnotInfoColumns)) + raise KeyError('item not available for links' % (KnotInfoColumns)) l = db.read(item) ind = db.read_row_dict()[self.name][0] @@ -416,10 +450,9 @@ def _offset_knots(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L._offset_knots() # optional - database_knotinfo - 2978 + 12966 """ return db.read_num_knots() @@ -431,12 +464,16 @@ def _braid_group(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L._braid_group() Braid group on 3 strands """ - n = self.braid_index() + try: + n = self.braid_index() + except NotImplementedError: + bn = self.braid_notation() + n = max(abs(i) for i in bn) + 1 + if n == 1: return BraidGroup(2) else: @@ -450,7 +487,6 @@ def _homfly_pol_ring(self, var1, var2): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_1 sage: L._homfly_pol_ring('u', 'v') Multivariate Laurent Polynomial Ring in u, v over Integer Ring @@ -477,7 +513,6 @@ def pd_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.pd_notation() [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]] @@ -520,7 +555,6 @@ def dt_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.dt_notation() [[6, 8], [2, 4]] @@ -563,7 +597,6 @@ def gauss_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.gauss_notation() [[1, -3, 2, -4], [3, -1, 4, -2]] @@ -606,7 +639,6 @@ def braid_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.braid_notation() (-2, -2, -1, 2, -1) @@ -661,17 +693,23 @@ def braid_index(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.braid_index() 3 + sage: KnotInfo.K13a_1.inject() # optional - database_knotinfo + Defining K13a_1 + sage: K13a_1.braid_index() # optional - database_knotinfo + Traceback (most recent call last): + ... + NotImplementedError: this integer is not provided by the database + """ if self.is_knot(): - return int(self[self.items.braid_index]) + return knotinfo_int(self[self.items.braid_index]) else: braid_notation = self[self.items.braid_notation] braid_notation = eval_knotinfo(braid_notation) - return int(braid_notation[0]) + return knotinfo_int(braid_notation[0]) @cached_method def braid_length(self): @@ -686,12 +724,11 @@ def braid_length(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.braid_length() 3 """ - return int(self[self.items.braid_length]) + return knotinfo_int(self[self.items.braid_length]) @cached_method def braid(self): @@ -700,12 +737,13 @@ def braid(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.braid() s^3 sage: K.braid_notation() (1, 1, 1) + sage: KnotInfo.K13n_1448.braid() # optional - database_knotinfo + s0^-1*s1*s2*s3*s4*s3^2*s2^-1*s1^-1*s0*s2^-1*s1*(s3*s2)^2*s4^-1*s3*s2*s1^-1*s3*s2^-1*s3 """ return self._braid_group()(self.braid_notation()) @@ -716,7 +754,6 @@ def num_components(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L6a1_0.num_components() 2 """ @@ -737,7 +774,6 @@ def crossing_number(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.crossing_number() 4 sage: KnotInfo.K3_1.crossing_number() @@ -745,7 +781,7 @@ def crossing_number(self): sage: Link(KnotInfo.L4a1_0.braid()) Link with 2 components represented by 5 crossings """ - return int(self[self.items.crossing_number]) + return knotinfo_int(self[self.items.crossing_number]) @cached_method def determinant(self): @@ -769,7 +805,6 @@ def determinant(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.determinant() 4 sage: KnotInfo.K3_1.determinant() @@ -780,7 +815,7 @@ def determinant(self): if self.crossing_number() == 0: # see note above return 1 - return int(self[self.items.determinant]) + return knotinfo_int(self[self.items.determinant]) @cached_method def three_genus(self): @@ -797,7 +832,6 @@ def three_genus(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.three_genus() # optional - databsase_knotinfo 1 @@ -808,7 +842,7 @@ def three_genus(self): sage: KnotInfo.K5_2.link().genus() 3 """ - return int(self[self.items.three_genus]) + return knotinfo_int(self[self.items.three_genus]) @cached_method def signature(self): @@ -826,11 +860,10 @@ def signature(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.signature() # optional - databsase_knotinfo 1 """ - return int(self[self.items.signature]) + return knotinfo_int(self[self.items.signature]) @cached_method def is_knot(self): @@ -839,7 +872,6 @@ def is_knot(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L7a1_0.is_knot() # optional - database_knotinfo False sage: KnotInfo.K6_3.is_knot() @@ -855,7 +887,6 @@ def name_unoriented(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L10a122_1_0.name_unoriented() # optional - database_knotinfo 'L10a122' """ @@ -890,7 +921,6 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_1.series().inject() Defining K6 sage: [(K.name, K.symmetry_type()) for K in K6] @@ -899,7 +929,7 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` ('K6_3', 'fully amphicheiral')] """ if not self.is_knot(): - raise NotImplementedError('This is only available for knots') + raise NotImplementedError('this is only available for knots') symmetry_type = self[self.items.symmetry_type].strip() # for example K10_88 is a case with trailing whitespaces if not symmetry_type and self.crossing_number() == 0: @@ -913,7 +943,6 @@ def is_reversible(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_3.is_reversible() True """ @@ -952,26 +981,24 @@ def is_amphicheiral(self, positive=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo - sage: Kp = KnotInfo.K12a_427 # optional - database_knotinfo - sage: Kp.is_amphicheiral() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: Kp = KnotInfo.K12a_427 + sage: Kp.is_amphicheiral() False - sage: Kp.is_amphicheiral(positive=True) # optional - database_knotinfo + sage: Kp.is_amphicheiral(positive=True) True - - sage: Kn = KnotInfo.K10_88 # optional - database_knotinfo - sage: Kn.is_amphicheiral() # optional - database_knotinfo + sage: Kn = KnotInfo.K10_88 + sage: Kn.is_amphicheiral() True - sage: Kn.is_amphicheiral(positive=True) # optional - database_knotinfo + sage: Kn.is_amphicheiral(positive=True) False - sage: KnotInfo.L4a1_0.is_amphicheiral() False - sage: KnotInfo.L10n59_1.is_amphicheiral() # optional - database_knotinfo + sage: KnotInfo.L10n59_1.is_amphicheiral() True - sage: KnotInfo.L10n36_0.inject() # optional - database_knotinfo + sage: KnotInfo.L10n36_0.inject() Defining L10n36_0 - sage: L10n36_0.is_amphicheiral() is None # optional - database_knotinfo + sage: L10n36_0.is_amphicheiral() is None True """ if self.is_knot(): @@ -1023,7 +1050,6 @@ def is_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_alternating() True """ @@ -1036,7 +1062,6 @@ def is_almost_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_almost_alternating() # optional - database_knotinfo False """ @@ -1050,7 +1075,6 @@ def is_quasi_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_quasi_alternating() # optional - database_knotinfo True """ @@ -1064,7 +1088,6 @@ def is_adequate(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_adequate() # optional - database_knotinfo True """ @@ -1078,7 +1101,6 @@ def is_positive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_positive() True """ @@ -1091,7 +1113,6 @@ def is_quasipositive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_quasipositive() # optional - database_knotinfo True """ @@ -1105,7 +1126,6 @@ def is_strongly_quasipositive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_strongly_quasipositive() # optional - database_knotinfo True """ @@ -1119,7 +1139,6 @@ def is_positive_braid(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_positive_braid() # optional - database_knotinfo False """ @@ -1133,7 +1152,6 @@ def is_fibered(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_3.is_fibered() True """ @@ -1146,7 +1164,6 @@ def is_oriented(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L6a2_1.is_oriented() True """ @@ -1191,12 +1208,11 @@ def homfly_polynomial(self, var1='v', var2='z', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K3_1 = KnotInfo.K3_1 sage: PK3_1 = K3_1.homfly_polynomial(); PK3_1 -v^4 + v^2*z^2 + 2*v^2 sage: K3_1.homfly_polynomial(original=True) - '(2*v^2-v^4)+(v^2)*z^2' + '(2*v^2-v^4)+v^2*z^2' sage: PK3_1 == K3_1.link().homfly_polynomial(normalization='vz') True @@ -1289,7 +1305,6 @@ def kauffman_polynomial(self, var1='a', var2='z', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L2a1_1 sage: L.kauffman_polynomial() a^-1*z - a^-1*z^-1 + a^-2 + a^-3*z - a^-3*z^-1 @@ -1396,7 +1411,6 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Kj = K.jones_polynomial(); Kj t^2 - t - 1/t + 1/t^2 + 1 @@ -1574,7 +1588,6 @@ def alexander_polynomial(self, var='t', original=False, laurent_poly=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Ka = K.alexander_polynomial(); Ka t^2 - 3*t + 1 @@ -1650,7 +1663,6 @@ def conway_polynomial(self, var='t', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Kc = K.conway_polynomial(); Kc -t^2 + 1 @@ -1704,10 +1716,8 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, the reduced version of the homology is used - ``odd`` -- boolean (default: ``False``); if set to ``True`` the odd version of the homology is used - - ``KhoHo`` -- boolean (default: ``False`` for knots and ``True`` - for multi-component links); if set to ``True`` the data calculated - using `KhoHo `__ is used - (see the note below) + - ``KhoHo`` -- boolean (deprecated). The corresponding values have + disappeared from the database since January 2024 OUTPUT: @@ -1718,28 +1728,14 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, .. NOTE :: The data used for multi-component links were calculated with the program - `KhoHo `__. These can still be - used for knots by setting the optional argument ``KhoHo`` to ``True``, - even though they will no longer be visible on the Knot website as of - October 30, 2022. Otherwise, for knots data calculated with + `KhoHo `__.which uses the ``DT`` + notation. For knots data calculated with `KnotJob `__ are used. The latter program is more accurate in terms of orientation and reflection as it is based on ``PD`` code. - Note that in the future columns that are not visible on the web page may - also be removed in the database (see the - `Python wrapper `__ - for updated information). Therefore, the ``KhoHo`` option cannot be - guaranteed to work after upgrading the ``database_knotinfo``-SPKG. - - Furthermore, since the results of ``KhoHo`` were computed using the ``DT`` - notation, the Khovanov polynomial returned by this option belongs to the - mirror image of the given knot for a `list of 140 exceptions - `__. - EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K6_3 sage: Kk = K.khovanov_polynomial(); Kk q^7*t^3 + q^5*t^2 + q^3*t^2 + q^3*t + q*t + 2*q + 2*q^-1 + q^-1*t^-1 @@ -1802,11 +1798,11 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, sage: K0_1.khovanov_polynomial(base_ring=GF(3), reduced=True) Traceback (most recent call last): ... - ValueError: Characteristic 3 of base ring is not valid + ValueError: characteristic 3 of base ring is not valid sage: K0_1.khovanov_polynomial(base_ring=GF(3), odd=True) Traceback (most recent call last): ... - ValueError: Characteristic 3 of base ring is not valid + ValueError: characteristic 3 of base ring is not valid sage: L.khovanov_polynomial(base_ring=GF(2)) Traceback (most recent call last): ... @@ -1822,11 +1818,13 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, integral = ch == 0 and base_ring.is_field() if not self.is_knot(): # KnotJob calculated results only available for knots - KhoHo = True - if KhoHo: - # use the old results obtained by the KhoHo software khovanov_polynomial = self[self.items.khovanov_polynomial] + KhoHo = True else: + if KhoHo: + KhoHo = False + from sage.misc.superseded import deprecation + deprecation(37014, "the KhoHo option is deprecated and ignored.") if reduced: if integral: khovanov_polynomial = self[self.items.khovanov_reduced_integral_polynomial] @@ -1835,7 +1833,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, elif ch == 2: khovanov_polynomial = self[self.items.khovanov_reduced_mod2_polynomial] else: - raise ValueError('Characteristic %s of base ring is not valid' % ch) + raise ValueError('characteristic %s of base ring is not valid' % ch) elif odd: if integral: khovanov_polynomial = self[self.items.khovanov_odd_integral_polynomial] @@ -1844,7 +1842,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, elif ch == 2: khovanov_polynomial = self[self.items.khovanov_odd_mod2_polynomial] else: - raise ValueError('Characteristic %s of base ring is not valid' % ch) + raise ValueError('characteristic %s of base ring is not valid' % ch) else: khovanov_polynomial = self[self.items.khovanov_unreduced_integral_polynomial] @@ -1932,7 +1930,6 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.link() Knot represented by 3 crossings @@ -1955,7 +1952,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): sage: L.link(use_item=L.items.dt_notation) Traceback (most recent call last): ... - ValueError: Link construction using Columns.dt_notation not possible + ValueError: link construction using Columns.dt_notation not possible using ``snappy``:: @@ -2015,7 +2012,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): try: from snappy import Link except ImportError: - raise ImportError('This option demands snappy to be installed') + raise ImportError('this option demands snappy to be installed') elif self.is_knot(): from sage.knots.knot import Knot as Link else: @@ -2037,7 +2034,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): elif use_item == self.items.gauss_notation: return Knots().from_gauss_code(self.gauss_notation()) - raise ValueError('Link construction using %s not possible' % use_item) + raise ValueError('link construction using %s not possible' % use_item) @cached_method def is_unique(self): @@ -2050,7 +2047,6 @@ def is_unique(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.is_unique() True sage: KnotInfo.L5a1_0.is_unique() @@ -2104,7 +2100,6 @@ def is_recoverable(self, unique=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.inject() Defining L4a1_0 sage: L4a1_0.is_recoverable() @@ -2170,7 +2165,6 @@ def inject(self, verbose=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.inject() Defining K5_2 sage: K5_2.is_alternating() @@ -2197,7 +2191,6 @@ def series(self, oriented=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K5 = KnotInfo.K5_2.series() sage: K5(1) @@ -2232,7 +2225,6 @@ def diagram(self, single=False, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.diagram() # not tested True @@ -2262,7 +2254,6 @@ def knot_atlas_webpage(self, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.knot_atlas_webpage() # not tested True @@ -2282,7 +2273,6 @@ def knotilus_webpage(self, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.knotilus_webpage(new=1) # not tested True @@ -2516,11 +2506,11 @@ def __getitem__(self, item): """ from sage.rings.integer import Integer if not type(item) in (int, Integer): - raise ValueError('Item must be an integer') + raise ValueError('item must be an integer') l = self.list() max_item = len(l) if item < 0 or item > max_item: - raise ValueError('Item must be non negative and smaller than %s' % (max_item)) + raise ValueError('item must be non negative and smaller than %s' % (max_item)) return l[item] @@ -2539,16 +2529,16 @@ def __call__(self, item): sage: K6(2) # indirect doctest - sage: from sage.knots.knotinfo import KnotInfo - sage: KnotInfo.L8a21_0_1_0.inject() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: KnotInfo.L8a21_0_1_0.inject() Defining L8a21_0_1_0 - sage: L8a21_0_1_0.series().inject() # optional - database_knotinfo + sage: L8a21_0_1_0.series().inject() Defining L8a - sage: L8a(1) # optional - database_knotinfo + sage: L8a(1) Series of links L8a1 - sage: L8a(21)(2) == L8a21_0_1_0 # optional - database_knotinfo + sage: L8a(21)(2) == L8a21_0_1_0 True - sage: L8a(21)('010') == L8a21_0_1_0 # optional - database_knotinfo + sage: L8a(21)('010') == L8a21_0_1_0 True """ if self._name_unoriented: @@ -2559,11 +2549,11 @@ def __call__(self, item): from sage.rings.integer import Integer if not type(item) in (int, Integer): - raise ValueError('Item must be an integer') + raise ValueError('item must be an integer') l = self.list() max_item = len(l) + 1 if item < 1 or item > max_item: - raise ValueError('Item must be positive and smaller than %s' % (max_item)) + raise ValueError('item must be positive and smaller than %s' % (max_item)) return l[item-1] @@ -2615,7 +2605,6 @@ def is_recoverable(self, unique=True, max_samples=8): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.series().inject() Defining L4a sage: L4a.is_recoverable() @@ -2644,7 +2633,6 @@ def _test_recover(self, **options): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: TestSuite(KnotInfo.L5a1_0.series()).run(verbose=True) # indirec doctest running ._test_category() . . . pass running ._test_new() . . . pass diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index eb3d51a883d..36f6e22c6c8 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -3955,8 +3955,8 @@ def _knotinfo_matching_list(self): # set the limits for the KnotInfoSeries if cr > 11 and co > 1: cr = 11 - if cr > 12: - cr = 12 + if cr > 13: + cr = 13 Hp = self.homfly_polynomial(normalization='vz') @@ -3995,8 +3995,9 @@ def _knotinfo_matching_list(self): if L.pd_notation() == pd_code: return [L], True # pd_notation is unique in the KnotInfo database - if L.braid_index() <= br_ind: - if self._markov_move_cmp(L.braid()): + Lbraid = L.braid() + if Lbraid.strands() <= br_ind: + if self._markov_move_cmp(Lbraid): res.append(L) if res: @@ -4058,27 +4059,29 @@ def get_knotinfo(self, mirror_version=True, unique=True): EXAMPLES:: + sage: # optional - database_knotinfo sage: from sage.knots.knotinfo import KnotInfo sage: L = Link([[4,1,5,2], [10,4,11,3], [5,17,6,16], [7,13,8,12], ....: [18,10,19,9], [2,12,3,11], [13,21,14,20], [15,7,16,6], ....: [22,17,1,18], [8,20,9,19], [21,15,22,14]]) - sage: L.get_knotinfo() # optional - database_knotinfo + sage: L.get_knotinfo() (, True) - - sage: K = KnotInfo.K10_25 # optional - database_knotinfo - sage: l = K.link() # optional - database_knotinfo - sage: l.get_knotinfo() # optional - database_knotinfo + sage: K = KnotInfo.K10_25 + sage: l = K.link() + sage: l.get_knotinfo() (, False) - Knots with more than 12 and proper links having more than 11 crossings + Knots with more than 13 and proper links having more than 11 crossings cannot be identified. In addition non prime links or even links whose HOMFLY-PT polynomial is not irreducible cannot be identified:: sage: b, = BraidGroup(2).gens() - sage: Link(b**13).get_knotinfo() + sage: Link(b**13).get_knotinfo() # optional - database_knotinfo + (, False) + sage: Link(b**14).get_knotinfo() Traceback (most recent call last): ... - NotImplementedError: this knot having more than 12 crossings cannot be determined + NotImplementedError: this link having more than 11 crossings cannot be determined sage: Link([[1, 4, 2, 5], [3, 8, 4, 1], [5, 2, 6, 3], [6, 10, 7, 9], [10, 8, 9, 7]]) Link with 2 components represented by 5 crossings @@ -4102,17 +4105,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): Usage of option ``unique``:: - sage: l = K.link(K.items.gauss_notation) # optional - database_knotinfo - sage: l.get_knotinfo() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: l = K.link(K.items.gauss_notation) + sage: l.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l.get_knotinfo(unique=False) [(, False), (, False)] - - sage: # optional - database_knotinfo sage: k11 = KnotInfo.K11n_82.link() sage: k11m = k11.mirror_image() sage: k11mr = k11m.reverse() @@ -4121,31 +4122,26 @@ def get_knotinfo(self, mirror_version=True, unique=True): ... NotImplementedError: mirror type of this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: k11mr.get_knotinfo(unique=False) # optional - database_knotinfo + sage: k11mr.get_knotinfo(unique=False) [(, '?')] - sage: t = (1, -2, 1, 1, -2, 1, -2, -2) sage: l8 = Link(BraidGroup(3)(t)) - sage: l8.get_knotinfo() # optional - database_knotinfo + sage: l8.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l8.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l8.get_knotinfo(unique=False) [(, None), (, None)] - sage: t = (2, -3, -3, -2, 3, 3, -2, 3, 1, -2, -2, 1) sage: l12 = Link(BraidGroup(5)(t)) - sage: l12.get_knotinfo() # optional - database_knotinfo + sage: l12.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link having more than 11 crossings cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l12.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l12.get_knotinfo(unique=False) [(, '?'), (, None), (, None), @@ -4189,14 +4185,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): `__:: sage: import snappy # optional - snappy - Plink failed to import tkinter. + ... + sage: # optional - database_knotinfo snappy sage: from sage.knots.knotinfo import KnotInfoSeries - sage: KnotInfoSeries(10, True, True) # optional - database_knotinfo + sage: KnotInfoSeries(10, True, True) Series of knots K10 - sage: _.inject() # optional - database_knotinfo + sage: _.inject() Defining K10 - sage: for i in range(160, 166): # optional - database_knotinfo snappy + sage: for i in range(160, 166): ....: K = K10(i) ....: k = K.link(K.items.name, snappy=True) ....: print(k, '--->', k.sage_link().get_knotinfo()) @@ -4206,16 +4203,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): ---> (, False) ---> (, False) ---> (, False) - - sage: snappy.Link('10_166') # optional - snappy + sage: snappy.Link('10_166') - sage: _.sage_link().get_knotinfo() # optional - database_knotinfo snappy + sage: _.sage_link().get_knotinfo() (, True) Another pair of confusion (see the corresponding `Warning `__):: - sage: # optional - snappy + sage: # optional - database_knotinfo snappy sage: Ks10_86 = snappy.Link('10_86') sage: Ks10_83 = snappy.Link('10_83') sage: Ks10_86.sage_link().get_knotinfo() @@ -4344,9 +4340,9 @@ def answer_list(l): uniq_txt = (' uniquely', non_unique_hint) cr = len(self.pd_code()) - if self.is_knot() and cr > 12: + if self.is_knot() and cr > 13: # we cannot not be sure if this link is recorded in the KnotInfo database - raise NotImplementedError('this knot having more than 12 crossings cannot be%s determined%s' % uniq_txt) + raise NotImplementedError('this knot having more than 13 crossings cannot be%s determined%s' % uniq_txt) if not self.is_knot() and cr > 11: # we cannot not be sure if this link is recorded in the KnotInfo database @@ -4389,20 +4385,21 @@ def is_isotopic(self, other): sage: l1.is_isotopic(l3) False + sage: # optional - database_knotinfo sage: from sage.knots.knotinfo import KnotInfo - sage: L = KnotInfo.L7a7_0_0 # optional - database_knotinfo - sage: L.series(oriented=True).inject() # optional - database_knotinfo + sage: L = KnotInfo.L7a7_0_0 + sage: L.series(oriented=True).inject() Defining L7a7 - sage: L == L7a7(0) # optional - database_knotinfo + sage: L == L7a7(0) True - sage: l = L.link() # optional - database_knotinfo - sage: l.is_isotopic(L7a7(1).link()) # optional - database_knotinfo + sage: l = L.link() + sage: l.is_isotopic(L7a7(1).link()) Traceback (most recent call last): ... NotImplementedError: comparison not possible! - sage: l.is_isotopic(L7a7(2).link()) # optional - database_knotinfo + sage: l.is_isotopic(L7a7(2).link()) True - sage: l.is_isotopic(L7a7(3).link()) # optional - database_knotinfo + sage: l.is_isotopic(L7a7(3).link()) False """ from sage.misc.verbose import verbose diff --git a/src/sage/libs/arb/types.pxd b/src/sage/libs/arb/types.pxd index 7a9a2fe1706..85969f380b8 100644 --- a/src/sage/libs/arb/types.pxd +++ b/src/sage/libs/arb/types.pxd @@ -29,12 +29,8 @@ from sage.libs.flint.types cimport ( acb_mat_t, acb_poly_struct, acb_poly_t, - acb_poly_ptr, - acb_poly_srcptr, acb_calc_integrate_opt_struct, acb_calc_integrate_opt_t, acb_calc_func_t, arb_poly_struct, - arb_poly_t, - arb_poly_ptr, - arb_poly_srcptr) + arb_poly_t) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index a4942781c5e..f2667d9ceca 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -1,18 +1,18 @@ """ Library interface to Embeddable Common Lisp (ECL) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Nils Bruin # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** -#This version of the library interface prefers to convert ECL integers and -#rationals to Sage types Integer and Rational. These parts could easily be -#adapted to work with pure Python types. +# This version of the library interface prefers to convert ECL integers and +# rationals to Sage types Integer and Rational. These parts could easily be +# adapted to work with pure Python types. from libc.stdlib cimport abort from libc.signal cimport SIGINT, SIGBUS, SIGFPE, SIGSEGV @@ -26,13 +26,13 @@ from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from cpython.object cimport Py_EQ, Py_NE -#it would be preferrable to let bint_symbolp wrap an efficient macro -#but the macro provided in object.h doesn't seem to work +# it would be preferrable to let bint_symbolp wrap an efficient macro +# but the macro provided in object.h doesn't seem to work cdef bint bint_symbolp(cl_object obj) noexcept: return not(cl_symbolp(obj) == ECL_NIL) -#these type predicates are only provided in "cl_*" form, so we wrap them -#with the proper type cast. +# these type predicates are only provided in "cl_*" form, so we wrap them +# with the proper type cast. cdef bint bint_numberp(cl_object obj) noexcept: return not(cl_numberp(obj) == ECL_NIL) @@ -77,24 +77,24 @@ cdef cl_object string_to_object(char * s) noexcept: # chained in a "free list" for quick allocation (and if the free list is empty # upon allocating a node, the array needs to be extended) -cdef cl_object insert_node_after(cl_object node,cl_object value) noexcept: - cdef cl_object next,newnode +cdef cl_object insert_node_after(cl_object node, cl_object value) noexcept: + cdef cl_object next, newnode - next=cl_cadr(node) - newnode=cl_cons(value,cl_cons(next,node)) - cl_rplaca(cl_cdr(node),newnode) + next = cl_cadr(node) + newnode = cl_cons(value, cl_cons(next, node)) + cl_rplaca(cl_cdr(node), newnode) if next != ECL_NIL: - cl_rplacd(cl_cdr(next),newnode) + cl_rplacd(cl_cdr(next), newnode) return newnode cdef void remove_node(cl_object node) noexcept: cdef cl_object next, prev - next=cl_cadr(node) - prev=cl_cddr(node) + next = cl_cadr(node) + prev = cl_cddr(node) if next != ECL_NIL: - cl_rplacd(cl_cdr(next),prev) + cl_rplacd(cl_cdr(next), prev) if prev != ECL_NIL: - cl_rplaca(cl_cdr(prev),next) + cl_rplaca(cl_cdr(prev), next) # our global list of pointers. This will be a pointer to a sentinel node, # after which all new nodes can be inserted. list_of_object gets initialised @@ -102,13 +102,15 @@ cdef void remove_node(cl_object node) noexcept: cdef cl_object list_of_objects -cdef cl_object read_from_string_clobj #our own error catching reader +cdef cl_object read_from_string_clobj # our own error catching reader cdef cl_object make_unicode_string_clobj cdef cl_object unicode_string_codepoints_clobj cdef bint ecl_has_booted = 0 -cdef char *argv = "sage" #we need a dummy argv for cl_boot (we just don't give any parameters) +cdef char *argv = "sage" +# we need a dummy argv for cl_boot (we just don't give any parameters) + # ECL signal handling @@ -135,6 +137,7 @@ def test_sigint_before_ecl_sig_on(): # We should never get here. abort() + def test_ecl_options(): """ Print an overview of the ECL options @@ -214,6 +217,7 @@ def test_ecl_options(): print('ECL_OPT_SET_GMP_MEMORY_FUNCTIONS = {0}'.format( ecl_get_option(ECL_OPT_SET_GMP_MEMORY_FUNCTIONS))) + def init_ecl(): r""" Internal function to initialize ecl. Do not call. @@ -253,23 +257,23 @@ def init_ecl(): for i in range(1, 32): sigaction(i, NULL, &sage_action[i]) - #initialize ECL + # initialize ECL ecl_set_option(ECL_OPT_SIGNAL_HANDLING_THREAD, 0) safe_cl_boot(1, &argv) - #save signal handler from ECL + # save signal handler from ECL sigaction(SIGINT, NULL, &ecl_sigint_handler) sigaction(SIGBUS, NULL, &ecl_sigbus_handler) sigaction(SIGFPE, NULL, &ecl_sigfpe_handler) sigaction(SIGSEGV, NULL, &ecl_sigsegv_handler) - #and put the Sage signal handlers back - for i in range(1,32): + # and put the Sage signal handlers back + for i in range(1, 32): sigaction(i, &sage_action[i], NULL) - #initialise list of objects and bind to global variable + # initialise list of objects and bind to global variable # *SAGE-LIST-OF-OBJECTS* to make it rooted in the reachable tree for the GC - list_of_objects=cl_cons(ECL_NIL,cl_cons(ECL_NIL,ECL_NIL)) + list_of_objects=cl_cons(ECL_NIL, cl_cons(ECL_NIL, ECL_NIL)) cl_set(string_to_object(b"*SAGE-LIST-OF-OBJECTS*"), list_of_objects) # We define our own error catching eval, apply and funcall/ @@ -277,13 +281,14 @@ def init_ecl(): # ever turn out to be a bottle neck, it should be easy to properly # compile them. - read_from_string_clobj=cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) + read_from_string_clobj = cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) - conditions_to_handle_clobj=ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) - insert_node_after(list_of_objects,conditions_to_handle_clobj) + conditions_to_handle_clobj = ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) + insert_node_after(list_of_objects, conditions_to_handle_clobj) ecl_has_booted = 1 + cdef ecl_string_to_python(cl_object s) noexcept: if bint_base_string_p(s): return char_to_str(ecl_base_string_pointer_safe(s)) @@ -342,7 +347,7 @@ cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: cdef cl_object ret, error = NULL ecl_sig_on() - ret = safe_cl_apply(&error,func,args) + ret = safe_cl_apply(&error, func, args) ecl_sig_off() if error != NULL: @@ -357,9 +362,10 @@ cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: cdef cl_object ecl_safe_read_string(char * s) except NULL: cdef cl_object o o = ecl_cstring_to_base_string_or_nil(s) - o = ecl_safe_funcall(read_from_string_clobj,o) + o = ecl_safe_funcall(read_from_string_clobj, o) return o + def shutdown_ecl(): r""" Shut down ecl. Do not call. @@ -375,8 +381,9 @@ def shutdown_ecl(): """ cl_shutdown() -#this prints the objects that sage wants the GC to keep track of. -#these should be all non-immediate EclObject wrapped objects + +# this prints the objects that sage wants the GC to keep track of. +# these should be all non-immediate EclObject wrapped objects def print_objects(): r""" Print GC-protection list @@ -411,6 +418,7 @@ def print_objects(): if c == ECL_NIL: break + cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: # conversion of a python object into an ecl object # most conversions are straightforward. Noteworthy are: @@ -420,9 +428,9 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: # otherwise creates a simple-string cdef bytes s - cdef cl_object L, ptr, o + cdef cl_object L, o - if isinstance(pyobj,bool): + if isinstance(pyobj, bool): if pyobj: return ECL_T else: @@ -451,7 +459,7 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: else: return o elif isinstance(pyobj, bytes): - s=pyobj + s = pyobj if read_strings: return ecl_safe_read_string(s) else: @@ -460,16 +468,16 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: - return ecl_bignum_from_mpz( (pyobj).value ) + return ecl_bignum_from_mpz((pyobj).value) elif isinstance(pyobj, Rational): return ecl_make_ratio( - python_to_ecl( (pyobj).numerator(), read_strings ), - python_to_ecl( (pyobj).denominator(), read_strings )) + python_to_ecl((pyobj).numerator(), read_strings), + python_to_ecl((pyobj).denominator(), read_strings)) elif isinstance(pyobj, EclObject): return (pyobj).obj elif isinstance(pyobj, list): L = ECL_NIL - for i in range(len(pyobj)-1,-1,-1): + for i in range(len(pyobj) - 1, -1, -1): L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L elif isinstance(pyobj, tuple): @@ -477,7 +485,7 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ECL_NIL else: L = python_to_ecl(pyobj[-1], read_strings) - for i in range(len(pyobj)-2,-1,-1): + for i in range(len(pyobj) - 2, -1, -1): L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L else: @@ -524,12 +532,12 @@ cdef ecl_to_python(cl_object o) noexcept: s = cl_write_to_string(1, o) return ecl_string_to_python(s) -#Maxima's BFLOAT multiprecision float type can be read with: -#def bfloat_to_python(e): -# prec=Integer(str(e.car().cddr().car())) -# mant=Integer(str(e.cdr().car())) -# exp=Integer(str(e.cddr().car())) -# return 2^(exp-prec)*mant +# Maxima's BFLOAT multiprecision float type can be read with: +# def bfloat_to_python(e): +# prec=Integer(str(e.car().cddr().car())) +# mant=Integer(str(e.cdr().car())) +# exp=Integer(str(e.cddr().car())) +# return 2^(exp-prec)*mant cdef class EclObject: r""" @@ -614,15 +622,15 @@ cdef class EclObject: EclObjects translate to themselves, so one can mix:: - sage: EclObject([1,2,EclObject([3])]) + sage: EclObject([1,2, EclObject([3])]) Calling an EclObject translates into the appropriate LISP ``apply``, where the argument is transformed into an EclObject itself, so one can flexibly apply LISP functions:: - sage: car=EclObject("car") - sage: cdr=EclObject("cdr") + sage: car = EclObject("car") + sage: cdr = EclObject("cdr") sage: car(cdr([1,2,3])) @@ -660,16 +668,16 @@ cdef class EclObject: """ - cdef cl_object obj #the wrapped object - cdef cl_object node #linked list pointer: car(node) == obj + cdef cl_object obj # the wrapped object + cdef cl_object node # linked list pointer: car(node) == obj cdef void set_obj(EclObject self, cl_object o) noexcept: if self.node: remove_node(self.node) - self.node=NULL - self.obj=o + self.node = NULL + self.obj = o if not(bint_fixnump(o) or bint_characterp(o) or bint_nullp(o)): - self.node=insert_node_after(list_of_objects,o) + self.node=insert_node_after(list_of_objects, o) def __init__(self, *args): r""" @@ -724,7 +732,7 @@ cdef class EclObject: EXAMPLES:: sage: from sage.libs.ecl import * - sage: L=EclObject([1,2,("three",'"four"')]) + sage: L = EclObject([1,2,("three",'"four"')]) sage: L.python() [1, 2, ('THREE', '"four"')] @@ -763,7 +771,7 @@ cdef class EclObject: '' """ - return "" + return "" def __str__(self): r""" @@ -827,7 +835,7 @@ cdef class EclObject: """ lispargs = EclObject(list(args)) - return ecl_wrap(ecl_safe_apply(self.obj,(lispargs).obj)) + return ecl_wrap(ecl_safe_apply(self.obj, (lispargs).obj)) def __richcmp__(left, right, int op): r""" @@ -853,20 +861,20 @@ cdef class EclObject: """ if op == Py_EQ: - if not(isinstance(left,EclObject) and isinstance(right,EclObject)): + if not(isinstance(left, EclObject) and isinstance(right, EclObject)): return False else: - return bint_equal((left).obj,(right).obj) + return bint_equal((left).obj, (right).obj) elif op == Py_NE: - if not(isinstance(left,EclObject) and isinstance(right,EclObject)): + if not(isinstance(left, EclObject) and isinstance(right, EclObject)): return True else: - return not(bint_equal((left).obj,(right).obj)) + return not(bint_equal((left).obj, (right).obj)) - #Common lisp only seems to be able to compare numeric and string types - #and does not have generic routines for doing that. - #we could dispatch based on type here, but that seems - #inappropriate for an *interface*. + # Common lisp only seems to be able to compare numeric and string types + # and does not have generic routines for doing that. + # we could dispatch based on type here, but that seems + # inappropriate for an *interface*. raise NotImplementedError("EclObjects can only be compared for equality") def __iter__(self): @@ -942,7 +950,7 @@ cdef class EclObject: raise RuntimeError("ECL runtime error") return ecl_wrap(o) - def cons(self,EclObject d): + def cons(self, EclObject d): r""" apply cons to self and argument and return the result. @@ -955,9 +963,9 @@ cdef class EclObject: """ - return ecl_wrap(cl_cons(self.obj,d.obj)) + return ecl_wrap(cl_cons(self.obj, d.obj)) - def rplaca(self,EclObject d): + def rplaca(self, EclObject d): r""" Destructively replace car(self) with d. @@ -977,8 +985,7 @@ cdef class EclObject: raise TypeError("rplaca can only be applied to a cons") cl_rplaca(self.obj, d.obj) - - def rplacd(self,EclObject d): + def rplacd(self, EclObject d): r""" Destructively replace cdr(self) with d. @@ -1340,13 +1347,13 @@ cdef class EclListIterator: self.current = ecl_wrap(ECL_NIL) return r -#input: a cl-object. Output: EclObject wrapping that. +# input: a cl-object. Output: EclObject wrapping that. cdef EclObject ecl_wrap(cl_object o) noexcept: cdef EclObject obj = EclObject.__new__(EclObject) obj.set_obj(o) return obj -#convenience routine to more easily evaluate strings +# convenience routine to more easily evaluate strings cpdef EclObject ecl_eval(str s) noexcept: r""" Read and evaluate string in Lisp and return the result @@ -1370,7 +1377,7 @@ cpdef EclObject ecl_eval(str s) noexcept: """ cdef cl_object o - o=ecl_safe_eval(python_to_ecl(s, True)) + o = ecl_safe_eval(python_to_ecl(s, True)) return ecl_wrap(o) init_ecl() diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 30e286cea16..419c23cbd70 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -569,7 +569,7 @@ def saturate(self, bound=-1, lower=2): self.__two_descent_data().saturate(bound, lower) self.__saturate = bound - def gens(self): + def gens(self) -> list: """ Return a list of the generators for the Mordell-Weil group. diff --git a/src/sage/libs/flint/flint_sage.pyx b/src/sage/libs/flint/flint_sage.pyx index 7ebbc1a7571..a3c2e40a09b 100644 --- a/src/sage/libs/flint/flint_sage.pyx +++ b/src/sage/libs/flint/flint_sage.pyx @@ -1,4 +1,9 @@ # distutils: extra_compile_args = -D_XPG6 + +# WARNING: src/sage/libs/flint/flint_sage.pyx is generated from +# src/sage_setup/autogen/flint/templates/flint_sage.pyx.template; +# please make sure that you are modifying the correct file! + """ Flint imports diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index 560a1827ed2..fcfe660a1f6 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -1,3 +1,7 @@ +/* WARNING: src/sage/libs/flint/flint_wrap.h is generated from + * src/sage_setup/autogen/flint/templates/flint_wrap.h.template + * please make sure that you are modifying the correct file! */ + #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H /* Using flint headers together in the same module as headers from diff --git a/src/sage/libs/flint/types.pxd b/src/sage/libs/flint/types.pxd index 8d23a1d480d..9e227fd87b0 100644 --- a/src/sage/libs/flint/types.pxd +++ b/src/sage/libs/flint/types.pxd @@ -1,5 +1,9 @@ # distutils: depends = flint/acb.h flint/acb_calc.h flint/acb_dft.h flint/acb_dirichlet.h flint/acb_elliptic.h flint/acb_hypgeom.h flint/acb_mat.h flint/acb_modular.h flint/acb_poly.h flint/acf.h flint/aprcl.h flint/arb.h flint/arb_calc.h flint/arb_fmpz_poly.h flint/arb_fpwrap.h flint/arb_hypgeom.h flint/arb_mat.h flint/arb_poly.h flint/arf.h flint/arith.h flint/bernoulli.h flint/bool_mat.h flint/ca.h flint/ca_ext.h flint/ca_field.h flint/ca_mat.h flint/ca_poly.h flint/ca_vec.h flint/calcium.h flint/d_mat.h flint/d_vec.h flint/dirichlet.h flint/dlog.h flint/double_extras.h flint/double_interval.h flint/fexpr.h flint/fexpr_builtin.h flint/fft.h flint/flint.h flint/fmpq.h flint/fmpq_mat.h flint/fmpq_mpoly.h flint/fmpq_mpoly_factor.h flint/fmpq_poly.h flint/fmpq_vec.h flint/fmpz.h flint/fmpz_extras.h flint/fmpz_factor.h flint/fmpz_lll.h flint/fmpz_mat.h flint/fmpz_mod.h flint/fmpz_mod_mat.h flint/fmpz_mod_mpoly.h flint/fmpz_mod_mpoly_factor.h flint/fmpz_mod_poly.h flint/fmpz_mod_poly_factor.h flint/fmpz_mod_vec.h flint/fmpz_mpoly.h flint/fmpz_mpoly_factor.h flint/fmpz_mpoly_q.h flint/fmpz_poly.h flint/fmpz_poly_factor.h flint/fmpz_poly_mat.h flint/fmpz_poly_q.h flint/fmpz_vec.h flint/fmpzi.h flint/fq.h flint/fq_default.h flint/fq_default_mat.h flint/fq_default_poly.h flint/fq_default_poly_factor.h flint/fq_embed.h flint/fq_mat.h flint/fq_nmod.h flint/fq_nmod_embed.h flint/fq_nmod_mat.h flint/fq_nmod_mpoly.h flint/fq_nmod_mpoly_factor.h flint/fq_nmod_poly.h flint/fq_nmod_poly_factor.h flint/fq_nmod_vec.h flint/fq_poly.h flint/fq_poly_factor.h flint/fq_vec.h flint/fq_zech.h flint/fq_zech_embed.h flint/fq_zech_mat.h flint/fq_zech_poly.h flint/fq_zech_poly_factor.h flint/fq_zech_vec.h flint/gr.h flint/gr_generic.h flint/gr_mat.h flint/gr_mpoly.h flint/gr_poly.h flint/gr_special.h flint/gr_vec.h flint/hypgeom.h flint/long_extras.h flint/mag.h flint/mpf_mat.h flint/mpf_vec.h flint/mpfr_mat.h flint/mpfr_vec.h flint/mpn_extras.h flint/mpoly.h flint/nf.h flint/nf_elem.h flint/nmod.h flint/nmod_mat.h flint/nmod_mpoly.h flint/nmod_mpoly_factor.h flint/nmod_poly.h flint/nmod_poly_factor.h flint/nmod_poly_mat.h flint/nmod_types.h flint/nmod_vec.h flint/padic.h flint/padic_mat.h flint/padic_poly.h flint/partitions.h flint/perm.h flint/profiler.h flint/qadic.h flint/qfb.h flint/qqbar.h flint/qsieve.h flint/thread_pool.h flint/ulong_extras.h +# WARNING: src/sage/libs/flint/types.pxd is generated from +# src/sage_setup/autogen/flint/templates/types.pxd.template +# please make sure that you are modifying the correct file! + """ Declarations for FLINT types """ @@ -129,10 +133,10 @@ cdef extern from "flint_wrap.h": ctypedef arb_mat_struct arb_mat_t[1] ctypedef struct arb_poly_struct: - pass + arb_ptr coeffs + long alloc + long length ctypedef arb_poly_struct[1] arb_poly_t - ctypedef arb_poly_struct * arb_poly_ptr - ctypedef const arb_poly_struct * arb_poly_srcptr # flint/arb_calc.h @@ -165,10 +169,10 @@ cdef extern from "flint_wrap.h": # flint/acb_poly.h ctypedef struct acb_poly_struct: - pass + acb_ptr coeffs + long alloc + long length ctypedef acb_poly_struct[1] acb_poly_t - ctypedef acb_poly_struct * acb_poly_ptr - ctypedef const acb_poly_struct * acb_poly_srcptr # flint/acb_calc.h ctypedef struct acb_calc_integrate_opt_struct: diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index dc642bf0f4e..d6c1d911067 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -39,9 +39,9 @@ from sage.misc.misc_c cimport normalize_index from sage.categories.fields import Fields from sage.categories.integral_domains import IntegralDomains +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.rings import Rings -from sage.rings.ring cimport CommutativeRing -from sage.rings.ring import is_Ring import sage.rings.abc from sage.rings.integer_ring import is_IntegerRing @@ -1660,7 +1660,7 @@ cdef class Matrix(sage.structure.element.Matrix): [---] [2 4] """ - if not is_Ring(ring): + if ring not in Rings(): raise TypeError("ring must be a ring") if ring is self._base_ring: @@ -5323,7 +5323,7 @@ cdef class Matrix(sage.structure.element.Matrix): [ -x*y*x*y x*y*x + x*y^2 x*y*x - x*y^2] """ # derived classes over a commutative base *just* overload _lmul_ (!!) - if isinstance(self._base_ring, CommutativeRing): + if self._base_ring in CommutativeRings(): return self._lmul_(left) cdef Py_ssize_t r,c x = self._base_ring(left) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 5d57530fa47..3f0ddb6d84a 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -13613,8 +13613,9 @@ cdef class Matrix(Matrix1): if max_location != -1: perm[k], perm[max_location] = perm[max_location], perm[k] M.swap_rows(k, max_location) + inv = M.get_unsafe(k, k).inverse() for j in range(k+1, m): - scale = -M.get_unsafe(j, k)/M.get_unsafe(k, k) + scale = -M.get_unsafe(j, k) * inv M.set_unsafe(j, k, -scale) for p in range(k+1, n): M.set_unsafe(j, p, M.get_unsafe(j, p) + scale*M.get_unsafe(k, p)) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index b54ea0c2afb..2ad9c008731 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -93,9 +93,11 @@ from sage.matrix.args cimport SparseEntry, MatrixArgs_init from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack, new_gen from cypari2.paridecl cimport * +from sage.libs.pari import pari from sage.libs.pari.convert_gmp cimport INT_to_mpz from sage.libs.pari.convert_flint cimport (_new_GEN_from_fmpz_mat_t, _new_GEN_from_fmpz_mat_t_rotate90, integer_matrix) +from sage.libs.pari.convert_sage_matrix import gen_to_sage_matrix ######################################################### from sage.arith.multi_modular cimport MultiModularBasis @@ -2953,10 +2955,10 @@ cdef class Matrix_integer_dense(Matrix_dense): def LLL(self, delta=None, eta=None, algorithm="fpLLL:wrapper", fp=None, prec=0, early_red=False, use_givens=False, use_siegel=False, transformation=False, **kwds): r""" - Return LLL reduced or approximated LLL reduced lattice `R` for this - matrix interpreted as a lattice. + Return LLL-reduced or approximated LLL reduced matrix `R` of the lattice + generated by the rows of self. - A lattice `(b_1, b_2, ..., b_d)` is `(\delta, \eta)`-LLL-reduced + A set of vectors `(b_1, b_2, ..., b_d)` is `(\delta, \eta)`-LLL-reduced if the two following conditions hold: - For any `i > j`, we have `\lvert \mu_{i,j} \rvert \leq \eta`. @@ -2974,23 +2976,26 @@ cdef class Matrix_integer_dense(Matrix_dense): complexity is only guaranteed for `\delta < 1`. Not every algorithm admits the case `\delta = 1`. - The lattice is returned as a matrix. Also the rank (and the - determinant) of ``self`` are cached if those are computed during - the reduction. Note that in general this only happens when - ``self.rank() == self.ncols()`` and the exact algorithm is used. + If the matrix has a nonzero kernel, the LLL-reduced matrix will contain + zero rows, so that the output has the same dimensions as the input. The + transformation matrix is always invertible over the integers. + + Also the rank (and the determinant) of ``self`` are cached if those are + computed during the reduction. Note that in general this only happens + when ``self.rank() == self.ncols()`` and the exact algorithm is used. INPUT: - ``delta`` -- (default: ``0.99``) `\delta` parameter as described - above + above, ignored by pari - ``eta`` -- (default: ``0.501``) `\eta` parameter as described above, - ignored by NTL + ignored by NTL and pari - ``algorithm`` -- string; one of the algorithms listed below (default: ``"fpLLL:wrapper"``). - - ``fp`` -- floating point number implementation: + - ``fp`` -- floating point number implementation, ignored by pari: - ``None`` -- NTL's exact reduction or fpLLL's wrapper - ``'fp'`` -- double precision: NTL's FP or fpLLL's double @@ -2999,17 +3004,17 @@ cdef class Matrix_integer_dense(Matrix_dense): - ``'xd'`` -- extended exponent: NTL's XD or fpLLL's dpe - ``'rr'`` -- arbitrary precision: NTL's RR or fpLLL's MPFR - - ``prec`` -- (default: auto choose) precision, ignored by NTL + - ``prec`` -- (default: auto choose) precision, ignored by NTL and pari - ``early_red`` -- (default: ``False``) perform early reduction, - ignored by NTL + ignored by NTL and pari - ``use_givens`` -- (default: ``False``) use Givens orthogonalization. Only applies to approximate reduction using NTL. This is slower but generally more stable. - ``use_siegel`` -- (default: ``False``) use Siegel's condition - instead of Lovász's condition, ignored by NTL + instead of Lovász's condition, ignored by NTL and pari - ``transformation`` -- (default: ``False``) also return transformation matrix. @@ -3032,6 +3037,8 @@ cdef class Matrix_integer_dense(Matrix_dense): - ``'fpLLL:wrapper'`` - fpLLL's automatic choice (default). + - ``'pari'`` - pari's qflll. + OUTPUT: A matrix over the integers. @@ -3081,14 +3088,32 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 10, 20) sage: R, U = A.LLL(transformation=True) - sage: U*A == R + sage: U * A == R True - sage: A = random_matrix(ZZ, 10, 20) sage: R, U = A.LLL(algorithm="NTL:LLL", transformation=True) - sage: U*A == R + sage: U * A == R True + sage: R, U = A.LLL(algorithm="pari", transformation=True) + sage: U * A == R + True + + Example with a nonzero kernel:: + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: M.LLL()[0:2] + [0 0 0] + [0 0 0] + + sage: M.LLL(algorithm="NTL:LLL")[0:2] + [0 0 0] + [0 0 0] + + sage: M.LLL(algorithm="pari")[0:2] + [0 0 0] + [0 0 0] + TESTS:: sage: matrix(ZZ, 0, 0).LLL() @@ -3119,6 +3144,17 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 0, 0) sage: R, U = A.LLL(transformation=True) + Test rank caching: + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: R = M.LLL(algorithm="NTL:LLL") + sage: M._cache + {'rank': 2} + sage: M._clear_cache() + sage: R = M.LLL(algorithm="pari") + sage: M._cache + {'rank': 2} + .. NOTE:: See :mod:`sage.libs.ntl.ntl_mat_ZZ.ntl_mat_ZZ.LLL` and @@ -3135,7 +3171,9 @@ cdef class Matrix_integer_dense(Matrix_dense): else: return self - U = None + r = None # rank + cdef Matrix_integer_dense R = None # LLL-reduced matrix + cdef Matrix_integer_dense U = None # transformation matrix tm = verbose("LLL of %sx%s matrix (algorithm %s)"%(self.nrows(), self.ncols(), algorithm)) import sage.libs.ntl.all @@ -3191,7 +3229,7 @@ cdef class Matrix_integer_dense(Matrix_dense): if algorithm == "NTL:LLL": if transformation: - r, det2, U = A.LLL(a,b, verbose=verb, return_U=True) + r, det2, UNTL = A.LLL(a,b, verbose=verb, return_U=True) else: r, det2 = A.LLL(a,b, verbose=verb) det2 = ZZ(det2) @@ -3224,23 +3262,23 @@ cdef class Matrix_integer_dense(Matrix_dense): raise TypeError("algorithm %s not supported"%algorithm) if isinstance(r, tuple): - r, U = r + r, UNTL = r if transformation: U = self.new_matrix(self.nrows(), self.nrows(), - entries=[ZZ(z) for z in U.list()]) + entries=[ZZ(z) for z in UNTL.list()]) r = ZZ(r) - R = self.new_matrix( + R = self.new_matrix( entries=[ZZ(z) for z in A.list()]) - self.cache("rank",r) elif algorithm.startswith('fpLLL:'): from fpylll import LLL, IntegerMatrix A = IntegerMatrix.from_matrix(self) + Ufplll = None if transformation: - U = IntegerMatrix(A.nrows, A.nrows) + Ufplll = IntegerMatrix(A.nrows, A.nrows) method = algorithm.replace("fpLLL:","") if verb: @@ -3250,14 +3288,28 @@ cdef class Matrix_integer_dense(Matrix_dense): if early_red: kwds["flags"] = kwds.get("flags", LLL.DEFAULT) | LLL.EARLY_RED - LLL.reduction(A, delta=delta, eta=eta, method=method, float_type=fp, precision=prec, U=U) + LLL.reduction(A, delta=delta, eta=eta, method=method, float_type=fp, precision=prec, U=Ufplll) R = A.to_matrix(self.new_matrix()) if transformation: - U = U.to_matrix(self.new_matrix(U.nrows, U.ncols)) + U = Ufplll.to_matrix(self.new_matrix(Ufplll.nrows, Ufplll.ncols)) + + elif algorithm == 'pari': + # call pari with flag=4: kernel+image + # pari uses column convention: need to transpose the matrices + A = integer_matrix(self._matrix, 1) + K, T = A.qflll(4) + r = ZZ(T.length()) + # TODO: there is no optimized matrix converter pari -> sage + U = gen_to_sage_matrix(pari.matconcat([K, T]).mattranspose()) + R = U * self + else: raise TypeError("algorithm %s not supported"%algorithm) verbose("LLL finished", tm) + if r is not None: + self.cache("rank", r) + R.cache("rank", r) if transformation: return R, U else: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 940358b2c81..2744af3efd0 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -482,10 +482,12 @@ class MatrixSpace(UniqueRepresentation, Parent): Full MatrixSpace of 10 by 5 dense matrices over Integer Ring sage: MatrixSpace(ZZ,10,5).category() Category of infinite enumerated finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ,10,10).category() Category of infinite enumerated finite dimensional algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ,10).category() Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) @@ -649,10 +651,12 @@ def __init__(self, base_ring, nrows, ncols, sparse, implementation): Full MatrixSpace of 10 by 5 dense matrices over Integer Ring sage: MatrixSpace(ZZ,10,5).category() Category of infinite enumerated finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ,10,10).category() Category of infinite enumerated finite dimensional algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ,10).category() Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) diff --git a/src/sage/matroids/circuits_matroid.pxd b/src/sage/matroids/circuits_matroid.pxd new file mode 100644 index 00000000000..2de06bd16ab --- /dev/null +++ b/src/sage/matroids/circuits_matroid.pxd @@ -0,0 +1,31 @@ +from sage.matroids.matroid cimport Matroid +from sage.matroids.set_system cimport SetSystem + +cdef class CircuitsMatroid(Matroid): + cdef frozenset _groundset # _E + cdef int _matroid_rank # _R + cdef SetSystem _C # circuits + cdef dict _k_C # k-circuits (k=len) + cdef bint _nsc_defined + cpdef groundset(self) noexcept + cpdef _rank(self, X) noexcept + cpdef full_rank(self) noexcept + cpdef _is_independent(self, F) noexcept + cpdef _max_independent(self, F) noexcept + cpdef _circuit(self, F) noexcept + + # enumeration + cpdef bases(self) noexcept + cpdef circuits(self, k=*) noexcept + cpdef nonspanning_circuits(self) noexcept + cpdef no_broken_circuits_sets(self, ordering=*) noexcept + + # properties + cpdef girth(self) noexcept + cpdef is_paving(self) noexcept + + # isomorphism + cpdef _is_isomorphic(self, other, certificate=*) noexcept + + # verification + cpdef is_valid(self) noexcept diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx new file mode 100644 index 00000000000..bbcf6ce8319 --- /dev/null +++ b/src/sage/matroids/circuits_matroid.pyx @@ -0,0 +1,750 @@ +r""" +Circuits matroids + +Matroids are characterized by a list of circuits, which are minimal dependent +sets. The ``CircuitsMatroid`` class implements matroids using this information +as data. + +A ``CircuitsMatroid`` can be created from another matroid or from a list of +circuits. For a full description of allowed inputs, see +:class:`below `. It is +recommended to use the :func:`Matroid() ` +function for a more flexible way of constructing a ``CircuitsMatroid`` and +other classes of matroids. For direct access to the ``CircuitsMatroid`` +constructor, run:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + +AUTHORS: + +- Giorgos Mousa (2023-12-23): initial version +""" + +# **************************************************************************** +# Copyright (C) 2023 Giorgos Mousa +# +# 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.structure.richcmp cimport rich_to_bool, richcmp +from sage.matroids.matroid cimport Matroid +from sage.matroids.set_system cimport SetSystem +from cpython.object cimport Py_EQ, Py_NE + + +cdef class CircuitsMatroid(Matroid): + r""" + A matroid defined by its circuits. + + INPUT: + + - ``M`` -- a matroid (default: ``None``) + - ``groundset`` -- a list (default: ``None``); the groundset of the matroid + - ``circuits`` -- a list (default: ``None``); the collection of circuits of + the matroid + - ``nsc_defined`` -- boolean (default: ``False``); whether the matroid was + defined by its nonspanning circuits + + .. NOTE:: + + For a more flexible means of input, use the ``Matroid()`` function. + """ + + # necessary (__init__, groundset, _rank) + + def __init__(self, M=None, groundset=None, circuits=None, nsc_defined=False): + """ + Initialization of the matroid. See class docstring for full + documentation. + + TESTS:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Fano()) + sage: TestSuite(M).run() + """ + if M is not None: + self._groundset = frozenset(M.groundset()) + self._C = SetSystem(list(M.groundset()), frozenset([frozenset(C) for C in M.circuits()])) + else: + self._groundset = frozenset(groundset) + self._C = SetSystem(list(groundset), frozenset([frozenset(C) for C in circuits])) + # k-circuits + self._k_C = {} + for C in self._C: + try: + self._k_C[len(C)] += [C] + except KeyError: + self._k_C[len(C)] = [] + self._k_C[len(C)] += [C] + self._matroid_rank = self.rank(self._groundset) + self._nsc_defined = nsc_defined + + cpdef groundset(self) noexcept: + """ + Return the groundset of the matroid. + + The groundset is the set of elements that comprise the matroid. + + OUTPUT: a set + + EXAMPLES:: + + sage: M = matroids.Theta(2) + sage: sorted(M.groundset()) + ['x0', 'x1', 'y0', 'y1'] + """ + return self._groundset + + cpdef _rank(self, X) noexcept: + """ + Return the rank of a set ``X``. + + This method does no checking on ``X``, and ``X`` may be assumed to have + the same interface as ``frozenset``. + + INPUT: + + - ``X`` -- an object with Python's ``frozenset`` interface + + OUTPUT: an integer; the rank of ``X`` in the matroid + + EXAMPLES:: + + sage: M = matroids.Theta(3) + sage: M._rank(['x1', 'y0', 'y2']) + 2 + """ + return len(self._max_independent(X)) + + # optional + + cpdef full_rank(self) noexcept: + r""" + Return the rank of the matroid. + + The *rank* of the matroid is the size of the largest independent + subset of the groundset. + + OUTPUT: an integer; the rank of the matroid + + EXAMPLES:: + + sage: M = matroids.Theta(20) + sage: M.full_rank() + 20 + """ + return self._matroid_rank + + cpdef _is_independent(self, F) noexcept: + """ + Test if input is independent. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()`` + + OUTPUT: boolean + + EXAMPLES:: + + sage: M = matroids.Theta(4) + sage: M._is_independent(['y0', 'y1', 'y3', 'x2']) + False + sage: M._is_independent(['y0', 'y2', 'y3', 'x2']) + True + """ + cdef set I = set(F) + cdef int s = len(F) + for i in self._k_C: + if i <= s: + for C in self._k_C[i]: + if C <= I: + return False + return True + + cpdef _max_independent(self, F) noexcept: + """ + Compute a maximal independent subset. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()`` + + OUTPUT: a frozenset; a maximal independent subset of ``X`` + + EXAMPLES:: + + sage: M = matroids.Theta(6) + sage: len(M._max_independent(M.groundset())) + 6 + """ + cdef set I = set(F) + for i in self._k_C: + for C in self._k_C[i]: + if i <= len(I) and i > 0: + if C <= I: + e = next(iter(C)) + I.remove(e) + + return frozenset(I) + + cpdef _circuit(self, F) noexcept: + """ + Return a minimal dependent subset. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()``. + + OUTPUT: a frozenset; a circuit contained in ``X``, if it exists. + Otherwise an error is raised. + + EXAMPLES:: + + sage: M = matroids.Theta(4) + sage: sorted(M._circuit(['y0', 'y1', 'y3', 'x2'])) + ['x2', 'y0', 'y1', 'y3'] + sage: M._circuit(['y0', 'y2', 'y3', 'x2']) + Traceback (most recent call last): + ... + ValueError: no circuit in independent set + """ + cdef set I = set(F) + for C in self._C: + if C <= I: + return C + raise ValueError("no circuit in independent set") + + cpdef _is_isomorphic(self, other, certificate=False) noexcept: + """ + Test if ``self`` is isomorphic to ``other``. + + INPUT: + + - ``other`` -- a matroid + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, and, if ``certificate=True``, a dictionary giving the + isomorphism or ``None`` + + EXAMPLES:: + + sage: M = matroids.Spike(3) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: N = BasisMatroid(M) + sage: M.is_isomorphic(N) + True + sage: N = matroids.catalog.Vamos() + sage: M.is_isomorphic(N) + False + + .. NOTE:: + + Internal version that does no input checking. + """ + if certificate: + return self._is_isomorphic(other), self._isomorphism(other) + N = CircuitsMatroid(other) + return self._C._isomorphism(N._C) is not None + + # representation + + def _repr_(self): + """ + Return a string representation of the matroid. + + EXAMPLES:: + + sage: matroids.Theta(10) + Theta_10: Matroid of rank 10 on 20 elements with 490 circuits + sage: matroids.catalog.NonDesargues() + NonDesargues: Matroid of rank 3 on 10 elements with 9 nonspanning circuits + """ + if self._nsc_defined: + return Matroid._repr_(self) + " with " + str(len(self.nonspanning_circuits())) + " nonspanning circuits" + else: + return Matroid._repr_(self) + " with " + str(len(self._C)) + " circuits" + + # comparison + + def __hash__(self): + r""" + Return an invariant of the matroid. + + This function is called when matroids are added to a set. It is very + desirable to override it so it can distinguish matroids on the same + groundset, which is a very typical use case! + + .. WARNING:: + + This method is linked to __richcmp__ (in Cython) and __cmp__ or + __eq__/__ne__ (in Python). If you override one, you should + (and in Cython: MUST) override the other! + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = CircuitsMatroid(matroids.catalog.Vamos()) + sage: hash(M) == hash(N) + True + sage: O = CircuitsMatroid(matroids.catalog.NonVamos()) + sage: hash(M) == hash(O) + False + """ + return hash(tuple([self.groundset(), frozenset(self._C)])) + + def __richcmp__(left, right, int op): + r""" + Compare two matroids. + + We take a very restricted view on equality: the objects need to be of + the exact same type (so no subclassing) and the internal data need to + be the same. For CircuitsMatroids, this means that the groundsets and + the sets of circuits of the two matroids are equal. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Pappus()) + sage: N = CircuitsMatroid(matroids.catalog.NonPappus()) + sage: M == N + False + sage: N = Matroid(circuits=M.circuits()) + sage: M == N + True + """ + cdef CircuitsMatroid lt, rt + if op not in [Py_EQ, Py_NE]: + return NotImplemented + if type(left) is not type(right): + return NotImplemented + lt = left + rt = right + if lt.groundset() != rt.groundset(): + return rich_to_bool(op, 1) + if lt.full_rank() != rt.full_rank(): + return rich_to_bool(op, 1) + return richcmp(frozenset(lt._C), frozenset(rt._C), op) + + # copying, loading, saving + + def __copy__(self): + """ + Create a shallow copy. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = copy(M) # indirect doctest + sage: M == N + True + sage: M.groundset() is N.groundset() + True + """ + N = CircuitsMatroid(groundset=[], circuits=[]) + N._groundset = self._groundset + N._C = self._C + N._k_C = self._k_C + N._nsc_defined = self._nsc_defined + N._matroid_rank = self._matroid_rank + N.rename(self.get_custom_name()) + return N + + def __deepcopy__(self, memo=None): + """ + Create a deep copy. + + .. NOTE:: + + Since matroids are immutable, a shallow copy normally suffices. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = deepcopy(M) # indirect doctest + sage: M == N + True + sage: M.groundset() is N.groundset() + False + """ + if memo is None: + memo = {} + from copy import deepcopy + # Since matroids are immutable, N cannot reference itself in correct code, so no need to worry about the recursion. + N = CircuitsMatroid(groundset=deepcopy(self._groundset, memo), circuits=deepcopy(frozenset(self._C), memo)) + N.rename(deepcopy(self.get_custom_name(), memo)) + return N + + def __reduce__(self): + """ + Save the matroid for later reloading. + + OUTPUT: + + A tuple ``(unpickle, (version, data))``, where ``unpickle`` is the + name of a function that, when called with ``(version, data)``, + produces a matroid isomorphic to ``self``. ``version`` is an integer + (currently 0) and ``data`` is a tuple ``(E, C, name)`` where ``E`` is + the groundset, ``C`` is the list of circuits, and ``name`` is a custom + name. + + EXAMPLES:: + + sage: M = matroids.Theta(5) + sage: M == loads(dumps(M)) # indirect doctest + True + sage: M.reset_name() + sage: loads(dumps(M)) + Matroid of rank 5 on 10 elements with 45 circuits + """ + import sage.matroids.unpickling + data = (self._groundset, frozenset(self._C), self.get_custom_name()) + version = 0 + return sage.matroids.unpickling.unpickle_circuits_matroid, (version, data) + + # enumeration + + cpdef bases(self) noexcept: + r""" + Return the bases of the matroid. + + OUTPUT: a SetSystem + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: len(M.bases()) + 6 + """ + cdef SetSystem B, NSC + cdef bint flag + B = SetSystem(list(self.groundset())) + NSC = self.nonspanning_circuits() + from itertools import combinations + for S in combinations(self._groundset, self._matroid_rank): + flag = True + S = frozenset(S) + for C in NSC: + if C <= S: + flag = False + break + if flag: + B.append(S) + return B + + def bases_iterator(self): + r""" + Return an iterator over the bases of the matroid. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: it = M.bases_iterator() + sage: it.__next__() + frozenset({0, 1}) + sage: sorted(M.bases_iterator(), key=str) + [frozenset({0, 1}), + frozenset({0, 2}), + frozenset({0, 3}), + frozenset({1, 2}), + frozenset({1, 3}), + frozenset({2, 3})] + """ + from itertools import combinations + cdef SetSystem NSC = self.nonspanning_circuits() + for B in combinations(self._groundset, self._matroid_rank): + B = frozenset(B) + if not any(C <= B for C in NSC): + yield B + + cpdef circuits(self, k=None) noexcept: + """ + Return the circuits of the matroid. + + INPUT: + + - ``k`` -- an integer (optional); the length of the circuits + + OUTPUT: a SetSystem + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: M.circuits() + Iterator over a system of subsets + sage: list(M.circuits(0)) + [] + sage: sorted(M.circuits(3), key=str) + [frozenset({0, 1, 2}), + frozenset({0, 1, 3}), + frozenset({0, 2, 3}), + frozenset({1, 2, 3})] + """ + cdef SetSystem C + C = SetSystem(list(self.groundset())) + if k is not None: + if k in self._k_C: + for c in self._k_C[k]: + C.append(c) + else: + for i in self._k_C: + for c in self._k_C[i]: + C.append(c) + return C + + def circuits_iterator(self, k=None): + """ + Return an iterator over the circuits of the matroid. + + INPUT: + + - ``k`` -- an integer (optional); the length of the circuits + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: sum(1 for C in M.circuits_iterator()) + 4 + sage: list(M.circuits_iterator(0)) + [] + sage: sorted(M.circuits_iterator(3), key=str) + [frozenset({0, 1, 2}), + frozenset({0, 1, 3}), + frozenset({0, 2, 3}), + frozenset({1, 2, 3})] + """ + if k is not None: + if k in self._k_C: + for C in self._k_C[k]: + yield C + else: + for i in self._k_C: + for C in self._k_C[i]: + yield C + + cpdef nonspanning_circuits(self) noexcept: + """ + Return the nonspanning circuits of the matroid. + + OUTPUT: a SetSystem + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: M.nonspanning_circuits() + Iterator over a system of subsets + """ + cdef list NSC = [] + for i in self._k_C: + if i <= self.rank(): + NSC.extend(self._k_C[i]) + return SetSystem(list(self.groundset()), NSC) + + def nonspanning_circuits_iterator(self): + """ + Return an iterator over the nonspanning circuits of the matroid. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: list(M.nonspanning_circuits_iterator()) + [] + """ + for i in self._k_C: + if i <= self.rank(): + for C in self._k_C[i]: + yield C + + cpdef no_broken_circuits_sets(self, ordering=None) noexcept: + r""" + Return the no broken circuits (NBC) sets of ``self``. + + An NBC set is a subset `A` of the ground set under some total + ordering `<` such that `A` contains no broken circuit. + + INPUT: + + - ``ordering`` -- a total ordering of the groundset given as a list + + OUTPUT: a list of frozensets + + EXAMPLES:: + + sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: SimplicialComplex(M.no_broken_circuits_sets()) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5)} + sage: SimplicialComplex(M.no_broken_circuits_sets([5,4,3,2,1])) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)} + + :: + + sage: M = Matroid(circuits=[[1,2,3], [1,4,5], [2,3,4,5]]) + sage: SimplicialComplex(M.no_broken_circuits_sets([5,4,3,2,1])) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 3, 5), (2, 3, 5), (2, 4, 5), (3, 4, 5)} + + TESTS:: + + sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: C1 = SimplicialComplex(M.no_broken_circuits_sets()) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: M = BasisMatroid(Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]])) + sage: C2 = SimplicialComplex(M.no_broken_circuits_sets()) + sage: C1 == C2 + True + """ + if ordering is None: + ordering = sorted(self.groundset(), key=str) + else: + if frozenset(ordering) != self.groundset(): + raise ValueError("not an ordering of the groundset") + + # compute broken circuits + cdef list BC = [] + for C in self._C: + for e in ordering: + if e in C: + BC.append(C - set([e])) + break + + cdef list F = [] # broken circuit complex facets + for B in self.bases(): + flag = True + for bc in BC: + if bc <= B: + flag = False + break + if flag: + F.append(B) + + from sage.topology.simplicial_complex import SimplicialComplex + return [frozenset(f) for f in SimplicialComplex(F).face_iterator()] + + # properties + + cpdef girth(self) noexcept: + r""" + Return the girth of the matroid. + + The girth is the size of the smallest circuit. In case the matroid has + no circuits the girth is `\infty`. + + EXAMPLES:: + + sage: matroids.Theta(10).girth() + 3 + + REFERENCES: + + [Oxl2011]_, p. 327. + """ + return min(self._k_C, default=float('inf')) + + cpdef is_paving(self) noexcept: + """ + Return if ``self`` is paving. + + A matroid is paving if each of its circuits has size `r` or `r+1`. + + OUTPUT: boolean + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: M.is_paving() + True + """ + return self.girth() >= self.rank() + + # verification + + cpdef is_valid(self) noexcept: + r""" + Test if ``self`` obeys the matroid axioms. + + For a matroid defined by its circuits, we check the circuit axioms. + + OUTPUT: boolean + + EXAMPLES:: + + sage: C = [[1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + True + sage: C = [[1,2], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[3,6], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[3,6], [1, 2, 3], [3, 4, 5], [1, 2, 6], [6, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + True + sage: C = [[], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[1, 2, 3], [3, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + """ + from itertools import combinations_with_replacement + cdef int i, j, k, S_len + cdef frozenset C1, C2, C3, I12, U12 + cdef bint flag + for (i, j) in combinations_with_replacement(sorted(self._k_C), 2): + # loop through all circuit length pairs (i, j) with i <= j + for C1 in self._k_C[i]: + if not C1: # the empty set can't be a circuit + return False + for C2 in self._k_C[j]: + I12 = C1 & C2 + if not I12: # C1 and C2 are disjoint; nothing to test + continue + if len(I12) == len(C1): + if len(C1) == len(C2): # they are the same circuit + break + # C1 < C2; a circuit can't be a subset of another circuit + return False + # check circuit elimination axiom + U12 = C1 | C2 + S_len = len(U12) - 1 # the size of S below + for e in I12: + flag = False + S = U12 - {e} + for k in self._k_C: + if k <= S_len: + for C3 in self._k_C[k]: + if C3 <= S: + flag = True + break + if flag: + break + if not flag: + return False + return True diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index 40a39a4eda8..137f4c28230 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -112,6 +112,7 @@ import sage.matroids.matroid import sage.matroids.basis_exchange_matroid from .rank_matroid import RankMatroid +from .circuits_matroid import CircuitsMatroid from .circuit_closures_matroid import CircuitClosuresMatroid from .basis_matroid import BasisMatroid from .linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid @@ -302,21 +303,23 @@ def Matroid(groundset=None, data=None, **kwds): :: sage: M1 = Matroid(groundset='abc', circuits=['bc']) - sage: M2 = Matroid(bases=['ab', 'ac']) - sage: M1 == M2 - True A matroid specified by a list of circuits gets converted to a - :class:`BasisMatroid ` + :class:`CircuitsMatroid ` internally:: + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M2 = CircuitsMatroid(Matroid(bases=['ab', 'ac'])) + sage: M1 == M2 + True + sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd', ....: 'bcd']) sage: type(M) - <... 'sage.matroids.basis_matroid.BasisMatroid'> + Strange things can happen if the input does not satisfy the circuit - axioms, and these are not always caught by the + axioms, and these can be caught by the :meth:`is_valid() ` method. So always check whether your input makes sense! @@ -324,11 +327,7 @@ def Matroid(groundset=None, data=None, **kwds): sage: M = Matroid('abcd', circuits=['ab', 'acd']) sage: M.is_valid() - True - sage: [sorted(C) for C in M.circuits()] # random - [['a']] - - + False #. Graph: @@ -762,17 +761,7 @@ def Matroid(groundset=None, data=None, **kwds): groundset = set() for C in data: groundset.update(C) - # determine the rank by computing a basis element - b = set(groundset) - for C in data: - I = b.intersection(C) - if len(I) >= len(C): - b.discard(I.pop()) - rk = len(b) - # Construct the basis matroid of appropriate rank. Note: slow! - BB = [frozenset(B) for B in combinations(groundset, rk) - if not any(frozenset(C).issubset(B) for C in data)] - M = BasisMatroid(groundset=groundset, bases=BB) + M = CircuitsMatroid(groundset=groundset, circuits=data) # Nonspanning circuits: elif key == 'nonspanning_circuits': @@ -796,10 +785,13 @@ def Matroid(groundset=None, data=None, **kwds): break if flag: B += [list(b)] - M = BasisMatroid(groundset=groundset, bases=B) + # convert to circuits matroid defined by non-spanning circuits + M = CircuitsMatroid( + BasisMatroid(groundset=groundset, bases=B), + nsc_defined=True + ) # Graphs: - elif key == 'graph': from sage.graphs.graph import Graph diff --git a/src/sage/matroids/database_collections.py b/src/sage/matroids/database_collections.py index 7480df75e62..c3276be61d6 100644 --- a/src/sage/matroids/database_collections.py +++ b/src/sage/matroids/database_collections.py @@ -250,8 +250,8 @@ def OxleyMatroids(): REFERENCES: - These matroids are the nonparametrized matroids that appear in the - Appendix ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). + These matroids are the nonparametrized matroids that appear in the Appendix + ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). """ from sage.matroids.database_matroids import ( U24, U25, U35, K4, Whirl3, Q6, P6, U36, R6, diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py index 1272efefff5..0a75ce22283 100644 --- a/src/sage/matroids/database_matroids.py +++ b/src/sage/matroids/database_matroids.py @@ -198,8 +198,8 @@ def Whirl3(): sage: W.automorphism_group().is_transitive() False - For all elements `e`, neither `\mathcal{W}_3 \setminus \{e\}` nor - `\mathcal{W}_3 / \{e\}` is `3`-connected:: + For all elements `e`, neither `\mathcal{W}_3 \setminus \{e\}` nor `\mathcal{W}_3 / \{e\}` + is `3`-connected:: sage: import random sage: e = random.choice(list(W.groundset())) @@ -266,8 +266,7 @@ def P6(): {2: {{'a', 'b', 'c'}}, 3: {{'a', 'b', 'c', 'd', 'e', 'f'}}} sage: len(set(M.nonspanning_circuits()).difference(M.nonbases())) == 0 True - sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, - ....: nrows=5)).has_minor(M) + sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, nrows=5)).has_minor(M) False sage: M.is_valid() True @@ -445,8 +444,8 @@ def NonFano(): sage: M = matroids.catalog.NonFano(); M NonFano: Ternary matroid of rank 3 on 7 elements, type 0- sage: setprint(M.nonbases()) - [{'a', 'b', 'f'}, {'a', 'c', 'e'}, {'a', 'd', 'g'}, {'b', 'c', 'd'}, - {'b', 'e', 'g'}, {'c', 'f', 'g'}] + [{'a', 'b', 'f'}, {'a', 'c', 'e'}, {'a', 'd', 'g'}, + {'b', 'c', 'd'}, {'b', 'e', 'g'}, {'c', 'f', 'g'}] sage: M.delete('f').is_isomorphic(matroids.CompleteGraphic(4)) True sage: M.delete('g').is_isomorphic(matroids.CompleteGraphic(4)) @@ -479,8 +478,8 @@ def NonFanoDual(): sage: sorted(M.groundset()) ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - Every single-element contraction of `(F_7^-)^*` is isomorphic to `M(K_4)` - or `\mathcal{W}^3`:: + Every single-element contraction of `(F_7^-)^*` is isomorphic to `M(K_4)` or + `\mathcal{W}^3`:: sage: import random sage: e = random.choice(list(M.groundset())) @@ -585,8 +584,8 @@ def AG32(): sage: M.equals(M.dual()) True - Every single-element deletion is isomorphic to `F_7^*` and every - single-element contraction is isomorphic to `F_7`:: + Every single-element deletion is isomorphic to `F_7^*` and every single-element + contraction is isomorphic to `F_7`:: sage: F7 = matroids.catalog.Fano() sage: F7D = matroids.catalog.FanoDual() @@ -640,8 +639,8 @@ def AG32prime(): sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - Every single-element deletion is isomorphic to `F_7^*` or `(F_7^-)^*` and - every single-element contraction is isomorphic to `F_7` or `F_7^-`:: + Every single-element deletion is isomorphic to `F_7^*` or `(F_7^-)^*` and every + single-element contraction is isomorphic to `F_7` or `F_7^-`:: sage: F7 = matroids.catalog.Fano() sage: F7D = matroids.catalog.FanoDual() @@ -740,8 +739,7 @@ def F8(): {'a', 'e', 'f', 'h'}, {'b', 'c', 'd', 'g'}, {'b', 'c', 'e', 'f'}, {'c', 'd', 'e', 'h'}, {'c', 'f', 'g', 'h'}, {'d', 'e', 'f', 'g'}}, 4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}} - sage: D = get_nonisomorphic_matroids([M.contract(i) - ....: for i in M.groundset()]) + sage: D = get_nonisomorphic_matroids([M.contract(i) for i in M.groundset()]) sage: len(D) 3 sage: [N.is_isomorphic(matroids.catalog.Fano()) for N in D] @@ -891,8 +889,7 @@ def S8(): sage: M.is_graphic() False sage: D = get_nonisomorphic_matroids( - ....: list(matroids.catalog.Fano().linear_coextensions( - ....: cosimple=True))) + ....: list(matroids.catalog.Fano().linear_coextensions(cosimple=True))) sage: len(D) 2 sage: [N.is_isomorphic(M) for N in D] @@ -1055,8 +1052,7 @@ def P8(): P8: Ternary matroid of rank 4 on 8 elements, type 2+ sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, - ....: nrows=5)).has_minor(M) + sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, nrows=5)).has_minor(M) False sage: M.bicycle_dimension() 2 @@ -1099,8 +1095,7 @@ def P8pp(): 4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}} sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - sage: len(get_nonisomorphic_matroids([M.contract(i) - ....: for i in M.groundset()])) + sage: len(get_nonisomorphic_matroids([M.contract(i) for i in M.groundset()])) 1 sage: M.is_valid() # long time True @@ -1181,8 +1176,7 @@ def K33dual(): sage: M = matroids.catalog.K33dual(); M M*(K3, 3): Regular matroid of rank 4 on 9 elements with 81 bases - sage: any(N.is_3connected() - ....: for N in M.linear_extensions(simple=True)) + sage: any(N.is_3connected() for N in M.linear_extensions(simple=True)) False sage: M.is_valid() True @@ -1308,7 +1302,7 @@ def R9(): EXAMPLES:: sage: M = matroids.catalog.R9(); M - R9: Matroid of rank 3 on 9 elements with 69 bases + R9: Matroid of rank 3 on 9 elements with 15 nonspanning circuits sage: M.is_valid() True sage: len(M.nonspanning_circuits()) @@ -1471,7 +1465,7 @@ def R10(): R10: Regular matroid of rank 5 on 10 elements with 162 bases sage: cct = [] sage: for i in M.circuits(): - ....: cct.append(len(i)) + ....: cct.append(len(i)) sage: Set(cct) {4, 6} sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) @@ -1527,7 +1521,8 @@ def NonDesargues(groundset=None): EXAMPLES:: - sage: M = matroids.catalog.NonDesargues() + sage: M = matroids.catalog.NonDesargues(); M + NonDesargues: Matroid of rank 3 on 10 elements with 9 nonspanning circuits sage: M.is_valid() True sage: M.automorphism_group().is_transitive() @@ -1728,9 +1723,7 @@ def Wheel(r, field=None, ring=None): will be a regular matroid. - ``field`` -- any field; same as ``ring``, but only fields are allowed - OUTPUT: - - the rank-`r` wheel matroid, represented as a regular matroid + OUTPUT: the rank-`r` wheel matroid, represented as a regular matroid EXAMPLES:: @@ -1802,9 +1795,7 @@ def Whirl(r): - ``r`` -- a positive integer; the rank of the desired matroid. - OUTPUT: - - the rank-`r` whirl matroid, represented as a ternary matroid + OUTPUT: the rank-`r` whirl matroid, represented as a ternary matroid EXAMPLES:: @@ -1813,8 +1804,8 @@ def Whirl(r): sage: M.is_valid() True sage: M.tutte_polynomial() - x^5 + y^5 + 5*x^4 + 5*x^3*y + 5*x^2*y^2 + 5*x*y^3 + 5*y^4 + 10*x^3 + - 15*x^2*y + 15*x*y^2 + 10*y^3 + 10*x^2 + 15*x*y + 10*y^2 + 5*x + 5*y + x^5 + y^5 + 5*x^4 + 5*x^3*y + 5*x^2*y^2 + 5*x*y^3 + 5*y^4 + 10*x^3 + 15*x^2*y + + 15*x*y^2 + 10*y^3 + 10*x^2 + 15*x*y + 10*y^2 + 5*x + 5*y sage: M.is_isomorphic(matroids.Wheel(5)) False sage: M = matroids.Whirl(3) @@ -1876,9 +1867,7 @@ def Uniform(r, n): - ``n`` -- a nonnegative integer; the number of elements of the uniform matroid - OUTPUT: - - the uniform matroid `U_{r,n}` + OUTPUT: the uniform matroid `U_{r,n}` EXAMPLES:: @@ -1930,9 +1919,7 @@ def PG(n, q, x=None): non-prime field, used for non-prime fields. If not supplied, ``'x'`` is used. - OUTPUT: - - a linear matroid whose elements are the points of `PG(n, q)` + OUTPUT: a linear matroid whose elements are the points of `PG(n, q)` EXAMPLES:: @@ -1942,8 +1929,8 @@ def PG(n, q, x=None): sage: matroids.PG(5, 4, 'z').size() == (4^6 - 1) / (4 - 1) True sage: M = matroids.PG(4, 7); M - PG(4, 7): Linear matroid of rank 5 on 2801 elements represented over - the Finite Field of size 7 + PG(4, 7): Linear matroid of rank 5 on 2801 elements represented over the Finite Field + of size 7 REFERENCES: @@ -1977,17 +1964,14 @@ def AG(n, q, x=None): non-prime field, used for non-prime fields. If not supplied, ``'x'`` is used. - OUTPUT: - - a linear matroid whose elements are the points of `AG(n, q)` + OUTPUT: a linear matroid whose elements are the points of `AG(n, q)` EXAMPLES:: sage: M = matroids.AG(2, 3).delete(8) sage: M.is_isomorphic(matroids.catalog.AG23minus()) True - sage: matroids.AG(5, 4, 'z').size() == ((4 ^ 6 - 1) / (4 - 1) - - ....: (4 ^ 5 - 1)/(4 - 1)) + sage: matroids.AG(5, 4, 'z').size() == ((4 ^ 6 - 1) / (4 - 1) - (4 ^ 5 - 1)/(4 - 1)) True sage: M = matroids.AG(4, 2); M AG(4, 2): Binary matroid of rank 5 on 16 elements, type (5, 0) @@ -2019,9 +2003,7 @@ def Z(r, t=True): - ``r`` -- an integer (`r \ge 3`); the rank of the spike - ``t`` -- a Boolean (default: ``True``); whether the spike is tipped - OUTPUT: - - a matroid; the unique rank-`r` binary spike (tipped or tipless) + OUTPUT: a matroid; the unique rank-`r` binary spike (tipped or tipless) EXAMPLES:: @@ -2096,7 +2078,7 @@ def Spike(r, t=True, C3=[]): The groundset is `E = \{t, x_1, x_2, \ldots, x_r, y_1, y_2, \ldots, y_r\}` with `r(E) = r`. - The non-spanning circuits are `\{L_1, L_2, \ldots, L_r\}`, all sets of the + The nonspanning circuits are `\{L_1, L_2, \ldots, L_r\}`, all sets of the form `(L_i \cup L_j) \setminus t` for `1 \le i < j \le r`, and some (possibly empty) collection `C_3` of sets of the form `\{z_1, z_2, \ldots, z_r\}` where `z_i \in \{x_i, y_i\}` for all `i`, and no two members of @@ -2105,18 +2087,17 @@ def Spike(r, t=True, C3=[]): INPUT: - ``r`` -- an integer (`r \ge 3`); the rank of the spike - - ``t`` -- a boolean (default: ``True``); whether the spike is tipped + - ``t`` -- boolean (default: ``True``); whether the spike is tipped - ``C3`` -- a list (default: ``[]``); a list of extra nonspanning circuits. The default (i.e. the empty list) results in a free `r`-spike - OUTPUT: - - a matroid; a rank-`r` spike (tipped or tipless) + OUTPUT: a matroid; a rank-`r` spike (tipped or tipless) EXAMPLES:: sage: M = matroids.Spike(3, False); M - Free 3-spike\t: Matroid of rank 3 on 6 elements with 20 bases + Free 3-spike\t: M \ {'t'}, where M is Matroid of rank 3 on 7 elements with 3 + nonspanning circuits sage: M.is_isomorphic(matroids.Uniform(3, 6)) True sage: len(matroids.Spike(8).bases()) @@ -2227,9 +2208,7 @@ def Theta(n): - ``n`` -- an integer (`n \ge 2`); the rank of the matroid - OUTPUT: - - a matroid (`\Theta_n`) + OUTPUT: a matroid (`\Theta_n`) EXAMPLES:: @@ -2253,8 +2232,7 @@ def Theta(n): sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - For `n \le 3`, its automorphism group is transitive, while for `n \ge 4` - it is not:: + For `n \le 3`, its automorphism group is transitive, while for `n \ge 4` it is not:: sage: n = random.choice(range(4, 8)) sage: M = matroids.Theta(2 + n % 2) @@ -2301,17 +2279,14 @@ def Psi(r): - ``r`` -- an integer (`r \ge 3`); the rank of the matroid - OUTPUT: - - a matroid (`\Psi_r`) + OUTPUT: a matroid (`\Psi_r`) EXAMPLES:: sage: matroids.Psi(7) Psi_7: Matroid of rank 7 on 14 elements with 2060 bases - The matroid `\Psi_r` is `3`-connected but, for all `r \ge 4`, not - `4`-connected:: + The matroid `\Psi_r` is `3`-connected but, for all `r \ge 4`, not `4`-connected:: sage: M = matroids.Psi(3) sage: M.is_4connected() @@ -2847,8 +2822,7 @@ def BB9gDY(): EXAMPLES:: sage: M = matroids.catalog.BB9gDY(); M - Segment cosegment exchange on BB9: Quaternary matroid of rank 5 on 9 - elements + Segment cosegment exchange on BB9: Quaternary matroid of rank 5 on 9 elements sage: M.is_valid() True """ @@ -4812,7 +4786,7 @@ def R9B(): def Block_9_4(): """ - Return the paving matroid whose non-spanning circuits form the blocks of a + Return the paving matroid whose nonspanning circuits form the blocks of a `2-(9, 4, 3)` design. EXAMPLES:: @@ -4897,7 +4871,7 @@ def N1(): def Block_10_5(): """ - Return the paving matroid whose non-spanning circuits form the blocks of a + Return the paving matroid whose nonspanning circuits form the blocks of a `3-(10, 5, 3)` design. EXAMPLES:: @@ -5099,8 +5073,7 @@ def ExtendedBinaryGolayCode(): EXAMPLES:: sage: M = matroids.catalog.ExtendedBinaryGolayCode(); M - Extended Binary Golay Code: Binary matroid of rank 12 on 24 elements, - type (12, 0) + Extended Binary Golay Code: Binary matroid of rank 12 on 24 elements, type (12, 0) sage: C = LinearCode(M.representation()) sage: C.is_permutation_equivalent(codes.GolayCode(GF(2))) True @@ -5151,9 +5124,7 @@ def CompleteGraphic(n): - ``n`` -- an integer, the number of vertices of the underlying complete graph. - OUTPUT: - - The graphic matroid associated with the `n`-vertex complete graph. + OUTPUT: The graphic matroid associated with the `n`-vertex complete graph. This matroid has rank `n - 1`. EXAMPLES:: diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index b2d86f1e64a..2338c144190 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -2956,9 +2956,12 @@ cdef class Matroid(SageObject): - ``ordering`` -- a total ordering of the groundset given as a list + OUTPUT: a list of frozensets + EXAMPLES:: - sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: M = BasisMatroid(Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]])) sage: SimplicialComplex(M.no_broken_circuits_sets()) # needs sage.graphs Simplicial complex with vertex set (1, 2, 3, 4, 5) and facets {(1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5)} @@ -3121,9 +3124,9 @@ cdef class Matroid(SageObject): A 6-dimensional polyhedron in ZZ^7 defined as the convex hull of 29 vertices - REFERENCE: + REFERENCES: - - [DLHK2007]_ + [DLHK2007]_ """ from sage.geometry.polyhedron.constructor import Polyhedron from sage.modules.free_module import FreeModule @@ -3164,7 +3167,7 @@ cdef class Matroid(SageObject): A 7-dimensional polyhedron in ZZ^7 defined as the convex hull of 58 vertices - REFERENCE: + REFERENCES: [DLHK2007]_ """ diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index d49cf378a63..2cd946d7503 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -26,18 +26,17 @@ AUTHORS: # **************************************************************************** from sage.data_structures.bitset_base cimport * -import sage.matroids.matroid -import sage.matroids.basis_exchange_matroid -from sage.matroids.minor_matroid import MinorMatroid -from sage.matroids.dual_matroid import DualMatroid -from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid +from sage.libs.gmp.mpq cimport mpq_set +from sage.rings.rational cimport Rational + from sage.matroids.basis_matroid cimport BasisMatroid -from sage.matroids.linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid -from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix +from sage.matroids.circuits_matroid cimport CircuitsMatroid +from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid +from sage.matroids.dual_matroid import DualMatroid from sage.matroids.graphic_matroid import GraphicMatroid - -from sage.rings.rational cimport Rational -from sage.libs.gmp.mpq cimport mpq_set +from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix +from sage.matroids.linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid +from sage.matroids.minor_matroid import MinorMatroid ############################################################################# @@ -88,6 +87,49 @@ def unpickle_basis_matroid(version, data): return M +############################################################################# +# CircuitsMatroid +############################################################################# + +def unpickle_circuits_matroid(version, data): + """ + Unpickle a CircuitsMatroid. + + *Pickling* is Python's term for the loading and saving of objects. + Functions like these serve to reconstruct a saved object. This all happens + transparently through the ``load`` and ``save`` commands, and you should + never have to call this function directly. + + INPUT: + + - ``version`` -- an integer, expected to be 0 + - ``data`` -- a tuple ``(E, C, name)`` in which ``E`` is the groundset + of the matroid, ``C`` is the list of circuits , and ``name`` is a custom + name. + + OUTPUT: + + A matroid. + + .. WARNING:: + + Users should never call this function directly. + + EXAMPLES:: + + sage: M = matroids.Theta(5) + sage: M == loads(dumps(M)) # indirect doctest + True + """ + cdef CircuitsMatroid M + if version != 0: + raise TypeError("object was created with newer version of Sage. Please upgrade.") + M = CircuitsMatroid(groundset=data[0], circuits=data[1]) + if data[2] is not None: + M.rename(data[2]) + return M + + ############################################################################# # CircuitClosuresMatroid ############################################################################# @@ -358,11 +400,11 @@ def unpickle_rational_matrix(version, data): mpq_set(A._entries[i], ( data[2][i]).value) return A + ############################################################################# # LinearMatroid and subclasses ############################################################################# - def unpickle_linear_matroid(version, data): """ Unpickle a LinearMatroid. @@ -643,11 +685,11 @@ def unpickle_minor_matroid(version, data): M.rename(data[3]) return M + ############################################################################# # Graphic Matroids ############################################################################# - def unpickle_graphic_matroid(version, data): """ Unpickle a GraphicMatroid. diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 7d0e14cf799..16c78d18462 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -118,8 +118,7 @@ def read_distribution(src_file): sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py')) '' """ - from Cython.Utils import open_source_file - with open_source_file(src_file, error_handling='ignore') as fh: + with open(src_file, encoding='utf-8', errors='ignore') as fh: for line in fh: # Adapted from Cython's Build/Dependencies.py line = line.lstrip() diff --git a/src/sage/modular/abvar/homology.py b/src/sage/modular/abvar/homology.py index 59cc17a77e7..da586d7c0e0 100644 --- a/src/sage/modular/abvar/homology.py +++ b/src/sage/modular/abvar/homology.py @@ -52,11 +52,11 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.categories.commutative_rings import CommutativeRings from sage.modular.hecke.module import HeckeModule_free_module from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal # TODO: we will probably also need homology that is *not* a Hecke module. @@ -74,11 +74,9 @@ def hecke_polynomial(self, n, var='x'): INPUT: + - ``n`` -- positive integer - - ``n`` - positive integer - - - ``var`` - string (default: 'x') the variable name - + - ``var`` -- string (default: 'x') the variable name OUTPUT: a polynomial over ZZ in the given variable @@ -117,7 +115,7 @@ def __init__(self, abvar, base): sage: loads(dumps(H)) == H True """ - if not isinstance(base, CommutativeRing): + if base not in CommutativeRings(): raise TypeError("base ring must be a commutative ring") HeckeModule_free_module.__init__( self, base, abvar.level(), weight=2) diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index 0547901b1f1..c647f8d0e50 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -571,7 +571,7 @@ def is_subgroup(self, right): w = self.gens() for g in w: - if not (g in right): + if g not in right: return False return True @@ -1028,7 +1028,7 @@ def generators(self, algorithm="farey"): else: raise ValueError("Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm) - def gens(self, *args, **kwds): + def gens(self, *args, **kwds) -> tuple: r""" Return a tuple of generators for this congruence subgroup. diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index fdb51078898..6ff94e3bcac 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -816,7 +816,7 @@ def relabel(self, inplace=True): """ if hasattr(self,'_canonical_label_group'): if inplace: - if not (self is self._canonical_label_group): + if self is not self._canonical_label_group: self.__dict__ = self._canonical_label_group.__dict__ self._canonical_label_group = self else: @@ -1811,7 +1811,7 @@ def cusp_widths(self,exp=False): c1 = min(L.orbit(inv(c0))) cusps.remove(c1) if exp: - if not len(c) in widths: + if len(c) not in widths: widths[len(c)] = 0 widths[len(c)] += 1 else: diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index 9310ecae804..02794c269ad 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -1045,7 +1045,7 @@ def is_subgroup(self, other): # difficult case t = other._list_of_elements_in_H() for x in self._generators_for_H(): - if not (x in t): + if x not in t: return False return True @@ -1402,7 +1402,7 @@ def _list_subgroup(N, gens): raise ValueError("gen (=%s) is not in (Z/%sZ)^*" % (g, N)) gk = int(g) % N sbgrp = [gk] - while not (gk in H): + while gk not in H: gk = (gk * g) % N sbgrp.append(gk) H = {(x * h) % N for x in sbgrp for h in H} diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index 155fb2ebb2e..828a003b0ab 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -695,7 +695,7 @@ def edge_between_vertices(self, v1, v2, normalized=False): - 2x2 integer matrix, representing the edge from ``v1`` to ``v2``. If ``v1`` and ``v2`` are not at distance `1`, raise - a ``ValueError``. + a :class:`ValueError`. EXAMPLES:: diff --git a/src/sage/modular/hecke/ambient_module.py b/src/sage/modular/hecke/ambient_module.py index 4d0b4c583ea..1ada75090e6 100644 --- a/src/sage/modular/hecke/ambient_module.py +++ b/src/sage/modular/hecke/ambient_module.py @@ -392,7 +392,7 @@ def degeneracy_map(self, codomain, t=1): "level (=%s) and t (=%s) must be a divisor of the quotient") % (self.level(), level, t)) eps = self.character() - if not (eps is None) and level % eps.conductor() != 0: + if eps is not None and level % eps.conductor() != 0: raise ArithmeticError("the conductor of the character of this space " "(=%s) must be divisible by the level (=%s)" % (eps.conductor(), level)) diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index d0d8d5e49d7..27b45c91447 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -22,7 +22,7 @@ from sage.modules.module import Module from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.structure.sequence import Sequence from . import algebra @@ -76,7 +76,7 @@ def __init__(self, base_ring, level, category=None): sage: ModularForms(3, 3).category() Category of Hecke modules over Rational Field """ - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring must be commutative ring") from sage.categories.hecke_modules import HeckeModules @@ -456,7 +456,7 @@ def rank(self): r""" Return the rank of this module over its base ring. - This raises a ``NotImplementedError``, since this is an + This raises a :class:`NotImplementedError`, since this is an abstract base class. EXAMPLES:: @@ -473,7 +473,7 @@ def submodule(self, X): Return the submodule of ``self`` corresponding to ``X``. As this is an abstract base class, this raises a - ``NotImplementedError``. + :class:`NotImplementedError`. EXAMPLES:: @@ -739,7 +739,7 @@ def ambient_hecke_module(self): r""" Return the ambient module associated to this module. - As this is an abstract base class, raise ``NotImplementedError``. + As this is an abstract base class, raise :class:`NotImplementedError`. EXAMPLES:: @@ -1501,7 +1501,7 @@ def is_simple(self): Return ``True`` if this space is simple as a module for the corresponding Hecke algebra. - Raises ``NotImplementedError``, as this is an abstract base + This raises :class:`NotImplementedError`, as this is an abstract base class. EXAMPLES:: diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 379daa00f7a..64791118387 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -95,7 +95,7 @@ def __init__(self, ambient, submodule, dual_free_module=None, check=True): module.HeckeModule_free_module.__init__(self, ambient.base_ring(), ambient.level(), ambient.weight()) - if not (dual_free_module is None): + if dual_free_module is not None: if not is_FreeModule(dual_free_module): raise TypeError("dual_free_module must be a free module") if dual_free_module.rank() != submodule.rank(): @@ -960,7 +960,7 @@ def submodule_from_nonembedded_module(self, V, Vdual=None, check=True): # so fast, and their are asymptotically fast algorithms. A = M_V * M_E V = A.row_space() - if not (Vdual is None): + if Vdual is not None: E = self.dual_free_module() M_Vdual = Vdual.matrix() M_E = E.matrix() diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 333b609aef6..cfce7550fda 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -802,7 +802,7 @@ def _test_unitgens(self, **options): g = I.small_residue(g) else: # I is an ideal of ZZ g = g % (I.gen()) - if not (g - 1 in I): + if g - 1 not in I: T.fail("For generator g=%s, g^%s = %s, which is not 1 mod I" % (gens[i], exps[i], g)) I = self.prime() if self.number_field() == QQ else self.ideal(1) T.assertEqual(gens[-1].valuation(I), 1) diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 2adde8bafc3..0a575af232f 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -37,7 +37,7 @@ import sage.modular.dirichlet as dirichlet from sage.rings.integer import Integer from sage.rings.rational_field import Q as QQ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from .ambient_eps import ModularFormsAmbient_eps from .ambient_g0 import ModularFormsAmbient_g0_Q @@ -122,11 +122,11 @@ def canonical_parameters(group, level, weight, base_ring): except TypeError: raise TypeError("group of unknown type.") level = Integer(level) - if ( m != level ): + if m != level: raise ValueError("group and level do not match.") group = arithgroup.Gamma0(m) - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring (=%s) must be a commutative ring" % base_ring) # it is *very* important to include the level as part of the data @@ -330,7 +330,7 @@ def ModularForms(group=1, if use_cache and key in _cache: M = _cache[key]() - if not (M is None): + if M is not None: M.set_precision(prec) return M diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index fcfb3b770a0..bd01c29d84d 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -365,7 +365,7 @@ def has_character(self): sage: CuspForms(DirichletGroup(11).0,3).has_character() True """ - return not self.character() is None + return self.character() is not None def is_ambient(self): """ @@ -1929,6 +1929,6 @@ def contains_each(V, B): False """ for b in B: - if not (b in V): + if b not in V: return False return True diff --git a/src/sage/modular/modsym/apply.pyx b/src/sage/modular/modsym/apply.pyx index 4e253c2f805..6017a933a5e 100644 --- a/src/sage/modular/modsym/apply.pyx +++ b/src/sage/modular/modsym/apply.pyx @@ -10,7 +10,7 @@ Monomial expansion of `(aX + bY)^i (cX + dY)^{j-i}` # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ########################################################################## diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index 80ccb3c4242..686e64e96d8 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -76,7 +76,7 @@ def set_modsym_print_mode(mode="manin"): sage: set_modsym_print_mode() """ mode = str(mode).lower() - if not (mode in ['manin', 'modular', 'vector']): + if mode not in ['manin', 'modular', 'vector']: raise ValueError("mode must be one of 'manin', 'modular', or 'vector'") global _print_mode _print_mode = mode diff --git a/src/sage/modular/modsym/ghlist.py b/src/sage/modular/modsym/ghlist.py index 2e74217f851..62989e22105 100644 --- a/src/sage/modular/modsym/ghlist.py +++ b/src/sage/modular/modsym/ghlist.py @@ -16,7 +16,7 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ########################################################################### from sage.structure.richcmp import richcmp_method, richcmp from sage.structure.sage_object import SageObject diff --git a/src/sage/modular/modsym/hecke_operator.py b/src/sage/modular/modsym/hecke_operator.py index 43e34b57a49..818de55e97e 100644 --- a/src/sage/modular/modsym/hecke_operator.py +++ b/src/sage/modular/modsym/hecke_operator.py @@ -9,7 +9,7 @@ # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ########################################################################## @@ -23,7 +23,7 @@ def apply_sparse(self, x): """ Return the image of ``x`` under ``self``. - If ``x`` is not in ``self.domain()``, raise a ``TypeError``. + If ``x`` is not in ``self.domain()``, raise a :class:`TypeError`. EXAMPLES:: diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index 962e3c168ed..346500b8b86 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -106,10 +106,11 @@ import weakref +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.fields import Fields import sage.modular.arithgroup.all as arithgroup import sage.modular.dirichlet as dirichlet from sage.rings.integer import Integer -from sage.rings.ring import CommutativeRing from sage.rings.rational_field import RationalField @@ -153,10 +154,10 @@ def canonical_parameters(group, weight, sign, base_ring): if base_ring is None: base_ring = RationalField() - if not isinstance(base_ring, CommutativeRing): + elif base_ring not in CommutativeRings(): raise TypeError(f"base_ring (={base_ring}) must be a commutative ring") - if not base_ring.is_field(): + elif base_ring not in Fields(): raise TypeError(f"(currently) base_ring (={base_ring}) must be a field") return group, weight, sign, base_ring diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 6fd1770c8bf..58f31f4af0c 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -261,7 +261,7 @@ def OverconvergentModularForms(prime, weight, radius, base_ring=QQ, prec=20, cha if key in __ocmfdict: w = __ocmfdict[key] M = w() - if not (M is None): + if M is not None: return M M = OverconvergentModularFormsSpace(*key) __ocmfdict[key] = weakref.ref(M) diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index a83121db4fa..f33b4022842 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -31,14 +31,14 @@ (1 + O(11^5), 2 + O(11^4), 3 + O(11^3), 4 + O(11^2), 5 + O(11)) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.lazy_import import lazy_import from sage.modules.module import Module @@ -49,7 +49,7 @@ from sage.categories.modules import Modules from sage.structure.factory import UniqueFactory -import sage.rings.ring as ring +from sage.rings.ring import Ring lazy_import('sage.modular.pollack_stevens.dist', 'get_dist_classes') lazy_import('sage.rings.padics.factory', ['ZpCA', 'QpCR']) @@ -281,7 +281,7 @@ def __init__(self, k, p=None, prec_cap=None, base=None, character=None, ... ValueError: p must be prime """ - if not isinstance(base, ring.Ring): + if not isinstance(base, Ring): raise TypeError("base must be a ring") #from sage.rings.padics.pow_computer import PowComputer # should eventually be the PowComputer on ZpCA once that uses longs. diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index ef067853f4a..cab712a95bf 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -12,15 +12,15 @@ - Robert Pollack, Jonathan Hanke (2012): initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # Jonathan Hanke # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.matrix.matrix_space import MatrixSpace from sage.modular.modsym.all import P1List diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 06aa3496e10..0af3ed946d0 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -34,14 +34,14 @@ 1 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.continued_fraction import convergents from .sigma0 import Sigma0 diff --git a/src/sage/modular/pollack_stevens/space.py b/src/sage/modular/pollack_stevens/space.py index 6fe7c12b95b..c0464dd61d8 100644 --- a/src/sage/modular/pollack_stevens/space.py +++ b/src/sage/modular/pollack_stevens/space.py @@ -56,14 +56,14 @@ sage: phi.parent() Space of modular symbols for Congruence Subgroup Gamma0(37) with sign 0 and values in Sym^0 Q^2 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.modules.module import Module from sage.modular.dirichlet import DirichletCharacter from sage.modular.arithgroup.all import Gamma0 @@ -76,7 +76,7 @@ from .distributions import OverconvergentDistributions, Symk from .modsym import (PSModularSymbolElement, PSModularSymbolElement_symk, - PSModularSymbolElement_dist, PSModSymAction) + PSModularSymbolElement_dist, PSModSymAction) from .manin_map import ManinMap from .sigma0 import Sigma0, Sigma0Element @@ -887,7 +887,7 @@ def ps_modsym_from_elliptic_curve(E, sign=0, implementation='eclib'): sage: symb.values() [-1/6, 1/3, 1/2, 1/6, -1/6, 1/3, -1/3, -1/2, -1/6, 1/6, 0, -1/6, -1/6] """ - if not (E.base_ring() is QQ): + if E.base_ring() is not QQ: raise ValueError("The elliptic curve must be defined over the " "rationals.") sign = Integer(sign) diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 1c74d953f6d..76f9c99324a 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -203,6 +203,7 @@ # **************************************************************************** from sage.arith.misc import gcd, factor, prime_divisors, kronecker, next_prime +from sage.categories.commutative_rings import CommutativeRings from sage.matrix.constructor import matrix from sage.matrix.matrix_space import MatrixSpace from sage.misc.cachefunc import cached_method @@ -219,7 +220,6 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing from sage.structure.richcmp import richcmp, richcmp_method lazy_import('sage.algebras.quatalg.quaternion_algebra', ['QuaternionAlgebra', 'basis_for_quaternion_lattice']) @@ -300,7 +300,7 @@ def BrandtModule(N, M=1, weight=2, base_ring=QQ, use_cache=True): raise ValueError("M must be coprime to N") if weight < 2: raise ValueError("weight must be at least 2") - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring must be a commutative ring") key = (N, M, weight, base_ring) if use_cache: @@ -1365,7 +1365,10 @@ def right_ideals(self, B=None): ideals_theta[J_theta] = [J] verbose("found %s of %s ideals" % (len(ideals), self.dimension()), level=2) if len(ideals) >= self.dimension(): - ideals = tuple(sorted(ideals)) + # order by basis matrix (as ideals were previously + # ordered) for backward compatibility and + # deterministic order of the output + ideals = tuple(sorted(ideals, key=lambda x: x.basis_matrix())) self.__right_ideals = ideals return ideals got_something_new = True diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index ef0af193559..cf6efb9ad8c 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1017,7 +1017,7 @@ def invariants(self, include_ones=False): self.invariants.set_cache(w, False) return self.invariants(include_ones) - def gens(self): + def gens(self) -> tuple: """ Return tuple of elements `g_0,...,g_n` of ``self`` such that the module generated by the `g_i` is isomorphic to the direct sum of `R/e_i R`, where `e_i` are the diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index ffd6260764d..7fe7ff4f02d 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -186,7 +186,8 @@ import sage.rings.integer import sage.rings.integer_ring import sage.rings.rational_field -import sage.rings.ring as ring +from sage.rings.ring import IntegralDomain, is_Ring +from sage.categories.fields import Fields from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.integral_domains import IntegralDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains @@ -213,6 +214,7 @@ ) from sage.structure.sequence import Sequence + ############################################################################### # # Constructor functions @@ -294,7 +296,7 @@ def create_object(self, version, key): and base_ring.is_maximal() and base_ring.class_number() == 1): return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) - if isinstance(base_ring, ring.IntegralDomain) or base_ring in IntegralDomains(): + if isinstance(base_ring, IntegralDomain) or base_ring in IntegralDomains(): return FreeModule_ambient_domain(base_ring, rank, sparse=sparse) return FreeModule_ambient(base_ring, rank, sparse=sparse) @@ -725,7 +727,7 @@ def span(gens, base_ring=None, check=True, already_echelonized=False): TypeError: generators must be lists of ring elements or free module elements! """ - if ring.is_Ring(gens): + if is_Ring(gens): # we allow the old input format with first input the base_ring. # Do we want to deprecate it?.. base_ring, gens = gens, base_ring @@ -1926,7 +1928,7 @@ class FreeModule_generic(Module_free_ambient): (finite enumerated fields and subquotients of monoids and quotients of semigroups) sage: FreeModule(ZZ,3).category() Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets + (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) sage: (QQ^0).category() Category of finite enumerated finite dimensional vector spaces with basis @@ -2513,7 +2515,7 @@ def cardinality(self): return sage.rings.integer.Integer(1) return self.base_ring().cardinality() ** self.rank() - __len__ = cardinality # for backward compatibility + __len__ = cardinality # for backward compatibility def basis(self): """ @@ -2530,7 +2532,7 @@ def basis(self): """ raise NotImplementedError - def gens(self): + def gens(self) -> tuple: """ Return a tuple of basis elements of ``self``. @@ -4346,7 +4348,7 @@ def __init__(self, base_field, dimension, degree, sparse=False, category=None): sage: FreeModule_generic_field(QQ, 5, 5) ) failed: NotImplementedError> """ - if not isinstance(base_field, ring.Field): + if base_field not in Fields(): raise TypeError("The base_field (=%s) must be a field" % base_field) super().__init__(base_field, dimension, degree, sparse=sparse, category=category) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 79c2b226436..d1bad17f001 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -28,7 +28,8 @@ to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: B = H.basis() sage: len(B) 6 diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index fa0b61e0af3..f4942502ad1 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -69,7 +69,7 @@ import sage.matrix.matrix_space import sage.misc.latex as latex -import sage.rings.ring as ring +from sage.rings.ring import Field, IntegralDomain from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.modules import free_module @@ -174,7 +174,7 @@ def FreeQuadraticModule(base_ring, rank, inner_product_matrix, M = FreeQuadraticModule_ambient_pid( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) - elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): + elif isinstance(base_ring, IntegralDomain) or base_ring.is_integral_domain(): M = FreeQuadraticModule_ambient_domain( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) else: @@ -713,7 +713,7 @@ def __init__(self, base_field, dimension, degree, inner_product_matrix, sparse=F [0 0 0 0 0 1 0] [0 0 0 0 0 0 1] """ - if not isinstance(base_field, ring.Field): + if not isinstance(base_field, Field): raise TypeError("the base_field (=%s) must be a field" % base_field) free_module.FreeModule_generic_field.__init__( self, base_field=base_field, dimension=dimension, degree=degree, sparse=sparse) diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index 09c8b313c91..c917188b029 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -119,7 +119,7 @@ def __hash__(self): """ return self.__hash - def gens(self): + def gens(self) -> tuple: """ Return the generators of this module. @@ -132,7 +132,7 @@ def gens(self): sage: Q.gens() ((1, 0), (0, 1)) """ - return tuple([self(list(g)) for g in self._module.gens()]) + return tuple(self(list(g)) for g in self._module.gens()) def gen(self, i=0): """ diff --git a/src/sage/modules/submodule.py b/src/sage/modules/submodule.py index 8d00d4f4553..69ec72c33f7 100644 --- a/src/sage/modules/submodule.py +++ b/src/sage/modules/submodule.py @@ -194,7 +194,7 @@ def relations(self): """ return self._ambient.relations() - def gens(self): + def gens(self) -> list: """ Return the generators of this submodule. diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 90d50f0e9a9..d6aea600270 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -520,7 +520,7 @@ def gram_matrix_quadratic(self): G[i, i] = gens[i].q().lift() return G - def gens(self): + def gens(self) -> tuple: r""" Return generators of ``self``. diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 6d1b69b59bb..a4b2465386b 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -265,12 +265,12 @@ def gen(self, i=0): n = self.__ngens if i < 0 or not i < n: raise IndexError(f"argument i (= {i}) must be between 0 and {n-1}") - x = [ 0 for j in range(n) ] + x = [0 for j in range(n)] x[int(i)] = 1 return self.element_class(self, x) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. @@ -280,7 +280,7 @@ def gens(self): sage: F.gens() (a0, a1, a2, a3, a4) """ - return tuple([self.gen(i) for i in range(self.__ngens)]) + return tuple(self.gen(i) for i in range(self.__ngens)) def ngens(self): """ diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index 31e6c219f89..7af83b5fe4b 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -46,9 +46,9 @@ def __init__(self, names): Parent.__init__(self, base=self, names=names, category=category) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Returns the generators for ``self``. + Return the generators for ``self``. EXAMPLES:: @@ -60,7 +60,7 @@ def gens(self): def monoid_generators(self): r""" - Returns the generators for ``self``. + Return the generators for ``self``. EXAMPLES:: diff --git a/src/sage/quadratic_forms/bqf_class_group.py b/src/sage/quadratic_forms/bqf_class_group.py index af9c9115185..6d3aefed881 100644 --- a/src/sage/quadratic_forms/bqf_class_group.py +++ b/src/sage/quadratic_forms/bqf_class_group.py @@ -314,7 +314,7 @@ def abelian_group(self): gens = [BinaryQF(g) for g in gens] return AdditiveAbelianGroupWrapper(self, gens, ords) - def gens(self): + def gens(self) -> list: r""" Return a generating set of this form class group. @@ -728,9 +728,6 @@ class BQFClassGroupQuotientMorphism(Morphism): is defined by finding a class representative `[a,b,c]` satisfying `f^2 \mid a` and `f \mid b` and substituting `x \mapsto x/f`. - Alternatively, one may pass the discriminants `f^2 D` and `D` instead - of the :class:`BQFClassGroup` objects `G` and `H`. - This map is a well-defined group homomorphism. EXAMPLES:: diff --git a/src/sage/quivers/algebra.py b/src/sage/quivers/algebra.py index fee19eb7e93..98e05593177 100644 --- a/src/sage/quivers/algebra.py +++ b/src/sage/quivers/algebra.py @@ -172,7 +172,7 @@ def __init__(self, k, P, order="negdegrevlex"): bracket=False) self._assign_names(self._semigroup.variable_names()) - def order_string(self): + def order_string(self) -> str: """ Return the string that defines the monomial order of this algebra. @@ -195,7 +195,7 @@ def order_string(self): return self._ordstr @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of this algebra (idempotents and arrows). diff --git a/src/sage/quivers/ar_quiver.py b/src/sage/quivers/ar_quiver.py new file mode 100644 index 00000000000..daa1e755fa5 --- /dev/null +++ b/src/sage/quivers/ar_quiver.py @@ -0,0 +1,915 @@ +r""" +Auslander-Reiten Quivers +""" + +# **************************************************************************** +# Copyright (C) 2024 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.structure.element import Element +from sage.structure.richcmp import richcmp +from sage.structure.global_options import GlobalOptions +from sage.categories.sets_cat import Sets +from sage.sets.family import Family +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.free_module import CombinatorialFreeModule +from sage.rings.integer_ring import ZZ +from sage.misc.cachefunc import cached_method +from sage.graphs.digraph import DiGraph + + +class AuslanderReitenQuiver(UniqueRepresentation, Parent): + r""" + The Auslander-Reiten quiver. + + Let `Q = (Q_0, Q_1)` be a finite acyclic quiver. The + *Auslander-Reiten quiver* (AR quiver) `\Gamma_Q` is the quiver + whose vertices correspond to the indecompositible modules of `Q` + (equivalently its path algebra over an algebraically closed field) + and edges are irreducible morphisms. + + In this implementation, we denote the vertices of `\Gamma_Q` as + certain pairs `\langle v, k \rangle`, where `v \in Q_0` and + `k \in \ZZ \setminus \{0\}` is called the *level*. When `k > 0` + (resp. `k < 0`), then it corresponds to a preprojective (resp. + postinjective) module. When the quiver is a finite type Dynkin + quiver, we consider all modules to be preprojectives and denoted + by a positive level. + + .. NOTE:: + + We use the terminology *postinjective* instead of *preinjective* + given that they follow from injectives by AR translation. + + ALGORITHM: + + We compute the dimension vectors of a projective `\langle v, 1 \rangle` + by counting the number of (directed) paths `u \to v` in `Q`. We then + proceed inductively to compute all of the dimension vectors of level + `k` by using the translation equation + + .. MATH:: + + dim \langle v, k-1 \rangle + \dim \langle v, k \rangle + = \sum_{u,k'} \dim \langle u, k' \rangle, + + where the sum is over all paths from `\langle v, k-1 \rangle` to + `\langle v, k \rangle` in `\Gamma_Q`. More specifically, for each edge + `(u, v, \ell) \in Q_1` (resp. `(v, u, \ell) \in Q_1`), we have + `\langle u, k-1 \rangle` (resp. `\langle u, k \rangle`) in the sum + (assuming the node is in the AR quiver). + + The algorithm for postinjectives is dual to the above. + + .. TODO:: + + This only is implemented for the preprojectives and postinjectives + when the quiver is not a finite type Dynkin quiver. + + .. TODO:: + + Implement this for general Artinian algebras. + + EXAMPLES: + + We create the AR quivers for finite type `A_3` Dynkin quivers:: + + sage: DA = DiGraph([[1, 2], [2, 3]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 2>), (<2, 1>, <1, 1>), (<2, 1>, <3, 2>), (<3, 1>, <2, 1>), + (<2, 2>, <3, 3>), (<3, 2>, <2, 2>)] + + sage: DA = DiGraph([[1, 2], [3, 2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 2>), (<2, 1>, <1, 1>), (<2, 1>, <3, 1>), (<3, 1>, <2, 2>), + (<2, 2>, <1, 2>), (<2, 2>, <3, 2>)] + + sage: DA = DiGraph([[2, 1], [2, 3]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 1>), (<2, 1>, <1, 2>), (<2, 1>, <3, 2>), (<3, 1>, <2, 1>), + (<1, 2>, <2, 2>), (<3, 2>, <2, 2>)] + + sage: DA = DiGraph([[2, 1], [3, 2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 1>), (<2, 1>, <3, 1>), (<2, 1>, <1, 2>), (<3, 1>, <2, 2>), + (<1, 2>, <2, 2>), (<2, 2>, <1, 3>)] + + An example for the type `D_5` Dynkin quiver:: + + sage: DD = DiGraph([[5,3], [4,3], [3,2], [2,1]]) + sage: AR = DD.auslander_reiten_quiver() + sage: AR + Auslander-Reiten quiver of a ['D', 5] Dynkin quiver + sage: len(list(DD)) + 5 + + An `E_8` Dynkin quiver:: + + sage: DE = DiGraph([[8,7], [7,6], [5,6], [5,3], [3,4], [3,2], [2,1]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR + Auslander-Reiten quiver of a ['E', 8] Dynkin quiver + sage: len(list(AR)) + 120 + sage: len(list(RootSystem(['E', 8]).root_lattice().positive_roots())) + 120 + + The Kronecker quiver:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: for i in range(1, 5): + ....: for v in D.vertices(): + ....: pp = AR(v, i) + ....: pi = AR(v, -i) + ....: print(pp, pp.dimension_vector(), " ", pi, pi.dimension_vector()) + <1, 1> v1 + 2*v2 <1, -1> v1 + <2, 1> v2 <2, -1> 2*v1 + v2 + <1, 2> 3*v1 + 4*v2 <1, -2> 3*v1 + 2*v2 + <2, 2> 2*v1 + 3*v2 <2, -2> 4*v1 + 3*v2 + <1, 3> 5*v1 + 6*v2 <1, -3> 5*v1 + 4*v2 + <2, 3> 4*v1 + 5*v2 <2, -3> 6*v1 + 5*v2 + <1, 4> 7*v1 + 8*v2 <1, -4> 7*v1 + 6*v2 + <2, 4> 6*v1 + 7*v2 <2, -4> 8*v1 + 7*v2 + """ + @staticmethod + def __classcall_private__(cls, quiver): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: D = DiGraph([[1,2], [2,3], [3,1]]) + sage: D.auslander_reiten_quiver() + Traceback (most recent call last): + ... + ValueError: the quiver must not have cycles + """ + if quiver.has_loops() or not quiver.is_directed_acyclic(): + raise ValueError("the quiver must not have cycles") + quiver = quiver.copy(immutable=True) + return super().__classcall__(cls, quiver) + + def __init__(self, quiver): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,6], [6,5], [5,3], [3,4], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: TestSuite(AR).run() + + sage: D = DiGraph([[1,2], [3,4]]) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR).run() + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR).run() + """ + self._quiver = quiver + self._top_sort = quiver.topological_sort() + self._dim_vec_space = CombinatorialFreeModule(ZZ, quiver.vertices(), prefix='v', bracket=False) + self._max_level = float('inf') + + dynkin_type = detect_dynkin_quiver(quiver) + if dynkin_type is not None: + self._cartan_type = dynkin_type + self._is_finite = dynkin_type is not None + cat = Sets().Enumerated().Finite() if self._is_finite else Sets().Infinite() + super().__init__(self, category=cat) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[5,6], [5,3], [3,4], [2,3], [1,2]]) + sage: DE.auslander_reiten_quiver() + Auslander-Reiten quiver of a ['E', 6] Dynkin quiver + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: D.auslander_reiten_quiver() + Auslander-Reiten quiver of Multi-digraph on 2 vertices + """ + if self._is_finite: + return "Auslander-Reiten quiver of a {} Dynkin quiver".format(self._cartan_type) + return "Auslander-Reiten quiver of {}".format(self._quiver) + + # add options to class + class options(GlobalOptions): + r""" + Sets and displays the global options for Auslander-Reiten quivers. + If no parameters are set, then the function returns a copy of the + options dictionary. + + The ``options`` to partitions can be accessed as the method + :obj:`AuslanderReitenQuiver.options` of + :class:`~sage.quivers.ar_quiver.AuslanderReitenQuiver`. + + @OPTIONS@ + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: node = AR(2, 2) + sage: latex(node) + \left\langle 2, 2 \right\rangle + sage: AR.options.latex = "dimension_vector" + sage: latex(node) + 2 v_{1} + 3 v_{2} + sage: AR.options.latex = "both" + sage: latex(node) + \begin{gathered} \left\langle 2, 2 \right\rangle \\ 2 v_{1} + 3 v_{2} \end{gathered} + sage: AR.options._reset() + """ + NAME = 'AuslanderReitenQuiver' + module = 'sage.quivers.ar_quiver' + latex = dict(default="node", + description='Specifies how nodes of the AR quiver should be latexed', + values=dict(node='latex as the node description', + dimension_vector='latex as the dimension vector', + both='latex as both'), + case_sensitive=False) + + def _an_element_(self): + r""" + Return an element of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR._an_element_() + <1, 1> + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR._an_element_() + <1, 1> + """ + return next(iter(self.projectives())) + + def quiver(self): + r""" + Return the quiver defining ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.quiver() == DE + True + """ + return self._quiver + + def projectives(self): + r""" + Return the projectives of ``self``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR.projectives() + Finite family {1: <1, 1>, 2: <2, 1>} + """ + return Family({v: self.element_class(self, v, 1) for v in self._quiver.vertex_iterator()}) + + @cached_method + def simples(self): + r""" + Return the simples of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.simples() + Finite family {1: <1, 15>, 2: <1, 14>, 3: <8, 4>, 4: <4, 15>, + 5: <8, 3>, 6: <6, 1>, 7: <7, 15>, 8: <8, 1>} + """ + ret = {} + for elt in self: + supp = elt.dimension_vector().support() + if len(supp) != 1: + continue + ret[next(iter(supp))] = elt + return Family(ret) + + def injectives(self): + r""" + Return the injectives of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,6], [6,5], [5,3], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.injectives() + Finite family {1: <1, 9>, 2: <2, 9>, 3: <3, 9>, 4: <4, 9>, + 5: <5, 9>, 6: <6, 9>, 7: <7, 9>} + """ + if self._is_finite: + self.digraph() # sets self._injective attribute + return self._injectives + return Family({v: self(v, -1) for v in self._quiver.vertex_iterator()}) + + def _digraph_set_latex_options(self, G): + """ + Set the latex options of the digraph ``G``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_preprojectives(2) + sage: G = AR._digraph_set_latex_options(G) + sage: G.latex_options().get_option('edge_labels') + True + """ + G.set_latex_options(edge_labels=True) + + from sage.graphs.dot2tex_utils import have_dot2tex + if have_dot2tex(): + from sage.misc.latex import LatexExpr + + def edge_options(data): + u, v, l = data + edge_opts = {} + if l == 'ART': + edge_opts["color"] = "dashed,blue" + edge_opts["label"] = LatexExpr(r"\tau") + return edge_opts + + G.set_latex_options(format="dot2tex", edge_options=edge_options) + return G + + def digraph_preprojectives(self, max_depth, with_translations=False): + r""" + Return the diagraph of preprojectives of ``self`` up to ``max_depth``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_preprojectives(3) + sage: [node.dimension_vector() for node in G] + [v1 + 2*v2, v2, 3*v1 + 4*v2, 2*v1 + 3*v2, 5*v1 + 6*v2, 4*v1 + 5*v2] + sage: AR.digraph_preprojectives(0) + Digraph on 0 vertices + """ + if max_depth < 1: + return self._digraph_set_latex_options(DiGraph()) + + k = 2 + prev = dict(self.projectives()) + verts = list(prev.values()) + edges = [(prev[v], prev[u], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(k) + while k <= max_depth: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, k) for v in cur} + verts.extend(cur.values()) + edges.extend((cur[v], cur[u], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((prev[u], cur[v], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + if with_translations: + edges.extend((cur[v], prev[v], 'ART') for v in cur if v in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(k) + + G = DiGraph([verts, edges], format="vertices_and_edges", multiedges=True, immutable=True) + return self._digraph_set_latex_options(G) + + def digraph_postinjectives(self, max_depth, with_translations=False): + """ + Return the diagraph of postinjectives of ``self`` up to ``max_depth``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_postinjectives(3) + sage: [node.dimension_vector() for node in G] + [5*v1 + 4*v2, 6*v1 + 5*v2, 3*v1 + 2*v2, 4*v1 + 3*v2, v1, 2*v1 + v2] + sage: AR.digraph_postinjectives(0) + Digraph on 0 vertices + """ + if max_depth < 1: + return self._digraph_set_latex_options(DiGraph()) + + k = 2 + prev = dict(self.injectives()) + verts = list(prev.values()) + edges = [(prev[u], prev[v], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(-k) + while k <= max_depth: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, -k) for v in cur} + verts.extend(cur.values()) + edges.extend((cur[u], cur[v], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((cur[v], prev[u], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + if with_translations: + edges.extend((prev[v], cur[v], 'ART') for v in cur if v in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(-k) + + G = DiGraph([verts, edges], format="vertices_and_edges", multiedges=True, immutable=True) + return self._digraph_set_latex_options(G) + + @cached_method + def digraph(self, with_translations=False): + r""" + Return the diagraph of ``self``. + + INPUT: + + - ``with_translations`` -- (default: ``False``) if ``True``, then + include the arrows corresponding to the translations. + + EXAMPLES:: + + sage: DA = DiGraph([[1,2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: G = AR.digraph(); G + Digraph on 3 vertices + sage: G.edges() + [(<1, 1>, <2, 2>, None), (<2, 1>, <1, 1>, None)] + sage: GT = AR.digraph(with_translations=True) + sage: GT.edges() + [(<1, 1>, <2, 2>, None), (<2, 1>, <1, 1>, None), (<2, 2>, <2, 1>, 'ART')] + """ + if not self._is_finite: + raise TypeError("the AR quiver is not finite") + + if with_translations: + G = self.digraph().copy(immutable=False) + for v in G.vertex_iterator(): + u = v.translation() + if u is not None: + G.add_edge(v, u, 'ART') + G = G.copy(immutable=True) + + else: + k = 2 + prev = dict(self.projectives()) + injectives = dict(prev) # make a shallow copy since we will mutate it + verts = list(prev.values()) + edges = [(prev[v], prev[u], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(k) + while cur: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, k) for v in cur} + injectives.update(cur) + verts.extend(cur.values()) + edges.extend((cur[v], cur[u], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((prev[u], cur[v], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(k) + + self._injectives = Family(injectives) + G = DiGraph([verts, edges], format="vertices_and_edges", immutable=True) + + return self._digraph_set_latex_options(G) + + def __iter__(self): + r""" + Iterate over ``self`` when possible. + + EXAMPLES:: + + sage: DD = DiGraph([[3,2], [4,2], [2,1]]) + sage: AR = DD.auslander_reiten_quiver() + sage: list(AR) + [<1, 1>, <2, 1>, <3, 1>, <4, 1>, <1, 2>, <2, 2>, <3, 2>, <4, 2>, + <1, 3>, <2, 3>, <3, 3>, <4, 3>] + """ + return iter(self.digraph()) + + def _element_constructor_(self, vertex, level=None): + r""" + Construct an element of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR(2, 2) + <2, 2> + sage: AR((2, 3)) + <2, 3> + sage: AR(2, 4) + Traceback (most recent call last): + ... + ValueError: no 2 at level 4 + sage: AR(2, 1/2) + Traceback (most recent call last): + ... + ValueError: the level 1/2 must be an integer + sage: AR(10, 1) + Traceback (most recent call last): + ... + ValueError: 10 is not a vertex of the quiver + """ + if level is None: + if len(vertex) == 2: + vertex, level = vertex + if vertex not in self._quiver: + raise ValueError(f"{vertex} is not a vertex of the quiver") + if level == 1: + return self.element_class(self, vertex, level) + if level not in ZZ: + raise ValueError(f"the level {level} must be an integer") + + if not self._is_finite: + if level == -1 or vertex in self._dim_vecs_level(level): + return self.element_class(self, vertex, level) + # This is likely never true + raise ValueError(f"no {vertex} at level {level}") + + # otherwise the AR quiver is finite + if level < 0: + self.digraph() # computes the max level + level = self._max_level - level + if level > 1: + if vertex in self._dim_vecs_level(level): + return self.element_class(self, vertex, level) + raise ValueError(f"no {vertex} at level {level}") + + @cached_method + def _dim_vecs_level(self, k): + r""" + Return a ``dict`` of dimension vectors of level ``k``. + + .. WARNING:: + + This is only meant to be used internally as the output is + mutable but cached. Thus, the output should not be changed. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR._dim_vecs_level(1) + {1: v1, 2: v1 + v2, 3: v1 + v2 + v3, 4: v1 + v2 + v3 + v4} + sage: AR._dim_vecs_level(2) + {1: v2, 2: v2 + v3, 3: v2 + v3 + v4} + sage: AR._dim_vecs_level(3) + {1: v3, 2: v3 + v4} + sage: AR._dim_vecs_level(4) + {1: v4} + sage: AR._dim_vecs_level(-1) + {1: v1 + v2 + v3 + v4, 2: v2 + v3 + v4, 3: v3 + v4, 4: v4} + sage: AR._dim_vecs_level(-2) + {2: v1 + v2 + v3, 3: v2 + v3, 4: v3} + sage: AR._dim_vecs_level(-3) + {3: v1 + v2, 4: v2} + sage: AR._dim_vecs_level(-4) + {4: v1} + """ + if k == 0: + raise ValueError("k must not be 0") + M = self._dim_vec_space + Q = self._quiver + if k == 1: + ret = {v: M._from_dict({u: ZZ(len(Q.all_paths(v, u, use_multiedges=True))) for u in Q.vertex_iterator()}) + for v in Q.vertex_iterator()} + elif k > 1: + if k > self._max_level: + return {} + prev = self._dim_vecs_level(k-1) + if k > self._max_level: # this might get set on the recursive call + return {} + ret = {} + for v in reversed(self._top_sort): + if v not in prev: # assumption: this vertex will never reappear + continue + temp = -prev[v] + for u, _, _ in Q.incoming_edge_iterator(v): + if u in prev: + temp += prev[u] + for _, u, _ in Q.outgoing_edge_iterator(v): + if u in ret: + temp += ret[u] + if all(coeff > 0 for key, coeff in temp): + ret[v] = temp + if not ret: + self._max_level = k + + elif k == -1: + ret = {v: M._from_dict({u: ZZ(len(Q.all_paths(u, v, use_multiedges=True))) for u in Q.vertex_iterator()}) + for v in Q.vertex_iterator()} + + elif k < -1: + prev = self._dim_vecs_level(k+1) + ret = {} + for v in self._top_sort: + if v not in prev: # assumption: this vertex will never reappear + continue + temp = -prev[v] + for _, u, _ in Q.outgoing_edge_iterator(v): + if u in prev: + temp += prev[u] + for u, _, _ in Q.incoming_edge_iterator(v): + if u in ret: + temp += ret[u] + if all(coeff > 0 for key, coeff in temp): + ret[v] = temp + + return ret + + def dimension_vectors_of_level(self, k): + r""" + Return a :class:`Family` of dimension vectors of level ``k``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.dimension_vectors_of_level(1) + {1: v1, 2: v1 + v2 + v3, 3: v3, 4: v3 + v4} + sage: AR.dimension_vectors_of_level(3) + {1: v4, 3: v2} + sage: AR.dimension_vectors_of_level(10) + {} + sage: AR.dimension_vectors_of_level(-1) + {1: v1 + v2, 2: v2, 3: v2 + v3 + v4, 4: v4} + sage: AR.dimension_vectors_of_level(-2) + {1: v3 + v4, 2: v1 + v2 + v3 + v4, 3: v1 + v2 + v3, 4: v2 + v3} + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR.dimension_vectors_of_level(1) + {1: v1 + 2*v2, 2: v2} + sage: AR.dimension_vectors_of_level(3) + {1: 5*v1 + 6*v2, 2: 4*v1 + 5*v2} + sage: AR.dimension_vectors_of_level(-1) + {1: v1, 2: 2*v1 + v2} + sage: AR.dimension_vectors_of_level(-3) + {1: 5*v1 + 4*v2, 2: 6*v1 + 5*v2} + """ + ret = self._dim_vecs_level(k) + return dict(ret) # make a (shallow) copy to allow a user to mutate it + + class Element(Element): + r""" + A node in the AR quiver. + """ + def __init__(self, parent, vertex, level): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: TestSuite(AR(1, 3)).run() + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR(2, 3)).run() + sage: TestSuite(AR(1, -4)).run() + """ + self._vertex = vertex + self._level = ZZ(level) + Element.__init__(self, parent) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR(1, 3) + <1, 3> + """ + return f"<{self._vertex}, {self._level}>" + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(2, 2) + sage: latex(node) + \left\langle 2, 2 \right\rangle + sage: AR.options.latex = "dimension_vector" + sage: latex(node) + v_{2} + v_{3} + sage: AR.options.latex = "both" + sage: latex(node) + \begin{gathered} \left\langle 2, 2 \right\rangle \\ v_{2} + v_{3} \end{gathered} + sage: AR.options._reset() + """ + from sage.misc.latex import latex + node = r"\left\langle {}, {} \right\rangle".format(latex(self._vertex), self._level) + latex_option = self.parent().options.latex + if latex_option == "node": + return node + dim_vec = latex(self.dimension_vector()) + if latex_option == "dimension_vector": + return dim_vec + return r"\begin{{gathered}} {} \\ {} \end{{gathered}}".format(node, dim_vec) + + def _richcmp_(self, other, op): + r""" + Rich comparison of ``self`` to ``other`` by ``op``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: sorted(AR) + [<1, 1>, <2, 1>, <3, 1>, <1, 2>, <2, 2>, <3, 2>] + """ + return richcmp((self._level, self._vertex), (other._level, other._vertex), op) + + def __hash__(self): + r""" + Return the hash of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: hash(node) == hash((2, 1)) + True + """ + return hash((self._level, self._vertex)) + + def vertex(self): + r""" + Return the vertex of the quiver corresponding to ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: node.vertex() + 1 + """ + return self._vertex + + def level(self): + r""" + Return the level of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: node.level() + 2 + """ + return self._level + + def translation(self): + r""" + Return the AR translation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 1) + sage: node.translation() is None + True + sage: node = AR(1, 2) + sage: node.translation() + <1, 1> + """ + if self._level == 1: + return None + dim_vecs = self.parent()._dim_vecs_level(self._level - 1) + if self._vertex not in dim_vecs: # this likely never happens + return None + return type(self)(self.parent(), self._vertex, self._level - 1) + + def inverse_translation(self): + r""" + Return the inverse AR translation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 1) + sage: node.inverse_translation() + <1, 2> + sage: node = AR(1, 2) + sage: node.inverse_translation() is None + True + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR(2, -1).inverse_translation() is None + True + """ + if self._level == -1: + return None + dim_vecs = self.parent()._dim_vecs_level(self._level + 1) + if self._vertex not in dim_vecs: + return None + return type(self)(self.parent(), self._vertex, self._level + 1) + + def dimension_vector(self): + r""" + Return the dimension vector of ``self``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: node = AR(2, -4) + sage: node.dimension_vector() + 8*v1 + 7*v2 + """ + return self.parent()._dim_vecs_level(self._level)[self._vertex] + + +def detect_dynkin_quiver(quiver): + """ + Determine if ``quiver`` is a finite type Dynkin quiver. + + EXAMPLES:: + + sage: from sage.quivers.ar_quiver import detect_dynkin_quiver + sage: D = DiGraph([[1,2], [2,3], [3, 4], [4,0], ['a','b'], ['b','c'], ['c','d'], ['c','e']]) + sage: detect_dynkin_quiver(D) + D5xA5 + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1, 2], [2, 3], [1, 3]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [1,3], [1,4], [1,5]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [2,4], [4,5], [6,4]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [3,4], [4,5], [5,6], [6,7], [7,8], [8,9], [0,3]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [3,4], [4,5], [5,6], [6,7], [0,4]]) + sage: detect_dynkin_quiver(D) is None + True + """ + dynkin_type = [] + for Q in quiver.connected_components_subgraphs(): + if Q.has_multiple_edges(): + return None + G = Q.to_undirected() + if G.is_path(): + dynkin_type.append(['A', Q.num_verts()]) + continue + degthree = G.vertices(degree=3) + if len(degthree) != 1: + return None + G = G.copy(immutable=False) + G.delete_vertex(degthree[0]) + path_lengths = sorted(G.connected_components_sizes()) + if len(path_lengths) != 3: + return None + if path_lengths[:2] == [1, 1]: + dynkin_type.append(['D', G.num_verts() + 1]) + elif path_lengths[:2] == [1, 2] and path_lengths[2] in [2, 3, 4]: + dynkin_type.append(['E', G.num_verts() + 1]) + else: + return None + if len(dynkin_type) == 1: + return CartanType(dynkin_type[0]) + return CartanType(dynkin_type) diff --git a/src/sage/quivers/path_semigroup.py b/src/sage/quivers/path_semigroup.py index 85aea6517d0..42ff7b0698e 100644 --- a/src/sage/quivers/path_semigroup.py +++ b/src/sage/quivers/path_semigroup.py @@ -468,7 +468,7 @@ def gen(self, i): return self.gens()[i] @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the tuple of generators. @@ -487,7 +487,7 @@ def gens(self): """ return self.idempotents() + self.arrows() - def is_finite(self): + def is_finite(self) -> bool: """ This partial semigroup is finite if and only if the underlying quiver is acyclic. diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 0f83c615da7..e3c7167fe12 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -16,7 +16,9 @@ # Ring base classes from sage.rings.ring import (Ring, Field, CommutativeRing, IntegralDomain, - DedekindDomain, PrincipalIdealDomain) + PrincipalIdealDomain) + +lazy_import("sage.rings.ring", "DedekindDomain") # Ring element base classes from sage.structure.element import (CommutativeAlgebraElement, diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 46f5ecbc0ce..3bbc58adad8 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -2453,7 +2453,7 @@ def exp(self, precision=None): """ return self.rpow('e', precision=precision) - def substitute(self, rules=None, domain=None, **kwds): + def subs(self, rules=None, domain=None, **kwds): r""" Substitute the given ``rules`` in this asymptotic expansion. @@ -2677,8 +2677,6 @@ def substitute(self, rules=None, domain=None, **kwds): TypeError('Cannot apply the substitution rules %s on %s ' 'in %s.' % (rules, self, self.parent())), e) - subs = substitute - def _substitute_(self, rules): r""" Substitute the given ``rules`` in this asymptotic expansion. diff --git a/src/sage/rings/commutative_algebra.py b/src/sage/rings/commutative_algebra.py index 92d734cce77..7f6b2ba5dfa 100644 --- a/src/sage/rings/commutative_algebra.py +++ b/src/sage/rings/commutative_algebra.py @@ -17,7 +17,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.ring import CommutativeAlgebra +from sage.categories.commutative_algebras import CommutativeAlgebras + def is_CommutativeAlgebra(x): """ @@ -35,4 +36,4 @@ def is_CommutativeAlgebra(x): """ from sage.misc.superseded import deprecation deprecation(35253, "the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead") - return isinstance(x, CommutativeAlgebra) + return x in CommutativeAlgebras(x.base_ring()) diff --git a/src/sage/rings/complex_double.pxd b/src/sage/rings/complex_double.pxd index 789db2ba3ad..f2af4bd67b7 100644 --- a/src/sage/rings/complex_double.pxd +++ b/src/sage/rings/complex_double.pxd @@ -1,7 +1,6 @@ from sage.libs.gsl.types cimport gsl_complex cimport sage.structure.element -cimport sage.rings.ring cimport sage.rings.abc diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 79c5a4cce1a..21af64dce8d 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -79,7 +79,6 @@ cdef extern from "": double cabs(double complex) import sage.rings.abc -cimport sage.rings.ring cimport sage.rings.integer from sage.structure.element cimport Element, FieldElement diff --git a/src/sage/rings/complex_mpc.pxd b/src/sage/rings/complex_mpc.pxd index 68fb73f4afa..60b3e739f83 100644 --- a/src/sage/rings/complex_mpc.pxd +++ b/src/sage/rings/complex_mpc.pxd @@ -1,7 +1,7 @@ from sage.libs.mpc.types cimport mpc_t, mpc_rnd_t cimport sage.structure.element -cimport sage.rings.ring +from sage.rings.ring cimport Field cdef class MPComplexNumber(sage.structure.element.FieldElement): cdef mpc_t value @@ -10,7 +10,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): cpdef _add_(self, other) noexcept cpdef _mul_(self, other) noexcept -cdef class MPComplexField_class(sage.rings.ring.Field): +cdef class MPComplexField_class(Field): cdef readonly int _prec cdef mpc_rnd_t __rnd cdef object __rnd_str diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index eb22622ecc4..44f25fee8c2 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -257,7 +257,7 @@ def MPComplexField(prec=53, rnd="RNDNN", names=None): return C -cdef class MPComplexField_class(sage.rings.ring.Field): +cdef class MPComplexField_class(Field): def __init__(self, int prec=53, rnd="RNDNN"): """ Initialize ``self``. diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index 02779f49bca..fcb3e2ab8d6 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -136,7 +136,7 @@ class PseudoConwayLattice(WithEqualityById, SageObject): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 TESTS:: @@ -164,16 +164,16 @@ def __init__(self, p, use_database=True): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(3) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + 2*x + 1 sage: # needs sage.rings.finite_rings sage: PCL = PseudoConwayLattice(5, use_database=False) - sage: PCL.polynomial(12) + sage: PCL.polynomial(12) # random x^12 + 4*x^11 + 2*x^10 + 4*x^9 + 2*x^8 + 2*x^7 + 4*x^6 + x^5 + 2*x^4 + 2*x^2 + x + 2 - sage: PCL.polynomial(6) + sage: PCL.polynomial(6) # random x^6 + x^5 + 4*x^4 + 3*x^3 + 3*x^2 + 2*x + 2 - sage: PCL.polynomial(11) + sage: PCL.polynomial(11) # random x^11 + x^6 + 3*x^3 + 4*x + 3 """ self.p = p @@ -215,11 +215,11 @@ def polynomial(self, n): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 - sage: PCL.polynomial(4) + sage: PCL.polynomial(4) # random x^4 + x^3 + 1 - sage: PCL.polynomial(60) + sage: PCL.polynomial(60) # random x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: @@ -239,7 +239,7 @@ def polynomial(self, n): # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } - xi = {q: self.polynomial(n//q).any_root(K, -n//q, assume_squarefree=True) + xi = {q: self.polynomial(n//q).any_root(K, n//q, assume_squarefree=True, assume_distinct_deg=True) for q in n.prime_divisors()} # The following is needed to ensure that in the concrete instantiation @@ -402,9 +402,9 @@ def _frobenius_shift(K, generators, check_only=False): sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1 sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1 sage: K. = GF(2^60, modulus='first_lexicographic') - sage: x30 = f30.any_root(K) - sage: x20 = f20.any_root(K) - sage: x12 = f12.any_root(K) + sage: x30 = f30.roots(K, multiplicities=False)[0] + sage: x20 = f20.roots(K, multiplicities=False)[0] + sage: x12 = f12.roots(K, multiplicities=False)[0] sage: generators = {2: x30, 3: x20, 5: x12} sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius sage: _frobenius_shift(K, generators) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 85dadd6fb2c..b7bbd417273 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -16,32 +16,35 @@ Construction of an embedding:: sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: f(t) + sage: f(t) # random T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: f(t) == f.im_gens()[0] + True The map `f` has a method ``section`` which returns a partially defined map which is the inverse of `f` on the image of `f`:: - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): ... ValueError: T is not in the image of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + Defn: ... There is no embedding of `GF(5^6)` into `GF(5^11)`:: @@ -130,8 +133,9 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): @@ -139,7 +143,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): ValueError: T is not in the image of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + Defn: t |--> ... """ for root, _ in x.minimal_polynomial().roots(ring=self.codomain()): if self._inverse(root) == x: @@ -158,7 +162,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g._repr_() + sage: g._repr_() # random 'Section of Ring morphism:\n From: Finite Field in t of size 3^7\n To: Finite Field in T of size 3^21\n Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T' """ return "Section of %s" % self._inverse @@ -202,11 +206,15 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: from sage.rings.finite_rings.hom_finite_field import FiniteFieldHomomorphism_generic sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^6) sage: K. = GF(3^9) @@ -299,7 +307,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^3) sage: K. = GF(3^9) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^6 + 2*T^4 + T^2 + T sage: a = k.random_element() @@ -368,20 +376,22 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^7) sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == a + b + True sage: g(T) Traceback (most recent call last): ... ValueError: T is not in the image of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + Defn: ... """ if self.base_map() is not None: raise NotImplementedError @@ -421,7 +431,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: embed = Frob.fixed_field()[1] - sage: hash(embed) # random + sage: hash(embed) # random -2441354824160407762 """ return Morphism.__hash__(self) @@ -751,15 +761,17 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t + sage: embed(tfixed) == embed.im_gens()[0] + True """ if self._degree_fixed == 1: k = FiniteField(self.domain().characteristic()) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index bc685731610..eebd20056e7 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -59,11 +59,15 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == g(f(a)) + g(f(b)) == a + b + True """ if not isinstance(inverse, FiniteFieldHomomorphism_givaro): raise TypeError("The given map is not an instance of FiniteFieldHomomorphism_givaro") @@ -114,7 +118,7 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ ValueError: T is not in the image of Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 - Defn: t |--> 2*T^3 + 2*T^2 + 1 + Defn: t |--> ... """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) @@ -140,11 +144,15 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: from sage.rings.finite_rings.hom_finite_field_givaro import FiniteFieldHomomorphism_givaro sage: k. = GF(3^2) sage: K. = GF(3^4) - sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^10) sage: K. = GF(3^20) @@ -182,8 +190,10 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^3 + 2*T^2 + 1 + sage: f(t) == f.im_gens()[0] + True """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) @@ -242,14 +252,14 @@ cdef class FrobeniusEndomorphism_givaro(FrobeniusEndomorphism_finite_field): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t """ if self._degree_fixed == 1: diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index f9d523199ec..5c122a1c6b7 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -334,7 +334,7 @@ def _an_element_(self): TESTS:: - sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() + sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() # random Ring morphism: From: Finite Field in a of size 3^3 To: Finite Field in b of size 3^6 diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index eb35543a20e..0a392c9b35c 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -65,7 +65,7 @@ from sage.arith.misc import factor from sage.arith.misc import primitive_root from sage.arith.misc import CRT_basis -import sage.rings.ring as ring +from sage.rings.ring import Field, CommutativeRing import sage.rings.abc from sage.rings.finite_rings import integer_mod import sage.rings.integer as integer @@ -85,6 +85,7 @@ class PariError(Exception): from sage.interfaces.abc import GapElement + class IntegerModFactory(UniqueFactory): r""" Return the quotient ring `\ZZ / n\ZZ`. @@ -616,14 +617,14 @@ def multiplicative_subgroups(self): EXAMPLES:: - sage: # needs sage.groups - sage: Integers(5).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: # optional - gap_package_polycyclic, needs sage.groups + sage: Integers(5).multiplicative_subgroups() ((2,), (4,), ()) - sage: Integers(15).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: Integers(15).multiplicative_subgroups() ((11, 7), (11, 4), (2,), (11,), (14,), (7,), (4,), ()) - sage: Integers(2).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: Integers(2).multiplicative_subgroups() ((),) - sage: len(Integers(341).multiplicative_subgroups()) # optional - gap_package_polycyclic + sage: len(Integers(341).multiplicative_subgroups()) 80 TESTS:: @@ -632,7 +633,7 @@ def multiplicative_subgroups(self): ((),) sage: IntegerModRing(2).multiplicative_subgroups() # needs sage.groups ((),) - sage: IntegerModRing(3).multiplicative_subgroups() # needs sage.groups # optional - gap_package_polycyclic + sage: IntegerModRing(3).multiplicative_subgroups() # optional - gap_package_polycyclic, needs sage.groups ((2,), ()) """ return tuple(tuple(g.value() for g in H.gens()) @@ -1261,7 +1262,7 @@ def _coerce_map_from_(self, S): elif S is integer_ring.ZZ: return integer_mod.Integer_to_IntegerMod(self) elif isinstance(S, IntegerModRing_generic): - if isinstance(S, ring.Field): + if isinstance(S, Field): return None try: return integer_mod.IntegerMod_to_IntegerMod(S, self) @@ -1554,7 +1555,7 @@ def random_element(self, bound=None): True """ if bound is not None: - return ring.CommutativeRing.random_element(self, bound) + return CommutativeRing.random_element(self, bound) a = random.randint(0, self.order() - 1) return self(a) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 4c99862ca24..ac833a196b4 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -268,7 +268,7 @@ cdef class FpTElement(FieldElement): """ return self.numer()(*args, **kwds) / self.denom()(*args, **kwds) - def subs(self, *args, **kwds): + def subs(self, in_dict=None, *args, **kwds): """ EXAMPLES:: @@ -280,7 +280,7 @@ cdef class FpTElement(FieldElement): sage: f.subs(X=2) (t + 1)/(t + 10) """ - return self.numer().subs(*args, **kwds) / self.denom().subs(*args, **kwds) + return self.numer().subs(in_dict, *args, **kwds) / self.denom().subs(in_dict, *args, **kwds) def valuation(self, v): """ diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 5b6cfa2e29f..5fd616f6b34 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -23,6 +23,7 @@ from sage.structure.element cimport FieldElement, parent from sage.structure.richcmp cimport richcmp from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ import sage.misc.latex as latex @@ -446,6 +447,41 @@ cdef class FractionFieldElement(FieldElement): """ return self._numerator(*x, **kwds) / self._denominator(*x, **kwds) + def subs(self, in_dict=None, *args, **kwds): + r""" + Substitute variables in the numerator and denominator of ``self``. + + If a dictionary is passed, the keys are mapped to generators + of the parent ring. Otherwise, the arguments are transmitted + unchanged to the method ``subs`` of the numerator and the + denominator. + + EXAMPLES:: + + sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() + sage: f = x^2 + y + x^2*y^2 + 5 + sage: (1/f).subs(x=5) + 1/(25*y^2 + y + 30) + + TESTS: + + Check that :issue:`37122` is fixed:: + + sage: P = PolynomialRing(QQ, ["x%s" % i for i in range(10000)]) + sage: PF = P.fraction_field() + sage: p = sum(i*P.gen(i) for i in range(5)) / sum(i*P.gen(i) for i in range(8)) + sage: v = P.gen(4) + sage: p.subs({v: 100}) + (x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400) + """ + if isinstance(in_dict, dict): + R = self.parent().base() + in_dict = {ZZ(m) if m in ZZ else R(m): v for m, v in in_dict.items()} + + num = self._numerator.subs(in_dict, *args, **kwds) + den = self._denominator.subs(in_dict, *args, **kwds) + return num / den + def _is_atomic(self): """ EXAMPLES:: diff --git a/src/sage/rings/function_field/function_field_polymod.py b/src/sage/rings/function_field/function_field_polymod.py index b740b771bec..44c1390bc86 100644 --- a/src/sage/rings/function_field/function_field_polymod.py +++ b/src/sage/rings/function_field/function_field_polymod.py @@ -418,10 +418,11 @@ def monic_integral_model(self, names=None): raise ValueError("names must contain at most 2 entries") if self.base_field() is not self.rational_function_field(): - L,from_L,to_L = self.simple_model() - ret,ret_to_L,L_to_ret = L.monic_integral_model(names) - from_ret = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - to_ret = self.hom( [L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())] ) + L, from_L, to_L = self.simple_model() + ret, ret_to_L, L_to_ret = L.monic_integral_model(names) + from_ret = ret.hom([from_L(ret_to_L(ret.gen())), + from_L(ret_to_L(ret.base_field().gen()))]) + to_ret = self.hom([L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())]) return ret, from_ret, to_ret else: if self.polynomial().is_monic() and all(c.denominator().is_one() for c in self.polynomial()): @@ -431,12 +432,12 @@ def monic_integral_model(self, names=None): return self.change_variable_name(names) else: if not names: - names = (self.variable_name()+"_",) + names = (self.variable_name() + "_",) if len(names) == 1: names = (names[0], self.rational_function_field().variable_name()) g, d = self._make_monic_integral(self.polynomial()) - K,from_K,to_K = self.base_field().change_variable_name(names[1]) + K, from_K, to_K = self.base_field().change_variable_name(names[1]) g = g.map_coefficients(to_K) ret = K.extension(g, names=names[0]) from_ret = ret.hom([self.gen() * d, self.base_field().gen()]) @@ -791,7 +792,7 @@ def free_module(self, base=None, basis=None, map=True): if not map: return V from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) + to_V = MapFunctionFieldToVectorSpace(self, V) return (V, from_V, to_V) def maximal_order(self): @@ -982,7 +983,7 @@ def hom(self, im_gens, base_morphism=None): yy |--> y """ - if not isinstance(im_gens, (list,tuple)): + if not isinstance(im_gens, (list, tuple)): im_gens = [im_gens] if len(im_gens) == 0: raise ValueError("no images specified") @@ -1025,11 +1026,11 @@ def genus(self): # making the auxiliary ring which only has polynomials # with integral coefficients. tmpAuxRing = PolynomialRing(self._base_field.constant_field(), - str(self._base_field.gen())+','+str(self._ring.gen())) + str(self._base_field.gen()) + ',' + str(self._ring.gen())) intMinPoly, d = self._make_monic_integral(self._polynomial) curveIdeal = tmpAuxRing.ideal(intMinPoly) - singular.lib('normal.lib') #loading genus method in Singular + singular.lib('normal.lib') # loading genus method in Singular return int(curveIdeal._singular_().genus()) else: @@ -1170,7 +1171,7 @@ def _simple_model(self, name='v'): B = MS(B) M_b = V_to_N(B.solve_left(M_to_V(b))) M_a = V_to_N(B.solve_left(M_to_V(a))) - M_to_N = M.hom([M_a,M_b]) + M_to_N = M.hom([M_a, M_b]) return N, N_to_M, M_to_N @@ -1285,7 +1286,7 @@ def simple_model(self, name=None): if isinstance(self.base_field(), RationalFunctionField): # the extension is simple already if name == self.variable_name(): - id = Hom(self,self).identity() + id = Hom(self, self).identity() return self, id, id else: ret = self.base_field().extension(self.polynomial(), names=(name,)) @@ -1299,8 +1300,8 @@ def simple_model(self, name=None): self_ = base_.extension(self.polynomial().map_coefficients(to_base_), names=(name,)) gens_in_base_ = [to_base_(k.gen()) for k in base._intermediate_fields(base.rational_function_field())] - to_self_ = self.hom([self_.gen()]+gens_in_base_) - from_self_ = self_.hom([self.gen(),from_base_(base_.gen())]) + to_self_ = self.hom([self_.gen()] + gens_in_base_) + from_self_ = self_.hom([self.gen(), from_base_(base_.gen())]) # now collapse self_/base_/K(x) ret, ret_to_self_, self__to_ret = self_._simple_model(name) @@ -1549,8 +1550,8 @@ def separable_model(self, names=None): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self.constant_base_field(), names=names) S = R.remove_var(names[1]) - f = R( L.polynomial().change_variable_name(names[1]).map_coefficients( - lambda c:c.numerator().change_variable_name(names[0]), S)) + f = R(L.polynomial().change_variable_name(names[1]).map_coefficients( + lambda c: c.numerator().change_variable_name(names[0]), S)) f = f.polynomial(R.gen(0)).change_ring(K) f /= f.leading_coefficient() # f must be separable in the other variable (otherwise it would factor) @@ -1558,11 +1559,11 @@ def separable_model(self, names=None): ret = K.extension(f, names=(names[0],)) # isomorphisms between L and ret are given by swapping generators - ret_to_L = ret.hom( [L(L.base_field().gen()), L.gen()] ) - L_to_ret = L.hom( [ret(K.gen()), ret.gen()] ) + ret_to_L = ret.hom([L(L.base_field().gen()), L.gen()]) + L_to_ret = L.hom([ret(K.gen()), ret.gen()]) # compose with from_L and to_L to get the desired isomorphisms between self and ret - f = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - t = self.hom( [L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))] ) + f = ret.hom([from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))]) + t = self.hom([L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))]) return ret, f, t def change_variable_name(self, name): @@ -1637,13 +1638,13 @@ def change_variable_name(self, name): raise ValueError("name must contain at least one string") elif len(name) == 1: base = self.base_field() - from_base = to_base = Hom(base,base).identity() + from_base = to_base = Hom(base, base).identity() else: base, from_base, to_base = self.base_field().change_variable_name(name[1:]) ret = base.extension(self.polynomial().map_coefficients(to_base), names=(name[0],)) - f = ret.hom( [k.gen() for k in self._intermediate_fields(self.rational_function_field())] ) - t = self.hom( [k.gen() for k in ret._intermediate_fields(ret.rational_function_field())] ) + f = ret.hom([k.gen() for k in self._intermediate_fields(self.rational_function_field())]) + t = self.hom([k.gen() for k in ret._intermediate_fields(ret.rational_function_field())]) return ret, f, t @@ -1701,7 +1702,7 @@ def _inversion_isomorphism(self): x |--> x) """ K = self.base_field() - R = PolynomialRing(K,'T') + R = PolynomialRing(K, 'T') x = K.gen() xinv = 1/x @@ -1709,8 +1710,8 @@ def _inversion_isomorphism(self): F_poly = R([h(c) for c in self.polynomial().list()]) F = K.extension(F_poly) - self2F = self.hom([F.gen(),xinv]) - F2self = F.hom([self.gen(),xinv]) + self2F = self.hom([F.gen(), xinv]) + F2self = F.hom([self.gen(), xinv]) M, M2F, F2M = F.monic_integral_model('s') @@ -1844,7 +1845,7 @@ def genus(self): The genus is computed by the Hurwitz genus formula. """ k, _ = self.exact_constant_field() - different_degree = self.different().degree() # must be even + different_degree = self.different().degree() # must be even return Integer(different_degree // 2 - self.degree() / k.degree()) + 1 def residue_field(self, place, name=None): @@ -2123,7 +2124,7 @@ def _places_finite(self, degree): for d in degree.divisors(): for p in K._places_finite(degree=d): - for prime,_,_ in O.decomposition(p.prime_ideal()): + for prime, _, _ in O.decomposition(p.prime_ideal()): place = prime.place() if place.degree() == degree: yield place @@ -2167,7 +2168,7 @@ def _places_infinite(self, degree): """ Oinf = self.maximal_order_infinite() - for prime,_,_ in Oinf.decomposition(): + for prime, _, _ in Oinf.decomposition(): place = prime.place() if place.degree() == degree: yield place @@ -2312,7 +2313,7 @@ def number_of_rational_places(self, r=1): L = self.L_polynomial() Lp = L.derivative() - R = IntegerRing()[[L.parent().gen()]] # power series ring + R = IntegerRing()[[L.parent().gen()]] # power series ring f = R(Lp / L, prec=r) n = f[r-1] + q**r + 1 @@ -2408,13 +2409,13 @@ def _maximal_order_basis(self): from .hermite_form_polynomial import reversed_hermite_form k = self.constant_base_field() - K = self.base_field() # rational function field + K = self.base_field() # rational function field n = self.degree() # Construct the defining polynomial of the function field as a # two-variate polynomial g in the ring k[y,x] where k is the constant # base field. - S,(y,x) = PolynomialRing(k, names='y,x', order='lex').objgens() + S, (y, x) = PolynomialRing(k, names='y,x', order='lex').objgens() v = self.polynomial().list() g = sum([v[i].numerator().subs(x) * y**i for i in range(len(v))]) @@ -2431,7 +2432,7 @@ def _maximal_order_basis(self): gflat = R.zero() for m in g.monomials(): c = g.monomial_coefficient(m).polynomial('zz') - gflat += R(c) * R(m) # R(m) is a monomial in yy and xx + gflat += R(c) * R(m) # R(m) is a monomial in yy and xx k_poly = R(k.polynomial('zz')) @@ -2439,7 +2440,7 @@ def _maximal_order_basis(self): pols_in_R = normalize(R.ideal([k_poly, gflat])) # reconstruct polynomials in S - h = R.hom([y,x,k.gen()],S) + h = R.hom([y, x, k.gen()], S) pols_in_S = [h(f) for f in pols_in_R] else: # Call Singular. Singular's "normal" function returns a basis diff --git a/src/sage/rings/function_field/function_field_rational.py b/src/sage/rings/function_field/function_field_rational.py index 226d87d084c..8a0a6f25764 100644 --- a/src/sage/rings/function_field/function_field_rational.py +++ b/src/sage/rings/function_field/function_field_rational.py @@ -323,7 +323,7 @@ def _to_polynomial(self, f): """ K = f.parent().constant_base_field() if f.denominator() in K: - return f.numerator()/K(f.denominator()) + return f.numerator() / K(f.denominator()) raise ValueError("only polynomials can be converted to the underlying polynomial ring") def _to_bivariate_polynomial(self, f): @@ -350,7 +350,8 @@ def _to_bivariate_polynomial(self, f): v = f.list() denom = lcm([a.denominator() for a in v]) S = denom.parent() - x,t = S.base_ring()['%s,%s' % (f.parent().variable_name(),self.variable_name())].gens() + x, t = S.base_ring()['%s,%s' % (f.parent().variable_name(), + self.variable_name())].gens() phi = S.hom([t]) return sum([phi((denom * v[i]).numerator()) * x**i for i in range(len(v))]), denom @@ -420,12 +421,12 @@ def _factor_univariate_polynomial(self, f, proof=None): x = f.parent().gen() t = f.parent().base_ring().gen() phi = F.parent().hom([x, t]) - v = [(phi(P),e) for P, e in fac] - unit = phi(fac.unit())/d + v = [(phi(P), e) for P, e in fac] + unit = phi(fac.unit()) / d w = [] for a, e in v: c = a.leading_coefficient() - a = a/c + a = a / c # undo any variable substitution that we introduced for the bivariate polynomial if old_variable_name != a.variable_name(): a = a.change_variable_name(old_variable_name) @@ -433,7 +434,7 @@ def _factor_univariate_polynomial(self, f, proof=None): if a.is_unit(): unit *= a**e else: - w.append((a,e)) + w.append((a, e)) from sage.structure.factorization import Factorization return Factorization(w, unit=unit) @@ -553,7 +554,7 @@ def free_module(self, base=None, basis=None, map=True): if not map: return V from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) + to_V = MapFunctionFieldToVectorSpace(self, V) return (V, from_V, to_V) def random_element(self, *args, **kwds): @@ -683,7 +684,7 @@ def hom(self, im_gens, base_morphism=None): """ if isinstance(im_gens, CategoryObject): return self.Hom(im_gens).natural_map() - if not isinstance(im_gens, (list,tuple)): + if not isinstance(im_gens, (list, tuple)): im_gens = [im_gens] if len(im_gens) != 1: raise ValueError("there must be exactly one generator") @@ -834,8 +835,8 @@ def change_variable_name(self, name): raise ValueError("names must be a tuple with a single string") name = name[0] if name == self.variable_name(): - id = Hom(self,self).identity() - return self,id,id + id = Hom(self, self).identity() + return self, id, id else: from .constructor import FunctionField ret = FunctionField(self.constant_base_field(), name) diff --git a/src/sage/rings/function_field/ideal_polymod.py b/src/sage/rings/function_field/ideal_polymod.py index 6735dd6a8fa..da45bc508cf 100644 --- a/src/sage/rings/function_field/ideal_polymod.py +++ b/src/sage/rings/function_field/ideal_polymod.py @@ -27,6 +27,7 @@ from .ideal import FunctionFieldIdeal, FunctionFieldIdealInfinite + class FunctionFieldIdeal_polymod(FunctionFieldIdeal): """ Fractional ideals of algebraic function fields @@ -407,7 +408,7 @@ def _mul_(self, other): g1, g2 = other._gens_two_vecs vecs = list(g1 * self._hnf) + [mul(g2, v) for v in self._hnf] else: - vecs = [mul(r1,r2) for r1 in self._hnf for r2 in other._hnf] + vecs = [mul(r1, r2) for r1 in self._hnf for r2 in other._hnf] return O._ideal_from_vectors_and_denominator(vecs, self._denominator * other._denominator) @@ -488,7 +489,7 @@ def intersect(self, other): n = A.ncols() # intersect the row spaces of A and B - M = block_matrix([[I,I],[A,O],[O,B]]) + M = block_matrix([[I, I], [A, O], [O, B]]) # reversed Hermite form U = reversed_hermite_form(M, transformation=True) @@ -601,7 +602,7 @@ def gens_over_base(self): sage: I.gens_over_base() (x^3 + 1, y + x) """ - gens, d = self._gens_over_base + gens, d = self._gens_over_base return tuple([~d * b for b in gens]) @lazy_attribute @@ -620,9 +621,8 @@ def _gens_over_base(self): sage: I._gens_over_base ([x, y], x) """ - gens = [] - for row in self._hnf: - gens.append(sum([c1*c2 for c1,c2 in zip(row, self._ring.basis())])) + gens = [sum([c1 * c2 for c1, c2 in zip(row, self._ring.basis())]) + for row in self._hnf] return gens, self._denominator def gens(self): @@ -864,7 +864,7 @@ def is_prime(self): [True, True] """ factors = self.factor() - if len(factors) == 1 and factors[0][1] == 1: # prime! + if len(factors) == 1 and factors[0][1] == 1: # prime! prime = factors[0][0] assert self == prime self._relative_degree = prime._relative_degree @@ -1013,13 +1013,13 @@ def _factor(self): i = d * self factors = [] - primes = set([o.ideal(p) for p,_ in d.factor()] + [p for p,_ in i.ideal_below().factor()]) + primes = set([o.ideal(p) for p, _ in d.factor()] + [p for p, _ in i.ideal_below().factor()]) for prime in primes: qs = [q[0] for q in O.decomposition(prime)] for q in qs: exp = q.valuation(self) if exp != 0: - factors.append((q,exp)) + factors.append((q, exp)) return factors @@ -1100,10 +1100,10 @@ def __pow__(self, mod): J = [ppow * v for v in I] else: p, q = self._gens_two_vecs - q = sum(e1 * e2 for e1,e2 in zip(O.basis(), q)) + q = sum(e1 * e2 for e1, e2 in zip(O.basis(), q)) ppow = p**mod qpow = O._coordinate_vector(q**mod) - J = [ppow * v for v in I] + [mul(qpow,v) for v in I] + J = [ppow * v for v in I] + [mul(qpow, v) for v in I] return O._ideal_from_vectors_and_denominator(J, self._denominator**mod) @@ -1171,7 +1171,7 @@ def gens_two(self): of Function field in y defined by y^2 + y + (x^2 + 1)/x """ d = self.denominator() - return tuple(e/d for e in self._gens_two()) + return tuple(e / d for e in self._gens_two()) @cached_method def _gens_two(self): @@ -1219,18 +1219,18 @@ def _gens_two(self): O = self.ring() F = O.fraction_field() - if self._kummer_form is not None: # prime ideal + if self._kummer_form is not None: # prime ideal _g1, _g2 = self._kummer_form g1 = F(_g1) - g2 = sum([c1*c2 for c1,c2 in zip(_g2, O.basis())]) + g2 = sum([c1 * c2 for c1, c2 in zip(_g2, O.basis())]) if g2: self._gens_two_vecs = (_g1, _g2) - return (g1,g2) + return (g1, g2) else: self._gens_two_vecs = (_g1,) return (g1,) - ### start to search for two generators + # ---- start to search for two generators hnf = self._hnf @@ -1238,7 +1238,7 @@ def _gens_two(self): for e in hnf.diagonal(): norm *= e - if norm.is_constant(): # unit ideal + if norm.is_constant(): # unit ideal self._gens_two_vecs = (1,) return (F(1),) @@ -1247,7 +1247,7 @@ def _gens_two(self): p = _l.degree() l = F(_l) - if self._hnf == O.ideal(l)._hnf: # principal ideal + if self._hnf == O.ideal(l)._hnf: # principal ideal self._gens_two_vecs = (_l,) return (l,) @@ -1255,13 +1255,13 @@ def _gens_two(self): basis = [] for row in hnf: - basis.append(sum([c1*c2 for c1,c2 in zip(row, O.basis())])) + basis.append(sum([c1 * c2 for c1, c2 in zip(row, O.basis())])) n = len(basis) alpha = None def check(alpha): - alpha_norm = alpha.norm().numerator() # denominator is 1 + alpha_norm = alpha.norm().numerator() # denominator is 1 return norm.gcd(alpha_norm // norm) == 1 # Trial 1: search for alpha among generators @@ -1273,9 +1273,9 @@ def check(alpha): # Trial 2: exhaustive search for alpha using only polynomials # with coefficients 0 or 1 for d in range(p): - G = itertools.product(itertools.product([0,1],repeat=d+1), repeat=n) + G = itertools.product(itertools.product([0, 1], repeat=d + 1), repeat=n) for g in G: - alpha = sum([R(c1)*c2 for c1,c2 in zip(g, basis)]) + alpha = sum([R(c1) * c2 for c1, c2 in zip(g, basis)]) if check(alpha): self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) return (l, alpha) @@ -1293,7 +1293,7 @@ def check(alpha): if g[j].leading_coefficient() != 1: continue - alpha = sum([c1*c2 for c1,c2 in zip(g, basis)]) + alpha = sum([c1 * c2 for c1, c2 in zip(g, basis)]) if check(alpha): self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) return (l, alpha) @@ -1383,7 +1383,7 @@ def __contains__(self, x): True """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return to_iF(x) in self._ideal def _add_(self, other): @@ -1580,7 +1580,7 @@ def gens(self): (x, y) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(b) for b in self._ideal.gens()) def gens_two(self): @@ -1606,7 +1606,7 @@ def gens_two(self): (x,) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(b) for b in self._ideal.gens_two()) def gens_over_base(self): @@ -1624,7 +1624,7 @@ def gens_over_base(self): (x, y, 1/x^2*y^2) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(g) for g in self._ideal.gens_over_base()) def ideal_below(self): diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 97f5a625819..067ca2ed090 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -547,7 +547,7 @@ def codifferent(self): + 6*x/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4)) of Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 """ - T = self._codifferent_matrix() + T = self._codifferent_matrix() return self._ideal_from_vectors(T.inverse().columns()) @cached_method @@ -686,7 +686,7 @@ def decomposition(self, ideal): m = [] for g in q.basis_matrix(): m.extend(matrix([g * mr for mr in matrices_reduced]).columns()) - beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] + beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] r = q index = 1 @@ -1291,7 +1291,7 @@ def decomposition(self, ideal): m = [] for i in range(n): m.append(sum(qgenb[j] * mtable[i][j] for j in range(n))) - beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] + beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] prime.is_prime.set_cache(True) prime._prime_below = ideal diff --git a/src/sage/rings/function_field/place_polymod.py b/src/sage/rings/function_field/place_polymod.py index e11a6c86ab7..f345c31129e 100644 --- a/src/sage/rings/function_field/place_polymod.py +++ b/src/sage/rings/function_field/place_polymod.py @@ -463,7 +463,7 @@ def _residue_field(self, name=None): prime = self.prime_ideal() # Let P be this prime ideal if self.is_infinite_place(): - _F, from_F, to_F = F._inversion_isomorphism() + _F, from_F, to_F = F._inversion_isomorphism() _prime = prime._ideal _place = _prime.place() diff --git a/src/sage/rings/generic.py b/src/sage/rings/generic.py index e0145c8e828..bfcb1d2bc50 100644 --- a/src/sage/rings/generic.py +++ b/src/sage/rings/generic.py @@ -36,7 +36,7 @@ class ProductTree: Similarly, the :meth:`interpolation` method can be used to implement the inverse Fast Fourier Transform:: - sage: tree.interpolation(zs).padded_list(len(ys)) == ys + sage: tree.interpolation(zs).padded_list(len(ys)) == ys # needs sage.rings.finite_rings True This class encodes the tree as *layers*: Layer `0` is just a tuple diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 83d56d432c4..559de2065e2 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -56,7 +56,7 @@ import sage.rings.rational_field import sage.rings.ideal import sage.libs.pari.all import sage.rings.ideal -from sage.categories.basic import EuclideanDomains +from sage.categories.basic import EuclideanDomains, DedekindDomains from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.rings.number_field.number_field_element_base import NumberFieldElement_base from sage.structure.coerce cimport is_numpy_type @@ -122,7 +122,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sage: Z.is_field() False sage: Z.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Z(2^(2^5) + 1) @@ -312,7 +313,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): True """ Parent.__init__(self, base=self, names=('x',), normalize=False, - category=(EuclideanDomains(), InfiniteEnumeratedSets().Metric())) + category=(EuclideanDomains(), DedekindDomains(), InfiniteEnumeratedSets().Metric())) self._populate_coercion_lists_(init_no_parent=True, convert_method_name='_integer_') @@ -852,17 +853,6 @@ cdef class IntegerRing_class(PrincipalIdealDomain): except TypeError: return False - def is_noetherian(self): - """ - Return ``True`` since the integers are a Noetherian ring. - - EXAMPLES:: - - sage: ZZ.is_noetherian() - True - """ - return True - def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. @@ -1132,6 +1122,11 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ Return the Krull dimension of the integers, which is 1. + .. NOTE:: + + This should rather be inherited from the category + of ``DedekindDomains``. + EXAMPLES:: sage: ZZ.krull_dimension() @@ -1143,6 +1138,11 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ Return that the integer ring is, in fact, integrally closed. + .. NOTE:: + + This should rather be inherited from the category + of ``DedekindDomains``. + EXAMPLES:: sage: ZZ.is_integrally_closed() diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index 187ac847522..cf77a8bb5a7 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -154,7 +154,8 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing): sage: LaurentSeriesRing(ZZ, 'x').category() Category of infinite commutative no zero divisors algebras - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: LaurentSeriesRing(QQ, 'x').category() Join of Category of complete discrete valuation fields and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets @@ -225,7 +226,8 @@ def __init__(self, power_series): sage: RZZ = LaurentSeriesRing(ZZ, 't') sage: RZZ.category() Category of infinite commutative no zero divisors algebras - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: TestSuite(RZZ).run() sage: R1 = LaurentSeriesRing(Zmod(1), 't') diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index ba444712df8..fb9990d0fce 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1474,7 +1474,8 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: TestSuite(L).run() sage: L.category() Category of infinite commutative no zero divisors algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: L = LazyLaurentSeriesRing(QQ, 't') sage: TestSuite(L).run() @@ -1488,7 +1489,8 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: L.category() Category of infinite commutative no zero divisors algebras over (unique factorization domains and commutative algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) and infinite sets) sage: L = LazyLaurentSeriesRing(GF(5), 't') diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index c65f75a248c..5c945bca692 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -410,6 +410,7 @@ from sage.rings import ideal import sage.structure.all from sage.structure.richcmp cimport (richcmp, rich_to_bool) from sage.misc.cachefunc import cached_method +from sage.categories.facade_sets import FacadeSets cdef class RingMap(Morphism): @@ -1951,6 +1952,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): y |--> x + y """ D = self.domain() + if D in FacadeSets(): + D, = D.facade_for() ig = self._im_gens s = '\n'.join('{} |--> {}'.format(D.gen(i), ig[i]) for i in range(D.ngens())) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 51b96a6c0c0..5fd638f9396 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -100,7 +100,7 @@ from sage.rings.finite_rings.integer_mod import mod from sage.categories.number_fields import NumberFields -import sage.rings.ring +from sage.rings.ring import Ring from sage.misc.latex import latex_variable_name from .unit_group import UnitGroup @@ -3536,7 +3536,7 @@ def ideal(self, *gens, **kwds): try: return self.fractional_ideal(*gens, **kwds) except ValueError: - return sage.rings.ring.Ring.ideal(self, gens, **kwds) + return Ring.ideal(self, gens, **kwds) def idealchinese(self, ideals, residues): r""" diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 8a7f6272da5..b80a4a407ef 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -3503,10 +3503,10 @@ def quotient_char_p(I, p): if not I.is_integral(): raise ValueError("I must be an integral ideal.") - K = I.number_field() - OK = K.maximal_order() # will in the long run only really need a p-maximal order. + K = I.number_field() + OK = K.maximal_order() # will in the long run only really need a p-maximal order. M_OK = OK.free_module() - M_I = I.free_module() + M_I = I.free_module() # Now we have to quite explicitly find a way to compute # with OK / I viewed as a quotient of two F_p vector spaces, diff --git a/src/sage/rings/number_field/selmer_group.py b/src/sage/rings/number_field/selmer_group.py index 37abe43101e..87221473348 100644 --- a/src/sage/rings/number_field/selmer_group.py +++ b/src/sage/rings/number_field/selmer_group.py @@ -129,7 +129,7 @@ def _coords_in_C_p(I, C, p): """ cyclic_orders = C.gens_orders() non_p_indices = [i for i,n in enumerate(cyclic_orders) if not p.divides(n)] - p_indices = [(i, n // p) for i,n in enumerate(cyclic_orders) if p.divides(n)] + p_indices = [(i, n // p) for i,n in enumerate(cyclic_orders) if p.divides(n)] coords = C(I).exponents() if all(coords[i] == 0 for i in non_p_indices) and all(coords[i] % n == 0 for i, n in p_indices): @@ -227,7 +227,7 @@ def _root_ideal(I, C, p): """ cyclic_orders = C.gens_orders() - cyclic_gens = C.gens_ideals() + cyclic_gens = C.gens_ideals() coords = C(I).exponents() # In the next line, e=(ci/p)%n should satisfy p*e=ci (mod n): we @@ -651,7 +651,7 @@ def to_KSp(a): if debug: assert all(v % p == 0 for v in vals) one = K(1) if K == QQ else K.ideal(1) - aa = a.abs() if K == QQ else K.ideal(a) + aa = a.abs() if K == QQ else K.ideal(a) B = prod((P ** (v // p) for P, v in zip(supp,vals)), one) if debug: assert B ** p == aa diff --git a/src/sage/rings/number_field/splitting_field.py b/src/sage/rings/number_field/splitting_field.py index bf04391a554..0bf41e50966 100644 --- a/src/sage/rings/number_field/splitting_field.py +++ b/src/sage/rings/number_field/splitting_field.py @@ -147,7 +147,7 @@ def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=No - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate - number fields using PARI's ``polred()``. This usually speeds + number fields using PARI's ``polredbest()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. @@ -549,11 +549,9 @@ def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=No # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() - M = Mpol.polred(flag=3) - n = len(M[0])-1 - Lpol = M[1][n].change_variable_name("y") - LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) - MtoL = LtoM.modreverse() + M = Mpol.polredbest(1) + Lpol = M[0].change_variable_name("y") + MtoL = M[1].lift().change_variable_name("y").Mod(Lpol) else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index e2c1ebccb9e..0aa4e396596 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -305,6 +305,14 @@ def __init__(self, number_field, proof=True, S=None): sage: tuple(US(K(u)) for u in US.gens()) == US.gens() True + Bug :issue:`36386` (pari stack overflow while expanding units):: + + sage: d = 12936642 + sage: K = QuadraticField(d) + sage: K.unit_group(proof=False) + Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 12936642 with a = 3596.754370262167? + + """ proof = get_flag(proof, "number_field") K = number_field @@ -340,20 +348,20 @@ def __init__(self, number_field, proof=True, S=None): # compute the additional S-unit generators: if S: self.__S_unit_data = pK.bnfunits(pS) + # TODO: converting the factored matrix representation of bnfunits into polynomial + # form is a *big* waste of time + su = [pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0][0:len(S)]] + su = [K(u, check=False) for u in su] else: - self.__S_unit_data = pK.bnfunits() - # TODO: converting the factored matrix representation of bnfunits into polynomial - # form is a *big* waste of time - su_fu_tu = [pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0]] - - self.__nfu = len(pK.bnf_get_fu()) # number of fundamental units - self.__nsu = len(su_fu_tu) - self.__nfu - 1 # number of S-units - self.__ntu = pK.bnf_get_tu()[0] # order of torsion + su = [] + + self.__nfu = len(fu) # number of fundamental units + self.__nsu = len(su) # number of S-units + self.__ntu = pK.bnf_get_tu()[0] # order of torsion self.__rank = self.__nfu + self.__nsu - # Move the torsion unit first, then fundamental units then S-units - gens = [K(u, check=False) for u in su_fu_tu] - gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu] + # Put the torsion unit first, then fundamental units then S-units + gens = [K(pK.bnf_get_tu()[1], check=False)] + fu + su # Construct the abstract group: gens_orders = tuple([ZZ(self.__ntu)]+[ZZ(0)]*(self.__rank)) diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index b44ee143680..e11dcf4b123 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -17,7 +17,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** import sage.libs.pari.all as pari -import sage.rings.ring as ring +from sage.rings.ring import Ring from sage.structure.element import RingElement from sage.structure.richcmp import richcmp from sage.misc.fast_methods import Singleton @@ -158,7 +158,7 @@ def __int__(self): return int(self.__x) -class PariRing(Singleton, ring.Ring): +class PariRing(Singleton, Ring): """ EXAMPLES:: @@ -170,7 +170,7 @@ class PariRing(Singleton, ring.Ring): Element = Pari def __init__(self): - ring.Ring.__init__(self, self) + Ring.__init__(self, self) def __repr__(self): return 'Pseudoring of all PARI objects.' diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 4ac7d585763..dd601dd5f8d 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -188,7 +188,7 @@ def from_polynomial(self, p): """ Convert a polynomial into the ring of integer-valued polynomials. - This raises a ``ValueError`` if this is not possible. + This raises a :class:`ValueError` if this is not possible. INPUT: @@ -1174,6 +1174,45 @@ def _poly(self, i): return binomial(x, i) class Element(CombinatorialFreeModule.Element): - pass + def variable_shift(self, k=1): + r""" + Return the image by the shift of variables. + + On polynomials, the action is the shift + on variables `x \mapsto x + k`. + + INPUT: + + - `k` -- integer (default: 1) + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).B() + sage: B = A.basis() + sage: B[5].variable_shift() + B[4] + B[5] + sage: B[5].variable_shift(-1) + -B[0] + B[1] - B[2] + B[3] - B[4] + B[5] + + TESTS:: + + sage: B[5].variable_shift(0) + B[5] + sage: B[5].variable_shift().variable_shift(-1) + B[5] + """ + if k == 0: + return self + A = self.parent() + + def on_basis(n): + return {A._indices(j): binomial(k, n - j) + for j in range(n + 1)} + + from sage.data_structures.blas_dict import linear_combination + mc = self._monomial_coefficients + ret = linear_combination((on_basis(index), coeff) + for (index, coeff) in mc.items()) + return A.element_class(A, ret) B = Binomial diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index 7832488a2ef..893b81c87af 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -1345,14 +1345,20 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: x.subs({x: 2}, x=1) 1 + + sage: f.subs({1: 2}, x=1) + 3*z + 5 """ cdef list variables = list(self._parent.gens()) cdef Py_ssize_t i for i in range(len(variables)): if str(variables[i]) in kwds: variables[i] = kwds[str(variables[i])] - elif in_dict and variables[i] in in_dict: - variables[i] = in_dict[variables[i]] + elif in_dict: + if variables[i] in in_dict: + variables[i] = in_dict[variables[i]] + elif i in in_dict: + variables[i] = in_dict[i] return self(tuple(variables)) def is_constant(self): diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 0369cb2edbd..4522e8e0a84 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -178,8 +178,8 @@ def __call__(self, *x, **kwds): except AttributeError: K = self.parent().base_ring() y = K(0) - for (m,c) in self.element().dict().items(): - y += c*prod([ x[i]**m[i] for i in range(n) if m[i] != 0]) + for m, c in self.element().dict().items(): + y += c * prod(v ** e for v, e in zip(x, m) if e) return y def _richcmp_(self, right, op): @@ -1388,7 +1388,7 @@ def is_term(self): """ return len(self.element()) == 1 - def subs(self, fixed=None, **kw): + def subs(self, fixed=None, **kwds): """ Fix some given variables in a given multivariate polynomial and return the changed multivariate polynomials. The polynomial itself @@ -1400,10 +1400,9 @@ def subs(self, fixed=None, **kw): INPUT: - - ``fixed`` - (optional) dictionary of inputs - - ``**kw`` - named parameters + - ``**kwds`` - named parameters OUTPUT: new :class:`MPolynomial` @@ -1419,11 +1418,14 @@ def subs(self, fixed=None, **kw): 25*y^2 + y + 30 """ variables = list(self.parent().gens()) - for i in range(0,len(variables)): - if str(variables[i]) in kw: - variables[i] = kw[str(variables[i])] - elif fixed and variables[i] in fixed: - variables[i] = fixed[variables[i]] + for i in range(len(variables)): + if str(variables[i]) in kwds: + variables[i] = kwds[str(variables[i])] + elif fixed: + if variables[i] in fixed: + variables[i] = fixed[variables[i]] + elif i in fixed: + variables[i] = fixed[i] return self(tuple(variables)) def monomials(self): diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index fa6655ca516..8f10f30add7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -405,7 +405,7 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) sage: len(gb) 5 """ - R = self.ring() + R = self.ring() if not deg_bound: mself = magma(self) else: @@ -3615,7 +3615,7 @@ def std(self): ALGORITHM: Uses Singular's ``std`` command """ - if self.side() == 'twosided': + if self.side() == 'twosided': return self.twostd() return self.ring().ideal( self.__call_singular('std'), side=self.side()) # return self.__call_singular('std') diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd index a090a15ac34..d6350c020d3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd @@ -1,7 +1,7 @@ -cimport sage.rings.ring +from sage.rings.ring cimport CommutativeRing, Ring from sage.structure.parent cimport Parent -cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): +cdef class MPolynomialRing_base(CommutativeRing): cdef object _ngens cdef object _term_order cdef public object _has_singular diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index dc3bfdc2459..c102c1af41b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -32,7 +32,7 @@ def is_MPolynomialRing(x): return isinstance(x, MPolynomialRing_base) -cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): +cdef class MPolynomialRing_base(CommutativeRing): def __init__(self, base_ring, n, names, order): """ Create a polynomial ring in several variables over a commutative ring. @@ -93,7 +93,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): category = categories.rings.Rings().Finite() else: category = polynomial_default_category(base_ring.category(), n) - sage.rings.ring.Ring.__init__(self, base_ring, names, category=category) + Ring.__init__(self, base_ring, names, category=category) def is_integral_domain(self, proof=True): """ diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index a48e7e2a84a..71a01bd3b50 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -140,7 +140,7 @@ def eliminate_ll_ranked(ll_system, to_reduce, reduction_function=ll_red_nf_noredsb, reduce_ll_system=False, prot=False): - assert(ll_system) + assert ll_system from_ring = ll_system[0].ring() ll_ranks = rank(ll_system) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 295b2b59757..94d5854976a 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -43,7 +43,7 @@ AUTHORS: - David Zureick-Brown (2017-09): added is_weil_polynomial - Sebastian Oehms (2018-10): made :meth:`roots` and :meth:`factor` work over more - cases of proper integral domains (see :trac:`26421`) + cases of proper integral domains (see :issue:`26421`) """ @@ -411,11 +411,9 @@ cdef class Polynomial(CommutativePolynomial): return self._parent.zero() return self * self._parent(right) - def subs(self, *x, **kwds): + def subs(self, in_dict=None, *args, **kwds): r""" - Identical to ``self(*x)``. - - See the docstring for :meth:`__call__`. + Substitute the variable in ``self``. EXAMPLES:: @@ -434,15 +432,18 @@ cdef class Polynomial(CommutativePolynomial): ... TypeError: keys do not match self's parent """ - if len(x) == 1 and isinstance(x[0], dict): - g = self._parent.gen() - if g in x[0]: - return self(x[0][g]) - elif len(x[0]) > 0: - raise TypeError("keys do not match self's parent") - return self - return self(*x, **kwds) - substitute = subs + if not in_dict: + return self(*args, **kwds) + + if isinstance(in_dict, dict): + if len(in_dict) > 1: + raise TypeError("only the generator can be substituted, use __call__ instead") + k, v = next(iter(in_dict.items())) + if not k or k == self._parent.gen(): + return self(v, *args, **kwds) + raise TypeError("keys do not match self's parent") + + return self(in_dict, *args, **kwds) @cython.boundscheck(False) @cython.wraparound(False) @@ -614,7 +615,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F(y=1) x*t - The following shows that :trac:`2360` is indeed fixed. :: + The following shows that :issue:`2360` is indeed fixed. :: sage: R. = ZZ[] sage: P. = ZZ[] @@ -629,13 +630,13 @@ cdef class Polynomial(CommutativePolynomial): sage: f(x) 6*x^4 - The following shows that :trac:`9006` is also fixed. :: + The following shows that :issue:`9006` is also fixed. :: sage: f = ZZ['x'](1000000 * [1]) sage: f(1) 1000000 - The following test came up in :trac:`9051`:: + The following test came up in :issue:`9051`:: sage: # needs sage.rings.complex_interval_field sage: Cif = ComplexIntervalField(64) @@ -645,7 +646,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f(jj).center(), f(jj).diameter() (1.00000000000000000, 4.00000000000000000) - The following failed before the patch to :trac:`3979` + The following failed before the patch to :issue:`3979` :: @@ -701,7 +702,7 @@ cdef class Polynomial(CommutativePolynomial): ... TypeError: Wrong number of arguments - Check that :trac:`22317` is fixed:: + Check that :issue:`22317` is fixed:: sage: R = ZZ['x']['y']['z'] sage: d = R.gens_dict_recursive() @@ -709,7 +710,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p(x=QQ(0)) 0 - Check that :trac:`32513` is fixed:: + Check that :issue:`32513` is fixed:: sage: R. = PolynomialRing(ZZ) sage: x.substitute([]) @@ -717,7 +718,7 @@ cdef class Polynomial(CommutativePolynomial): sage: Polynomial.__call__(x, []) x - These were drastically slower prior to :trac:`33165`:: + These were drastically slower prior to :issue:`33165`:: sage: # needs sage.rings.finite_rings sage: R. = GF(31337)[] @@ -755,7 +756,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g(U(2)) -8268 - Sparse tests for :trac:`33165`:: + Sparse tests for :issue:`33165`:: sage: R. = PolynomialRing(QQ, sparse=True) sage: f = x^1000000 + 1 @@ -1031,7 +1032,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x^3 - 3 > 393939393 True - Test comparison with zero (:trac:`18633`):: + Test comparison with zero (:issue:`18633`):: sage: 0 < R(1) True @@ -1151,7 +1152,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pol[:6] 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x - Any other kind of slicing is an error, see :trac:`18940`:: + Any other kind of slicing is an error, see :issue:`18940`:: sage: f[1:3] Traceback (most recent call last): @@ -1262,7 +1263,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Verify that :trac:`16251` has been resolved, i.e., polynomials with + Verify that :issue:`16251` has been resolved, i.e., polynomials with unhashable coefficients are unhashable:: sage: K. = Qq(9) # needs sage.rings.padics @@ -1496,7 +1497,7 @@ cdef class Polynomial(CommutativePolynomial): 10 The polynomial has to be over a field of characteristic 0 (see - :trac:`24072`):: + :issue:`24072`):: sage: R. = GF(7)[] sage: f = SR(2*w^3 + 1); f # needs sage.symbolic @@ -2013,7 +2014,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Make sure :trac:`9093` is fixed:: + Make sure :issue:`9093` is fixed:: sage: R(1).is_square() True @@ -2024,7 +2025,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(0).is_square() True - Make sure :trac:`35860` is fixed:: + Make sure :issue:`35860` is fixed:: sage: S. = PolynomialRing(ZZ) sage: is_square(S(1), True)[1].parent() @@ -2048,7 +2049,375 @@ cdef class Polynomial(CommutativePolynomial): else: return (False, None) if root else False - def any_root(self, ring=None, degree=None, assume_squarefree=False): + def _distinct_degree_factorisation_squarefree(self): + """ + Helper function for any_irreducible_factor which computes + the distinct degree factorisation of `self`. + + Creates an iterator for all valid degrees `d`, and returns + tuples of the form `(a_d, d)` for a polynomial `a_d` the + product of irreducible polynomials of degree `d`. + + Assumes that this polynomial is squarefree. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 162) * (x^3 + 7*x + 161) * (x^7 + 9*x + 161) + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + 162, 1), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] + + :: + + sage: # needs sage.rings.finite_rings + sage: K. = GF(2^8) + sage: R. = K[] + sage: u1 = x + z8^6 + z8^3 + z8 + sage: u2 = x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1 + sage: u3 = x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8 + sage: f = u1 * u2 * u3 + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + z8^6 + z8^3 + z8, 1), + (x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1, 2), + (x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8, 3)] + """ + q = self.base_ring().order() # p^k + R, x = self.parent().objgen() + + # Initialise values + v = self + w = x + d = 0 + e = v.degree() + + # Iterate over all possible degrees with degree + while 2*(d + 1) <= e: + d = d + 1 + w = pow(w, q, v) + + ad = v.gcd(w - x) + if not ad.is_one(): + yield (ad, d) + v = v // ad + + e = v.degree() + + # Last case, v itself might be irreducible + if e > 0: + yield (v, e) + return + + def _cantor_zassenhaus_split_to_irreducible(self, degree): + """ + Helper function for :meth:`any_irreducible_factor` which computes + a factor from a polynomial of the form `self = prod g_i(x)` + with all `g_i(x)` irreducible of the same degree. Uses the + Cantor-Zassenhaus splitting method. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 112*x + 48 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 34 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 132 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(2) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^2 + z4) * (x + z4^2 + z4 + 1) + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + 1 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(1) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True + """ + R = self.parent() + q = self.base_ring().order() + + # Polynomial is already irreducible by the assumptions of the function + if self.degree() == degree: + return self + + # We expect to succeed with greater than 1/2 probability, + # so if we try 1000 times and fail, there's a bug somewhere. + for _ in range(1000): + # Sample a polynomial "uniformly" from R + # TODO: once #37118 has been merged, this can be made cleaner, + # as we will actually have access to uniform sampling. + # At the moment, we make an ugly call for polynomials of + # degree exactly 2*degree + 1, instead of polynomials of degree + # less than `self.degree()` as the original desc. of C-Z. + # Additionally, we can use the `monic` flag: + # R.random_element(degree=(self.degree() - 1), monic=True) + T = R.random_element(2*degree + 1).monic() + + # Need to handle odd and even characteristic separately + if q % 2: + h = self.gcd(pow(T, (q-1)//2, self) - 1) + else: + # Compute the trace of T with field of order 2^k + # sum T^(2^i) for i in range (degree * k) + # We use repeated squaring to avoid redundent multiplications + C, TT = T, T + for _ in range(degree * self.base_ring().degree() - 1): + TT = TT * TT % self + C += TT + h = self.gcd(C) + + hd = h.degree() + + # If we found a factor of desired degree, return it + if hd == degree: + return h + + # Else check if we have a non-trivial factor and keep going + if 0 < hd < self.degree(): + if 2*hd > self.degree(): + h = self // h + return h._cantor_zassenhaus_split_to_irreducible(degree) + + # If you are reaching this error, chances are there's a bug in the code. + raise AssertionError(f"no splitting of degree {degree} found for {self}") + + def _any_irreducible_factor_squarefree(self, degree=None): + """ + Helper function for any_irreducible_factor which computes + an irreducible factor from self, assuming the input is + squarefree. + + Does this by first computing the distinct degree factorisations + of self and then finds a factor with Cantor-Zassenhaus + splitting. + + If degree is not None, then only irreducible factors of degree + `degree` are searched for, otherwise the smallest degree factor + is found. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f = (x^2 + 50*x + 28) * (x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40) * (x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84) + sage: f._any_irreducible_factor_squarefree() + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=5) + x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40 + sage: f._any_irreducible_factor_squarefree(degree=7) + x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84 + sage: g = (x^2 + 50*x + 28) + sage: g._any_irreducible_factor_squarefree(degree=1) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 1 could be computed from x^2 + 50*x + 28 + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2 + z4) * (x^2 + x + z4^3 + 1) * (x^3 + (z4^3 + z4)*x^2 + z4^2 + 1) + sage: f._any_irreducible_factor_squarefree() + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=1) + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + x + z4^3 + 1 + sage: f._any_irreducible_factor_squarefree(degree=3) + x^3 + (z4^3 + z4)*x^2 + z4^2 + 1 + """ + # If the degree is not None we only want to check a single polynomial + if degree is not None: + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + if d == degree: + return poly._cantor_zassenhaus_split_to_irreducible(degree) + # Stop iterating early if the degree is too large + elif d > degree: + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + + # Otherwise we use the smallest possible d value + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + return poly._cantor_zassenhaus_split_to_irreducible(d) + raise ValueError(f"no irreducible factor could be computed from {self}") + + def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): + """ + Return an irreducible factor of this polynomial. + + INPUT: + + - ``degree`` (None or positive integer) -- (default: ``None``). + Used for polynomials over finite fields. If ``None``, returns + the the first factor found (usually the smallest). Otherwise, + attempts to return an irreducible factor of ``self`` of chosen + degree ``degree``. + + - ``assume_squarefree`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be squarefree. + + - ``assume_distinct_deg`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be the product of irreducible + polynomials of degree equal to ``degree``. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 40)^3 * (x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27)^2 * (x^6 + 50*x^5 + 143*x^4 + 162*x^2 + 109*x + 140) + sage: f.any_irreducible_factor() + x + 40 + sage: f.any_irreducible_factor(degree=5) + x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27 + + When the polynomial is known to be squarefree we can optimise the call + by setting ``assume_squarefree`` to be ``True``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(assume_squarefree=True) + x + 162 + sage: g.any_irreducible_factor(degree=3, assume_squarefree=True) + x^3 + 7*x + 161 + + If we ask for an irreducible factor which does not exist, the function + will throw a ``ValueError``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(degree=2, assume_squarefree=True) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 2 could be computed from x^4 + 162*x^3 + 7*x^2 + 154*x + 2 + + If we assume that the polynomial is product of irreducible polynomials of the + same degree, we must also supply the degree:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random + x + 98 + sage: h.any_irreducible_factor(assume_distinct_deg=True) + Traceback (most recent call last): + ... + ValueError: degree must be known if distinct degree factorisation is assumed + + Also works for extension fields and even characteristic:: + + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2)^4 * (x^2 + z4*x + z4) * (x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1) + sage: f.any_irreducible_factor() + x + z4^3 + z4^2 + sage: f.any_irreducible_factor(degree=2) # random + x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1 + + We can also use this function for polynomials which are not defined over finite + fields, but this simply falls back to a slow method of factorisation:: + + sage: R. = ZZ[] + sage: f = 3*x^4 + 2*x^3 + sage: f.any_irreducible_factor() + 3*x + 2 + """ + # Make sure the user inputted something reasonable for degree + if degree is not None: + degree = ZZ(degree) + if degree < 1: + raise ValueError(f"{degree = } must be positive") + + if assume_distinct_deg and degree is None: + raise ValueError("degree must be known if distinct degree factorisation is assumed") + + # When not working over a finite field, do the simple thing of factoring. + # If degree is none, we return the first factor, otherwise we iterate + # through and look for a polynomial with the desired degree. + from sage.categories.finite_fields import FiniteFields + if self.base_ring() not in FiniteFields(): + try: + factorisation = self.factor() + except (NotImplementedError, ValueError): + raise ValueError(f"cannot factor {self} over the base ring {self.base_ring()}") + if degree is None: + return factorisation[0][0] + for (poly, e) in factorisation: + if poly.degree() == degree: + return poly + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + + # For finite fields, we find irreducible factors in the following three steps: + # + # 1. Compute the squarefree decomposition of the polynomial `self` + # 2. For each squarefree polynomial find the distinct degree `d` + # factorisation, F, which is the product of degree `d` polynomials + # dividing the squarefree polynomial + # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a + # single linear factor and return the root. + # + # When degree is None, we check all degrees smaller than the degree of the + # squarefree polynomial, otherwise we work with only a single degree set by + # the user. + + # Initial checks for bad input + if self.degree() <= 0: + raise ValueError(f"there are no irreducible factors of {self}") + + # If we know the polynomial is square-free, we can start here + if assume_squarefree: + if assume_distinct_deg: + return self._cantor_zassenhaus_split_to_irreducible(degree) + return self._any_irreducible_factor_squarefree(degree) + + # Otherwise we compute the squarefree decomposition and check each + # polynomial for a root. If no poly has a root, we raise an error. + SFD = self.squarefree_decomposition() + SFD.sort() + for poly, _ in SFD: + try: + return poly._any_irreducible_factor_squarefree(degree) + except ValueError: + pass + + # If degree has been set, there could just be no factor of the desired degree + if degree: + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + # But if any degree is allowed then there should certainly be a factor if self has degree > 0 + raise AssertionError(f"no irreducible factor was computed for {self}. Bug.") + + def any_root(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ Return a root of this polynomial in the given ring. @@ -2068,6 +2437,15 @@ cdef class Polynomial(CommutativePolynomial): finite fields. If ``True``, this polynomial is assumed to be squarefree. + - ``assume_distinct_deg`` (bool) -- Used for polynomials over + finite fields. If ``True``, all factors of this polynomial + are assumed to have degree ``degree``. + + .. WARNING:: + + Negative degree input will be deprecated. Instead use + ``assume_distinct_deg``. + EXAMPLES:: sage: # needs sage.rings.finite_rings @@ -2078,26 +2456,30 @@ cdef class Polynomial(CommutativePolynomial): sage: f.factor() (7) * (x + 9) * (x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2) sage: f = x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2 - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a - sage: sorted(f.roots(GF(11^6, 'a'))) - [(10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, 1), - (a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, 1), - (9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, 1), - (2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, 1), - (a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, 1), - (10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4, 1)] - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a + sage: root = f.any_root(GF(11^6, 'a')) + sage: roots = sorted(f.roots(GF(11^6, 'a'), multiplicities=False)) + sage: roots + [10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, + a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, + 9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, + 2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, + a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, + 10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4] + sage: root in roots + True sage: # needs sage.rings.finite_rings - sage: g = (x-1)*(x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) + sage: g = (x-1) * (x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) sage: g.any_root(ring=GF(11^10, 'b'), degree=1) 1 - sage: g.any_root(ring=GF(11^10, 'b'), degree=2) - 5*b^9 + 4*b^7 + 4*b^6 + 8*b^5 + 10*b^2 + 10*b + 5 - sage: g.any_root(ring=GF(11^10, 'b'), degree=5) - 5*b^9 + b^8 + 3*b^7 + 2*b^6 + b^5 + 4*b^4 + 3*b^3 + 7*b^2 + 10*b + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=2) + sage: roots = (x^2 + 3*x + 9).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=5) + sage: roots = (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True TESTS:: @@ -2108,7 +2490,7 @@ cdef class Polynomial(CommutativePolynomial): ....: assert f(f.any_root(K)) == 0 Check that our Cantor-Zassenhaus implementation does not loop - over finite fields of even characteristic (see :trac:`16162`):: + over finite fields of even characteristic (see :issue:`16162`):: sage: # needs sage.rings.finite_rings sage: K. = GF(2**8) @@ -2151,7 +2533,7 @@ cdef class Polynomial(CommutativePolynomial): + a^27 + a^25 + a^23 + a^22 + a^20 + a^18 + a^16 + a^14 + a^11 + a^10 + a^8 + a^6 + a^5 + a^4 + a + 1 - Check that :trac:`21998` has been resolved:: + Check that :issue:`21998` has been resolved:: sage: # needs sage.rings.finite_rings sage: K. = GF(2^4) @@ -2167,164 +2549,81 @@ cdef class Polynomial(CommutativePolynomial): sage: (x^2 + 1).any_root() Traceback (most recent call last): ... - ValueError: no roots (non-field) x^2 + 1 - """ - if self.base_ring().is_finite() and self.base_ring().is_field(): - if self.degree() < 0: - return ring(0) - if self.degree() == 0: - raise ValueError("no roots A %s" % self) - if not assume_squarefree: - SFD = self.squarefree_decomposition() - SFD.sort() - for f, e in SFD: - try: - return f.any_root(ring, degree, True) - except ValueError: - pass - if self.degree() == 1 and (degree is None or degree == 1): - if ring is None: - return -self.get_unsafe(0) / self.get_unsafe(1) - else: - return ring(-self.get_unsafe(0) / self.get_unsafe(1)) - q = self.base_ring().order() - if ring is None: - allowed_deg_mult = Integer(1) - else: - if not (self.base_ring().is_field() and self.base_ring().is_finite()): - raise NotImplementedError - if ring.characteristic() != self.base_ring().characteristic(): - raise ValueError("ring must be an extension of the base ring") - if not (ring.is_field() and ring.is_finite()): - raise NotImplementedError - allowed_deg_mult = Integer(ring.factored_order()[0][1]) # generally it will be the quotient of this by the degree of the base ring. - if degree is None: - x = self._parent.gen() - if allowed_deg_mult == 1: - xq = pow(x,q,self) - self = self.gcd(xq-x) - degree = -1 - if self.degree() == 0: - raise ValueError("no roots B %s" % self) - else: - xq = x - d = Integer(0) - while True: - # one pass for roots that actually lie within ring. - e = self.degree() - if 2*d+2 > e: - # this polynomial has no factors dividing allowed_deg_mult - if allowed_deg_mult % e == 0: - degree = -e - break - while d < allowed_deg_mult: - d = d+1 - xq = pow(xq,q,self) - if d.divides(allowed_deg_mult): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if d == allowed_deg_mult: - break - if degree is None: - if allowed_deg_mult == 1: - raise ValueError("no roots C %s" % self) - xq = x - d = Integer(0) - while True: - # now another for roots that will lie in an extension. - e = self.degree() - if 2*d+2 > e: - # this polynomial is irreducible. - degree = -e - break - while True: - # we waste a little effort here in computing the xq again. - d = d+1 - xq = pow(xq,q,self) - if allowed_deg_mult.divides(d): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if degree == 0: - raise ValueError("degree should be nonzero") - R = self._parent - x = R.gen() - if degree > 0: - xq = x - d = 0 - while True: - e = self.degree() - if 2*d > e: - if degree != e: - raise ValueError("no roots D %s" % self) - break - d = d+1 - xq = pow(xq,q,self) - if d == degree: - break - A = self.gcd(xq-x) - if A != 1: - self = self // A - if d == degree: - self = self.gcd(xq-x) - if self.degree() == 0: - raise ValueError("no roots E %s" % self) - else: - degree = -degree - if ring is None: - if degree == 1: - ring = self.base_ring() - else: - ring = self.base_ring().extension(degree) # this won't work yet. - # now self has only roots of degree ``degree``. - # for now, we only implement the Cantor-Zassenhaus split - k = self.degree() // degree - if k == 1: - try: - return self.roots(ring, multiplicities=False)[0] # is there something better to do here? - except IndexError: - raise ValueError("no roots F %s" % self) - if q % 2 == 0: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue - T = T.monic() - C = T - for i in range(degree-1): - C = T + pow(C,q,self) - h = self.gcd(C) - hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) - else: - return (self//h).any_root(ring, -degree, True) - else: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue - T = T.monic() - h = self.gcd(pow(T, Integer((q**degree-1)/2), self)-1) - hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) - else: - return (self//h).any_root(ring, -degree, True) - else: + ValueError: polynomial x^2 + 1 has no roots + """ + # When not working over a finite field, do the simple thing of factoring for + # roots and picking the first root. If none are available, raise an error. + from sage.categories.finite_fields import FiniteFields + if not self.base_ring() in FiniteFields(): rs = self.roots(ring=ring, multiplicities=False) if rs: return rs[0] - raise ValueError("no roots (non-field) %s" % self) + raise ValueError(f"polynomial {self} has no roots") + + # When the degree is none, we only look for a linear factor + if degree is None: + # if a ring is given try and coerce the polynomial into this ring + if ring is not None: + try: + self = self.change_ring(ring) + except ValueError: + raise(f"cannot coerce polynomial {self} to the new ring: {ring}") + + # try and find a linear irreducible polynomial from f to compute a root + try: + f = self.any_irreducible_factor(degree=1, assume_squarefree=assume_squarefree) + except ValueError: + raise ValueError(f"no root of polynomial {self} can be computed") + + return - f[0] / f[1] + + # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial + # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and + # ensure that the degree is positive. + degree = ZZ(degree) + if degree < 0: + from sage.misc.superseded import deprecation + deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_distinct_deg`.") + degree = -degree + assume_distinct_deg = True + + # If a certain degree is requested, then we find an irreducible factor of degree `degree` + # use this to compute a field extension and return the generator as root of this polynomial + # if the degree and a ring is given however, instead compute a degree `degree` factor in the + # base ring and then find a factor from this in the supplied ring. + try: + f = self.any_irreducible_factor(degree=degree, + assume_squarefree=assume_squarefree, + assume_distinct_deg=assume_distinct_deg) + except ValueError: + raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") + + # For the case when the degree is one, just return the root + if degree.is_one(): + root = - f[0] / f[1] + if ring is None: + return root + return ring(root) + + # We are now in the case where the irreducible polynomial we have found is + # of degree > 1. The old version of this function simply computed the roots + # of this by calling f.roots(ring)... I don't really understand why + # though, as we can simply ask for f.any_root() for this polynomial over the + # new ring? + if ring is None: + # TODO: a faster option would be to create an extension with `f` + # as F_ext = self.base_ring().extension(f, names="a") + # however this returns a quotient ring rather than a + # FiniteField type if the base field is a non-prime field, + # so this slower option is chosen to ensure the root is + # over explicitly a FiniteField type. + ring = self.base_ring().extension(f.degree(), names="a") + + # Now we look for a linear root of this irreducible polynomial of degree `degree` + # over the user supplied ring or the extension we just computed. If the user sent + # a bad ring here of course there may be no root found. + f = f.change_ring(ring) + return f.any_root() def __truediv__(left, right): r""" @@ -2378,7 +2677,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`12217` is fixed:: + Check that :issue:`12217` is fixed:: sage: P. = GF(5)[] sage: x/0 @@ -2392,7 +2691,7 @@ cdef class Polynomial(CommutativePolynomial): ... ZeroDivisionError: division by zero in finite field - Check that :trac:`23611` is fixed:: + Check that :issue:`23611` is fixed:: sage: int(1) / x 1/x @@ -2469,7 +2768,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pow(f, 10**7, h) 4*x*t^3 + 2*x*t^2 + 4*x*t + 4 - Check that :trac:`18457` is fixed:: + Check that :issue:`18457` is fixed:: sage: R. = PolynomialRing(GF(5), sparse=True) sage: (1+x)^(5^10) @@ -2507,7 +2806,7 @@ cdef class Polynomial(CommutativePolynomial): 18009460*y^6*x^6 + 2349060*y^5*x^5 + ... + 51*y*x + 1 Check that fallback method is used when it is not possible to compute - the characteristic of the base ring (:trac:`24308`):: + the characteristic of the base ring (:issue:`24308`):: sage: # needs sage.libs.singular sage: kk. = GF(2)[] @@ -2702,7 +3001,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - We verify that :trac:`23020` has been resolved. (There are no + We verify that :issue:`23020` has been resolved. (There are no elements in the Sage library yet that do not implement ``__bool__``, so we have to create one artificially.):: @@ -2795,7 +3094,7 @@ cdef class Polynomial(CommutativePolynomial): sage: latex(x+2) x + 2.0 - The following illustrates the fix of :trac:`2586`:: + The following illustrates the fix of :issue:`2586`:: sage: latex(ZZ['alpha']['b']([0, ZZ['alpha'].0])) \alpha b @@ -2982,7 +3281,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check the problem reported at :trac:`12529` is fixed:: + Check the problem reported at :issue:`12529` is fixed:: sage: # needs sage.rings.finite_rings sage: gens = 'y a0 a1 a2 b0 b1 b2 c1 c2 d0 d1 d2 d3 d4 d5 d6 d7'.split() @@ -3451,7 +3750,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`25022` is fixed:: + Check that :issue:`25022` is fixed:: sage: K. = ZZ[] sage: x.change_ring(SR) == SR['x'].gen() # needs sage.symbolic @@ -3459,7 +3758,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x.change_ring(ZZ['x']) == ZZ['x']['x'].gen() True - Check that :trac:`28541` is fixed:: + Check that :issue:`28541` is fixed:: sage: # needs sage.rings.finite_rings sage: F. = GF(7^2) @@ -3663,7 +3962,7 @@ cdef class Polynomial(CommutativePolynomial): 1.00000000000000 Check that the denominator is an element over the base whenever the base - has no :meth:`denominator` method. This closes :trac:`9063`. :: + has no :meth:`denominator` method. This closes :issue:`9063`. :: sage: R. = GF(5)[] sage: x = R(0) @@ -3678,7 +3977,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18518` is fixed:: + Check that :issue:`18518` is fixed:: sage: R. = PolynomialRing(QQ, sparse=True) sage: p = x^(2^100) - 1/2 @@ -3871,7 +4170,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(1).derivative(R(x)) 0 - Check that :trac:`28147` is fixed:: + Check that :issue:`28147` is fixed:: sage: # needs sage.rings.finite_rings sage: R. = GF(65537)[] @@ -3908,7 +4207,7 @@ cdef class Polynomial(CommutativePolynomial): ValueError: cannot differentiate with respect to y - Check that :trac:`26844` is fixed by :trac:`28147`:: + Check that :issue:`26844` is fixed by :issue:`28147`:: sage: A = PolynomialRing(GF(3), name='t') sage: K = A.fraction_field() @@ -3916,7 +4215,7 @@ cdef class Polynomial(CommutativePolynomial): sage: t.derivative(t) 1 - Check that :trac:`28187` is fixed:: + Check that :issue:`28187` is fixed:: sage: R. = GF(65537)[] # needs sage.rings.finite_rings sage: x._derivative(2*x) @@ -4001,7 +4300,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g.parent() Univariate Polynomial Ring in x over Rational Field - This shows that the issue at :trac:`7711` is resolved:: + This shows that the issue at :issue:`7711` is resolved:: sage: # needs sage.rings.finite_rings sage: P. = PolynomialRing(GF(2147483647)) @@ -4052,7 +4351,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: Sx. = ZZ[] sage: Sxy. = Sx[] @@ -4073,7 +4372,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.integral(x).derivative(x) == p True - Check that it works with non-integral domains (:trac:`18600`):: + Check that it works with non-integral domains (:issue:`18600`):: sage: x = polygen(Zmod(4)) sage: p = x**4 + 1 @@ -4304,7 +4603,7 @@ cdef class Polynomial(CommutativePolynomial): rings with composite characteristic is not implemented Factoring polynomials over the algebraic numbers (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = QQbar[] # needs sage.rings.number_field sage: (x^8 - 1).factor() # needs sage.rings.number_field @@ -4314,7 +4613,7 @@ cdef class Polynomial(CommutativePolynomial): * (x + 0.7071067811865475? + 0.7071067811865475?*I) * (x + 1) Factoring polynomials over the algebraic reals (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = AA[] # needs sage.rings.number_field sage: (x^8 + 1).factor() # needs sage.rings.number_field @@ -4325,7 +4624,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - This came up in :trac:`7088`:: + This came up in :issue:`7088`:: sage: R. = PolynomialRing(ZZ) sage: f = 12*x^10 + x^9 + 432*x^3 + 9011 @@ -4336,7 +4635,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F = f^2 * g^3 * 7; F.factor() # needs sage.libs.pari 7 * (12*x^10 + x^9 + 432*x^3 + 9011)^2 * (13*x^11 + 89*x^3 + 1)^3 - This example came up in :trac:`7097`:: + This example came up in :issue:`7097`:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4428,7 +4727,7 @@ cdef class Polynomial(CommutativePolynomial): sage: A(x^2 - 1/3).factor() (T - a) * (T + a) - Test that :trac:`10279` is fixed:: + Test that :issue:`10279` is fixed:: sage: # needs sage.rings.number_field sage: R. = PolynomialRing(QQ) @@ -4447,7 +4746,7 @@ cdef class Polynomial(CommutativePolynomial): ... sage: pari.default("debug", 0) - Test that :trac:`10369` is fixed:: + Test that :issue:`10369` is fixed:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4531,7 +4830,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(f) (x - a1) * (x^2 + a1*x + a1^2) - We check that :trac:`7554` is fixed:: + We check that :issue:`7554` is fixed:: sage: L. = LaurentPolynomialRing(QQ) sage: F = L.fraction_field() @@ -4549,14 +4848,14 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(p) # needs sage.libs.singular (a/(a + 2)) * (x - a) * (b*x + c)^2 - Check that :trac:`24973` is fixed:: + Check that :issue:`24973` is fixed:: sage: x1 = ZZ['x'].gen() sage: x2 = ZZ['x']['x'].gen() sage: (x1 - x2).factor() # needs sage.libs.singular -x + x - Check that :trac:`26421' is fixed:: + Check that :issue:`26421' is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -4564,7 +4863,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.factor() # needs sage.libs.singular (x - 5) * (x - 2*t) * (x^2 - 2) - Check that :trac:`29266` is fixed: + Check that :issue:`29266` is fixed: sage: f = t*x + t sage: f.is_irreducible() # needs sage.libs.singular @@ -5116,7 +5415,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`32033` has been fixed:: + Check that :issue:`32033` has been fixed:: sage: R. = GF(3)[] sage: lcm(R(0), R(0)) @@ -5730,7 +6029,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: c = x^2^100 + 1 @@ -5775,7 +6074,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(Zmod(4), sparse=True) sage: (2*x^2^100 + 2).is_nilpotent() @@ -6258,7 +6557,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + x^8 - 1).padded_list(10) @@ -6899,7 +7198,7 @@ cdef class Polynomial(CommutativePolynomial): sage: h.parent() is R # needs sage.libs.pari sage.modules True - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(2)[] sage: S. = R[] @@ -6908,7 +7207,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari t^4 + t - Check that :trac:`15061` is fixed:: + Check that :issue:`15061` is fixed:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -6917,7 +7216,7 @@ cdef class Polynomial(CommutativePolynomial): sage: P.resultant(P.derivative()) # needs sage.libs.pari -4 - 4*T + O(T^2) - Check that :trac:`16360` is fixed:: + Check that :issue:`16360` is fixed:: sage: K. = FunctionField(QQ) sage: R. = K[] @@ -6934,7 +7233,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari 0 - Check that :trac:`17817` is fixed:: + Check that :issue:`17817` is fixed:: sage: A. = Frac(PolynomialRing(QQ,'a,b,c')) sage: B. = PolynomialRing(A,'d,e,f') @@ -6948,7 +7247,7 @@ cdef class Polynomial(CommutativePolynomial): + (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 - Test for :trac:`10978`:: + Test for :issue:`10978`:: sage: # needs sage.libs.pari sage.rings.complex_double sage.symbolic sage: R. = PolynomialRing(CDF) @@ -7559,7 +7858,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.discriminant() # needs sage.libs.pari 1 - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(5)[] sage: S. = R[] @@ -7567,7 +7866,7 @@ cdef class Polynomial(CommutativePolynomial): sage: (f - t).discriminant() # needs sage.rings.finite_rings 4*t^5 - The following examples show that :trac:`11782` has been fixed:: + The following examples show that :issue:`11782` has been fixed:: sage: var('x') # needs sage.symbolic x @@ -7576,13 +7875,13 @@ cdef class Polynomial(CommutativePolynomial): sage: ZZ.quo(9)['x'](2*x^3 + x^2 + x).discriminant() # needs sage.libs.pari sage.symbolic 2 - This was fixed by :trac:`15422`:: + This was fixed by :issue:`15422`:: sage: R. = PolynomialRing(Qp(2)) # needs sage.rings.padics sage: (s^2).discriminant() # needs sage.rings.padics 0 - This was fixed by :trac:`16014`:: + This was fixed by :issue:`16014`:: sage: # needs sage.modules sage: PR. = QQ[] @@ -7594,7 +7893,7 @@ cdef class Polynomial(CommutativePolynomial): sage: det(mu*E1 + E2).discriminant().degrees() # needs sage.libs.pari (24, 12, 12, 8, 8, 8, 8) - This addresses an issue raised by :trac:`15061`:: + This addresses an issue raised by :issue:`15061`:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -7879,7 +8178,7 @@ cdef class Polynomial(CommutativePolynomial): [] For some other polynomials, no roots can be found at the moment - due to the way roots are computed. :trac:`17516` addresses + due to the way roots are computed. :issue:`17516` addresses these defects. Until that gets implemented, one such example is the following:: @@ -8194,12 +8493,12 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots(ring=QQbar) # needs sage.rings.number_field [(-5.9223865215328558?e225, 1), (5.9223865215328558?e225, 1)] - Check that :trac:`30522` is fixed:: + Check that :issue:`30522` is fixed:: sage: PolynomialRing(SR, names="x")("x^2").roots() # needs sage.symbolic [(0, 2)] - Check that :trac:`30523` is fixed:: + Check that :issue:`30523` is fixed:: sage: PolynomialRing(SR, names="x")("x^2 + q").roots() # needs sage.symbolic [(-sqrt(-q), 1), (sqrt(-q), 1)] @@ -8232,7 +8531,7 @@ cdef class Polynomial(CommutativePolynomial): :meth:`sage.symbolic.expression.Expression.solve` which in turn uses Maxima to find radical solutions. Some solutions may be lost in this approach. - Once :trac:`17516` gets implemented, all possible radical + Once :issue:`17516` gets implemented, all possible radical solutions should become available. If `L` is ``AA`` or ``RIF``, and `K` is ``ZZ``, ``QQ``, or ``AA``, then the root isolation @@ -8299,7 +8598,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(x^3 - 1) (x - 1) * (x^2 + x + 1) - This shows that the issue from :trac:`6237` is fixed:: + This shows that the issue from :issue:`6237` is fixed:: sage: R. = QQ[] sage: g = -27*u^14 - 32*u^9 @@ -8310,7 +8609,7 @@ cdef class Polynomial(CommutativePolynomial): [(-1.0345637159435719, 1), (0.0, 9), (-0.3196977699902601 - 0.9839285635706636*I, 1), (-0.3196977699902601 + 0.9839285635706636*I, 1), (0.8369796279620465 - 0.6081012947885318*I, 1), (0.8369796279620465 + 0.6081012947885318*I, 1)] - This shows that the issue at :trac:`2418` is fixed:: + This shows that the issue at :issue:`2418` is fixed:: sage: x = polygen(QQ) sage: p = (x^50/2^100 + x^10 + x + 1).change_ring(ComplexField(106)) # needs sage.rings.real_mpfr @@ -8319,7 +8618,7 @@ cdef class Polynomial(CommutativePolynomial): sage: [abs(p(rt)) < eps for rt in rts] == [True]*50 # needs sage.rings.number_field True - This shows that the issue at :trac:`10901` is fixed:: + This shows that the issue at :issue:`10901` is fixed:: sage: # needs sage.symbolic sage: a = var('a'); R. = SR[] @@ -8334,7 +8633,7 @@ cdef class Polynomial(CommutativePolynomial): TypeError: cannot evaluate symbolic expression to a numeric value We can find roots of polynomials defined over `\ZZ` or `\QQ` - over the `p`-adics, see :trac:`15422`:: + over the `p`-adics, see :issue:`15422`:: sage: R. = ZZ[] sage: pol = (x - 1)^2 @@ -8353,14 +8652,14 @@ cdef class Polynomial(CommutativePolynomial): sage: parent(r[0]) 3-adic Ring with capped relative precision 5 - Spurious crash with pari-2.5.5, see :trac:`16165`:: + Spurious crash with pari-2.5.5, see :issue:`16165`:: sage: f = (1+x+x^2)^3 sage: f.roots(ring=CC) # needs sage.libs.pari sage.rings.real_mpfr [(-0.500000000000000 - 0.866025403784439*I, 3), (-0.500000000000000 + 0.866025403784439*I, 3)] - Test a crash reported at :trac:`19649`:: + Test a crash reported at :issue:`19649`:: sage: polRing. = PolynomialRing(ZZ) sage: j = (x+1)^2 * (x-1)^7 * (x^2-x+1)^5 @@ -8370,19 +8669,19 @@ cdef class Polynomial(CommutativePolynomial): (0.500000000000000 - 0.866025403784439*I, 5), (0.500000000000000 + 0.866025403784439*I, 5)] - Test that some large finite rings can be handled (:trac:`13825`):: + Test that some large finite rings can be handled (:issue:`13825`):: sage: R. = IntegerModRing(20000009)[] # needs sage.libs.pari sage: eq = x^6+x-17 sage: eq.roots(multiplicities=False) # needs sage.libs.pari [3109038, 17207405] - Test that roots in fixed modulus p-adic fields work (:trac:`17598`):: + Test that roots in fixed modulus p-adic fields work (:issue:`17598`):: sage: len(cyclotomic_polynomial(3).roots(ZpFM(739, 566))) # needs sage.rings.padics 2 - Check that :trac:`26421` is fixed:: + Check that :issue:`26421` is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -8390,7 +8689,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots() # needs sage.libs.singular [(5, 1), (2*t, 1)] - Check that :trac:`31040` is fixed:: + Check that :issue:`31040` is fixed:: sage: R. = QQ[] sage: K. = Qq(3).extension(x^2 + 1) # needs sage.rings.padics @@ -8400,14 +8699,14 @@ cdef class Polynomial(CommutativePolynomial): + 2*a*3^11 + 2*a*3^12 + 2*a*3^13 + 2*a*3^14 + 2*a*3^15 + 2*a*3^16 + 2*a*3^17 + 2*a*3^18 + 2*a*3^19 + O(3^20), 1)] - Check that :trac:`31710` is fixed:: + Check that :issue:`31710` is fixed:: sage: CBF['x'].zero().roots(multiplicities=False) # needs sage.libs.flint Traceback (most recent call last): ... ArithmeticError: taking the roots of the zero polynomial - Check that :trac:`33979` is fixed:: + Check that :issue:`33979` is fixed:: sage: # needs sage.libs.pari sage: n = randint(2, 10^6) @@ -9078,7 +9377,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^4 + 3*t^2 + 1 @@ -9157,7 +9456,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^10 + 4*t^9 + 8*t^8 + 18*t^7 + 81*t^6 + 272*t^5 + 567*t^4 + 882*t^3 + 2744*t^2 + 9604*t + 16807 @@ -10036,7 +10335,7 @@ cdef class Polynomial(CommutativePolynomial): sage: radical(12 * x^5) 6*x - If self has a factor of multiplicity divisible by the characteristic (see :trac:`8736`):: + If self has a factor of multiplicity divisible by the characteristic (see :issue:`8736`):: sage: P. = GF(2)[] sage: (x^3 + x^2).radical() # needs sage.rings.finite_rings @@ -10128,7 +10427,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.norm(int(2)) 2.00000000000000 - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + 1).norm(1) # needs sage.rings.real_mpfr @@ -11759,7 +12058,7 @@ cdef class Polynomial_generic_dense(Polynomial): """ TESTS: - Check that exceptions are propagated correctly (:trac:`18274`):: + Check that exceptions are propagated correctly (:issue:`18274`):: sage: class BrokenRational(Rational): ....: def __bool__(self): @@ -11856,7 +12155,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`13048` and :trac:`2034` are fixed:: + Check that :issue:`13048` and :issue:`2034` are fixed:: sage: # needs sage.rings.number_field sage: R. = QQbar[] @@ -12004,7 +12303,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`12552` is fixed:: + Check that :issue:`12552` is fixed:: sage: type(f.degree()) @@ -12087,7 +12386,7 @@ cdef class Polynomial_generic_dense(Polynomial): ZeroDivisionError: division by zero polynomial Polynomials over noncommutative rings are also allowed - (after :trac:`34733`):: + (after :issue:`34733`):: sage: # needs sage.combinat sage.modules sage: HH = QuaternionAlgebra(QQ, -1, -1) @@ -12100,7 +12399,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - The following shows that :trac:`16649` is indeed fixed. :: + The following shows that :issue:`16649` is indeed fixed. :: sage: P. = QQ[] sage: R. = P[] @@ -12114,7 +12413,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: h == q*f + r and r.degree() < f.degree() True - :trac:`26907`:: + :issue:`26907`:: sage: P. = ZZ[] sage: R. = P[] @@ -12266,7 +12565,7 @@ cpdef Polynomial generic_power_trunc(Polynomial p, Integer n, long prec) noexcep sage: from sage.rings.polynomial.polynomial_element import generic_power_trunc - sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :trac:`32075`) + sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :issue:`32075`) ....: R = PolynomialRing(S, 'x') ....: for _ in range(100): ....: p = R.random_element() @@ -12482,7 +12781,7 @@ cdef class ConstantPolynomialSection(Map): """ This class is used for conversion from a polynomial ring to its base ring. - Since :trac:`9944`, it calls the :meth:`constant_coefficient` method, + Since :issue:`9944`, it calls the :meth:`constant_coefficient` method, which can be optimized for a particular polynomial type. EXAMPLES:: @@ -12549,7 +12848,7 @@ cdef class PolynomialBaseringInjection(Morphism): We demonstrate that most polynomial ring classes use polynomial base injection maps for coercion. They are supposed to be the fastest maps for that purpose. See - :trac:`9944`. :: + :issue:`9944`. :: sage: # needs sage.rings.padics sage: R. = Qp(3)[] @@ -12576,7 +12875,7 @@ cdef class PolynomialBaseringInjection(Morphism): From: Rational Field To: Univariate Polynomial Ring in x over Rational Field - By :trac:`9944`, there are now only very few exceptions:: + By :issue:`9944`, there are now only very few exceptions:: sage: PolynomialRing(QQ,names=[]).coerce_map_from(QQ) Call morphism: @@ -12709,7 +13008,7 @@ cdef class PolynomialBaseringInjection(Morphism): sage: S.coerce_map_from(R).is_injective() True - Check that :trac:`23203` has been resolved:: + Check that :issue:`23203` has been resolved:: sage: R.is_subring(S) # indirect doctest True diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 6b39d95752b..004078b4559 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -2089,7 +2089,7 @@ def _isomorphic_ring(self): base_image = self.base_ring().modulus().change_ring(isomorphic_ring).any_root() base_to_isomorphic_ring = self.base_ring().hom([isomorphic_ring(base_image)]) modulus = self.modulus().map_coefficients(base_to_isomorphic_ring) - gen = modulus.any_root(assume_squarefree=True, degree=-1) + gen = modulus.any_root(assume_squarefree=True, degree=1, assume_distinct_deg=True) homspace = Hom(self, isomorphic_ring) to_isomorphic_ring = homspace.__make_element_class__(SetMorphism)(homspace, diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 57947c345bf..0754ed31e7c 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -714,7 +714,7 @@ def minpoly(self): sage: (u + 1).minpoly() # needs sage.modules x^6 + 425*x^5 + 19*x^4 + 125*x^3 + 189*x^2 + 239*x + 302 sage: ext = F6.over(F2) # needs sage.modules - sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules + sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules # random x^3 + (396*i + 428)*x^2 + (80*i + 39)*x + 9*i + 178 TESTS: diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 8dd23a143a1..1ef3cbaf681 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2127,11 +2127,11 @@ cdef class Polynomial_rational_flint(Polynomial): :: - sage: # needs sage.libs.pari + sage: # needs sage.groups sage.libs.pari 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) # needs sage.groups + sage: PermutationGroup(G) 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 858df388caf..4d71f81b313 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -148,7 +148,8 @@ import sage.categories as categories from sage.categories.morphism import IdentityMorphism -import sage.rings.ring as ring +from sage.rings.ring import (Algebra, CommutativeAlgebra, IntegralDomain, + PrincipalIdealDomain, is_Ring) from sage.structure.element import is_RingElement import sage.rings.rational_field as rational_field from sage.rings.rational_field import QQ @@ -225,7 +226,7 @@ def is_PolynomialRing(x): ######################################################################################### -class PolynomialRing_general(ring.Algebra): +class PolynomialRing_general(Algebra): """ Univariate polynomial ring over a ring. """ @@ -244,7 +245,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: category(ZZ['x']) Join of Category of unique factorization domains and Category of commutative algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) and Category of infinite sets sage: category(GF(7)['x']) Join of Category of euclidean domains @@ -301,7 +303,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, self.Element = self._polynomial_class self.__cyclopoly_cache = {} self._has_singular = False - ring.Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) + Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) self._populate_coercion_lists_(convert_method_name='_polynomial_') def __reduce__(self): @@ -1707,7 +1709,7 @@ def monics( self, of_degree=None, max_degree=None ): raise ValueError("you should pass exactly one of of_degree and max_degree") -class PolynomialRing_commutative(PolynomialRing_general, ring.CommutativeAlgebra): +class PolynomialRing_commutative(PolynomialRing_general, CommutativeAlgebra): """ Univariate polynomial ring over a commutative ring. """ @@ -1834,7 +1836,7 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=True, algori class PolynomialRing_integral_domain(PolynomialRing_commutative, PolynomialRing_singular_repr, - ring.IntegralDomain): + IntegralDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, element_class=None, category=None): """ @@ -2054,8 +2056,9 @@ def construction(self): return categories.pushout.PolynomialFunctor(self.variable_name(), sparse=self.is_sparse(), implementation=implementation), self.base_ring() + class PolynomialRing_field(PolynomialRing_integral_domain, - ring.PrincipalIdealDomain): + PrincipalIdealDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, element_class=None, category=None): """ @@ -3593,7 +3596,7 @@ def polygen(ring_or_element, name="x"): """ if is_RingElement(ring_or_element): base_ring = ring_or_element.parent() - elif ring.is_Ring(ring_or_element): + elif is_Ring(ring_or_element): base_ring = ring_or_element else: raise TypeError("input must be a ring or ring element") diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index ba9b9453e76..42391e37f61 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -21,7 +21,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from sage.structure.category_object import normalize_names -import sage.rings.ring as ring +from sage.rings.ring import is_Ring, IntegralDomain try: import sage.rings.padics.padic_base_leaves as padic_base_leaves @@ -621,7 +621,7 @@ def PolynomialRing(base_ring, *args, **kwds): sage: R. = PolynomialRing(RIF,2) sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) """ - if not ring.is_Ring(base_ring): + if not is_Ring(base_ring): raise TypeError("base_ring {!r} must be a ring".format(base_ring)) n = -1 # Unknown number of variables @@ -860,7 +860,7 @@ def _multi_variate(base_ring, names, sparse=None, order="degrevlex", implementat if R is None and implementation == "generic": from . import multi_polynomial_ring - if isinstance(base_ring, ring.IntegralDomain): + if isinstance(base_ring, IntegralDomain): constructor = multi_polynomial_ring.MPolynomialRing_polydict_domain else: constructor = multi_polynomial_ring.MPolynomialRing_polydict diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 9d3ea67ee46..db5285ff66f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -46,7 +46,7 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.structure.category_object import normalize_names -from sage.rings.ring import Field +from sage.categories.fields import Fields from sage.matrix.matrix_space import MatrixSpace from sage.rings.morphism import RingHomomorphism @@ -85,17 +85,14 @@ def _base_ring_to_fraction_field(S): Ore Polynomial Ring in x over Fraction Field of Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 """ R = S.base_ring() - if isinstance(R, Field): + if R in Fields(): return S - else: - Q = R.fraction_field() - gens = R.gens() - sigmaS = S.twisting_morphism() - # try: - sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) - return Q[S.variable_name(), sigmaQ] - # except Exception, e: - # raise ValueError("unable to lift the twisting morphism to a twisting morphism over %s (error was: %s)" % (Q, e)) + + Q = R.fraction_field() + gens = R.gens() + sigmaS = S.twisting_morphism() + sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) + return Q[S.variable_name(), sigmaQ] def _minimal_vanishing_polynomial(R, eval_pts): diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py index 687545c0c39..48e49ac89fa 100644 --- a/src/sage/rings/polynomial/term_order.py +++ b/src/sage/rings/polynomial/term_order.py @@ -802,7 +802,7 @@ def __init__(self, name='lex', n=0, force=False): singular_str = [] macaulay2_str = [] - length_pattern = re.compile(r"\(([0-9]+)\)$") # match with parenthesized block length at end + length_pattern = re.compile(r"\(([0-9]+)\)$") # match with parenthesized block length at end for block in block_names: try: block_name, block_length, _ = re.split(length_pattern,block.strip()) @@ -1312,7 +1312,7 @@ def greater_tuple_deglex(self,f,g): """ sf = sum(f.nonzero_values(sort=False)) sg = sum(g.nonzero_values(sort=False)) - return ( sf > sg or ( sf == sg and f > g )) and f or g + return ( sf > sg or ( sf == sg and f > g )) and f or g def greater_tuple_degrevlex(self,f,g): """ @@ -1396,7 +1396,7 @@ def greater_tuple_negdeglex(self,f,g): """ sf = sum(f.nonzero_values(sort=False)) sg = sum(g.nonzero_values(sort=False)) - return ( sf < sg or ( sf == sg and f > g )) and f or g + return ( sf < sg or ( sf == sg and f > g )) and f or g def greater_tuple_degneglex(self,f,g): """ @@ -1474,7 +1474,7 @@ def greater_tuple_wdeglex(self,f,g): """ sf = sum(l*r for (l,r) in zip(f,self._weights)) sg = sum(l*r for (l,r) in zip(g,self._weights)) - return (sf > sg or ( sf == sg and f > g )) and f or g + return (sf > sg or ( sf == sg and f > g )) and f or g def greater_tuple_wdegrevlex(self,f,g): """ @@ -1531,7 +1531,7 @@ def greater_tuple_negwdeglex(self,f,g): """ sf = sum(l*r for (l,r) in zip(f,self._weights)) sg = sum(l*r for (l,r) in zip(g,self._weights)) - return (sf < sg or ( sf == sg and f > g )) and f or g + return (sf < sg or ( sf == sg and f > g )) and f or g def greater_tuple_negwdegrevlex(self,f,g): """ diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 9c286af01ec..4d92d64d80f 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -560,7 +560,6 @@ import sage.rings.abc import sage.rings.number_field.number_field_base -import sage.rings.ring from sage.arith.misc import factor from sage.categories.action import Action from sage.misc.cachefunc import cached_method diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index f67c7192f11..56829641624 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -422,7 +422,7 @@ def _div_(self, right): # We are computing L/R modulo the ideal. (L, R) = (self.__rep, right.__rep) - P = self.parent() + P = self.parent() I = P.defining_ideal() if not hasattr(I, 'groebner_basis'): diff --git a/src/sage/rings/real_mpfr.pxd b/src/sage/rings/real_mpfr.pxd index f89ec9557fd..6e06e5c5ea4 100644 --- a/src/sage/rings/real_mpfr.pxd +++ b/src/sage/rings/real_mpfr.pxd @@ -1,6 +1,5 @@ from sage.libs.mpfr.types cimport mpfr_rnd_t, mpfr_t, mpfr_prec_t -cimport sage.rings.ring cimport sage.rings.abc cimport sage.structure.element diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 650fb387fa0..2356eb44981 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -7,25 +7,25 @@ specific base classes. .. WARNING:: - Those classes, except maybe for the lowest ones like :class:`Ring`, - :class:`CommutativeRing`, :class:`Algebra` and :class:`CommutativeAlgebra`, + Those classes, except maybe for the lowest ones like + :class:`CommutativeRing` and :class:`CommutativeAlgebra`, are being progressively deprecated in favor of the corresponding categories. which are more flexible, in particular with respect to multiple inheritance. The class inheritance hierarchy is: -- :class:`Ring` +- :class:`Ring` (to be deprecated) - - :class:`Algebra` + - :class:`Algebra` (to be deprecated) - :class:`CommutativeRing` - - :class:`NoetherianRing` - - :class:`CommutativeAlgebra` - - :class:`IntegralDomain` + - :class:`NoetherianRing` (deprecated) + - :class:`CommutativeAlgebra` (to be deprecated) + - :class:`IntegralDomain` (deprecated) - - :class:`DedekindDomain` - - :class:`PrincipalIdealDomain` + - :class:`DedekindDomain` (deprecated and essentially removed) + - :class:`PrincipalIdealDomain` (deprecated) Subclasses of :class:`PrincipalIdealDomain` are @@ -43,7 +43,7 @@ are Noetherian PIDs. advance* whether or not a ring belongs in one of these classes; e.g. some orders in number fields are Dedekind domains, but others are not, and we still want to offer a unified interface, so orders are never instances of the -:class:`DedekindDomain` class.) +deprecated :class:`DedekindDomain` class.) AUTHORS: @@ -54,18 +54,32 @@ AUTHORS: - Simon King (2011-05-20): Modify multiplication and _ideal_class_ to support ideals of non-commutative rings. +TESTS: + +This is to test a deprecation:: + + sage: from sage.rings.ring import DedekindDomain + sage: class No(DedekindDomain): + ....: pass + sage: F = No(QQ) + ...: + DeprecationWarning: use the category DedekindDomains + See https://github.com/sagemath/sage/issues/37234 for details. + sage: F.category() + Category of Dedekind domains """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005, 2007 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecation from sage.structure.coerce cimport coercion_model from sage.structure.parent cimport Parent @@ -75,6 +89,7 @@ from sage.misc.prandom import randint from sage.categories.rings import Rings from sage.categories.commutative_rings import CommutativeRings from sage.categories.integral_domains import IntegralDomains +from sage.categories.dedekind_domains import DedekindDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains _Rings = Rings() @@ -1608,7 +1623,7 @@ cdef class IntegralDomain(CommutativeRing): This method is used by all the abstract subclasses of :class:`IntegralDomain`, like :class:`NoetherianRing`, - :class:`PrincipalIdealDomain`, :class:`DedekindDomain`, + :class:`PrincipalIdealDomain`, :class:`Field`, ... in order to avoid cascade calls Field.__init__ -> PrincipalIdealDomain.__init__ -> IntegralDomain.__init__ -> @@ -1753,136 +1768,13 @@ cdef class NoetherianRing(CommutativeRing): """ return True -cdef class DedekindDomain(IntegralDomain): - """ - Generic Dedekind domain class. - - A Dedekind domain is a Noetherian integral domain of Krull - dimension one that is integrally closed in its field of fractions. - - This class is deprecated, and not actually used anywhere in the - Sage code base. If you think you need it, please create a - category :class:`DedekindDomains`, move the code of this class - there, and use it instead. - """ - def krull_dimension(self): - """ - Return 1 since Dedekind domains have Krull dimension 1. - - EXAMPLES: - - The following are examples of Dedekind domains (Noetherian integral - domains of Krull dimension one that are integrally closed over its - field of fractions):: - sage: ZZ.krull_dimension() - 1 - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.krull_dimension() # needs sage.rings.number_field - 1 - - The following are not Dedekind domains but have - a ``krull_dimension`` function:: - - sage: QQ.krull_dimension() - 0 - sage: T. = PolynomialRing(QQ,2); T - Multivariate Polynomial Ring in x, y over Rational Field - sage: T.krull_dimension() - 2 - sage: U. = PolynomialRing(ZZ,3); U - Multivariate Polynomial Ring in x, y, z over Integer Ring - sage: U.krull_dimension() - 4 - - sage: # needs sage.rings.number_field - sage: K. = QuadraticField(-1) - sage: R = K.order(2*i); R - Order of conductor 2 generated by 2*i - in Number Field in i with defining polynomial x^2 + 1 with i = 1*I - sage: R.is_maximal() - False - sage: R.krull_dimension() - 1 - """ - return 1 - - def is_integrally_closed(self): - """ - Return ``True`` since Dedekind domains are integrally closed. - - EXAMPLES: - - The following are examples of Dedekind domains (Noetherian integral - domains of Krull dimension one that are integrally closed over its - field of fractions). - - :: - - sage: ZZ.is_integrally_closed() - True - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.is_integrally_closed() # needs sage.rings.number_field - True - - These, however, are not Dedekind domains:: - - sage: QQ.is_integrally_closed() - True - sage: S = ZZ[sqrt(5)]; S.is_integrally_closed() # needs sage.rings.number_field sage.symbolic - False - sage: T. = PolynomialRing(QQ, 2); T - Multivariate Polynomial Ring in x, y over Rational Field - sage: T.is_integral_domain() - True - """ - return True - - def integral_closure(self): - r""" - Return ``self`` since Dedekind domains are integrally closed. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') - sage: OK = K.ring_of_integers() - sage: OK.integral_closure() - Gaussian Integers generated by s in Number Field in s - with defining polynomial x^2 + 1 - sage: OK.integral_closure() == OK - True - - sage: QQ.integral_closure() == QQ - True - """ - return self - - def is_noetherian(self): - r""" - Return ``True`` since Dedekind domains are Noetherian. - - EXAMPLES: - - The integers, `\ZZ`, and rings of integers of number - fields are Dedekind domains:: +cdef class DedekindDomain(IntegralDomain): + _default_category = DedekindDomains() - sage: ZZ.is_noetherian() - True - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.is_noetherian() # needs sage.rings.number_field - True - sage: QQ.is_noetherian() - True - """ - return True + def __init__(self, *args, **kwds): + deprecation(37234, "use the category DedekindDomains") + super().__init__(*args, **kwds) cdef class PrincipalIdealDomain(IntegralDomain): diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d5d97e5e044..d5ea150f98d 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1801,6 +1801,7 @@ def fundamental_group(self, simplified=True, puiseux=False): to the algebraic field:: sage: # needs sage.rings.number_field + sage: x = polygen(ZZ) sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) sage: F.inject_variables() diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 58bed0a67a4..1f0fd8bd34e 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -271,7 +271,7 @@ def is_HCP(f, check_monic_irreducible=True): continue if d < h and d not in h2list: return zero - jp = fp.any_root(degree=-1, assume_squarefree=True) + jp = fp.any_root(degree=1, assume_squarefree=True, assume_distinct_deg=True) E = EllipticCurve(j=jp) if E.is_supersingular(): continue diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 5127b53481e..396a2ab2797 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -921,7 +921,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: f([1,-1,1]) Traceback (most recent call last): ... - ValueError: [0, 0, 0] does not define a valid point since all entries are 0 + ValueError: [0, 0, 0] does not define a valid projective point since all entries are zero Using the group law on the codomain elliptic curve, which has rank 1 and full 2-torsion, and the inverse morphism, we can find many diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 0e2497fbcda..e0f07c0191d 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1074,7 +1074,7 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True self.__algorithm = algorithm if algorithm == 'velu': - self.__init_from_kernel_list(kernel) + self.__init_from_kernel_gens(kernel) elif algorithm == 'kohel': self.__init_from_kernel_polynomial(kernel) else: @@ -1870,7 +1870,7 @@ def __setup_post_isomorphism(self, codomain, model): # Setup function for Velu's formula # - def __init_from_kernel_list(self, kernel_gens): + def __init_from_kernel_gens(self, kernel_gens): r""" Private function that initializes the isogeny from a list of points which generate the kernel (For Vélu's formulas.) @@ -1884,7 +1884,7 @@ def __init_from_kernel_list(self, kernel_gens): Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))]) + sage: phi._EllipticCurveIsogeny__init_from_kernel_gens([E(0), E((0,0))]) The following example demonstrates the necessity of avoiding any calls to P.order(), since such calls involve factoring the group order which @@ -1907,6 +1907,14 @@ def __init_from_kernel_list(self, kernel_gens): if not P.has_finite_order(): raise ValueError("given kernel contains point of infinite order") + self.__kernel_mod_sign = {} + self.__v = self.__w = 0 + + # Fast path: The kernel is given by a single generating point. + if len(kernel_gens) == 1 and kernel_gens[0]: + self.__init_from_kernel_point(kernel_gens[0]) + return + # Compute a list of points in the subgroup generated by the # points in kernel_gens. This is very naive: when finite # subgroups are implemented better, this could be simplified, @@ -1927,12 +1935,86 @@ def all_multiples(itr, terminal): self._degree = Integer(len(kernel_set)) self.__kernel_list = list(kernel_set) - self.__sort_kernel_list() + self.__init_from_kernel_list() # # Precompute the values in Velu's Formula. # - def __sort_kernel_list(self): + def __update_kernel_data(self, xQ, yQ): + r""" + Internal helper function to update some data coming from the + kernel points of this isogeny when using Vélu's formulas. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + """ + a1, a2, a3, a4, _ = self._domain.a_invariants() + + gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ + gyQ = -2*yQ - a1*xQ - a3 + + uQ = gyQ**2 + + if 2*yQ == -a1*xQ - a3: # Q is 2-torsion + vQ = gxQ + else: # Q is not 2-torsion + vQ = 2*gxQ - a1*gyQ + + self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ + + self.__v += vQ + self.__w += uQ + xQ*vQ + + def __init_from_kernel_point(self, ker): + r""" + Private function with functionality equivalent to + :meth:`__init_from_kernel_list`, but optimized for when + the kernel is given by a single point. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, P); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + + We check that the result is the same as for :meth:`__init_from_kernel_list`:: + + sage: psi = EllipticCurveIsogeny(E, [P, P]); psi + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + sage: phi == psi + True + """ + self._degree = Integer(1) + + Q, prevQ = ker, self._domain(0) + + while Q and Q != -prevQ: + self.__update_kernel_data(*Q.xy()) + + if Q == -Q: + self._degree += 1 + break + + prevQ = Q + Q += ker + self._degree += 2 + + def __init_from_kernel_list(self): r""" Private function that sorts the list of points in the kernel (For Vélu's formulas). Sorts out the 2-torsion points, and @@ -1944,43 +2026,22 @@ def __sort_kernel_list(self): sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) sage: P = E((4,2)) - sage: phi = EllipticCurveIsogeny(E, P); phi + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__sort_kernel_list() """ - a1, a2, a3, a4, _ = self._domain.a_invariants() - - self.__kernel_mod_sign = {} - v = w = 0 - for Q in self.__kernel_list: if Q.is_zero(): continue - xQ,yQ = Q.xy() + xQ, yQ = Q.xy() if xQ in self.__kernel_mod_sign: continue - gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ - gyQ = -2*yQ - a1*xQ - a3 - - uQ = gyQ**2 - - if 2*yQ == -a1*xQ - a3: # Q is 2-torsion - vQ = gxQ - else: # Q is not 2-torsion - vQ = 2*gxQ - a1*gyQ - - self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ - - v += vQ - w += uQ + xQ*vQ - - self.__v, self.__w = v, w + self.__update_kernel_data(xQ, yQ) # # Velu's formula computing the codomain curve diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 944be554fd4..0c944f0f938 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -2383,7 +2383,7 @@ def point_of_order(E, n): sage: from sage.schemes.elliptic_curves.ell_field import point_of_order sage: E = EllipticCurve(GF(101), [1,2,3,4,5]) - sage: P = point_of_order(E, 5); P + sage: P = point_of_order(E, 5); P # random (50*Y^5 + 48*Y^4 + 26*Y^3 + 37*Y^2 + 48*Y + 15 : 25*Y^5 + 31*Y^4 + 79*Y^3 + 39*Y^2 + 3*Y + 20 : 1) sage: P.base_ring() Finite Field in Y of size 101^6 @@ -2394,7 +2394,7 @@ def point_of_order(E, n): :: - sage: Q = point_of_order(E, 8); Q + sage: Q = point_of_order(E, 8); Q # random (69*x^5 + 24*x^4 + 100*x^3 + 65*x^2 + 88*x + 97 : 65*x^5 + 28*x^4 + 5*x^3 + 45*x^2 + 42*x + 18 : 1) sage: 8*Q == 0 and 4*Q != 0 True diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 442ef5cd84b..367d79f703a 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -610,6 +610,24 @@ def cardinality(self, algorithm=None, extension_degree=1): order = cardinality # alias + @cached_method + def multiplication_by_p_isogeny(self): + r""" + Return the multiplication-by-`p` isogeny. + + EXAMPLES:: + + sage: p = 23 + sage: K. = GF(p^3) + sage: E = EllipticCurve(j=K.random_element()) + sage: phi = E.multiplication_by_p_isogeny() + sage: assert phi.degree() == p**2 + sage: P = E.random_element() + sage: assert phi(P) == P * p + """ + frob = self.frobenius_isogeny() + return frob.dual() * frob + def frobenius_polynomial(self): r""" Return the characteristic polynomial of Frobenius. @@ -1773,13 +1791,7 @@ def twists(self): sage: p = next_prime(randrange(2,100)) sage: e = randrange(1,10) sage: F. = GF((p,e)) - sage: while True: - ....: try: - ....: E = EllipticCurve([F.random_element() for _ in range(5)]) - ....: except ArithmeticError: - ....: pass - ....: else: - ....: break + sage: E = EllipticCurve(j=F.random_element()) sage: twists1 = E.twists() sage: {sum(E1.is_isomorphic(E2) for E2 in twists1) == 1 for E1 in twists1} {True} diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 65be8c246c0..07271b9fd39 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -53,6 +53,7 @@ # **************************************************************************** import math +from sage.arith.misc import valuation import sage.rings.abc from sage.rings.finite_rings.integer_mod import mod @@ -66,8 +67,6 @@ from sage.arith.functions import lcm from sage.rings.integer import Integer -from sage.rings.big_oh import O -from sage.rings.infinity import Infinity as oo from sage.rings.rational import Rational from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.rational_field import RationalField @@ -963,6 +962,7 @@ def lift_x(self, x, all=False, extend=False): else: ys = [y1, y2] ys.sort() # ensure deterministic behavior + x = M(x) one = M.one() if all: return [EM.point([x, y, one], check=False) for y in ys] @@ -2421,15 +2421,53 @@ def multiplication_by_m(self, m, x_only=False): sage: my_eval = lambda f,P: [fi(P[0],P[1]) for fi in f] sage: f = E.multiplication_by_m(2) sage: assert(E(eval(f,P)) == 2*P) + + The following test shows that :trac:`6413` is fixed for elliptic curves over finite fields:: + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [a + 3, 5 - a]) + sage: k = p^2 * 3 + sage: f, g = E.multiplication_by_m(k) + sage: for _ in range(100): + ....: P = E.random_point() + ....: if P * k == 0: + ....: continue + ....: Qx = f.subs(x=P[0]) + ....: Qy = g.subs(x=P[0], y=P[1]) + ....: assert (P * k).xy() == (Qx, Qy) + + However, it still fails for elliptic curves over positive-characteristic fields:: + + sage: F. = FunctionField(GF(7)) + sage: E = EllipticCurve(F, [a, 1 / a]) + sage: E.multiplication_by_m(7) + Traceback (most recent call last): + ... + NotImplementedError: multiplication by integer not coprime to p is only implemented for curves over finite fields + + :: + + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: E.multiplication_by_m(p * 2)[0] == E.multiplication_by_m(p * 2, x_only=True) + True """ # Coerce the input m to be an integer m = Integer(m) + p = self.base_ring().characteristic() + if m == 0: raise ValueError("m must be a non-zero integer") if x_only: x = polygen(self.base_ring(), 'x') else: + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + if p != 0 and m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): + # TODO: Implement the correct formula? + raise NotImplementedError("multiplication by integer not coprime to p " + "is only implemented for curves over finite fields") x, y = polygens(self.base_ring(), 'x,y') # Special case of multiplication by 1 is easy. @@ -2440,7 +2478,7 @@ def multiplication_by_m(self, m, x_only=False): return x # Grab curve invariants - a1, a2, a3, a4, a6 = self.a_invariants() + a1, _, a3, _, _ = self.a_invariants() if m == -1: if not x_only: @@ -2448,22 +2486,33 @@ def multiplication_by_m(self, m, x_only=False): else: return x - # the x-coordinate does not depend on the sign of m. The work + # If we only require the x coordinate, it is faster to use the recursive formula + # since substituting polynomials is quite slow. + v_p = 0 if p == 0 else valuation(m, p) + if not x_only: + m //= p**v_p + + # the x-coordinate does not depend on the sign of m. The work # here is done by functions defined earlier: mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) / x.parent()(self._multiple_x_denominator(m.abs(), x))) if x_only: - # Return it if the optional parameter x_only is set. return mx - # Consideration of the invariant differential - # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) - # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx - + # Consideration of the invariant differential + # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) + # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 + if v_p > 0: + isog = self.multiplication_by_p_isogeny()**v_p + fx, fy = isog.rational_maps() + # slow... + mx = mx.subs(x=fx) + my = my.subs(x=fx, y=fy) + return mx, my def multiplication_by_m_isogeny(self, m): diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index f3f8fdfe0f3..bcfe8d5c1d2 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -273,44 +273,12 @@ def __init__(self, curve, v, check=True): v = list(v) elif v == 0: v = (R.zero(), R.one(), R.zero()) - if check: - # mostly from SchemeMorphism_point_projective_field - d = point_homset.codomain().ambient_space().ngens() - if not isinstance(v, (list, tuple)): - raise TypeError("Argument v (= %s) must be a scheme point, list, or tuple." % str(v)) - if len(v) != d and len(v) != d-1: - raise TypeError("v (=%s) must have %s components" % (v, d)) - v = Sequence(v, R) - if len(v) == d-1: # very common special case - v.append(v.universe()(1)) - - n = len(v) - all_zero = True - for i in range(n): - c = v[n-1-i] - if c: - all_zero = False - if c == 1: - break - for j in range(n-i): - v[j] /= c - break - if all_zero: - raise ValueError("%s does not define a valid point " - "since all entries are 0" % repr(v)) - x, y, z = v - if z == 0: - test = x - else: - a1, a2, a3, a4, a6 = curve.ainvs() - test = y**2 + (a1*x+a3)*y - (((x+a2)*x+a4)*x+a6) - if not test == 0: - raise TypeError("Coordinates %s do not define a point on %s" % (list(v), curve)) - - SchemeMorphism_point_abelian_variety_field.__init__(self, point_homset, v, check=False) + SchemeMorphism_point_abelian_variety_field.__init__(self, point_homset, v, check=check) # AdditiveGroupElement.__init__(self, point_homset) + self.normalize_coordinates() + def _repr_(self): """ Return a string representation of this point. @@ -452,8 +420,9 @@ def __pari__(self): sage: pari(E).elladd(O, P) [Mod(1, 11), Mod(2, 11)] """ - if self[2]: - return pari([self[0]/self[2], self[1]/self[2]]) + x,y,z = self._coords + if z: + return pari([x/z, y/z]) else: return pari([0]) @@ -558,7 +527,7 @@ def __bool__(self): sage: P.is_zero() False """ - return bool(self[2]) + return bool(self._coords[2]) def has_order(self, n): r""" diff --git a/src/sage/schemes/hyperelliptic_curves/all.py b/src/sage/schemes/hyperelliptic_curves/all.py index ff4d929e891..88733235129 100644 --- a/src/sage/schemes/hyperelliptic_curves/all.py +++ b/src/sage/schemes/hyperelliptic_curves/all.py @@ -1,45 +1,4 @@ -""" -Tests for deprecations of imports in global namespace from :trac:`28064`:: - - sage: igusa_clebsch_invariants - doctest:warning...: - DeprecationWarning: - Importing igusa_clebsch_invariants from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import igusa_clebsch_invariants" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: absolute_igusa_invariants_kohel - doctest:warning...: - DeprecationWarning: - Importing absolute_igusa_invariants_kohel from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_kohel" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: absolute_igusa_invariants_wamelen - doctest:warning...: - DeprecationWarning: - Importing absolute_igusa_invariants_wamelen from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_wamelen" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: clebsch_invariants - doctest:warning...: - DeprecationWarning: - Importing clebsch_invariants from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import clebsch_invariants" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... -""" -from sage.misc.lazy_import import lazy_import - from .constructor import HyperellipticCurve from .kummer_surface import KummerSurface -lazy_import('sage.schemes.hyperelliptic_curves.invariants', - ['igusa_clebsch_invariants', 'absolute_igusa_invariants_kohel', - 'absolute_igusa_invariants_wamelen', 'clebsch_invariants'], - deprecation=28064) from .mestre import (Mestre_conic, HyperellipticCurve_from_invariants) from . import monsky_washnitzer diff --git a/src/sage/schemes/hyperelliptic_curves/invariants.py b/src/sage/schemes/hyperelliptic_curves/invariants.py index a0101f60a3c..258775c7bc6 100644 --- a/src/sage/schemes/hyperelliptic_curves/invariants.py +++ b/src/sage/schemes/hyperelliptic_curves/invariants.py @@ -322,6 +322,7 @@ def igusa_clebsch_invariants(f): sage: igusa_clebsch_invariants(x^5 + a*x^4 + b*x^3 + c*x^2 + d*x + e)[0] 6*b^2 - 16*a*c + 40*d + sage: from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_wamelen sage: absolute_igusa_invariants_wamelen(GF(5)['x'](x^6 - 2*x)) Traceback (most recent call last): ... diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 2519cdf5117..03b795a4704 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -68,7 +68,7 @@ from sage.rings.laurent_series_ring import LaurentSeriesRing from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ -from sage.rings.ring import IntegralDomain +from sage.categories.integral_domains import IntegralDomains from sage.rings.infinity import Infinity from sage.rings.laurent_series_ring import is_LaurentSeriesRing from sage.rings.padics.factory import Qp as pAdicField @@ -3837,13 +3837,11 @@ def helper_matrix(self): pass # The smallest y term of (1/j) d(x^i y^j) is constant for all j. - L = [] x, y = self.base_ring().gens() n = self.degree() - for i in range(n): - L.append((y*x**i).diff().extract_pow_y(0)) + L = [(y * x**i).diff().extract_pow_y(0) for i in range(n)] A = matrix(L).transpose() - if not isinstance(A.base_ring(), IntegralDomain): + if A.base_ring() not in IntegralDomains(): # must be using integer_mod or something to approximate self._helper_matrix = (~A.change_ring(QQ)).change_ring(A.base_ring()) else: diff --git a/src/sage/schemes/plane_conics/constructor.py b/src/sage/schemes/plane_conics/constructor.py index 7bb681a4268..a078bc07d22 100644 --- a/src/sage/schemes/plane_conics/constructor.py +++ b/src/sage/schemes/plane_conics/constructor.py @@ -26,7 +26,7 @@ from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector -from sage.rings.ring import IntegralDomain +from sage.categories.integral_domains import IntegralDomains from sage.rings.rational_field import is_RationalField from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.fraction_field import is_FractionField @@ -143,7 +143,7 @@ def Conic(base_field, F=None, names=None, unique=True): sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ - if not (base_field is None or isinstance(base_field, IntegralDomain)): + if not (base_field is None or base_field in IntegralDomains()): if names is None: names = F F = base_field @@ -173,7 +173,7 @@ def Conic(base_field, F=None, names=None, unique=True): if len(C) != 3: raise TypeError("points in F (=%s) must be planar" % F) P = C.universe() - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise TypeError("coordinates of points in F (=%s) must " "be in an integral domain" % F) L.append(Sequence([C[0]**2, C[0] * C[1], @@ -217,8 +217,8 @@ def Conic(base_field, F=None, names=None, unique=True): if base_field is None: base_field = F.base_ring() - if not isinstance(base_field, IntegralDomain): - raise ValueError("Base field (=%s) must be a field" % base_field) + if base_field not in IntegralDomains(): + raise ValueError(f"Base field (={base_field}) must be a field") base_field = base_field.fraction_field() if names is None: names = F.parent().variable_names() diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 55bd5354976..dea15db15e5 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -105,8 +105,7 @@ def __init__(self, X, v, check=True): sage: P(0,0,0,0) Traceback (most recent call last): ... - ValueError: [0, 0, 0, 0] does not define a point in Projective Space of dimension 3 - over Integer Ring since all entries are zero + ValueError: [0, 0, 0, 0] does not define a valid projective point since all entries are zero :: @@ -120,8 +119,7 @@ def __init__(self, X, v, check=True): sage: P(0,5,10,15) Traceback (most recent call last): ... - ValueError: [0, 5, 10, 0] does not define a point in Projective Space of dimension 3 - over Ring of integers modulo 15 since it is a multiple of a zero divisor + ValueError: [0, 5, 10, 0] does not define a valid projective point since it is a multiple of a zero divisor It is possible to avoid the possibly time-consuming checks, but be careful!! :: @@ -164,6 +162,7 @@ def __init__(self, X, v, check=True): ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) + if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from sage.rings.ring import CommutativeRing @@ -190,21 +189,20 @@ def __init__(self, X, v, check=True): # Over integral domains, any tuple with at least one # non-zero coordinate is a valid projective point. if not any(v): - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " - "since all entries are zero") + raise ValueError(f"{v} does not define a valid projective " + "point since all entries are zero") else: # Over rings with zero divisors, a more careful check # is required: We test whether the coordinates of the # point generate the unit ideal. See #31576. if 1 not in R.ideal(v): - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " + raise ValueError(f"{v} does not define a valid projective point " "since it is a multiple of a zero divisor") X.extended_codomain()._check_satisfies_equations(v) self._coords = tuple(v) + self._normalized = False def _richcmp_(self, right, op): """ @@ -531,11 +529,12 @@ def scale_by(self, t): R = self.codomain().base_ring() if isinstance(R, QuotientRing_generic): for i in range(self.codomain().ambient_space().dimension_relative()+1): - new_coords = [R(u.lift()*t) for u in self] + new_coords = [R(u.lift()*t) for u in self._coords] else: for i in range(self.codomain().ambient_space().dimension_relative()+1): - new_coords = [R(u*t) for u in self] + new_coords = [R(u*t) for u in self._coords] self._coords = tuple(new_coords) + self._normalized = False def normalize_coordinates(self): """ @@ -596,7 +595,7 @@ def normalize_coordinates(self): :: sage: # needs sage.libs.singular - sage: R. = PolynomialRing(QQ, 1) + sage: R. = QQ[] sage: S = R.quotient_ring(R.ideal(t^3)) sage: P. = ProjectiveSpace(S, 1) sage: Q = P(t + 1, t^2 + t) @@ -604,35 +603,32 @@ def normalize_coordinates(self): sage: Q (1 : tbar) """ + if self._normalized: + return R = self.codomain().base_ring() - if isinstance(R,(QuotientRing_generic)): - GCD = gcd(self[0].lift(),self[1].lift()) - index = 2 - if self[0].lift() > 0 or self[1].lift() > 0: - neg = 1 - else: - neg = -1 - while GCD != 1 and index < len(self._coords): - if self[index].lift() > 0: - neg = 1 - GCD = gcd(GCD,self[index].lift()) - index += 1 + if isinstance(R, QuotientRing_generic): + index = self.codomain().ambient_space().dimension_relative() + while not self._coords[index]: + index -= 1 + last = self._coords[index].lift() + mod, = R.defining_ideal().gens() + unit = last + while not (zdiv := mod.gcd(unit)).is_unit(): + unit //= zdiv + self.scale_by(unit.inverse_mod(mod)) else: - GCD = R(gcd(self[0], self[1])) + GCD = R(gcd(self._coords[0], self._coords[1])) index = 2 - if self[0] > 0 or self[1] > 0: - neg = R(1) - else: - neg = R(-1) + neg = self._coords[0] <= 0 and self._coords[1] <= 0 while GCD != 1 and index < len(self._coords): - if self[index] > 0: - neg = R(1) - GCD = R(gcd(GCD,self[index])) + neg = self._coords[index] <= 0 + GCD = R(gcd(GCD, self._coords[index])) index += 1 - if GCD != 1: - self.scale_by(neg/GCD) - elif neg == -1: - self.scale_by(neg) + if GCD != 1: + self.scale_by(~GCD) + if neg: + self.scale_by(-1) + self._normalized = True def dehomogenize(self,n): r""" @@ -1103,8 +1099,7 @@ def __init__(self, X, v, check=True): sage: P(0, 0, 0, 0) Traceback (most recent call last): ... - ValueError: [0, 0, 0, 0] does not define a point in Projective Space of dimension 3 - over Rational Field since all entries are zero + ValueError: [0, 0, 0, 0] does not define a valid projective point since all entries are zero :: @@ -1141,6 +1136,9 @@ def __init__(self, X, v, check=True): ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) + + self._normalized = False + if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from sage.rings.ring import CommutativeRing @@ -1175,11 +1173,11 @@ def __init__(self, X, v, check=True): for j in range(last): v[j] /= c v[last] = R.one() + self._normalized = True break if all_zero: - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " - "since all entries are zero") + raise ValueError(f"{v} does not define a valid projective " + "point since all entries are zero") X.extended_codomain()._check_satisfies_equations(v) @@ -1224,10 +1222,19 @@ def normalize_coordinates(self): sage: Q.normalize_coordinates(); Q (1/2 : 1/2 : 1) """ + if self._normalized: + return index = self.codomain().ambient_space().dimension_relative() - while self[index] == 0: + while not self._coords[index]: index -= 1 - self.scale_by(1/self[index]) + inv = self._coords[index].inverse() + new_coords = [] + for i in range(index): + new_coords.append(self._coords[i] * inv) + new_coords.append(self.base_ring().one()) + new_coords.extend(self._coords[index+1:]) + self._coords = tuple(new_coords) + self._normalized = True def _number_field_from_algebraics(self): r""" diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index eba55e9f9f6..3e1eb38c73d 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -210,9 +210,11 @@ cdef class CategoryObject(SageObject): EXAMPLES:: sage: ZZ.categories() - [Join of Category of euclidean domains + [Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces, + Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index f1cf9d26ac7..078647717a3 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -827,6 +827,28 @@ cdef class Element(SageObject): variables.append(gen) return self(*variables) + def substitute(self, *args, **kwds): + """ + This calls :meth:`self.subs`. + + EXAMPLES:: + + sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() + sage: f = x^2 + y + x^2*y^2 + 5 + sage: f((5,y)) + 25*y^2 + y + 30 + sage: f.substitute({x: 5}) + 25*y^2 + y + 30 + sage: f.substitute(x=5) + 25*y^2 + y + 30 + sage: (1/f).substitute(x=5) + 1/(25*y^2 + y + 30) + sage: Integer(5).substitute(x=4) + 5 + """ + return self.subs(*args, **kwds) + + def numerical_approx(self, prec=None, digits=None, algorithm=None): """ Return a numerical approximation of ``self`` with ``prec`` bits @@ -906,37 +928,6 @@ cdef class Element(SageObject): """ return self.n(prec)._mpmath_(prec=prec) - def substitute(self,in_dict=None,**kwds): - """ - This is an alias for self.subs(). - - INPUT: - - - ``in_dict`` - (optional) dictionary of inputs - - - ``**kwds`` - named parameters - - OUTPUT: - - - new object if substitution is possible, otherwise self. - - EXAMPLES:: - - sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() - sage: f = x^2 + y + x^2*y^2 + 5 - sage: f((5,y)) - 25*y^2 + y + 30 - sage: f.substitute({x: 5}) - 25*y^2 + y + 30 - sage: f.substitute(x=5) - 25*y^2 + y + 30 - sage: (1/f).substitute(x=5) - 1/(25*y^2 + y + 30) - sage: Integer(5).substitute(x=4) - 5 - """ - return self.subs(in_dict,**kwds) - cpdef _act_on_(self, x, bint self_on_left) noexcept: """ Use this method to implement ``self`` acting on ``x``. @@ -3344,12 +3335,12 @@ cdef class CommutativeRingElement(RingElement): #This code is very general, it works for all integral domains that have the #is_square(root = True) option - from sage.rings.ring import IntegralDomain + from sage.categories.integral_domains import IntegralDomains P = self._parent is_sqr, sq_rt = self.is_square(root=True) if is_sqr: if all: - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise NotImplementedError('sqrt() with all=True is only implemented for integral domains, not for %s' % P) if P.characteristic()==2 or sq_rt==0: #0 has only one square root, and in characteristic 2 everything also has only 1 root @@ -3357,7 +3348,7 @@ cdef class CommutativeRingElement(RingElement): return [ sq_rt, -sq_rt ] return sq_rt #from now on we know that self is not a square - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise NotImplementedError('sqrt() of non squares is only implemented for integral domains, not for %s' % P) if not extend: #all square roots of a non-square should be an empty list diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 345ee9b713b..ab19b46c181 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5574,7 +5574,7 @@ cdef class Expression(Expression_abc): cdef Expression p = self.coerce_in(pattern) return self._gobj.has(p._gobj) - def substitute(self, *args, **kwds): + def subs(self, *args, **kwds): """ Substitute the given subexpressions in this expression. @@ -5899,8 +5899,6 @@ cdef class Expression(Expression_abc): res = self._gobj.subs_map(smap, 0) return new_Expression_from_GEx(self._parent, res) - subs = substitute - cpdef Expression _subs_expr(self, expr) noexcept: """ EXAMPLES:: diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index d8d8686eeae..23b043bf5c4 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -248,7 +248,8 @@ class :class:`~sage.modules.free_module.FreeModule_generic` Category of finite dimensional modules over Integer Ring sage: N.category() Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) In other words, the module created by ``FreeModule`` is actually `\ZZ^3`, while, in the absence of any distinguished basis, no *canonical* isomorphism diff --git a/src/sage/tests/books/__init__.py b/src/sage/tests/books/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/version.py b/src/sage/version.py index 9864c02bde4..3662aa06873 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.beta7' -date = '2024-02-02' -banner = 'SageMath version 10.3.beta7, Release Date: 2024-02-02' +version = '10.3.beta8' +date = '2024-02-13' +banner = 'SageMath version 10.3.beta8, Release Date: 2024-02-13' diff --git a/src/sage_docbuild/__main__.py b/src/sage_docbuild/__main__.py index 0d15808a69c..acfcb8392a0 100644 --- a/src/sage_docbuild/__main__.py +++ b/src/sage_docbuild/__main__.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sphinx r""" Sage docbuild main diff --git a/src/sage_docbuild/builders.py b/src/sage_docbuild/builders.py index a43fdda7350..0be43be307f 100644 --- a/src/sage_docbuild/builders.py +++ b/src/sage_docbuild/builders.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sphinx """ Documentation builders diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 6b84e772dd1..f940adb0755 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -57,7 +57,7 @@ jupyter_execute_default_kernel = 'sagemath' -if os.environ.get('SAGE_LIVE_DOC', 'no') == 'yes': +if os.environ.get('SAGE_LIVE_DOC', 'no') == 'yes': SAGE_JUPYTER_SERVER = os.environ.get('SAGE_JUPYTER_SERVER', 'binder') if SAGE_JUPYTER_SERVER.startswith('binder'): # format: "binder" or @@ -197,7 +197,7 @@ def sphinx_plot(graphics, **kwds): # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # The LaTeX engine to build the docs. # https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-latex_engine @@ -205,9 +205,9 @@ def sphinx_plot(graphics, **kwds): # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of glob-style patterns that should be excluded when looking for # source files. [1] They are matched against the source file names @@ -216,11 +216,11 @@ def sphinx_plot(graphics, **kwds): exclude_patterns = ['.build'] # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. @@ -274,9 +274,9 @@ def set_intersphinx_mappings(app, config): return app.config.intersphinx_mapping = { - 'python': ('https://docs.python.org/', - os.path.join(SAGE_DOC_SRC, "common", - "python{}.inv".format(python_version))), + 'python': ('https://docs.python.org/', + os.path.join(SAGE_DOC_SRC, "common", + "python{}.inv".format(python_version))), } if PPLPY_DOCS and os.path.exists(os.path.join(PPLPY_DOCS, 'objects.inv')): app.config.intersphinx_mapping['pplpy'] = (PPLPY_DOCS, None) @@ -379,18 +379,18 @@ def set_intersphinx_mappings(app, config): html_theme_options = {} # HTML style sheet. This overrides a HTML theme's corresponding setting. -#html_style = 'default.css' +# html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. -#html_logo = 'sagelogo-word.ico' +# html_logo = 'sagelogo-word.ico' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -439,17 +439,17 @@ def set_intersphinx_mappings(app, config): # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # A list of prefixes that are ignored for sorting the Python module index ( if # this is set to ['foo.'], then foo.bar is shown under B, not F). Works only @@ -852,6 +852,7 @@ def apply(self): node.parent.insert(node.parent.index(node) + 1, cell_node) + # This replaces the setup() in sage.misc.sagedoc_conf def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 5ae1d2e6b10..6d5823934b8 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# sage.doctest: needs sphinx r""" Sphinx build script diff --git a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template index 24edaaa5945..af50c1b256d 100644 --- a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template +++ b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template @@ -1,4 +1,9 @@ # distutils: extra_compile_args = -D_XPG6 + +# WARNING: src/sage/libs/flint/flint_sage.pyx is generated from +# src/sage_setup/autogen/flint/templates/flint_sage.pyx.template; +# please make sure that you are modifying the correct file! + """ Flint imports diff --git a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template index 27237dcb2a5..97323ede6ce 100644 --- a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template +++ b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template @@ -1,3 +1,7 @@ +/* WARNING: src/sage/libs/flint/flint_wrap.h is generated from + * src/sage_setup/autogen/flint/templates/flint_wrap.h.template + * please make sure that you are modifying the correct file! */ + #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H /* Using flint headers together in the same module as headers from @@ -40,4 +44,10 @@ #pragma pop_macro("ulong") +/* CPU_SIZE_1 and SIZE_RED_FAILURE_THRESH are defined as macros in flint/fmpz_lll.h + * and as variables in fplll/defs.h, which breaks build if linbox is compiled with fplll */ + +#undef CPU_SIZE_1 +#undef SIZE_RED_FAILURE_THRESH + #endif diff --git a/src/sage_setup/autogen/flint/templates/types.pxd.template b/src/sage_setup/autogen/flint/templates/types.pxd.template index a60b10839ab..8d51dc29a10 100644 --- a/src/sage_setup/autogen/flint/templates/types.pxd.template +++ b/src/sage_setup/autogen/flint/templates/types.pxd.template @@ -1,5 +1,9 @@ # distutils: depends = {HEADER_LIST} +# WARNING: src/sage/libs/flint/types.pxd is generated from +# src/sage_setup/autogen/flint/templates/types.pxd.template +# please make sure that you are modifying the correct file! + """ Declarations for FLINT types """ @@ -129,10 +133,10 @@ cdef extern from "flint_wrap.h": ctypedef arb_mat_struct arb_mat_t[1] ctypedef struct arb_poly_struct: - pass + arb_ptr coeffs + long alloc + long length ctypedef arb_poly_struct[1] arb_poly_t - ctypedef arb_poly_struct * arb_poly_ptr - ctypedef const arb_poly_struct * arb_poly_srcptr # flint/arb_calc.h @@ -165,10 +169,10 @@ cdef extern from "flint_wrap.h": # flint/acb_poly.h ctypedef struct acb_poly_struct: - pass + acb_ptr coeffs + long alloc + long length ctypedef acb_poly_struct[1] acb_poly_t - ctypedef acb_poly_struct * acb_poly_ptr - ctypedef const acb_poly_struct * acb_poly_srcptr # flint/acb_calc.h ctypedef struct acb_calc_integrate_opt_struct: diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 23f3d6cb166..e2a3330518d 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -9,7 +9,7 @@ license_files = LICENSE.txt include(`setup_cfg_metadata.m4')dnl' [options] -python_requires = >=3.9, <3.12 +python_requires = >=3.9, <3.13 install_requires = SPKG_INSTALL_REQUIRES_sage_conf SPKG_INSTALL_REQUIRES_six diff --git a/src/tox.ini b/src/tox.ini index f2f1fc158fd..8e562cced8e 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -162,7 +162,7 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E21,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E271,E301,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] diff --git a/tox.ini b/tox.ini index 37d2aabb3c0..e2d746f5cdc 100644 --- a/tox.ini +++ b/tox.ini @@ -266,7 +266,7 @@ setenv = linuxmint-21.2: BASE_IMAGE=linuxmintd/mint21.2 # # https://hub.docker.com/_/fedora - # as of 2023-05, latest=38, rawhide=39 + # as of 2024-01, latest=39, rawhide=40 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora-26: BASE_TAG=26 @@ -283,17 +283,16 @@ setenv = fedora-32: BASE_TAG=32 fedora-33: BASE_TAG=33 fedora-34: BASE_TAG=34 - fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-35: BASE_TAG=35 - fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-36: BASE_TAG=36 - fedora-36: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-37: BASE_TAG=37 fedora-37: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-38: BASE_TAG=38 fedora-38: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-39: BASE_TAG=39 fedora-39: IGNORE_MISSING_SYSTEM_PACKAGES=yes + fedora-40: BASE_TAG=40 + fedora-40: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/scientificlinux/sl #