diff --git a/.ci/write-dockerfile.sh b/.ci/write-dockerfile.sh index 68c19ca675e..75737acf106 100755 --- a/.ci/write-dockerfile.sh +++ b/.ci/write-dockerfile.sh @@ -35,7 +35,7 @@ STRIP_COMMENTS="sed s/#.*//;" SAGE_ROOT=. export PATH="$SAGE_ROOT"/build/bin:$PATH SYSTEM_PACKAGES=$EXTRA_SYSTEM_PACKAGES -SYSTEM_CONFIGURE_ARGS="--enable-option-checking " +SYSTEM_CONFIGURE_ARGS=" --enable-option-checking" for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do SYSTEM_PACKAGE=$(sage-get-system-packages $SYSTEM $SPKG) if [ -n "${SYSTEM_PACKAGE}" ]; then @@ -45,13 +45,20 @@ for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ # shell-quote package if necessary SYSTEM_PACKAGES+=$(printf " %q" "$a") done - SYSTEM_CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} " + # Check if SPKG is not a dummy package + if [[ $SPKG != _* ]]; then + SYSTEM_CONFIGURE_ARGS+=" --with-system-${SPKG}=${WITH_SYSTEM_SPKG}" + fi fi done echo "# Automatically generated by SAGE_ROOT/.ci/write-dockerfile.sh" echo "# the :comments: separate the generated file into sections" echo "# to simplify writing scripts that customize this file" -ADD="ADD $__CHOWN" +if [ -z "$__CHOWN" ]; then + ADD="ADD" +else + ADD="ADD $__CHOWN" +fi RUN=RUN cat < /dev/null; then \ - (yes | unminimize) || echo "(ignored)"; \ - rm -f "\$(command -v unminimize)"; \ - fi +RUN if command -v unminimize > /dev/null; then (yes | unminimize) || echo "(ignored)"; rm -f "\$(command -v unminimize)"; fi EOF if [ -n "$DIST_UPGRADE" ]; then cat <> /sage/.gitignore && \ - printf '/src/*\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /new/.gitignore && \ - if ! (cd /new && /.ci/retrofit-worktree.sh worktree-image /sage); then \ - echo "retrofit-worktree.sh failed, falling back to replacing /sage"; \ - for a in local logs; do \ - if [ -d /sage/\$a ]; then mv /sage/\$a /new/; fi; \ - done; \ - rm -rf /sage; \ - mv /new /sage; \ - fi; \ - else \ - mv /new /sage; \ +RUN if [ -d /sage ]; then \\ + echo "### Incremental build from \$(cat /sage/VERSION.txt)" && \\ + printf '/src/*\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /sage/.gitignore && \\ + printf '/src/*\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /new/.gitignore && \\ + if ! (cd /new && /.ci/retrofit-worktree.sh worktree-image /sage); then \\ + echo "retrofit-worktree.sh failed, falling back to replacing /sage"; \\ + for a in local logs; do \\ + if [ -d /sage/\$a ]; then mv /sage/\$a /new/; fi; \\ + done; \\ + rm -rf /sage; \\ + mv /new /sage; \\ + fi; \\ + else \\ + mv /new /sage; \\ fi WORKDIR /sage - ARG BOOTSTRAP="${BOOTSTRAP-./bootstrap}" -$RUN sh -x -c "\${BOOTSTRAP}" $ENDRUN $THEN_SAVE_STATUS +$RUN sh -x -c "\${BOOTSTRAP}"$ENDRUN$THEN_SAVE_STATUS FROM bootstrapped AS configured #:configuring: -RUN $CHECK_STATUS_THEN mkdir -p logs/pkgs; rm -f config.log; ln -s logs/pkgs/config.log config.log +RUN$CHECK_STATUS_THEN mkdir -p logs/pkgs; rm -f config.log; ln -s logs/pkgs/config.log config.log ARG CONFIGURE_ARGS="${CONFIGURE_ARGS:---enable-build-as-root}" EOF if [ ${WITH_SYSTEM_SPKG} = "force" ]; then cat <> $GITHUB_OUTPUT + echo "build_targets=$uninstall_targets reconfigure $build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT else - echo "build_targets=$build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT + echo "build_targets=$build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT fi cat $GITHUB_OUTPUT + - uses: actions/checkout@v4 with: ref: ${{ github.base_ref }} path: worktree-base if: github.base_ref && steps.changed-files.outputs.pkgs_all_changed_files + - name: Compute metrics run: | export PATH=build/bin:$PATH @@ -140,6 +163,7 @@ jobs: else sage-package metrics :all: fi + - name: Install test prerequisites # From docker.yml run: | @@ -147,6 +171,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -254,9 +279,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -264,6 +291,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -352,9 +380,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -362,6 +392,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -469,9 +500,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -479,6 +512,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG diff --git a/CITATION.cff b/CITATION.cff index b74bd3b74a2..8982711590a 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.5.beta6 +version: 10.5.beta7 doi: 10.5281/zenodo.8042260 -date-released: 2024-09-29 +date-released: 2024-10-12 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index b250e9488be..ff5659834ae 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.5.beta6, Release Date: 2024-09-29 +SageMath version 10.5.beta7, Release Date: 2024-10-12 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 77808d2891f..daf9720f908 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=140d921780212198287a0d626b8c9655d4408f7a -sha256=4f82ec8bdb67c4dffd43daddd5160e8f9c624f6a5cf16c4128bfab2e5ddca459 +sha1=978eb775a20fea3ed9b88f0d67ecd84a3d9cd6ea +sha256=c3987bb0f8aca81e112a17d8904ef2353a706159d43250305dc2bcac4ca2e33a diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 87a46389400..009fd772239 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -2b4fe39e420df022d34a67fbc820252618e15f83 +4326d0d9422011034a230ab3c1445fafeb2ac444 diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 5ece8b4891e..207487119a6 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,7 +1,8 @@ SAGE_SPKG_CONFIGURE([pari], [ dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV m4_pushdef([SAGE_PARI_MINVER],["134916"])dnl this version and higher allowed - m4_pushdef([SAGE_PARI_MAXVER],["999999"])dnl this version and higher not allowed + dnl Do not allow Pari 2.17 or later, see #38769: + m4_pushdef([SAGE_PARI_MAXVER],["135424"])dnl this version and higher not allowed SAGE_SPKG_DEPCHECK([gmp readline], [ AC_PATH_PROG([GP], [gp]) if test x$GP = x; then dnl GP test diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 deleted file mode 100644 index 0552d0c56ce..00000000000 --- a/build/pkgs/r/spkg-configure.m4 +++ /dev/null @@ -1,14 +0,0 @@ -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 - ]) - ], [sage_spkg_install_r=yes]) - m4_popdef([SAGE_R_MINVER]) -]) diff --git a/build/pkgs/r_jupyter/dependencies b/build/pkgs/r_jupyter/dependencies index 44078fe297e..c9895da2b6d 100644 --- a/build/pkgs/r_jupyter/dependencies +++ b/build/pkgs/r_jupyter/dependencies @@ -1 +1,5 @@ -notebook r +notebook rpy2 + +---------- +R is the real dependency. But SPKG r is a dummy package, and does not install R. +Since SPKG rpy2 checks for the system R, we put rpy2 as a dependency instead. diff --git a/build/pkgs/rpy2/dependencies b/build/pkgs/rpy2/dependencies index b88615716d4..271ec08a7f9 100644 --- a/build/pkgs/rpy2/dependencies +++ b/build/pkgs/rpy2/dependencies @@ -1,4 +1,4 @@ - r cffi tzlocal pytz jinja2 | $(PYTHON_TOOLCHAIN) pycparser $(PYTHON) +cffi tzlocal pytz jinja2 | $(PYTHON_TOOLCHAIN) pycparser $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/rpy2/spkg-configure.m4 b/build/pkgs/rpy2/spkg-configure.m4 index 0cb3784ea9c..e19713c5775 100644 --- a/build/pkgs/rpy2/spkg-configure.m4 +++ b/build/pkgs/rpy2/spkg-configure.m4 @@ -1,14 +1,36 @@ SAGE_SPKG_CONFIGURE([rpy2], [ - SAGE_PYTHON_PACKAGE_CHECK([rpy2]) + SAGE_PYTHON_PACKAGE_CHECK([rpy2]) ], [dnl REQUIRED-CHECK - AC_REQUIRE([SAGE_SPKG_CONFIGURE_R]) - dnl rpy2 is only needed when there is a usable system R - AS_VAR_IF([sage_spkg_install_r], [yes], [dnl - AS_VAR_IF([sage_use_system_r], [installed], [dnl - dnl Legacy SPKG installation of r - AS_VAR_SET([SPKG_REQUIRE], [yes]) - ], [dnl No system package, no legacy SPKG installation - AS_VAR_SET([SPKG_REQUIRE], [no]) - ]) + dnl rpy2 is only needed when there is a usable system R + dnl Check for the R installation and version + 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_EXECUTABLE], [R]) + AS_IF([test "x$R_EXECUTABLE" = x], [dnl + AC_MSG_NOTICE([R is not found]) + dnl No R found, so do not require rpy2 package + AS_VAR_SET([SPKG_REQUIRE], [no]) + ], [dnl Extract R version + AC_MSG_CHECKING([for version of R executable]) + R_VERSION=$($R_EXECUTABLE --version | sed -n 's/^R version \([[0-9.]]*\).*/\1/p') + AC_MSG_RESULT([$R_VERSION]) + dnl Extract libR version + AC_MSG_CHECKING([for version of libR]) + LIBR_VERSION=$(pkg-config --modversion libR) + AC_MSG_RESULT([$LIBR_VERSION]) + dnl Compare R and libR versions + AS_IF([test "x$R_VERSION" = "x$LIBR_VERSION"], [dnl + AC_MSG_NOTICE([R and libR versions match ($R_VERSION)]) + dnl Good system R is found, require rpy2 package + AS_VAR_SET([SPKG_REQUIRE], [yes]) + ], [dnl R and libR versions do not match + AC_MSG_NOTICE([R version ($R_VERSION) does not match libR version ($LIBR_VERSION)]) + AS_VAR_SET([SPKG_REQUIRE], [no]) + ]) ]) + ], [dnl libR not found or outdated + AS_VAR_SET([SPKG_REQUIRE], [no]) + ]) + m4_popdef([SAGE_R_MINVER]) ]) diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index e8756c3ed2e..d44223c9381 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.5b6 +sage-conf ~= 10.5b7 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index 0d48260c2b5..4547750313b 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.5b6 +sage-docbuild ~= 10.5b7 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index 8ed26a30af6..e5817221c36 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.5b6 +sage-setup ~= 10.5b7 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index d7e1976c10f..98387b4f230 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.5b6 +sage-sws2rst ~= 10.5b7 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 6a025036f77..945f0defa0c 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.5b6 +sagemath-standard ~= 10.5b7 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index 0c4ed7bfbe2..c800884c141 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.5b6 +sagemath-bliss ~= 10.5b7 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 332e97045f1..207da958f87 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.5b6 +sagemath-categories ~= 10.5b7 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index 358218fdf52..4f875089251 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.5b6 +sagemath-coxeter3 ~= 10.5b7 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 6c1bfca1159..826485f3148 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.5b6 +sagemath-environment ~= 10.5b7 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index 7d912264960..6cc3008eebd 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.5b6 +sagemath-mcqd ~= 10.5b7 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index eaac709b9e8..a1468e68dcd 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.5b6 +sagemath-meataxe ~= 10.5b7 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 7548688007d..bfc33505932 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.5b6 +sagemath-objects ~= 10.5b7 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index ecc83e28712..6ee449e0010 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.5b6 +sagemath-repl ~= 10.5b7 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index 5e1de281a8c..4807b6d0eb8 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.5b6 +sagemath-sirocco ~= 10.5b7 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index a18dda841b4..bf62c189022 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.5b6 +sagemath-tdlib ~= 10.5b7 diff --git a/build/pkgs/sagetex/dependencies b/build/pkgs/sagetex/dependencies index af9b5f370fb..13610c4b390 100644 --- a/build/pkgs/sagetex/dependencies +++ b/build/pkgs/sagetex/dependencies @@ -1,4 +1,4 @@ - maxima scipy matplotlib pillow tachyon pyparsing | $(PYTHON) + maxima scipy matplotlib pillow tachyon pyparsing | $(PYTHON_TOOLCHAIN) $(PYTHON) To build SageTeX, you just need Python and pyparsing, but to test (SAGE_CHECK=yes) SageTeX, you actually need to run Sage, produce plots,... diff --git a/build/pkgs/suitesparse/spkg-install.in b/build/pkgs/suitesparse/spkg-install.in index ab5253d2e77..f7ef16bb48b 100644 --- a/build/pkgs/suitesparse/spkg-install.in +++ b/build/pkgs/suitesparse/spkg-install.in @@ -9,7 +9,10 @@ echo "Configuring suitesparse" # gcc and gfortran version are not matching. # * SUITESPARSE_ENABLE_PROJECTS semi column separated list of the desired packages. Default is # all the packages in the suitesparse tarball. +# On macOS ARM cvxopt does not start if suitesparse uses @rpath, set explicit install name dir instead sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_INSTALL_NAME_DIR="$SAGE_LOCAL/lib" \ + -DCMAKE_MACOSX_RPATH=OFF \ -DNSTATIC=ON \ -DSUITESPARSE_USE_FORTRAN=OFF \ -DSUITESPARSE_INCLUDEDIR_POSTFIX="" \ diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/src/VERSION.txt b/src/VERSION.txt index fd111ad93ec..92fecb8fddc 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.5.beta6 +10.5.beta7 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 23685eb51c0..6e92078d830 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.5.beta6' -SAGE_RELEASE_DATE='2024-09-29' -SAGE_VERSION_BANNER='SageMath version 10.5.beta6, Release Date: 2024-09-29' +SAGE_VERSION='10.5.beta7' +SAGE_RELEASE_DATE='2024-10-12' +SAGE_VERSION_BANNER='SageMath version 10.5.beta7, Release Date: 2024-10-12' diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 190e951186d..6bc9946a442 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1999,6 +1999,10 @@ REFERENCES: .. [CS2018] Craig Costello and Benjamin Smith: Montgomery curves and their arithmetic. J. Cryptogr. Eng. 8 (2018), 227-240. +.. [CS2022] \Logan Crew, and Sophie Spirkl. *Modular relations of the Tutte symmetric function*, + Journal of Combinatorial Theory, Series A, Volume 187, 2022, 105572, + :doi:`10.1016/j.jcta.2021.105572.` + .. [CST2010] Tullio Ceccherini-Silberstein, Fabio Scarabotti, Filippo Tolli. *Representation Theory of the Symmetric Groups: The @@ -6151,6 +6155,11 @@ REFERENCES: of the chromatic polynomial of a graph*, Adv. Math., ***111*** no.1 (1995), 166-194. :doi:`10.1006/aima.1995.1020`. +.. [Sta1998] \R. P. Stanley, *Graph colorings and related symmetric functions: + ideas and applications A description of results, interesting applications, + & notable open problems*, Discrete Mathematics, 193, no.1-3, (1998), + 267-286. :doi:`10.1016/S0012-365X(98)00146-0`. + .. [Sta2002] Richard P. Stanley, *The rank and minimal border strip decompositions of a skew partition*, diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index a101064eb0e..6772673cbcb 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -462,7 +462,7 @@ def d_vector(self) -> tuple: sage: x.d_vector() (1, 1, 2, -2) """ - monomials = self.lift().dict() + monomials = self.lift().monomial_coefficients() minimal = map(min, zip(*monomials)) return tuple(-vector(minimal))[:self.parent().rank()] @@ -615,8 +615,9 @@ def theta_basis_decomposition(self): f_poly = components[g_vect].F_polynomial() g_vect = vector(g_vect) while f_poly != zero_U: - y_exp = min(f_poly.dict()) - coeff = f_poly.dict()[y_exp] + coeffs = f_poly.monomial_coefficients() + y_exp = min(coeffs) + coeff = coeffs[y_exp] g_theta = tuple(g_vect + B * vector(y_exp)) out[g_theta] = out.get(g_theta, zero_A) + A({zero_t + tuple(y_exp): coeff}) f_poly -= U({y_exp: coeff}) * A.theta_basis_F_polynomial(g_theta) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index d71f93a2600..fdcaeedaad8 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -222,7 +222,7 @@ def image_monomial(exponent): return A.zero() for g in I.gens(): - d = g.dict() + d = g.monomial_coefficients() res = A.sum(d[ex] * image_monomial(ex) for ex in d) if not res.is_zero(): raise ValueError("the differential does not preserve the ideal") @@ -303,7 +303,7 @@ def _call_(self, x): if x.is_zero(): return self.codomain().zero() res = self.codomain().zero() - dic = x.dict() + dic = x.monomial_coefficients() for key in dic: keyl = list(key) coef = dic[key] @@ -392,11 +392,11 @@ def differential_matrix(self, n): A = self.domain() dom = A.basis(n) cod = A.basis(n + 1) - cokeys = [next(iter(a.lift().dict().keys())) for a in cod] + cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod] m = matrix(A.base_ring(), len(dom), len(cod)) for i, domi in enumerate(dom): im = self(domi) - dic = im.lift().dict() + dic = im.lift().monomial_coefficients() for j in dic.keys(): k = cokeys.index(j) m[i, k] = dic[j] @@ -670,11 +670,11 @@ def differential_matrix_multigraded(self, n, total=False): n = G(vector(n)) dom = A.basis(n) cod = A.basis(n + self._degree_of_differential) - cokeys = [next(iter(a.lift().dict().keys())) for a in cod] + cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod] m = matrix(self.base_ring(), len(dom), len(cod)) for i, domi in enumerate(dom): im = self(domi) - dic = im.lift().dict() + dic = im.lift().monomial_coefficients() for j in dic.keys(): k = cokeys.index(j) m[i, k] = dic[j] @@ -1182,7 +1182,7 @@ def basis(self, n): basis = [] for v in free_basis: el = prod([self.gen(i)**v[i] for i in range(len(v))]) - di = el.dict() + di = el.monomial_coefficients() if len(di) == 1: k, = di.keys() if tuple(k) == v: @@ -1477,7 +1477,7 @@ def degree(self, total=False): """ if self.is_zero(): raise ValueError("the zero element does not have a well-defined degree") - exps = self.lift().dict().keys() + exps = self.monomial_coefficients().keys() degrees = self.parent()._degrees n = self.parent().ngens() l = [sum(e[i] * degrees[i] for i in range(n)) for e in exps] @@ -1548,7 +1548,7 @@ def homogeneous_parts(self): sage: a.homogeneous_parts() {1: -2*e3 + e5, 2: e1*e2, 3: e1*e3*e5 - 3*e2*e3*e5} """ - dic = self.dict() + dic = self.monomial_coefficients() terms = [self.parent()({t: dic[t]}) for t in dic.keys()] res = {} for term in terms: @@ -1559,7 +1559,7 @@ def homogeneous_parts(self): res[deg] = term return {i: res[i] for i in sorted(res.keys())} - def dict(self): + def monomial_coefficients(self): r""" A dictionary that determines the element. @@ -1569,11 +1569,18 @@ def dict(self): EXAMPLES:: sage: A. = GradedCommutativeAlgebra(QQ, degrees=(1, 2, 2, 3)) - sage: dic = (x*y - 5*y*z + 7*x*y^2*z^3*t).dict() - sage: sorted(dic.items()) + sage: elt = x*y - 5*y*z + 7*x*y^2*z^3*t + sage: sorted(elt.monomial_coefficients().items()) + [((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)] + + ``dict`` is an alias:: + + sage: sorted(elt.dict().items()) [((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)] """ - return self.lift().dict() + return self.lift().monomial_coefficients() + + dict = monomial_coefficients def __call__(self, *values, **kwargs): r""" @@ -1645,8 +1652,8 @@ def __call__(self, *values, **kwargs): if gstr in kwargs: images[i] = kwargs[gstr] res = 0 - for (m, c) in self.dict().items(): - term = prod((gen**y for (y, gen) in zip(m, images)), c) + for m, c in self.monomial_coefficients().items(): + term = prod((gen ** y for y, gen in zip(m, images)), c) res += term return res @@ -2000,7 +2007,7 @@ def degree(self, total=False): raise ValueError("the zero element does not have a well-defined degree") degrees = self.parent()._degrees_multi n = self.parent().ngens() - exps = self.lift().dict().keys() + exps = self.monomial_coefficients().keys() l = [sum(exp[i] * degrees[i] for i in range(n)) for exp in exps] if len(set(l)) == 1: return l[0] @@ -3861,7 +3868,7 @@ def _call_(self, x): """ codomain = self.codomain() result = codomain.zero() - for mono, coeff in x.dict().items(): + for mono, coeff in x.monomial_coefficients().items(): term = prod([gen**y for (y, gen) in zip(mono, self.im_gens())], codomain.one()) result += coeff * term diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index 6080b82a396..8b73482e7bf 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -651,7 +651,7 @@ def exp_to_monomial(T): return M([(i % ngens, Ti) for i, Ti in enumerate(T) if Ti]) return self.element_class(self, {exp_to_monomial(T): c - for T, c in x.letterplace_polynomial().dict().items()}) + for T, c in x.letterplace_polynomial().monomial_coefficients().items()}) # ok, not a free algebra element (or should not be viewed as one). if isinstance(x, str): from sage.misc.sage_eval import sage_eval diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index 2e04549ec7f..ba2a626c9f0 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -273,7 +273,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): if i % n_proc == child_id: he = req_cy(basis, r_matrix, fvars, _Nk_ij, id_anyon, sextuple) if he: - red = reduce_poly_dict(he.dict(), _nnz, _ks, one) + red = reduce_poly_dict(he.monomial_coefficients(), _nnz, _ks, one) # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -341,7 +341,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): if i % n_proc == child_id: pe = feq_cy(basis, fvars, _Nk_ij, id_anyon, zero, nonuple, prune=True) if pe: - red = reduce_poly_dict(pe.dict(), _nnz, _ks, one) + red = reduce_poly_dict(pe.monomial_coefficients(), _nnz, _ks, one) # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index 6c0b8d2ac27..8d6869bd484 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -26,7 +26,7 @@ cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): sage: poly_to_tup(x**2*y**4 - 4/5*x*y**2 + 1/3 * y) (((2, 4), 1), ((1, 2), -4/5), ((0, 1), 1/3)) """ - return tuple(poly.dict().items()) + return tuple(poly.monomial_coefficients().items()) cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): r""" diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py index 3521e9bd478..b980e2fa80c 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py @@ -148,7 +148,7 @@ def _act_(self, perm, pol): if not self.is_left(): perm, pol = pol, perm pol_dict = {} - for key, value in pol.dict().items(): + for key, value in pol.monomial_coefficients().items(): newkey = [0] * len(key) for pos, k in enumerate(key): newkey[perm(pos + 1) - 1] = k diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index 48796fbd605..01390f1a5a4 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -83,7 +83,7 @@ def normalized_laurent_polynomial(R, p): u + v^-1 + u^-1 """ try: - return R({k: R._base(c) for k, c in p.dict().items()}) + return R({k: R._base(c) for k, c in p.monomial_coefficients().items()}) except (AttributeError, TypeError): return R(p) diff --git a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx index 5b99c87dd17..2a977b1a513 100644 --- a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx @@ -139,8 +139,8 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: sorted(p) # indirect doctest [((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)] """ - cdef dict d = self._poly.dict() - yield from d.iteritems() + cdef dict d = self._poly.monomial_coefficients() + yield from d.items() def _repr_(self): """ diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index ff888ba5aaa..99c0c1e226f 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -533,7 +533,7 @@ def product_on_basis(self, m1, m2): poly *= self._w_poly.monomial(*v) poly = poly.reduce([vp[i]**(4*k) - (1 + q**(-2*k)) * vp[i]**(2*k) + q**(-2*k) for i in range(self._n)]) - pdict = poly.dict() + pdict = poly.monomial_coefficients() ret = {(self._psi(p), tuple(e)): pdict[e] * q**q_power * sign for e in pdict} @@ -610,7 +610,7 @@ def inverse(self): for wi in wp}) poly = poly.reduce([wi**(4*k) - (1 + q**(-2*k)) * wi**(2*k) + q**(-2*k) for wi in wp]) - pdict = poly.dict() + pdict = poly.monomial_coefficients() coeff = coeff.inverse_of_unit() ret = {(p, tuple(e)): coeff * c for e, c in pdict.items()} return Cl.element_class(Cl, ret) diff --git a/src/sage/algebras/rational_cherednik_algebra.py b/src/sage/algebras/rational_cherednik_algebra.py index c3ff9ff25e6..1ded26a1112 100644 --- a/src/sage/algebras/rational_cherednik_algebra.py +++ b/src/sage/algebras/rational_cherednik_algebra.py @@ -328,11 +328,11 @@ def product_on_basis(self, left, right): def commute_w_hd(w, al): # al is given as a dictionary ret = P.one() for k in al: - x = sum(c * gens_dict[i] for i,c in alpha[k].weyl_action(w)) + x = sum(c * gens_dict[i] for i, c in alpha[k].weyl_action(w)) ret *= x**al[k] - ret = ret.dict() + ret = ret.monomial_coefficients() for k in ret: - yield (self._hd({I[i]: e for i,e in enumerate(k) if e != 0}), ret[k]) + yield (self._hd({I[i]: e for i, e in enumerate(k) if e != 0}), ret[k]) # Do Lac Ra if they are both non-trivial if dl and dr: @@ -374,7 +374,7 @@ def commute_w_hd(w, al): # al is given as a dictionary for i,c in alphacheck[k].weyl_action(right[1].reduced_word(), inverse=True)) ret *= x**dl[k] - ret = ret.dict() + ret = ret.monomial_coefficients() w = left[1]*right[1] return self._from_dict({(left[0], w, self._h({I[i]: e for i,e in enumerate(k) diff --git a/src/sage/algebras/splitting_algebra.py b/src/sage/algebras/splitting_algebra.py index cfcb86e5c28..43d72ed7470 100644 --- a/src/sage/algebras/splitting_algebra.py +++ b/src/sage/algebras/splitting_algebra.py @@ -102,7 +102,7 @@ def is_unit(self): return super().is_unit() - def dict(self): + def monomial_coefficients(self): r""" Return the dictionary of ``self`` according to its lift to the cover. @@ -110,11 +110,18 @@ def dict(self): sage: from sage.algebras.splitting_algebra import SplittingAlgebra sage: CR3. = SplittingAlgebra(cyclotomic_polynomial(3)) - sage: (e3 + 42).dict() + sage: f = e3 + 42 + sage: f.monomial_coefficients() + {0: 42, 1: 1} + + ``dict`` is an alias:: + + sage: f.dict() {0: 42, 1: 1} """ - return self.lift().dict() + return self.lift().monomial_coefficients() + dict = monomial_coefficients # ------------------------------------------------------------------------------------------------------------------ # Parent class of the splitting algebra @@ -282,7 +289,7 @@ def __init__(self, monic_polynomial, names='X', iterate=True, warning=True): root_names_reduces.remove(root_name) P = base_ring_step[root_names_reduces[0]] - p = P(monic_polynomial.dict()) + p = P(monic_polynomial.monomial_coefficients()) q, _ = p.quo_rem(P.gen() - first_root) verbose("Invoking recursion with: %s" % (q,)) diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index ac554795cfd..8eae4797c7d 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -786,7 +786,7 @@ def _element_constructor_(self, x): return self.element_class(self, {i: R(c) for i, c in x if R(c) != zero}) x = self._poly_ring(x) return self.element_class(self, {(tuple(m), t): c - for m, c in x.dict().items()}) + for m, c in x.monomial_coefficients().items()}) def _coerce_map_from_(self, R): """ diff --git a/src/sage/categories/additive_magmas.py b/src/sage/categories/additive_magmas.py index 691486e03d4..ccfda512014 100644 --- a/src/sage/categories/additive_magmas.py +++ b/src/sage/categories/additive_magmas.py @@ -971,8 +971,8 @@ def extra_super_categories(self): [Category of unital magmas] sage: C.super_categories() - [Category of unital algebras with basis over Rational Field, - Category of additive magma algebras over Rational Field] + [Category of additive magma algebras over Rational Field, + Category of unital algebras with basis over Rational Field] """ from sage.categories.magmas import Magmas return [Magmas().Unital()] diff --git a/src/sage/categories/algebra_functor.py b/src/sage/categories/algebra_functor.py index 0d6cb19b0e1..ae5ef793296 100644 --- a/src/sage/categories/algebra_functor.py +++ b/src/sage/categories/algebra_functor.py @@ -128,7 +128,6 @@ Category of semigroup algebras over Rational Field, ... Category of unital magma algebras over Rational Field, - ... Category of magma algebras over Rational Field, ... Category of set algebras over Rational Field, diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 03ae127de8f..f4a31b557f9 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -145,6 +145,7 @@ def radical_basis(self): We compute the radical basis in a subalgebra using the inherited product:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -155,14 +156,14 @@ def radical_basis(self): TESTS:: - sage: A = KleinFourGroup().algebra(GF(2)) # needs sage.groups sage.modules - sage: A.radical_basis() # needs sage.groups sage.modules + sage: # needs sage.groups sage.modules + sage: A = KleinFourGroup().algebra(GF(2)) + sage: A.radical_basis() (() + (1,2)(3,4), (3,4) + (1,2)(3,4), (1,2) + (1,2)(3,4)) - - sage: A = KleinFourGroup().algebra(QQ, category=Monoids()) # needs sage.groups sage.modules - sage: A.radical_basis.__module__ # needs sage.groups sage.modules + sage: A = KleinFourGroup().algebra(QQ, category=Monoids()) + sage: A.radical_basis.__module__ 'sage.categories.finite_dimensional_algebras_with_basis' - sage: A.radical_basis() # needs sage.groups sage.modules + sage: A.radical_basis() () """ F = self.base_ring() @@ -421,6 +422,7 @@ def subalgebra(self, gens, category=None, *args, **opts): EXAMPLES:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -429,6 +431,7 @@ def subalgebra(self, gens, category=None, *args, **opts): sage: A.dimension() 7 + sage: # needs sage.modules sage: L. = LieAlgebra(GF(3), {('x','z'): {'x':1, 'y':1}, ('y','z'): {'y':1}}) sage: MS = MatrixSpace(L.base_ring(), L.dimension()) sage: gens = [b.adjoint_matrix() for b in L.basis()] @@ -473,6 +476,7 @@ def ideal_submodule(self, gens, side='left', category=None, *args, **opts): EXAMPLES:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -1537,18 +1541,21 @@ def simple_module_parameterization(self): EXAMPLES:: + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(5, 30, QQ) # semisimple sage: len(TL.radical_basis()) 0 sage: TL.simple_module_parameterization() (1, 3, 5) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(5, 1, QQ) # not semisimple sage: len(TL.radical_basis()) 24 sage: TL.simple_module_parameterization() (1, 3, 5) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(6, 30, QQ) # semisimple sage: all(TL.cell_module(la).dimension() ....: == TL.cell_module(la).simple_module().dimension() @@ -1557,6 +1564,7 @@ def simple_module_parameterization(self): sage: TL.simple_module_parameterization() (0, 2, 4, 6) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(6, 0, QQ) # not semisimple sage: TL.simple_module_parameterization() (2, 4, 6) diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index aaa52b56ad1..43e476847d0 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -655,9 +655,11 @@ cdef class Map(Element): sage: f = R.hom([x+y, x-y], R) sage: f.category_for() Join of Category of unique factorization domains - and Category of commutative algebras - over (number fields and quotient fields and metric spaces) - and Category of infinite sets + and Category of algebras with basis over + (number fields and quotient fields and metric spaces) + and Category of commutative algebras over + (number fields and quotient fields and metric spaces) + and Category of infinite sets sage: f.category() Category of endsets of unital magmas and right modules over (number fields and quotient fields and metric spaces) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index d6d63b4df0a..f9bc036910f 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -893,7 +893,7 @@ def __init_extra__(self): (Vector space of dimension 2 over Rational Field, Univariate Polynomial Ring in x over Rational Field) sage: A.category() # needs sage.modules - Category of Cartesian products of vector spaces + Category of Cartesian products of vector spaces with basis over (number fields and quotient fields and metric spaces) sage: A.base_ring() # needs sage.modules Rational Field diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 7114761aaac..1635296450b 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -511,6 +511,7 @@ def representation(self, module, on_basis, side='left', *args, **kwargs): EXAMPLES:: + sage: # needs sage.groups sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: def on_basis(g, m): # cyclically permute generators @@ -1059,6 +1060,7 @@ def representation(self, module, on_basis, side='left', *args, **kwargs): EXAMPLES:: + sage: # needs sage.groups sage: G = groups.permutation.Dihedral(5) sage: CFM = CombinatorialFreeModule(GF(2), [1, 2, 3, 4, 5]) sage: A = G.algebra(GF(2)) diff --git a/src/sage/categories/triangular_kac_moody_algebras.py b/src/sage/categories/triangular_kac_moody_algebras.py index 19ff3846b67..d6541107a0f 100644 --- a/src/sage/categories/triangular_kac_moody_algebras.py +++ b/src/sage/categories/triangular_kac_moody_algebras.py @@ -215,6 +215,7 @@ def _triangular_key(self, x): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: L = lie_algebras.sl(QQ, 3) sage: La = L.cartan_type().root_system().weight_lattice().fundamental_weights() sage: sorted(L.basis().keys(), key=L._basis_key) @@ -352,6 +353,7 @@ def _transpose_basis_mapping(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['A', 2]) sage: g._transpose_basis_mapping {-alpha[1]: alpha[1], @@ -384,6 +386,7 @@ def _transpose_on_basis(self, m): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['B', 2]) sage: B = g.basis() sage: [(B[k], g._transpose_on_basis(k)) for k in B.keys()] @@ -407,6 +410,7 @@ def transpose(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['B', 2]) sage: g.transpose Generic endomorphism of Lie algebra of ['B', 2] in the Chevalley basis @@ -425,6 +429,7 @@ def transpose(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['G', 2]) sage: for b in g.basis(): ....: for bp in g.basis(): diff --git a/src/sage/coding/gabidulin_code.py b/src/sage/coding/gabidulin_code.py index cce8fcfc7d5..3db66e4af7b 100644 --- a/src/sage/coding/gabidulin_code.py +++ b/src/sage/coding/gabidulin_code.py @@ -207,7 +207,7 @@ def __init__(self, base_field, length, dimension, sub_field=None, if not len(evaluation_points) == length: raise ValueError("the number of evaluation points should be equal to the length of the code") for i in range(length): - if not evaluation_points[i] in base_field: + if evaluation_points[i] not in base_field: raise ValueError("evaluation point does not belong to the 'base field'") basis = self.matrix_form_of_vector(vector(evaluation_points)) if basis.rank() != length: diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 93cdfef5c8a..7e07835b935 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -232,7 +232,8 @@ def __init__(self, data, frozen=None, user_labels=None): values)) # constructs a quiver from a mutation type - if type( data ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]: + if isinstance(data, (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)): if frozen is not None: print('The input specifies a mutation type, so the' ' additional parameter frozen is ignored.' @@ -1950,10 +1951,15 @@ def is_mutation_finite( self, nr_of_checks=None, return_path=False ): path = None elif not return_path and self._mutation_type == 'undetermined infinite mutation type': is_finite = False - elif type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and self._mutation_type.is_mutation_finite(): + elif (isinstance(self._mutation_type, (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)) + and self._mutation_type.is_mutation_finite()): is_finite = True path = None - elif not return_path and type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and not self._mutation_type.is_mutation_finite(): + elif (not return_path and isinstance(self._mutation_type, + (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)) + and not self._mutation_type.is_mutation_finite()): is_finite = False else: # turning dg_component into a canonical form diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py index 25610dff117..c9f06a4761f 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py @@ -67,7 +67,9 @@ def __call__(self, *args): _mutation_type_error(data) # check for reducible types - if all(type(data_component) in [list, tuple, QuiverMutationType_Irreducible] for data_component in data): + if all(isinstance(data_component, (list, tuple, + QuiverMutationType_Irreducible)) + for data_component in data): if len(data) == 1: return QuiverMutationType(data[0]) else: diff --git a/src/sage/combinat/core.py b/src/sage/combinat/core.py index d6775040c48..21ce49f57f1 100644 --- a/src/sage/combinat/core.py +++ b/src/sage/combinat/core.py @@ -102,7 +102,7 @@ def __init__(self, parent, core): raise ValueError("%s is not a %s-core" % (part, k)) CombinatorialElement.__init__(self, parent, core) - def __eq__(self, other): + def __eq__(self, other) -> bool: """ Test for equality. @@ -123,7 +123,7 @@ def __eq__(self, other): self.parent().k == other.parent().k) return False - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Test for un-equality. @@ -169,7 +169,7 @@ def __hash__(self): self._hash = hash(tuple(self._list)) + hash(self.parent().k) return self._hash - def _latex_(self): + def _latex_(self) -> str: r""" Output the LaTeX representation of this core as a partition. @@ -449,7 +449,7 @@ def weak_le(self, other): ... ValueError: the two cores do not have the same k """ - if type(self) is type(other): + if isinstance(other, Core): if self.k() != other.k(): raise ValueError("the two cores do not have the same k") else: @@ -504,14 +504,14 @@ def strong_le(self, other): ... ValueError: the two cores do not have the same k """ - if type(self) is type(other): + if isinstance(other, Core): if self.k() != other.k(): raise ValueError("the two cores do not have the same k") else: other = Core(other, self.k()) return other.contains(self) - def contains(self, other): + def contains(self, other) -> bool: r""" Check whether ``self`` contains ``other``. diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 30bbeafb708..7977de93d9d 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -21,7 +21,7 @@ REFERENCES: .. [1] La Jolla Covering Repository, - https://ljcr.dmgordon.org/cover.html + https://dmgordon.org/cover .. [2] Daniel M. Gordon and Douglas R. Stinson, *Coverings*, Chapter 1 in: Charles J. Colbourn and Jeffrey H. Dinitz, @@ -516,7 +516,7 @@ def best_known_covering_design_www(v, k, t, verbose=False): k = int(k) t = int(t) param = "?v=%s&k=%s&t=%s" % (v, k, t) - url = "https://ljcr.dmgordon.org/cover/get_cover.php" + param + url = "https://ljcr.dmgordon.org/get_cover.php" + param if verbose: print("Looking up the bounds at %s" % url) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index dc614bd7b25..19eb9d26165 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -3143,15 +3143,58 @@ def QDM_57_9_1_1_8(): [0,2,13,18,28,30,44,48,50,51,57,61], [0,4,21,26,29,33,35,36,47,55,56,60]]}, -# a 133-cyclic set from Ken Smith database -# see https://math.ccrwest.org/diffsets/diff_sets/DS_133_33_8_133.html +# a (133,33,8)-cyclic difference set +# see https://dmgordon.org/diffset (133,33, 8): - {(133,): [[0,4,7,8,15,17,19,22,24,25,29,30,38, - 47,49,50,55,58,61,62,71,73,76,77,78, - 82,95,111,113,114,121,123,127]]}, - -# a 901-cyclic -# see https://math.ccrwest.org/diffsets/diff_sets/DS_901_225_56_901.html + {(133,): [[1,5,14,22,25,27,29,32,34,38, + 46,64,65,66,76,78,81,82,84,89, + 92,93,99,103,104,106,107,112,113,122, + 126,128,129]]}, + +# a (144,66,30) non-cyclic difference set in AbelianGroup([2,8,3,3]) +# given in unpublished paper by Kroeger, Miller, Mooney, Shepard and Smith +# see https://dmgordon.org/diffset +(144,66,30): + {(2,8,3,3): [[(0,1,0,0),(0,7,0,2),(0,5,0,1),(0,3,0,0),(0,6,0,1), + (0,1,0,2),(0,4,0,0),(0,2,0,2),(0,6,0,0),(0,1,0,1), + (0,4,0,2),(0,2,0,1),(1,2,2,0),(1,3,2,0),(1,4,2,0), + (1,5,2,0),(1,6,2,0),(1,7,2,0),(0,6,1,2),(0,1,1,0), + (0,4,1,1),(0,3,1,0),(0,1,1,2),(0,4,1,0),(0,7,1,1), + (0,2,1,2),(0,6,1,0),(0,1,1,1),(0,2,1,1),(0,5,1,2), + (1,0,0,0),(1,6,0,2),(1,1,0,0),(1,4,0,1),(1,7,0,2), + (1,2,0,0),(1,5,0,1),(1,0,0,2),(1,3,0,0),(1,1,0,2), + (1,0,0,1),(1,1,0,1),(0,0,2,0),(0,6,2,2),(0,4,2,1), + (0,0,2,2),(0,3,2,0),(0,6,2,1),(0,2,2,2),(0,5,2,0), + (0,0,2,1),(0,4,2,2),(0,7,2,0),(0,2,2,1),(1,0,1,0), + (1,1,1,0),(1,2,1,0),(1,0,1,2),(1,3,1,0),(1,6,1,1), + (1,1,1,2),(1,7,1,1),(1,0,1,1),(1,1,1,1),(1,4,1,2), + (1,5,1,2)]]}, + +# a (320,88,24) non-cyclic difference set in AbelianGroup([4,4,4,5]), +# given in Arasu and Chen, Designs, Codes and Cryptography 2001 +# see https://dmgordon.org/diffset +(320,88,24): + {(4,4,4,5): [[(3,3,3,0),(2,3,2,0),(3,1,3,0),(2,2,3,0),(1,3,3,0), + (3,2,1,0),(2,2,2,0),(2,2,1,0),(2,1,2,0),(0,3,2,0), + (2,0,3,0),(1,1,3,0),(0,2,3,0),(3,0,1,0),(1,2,1,0), + (2,0,2,0),(0,2,2,0),(2,0,1,0),(0,2,1,0),(0,1,2,0), + (0,0,3,0),(1,0,1,0),(0,0,2,0),(0,0,1,0),(3,3,3,1), + (3,3,1,1),(3,0,3,1),(0,3,3,1),(3,0,1,1),(0,3,1,1), + (1,1,2,1),(1,0,2,1),(0,1,2,1),(0,0,3,1),(1,1,0,1), + (0,0,2,1),(1,0,0,1),(0,1,0,1),(0,0,1,1),(0,0,0,1), + (1,1,3,2),(3,1,1,2),(2,3,3,2),(2,2,3,2),(0,3,2,2), + (0,3,1,2),(0,2,1,2),(3,2,2,2),(3,1,2,2),(3,0,3,2), + (2,3,0,2),(2,0,2,2),(1,2,0,2),(1,1,0,2),(1,0,1,2), + (0,0,0,2),(1,1,1,3),(1,3,3,3),(3,2,1,3),(2,2,3,3), + (3,0,0,3),(3,0,3,3),(1,3,0,3),(2,0,1,3),(3,2,2,3), + (2,3,2,3),(0,3,3,3),(1,1,2,3),(0,2,2,3),(2,1,0,3), + (0,1,1,3),(0,0,0,3),(2,0,3,4),(1,1,2,4),(0,2,1,4), + (0,1,3,4),(3,2,3,4),(3,2,2,4),(2,3,2,4),(3,1,3,4), + (3,3,0,4),(2,3,1,4),(1,0,1,4),(2,2,2,4),(1,3,1,4), + (1,0,0,4),(0,1,0,4),(0,0,0,4)]]}, + +# a (901,225,56)-cyclic difference set +# see https://dmgordon.org/diffset (901,225,56): {(901,): [[ 0, 1, 5, 9, 12, 13, 14, 16, 22, 25, 41, 43, 45, 47, 53, 59, 60, 65, 69, 70, 71, 79, 80, 81, @@ -4283,7 +4326,7 @@ def BIBD_66_6_1(): Return a (66,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: @@ -4307,7 +4350,7 @@ def BIBD_76_6_1(): Return a (76,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: @@ -4331,7 +4374,7 @@ def BIBD_96_6_1(): Return a (96,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 8bf7f14fd0b..b5135151866 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -72,7 +72,7 @@ REFERENCES: .. [1] La Jolla Covering Repository, - https://math.ccrwest.org/cover.html + https://dmgordon.org/cover """ from sage.misc.lazy_import import lazy_import diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 2ec008f16db..49b25c12dd0 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -3690,7 +3690,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch 3 Turyn 1965 construction 4 McFarland 1973 construction 5 False - 6 Unknown + 6 The database contains a (144,66,30)-difference family 7 False 8 McFarland 1973 construction 9 Unknown diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 39d1769f0b6..af82a1569f3 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -697,7 +697,7 @@ def from_polynomial(self, f, check=True): -F[1, 1] + F[2] """ assert self.base_ring() == f.base_ring() - exponent_coefficient = f.dict() + exponent_coefficient = f.monomial_coefficients() z = {} for e, c in exponent_coefficient.items(): I = Compositions()([ei for ei in e if ei]) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 6471163fe00..e565eb3d2ee 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -8884,11 +8884,11 @@ class FinitePosets_n(UniqueRepresentation, Parent): sage: P.cardinality() 5 sage: for p in P: print(p.cover_relations()) - [] - [[1, 2]] + [[1, 2], [0, 2]] [[0, 1], [0, 2]] + [[0, 2]] [[0, 1], [1, 2]] - [[1, 2], [0, 2]] + [] """ def __init__(self, n) -> None: @@ -8930,27 +8930,40 @@ def __contains__(self, P) -> bool: return P in FinitePosets() and P.cardinality() == self._n def __iter__(self): - """ + r""" Return an iterator of representatives of the isomorphism classes of finite posets of a given size. .. NOTE:: - This uses the DiGraph iterator as a backend to construct - transitively-reduced, acyclic digraphs. + If the size `n\leq 16`, this uses an iterator from + ``nauty`` as a backend to construct only transitively-reduced, + acyclic digraphs. Otherwise it uses a slow naive iterator, + as the ``nauty`` iterator is not available. EXAMPLES:: sage: P = Posets(2) sage: list(P) [Finite poset containing 2 elements, Finite poset containing 2 elements] + + TESTS:: + + sage: it = iter(Posets(17)) + sage: next(it) + Finite poset containing 17 elements """ - from sage.graphs.digraph_generators import DiGraphGenerators - for dig in DiGraphGenerators()(self._n, is_poset): + if self._n <= 16: + it = digraphs.nauty_posetg(f"{self._n} o") + else: + it = digraphs(self._n, is_poset) + + for dig in it: # We need to relabel the digraph since range(self._n) must be a linear # extension. Too bad we need to compute this again. TODO: Fix this. - label_dict = dict(zip(dig.topological_sort(), range(dig.order()))) - yield FinitePoset(dig.relabel(label_dict, inplace=False)) + label_dict = dict(zip(dig.topological_sort(), range(self._n))) + dig.relabel(label_dict, inplace=True) + yield FinitePoset(dig) def cardinality(self, from_iterator=False): r""" diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 5fe5e161575..0d00c45273f 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -369,8 +369,7 @@ def _repr_(self): def inner_product(self, lambdacheck): """ - The scalar product with elements of the coroot lattice - embedded in the ambient space. + The scalar product with elements of the ambient space. EXAMPLES:: @@ -379,15 +378,32 @@ def inner_product(self, lambdacheck): (-1, 0, 0) sage: a.inner_product(a) 2 + + TESTS: + + Verify that :issue:`15325` (A) is fixed:: + + sage: rt = RootSystem(['E', 8]) + sage: lat = rt.root_lattice() + sage: spc = rt.ambient_space() + sage: spc.simple_root(1).scalar(lat.simple_coroot(2)) + 0 """ + if self.parent() is not lambdacheck.parent(): + try: + # see if lambdacheck can be converted to the ambient space + lambdacheck = self.parent()(lambdacheck) + except (TypeError, ValueError): + raise TypeError(f"unable to coerce {lambdacheck} into {self.parent()}") + + # self and lambdacheck both belong to the same ambient space, so use the inner product there self_mc = self._monomial_coefficients lambdacheck_mc = lambdacheck._monomial_coefficients result = self.parent().base_ring().zero() for t,c in lambdacheck_mc.items(): - if t not in self_mc: - continue - result += c*self_mc[t] + if t in self_mc: + result += c*self_mc[t] return result scalar = inner_product diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index b7d4b163242..0cda82af318 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -1046,7 +1046,7 @@ def modular_characteristic(self, mu=None): sage: [V.modular_characteristic(x) for x in V.dominant_maximal_weights()] [11/56, -1/280, 111/280] """ - if type(mu) is tuple: + if isinstance(mu, tuple): n = mu else: n = self.from_weight(mu) diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index 030b108c774..970f134ba90 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -121,7 +121,7 @@ def from_polynomial(self, p): """ L = self.basis().keys() return self.sum_of_terms((L.from_vector(vector(t)), c) - for (t,c) in p.dict().items()) + for t, c in p.monomial_coefficients().items()) @cached_method def divided_difference_on_basis(self, weight, i): diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index 96a68eacc0f..14f5ed4ab23 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -260,13 +260,34 @@ def scalar(self, lambdacheck): [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -2 2] + + TESTS: + + Verify that :issue:`15325` (A) is fixed:: + + sage: rt = RootSystem(['E', 8]) + sage: lat = rt.root_lattice() + sage: spc = rt.ambient_space() + sage: lat.simple_root(1).scalar(spc.simple_coroot(2)) + 0 + + Verify that directionality is correct for roots of different lengths:: + + sage: lat = RootSystem(['B', 3]).root_lattice() + sage: lat.simple_root(2).scalar(lat.simple_coroot(3)) + -2 """ - # Find some better test - if not (lambdacheck in self.parent().coroot_lattice() or lambdacheck in self.parent().coroot_space()): - raise TypeError("%s is not in a coroot lattice/space" % (lambdacheck)) - zero = self.parent().base_ring().zero() - cartan_matrix = self.parent().dynkin_diagram() - return sum( (sum( (lambdacheck[i]*s for i,s in cartan_matrix.column(j)), zero) * c for j,c in self), zero) + if lambdacheck in self.parent().coroot_lattice() or lambdacheck in self.parent().coroot_space(): + # This is the mathematically canonical case, where we use the Cartan matrix to find the scalar product + zero = self.parent().base_ring().zero() + cartan_matrix = self.parent().dynkin_diagram() + return sum( (sum( (lambdacheck[i]*s for i,s in cartan_matrix.column(j)), zero) * c for j,c in self), zero) + + if lambdacheck in self.parent().root_system.ambient_space(): + # lambdacheck lives in the ambient space of the root space, so we take the usual dot product in the ambient space + return self.to_ambient().dot_product(lambdacheck) + + raise TypeError(f"{lambdacheck} is not in a coroot lattice/space") def is_positive_root(self): """ diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index ac70314eae1..06210a749e7 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -787,6 +787,55 @@ def coambient_space(self, base_ring=QQ): """ return self.dual.ambient_space(base_ring) + def coxeter_number(self): + """ + Return the Coxeter number of an irreducible finite root system. + + .. SEEALSO:: + + :meth:`~sage.combinat.root_system.cartan_type.CartanType_standard_finite.coxeter_number`. + + EXAMPLES:: + + sage: rt = RootSystem(['C', 5]) + sage: rt.coxeter_number() + 10 + """ + # Check if RootSystem is finite and irreducible + if not (self.is_finite() and self.is_irreducible()): + raise ValueError("the Coxeter number is defined only for finite and irreducible root systems") + # Hand over to CartanType method + return self._cartan_type.coxeter_number() + + def dual_coxeter_number(self): + """ + Return the dual Coxeter number of a irreducible finite root system. + + The dual Coxeter number is equal to 1 plus the sum of the coefficients + of simple roots in the highest short root of the dual root system. + + .. SEEALSO:: :meth:`~sage.combinat.root_system.cartan_type.CartanType_standard_finite.dual_coxeter_number` + + EXAMPLES:: + + sage: rt = RootSystem(['C', 5]) + sage: rt.dual_coxeter_number() + 6 + + The dual Coxeter number is not the same concept as the Coxeter number + of the dual root system:: + + sage: rt.dual + Dual of root system of type ['C', 5] + sage: rt.dual.coxeter_number() + 10 + """ + # Check if RootSystem is finite and irreducible + if not (self.is_finite() and self.is_irreducible()): + raise ValueError("the dual Coxeter number is defined only for finite and irreducible root systems") + # Hand over to CartanType method + return self._cartan_type.dual_coxeter_number() + def WeylDim(ct, coeffs): """ diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 0afecfa4b89..bb7b54bae4e 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -135,21 +135,25 @@ def product(self, left, right): return self._from_dict(z_elt) def from_polynomial(self, f, check=True): - """ - Return the symmetric function in the monomial basis corresponding to the polynomial ``f``. + r""" + Return the symmetric function in the monomial basis corresponding + to the polynomial ``f``. INPUT: - ``self`` -- a monomial symmetric function basis - - ``f`` -- a polynomial in finitely many variables over the same base ring as ``self``; - it is assumed that this polynomial is symmetric - - ``check`` -- boolean (default: ``True``); checks whether the polynomial is indeed symmetric + - ``f`` -- a polynomial in finitely many variables over the + same base ring as ``self``; it is assumed that this + polynomial is symmetric + - ``check`` -- boolean (default: ``True``); checks whether + the polynomial is indeed symmetric OUTPUT: - - This function converts a symmetric polynomial `f` in a polynomial ring in finitely - many variables to a symmetric function in the monomial - basis of the ring of symmetric functions over the same base ring. + - This function converts a symmetric polynomial `f` in a + polynomial ring in finitely many variables to a symmetric + function in the monomial basis of the ring of symmetric + functions over the same base ring. EXAMPLES:: @@ -173,12 +177,13 @@ def from_polynomial(self, f, check=True): sage: f = (2*m[2,1]+m[1,1]+3*m[3]).expand(3) sage: m.from_polynomial(f) m[1, 1] + 2*m[2, 1] + 3*m[3] + """ assert self.base_ring() == f.base_ring() if check and not f.is_symmetric(): raise ValueError("%s is not a symmetric polynomial" % f) out = self._from_dict({_Partitions.element_class(_Partitions, list(e)): c - for (e,c) in f.dict().items() + for e, c in f.monomial_coefficients().items() if all(e[i+1] <= e[i] for i in range(len(e)-1))}, remove_zeros=False) return out diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 602e366d0f8..c496b8f2e09 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -6903,7 +6903,7 @@ def _from_polynomial(p, f): n = p.parent().ngens() if n == 1: d = {_Partitions.from_exp([e]): c - for e, c in p.dict().items()} + for e, c in p.monomial_coefficients().items()} else: d = {_Partitions.from_exp(e): c for e, c in p.iterator_exp_coeff(False)} diff --git a/src/sage/combinat/shard_order.py b/src/sage/combinat/shard_order.py index 4bcc75d0871..f9081ef77dc 100644 --- a/src/sage/combinat/shard_order.py +++ b/src/sage/combinat/shard_order.py @@ -127,7 +127,7 @@ def __le__(self, other): sage: e1 <= e0 False """ - if type(self) is not type(other) or len(self) != len(other): + if not isinstance(other, ShardPosetElement) or len(self) != len(other): raise TypeError("these are not comparable") if self.runs == other.runs: return True diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index ebd457d7487..c8248bdc5f2 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -381,7 +381,7 @@ def dual(self): A = self._poly.parent() dict_dual = {(n - dy, n - dx): coeff - for (dx, dy), coeff in self._poly.dict().items()} + for (dx, dy), coeff in self._poly.monomial_coefficients().items()} return M_triangle(A(dict_dual), variables=(x, y)) def transmute(self): @@ -484,7 +484,7 @@ def transpose(self): A = self._poly.parent() dict_dual = {(n - dy, n - dx): coeff - for (dx, dy), coeff in self._poly.dict().items()} + for (dx, dy), coeff in self._poly.monomial_coefficients().items()} return H_triangle(A(dict_dual), variables=(x, y)) def m(self): diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 1b8555f4565..ef77bf66d4e 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -844,15 +844,15 @@ Partial orders on a set of `8` elements, up to isomorphism:: - sage: C = Posets(8); C - Posets containing 8 elements + sage: C = Posets(7); C + Posets containing 7 elements sage: C.cardinality() - 16999 + 2045 :: sage: C.unrank(20).plot() - Graphics object consisting of 20 graphics primitives + Graphics object consisting of ... graphics primitives .. image:: ../../media/a_poset.png diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index ef8d1f7f1c5..9e5ec47a40c 100644 --- a/src/sage/databases/cubic_hecke_db.py +++ b/src/sage/databases/cubic_hecke_db.py @@ -117,7 +117,9 @@ def simplify(mat): d = mat.dict() if isinstance(B, CubicHeckeExtensionRing): # Laurent polynomial cannot be reconstructed from string - res = {k: {tuple(j): u.dict() for j, u in v.dict().items()} for k, v in d.items()} + res = {k: {tuple(j): u.monomial_coefficients() + for j, u in v.monomial_coefficients().items()} + for k, v in d.items()} else: res = {k: str(v) for k, v in d.items()} return res diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 89cd74f457e..c3a886645ac 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -4002,16 +4002,17 @@ def _finite_lattices(n): TESTS:: sage: from sage.databases.findstat import _finite_lattices - sage: [L.cover_relations() for L in _finite_lattices(4)] - [[['bottom', 0], ['bottom', 1], [0, 'top'], [1, 'top']], - [['bottom', 0], [0, 1], [1, 'top']]] + sage: sorted((L.cover_relations() for L in _finite_lattices(4)), + ....: key=len) + [[['bottom', 0], [0, 1], [1, 'top']], + [['bottom', 0], ['bottom', 1], [0, 'top'], [1, 'top']]] """ if n <= 2: for P in Posets(n): if P.is_lattice(): yield LatticePoset(P) else: - for P in Posets(n-2): + for P in Posets(n - 2): Q = P.with_bounds() if Q.is_lattice(): yield LatticePoset(Q) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py b/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py index f6ffb67d832..c8528c4821f 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py @@ -590,7 +590,7 @@ def BM_all_minimal(vp, return_transformation=False, D=None): for M in all_M: new_map = mp.conjugate(M) new_map.normalize_coordinates() - if not [new_map, M] in all_maps: + if [new_map, M] not in all_maps: all_maps.append([new_map, M]) #Split into conjugacy classes diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 262c063a35d..a74efd9129a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -2935,7 +2935,7 @@ def parallel_function(morphism): # Calling possible_periods for each prime in parallel parallel_data = [] for q in primes(primebound[0], primebound[1] + 1): - if not (q in badprimes): + if q not in badprimes: F = self.change_ring(GF(q)) parallel_data.append(((F,), {})) @@ -6768,7 +6768,7 @@ def is_Lattes(self): (crit_set, post_crit_set) = crit, list(post_crit) # All Lattes maps have 3 or 4 post critical values - if not len(post_crit_set) in [3, 4]: + if len(post_crit_set) not in [3, 4]: return False f = F_crit.dehomogenize(1)[0] @@ -7228,7 +7228,7 @@ def lift_to_rational_periodic(self, points_modp, B=None): while not done and k <= n: newP = self(newP) if newP == P: - if not ([P, k] in good_points): + if [P, k] not in good_points: good_points.append([newP, k]) done = True k += 1 @@ -7499,7 +7499,7 @@ def all_periodic_points(self, **kwds): pos_points = [] # check period, remove duplicates for i in range(len(all_points)): - if all_points[i][1] in periods and not (all_points[i] in pos_points): + if all_points[i][1] in periods and all_points[i] not in pos_points: pos_points.append(all_points[i]) periodic_points = DS.lift_to_rational_periodic(pos_points,B) for p,n in periodic_points: @@ -7594,7 +7594,7 @@ def all_rational_preimages(self, points): P = points.pop() preimages = self.rational_preimages(P) for i in range(len(preimages)): - if not preimages[i] in preperiodic: + if preimages[i] not in preperiodic: points.append(preimages[i]) preperiodic.add(preimages[i]) return list(preperiodic) @@ -9073,7 +9073,7 @@ def is_newton(self, return_conjugation=False): """ if self.degree() == 1: raise NotImplementedError("degree one Newton maps are trivial") - if not self.base_ring() in NumberFields(): + if self.base_ring() not in NumberFields(): raise NotImplementedError("only implemented over number fields") # check if Newton map sigma_1 = self.sigma_invariants(1) diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 2eced09d619..1cd132b3a45 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1228,7 +1228,7 @@ def Bessel(*args, **kwds): _type = kwds['typ'] else: _type = 'J' - if not (_type in ['I', 'J', 'K', 'Y']): + if _type not in ['I', 'J', 'K', 'Y']: raise ValueError("type must be one of I, J, K, Y") # return the function diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 78f4c6bc712..4bf17b6c049 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -2343,10 +2343,10 @@ class Func_ultraspherical(GinacFunction): sage: # needs mpmath sage: from mpmath import gegenbauer as gegenbauer_mp sage: from mpmath import mp - sage: mp.pretty = True; mp.dps=25 - sage: gegenbauer_mp(-7,0.5,0.3) + sage: print(gegenbauer_mp(-7,0.5,0.3)) 0.1291811875 - sage: gegenbauer_mp(2+3j, -0.75, -1000j) + sage: with mp.workdps(25): + ....: print(gegenbauer_mp(2+3j, -0.75, -1000j)) (-5038991.358609026523401901 + 9414549.285447104177860806j) TESTS: diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 4e0274f810e..d7370a5cd30 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -274,7 +274,7 @@ def simplify(ex): OUTPUT: - A piecewise function whose operands are not piecewiese if + A piecewise function whose operands are not piecewise if possible, that is, as long as the piecewise variable is the same. EXAMPLES:: @@ -602,6 +602,8 @@ def unextend_zero(self, parameters, variable): """ result = [(domain, func) for domain, func in parameters if func != 0] + if len(result) == len(self): + return self return piecewise(result, var=variable) def pieces(self, parameters, variable): @@ -980,6 +982,17 @@ def convolution(self, parameters, variable, other): x|-->-x + 6 on (3, 4], x|-->-2*x + 10 on (4, 5]; x) + Some unbounded but convergent cases now work:: + + sage: p = piecewise([[(2,oo),exp(-x)]]) + sage: q = piecewise([[[2,3],x]]) + sage: p.convolution(q) + piecewise(x|-->(x - 3)*e^(-2) - e^(-x + 2) on (4, 5]; x) + sage: q.convolution(p) + piecewise(x|-->(x - 3)*e^(-2) - e^(-x + 2) on (4, 5]; x) + + TESTS: + Check that the bugs raised in :issue:`12123` are fixed:: sage: f = piecewise([[(-2, 2), 2]]) @@ -1003,45 +1016,56 @@ def convolution(self, parameters, variable, other): fd, f0 = parameters[0] gd, g0 = next(other.items()) if len(f) == 1 == len(g): - f = f.unextend_zero() - g = g.unextend_zero() a1 = fd[0].lower() a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() - with SR.temp_var() as tt: - with SR.temp_var() as uu: - i1 = f0.subs({variable: uu}) - i2 = g0.subs({variable: tt-uu}) - fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs({tt: variable}) - fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs({tt: variable}) - fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs({tt: variable}) - fg4 = definite_integral(i1*i2, uu, a1, a2).subs({tt: variable}) - if a1-b1 < a2-b2: - if a2+b1 != a1+b2: - h = piecewise([[(a1+b1, a1+b2), fg1], - [(a1+b2, a2+b1), fg2], - [(a2+b1, a2+b2), fg3]]) - else: - h = piecewise([[(a1+b1, a1+b2), fg1], - [(a1+b2, a2+b2), fg3]]) + a1b1 = a1 + b1 + a2b2 = a2 + b2 + delta_a = a2 - a1 + delta_b = b2 - b1 + + # this fails in some unbounded cases: + a1b2 = a1 + b2 + a2b1 = a2 + b1 + + todo = [] + if delta_a > delta_b: + if a1b2 is not minus_infinity: + todo.append((a1b1, a1b2, a1, variable - b1)) + todo.append((a1b2, a2b1, variable - b2, variable - b1)) + if a2b1 is not infinity: + todo.append((a2b1, a2b2, variable - b2, a2)) + elif delta_a < delta_b: + if a2b1 is not minus_infinity: + todo.append((a1b1, a2b1, a1, variable - b1)) + todo.append((a2b1, a1b2, a1, a2)) + if a1b2 is not infinity: + todo.append((a1b2, a2b2, variable - b2, a2)) else: - if a1+b2 != a2+b1: - h = piecewise([[(a1+b1, a2+b1), fg1], - [(a2+b1, a1+b2), fg4], - [(a1+b2, a2+b2), fg3]]) - else: - h = piecewise([[(a1+b1, a2+b1), fg1], - [(a2+b1, a2+b2), fg3]]) - return (piecewise([[(minus_infinity, infinity), 0]]).piecewise_add(h)).unextend_zero() - - if len(f) > 1 or len(g) > 1: - z = piecewise([[(0, 0), 0]]) - for fpiece in f.pieces(): - for gpiece in g.pieces(): - h = gpiece.convolution(fpiece) - z = z.piecewise_add(h) - return z.unextend_zero() + if a2b1 is not minus_infinity: + todo.append((a1b1, a2b1, a1, variable - b1)) + todo.append((a2b1, a2b2, variable - b2, a2)) + + if not todo: + raise ValueError("no domain of integration") + + with SR.temp_var() as uu: + i1 = f0.subs({variable: uu}) + i2 = g0.subs({variable: variable - uu}) + expr = i1 * i2 + h = piecewise([[(start, stop), + definite_integral(expr, uu, mini, maxi)] + for start, stop, mini, maxi in todo]) + flat_zero = piecewise([[(minus_infinity, infinity), 0]]) + return (flat_zero.piecewise_add(h)).unextend_zero() # why ? + + z = piecewise([[(0, 0), 0]]) + for fpiece in f.pieces(): + for gpiece in g.pieces(): + h = gpiece.convolution(fpiece) + z = z.piecewise_add(h) + return z.unextend_zero() def trapezoid(self, parameters, variable, N): """ diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index 5d3e2785900..6a8b5e178e5 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -466,15 +466,15 @@ def find_hexad3(self, pts, x0, x1): L = set(pts) H = {x0, x1} for i in range(18): - if (not (MINIMOG[0][2] in H) and L <= picture_set(self.picture21, self.square[i])): + if (MINIMOG[0][2] not in H and L <= picture_set(self.picture21, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[0][2])] H = H | picture_set(self.picture21, self.square[i]) return list(H), WHAT - if (not (MINIMOG[2][1] in H) and L <= picture_set(self.picture02, self.square[i])): + if (MINIMOG[2][1] not in H and L <= picture_set(self.picture02, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[2][1])] H = H | picture_set(self.picture02, self.square[i]) return list(H), WHAT - if (not (MINIMOG[0][0] in H) and L <= picture_set(self.picture00, self.square[i])): + if (MINIMOG[0][0] not in H and L <= picture_set(self.picture00, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[0][0])] H = H | picture_set(self.picture00, self.square[i]) return list(H), WHAT diff --git a/src/sage/geometry/hyperplane_arrangement/plot.py b/src/sage/geometry/hyperplane_arrangement/plot.py index 190bef9982f..7e550bfd472 100644 --- a/src/sage/geometry/hyperplane_arrangement/plot.py +++ b/src/sage/geometry/hyperplane_arrangement/plot.py @@ -212,14 +212,14 @@ def plot(hyperplane_arrangement, **kwds): if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') - if not type(ranges) in [list,tuple]: # ranges is a single number + if type(ranges) not in [list,tuple]: # ranges is a single number ranges = [ranges] * N # So ranges is some type of list. elif dim == 2: # arrangement of lines in the plane - if not type(ranges[0]) in [list,tuple]: # a single interval + if type(ranges[0]) not in [list,tuple]: # a single interval ranges = [ranges] * N elif dim == 3: # arrangement of planes in 3-space - if not type(ranges[0][0]) in [list,tuple]: + if type(ranges[0][0]) not in [list,tuple]: ranges = [ranges] * N elif dim not in [2,3]: # ranges is not an option unless dim is 2 or 3 ranges_set = False diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index d683955b53a..21a463f1234 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -2452,7 +2452,7 @@ def _test_lawrence(self, tester=None, **options): if tester is None: tester = self._tester(**options) - if self.backend() == 'normaliz' and not self.base_ring() in (ZZ, QQ): + if self.backend() == 'normaliz' and self.base_ring() not in (ZZ, QQ): # Speeds up the doctest for significantly. self = self.change_ring(self._internal_base_ring) diff --git a/src/sage/geometry/polyhedron/generating_function.py b/src/sage/geometry/polyhedron/generating_function.py index 16200bf6616..c4bece2af55 100644 --- a/src/sage/geometry/polyhedron/generating_function.py +++ b/src/sage/geometry/polyhedron/generating_function.py @@ -680,7 +680,7 @@ def __generating_function_of_integral_points__( if sort_factors: def key(t): - D = t.dict().popitem()[0] + D = t.monomial_coefficients().popitem()[0] return (-sum(abs(d) for d in D), D) terms = sorted(terms, key=key, reverse=True) return Factorization([(numerator, 1)] + @@ -748,7 +748,7 @@ def _generating_function_via_Omega_(inequalities, B, skip_indices=()): logger.debug('terms denominator %s', terms) def decode_factor(factor): - D = factor.dict() + D = factor.monomial_coefficients() assert len(D) == 1 exponent, coefficient = next(iter(D.items())) return coefficient, exponent @@ -764,7 +764,7 @@ def decode_factor(factor): lambda factor: factor[1] == 0) other_factors = tuple(factor[0] for factor in other_factors) numerator, factors_denominator = \ - _Omega_(numerator.dict(), tuple(decoded_factors)) + _Omega_(numerator.monomial_coefficients(), tuple(decoded_factors)) terms = other_factors + factors_denominator return _simplify_(numerator, terms) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 19b73495394..05138c68f49 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -26,6 +26,7 @@ Here is what the module can do: :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. :meth:`is_edge_cut` | Check whether the input edges form an edge cut. :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex. + :meth:`is_vertex_cut` | Check whether the input vertices form a vertex cut. :meth:`edge_connectivity` | Return the edge connectivity of the graph. :meth:`vertex_connectivity` | Return the vertex connectivity of the graph. @@ -971,58 +972,64 @@ def is_cut_edge(G, u, v=None, label=None): return sol -def is_cut_vertex(G, u, weak=False): +def is_vertex_cut(G, cut, weak=False): r""" - Check whether the input vertex is a cut-vertex. + Check whether the input vertices form a vertex cut. - A vertex is a cut-vertex if its removal from the (di)graph increases the - number of (strongly) connected components. Isolated vertices or leafs are - not cut-vertices. This function works with simple graphs as well as graphs - with loops and multiple edges. + A set of vertices is a vertex cut if its removal from the (di)graph + increases the number of (strongly) connected components. This function works + with simple graphs as well as graphs with loops and multiple edges. INPUT: - ``G`` -- a Sage (Di)Graph - - ``u`` -- a vertex + - ``cut`` -- a set of vertices - ``weak`` -- boolean (default: ``False``); whether the connectivity of directed graphs is to be taken in the weak sense, that is ignoring edges orientations - OUTPUT: - - Return ``True`` if ``u`` is a cut-vertex, and ``False`` otherwise. - EXAMPLES: - Giving a LollipopGraph(4,2), that is a complete graph with 4 vertices with a - pending edge:: + Giving a cycle graph of order 4:: - sage: from sage.graphs.connectivity import is_cut_vertex - sage: G = graphs.LollipopGraph(4, 2) - sage: is_cut_vertex(G, 0) + sage: from sage.graphs.connectivity import is_vertex_cut + sage: G = graphs.CycleGraph(4) + sage: is_vertex_cut(G, [0, 1]) False - sage: is_cut_vertex(G, 3) + sage: is_vertex_cut(G, [0, 2]) True - sage: G.is_cut_vertex(3) + + Giving a disconnected graph:: + + sage: from sage.graphs.connectivity import is_vertex_cut + sage: G = graphs.CycleGraph(4) * 2 + sage: G.connected_components() + [[0, 1, 2, 3], [4, 5, 6, 7]] + sage: is_vertex_cut(G, [0, 2]) + True + sage: is_vertex_cut(G, [4, 6]) + True + sage: is_vertex_cut(G, [0, 6]) + False + sage: is_vertex_cut(G, [0, 4, 6]) True Comparing the weak and strong connectivity of a digraph:: - sage: from sage.graphs.connectivity import is_strongly_connected sage: D = digraphs.Circuit(6) - sage: is_strongly_connected(D) + sage: D.is_strongly_connected() True - sage: is_cut_vertex(D, 2) + sage: is_vertex_cut(D, [2]) True - sage: is_cut_vertex(D, 2, weak=True) + sage: is_vertex_cut(D, [2], weak=True) False Giving a vertex that is not in the graph:: sage: G = graphs.CompleteGraph(4) - sage: is_cut_vertex(G, 7) + sage: is_vertex_cut(G, [7]) Traceback (most recent call last): ... ValueError: vertex (7) is not a vertex of the graph @@ -1031,7 +1038,7 @@ def is_cut_vertex(G, u, weak=False): If ``G`` is not a Sage graph, an error is raised:: - sage: is_cut_vertex('I am not a graph', 0) + sage: is_vertex_cut('I am not a graph', [0]) Traceback (most recent call last): ... TypeError: the input must be a Sage graph @@ -1040,68 +1047,144 @@ def is_cut_vertex(G, u, weak=False): if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") - if u not in G: - raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(u))) - - # Initialization - cdef set CC - cdef list neighbors_func - if not G.is_directed() or weak: - # Weak connectivity + cdef set cutset = set(cut) + for u in cutset: + if u not in G: + raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(u))) - if G.degree(u) < 2: - # An isolated or a leaf vertex is not a cut vertex - return False - - neighbors_func = [G.neighbor_iterator] - start = next(G.neighbor_iterator(u)) - CC = set(G) + if len(cutset) >= G.order() - 1: + # A vertex cut must be of size at most n - 2 + return False + # We deal with graphs with multiple (strongly) connected components + cdef list CC + if G.is_directed() and not weak: + CC = G.strongly_connected_components() else: - # Strong connectivity for digraphs - - if not G.out_degree(u) or not G.in_degree(u): - # A vertex without in or out neighbors is not a cut vertex - return False + CC = G.connected_components(sort=False) + if len(CC) > 1: + for comp in CC: + subcut = cutset.intersection(comp) + if subcut and is_vertex_cut(G.subgraph(comp), subcut, weak=weak): + return True + return False - # We consider only the strongly connected component containing u - CC = set(strongly_connected_component_containing_vertex(G, u)) + cdef list boundary = G.vertex_boundary(cutset) + if not boundary: + # We need at least 1 vertex in the boundary of the cut + return False - # We perform two DFS starting from an out neighbor of u and avoiding - # u. The first DFS follows the edges directions, and the second is - # in the reverse order. If both allow to reach all neighbors of u, - # then u is not a cut vertex - neighbors_func = [G.neighbor_out_iterator, G.neighbor_in_iterator] - start = next(G.neighbor_out_iterator(u)) + cdef list cases = [(G.neighbor_iterator, boundary)] + if not weak and G.is_directed(): + # Strong connectivity for digraphs. + # We perform two DFS starting from an out neighbor of cut and avoiding + # cut. The first DFS follows the edges directions, and the second is + # in the reverse order. If both allow to reach all neighbors of cut, + # then it is not a vertex cut. + # We set data for the reverse order + in_boundary = set() + for u in cutset: + in_boundary.update(G.neighbor_in_iterator(u)) + in_boundary.difference_update(cutset) + if not in_boundary: + return False + cases.append((G.neighbor_in_iterator, list(in_boundary))) - CC.discard(u) - CC.discard(start) cdef list queue cdef set seen cdef set targets + start = boundary[0] - for neighbors in neighbors_func: + for neighbors, this_boundary in cases: - # We perform a DFS starting from a neighbor of u and avoiding u + # We perform a DFS starting from start and avoiding cut queue = [start] - seen = set(queue) - targets = CC.intersection(G.neighbor_iterator(u)) + seen = set(cutset) + seen.add(start) + targets = set(this_boundary) targets.discard(start) - while queue and targets: + while queue: v = queue.pop() for w in neighbors(v): - if w not in seen and w in CC: + if w not in seen: seen.add(w) queue.append(w) targets.discard(w) - # If some neighbors cannot be reached, u is a cut vertex. + # If some neighbors cannot be reached, we have a vertex cut if targets: return True return False +def is_cut_vertex(G, u, weak=False): + r""" + Check whether the input vertex is a cut-vertex. + + A vertex is a cut-vertex if its removal from the (di)graph increases the + number of (strongly) connected components. Isolated vertices or leaves are + not cut-vertices. This function works with simple graphs as well as graphs + with loops and multiple edges. + + INPUT: + + - ``G`` -- a Sage (Di)Graph + + - ``u`` -- a vertex + + - ``weak`` -- boolean (default: ``False``); whether the connectivity of + directed graphs is to be taken in the weak sense, that is ignoring edges + orientations + + OUTPUT: + + Return ``True`` if ``u`` is a cut-vertex, and ``False`` otherwise. + + EXAMPLES: + + Giving a LollipopGraph(4,2), that is a complete graph with 4 vertices with a + pending edge:: + + sage: from sage.graphs.connectivity import is_cut_vertex + sage: G = graphs.LollipopGraph(4, 2) + sage: is_cut_vertex(G, 0) + False + sage: is_cut_vertex(G, 3) + True + sage: G.is_cut_vertex(3) + True + + Comparing the weak and strong connectivity of a digraph:: + + sage: D = digraphs.Circuit(6) + sage: D.is_strongly_connected() + True + sage: is_cut_vertex(D, 2) + True + sage: is_cut_vertex(D, 2, weak=True) + False + + Giving a vertex that is not in the graph:: + + sage: G = graphs.CompleteGraph(4) + sage: is_cut_vertex(G, 7) + Traceback (most recent call last): + ... + ValueError: vertex (7) is not a vertex of the graph + + TESTS: + + If ``G`` is not a Sage graph, an error is raised:: + + sage: is_cut_vertex('I am not a graph', 0) + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + """ + return is_vertex_cut(G, [u], weak=weak) + + def edge_connectivity(G, value_only=True, implementation=None, diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index ed0c5c82c81..b374392163e 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -1657,7 +1657,7 @@ def RandomDirectedGNM(self, n, m, loops=False): if is_dense: for u in range(n): for v in range(n): - if ((u != v) or loops) and (not (v in adj[u])): + if ((u != v) or loops) and (v not in adj[u]): D.add_edge(u, v) return D diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index f5dbad17169..8cd60e5862e 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1107,7 +1107,7 @@ def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): - if not len(HO.intersection(L)) in [0, 2]: + if len(HO.intersection(L)) not in [0, 2]: raise RuntimeError("incorrect hyperoval") L = [[y for y in z if y not in HO] diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c55dfc08f92..3ae14de7877 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -245,7 +245,8 @@ :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. :meth:`~GenericGraph.`is_edge_cut` | Check whether the input edges form an edge cut. - :meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex. + :meth:`~GenericGraph.is_cut_vertex` | Check whether the input vertex is a cut-vertex. + :meth:`~GenericGraph.is_vertex_cut` | Check whether the input vertices form a vertex cut. :meth:`~GenericGraph.edge_cut` | Return a minimum edge cut between vertices `s` and `t` :meth:`~GenericGraph.vertex_cut` | Return a minimum vertex cut between non-adjacent vertices `s` and `t` :meth:`~GenericGraph.flow` | Return a maximum flow in the graph from ``x`` to ``y`` @@ -7512,7 +7513,7 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, sage: g.edge_cut(1, 2, value_only=True, algorithm='LP') # needs sage.numerical.mip 3 - :issue:`12797`:: + Check that :issue:`12797` and :issue:`38713` are fixed:: sage: G = Graph([(0, 3, 1), (0, 4, 1), (1, 2, 1), (2, 3, 1), (2, 4, 1)]) sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True) @@ -7521,7 +7522,7 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True) [1, [(2, 1, 1)]] sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True, algorithm='LP') # needs sage.numerical.mip - (1, [(2, 1)]) + (1, [(2, 1, 1)]) """ self._scream_if_not_simple(allow_loops=True) if vertices: @@ -7574,9 +7575,10 @@ def weight(x): # frozensets otherwise if g.is_directed(): def good_edge(e): - return e + return (e[0], e[1]) else: - good_edge = frozenset + def good_edge(e): + return frozenset((e[0], e[1])) # Some vertices belong to part 1, others to part 0 p.add_constraint(v[s], min=0, max=0) @@ -7589,7 +7591,7 @@ def good_edge(e): # Adjacent vertices can belong to different parts only if the # edge that connects them is part of the cut - for x, y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=False): p.add_constraint(v[x] + b[good_edge((x, y))] - v[y], min=0) else: @@ -7597,7 +7599,7 @@ def good_edge(e): p.set_objective(p.sum(weight(w) * b[good_edge((x, y))] for x, y, w in g.edge_iterator())) # Adjacent vertices can belong to different parts only if the # edge that connects them is part of the cut - for x, y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=False): p.add_constraint(v[x] + b[good_edge((x, y))] - v[y], min=0) p.add_constraint(v[y] + b[good_edge((x, y))] - v[x], min=0) @@ -7612,7 +7614,7 @@ def good_edge(e): return obj answer = [obj] - answer.append([e for e in g.edge_iterator(labels=False) if b[good_edge(e)]]) + answer.append([e for e in g.edge_iterator(labels=True) if b[good_edge(e)]]) if vertices: v = p.get_values(v, convert=bool, tolerance=integrality_tolerance) @@ -18926,7 +18928,7 @@ def breadth_first_search(self, start, ignore_direction=False, # Non-existing start vertex is detected later if distance > 0. if not distance: for v in queue: - if not v[0] in self: + if v[0] not in self: raise LookupError("start vertex ({0}) is not a vertex of the graph".format(v[0])) for v, d in queue: @@ -22676,7 +22678,7 @@ def graphviz_string(self, **options): for f in edge_option_functions: edge_options.update(f((u, v, label))) - if not edge_options['edge_string'] in ['--', '->']: + if edge_options['edge_string'] not in ['--', '->']: raise ValueError("edge_string(='{}') in edge_options dict for " "the edge ({}, {}) should be '--' or '->'" .format(edge_options['edge_string'], u, v)) @@ -25003,6 +25005,7 @@ def is_self_complementary(self): from sage.graphs.connectivity import is_cut_edge from sage.graphs.connectivity import is_edge_cut from sage.graphs.connectivity import is_cut_vertex + from sage.graphs.connectivity import is_vertex_cut from sage.graphs.connectivity import edge_connectivity from sage.graphs.connectivity import vertex_connectivity from sage.graphs.distances_all_pairs import szeged_index diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 976b629d49a..711ea03afc3 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -4122,6 +4122,100 @@ def asc(sigma): ret += M.term(sigma.to_composition(), t**asc(sigma)) return ret + @doc_index("Coloring") + def tutte_symmetric_function(self, R=None, t=None): + r""" + Return the Tutte symmetric function of ``self``. + + Let `G` be a graph. The Tutte symmetric function `XB_G` of the graph + `G` was introduced in [Sta1998]_. We present the equivalent definition + given in [CS2022]_. + + .. MATH:: + + XB_G = \sum_{\pi \vdash V} (1+t)^{e(\pi)} \tilde{m}_{\lambda(\pi)}, + + where the sum ranges over all set-partitions `\pi` of the vertex set + `V`, `\lambda(\pi)` is the partition determined by the sizes of the + blocks of `\pi`, and `e(\pi)` is the number of edges whose endpoints + lie in the same block of `\pi`. In particular, the coefficients of + `XB_G` when expanded in terms of augmented monomial symmetric functions + are polynomials in `t` with non-negative integer coefficients. + + For an integer partition `\lambda = 1^{r_1}2^{r_2}\cdots` expressed in + the exponential notation, the augmented monomial symmetric function + is defined as + + .. MATH:: + + \tilde{m}_{\lambda} = \left(\prod_{i} r_i! \right) m_{\lambda}. + + INPUT: + + - ``R`` -- (default: the parent of ``t``) the base ring for the symmetric + functions + + - ``t`` -- (default: `t` in `\ZZ[t]`) the parameter `t` + + EXAMPLES:: + + sage: p = SymmetricFunctions(ZZ).p() # needs sage.combinat sage.modules + sage: G = Graph([[1,2],[2,3],[3,4],[4,1],[1,3]]) + sage: XB_G = G.tutte_symmetric_function(); XB_G # needs sage.combinat sage.modules + 24*m[1, 1, 1, 1] + (10*t+12)*m[2, 1, 1] + (4*t^2+10*t+6)*m[2, 2] + + (2*t^3+8*t^2+10*t+4)*m[3, 1] + + (t^5+5*t^4+10*t^3+10*t^2+5*t+1)*m[4] + sage: p(XB_G) # needs sage.combinat sage.modules + p[1, 1, 1, 1] + 5*t*p[2, 1, 1] + 2*t^2*p[2, 2] + + (2*t^3+8*t^2)*p[3, 1] + (t^5+5*t^4+8*t^3)*p[4] + + Graphs are allowed to have multiedges and loops:: + + sage: G = Graph([[1,2],[2,3],[2,3]], multiedges = True) + sage: XB_G = G.tutte_symmetric_function(); XB_G # needs sage.combinat sage.modules + 6*m[1, 1, 1] + (t^2+3*t+3)*m[2, 1] + (t^3+3*t^2+3*t+1)*m[3] + + We check that at `t = -1`, we recover the usual chromatic symmetric + function:: + + sage: G = Graph([[1,2],[1,2],[2,3],[3,4],[4,5]], multiedges=True) + sage: XB_G = G.tutte_symmetric_function(t=-1); XB_G # needs sage.combinat sage.modules + 120*m[1, 1, 1, 1, 1] + 36*m[2, 1, 1, 1] + 12*m[2, 2, 1] + + 2*m[3, 1, 1] + m[3, 2] + sage: X_G = G.chromatic_symmetric_function(); X_G # needs sage.combinat sage.modules + p[1, 1, 1, 1, 1] - 4*p[2, 1, 1, 1] + 3*p[2, 2, 1] + 3*p[3, 1, 1] + - 2*p[3, 2] - 2*p[4, 1] + p[5] + sage: XB_G == X_G # needs sage.combinat sage.modules + True + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.set_partition import SetPartitions + from sage.misc.misc_c import prod + from collections import Counter + + if t is None: + t = ZZ['t'].gen() + if R is None: + R = t.parent() + m = SymmetricFunctions(R).m() + ret = m.zero() + V = self.vertices() + M = Counter(self.edge_iterator(labels=False)) + fact = [1] + fact.extend(fact[-1] * i for i in range(1, len(V)+1)) + + def mono(pi): + arcs = 0 + for s in pi: + for u in s: + arcs += sum(M[(u, v)] for v in s if self.has_edge(u, v)) + return arcs + + for pi in SetPartitions(V): + pa = pi.to_partition() + ret += prod(fact[i] for i in pa.to_exp()) * m[pa] * (1+t)**mono(pi) + return ret + @doc_index("Algorithmically hard stuff") def has_homomorphism_to(self, H, core=False, solver=None, verbose=0, *, integrality_tolerance=1e-3): @@ -9596,7 +9690,10 @@ def bipartite_double(self, extended=False): from sage.graphs.tutte_polynomial import tutte_polynomial from sage.graphs.lovasz_theta import lovasz_theta from sage.graphs.partial_cube import is_partial_cube - from sage.graphs.orientations import strong_orientations_iterator, random_orientation, acyclic_orientations + from sage.graphs.orientations import orient + from sage.graphs.orientations import strong_orientations_iterator + from sage.graphs.orientations import random_orientation + from sage.graphs.orientations import acyclic_orientations from sage.graphs.connectivity import bridges, cleave, spqr_tree from sage.graphs.connectivity import is_triconnected from sage.graphs.comparability import is_comparability @@ -9646,6 +9743,7 @@ def bipartite_double(self, extended=False): "is_permutation" : "Graph properties", "tutte_polynomial" : "Algorithmically hard stuff", "lovasz_theta" : "Leftovers", + "orient" : "Connectivity, orientations, trees", "strong_orientations_iterator" : "Connectivity, orientations, trees", "random_orientation" : "Connectivity, orientations, trees", "acyclic_orientations" : "Connectivity, orientations, trees", diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py index 4606c56b4f9..f0fb9329002 100644 --- a/src/sage/graphs/graph_latex.py +++ b/src/sage/graphs/graph_latex.py @@ -1170,7 +1170,7 @@ def set_option(self, option_name, option_value=None): raise TypeError('%s option must be a dictionary, not %s' % (name, value)) else: for key, x in value.items(): - if not type(x) in [int, Integer, float, RealLiteral] or not x >= 0.0: + if type(x) not in [int, Integer, float, RealLiteral] or not x >= 0.0: raise ValueError('%s option for %s needs to be a positive number, not %s' % (name, key, x)) elif name in boolean_dicts: if not isinstance(value, dict): @@ -1562,6 +1562,21 @@ def tkz_picture(self): % % \end{tikzpicture} + + For a complicated vertex, a TeX box is used. :: + + sage: B = crystals.Tableaux(['B', 2], shape=[1]) + sage: latex(B) + \begin{tikzpicture} + ... + \newsavebox{\vertex} + \sbox{\vertex}{${\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{1}c}\cline{1-1} + \lr{1}\\\cline{1-1} + \end{array}$} + }$}\Vertex[style={minimum size=1.0cm,draw=cv0,fill=cfv0,text=clv0,shape=circle},LabelOut=false,L=\usebox{\vertex},x=...,y=...]{v0} + ... + \end{tikzpicture} """ # This routine does not handle multiple edges # It will properly handle digraphs where a pair of vertices has an edge @@ -1938,48 +1953,66 @@ def translate(p): s += [str(round(el_color[edge][2], 4)), '}\n'] s += ['%\n'] - # Create each vertex + # Create vertices + v = [] + box = '' + used = False for u in vertex_list: - s += ['\\Vertex['] + t = [r'\Vertex['] # colors, shapes, sizes, labels/placement for 'Custom' style if customized: - s += ['style={'] # begin style list - s += ['minimum size=', str(round(float(scale * v_size[u]), 4)), + t += ['style={'] # begin style list + t += ['minimum size=', str(round(float(scale * v_size[u]), 4)), units, ','] - s += ['draw=', vertex_color_names[u], ','] - s += ['fill=', vertex_fill_color_names[u], ','] + t += ['draw=', vertex_color_names[u], ','] + t += ['fill=', vertex_fill_color_names[u], ','] if vertex_labels: - s += ['text=', vertex_label_color_names[u], ','] + t += ['text=', vertex_label_color_names[u], ','] if v_shape[u] == 'sphere': - s += ['shape=circle,shading=ball,line width=0pt,ball color=', vertex_color_names[u], ','] + t += ['shape=circle,shading=ball,line width=0pt,ball color=', vertex_color_names[u], ','] else: - s += ['shape=', v_shape[u]] - s += ['},'] # end style list + t += ['shape=', v_shape[u]] + t += ['},'] # end style list if vertex_labels: if vl_placement[u] == 'center': - s += ['LabelOut=false,'] + t += ['LabelOut=false,'] else: - s += ['LabelOut=true,'] - s += ['Ldist=', str(round(float(scale * vl_placement[u][0]), 4)), units, ','] - s += ['Lpos=', str(round(float(vl_placement[u][1]), 4)), ','] # degrees, no units + t += ['LabelOut=true,'] + t += ['Ldist=', str(round(float(scale * vl_placement[u][0]), 4)), units, ','] + t += ['Lpos=', str(round(float(vl_placement[u][1]), 4)), ','] # degrees, no units else: - s += ['NoLabel,'] + t += ['NoLabel,'] # vertex label information is available to all pre-built styles # but may be ignored by the style, so not apparent if vertex_labels or not customized: if vertex_labels_math and not (isinstance(u, str) and u[0] == '$' and u[-1] == '$'): - lab = r'\hbox{$%s$}' % latex(u) + ltx = str(latex(u)) + if '\\' in ltx: # complicated case; use \sbox + box = r'\sbox{\vertex}{$' + ltx + '$}' + lab = r'\usebox{\vertex}' + else: + lab = r'\hbox{$%s$}' % ltx else: lab = r'\hbox{%s}' % u - s += ['L=', lab, ','] + t += ['L=', lab, ','] scaled_pos = translate(pos[u]) - s += ['x=', str(round(float(scale * scaled_pos[0]), 4)), units, ','] - s += ['y=', str(round(float(scale * scaled_pos[1]), 4)), units] - s += [']'] - s += ['{', prefix, str(index_of_vertex[u]), '}\n'] + t += ['x=', str(round(float(scale * scaled_pos[0]), 4)), units, ','] + t += ['y=', str(round(float(scale * scaled_pos[1]), 4)), units] + t += [']'] + t += ['{', prefix, str(index_of_vertex[u]), '}\n'] + if box: + v += [box] + t + box = '' + used = True + else: + v += t + if used: + s += [r'\newsavebox{\vertex}' + '\n'] + v + else: + s += v s += ['%\n'] - # Create each edge or loop + # Create edges and loops for e in self._graph.edges(sort=False): edge = (e[0], e[1]) loop = e[0] == e[1] diff --git a/src/sage/graphs/orientations.py b/src/sage/graphs/orientations.py index 2dac79b7146..26dc4df7722 100644 --- a/src/sage/graphs/orientations.py +++ b/src/sage/graphs/orientations.py @@ -12,6 +12,8 @@ :widths: 30, 70 :delim: | + :meth:`orient` | Return an oriented version of `G` according the input function `f`. + :meth:`acyclic_orientations` | Return an iterator over all acyclic orientations of an undirected graph `G`. :meth:`strong_orientations_iterator` | Return an iterator over all strong orientations of a graph `G` :meth:`random_orientation` | Return a random orientation of a graph `G` @@ -28,7 +30,7 @@ # **************************************************************************** # Copyright (C) 2017 Kolja Knauer # 2017 Petru Valicov -# 2017-2023 David Coudert +# 2017-2024 David Coudert # # 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 @@ -41,6 +43,183 @@ from sage.graphs.digraph import DiGraph +def orient(G, f, weighted=None, data_structure=None, sparse=None, + immutable=None, hash_labels=None): + r""" + Return an oriented version of `G` according the input function `f`. + + INPUT: + + - ``G`` -- an undirected graph + + - ``f`` -- a function that inputs an edge and outputs an orientation of this + edge + + - ``weighted`` -- boolean (default: ``None``); weightedness for the oriented + digraph. By default (``None``), the graph and its orientation will behave + the same. + + - ``sparse`` -- boolean (default: ``None``); ``sparse=True`` is an alias for + ``data_structure="sparse"``, and ``sparse=False`` is an alias for + ``data_structure="dense"``. Only used when ``data_structure=None``. + + - ``data_structure`` -- string (default: ``None``); one of ``'sparse'``, + ``'static_sparse'``, or ``'dense'``. See the documentation of + :class:`DiGraph`. + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable digraph. Only used when ``data_structure=None``. + + * ``immutable=None`` (default) means that the graph and its orientation + will behave the same way. + + * ``immutable=True`` is a shortcut for ``data_structure='static_sparse'`` + + * ``immutable=False`` means that the created digraph is mutable. When used + to orient an immutable graph, the data structure used is ``'sparse'`` + unless anything else is specified. + + - ``hash_labels`` -- boolean (default: ``None``); whether to include edge + labels during hashing of the oriented digraph. This parameter defaults to + ``True`` if the graph is weighted. This parameter is ignored when + parameter ``immutable`` is not ``True``. Beware that trying to hash + unhashable labels will raise an error. + + OUTPUT: a :class:`DiGraph` object + + .. NOTE:: + + This method behaves similarly to method + :meth:`~sage.graphs.generic_graph.GenericGraph.copy`. That is, the + returned digraph uses the same data structure by default, unless the + user asks to use another data structure, and the attributes of the input + graph are copied. + + EXAMPLES:: + + sage: G = graphs.CycleGraph(4); G + Cycle graph: Graph on 4 vertices + sage: D = G.orient(lambda e:e if e[0] < e[1] else (e[1], e[0], e[2])); D + Orientation of Cycle graph: Digraph on 4 vertices + sage: sorted(D.edges(labels=False)) + [(0, 1), (0, 3), (1, 2), (2, 3)] + + TESTS: + + We make sure that one can get an immutable orientation by providing the + ``data_structure`` optional argument:: + + sage: def foo(e): + ....: return e if e[0] < e[1] else (e[1], e[0], e[2]) + sage: G = graphs.CycleGraph(4) + sage: D = G.orient(foo, data_structure='static_sparse') + sage: D.is_immutable() + True + sage: D = G.orient(foo, immutable=True) + sage: D.is_immutable() + True + + Bad input:: + + sage: G.orient(foo, data_structure='sparse', sparse=False) + Traceback (most recent call last): + ... + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value + sage: G.orient(foo, data_structure='sparse', immutable=True) + Traceback (most recent call last): + ... + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value + sage: G.orient(foo, immutable=True, sparse=False) + Traceback (most recent call last): + ... + ValueError: there is no dense immutable backend at the moment + + Which backend? :: + + sage: G.orient(foo, data_structure='sparse')._backend + + sage: G.orient(foo, data_structure='dense')._backend + + sage: G.orient(foo, data_structure='static_sparse')._backend + + sage: G.orient(foo, immutable=True)._backend + + sage: G.orient(foo, immutable=True, sparse=True)._backend + + sage: G.orient(foo, immutable=False, sparse=True)._backend + + sage: G.orient(foo, immutable=False, sparse=False)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=True)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=False)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=None)._backend + + sage: H = Graph(data_structure='dense') + sage: H.orient(foo, data_structure=None, immutable=None, sparse=None)._backend + + """ + # Which data structure should be used ? + if data_structure is not None: + # data_structure is already defined so there is nothing left to do + # here. Did the user try to define too much ? + if immutable is not None or sparse is not None: + raise ValueError("you cannot define 'immutable' or 'sparse' " + "when 'data_structure' has a value") + # At this point, data_structure is None. + elif immutable is True: + data_structure = 'static_sparse' + if sparse is False: + raise ValueError("there is no dense immutable backend at the moment") + elif immutable is False: + # If the user requests a mutable digraph and input is immutable, we + # choose the 'sparse' cgraph backend. Unless the user explicitly + # asked for something different. + if G.is_immutable(): + data_structure = 'dense' if sparse is False else 'sparse' + elif sparse is True: + data_structure = "sparse" + elif sparse is False: + data_structure = "dense" + + if data_structure is None: + from sage.graphs.base.dense_graph import DenseGraphBackend + if isinstance(G._backend, DenseGraphBackend): + data_structure = "dense" + else: + data_structure = "sparse" + + if weighted is None: + weighted = G.weighted() + + edges = (f(e) for e in G.edge_iterator()) + D = DiGraph([G, edges], format='vertices_and_edges', + data_structure=data_structure, + loops=G.allows_loops(), + multiedges=G.allows_multiple_edges(), + name=f"Orientation of {G.name()}", + pos=copy(G._pos), weighted=weighted, + hash_labels=hash_labels) + + attributes_to_copy = ('_assoc', '_embedding') + for attr in attributes_to_copy: + if hasattr(G, attr): + copy_attr = {} + old_attr = getattr(G, attr) + if isinstance(old_attr, dict): + for v, value in old_attr.items(): + try: + copy_attr[v] = value.copy() + except AttributeError: + copy_attr[v] = copy(value) + setattr(D, attr, copy_attr) + else: + setattr(D, attr, copy(old_attr)) + + return D + + def acyclic_orientations(G): r""" Return an iterator over all acyclic orientations of an undirected graph `G`. @@ -67,11 +246,12 @@ def acyclic_orientations(G): .. NOTE:: - The function assumes that the input graph is undirected and the edges are unlabelled. + The function assumes that the input graph is undirected and the edges + are unlabelled. EXAMPLES: - To count number acyclic orientations for a graph:: + To count the number of acyclic orientations for a graph:: sage: g = Graph([(0, 3), (0, 4), (3, 4), (1, 3), (1, 2), (2, 3), (2, 4)]) sage: it = g.acyclic_orientations() @@ -80,11 +260,21 @@ def acyclic_orientations(G): Test for arbitrary vertex labels:: - sage: g_str = Graph([('abc', 'def'), ('ghi', 'def'), ('xyz', 'abc'), ('xyz', 'uvw'), ('uvw', 'abc'), ('uvw', 'ghi')]) + sage: g_str = Graph([('abc', 'def'), ('ghi', 'def'), ('xyz', 'abc'), + ....: ('xyz', 'uvw'), ('uvw', 'abc'), ('uvw', 'ghi')]) sage: it = g_str.acyclic_orientations() sage: len(list(it)) 42 + Check that the method returns properly relabeled acyclic digraphs:: + + sage: g = Graph([(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)]) + sage: orientations = set([frozenset(d.edges(labels=false)) for d in g.acyclic_orientations()]) + sage: len(orientations) + 18 + sage: all(d.is_directed_acyclic() for d in g.acyclic_orientations()) + True + TESTS: To count the number of acyclic orientations for a graph with 0 vertices:: @@ -291,8 +481,9 @@ def helper(G, globO, m, k): # Iterate over acyclic orientations and create relabeled graphs for orientation in orientations: - relabeled_graph = DiGraph([(reverse_vertex_labels[u], reverse_vertex_labels[v], label) for (u, v), label in orientation.items()]) - yield relabeled_graph + D = DiGraph([(u, v) if label else (v, u) for (u, v), label in orientation.items()]) + D.relabel(perm=reverse_vertex_labels, inplace=True) + yield D def strong_orientations_iterator(G): @@ -467,7 +658,7 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E): while i < length: u, v = E[i] Dg.delete_edge(u, v) - if not (v in Dg.depth_first_search(u)): + if v not in Dg.depth_first_search(u): # del E[i] in constant time E[i] = E[-1] E.pop() @@ -478,7 +669,7 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E): else: Dg.add_edge(u, v) Dg.delete_edge(v, u) - if not (u in Dg.depth_first_search(v)): + if u not in Dg.depth_first_search(v): # del E[i] in constant time E[i] = E[-1] E.pop() diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 88aa11802de..8945175cf7a 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -888,7 +888,7 @@ def links_gould_polynomial(self, varnames=None, use_symbolics=False): # Since the result of the calculation is known to be a Laurent polynomial # in t0 and t1 all exponents of ltemp must be divisable by 2 L = ltemp.parent() - lred = L({(k[0]/2, k[1]/2): v for k, v in ltemp.dict().items()}) + lred = L({(k[0]/2, k[1]/2): v for k, v in ltemp.monomial_coefficients().items()}) t0, t1 = R.gens() return lred(t0, t1) diff --git a/src/sage/interfaces/magma_free.py b/src/sage/interfaces/magma_free.py index 34d5b70f29a..5938e9fa4af 100644 --- a/src/sage/interfaces/magma_free.py +++ b/src/sage/interfaces/magma_free.py @@ -26,8 +26,10 @@ def magma_free_eval(code, strip=True, columns=0): Use the free online MAGMA calculator to evaluate the given input code and return the answer as a string. - LIMITATIONS: The code must evaluate in at most 20 seconds - and there is a limitation on the amount of RAM. + .. WARNING:: + + The code must evaluate in at most 120 seconds + and there is a limitation on the amount of RAM. EXAMPLES:: diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 1e6f85cfae0..499b2565236 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1657,7 +1657,7 @@ def sr_to_max(expr): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) elif op == tuple: return EclObject(([mlist], list(sr_to_max(op) for op in expr.operands()))) - elif not (op in sage_op_dict): + elif op not in sage_op_dict: # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr # op_max=caar(maxima(expr).ecl()) diff --git a/src/sage/interfaces/tab_completion.py b/src/sage/interfaces/tab_completion.py index fdbe51ffd8e..d0e8bc11309 100644 --- a/src/sage/interfaces/tab_completion.py +++ b/src/sage/interfaces/tab_completion.py @@ -71,7 +71,7 @@ def completions(s, globs): sage: import sage.interfaces.tab_completion as s sage: p = x**2 + 1 sage: s.completions('p.co',globals()) # indirect doctest - ['p.coefficients',...] + ['p.coefficient',...] sage: s.completions('dic',globals()) # indirect doctest ['dickman_rho', 'dict'] diff --git a/src/sage/knots/free_knotinfo_monoid.py b/src/sage/knots/free_knotinfo_monoid.py index c8301a4d138..47429712851 100644 --- a/src/sage/knots/free_knotinfo_monoid.py +++ b/src/sage/knots/free_knotinfo_monoid.py @@ -1,13 +1,15 @@ # sage_setup: distribution = sagemath-graphs r""" -Free monoid genereated by prime knots available via the :class:`~sage.knots.knotinfo.KnotInfoBase` class. +Free monoid generated by prime knots available via the +:class:`~sage.knots.knotinfo.KnotInfoBase` class. -A generator of this free abelian monoid is a prime knot according to the list at -`KnotInfo `__. A fully amphicheiral prime -knot is represented by exactly one generator with the corresponding name. For -non-chiral prime knots, there are additionally one or three generators with the -suffixes ``m``, ``r`` and ``c`` which specify the mirror and reverse images -according to their symmetry type. +A generator of this free abelian monoid is a prime knot according to +the list at `KnotInfo `__. A fully +amphicheiral prime knot is represented by exactly one generator with +the corresponding name. For non-chiral prime knots, there are +additionally one or three generators with the suffixes ``m``, ``r`` +and ``c`` which specify the mirror and reverse images according to +their symmetry type. AUTHORS: @@ -25,7 +27,8 @@ ############################################################################## from sage.knots.knotinfo import SymmetryMutant -from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid, IndexedFreeAbelianMonoidElement +from sage.monoids.indexed_free_monoid import (IndexedFreeAbelianMonoid, + IndexedFreeAbelianMonoidElement) from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation @@ -67,8 +70,10 @@ def as_knot(self): def to_knotinfo(self): r""" - Return a word representing ``self`` as a list of pairs where each pair - ``(ki, sym)`` consists of a :class:`~sage.knots.knotinfo.KontInfoBase` instance ``ki`` and + Return a word representing ``self`` as a list of pairs. + + Each pair ``(ki, sym)`` consists of a + :class:`~sage.knots.knotinfo.KnotInfoBase` instance ``ki`` and :class:`~sage.knots.knotinfo.SymmetryMutant` instance ``sym``. EXAMPLES:: @@ -109,7 +114,8 @@ def __classcall_private__(cls, max_crossing_number=6, prefix=None, **kwds): if not prefix: prefix = 'KnotInfo' # We skip the IndexedMonoid__classcall__ - return UniqueRepresentation.__classcall__(cls, max_crossing_number, prefix=prefix, **kwds) + return UniqueRepresentation.__classcall__(cls, max_crossing_number, + prefix=prefix, **kwds) def __init__(self, max_crossing_number, category=None, prefix=None, **kwds): r""" @@ -405,13 +411,14 @@ def from_knot(self, knot, unique=True): sage: FKIM.from_knot(K) # long time Traceback (most recent call last): ... - NotImplementedError: this (possibly non prime) knot cannot be identified uniquely by KnotInfo + NotImplementedError: this (possibly non prime) knot cannot be + identified uniquely by KnotInfo use keyword argument `unique` to obtain more details sage: FKIM.from_knot(K, unique=False) # long time [KnotInfo['K4_1']*KnotInfo['K5_2'], KnotInfo['K9_12']] """ hp = knot.homfly_polynomial(normalization='vz') - num_summands = sum(e for f, e in hp.factor()) + num_summands = sum(e for _, e in hp.factor()) if num_summands == 1: return knot.get_knotinfo() @@ -420,17 +427,15 @@ def from_knot(self, knot, unique=True): if len(res) == 1: if unique: return res[0] - else: - return [res[0]] # to be consistent with get_knotinfo + return [res[0]] # to be consistent with get_knotinfo k = self._check_elements(knot, res) if k: if unique: return k - else: - return[k] # to be consistent with get_knotinfo + return [k] # to be consistent with get_knotinfo if res and not unique: - return sorted(list(set(res))) + return sorted(set(res)) if unique and len(res) > 1: non_unique_hint = '\nuse keyword argument `unique` to obtain more details' raise NotImplementedError('this (possibly non prime) knot cannot be identified uniquely by KnotInfo%s' % non_unique_hint) @@ -487,7 +492,8 @@ def inject_variables(self, select=None, verbose=True): crn = select if crn > max_crn: self._set_index_dictionary(max_crossing_number=crn) - gen_list += [k for k, v in idx_dict.items() if v[0].crossing_number() == crn] + gen_list += [k for k, v in idx_dict.items() + if v[0].crossing_number() == crn] else: raise TypeError('cannot select generators by %s' % select) else: diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index 6001f7abeff..235ba1fddef 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -2658,7 +2658,7 @@ def __getitem__(self, item): [, , ] """ from sage.rings.integer import Integer - if not type(item) in (int, Integer): + if type(item) not in (int, Integer): raise ValueError('item must be an integer') l = self.list() max_item = len(l) @@ -2701,7 +2701,7 @@ def __call__(self, item): return self[item] from sage.rings.integer import Integer - if not type(item) in (int, Integer): + if type(item) not in (int, Integer): raise ValueError('item must be an integer') l = self.list() max_item = len(l) + 1 diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index dbaca5662c8..0e6158c924b 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -1201,7 +1201,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): else: m = matrix(ring, len(bases[(i,j)]), 0) complexes[i] = m.transpose() - if not (i-1, j) in bases: + if (i-1, j) not in bases: complexes[i-1] = matrix(ring, len(bases[(i,j)]), 0) homologies = ChainComplex(complexes).homology() return tuple(sorted(homologies.items())) @@ -2313,7 +2313,7 @@ def seifert_circles(self): while a not in par: par.append(a) posnext = C[(C.index(a) - 1) % 4] - if tails[posnext] == C and not [posnext] in result: + if tails[posnext] == C and [posnext] not in result: a = posnext else: a = C[(C.index(a) + 1) % 4] @@ -4447,7 +4447,7 @@ def answer_list(l): a = answer(L) if a: ansl.append(a) - return sorted(list(set(ansl))) + return sorted(set(ansl)) if len(set(l)) == 1: return answer(l[0]) diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi index b2450ed190b..b1be0216a2e 100644 --- a/src/sage/libs/flint/nmod_poly_linkage.pxi +++ b/src/sage/libs/flint/nmod_poly_linkage.pxi @@ -577,18 +577,37 @@ cdef inline int celement_gcd(nmod_poly_t res, nmod_poly_t a, nmod_poly_t b, unsi True sage: (G//d)*d == G True + + Check that we catch a case where FLINT fails. These generate the unit + ideal, so their GCD should be 1 (or another unit):: + + sage: R. = Integers(121)[] + sage: f = 11*x^2 + 1 + sage: f - 61*x*f.derivative() + 1 + sage: gcd(f, f.derivative()) + Traceback (most recent call last): + ... + RuntimeError: FLINT gcd calculation failed """ if celement_is_zero(b, n): nmod_poly_set(res, a) return 0 - # A check that the leading coefficients are invertible is *not* sufficient + # FLINT provides no interface for detecting errors here try: sig_on() - nmod_poly_gcd(res, a, b) - sig_off() + try: + nmod_poly_gcd(res, a, b) + finally: + sig_off() except RuntimeError: - raise ValueError("non-invertible elements encountered during GCD") + raise RuntimeError("FLINT gcd calculation failed") + + cdef unsigned long leadcoeff = nmod_poly_get_coeff_ui(res, nmod_poly_degree(res)) + cdef unsigned long modulus = nmod_poly_modulus(res) + if n_gcd(modulus, leadcoeff) == 1: + nmod_poly_make_monic(res, res) cdef inline int celement_xgcd(nmod_poly_t res, nmod_poly_t s, nmod_poly_t t, nmod_poly_t a, nmod_poly_t b, unsigned long n) except -2: """ diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index 24db9c07cb8..c24408f401a 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -2437,31 +2437,22 @@ cdef class GapElement_Function(GapElement): sage: b Sym( [ 1 .. 4 ] ) - sage: sorted(a(b)) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(a(b))] + ["1", "S4", "A4", "C2 x C2"] sage: libgap.eval("a := NormalSubgroups") sage: libgap.eval("b := SymmetricGroup(4)") Sym( [ 1 .. 4 ] ) sage: libgap.collect() - sage: sorted(libgap.eval('a') (libgap.eval('b'))) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(libgap.eval('a') (libgap.eval('b')))] + ["1", "S4", "A4", "C2 x C2"] sage: a = libgap.eval('a') sage: b = libgap.eval('b') sage: libgap.collect() - sage: sorted(a(b)) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(a(b))] + ["1", "S4", "A4", "C2 x C2"] Not every ``GapElement`` is callable:: diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 11ccfc701d2..96111f7b942 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -983,7 +983,6 @@ cdef class Context: TESTS:: sage: from mpmath import mp - sage: mp.pretty = True sage: (x, T) = mp._convert_param(3) sage: (x, type(x).__name__, T) (3, 'int', 'Z') @@ -991,11 +990,11 @@ cdef class Context: sage: (x, type(x).__name__, T) (mpq(5,2), 'mpq', 'Q') sage: (x, T) = mp._convert_param(2.3) - sage: (x, type(x).__name__, T) - (2.3, 'mpf', 'R') + sage: (str(x), type(x).__name__, T) + ('2.3', 'mpf', 'R') sage: (x, T) = mp._convert_param(2+3j) sage: (x, type(x).__name__, T) - ((2.0 + 3.0j), 'mpc', 'C') + (mpc(real='2.0', imag='3.0'), 'mpc', 'C') sage: mp.pretty = False """ cdef MPF v @@ -1056,7 +1055,6 @@ cdef class Context: TESTS:: sage: from mpmath import * - sage: mp.pretty = True sage: mag(10), mag(10.0), mag(mpf(10)), int(ceil(log(10,2))) (4, 4, 4, 4) sage: mag(10j), mag(10+10j) @@ -1064,7 +1062,7 @@ cdef class Context: sage: mag(0.01), int(ceil(log(0.01,2))) (-6, -6) sage: mag(0), mag(inf), mag(-inf), mag(nan) - (-inf, +inf, +inf, nan) + (mpf('-inf'), mpf('+inf'), mpf('+inf'), mpf('nan')) :: @@ -2203,12 +2201,13 @@ cdef class constant(mpf_base): Represent ``self`` as a string. With mp.pretty=False, the representation differs from that of an ordinary mpf:: - sage: from mpmath import mp, pi - sage: mp.pretty = True - sage: repr(pi) + sage: from mpmath import mp + sage: mp2 = mp.clone() + sage: mp2.pretty = True + sage: repr(mp2.pi) '3.14159265358979' - sage: mp.pretty = False - sage: repr(pi) + sage: mp2.pretty = False + sage: repr(mp2.pi) '' """ if global_context.pretty: @@ -2374,11 +2373,12 @@ cdef class mpc(mpnumber): TESTS:: sage: from mpmath import mp - sage: mp.pretty = True - sage: repr(mp.mpc(2,3)) + sage: mp2 = mp.clone() + sage: mp2.pretty = True + sage: repr(mp2.mpc(2,3)) '(2.0 + 3.0j)' - sage: mp.pretty = False - sage: repr(mp.mpc(2,3)) + sage: mp2.pretty = False + sage: repr(mp2.mpc(2,3)) "mpc(real='2.0', imag='3.0')" """ if global_context.pretty: diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index b51c08b8506..9c7b4078583 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1188,8 +1188,8 @@ cdef number *sa2si_transext_QQ(object elem, ring *_ring) noexcept: if nMapFuncPtr is NULL: raise RuntimeError("Failed to determine nMapFuncPtr") - numerdic = elem.numerator().dict() - denomdic = elem.denominator().dict() + numerdic = elem.numerator().monomial_coefficients() + denomdic = elem.denominator().monomial_coefficients() if numerdic and not isinstance(list(numerdic)[0], (tuple, ETuple)): numerdic = {(k,):b for k,b in numerdic.items()} @@ -1303,8 +1303,8 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring) noexcept: if nMapFuncPtr is NULL: raise RuntimeError("Failed to determine nMapFuncPtr") - numerdic = elem.numerator().dict() - denomdic = elem.denominator().dict() + numerdic = elem.numerator().monomial_coefficients() + denomdic = elem.denominator().monomial_coefficients() if numerdic and not isinstance(list(numerdic)[0], (tuple, ETuple)): numerdic = {(k,):b for k,b in numerdic.items()} diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index 3971a85c776..f50ae75f553 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -689,7 +689,7 @@ def __eq__(self, other): diff = xs - xo period = periods[ind] if period is not None: - if not (diff/period in ZZ): + if diff/period not in ZZ: return False else: if isinstance(diff, Expression) and not diff.is_trivial_zero(): diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 2d5c6ae7475..9d8fcd4a341 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -1683,7 +1683,9 @@ def _latex_file_(objects, title='SAGE', debug=False, s = LATEX_HEADER + '\n' + MACROS + s + '\n\\end{document}' if debug: + print('----') print(s) + print('----') return s @@ -1827,7 +1829,6 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, ... ValueError: Unsupported LaTeX engine. """ - if tightpage: if margin is None: margin_str = "" @@ -1870,16 +1871,16 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, tmp.cleanup() return output_file = os.path.join(tmp.name, "sage." + suffix) - # this should get changed if we switch the stuff in misc.viewer to - # producing lists + if debug: - print('viewer: "{}"'.format(viewer)) + print(f'temporary file: "{output_file}"') + print(f'viewer: "{viewer}"') # Return immediately but only clean up the temporary file after # the viewer has closed. This function is synchronous and waits # for the process to complete... def run_viewer(): - run([viewer, output_file], capture_output=True) + run([*viewer.split(), output_file], capture_output=True) tmp.cleanup() # ...but we execute it asynchronously so that view() completes @@ -1890,7 +1891,78 @@ def run_viewer(): t.daemon = True t.start() - return + +def pdf(x, filename, tiny=False, tightpage=True, margin=None, engine=None, debug=False): + """ + Create an image from the latex representation of ``x`` and save it as a pdf + file with the given filename. + + INPUT: + + - ``x`` -- a Sage object + + - ``filename`` -- the filename with which to save the image + + - ``tiny`` -- boolean (default: ``False``); if ``True``, use a tiny font + + - ``tightpage`` -- boolean (default: ``True``); use the LaTeX package + ``preview`` with the 'tightpage' option + + - ``margin`` -- float (default: no margin); width of border, only effective + with 'tight page' + + - ``engine`` -- (default: ``None``) ``'latex'``, ``'pdflatex'``, + ``'xelatex'`` or ``'lualatex'``; if ``None``, the value defined in the + LaTeX global preferences ``latex.engine()`` is used + + - ``debug`` -- boolean (default: ``False``); if ``True``, print verbose output + + EXAMPLES:: + + sage: # optional - latex + sage: from sage.misc.latex import pdf + sage: import tempfile + sage: with tempfile.NamedTemporaryFile(suffix=".pdf") as f: # random + ....: pdf(ZZ[x], f.name) + """ + from sage.plot.graphics import Graphics + if isinstance(x, Graphics): + x.save(filename) + return + + if tightpage: + if margin is None: + margin_str = "" + else: + margin_str = '\n\\setlength\\PreviewBorder{%fmm}' % margin + latex_options = {'extra_preamble': + '\\usepackage[tightpage,active]{preview}\n' + + '\\PreviewEnvironment{page}%s' % margin_str, + 'math_left': '\\begin{page}$', + 'math_right': '$\\end{page}'} + else: + latex_options = {} + + # create a string of latex code to write in a file + s = _latex_file_([x], title='', tiny=tiny, debug=debug, **latex_options) + if engine is None: + engine = _Latex_prefs._option["engine"] + # path name for permanent pdf output + abs_path_to_pdf = os.path.abspath(filename) + # temporary directory to store stuff + with TemporaryDirectory() as tmp: + tex_file = os.path.join(tmp, "sage.tex") + pdf_file = os.path.join(tmp, "sage.pdf") + # write latex string to file + with open(tex_file, 'w') as file: + file.write(s) + # run latex on the file + e = _run_latex_(tex_file, debug=debug, engine=engine) + if e == 'pdf': + # if no errors, copy pdf_file to the appropriate place + shutil.copy(pdf_file, abs_path_to_pdf) + else: + print("Latex error or no pdf was generated.") def png(x, filename, density=150, debug=False, diff --git a/src/sage/misc/viewer.py b/src/sage/misc/viewer.py index 4f62b1c5022..badc70a963a 100644 --- a/src/sage/misc/viewer.py +++ b/src/sage/misc/viewer.py @@ -69,7 +69,7 @@ def default_viewer(viewer=None): elif os.uname()[0] == 'Darwin': # Simple on OS X, since there is an open command that opens # anything, using the user's preferences. - BROWSER = 'open' + BROWSER = 'open -W' DVI_VIEWER = BROWSER PDF_VIEWER = BROWSER PNG_VIEWER = BROWSER diff --git a/src/sage/modular/abvar/constructor.py b/src/sage/modular/abvar/constructor.py index 78e160edfe6..53561063eaa 100644 --- a/src/sage/modular/abvar/constructor.py +++ b/src/sage/modular/abvar/constructor.py @@ -23,6 +23,7 @@ _cache = {} + def _get(key): """ Return the cached abelian variety with given key. This is used @@ -49,6 +50,7 @@ def _get(key): return z raise ValueError("element not in cache") + def _saved(key, J): """ Return the cached abelian variety with given key. This is used @@ -95,6 +97,7 @@ def J0(N): J = Gamma0(N).modular_abelian_variety() return _saved(key, J) + def J1(N): """ Return the Jacobian `J_1(N)` of the modular curve @@ -112,6 +115,7 @@ def J1(N): from sage.modular.arithgroup.all import Gamma1 return _saved(key, Gamma1(N).modular_abelian_variety()) + def JH(N, H): """ Return the Jacobian `J_H(N)` of the modular curve @@ -129,6 +133,7 @@ def JH(N, H): from sage.modular.arithgroup.all import GammaH return _saved(key, GammaH(N, H).modular_abelian_variety()) + def AbelianVariety(X): """ Create the abelian variety corresponding to the given defining diff --git a/src/sage/modular/abvar/cuspidal_subgroup.py b/src/sage/modular/abvar/cuspidal_subgroup.py index c50a2b109f8..7a0921076d9 100644 --- a/src/sage/modular/abvar/cuspidal_subgroup.py +++ b/src/sage/modular/abvar/cuspidal_subgroup.py @@ -59,15 +59,15 @@ True """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2007 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.matrix.constructor import matrix from sage.modular.arithgroup.all import Gamma0_class diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index 347e45fa418..cfaf762eae2 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -88,15 +88,15 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sage.rings.abc diff --git a/src/sage/modular/abvar/torsion_subgroup.py b/src/sage/modular/abvar/torsion_subgroup.py index 516acf5f7fd..84ee1abe795 100644 --- a/src/sage/modular/abvar/torsion_subgroup.py +++ b/src/sage/modular/abvar/torsion_subgroup.py @@ -79,15 +79,15 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import divisors, gcd from sage.misc.misc_c import prod diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index 3431dfcac5c..388cabfeed1 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -124,6 +124,7 @@ Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z) Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z) + def sl2z_word_problem(A): r""" Given an element of `\SL_2(\ZZ)`, express it as a word in the generators L = @@ -151,7 +152,7 @@ def sl2z_word_problem(A): output = [] # If A00 is zero - if A[0,0] == 0: + if A[0, 0] == 0: c = A[1,1] if c != 1: A = A*Lm**(c-1)*Rm*Lmi @@ -160,12 +161,12 @@ def sl2z_word_problem(A): A = A*Rm*Lmi output.extend([(1,-1),(0,1)]) - if A[0,0] < 0: # Make sure A00 is positive + if A[0, 0] < 0: # Make sure A00 is positive A = SL2Z(-1)*A output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)]) if A[0,1] < 0: # if A01 is negative make it positive - n = (-A[0,1]/A[0,0]).ceil() #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0] + n = (-A[0,1]/A[0,0]).ceil() # n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0] A = A*Lm**n output.append((0, -n)) # At this point A00>0 and A01>=0 @@ -180,10 +181,10 @@ def sl2z_word_problem(A): A = A*SL2Z([1,-n,0,1]) output.append((0, n)) - if A == SL2Z(1): + if A == SL2Z.one(): pass # done, so don't add R^0 - elif A[0,0] == 0: - c = A[1,1] + elif A[0, 0] == 0: + c = A[1, 1] if c != 1: A = A*Lm**(c-1)*Rm*Lmi output.extend([(0,1-c),(1,-1),(0, 1)]) @@ -199,6 +200,7 @@ def sl2z_word_problem(A): output.reverse() return output + def eval_sl2z_word(w): r""" Given a word in the format output by :func:`sl2z_word_problem`, convert it back @@ -216,6 +218,7 @@ def eval_sl2z_word(w): w1 = w return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm) + def word_of_perms(w, p1, p2): r""" Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations @@ -260,6 +263,7 @@ def word_of_perms(w, p1, p2): return M + def _equalize_perms(l): r""" Transform a list of lists into a list of lists with identical length. Each @@ -286,6 +290,7 @@ def _equalize_perms(l): # interpreted as L and R. Hence the order of the arguments is slightly # different from the class __init__ methods. + def ArithmeticSubgroup_Permutation( L=None, R=None, S2=None, S3=None, relabel=False, @@ -454,6 +459,7 @@ def ArithmeticSubgroup_Permutation( return G + class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup): r""" A subgroup of `\SL_2(\ZZ)` defined by the action of generators on its @@ -1484,7 +1490,7 @@ def is_congruence(self): r = R**d s = l**20 * r**onefifth * l**(-4) * ~r - #Congruence if the seven permutations below are trivial: + # Congruence if the seven permutations below are trivial: rel = ~a*~r*a*r if not rel.is_one(): verbose("Failed relation B1") @@ -1853,6 +1859,7 @@ def ncusps(self): m += 1 return n + m//2 + class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class): r""" An arithmetic subgroup of `\SL(2, \ZZ)` containing `-1`, represented diff --git a/src/sage/modular/arithgroup/congroup_gamma.py b/src/sage/modular/arithgroup/congroup_gamma.py index 2eec9b267ac..78d878aaf9d 100644 --- a/src/sage/modular/arithgroup/congroup_gamma.py +++ b/src/sage/modular/arithgroup/congroup_gamma.py @@ -2,13 +2,13 @@ Congruence subgroup `\Gamma(N)` """ -#***************************************************************************** +# **************************************************************************** # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import gcd from sage.groups.matrix_gps.finitely_generated import MatrixGroup @@ -26,6 +26,8 @@ _gamma_cache = {} + + def Gamma_constructor(N): r""" Return the congruence subgroup `\Gamma(N)`. diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index a01c1dd1683..ca0d050fe6f 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -8,8 +8,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import kronecker_symbol, divisors, euler_phi, gcd, moebius from sage.misc.cachefunc import cached_method @@ -47,6 +47,8 @@ def is_Gamma0(x): _gamma0_cache = {} + + def Gamma0_constructor(N): """ Return the congruence subgroup Gamma0(N). @@ -143,7 +145,7 @@ def __init__(self, level): # be done if needed by the _generators_for_H and _list_of_elements_in_H # methods. # - #GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()]) + # GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()]) def _repr_(self): """ diff --git a/src/sage/modular/arithgroup/congroup_gamma1.py b/src/sage/modular/arithgroup/congroup_gamma1.py index 18a1c8c0950..c450d98a783 100644 --- a/src/sage/modular/arithgroup/congroup_gamma1.py +++ b/src/sage/modular/arithgroup/congroup_gamma1.py @@ -3,13 +3,13 @@ Congruence subgroup `\Gamma_1(N)` """ -#***************************************************************************** +# **************************************************************************** # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method @@ -44,13 +44,14 @@ def is_Gamma1(x): """ from sage.misc.superseded import deprecation deprecation(38035, "The function is_Gamma1 is deprecated; use 'isinstance(..., Gamma1_class)' instead.") - #from congroup_sl2z import is_SL2Z - #return (isinstance(x, Gamma1_class) or is_SL2Z(x)) + # from congroup_sl2z import is_SL2Z + # return (isinstance(x, Gamma1_class) or is_SL2Z(x)) return isinstance(x, Gamma1_class) _gamma1_cache = {} + def Gamma1_constructor(N): r""" Return the congruence subgroup `\Gamma_1(N)`. @@ -487,7 +488,7 @@ def dimension_cusp_forms(self, k=2, eps=None, algorithm='CohenOesterle'): from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) ) - else: #algorithm not in ["CohenOesterle", "Quer"]: + else: # algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms") def dimension_eis(self, k=2, eps=None, algorithm='CohenOesterle'): @@ -573,7 +574,7 @@ def dimension_eis(self, k=2, eps=None, algorithm='CohenOesterle'): else: return alpha - self.dimension_cusp_forms(k, eps) - else: #algorithm not in ["CohenOesterle", "Quer"]: + else: # algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis") def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm='CohenOesterle'): diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py index 507418bd239..41a09602a88 100644 --- a/src/sage/modular/arithgroup/congroup_generic.py +++ b/src/sage/modular/arithgroup/congroup_generic.py @@ -432,6 +432,7 @@ def image_mod_n(self): """ return self.__G + class CongruenceSubgroup(CongruenceSubgroupFromGroup): r""" One of the "standard" congruence subgroups `\Gamma_0(N)`, `\Gamma_1(N)`, @@ -569,6 +570,7 @@ def _new_group_from_level(self, level): else: raise NotImplementedError + def _minimize_level(G): r""" Utility function. Given a matrix group `G` contained in `SL(2, \ZZ / N\ZZ)` diff --git a/src/sage/modular/arithgroup/congroup_sl2z.py b/src/sage/modular/arithgroup/congroup_sl2z.py index 7e15eaca383..d95c9e18764 100644 --- a/src/sage/modular/arithgroup/congroup_sl2z.py +++ b/src/sage/modular/arithgroup/congroup_sl2z.py @@ -46,6 +46,7 @@ def is_SL2Z(x): deprecation(38035, "The function is_SL2Z is deprecated; use 'isinstance(..., SL2Z_class)' instead.") return isinstance(x, SL2Z_class) + class SL2Z_class(Gamma0_class): r""" The full modular group `\SL_2(\ZZ)`, regarded as a congruence @@ -250,6 +251,7 @@ def random_element(self, bound=100, *args, **kwds): SL2Z = SL2Z_class() + def _SL2Z_ref(): """ Return SL2Z. (Used for pickling SL2Z.). diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index bb48288eec2..c965015b41d 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -279,7 +279,7 @@ cdef class Farey: [ -3 1] [-40 13] """ - gens_dict = {g:i+1 for i,g in enumerate(self.generators())} + gens_dict = {g: i+1 for i, g in enumerate(self.generators())} ans = [] for pm in self.pairing_matrices(): a, b, c, d = pm.matrix().list() @@ -331,7 +331,7 @@ cdef class Farey: sage: (-g.matrix()).is_one() True """ - for i,g in enumerate(self.generators()): + for i, g in enumerate(self.generators()): m = g.matrix() if (-m).is_one(): return [i + 1] @@ -342,15 +342,17 @@ cdef class Farey: return 3 * [i + 1] return [] - def word_problem(self, M, output = 'standard', check = True): + def word_problem(self, M, output='standard', check=True): r""" Solve the word problem (up to sign) using this Farey symbol. INPUT: - ``M`` -- an element `M` of `\SL_2(\ZZ)` - - ``output`` -- (default: ``'standard'``) should be one of ``'standard'``, - ``'syllables'``, ``'gens'``. + + - ``output`` -- (default: ``'standard'``) should be one of + ``'standard'``, ``'syllables'``, ``'gens'``. + - ``check`` -- boolean (default: ``True``); whether to check for correct input and output @@ -359,17 +361,17 @@ cdef class Farey: A solution to the word problem for the matrix `M`. The format depends on the ``output`` parameter, as follows. - - ``standard`` returns the so called the Tietze representation, - consists of a tuple of nonzero integers `i`, where if `i` > 0 - then it indicates the `i`-th generator (that is, ``self.generators()[0]`` - would correspond to `i` = 1), and if `i` < 0 then it indicates - the inverse of the `i`-th generator. - - ``syllables`` returns a tuple of tuples of the form `(i,n)`, where - `(i,n)` represents ``self.generators()[i] ^ n``, + - ``'standard'`` returns the so called Tietze representation, + which consists of a tuple of nonzero integers. A positive + integer `i` indicates the `i`-th generator (that is, + ``self.generators()[i-1]``), while a negative integer `i` + indicates the inverse of the `i`-th generator. + - ``'syllables'`` returns a tuple of tuples of the form + `(i, n)`, where `(i, n)` represents ``self.generators()[i] ^ n``, whose product equals `M` up to sign. - - ``gens`` returns tuple of tuples of the form `(g,n)`, - `(g,n)` such that the product of the matrices `g^n` - equals `M` up to sign. + - ``'gens'`` returns a tuple of pairs `(g, n)`, where `g` is a + matrix and `n` an integer, such that the product of the + matrices `g^n` equals `M` up to sign. EXAMPLES:: @@ -385,7 +387,7 @@ cdef class Farey: sage: g [-5048053 586303] [-5558280 645563] - sage: F.word_problem(g, output = 'gens') + sage: F.word_problem(g, output='gens') (( [109 -10] [120 -11], 1 @@ -402,7 +404,7 @@ cdef class Farey: [17 -2] [60 -7], 1 )) - sage: F.word_problem(g, output = 'syllables') + sage: F.word_problem(g, output='syllables') ((3, 1), (10, 2), (8, -1), (5, 1)) TESTS: @@ -413,7 +415,7 @@ cdef class Farey: sage: G = Gamma0(10) sage: F = G.farey_symbol() sage: g = G([-701,-137,4600,899]) - sage: g1 = prod(F.generators()[i]**a for i,a in F.word_problem(g, output = 'syllables')) + sage: g1 = prod(F.generators()[i]**a for i, a in F.word_problem(g, output='syllables')) sage: g == g1 True @@ -426,15 +428,16 @@ cdef class Farey: Check that :issue:`20347` is solved:: sage: from sage.misc.misc_c import prod - sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)") + sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)", S3="(1,2,3)") sage: S = G.farey_symbol() - sage: g1,g2 = S.generators() + sage: g1, g2 = S.generators() sage: g = g1^3 * g2^-2 * g1 * g2 sage: S.word_problem(g) (2, 2, 2, 1, 1, 1, 2, 1, 2) - sage: h = prod(S.generators()[i]**a for i,a in S.word_problem(g, output = 'syllables')) + sage: h = prod(S.generators()[i]**a for i, a in S.word_problem(g, output='syllables')) sage: g == h True + """ if output not in ['standard', 'syllables', 'gens']: raise ValueError('Unrecognized output format') @@ -464,7 +467,7 @@ cdef class Farey: if sgn == -1: beta, mbeta = mbeta, beta - gens_dict = {g:i+1 for i,g in enumerate(self.generators())} + gens_dict = {g: i+1 for i, g in enumerate(self.generators())} extra_tietze = [] if beta.is_one(): found = True @@ -501,13 +504,13 @@ cdef class Farey: for i in range(len(tietze)): t = tietze[i] tmp = tmp * gens[t-1] if t > 0 else tmp * gens[-t-1]**-1 - assert tmp.matrix() == M.matrix(),'%s %s %s' % (tietze, tmp.matrix(),M.matrix()) + assert tmp.matrix() == M.matrix(), '%s %s %s' % (tietze, tmp.matrix(), M.matrix()) if output == 'standard': return tuple(tietze) if output == 'syllables': - return tuple((a-1,len(list(g))) if a > 0 else (-a-1,-len(list(g))) for a,g in groupby(tietze)) + return tuple((a-1, len(list(g))) if a > 0 else (-a-1, -len(list(g))) for a, g in groupby(tietze)) else: # output == 'gens' - return tuple((gens[a-1],len(list(g))) if a > 0 else (gens[-a-1],-len(list(g))) for a, g in groupby(tietze)) + return tuple((gens[a-1], len(list(g))) if a > 0 else (gens[-a-1], -len(list(g))) for a, g in groupby(tietze)) def __contains__(self, M): r""" @@ -594,9 +597,9 @@ cdef class Farey: EXAMPLES:: - sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'plain') + sage: FareySymbol(Gamma0(11))._latex_(forced_format='plain') '\\left( -\\infty\\underbrace{\\quad}_{1} 0\\underbrace{\\quad}_{2} \\frac{1}{3}\\underbrace{\\quad}_{3} \\frac{1}{2}\\underbrace{\\quad}_{2} \\frac{2}{3}\\underbrace{\\quad}_{3} 1\\underbrace{\\quad}_{1} \\infty\\right)' - sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'xymatrix') + sage: FareySymbol(Gamma0(11))._latex_(forced_format='xymatrix') '\\begin{xy}\\xymatrix{& -\\infty \\ar@{-}@/_1pc/[r]_{1}& 0 \\ar@{-}@/_1pc/[r]_{2}& \\frac{1}{3} \\ar@{-}@/_1pc/[r]_{3}& \\frac{1}{2} \\ar@{-}@/_1pc/[r]_{2}& \\frac{2}{3} \\ar@{-}@/_1pc/[r]_{3}& 1 \\ar@{-}@/_1pc/[r]_{1}& \\infty }\\end{xy}' sage: 'xymatrix' in FareySymbol(Gamma0(11))._latex_() diff --git a/src/sage/modular/drinfeld_modform/element.py b/src/sage/modular/drinfeld_modform/element.py index dd6d48ad99e..59d7a9e23ff 100644 --- a/src/sage/modular/drinfeld_modform/element.py +++ b/src/sage/modular/drinfeld_modform/element.py @@ -25,6 +25,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.polynomial.multi_polynomial import MPolynomial + class DrinfeldModularFormsElement(ModuleElement): r""" Element class of rings of Drinfeld modular forms. diff --git a/src/sage/modular/drinfeld_modform/ring.py b/src/sage/modular/drinfeld_modform/ring.py index c113523fc48..a0e03ea017b 100644 --- a/src/sage/modular/drinfeld_modform/ring.py +++ b/src/sage/modular/drinfeld_modform/ring.py @@ -44,6 +44,7 @@ from .element import DrinfeldModularFormsElement + class DrinfeldModularForms(Parent, UniqueRepresentation): r""" Base class for the graded ring of Drinfeld modular forms. diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 32c7c88c0cf..5418a1fff58 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -187,6 +187,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f data[k] = sign * coeffs[d - k] * q**(i * (k - d / 2)) return ring(data) + def enumerate_hypergeometric_data(d, weight=None): r""" Return an iterator over parameters of hypergeometric motives (up to swapping). diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index 029dcc822b0..04d9123ab57 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -37,6 +37,7 @@ from .type_space import TypeSpace from .smoothchar import SmoothCharacterGroupQp, SmoothCharacterGroupUnramifiedQuadratic, SmoothCharacterGroupRamifiedQuadratic + def LocalComponent(f, p, twist_factor=None): r""" Calculate the local component at the prime `p` of the automorphic @@ -115,6 +116,7 @@ def LocalComponent(f, p, twist_factor=None): mintwist = LocalComponent(g, p, twist_factor) return ImprimitiveLocalComponent(f, p, twist_factor, mintwist, chi) + class LocalComponentBase(SageObject): r""" Base class for local components of newforms. Not to be directly instantiated; use the :func:`~LocalComponent` constructor function. @@ -316,6 +318,7 @@ def __ne__(self, other): """ return not (self == other) + class PrimitiveLocalComponent(LocalComponentBase): r""" Base class for primitive (twist-minimal) local components. @@ -346,6 +349,7 @@ def minimal_twist(self): """ return self + class PrincipalSeries(PrimitiveLocalComponent): r""" A principal series representation. This is an abstract base class, not to @@ -399,6 +403,7 @@ def characters(self): """ pass + class UnramifiedPrincipalSeries(PrincipalSeries): r""" An unramified principal series representation of `{\rm GL}_2(\QQ_p)` @@ -469,6 +474,7 @@ def characters(self): G = SmoothCharacterGroupQp(self.prime(), d.parent()) return Sequence([G.character(0, [d]), G.character(0, [self.newform()[self.prime()] - d])], cr=True, universe=G) + class PrimitivePrincipalSeries(PrincipalSeries): r""" A ramified principal series of the form `\pi(\chi_1, \chi_2)` @@ -500,6 +506,7 @@ def characters(self): chi2 = G.character(0, [self.prime()]) * self.central_character() / chi1 return Sequence([chi1, chi2], cr=True, universe=G) + class PrimitiveSpecial(PrimitiveLocalComponent): r""" A primitive special representation: that is, the Steinberg representation @@ -586,6 +593,7 @@ def check_tempered(self): for sigma in K.embeddings(QQbar): assert sigma(c1(p)).abs() == w + class PrimitiveSupercuspidal(PrimitiveLocalComponent): r""" A primitive supercuspidal representation. @@ -971,6 +979,7 @@ def check_tempered(self): for sigma in K.embeddings(QQbar): assert sigma(c1(p)).abs() == sigma(c2(p)).abs() == w + class ImprimitiveLocalComponent(LocalComponentBase): r""" A smooth representation which is not of minimal level among its character diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 1a05d448d9b..e2cadd4219a 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -1103,6 +1103,7 @@ def quadratic_chars(self): nr = self.character(0, [-1]) return sorted([nr] + list(ram) + [f*nr for f in ram]) + class SmoothCharacterGroupQuadratic(SmoothCharacterGroupGeneric): r""" The group of smooth characters of `E^\times`, where `E` is a quadratic extension of `\QQ_p`. @@ -1391,6 +1392,7 @@ def extend_character(self, level, chi, vals, check=True): raise ValueError("Invalid values for extension") return chiE + class SmoothCharacterGroupUnramifiedQuadratic(SmoothCharacterGroupQuadratic): r""" The group of smooth characters of `\QQ_{p^2}^\times`, where `\QQ_{p^2}` is diff --git a/src/sage/modular/local_comp/type_space.py b/src/sage/modular/local_comp/type_space.py index 9e20c121108..ea43ec89ccf 100644 --- a/src/sage/modular/local_comp/type_space.py +++ b/src/sage/modular/local_comp/type_space.py @@ -62,6 +62,7 @@ def example_type_space(example_no=0): # a ramified (odd p-power level) case return TypeSpace(Newform_constructor('27a'), 3) + def find_in_space(f, A, base_extend=False): r""" Given a Newform object `f`, and a space `A` of modular symbols of the same @@ -147,6 +148,7 @@ def find_in_space(f, A, base_extend=False): return D + class TypeSpace(SageObject): r""" The modular symbol type space associated to a newform, at a prime dividing @@ -401,12 +403,11 @@ def minimal_twist(self): V = A.submodule(VV, check=False) D = V.decomposition()[0] - #if len(D.star_eigenvalues()) == 2: + # if len(D.star_eigenvalues()) == 2: # D = D.sign_submodule(1) D1 = D.modular_symbols_of_sign(1) M = ModularForms(D1.group(), D1.weight(), D1.base_ring()) - ff = Newform(M, D1, names='a') - return ff + return Newform(M, D1, names='a') ##################################### # The group action on the type space. @@ -634,7 +635,7 @@ def _discover_torus_action(self): mats = self._intertwining_basis(a) V = self.t_space.nonembedded_free_module() v = self.eigensymbol_subspace().gen(0) - w = V.submodule_with_basis([m * v for m in mats]).coordinates(v) #v * self.e_space.diamond_eigenvalue(crt(a, 1, f, self.tame_level()))) + w = V.submodule_with_basis([m * v for m in mats]).coordinates(v) # v * self.e_space.diamond_eigenvalue(crt(a, 1, f, self.tame_level()))) self._a = a self._amat = sum([mats[i] * w[i] for i in range(len(mats))]) diff --git a/src/sage/modular/modform/ambient_R.py b/src/sage/modular/modform/ambient_R.py index fcf8a598721..c07597e25dc 100644 --- a/src/sage/modular/modform/ambient_R.py +++ b/src/sage/modular/modform/ambient_R.py @@ -16,6 +16,7 @@ from sage.rings.integer_ring import ZZ from sage.misc.cachefunc import cached_method + class ModularFormsAmbient_R(ambient.ModularFormsAmbient): def __init__(self, M, base_ring): """ diff --git a/src/sage/modular/modform/ambient_eps.py b/src/sage/modular/modform/ambient_eps.py index cf2ab2b9db5..ddd4ed467bc 100644 --- a/src/sage/modular/modform/ambient_eps.py +++ b/src/sage/modular/modform/ambient_eps.py @@ -94,6 +94,7 @@ from . import cuspidal_submodule from . import eisenstein_submodule + class ModularFormsAmbient_eps(ModularFormsAmbient): """ A space of modular forms with character. diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 30a264905bc..ffe19d3d708 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -19,15 +19,15 @@ ] """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004-2006 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import weakref import re @@ -94,13 +94,13 @@ def canonical_parameters(group, level, weight, base_ring): raise NotImplementedError("weight must be at least 1") if isinstance(group, dirichlet.DirichletCharacter): - if ( group.level() != Integer(level) ): + if group.level() != Integer(level): raise ValueError("group.level() and level do not match.") group = group.minimize_base_ring() level = Integer(level) elif isinstance(group, arithgroup.CongruenceSubgroupBase): - if ( Integer(level) != group.level() ): + if Integer(level) != group.level(): raise ValueError("group.level() and level do not match.") # normalize the case of SL2Z if isinstance(group, arithgroup.SL2Z_class) or \ @@ -132,6 +132,7 @@ def canonical_parameters(group, level, weight, base_ring): _cache = {} + def ModularForms_clear_cache(): """ Clear the cache of modular forms. diff --git a/src/sage/modular/modform/cuspidal_submodule.py b/src/sage/modular/modform/cuspidal_submodule.py index 2f5a94cbf58..5de0805b386 100644 --- a/src/sage/modular/modform/cuspidal_submodule.py +++ b/src/sage/modular/modform/cuspidal_submodule.py @@ -52,6 +52,7 @@ from .submodule import ModularFormsSubmodule from . import weight1 + class CuspidalSubmodule(ModularFormsSubmodule): """ Base class for cuspidal submodules of ambient spaces of modular forms. @@ -211,6 +212,7 @@ def change_ring(self, R): """ return self.ambient_module().change_ring(R).cuspidal_submodule() + class CuspidalSubmodule_R(CuspidalSubmodule): """ Cuspidal submodule over a non-minimal base ring. @@ -626,11 +628,13 @@ def _compute_diamond_matrix(self, d): symbs = self.modular_symbols(sign=1) return _convert_matrix_from_modsyms(symbs, symbs.diamond_bracket_matrix(d))[0] + class CuspidalSubmodule_g1_Q(CuspidalSubmodule_gH_Q): r""" Space of cusp forms for `\Gamma_1(N)` over `\QQ`. """ + class CuspidalSubmodule_eps(CuspidalSubmodule_modsym_qexp): """ Space of cusp forms with given Dirichlet character. @@ -659,6 +663,7 @@ class CuspidalSubmodule_eps(CuspidalSubmodule_modsym_qexp): """ pass + def _convert_matrix_from_modsyms(symbs, T): r""" Given a space of modular symbols and a matrix T acting on it, calculate the @@ -694,17 +699,19 @@ def _convert_matrix_from_modsyms(symbs, T): # we repeatedly use these matrices below, so we store them # once as lists to save time. - hecke_matrix_ls = [ symbs.hecke_matrix(m).list() for m in range(1,r+1) ] - hecke_image_ls = [ (T*symbs.hecke_matrix(m)).list() for m in range(1,r+1) ] + hecke_matrix_ls = [symbs.hecke_matrix(m).list() + for m in range(1, r + 1)] + hecke_image_ls = [(T * symbs.hecke_matrix(m)).list() + for m in range(1, r + 1)] # compute the q-expansions of some cusp forms and their # images under T_n for i in range(d**2): - v = X([ hecke_matrix_ls[m][i] for m in range(r) ]) + v = X([hecke_matrix_ls[m][i] for m in range(r)]) Ynew = Y.span(Y.basis() + [v]) if Ynew.rank() > Y.rank(): basis.append(v) - basis_images.append(X([ hecke_image_ls[m][i] for m in range(r) ])) + basis_images.append(X([hecke_image_ls[m][i] for m in range(r)])) Y = Ynew if len(basis) == d: break diff --git a/src/sage/modular/modform/eis_series.py b/src/sage/modular/modform/eis_series.py index dbdf09d6512..73ea6dcaf1c 100644 --- a/src/sage/modular/modform/eis_series.py +++ b/src/sage/modular/modform/eis_series.py @@ -159,7 +159,7 @@ def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'): E._unsafe_mutate(0, a0) return R(E, prec) # The following is an older slower alternative to the above three lines: - #return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False) + # return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False) else: # This used to work with check=False, but that can only be regarded as # an improbable lucky miracle. Enabling checking is a noticeable speed @@ -170,6 +170,7 @@ def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'): else: return R(eisenstein_series_poly(k, prec).list(), prec=prec, check=True) + def __common_minimal_basering(chi, psi): """ Find the smallest basering over which chi and psi are valued, and @@ -279,7 +280,7 @@ def __find_eisen_chars(character, k): if L not in C: continue GL = C[L] - for R in divisors(N/L): + for R in divisors(N // L): if R not in C: continue GR = C[R] @@ -288,8 +289,8 @@ def __find_eisen_chars(character, k): if chi*psi == eps: chi0, psi0 = __common_minimal_basering(chi, psi) for t in divisors(N//(R*L)): - if k != 1 or ((psi0, chi0, t) not in params): - params.append( (chi0,psi0,t) ) + if k != 1 or (psi0, chi0, t) not in params: + params.append((chi0, psi0, t)) return params @@ -365,8 +366,6 @@ def __find_eisen_chars_gamma1(N, k): pairs.append((psi, chi)) else: pairs.append((chi, psi)) - #end fors - #end if triples = [] for chi, psi in pairs: @@ -376,14 +375,14 @@ def __find_eisen_chars_gamma1(N, k): if k == 2 and chi.is_trivial() and psi.is_trivial(): D.remove(1) chi, psi = __common_minimal_basering(chi, psi) - for t in D: - triples.append((chi, psi, t)) + triples.extend((chi, psi, t) for t in D) + return triples def eisenstein_series_lseries(weight, prec=53, - max_imaginary_part=0, - max_asymp_coeffs=40): + max_imaginary_part=0, + max_asymp_coeffs=40): r""" Return the `L`-series of the weight `2k` Eisenstein series on `\SL_2(\ZZ)`. diff --git a/src/sage/modular/modform/eisenstein_submodule.py b/src/sage/modular/modform/eisenstein_submodule.py index 2110ccf695f..fbbe2e06da3 100644 --- a/src/sage/modular/modform/eisenstein_submodule.py +++ b/src/sage/modular/modform/eisenstein_submodule.py @@ -119,6 +119,7 @@ def modular_symbols(self, sign=0): A = self.ambient_module() return A.modular_symbols(sign).eisenstein_submodule() + class EisensteinSubmodule_params(EisensteinSubmodule): @cached_method @@ -586,9 +587,9 @@ class EisensteinSubmodule_eps(EisensteinSubmodule_params): ] """ # TODO - #def _compute_q_expansion_basis(self, prec): - #B = EisensteinSubmodule_params._compute_q_expansion_basis(self, prec) - #raise NotImplementedError, "must restrict scalars down correctly." + # def _compute_q_expansion_basis(self, prec): + # B = EisensteinSubmodule_params._compute_q_expansion_basis(self, prec) + # raise NotImplementedError("must restrict scalars down correctly.") def cyclotomic_restriction(L, K): @@ -667,8 +668,8 @@ def cyclotomic_restriction_tower(L, K): f = L.defining_polynomial() R = K['x'] g = R(f) - h_ls = [ t[0] for t in g.factor() if t[0](L.gen(0)) == 0 ] - if len(h_ls) == 0: + h_ls = [t[0] for t in g.factor() if t[0](L.gen(0)) == 0] + if not h_ls: raise ValueError(r"K (= Q(\zeta_%s)) is not contained in L (= Q(\zeta_%s))" % (K._n(), L._n())) h = h_ls[0] diff --git a/src/sage/modular/modform/half_integral.py b/src/sage/modular/modform/half_integral.py index 801e3715e21..49e205cafab 100644 --- a/src/sage/modular/modform/half_integral.py +++ b/src/sage/modular/modform/half_integral.py @@ -16,6 +16,7 @@ from .theta import theta2_qexp, theta_qexp from copy import copy + def half_integral_weight_modform_basis(chi, k, prec): r""" A basis for the space of weight `k/2` forms with character diff --git a/src/sage/modular/modform/hecke_operator_on_qexp.py b/src/sage/modular/modform/hecke_operator_on_qexp.py index 7191b52abfd..346a073f20a 100644 --- a/src/sage/modular/modform/hecke_operator_on_qexp.py +++ b/src/sage/modular/modform/hecke_operator_on_qexp.py @@ -26,6 +26,7 @@ from sage.modular.dirichlet import DirichletGroup, DirichletCharacter from .element import ModularFormElement + def hecke_operator_on_qexp(f, n, k, eps=None, prec=None, check=True, _return_list=False): r""" diff --git a/src/sage/modular/modform/j_invariant.py b/src/sage/modular/modform/j_invariant.py index 48b7c91fcb0..a74d42b91b2 100644 --- a/src/sage/modular/modform/j_invariant.py +++ b/src/sage/modular/modform/j_invariant.py @@ -6,6 +6,7 @@ from .vm_basis import delta_qexp from sage.rings.rational_field import QQ + def j_invariant_qexp(prec=10, K=QQ): r""" Return the `q`-expansion of the `j`-invariant to diff --git a/src/sage/modular/modform/numerical.py b/src/sage/modular/modform/numerical.py index 253047b2eab..dd14ff15995 100644 --- a/src/sage/modular/modform/numerical.py +++ b/src/sage/modular/modform/numerical.py @@ -3,15 +3,15 @@ Numerical computation of newforms """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004-2006 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.fast_arith import prime_range from sage.matrix.constructor import matrix @@ -31,6 +31,7 @@ # This variable controls importing the SciPy library sparingly scipy = None + @richcmp_method class NumericalEigenforms(SageObject): """ @@ -488,6 +489,7 @@ def systems_of_abs(self, bound): v.set_immutable() return v + def support(v, eps): """ Given a vector `v` and a threshold eps, return all diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index bf4457d7748..dacfda54bae 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -10,7 +10,7 @@ - William Stein (2007-08-24): first version - David Ayotte (2021-06): implemented category and Parent/Element frameworks """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # 2021 David Ayotte # @@ -18,8 +18,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from random import shuffle @@ -143,6 +143,7 @@ def _span_of_forms_in_weight(forms, weight, prec, stop_dim=None, use_random=Fals verbose('span has dimension %s' % W.rank(), t) return W + @richcmp_method class ModularFormsRing(Parent): r""" diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 87d96b21356..202ea687953 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1698,15 +1698,15 @@ def newforms(self, names=None): """ M = self.modular_symbols(sign=1) factors = M.cuspidal_subspace().new_subspace().decomposition() - large_dims = [ X.dimension() for X in factors if X.dimension() != 1 ] - if len(large_dims) > 0 and names is None: + large_dims = [X.dimension() for X in factors if X.dimension() != 1] + if large_dims and names is None: raise ValueError("Please specify a name to be used when generating names for generators of Hecke eigenvalue fields corresponding to the newforms.") elif names is None: # In this case, we don't need a variable name, so insert # something to get passed along below names = 'a' - return [ Newform(self, factors[i], names=(names+str(i)) ) - for i in range(len(factors)) ] + return [Newform(self, factors[i], names=names + str(i)) + for i in range(len(factors))] @cached_method def eisenstein_submodule(self): diff --git a/src/sage/modular/modform/theta.py b/src/sage/modular/modform/theta.py index 92c5f945a1c..fbe34473f98 100644 --- a/src/sage/modular/modform/theta.py +++ b/src/sage/modular/modform/theta.py @@ -11,6 +11,7 @@ from math import sqrt + def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" Return the `q`-expansion of the series `\theta_2 = \sum_{n \text{ odd}} q^{n^2}`. @@ -58,6 +59,7 @@ def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): R = PowerSeriesRing(K, sparse=sparse, names=var) return R(v, prec=prec) + def theta_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" Return the `q`-expansion of the standard `\theta` series diff --git a/src/sage/modular/modform/vm_basis.py b/src/sage/modular/modform/vm_basis.py index bbbbd9b4513..58f36fd0f1a 100644 --- a/src/sage/modular/modform/vm_basis.py +++ b/src/sage/modular/modform/vm_basis.py @@ -19,14 +19,14 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 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/ +# **************************************************************************** import math @@ -156,22 +156,22 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): ls[i] = err return Sequence(ls, cr=True) - F6 = eisenstein_series_poly(6,prec) + F6 = eisenstein_series_poly(6, prec) if e == 0: A = Fmpz_poly(1) elif e == 4: - A = eisenstein_series_poly(4,prec) + A = eisenstein_series_poly(4, prec) elif e == 6: A = F6 elif e == 8: - A = eisenstein_series_poly(8,prec) + A = eisenstein_series_poly(8, prec) elif e == 10: - A = eisenstein_series_poly(10,prec) - else: # e == 14 - A = eisenstein_series_poly(14,prec) + A = eisenstein_series_poly(10, prec) + else: # e == 14 + A = eisenstein_series_poly(14, prec) - if A[0] == -1 : + if A[0] == -1: A = -A if n == 0: @@ -186,9 +186,9 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): if cusp_only: ls = [Fmpz_poly(0)] + [A] * n else: - ls = [A] * (n+1) + ls = [A] * (n + 1) - for i in range(1,n+1): + for i in range(1, n + 1): ls[n-i] *= Fprod ls[i] *= Dprod ls[n-i]._unsafe_mutate_truncate(prec) @@ -201,17 +201,17 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): P = PowerSeriesRing(ZZ, var) if cusp_only: - for i in range(1,n+1) : - for j in range(1, i) : + for i in range(1, n + 1): + for j in range(1, i): ls[j] = ls[j] - ls[j][i]*ls[i] - return Sequence([P(l.list()).add_bigoh(prec) for l in ls[1:]],cr=True) - else : - for i in range(1,n+1): - for j in range(i): - ls[j] = ls[j] - ls[j][i]*ls[i] + return Sequence([P(l.list()).add_bigoh(prec) for l in ls[1:]], cr=True) + + for i in range(1, n + 1): + for j in range(i): + ls[j] = ls[j] - ls[j][i] * ls[i] - return Sequence([P(l.list()).add_bigoh(prec) for l in ls], cr=True) + return Sequence([P(l.list()).add_bigoh(prec) for l in ls], cr=True) def _delta_poly(prec=10): @@ -284,9 +284,9 @@ def _delta_poly_modulo(N, prec=10): OUTPUT: - the polynomial of degree ``prec``-1 which is the truncation - of `\Delta` modulo `N`, as an element of the polynomial - ring in `q` over the integers modulo `N`. + the polynomial of degree ``prec``-1 which is the truncation + of `\Delta` modulo `N`, as an element of the polynomial + ring in `q` over the integers modulo `N`. EXAMPLES:: @@ -297,7 +297,7 @@ def _delta_poly_modulo(N, prec=10): 2*q^11 + 7*q^9 + 6*q^7 + 2*q^6 + 8*q^4 + 2*q^3 + 6*q^2 + q """ if prec <= 0: - raise ValueError( "prec must be positive" ) + raise ValueError("prec must be positive") v = [0] * prec # Let F = \sum_{n >= 0} (-1)^n (2n+1) q^(floor(n(n+1)/2)). @@ -324,7 +324,7 @@ def _delta_poly_modulo(N, prec=10): return f -def delta_qexp(prec=10, var='q', K=ZZ) : +def delta_qexp(prec=10, var='q', K=ZZ): r""" Return the `q`-expansion of the weight 12 cusp form `\Delta` as a power series with coefficients in the ring K (`= \ZZ` by default). diff --git a/src/sage/modular/modform/weight1.py b/src/sage/modular/modform/weight1.py index a2f5aa10108..9aa75074676 100644 --- a/src/sage/modular/modform/weight1.py +++ b/src/sage/modular/modform/weight1.py @@ -20,6 +20,7 @@ from sage.modular.arithgroup.all import Gamma0, GammaH from sage.modular.arithgroup.arithgroup_generic import ArithmeticSubgroup + @cached_function def modular_ratio_space(chi): r""" @@ -100,6 +101,7 @@ def modular_ratio_to_prec(chi, qexp, prec): fB_elt = C(fB, check=False) return fB_elt.qexp(prec) / B + @cached_function def hecke_stable_subspace(chi, aux_prime=ZZ(2)): r""" @@ -187,6 +189,7 @@ def hecke_stable_subspace(chi, aux_prime=ZZ(2)): qexps = Sequence(A(x.list()).add_bigoh(R) for x in J.gens()) return qexps + @cached_function def dimension_wt1_cusp_forms(chi): r""" @@ -200,6 +203,7 @@ def dimension_wt1_cusp_forms(chi): """ return len(hecke_stable_subspace(chi)) + @cached_function def dimension_wt1_cusp_forms_gH(group): r""" diff --git a/src/sage/modular/modform_hecketriangle/constructor.py b/src/sage/modular/modform_hecketriangle/constructor.py index e245e1d2394..e3525879e6e 100644 --- a/src/sage/modular/modform_hecketriangle/constructor.py +++ b/src/sage/modular/modform_hecketriangle/constructor.py @@ -117,11 +117,11 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): analytic_type = AT(["quasi", "mero"]) - R = PolynomialRing(base_ring,'x,y,z,d') + R = PolynomialRing(base_ring, 'x,y,z,d') F = FractionField(R) - (x,y,z,d) = R.gens() + x, y, z, d = R.gens() R2 = PolynomialRing(PolynomialRing(base_ring, 'd'), 'x,y,z') - dhom = R.hom( R2.gens() + (R2.base().gen(),), R2) + dhom = R.hom(R2.gens() + (R2.base().gen(),), R2) f = F(f) @@ -131,12 +131,12 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): ep_denom = {ZZ.one() - 2*((sum([g.exponents()[0][m] for m in [1, 2]])) % 2) for g in dhom(denom).monomials()} if (n == infinity): - hom_num = R( num.subs(x=x**4, y=y**2, z=z**2) ) - hom_denom = R( denom.subs(x=x**4, y=y**2, z=z**2) ) + hom_num = R(num.subs(x=x**4, y=y**2, z=z**2)) + hom_denom = R(denom.subs(x=x**4, y=y**2, z=z**2)) else: n = ZZ(n) - hom_num = R( num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) - hom_denom = R( denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) + hom_num = R(num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2)))) + hom_denom = R(denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2)))) # Determine whether the denominator of f is homogeneous if (len(ep_denom) == 1 and dhom(hom_denom).is_homogeneous()): @@ -146,9 +146,9 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): return (False, False, None, None, None) # Determine whether f is homogeneous - if (len(ep_num) == 1 and dhom(hom_num).is_homogeneous()): + if len(ep_num) == 1 and dhom(hom_num).is_homogeneous(): homo = True - if (n == infinity): + if n == infinity: weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) else: weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) / (n-2) @@ -160,17 +160,17 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): ep = None # Note that we intentionally leave out the d-factor! - if (n == infinity): + if n == infinity: finf_pol = (x-y**2) else: finf_pol = x**n-y**2 # Determine whether f is modular - if not ( (num.degree(z) > 0) or (denom.degree(z) > 0) ): + if not (num.degree(z) > 0 or denom.degree(z) > 0): analytic_type = analytic_type.reduce_to("mero") # Determine whether f is holomorphic - if (dhom(denom).is_constant()): + if dhom(denom).is_constant(): analytic_type = analytic_type.reduce_to(["quasi", "holo"]) # Determine whether f is cuspidal in the sense that finf divides it... # Bug in singular: finf_pol.divides(1.0) fails over RR @@ -180,7 +180,7 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): else: # -> Because of a bug with singular in some cases try: - while (finf_pol.divides(denom)): + while finf_pol.divides(denom): # a simple "denom /= finf_pol" is strangely not enough for non-exact rings # and dividing would/may result with an element of the quotient ring of the polynomial ring denom = denom.quo_rem(finf_pol)[0] diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index df9568bb2ab..e7c56fd0e94 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -25,11 +25,11 @@ from sage.rings.infinity import infinity from sage.rings.cc import CC -lazy_import('sage.rings.qqbar', 'AA') - from sage.groups.matrix_gps.group_element import MatrixGroupElement_generic from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicPlane +lazy_import('sage.rings.qqbar', 'AA') + # We want to simplify p after the coercion (pari bug for AA) def coerce_AA(p): @@ -51,7 +51,7 @@ def coerce_AA(p): """ el = AA(p) el.simplify() - #el.exactify() + # el.exactify() return el @@ -221,13 +221,13 @@ def _word_S_T_data(self): half = one / ZZ(2) while True: - a,b,c,d = M.list() + a, b, c, d = M.list() mshift = coerce_AA((4*a*c + b*d) / (4*c*c + d*d)) m = (mshift / lam + half).floor() if m != zero: res.append((one, m),) M = T**(-m) * M - a,b,c,d = M.list() + a, b, c, d = M.list() abs_t = coerce_AA((4*a*a + b*b) / (4*c*c + d*d)) if coerce_AA(abs_t) < 1: @@ -452,7 +452,7 @@ def string_repr(self, method='default'): return "-1" if sgn < 0 else "1" Lstr = list(L) - for i,(v0,v1) in enumerate(Lstr): + for i, (v0, v1) in enumerate(Lstr): if v0 == 0: Lstr[i] = "S" elif v1 == 1: @@ -479,7 +479,7 @@ def string_repr(self, method='default'): if sgn < 0: repr_str = repr_str[1:] - #if self != R.inverse().acton(self): + # if self != R.inverse().acton(self): if R.is_identity(): repr_str = "{}{}".format("-" if sgn < 0 else "", repr_str) else: @@ -496,7 +496,7 @@ def string_repr(self, method='default'): (L, R, sgn) = self._block_decomposition_data() if self.is_elliptic(): - L = [ L ] + L = [L] repr_str = "" begin = True @@ -633,7 +633,7 @@ def continued_fraction(self): if p == infinity: # TODO: The choice of r doesn't matter? r = ZZ.zero() - #elif self.is_elliptic(): + # elif self.is_elliptic(): # r = ZZ(emb(p/lam).real().floor() + 1) else: emb_res = emb(p/lam) @@ -645,7 +645,7 @@ def continued_fraction(self): cf_index += one preperiod_len = cf_dict[p] - #period_len = cf_index - preperiod_len + # period_len = cf_index - preperiod_len return (tuple(L[:preperiod_len]), tuple(L[preperiod_len:])) @@ -807,7 +807,7 @@ def _primitive_block_decomposition_data(self): L = (one, one) else: raise RuntimeError("There is something wrong in the method " - "_primitive_block_decomposition_data. Please contact sage-devel@googlegroups.com") + "_primitive_block_decomposition_data. Please contact sage-devel@googlegroups.com") return (L, R) @@ -835,11 +835,11 @@ def _primitive_block_decomposition_data(self): initial_ones = number_of_ones.pop(0) if not list_larger: - list_v1 = [-ZZ(1)] - list_vlarger = [ initial_ones + 2 ] + list_v1 = [-ZZ.one()] + list_vlarger = [initial_ones + 2] else: - list_v1 = [ v-2 for v in list_larger ] - list_vlarger = [ v+2 for v in number_of_ones ] + list_v1 = [v - 2 for v in list_larger] + list_vlarger = [v + 2 for v in number_of_ones] list_vlarger[-1] += initial_ones L = [] @@ -1363,14 +1363,14 @@ def primitive_power(self, method='cf'): for j in range(1, G.n()): Uj *= U if U_power == Uj: - #L = [one, ZZ(j)] + # L = [one, ZZ(j)] break elif U_power == -Uj: - #L = [one, ZZ(-j)] + # L = [one, ZZ(-j)] break else: raise RuntimeError("There is a problem in the method " - "'primitive_power'. Please contact sage-devel@googlegroups.com") + "'primitive_power'. Please contact sage-devel@googlegroups.com") if abs(j) < G.n()/two: return j @@ -1514,7 +1514,7 @@ def block_length(self, primitive=False): else: return sum(abs(v[1]) for v in L) - #@cached_method + # @cached_method def _block_decomposition_data(self): r""" Return a tuple ``(L, R, sgn)`` which describes the @@ -1771,7 +1771,7 @@ def block_decomposition(self): P = G.U() return ((P**L[1],), R, sgn) else: - return (tuple(G.V(v[0])**v[1] for v in L ), R, sgn) + return (tuple(G.V(v[0])**v[1] for v in L), R, sgn) def conjugacy_type(self, ignore_sign=True, primitive=False): r""" @@ -2206,7 +2206,7 @@ def is_translation(self, exclude_one=False): sage: (-HeckeTriangleGroup(n=7).I()).is_translation(exclude_one=True) False """ - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if not (c.is_zero() and a == d and (a.is_one() or (-a).is_one())): return False @@ -2488,7 +2488,7 @@ def is_simple(self): return False # The last condition is/should be equivalent to: - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() return (coerce_AA(a) > 0 and coerce_AA(b) > 0 and coerce_AA(c) > 0 and coerce_AA(d) > 0) def is_hecke_symmetric(self): @@ -2675,7 +2675,7 @@ def rational_period_function(self, k): if k % 2: raise TypeError except TypeError: - raise ValueError("k={} must be an even integer!".format(k)) + raise ValueError(f"k={k} must be an even integer!") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing P = PolynomialRing(self.parent().base_ring(), 'z') @@ -2683,14 +2683,14 @@ def rational_period_function(self, k): s = P.zero() - #L1 = [] + # L1 = [] for v in self.simple_elements(): - a,b,c,d = v._matrix.list() + a, b, c, d = v._matrix.list() Q = c*z**2 + (d - a)*z - b s += Q**(-k/ZZ(2)) for v in self.inverse().simple_elements(): - a,b,c,d = v._matrix.list() + a, b, c, d = v._matrix.list() Q = c*z**2 + (d - a)*z - b s -= ZZ(-1)**(k/ZZ(2)) * Q**(-k/ZZ(2)) @@ -3049,7 +3049,7 @@ def fixed_points(self, embedded=False, order='default'): else: e = self.root_extension_field().gen() - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if order == "none": sgn = ZZ(1) @@ -3170,7 +3170,7 @@ def acton(self, tau): model = tau.model() tau = tau.to_model('UHP').coordinates() - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if tau == infinity: if c.is_zero(): diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index fedf6160c50..0b1502d2d9a 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -87,6 +87,7 @@ # AUXILIARY CODE: SPACES OF MODULAR FORMS AND LINEAR ALGEBRA + def compute_G(p, F): r""" Given a power series `F \in R[[q]]^\times`, for some ring `R`, and an @@ -151,6 +152,7 @@ def low_weight_bases(N, p, m, NN, weightbound): generators.append(list(b)) return generators + def random_low_weight_bases(N,p,m,NN,weightbound): r""" Return list of random integral bases of modular forms of level `N` and @@ -194,6 +196,7 @@ def random_low_weight_bases(N,p,m,NN,weightbound): return RandomLWB + def low_weight_generators(N,p,m,NN): r""" Return a list of lists of modular forms, and an even natural number. @@ -244,6 +247,7 @@ def low_weight_generators(N,p,m,NN): return generators, weightbound + def random_solution(B,K): r""" Return a random solution in nonnegative integers to the equation `a_1 + 2 @@ -398,6 +402,7 @@ def random_new_basis_modp(N, p, k, LWBModp, TotalBasisModp, elldash, bound): return NewBasisCode + def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound): r""" Return a list of lists of lists of lists ``[j, a]``. The pairs ``[j, a]`` @@ -446,6 +451,7 @@ def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound): return CompSpacesCode + def complementary_spaces(N, p, k0, n, mdash, elldashp, elldash, modformsring, bound): r""" Return a list ``Ws``, each element in which is a list ``Wi`` of @@ -523,6 +529,7 @@ def complementary_spaces(N, p, k0, n, mdash, elldashp, elldash, modformsring, bo # AUXILIARY CODE: KATZ EXPANSIONS + def higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, bound): r""" Return a matrix `e` of size ``ell x elldashp`` over the integers modulo @@ -587,6 +594,7 @@ def higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, b return M, Ep1 + def compute_elldash(p, N, k0, n): r""" Return the "Sturm bound" for the space of modular forms of level @@ -615,6 +623,7 @@ def compute_elldash(p, N, k0, n): # *** DEGREE BOUND ON HECKE SERIES *** + def hecke_series_degree_bound(p, N, k, m): r""" Return the ``Wan bound`` on the degree of the characteristic series of the @@ -659,6 +668,7 @@ def hecke_series_degree_bound(p, N, k, m): # Returns matrix A modulo p^m from Step 6 of Algorithm 2. + def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): r""" Return a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` @@ -874,6 +884,7 @@ def compute_Wi(k, p, h, hj, E4, E6): return Wi, hj + def katz_expansions(k0, p, ellp, mdash, n): r""" Return a list `e` of `q`-expansions, and the Eisenstein series `E_{p-1} = 1 + @@ -926,6 +937,7 @@ def katz_expansions(k0, p, ellp, mdash, n): # *** MAIN FUNCTION FOR LEVEL 1 *** + def level1_UpGj(p, klist, m, extra_data=False): r""" Return a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` @@ -1038,6 +1050,7 @@ def level1_UpGj(p, klist, m, extra_data=False): # *** CODE FOR GENERAL LEVEL *** + def is_valid_weight_list(klist, p): r""" This function checks that ``klist`` is a nonempty list of integers all of @@ -1070,6 +1083,7 @@ def is_valid_weight_list(klist, p): if (klist[i] % (p-1)) != k0: raise ValueError("List of weights must be all congruent modulo p-1 = %s, but given list contains %s and %s which are not congruent" % (p - 1, klist[0], klist[i])) + def hecke_series(p, N, klist, m, modformsring=False, weightbound=6): r""" Return the characteristic series modulo `p^m` of the Atkin operator `U_p` diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index c159ac5eee3..40110016c52 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -85,6 +85,8 @@ _wscache = {} + + def WeightSpace_constructor(p, base_ring=None): r""" Construct the `p`-adic weight space for the given prime p. diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index c892748174d..8df8655e5e9 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -114,6 +114,7 @@ def _iterate_Up(Phi, p, M, ap, q, aq, check): Phi = ~(q ** (k + 1) + 1 - aq) * Phi return Phi + class PSModSymAction(Action): def __init__(self, actor, MSspace): r""" diff --git a/src/sage/modular/pollack_stevens/sigma0.py b/src/sage/modular/pollack_stevens/sigma0.py index c5ae3741f2f..2659f12cfa3 100644 --- a/src/sage/modular/pollack_stevens/sigma0.py +++ b/src/sage/modular/pollack_stevens/sigma0.py @@ -106,6 +106,7 @@ def __call__(self, g): """ return tuple(g.list()) + class Sigma0_factory(UniqueFactory): r""" Create the monoid of non-singular matrices, upper triangular mod `N`. diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 23b11fef749..c8bbdd1a9a3 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -31,6 +31,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.integer_ring import ZZ + class QuasiModularFormsElement(ModuleElement): r""" A quasimodular forms ring element. Such an element is described by @@ -142,11 +143,11 @@ def q_expansion(self, prec=6): sage: E2.q_expansion(prec=10) 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 - 288*q^6 - 192*q^7 - 360*q^8 - 312*q^9 + O(q^10) """ - E2 = eisenstein_series_qexp(2, prec=prec, K=self.base_ring(), normalization='constant') #normalization -> to force integer coefficients + E2 = eisenstein_series_qexp(2, prec=prec, K=self.base_ring(), normalization='constant') # normalization -> to force integer coefficients coefficients = self._polynomial.coefficients(sparse=False) - return sum(f.q_expansion(prec=prec)*E2**idx for idx, f in enumerate(coefficients)) + return sum(f.q_expansion(prec=prec) * E2**idx for idx, f in enumerate(coefficients)) - qexp = q_expansion # alias + qexp = q_expansion # alias def _repr_(self): r""" diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 62b5c98f90b..0227fa8319a 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -509,12 +509,12 @@ def is_2_adic_genus(genus_symbol_quintuple_list) -> bool: return False if s[1] == 2 and s[3] == 1: if s[2] % 8 in (1, 7): - if not s[4] in (0, 2, 6): + if s[4] not in (0, 2, 6): return False if s[2] % 8 in (3, 5): - if not s[4] in (2, 4, 6): + if s[4] not in (2, 4, 6): return False - if (s[1] - s[4]) % 2 == 1: + if (s[1] - s[4]) % 2: return False if s[3] == 0 and s[4] != 0: return False @@ -2802,7 +2802,8 @@ def direct_sum(self, other): signature_pair = (p1 + p2, n1 + n2) primes = [s.prime() for s in self.local_symbols()] - primes += [s.prime() for s in other.local_symbols() if not s.prime() in primes] + primes.extend(s.prime() for s in other.local_symbols() + if s.prime() not in primes) primes.sort() local_symbols = [] for p in primes: diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index 473e860ba42..31bc1db3ffd 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -634,10 +634,10 @@ def _rich_output_formatter(self, obj, rich_repr_kwds): rich_output = self._promote_output(rich_output) # check that the output container types are valid for the backend supported = self._backend.supported_output() - if not (type(plain_text) in supported): + if type(plain_text) not in supported: raise OutputTypeException( 'text output container not supported: {0}'.format(type(plain_text))) - if not (type(rich_output) in supported): + if type(rich_output) not in supported: raise OutputTypeException( 'output container not supported: {0}'.format(type(rich_output))) return plain_text, rich_output diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index f64c223f18b..2863732cf3b 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -202,6 +202,7 @@ cdef class FunctionFieldElement(FieldElement): We also substitute the generators in any base fields:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) @@ -224,6 +225,8 @@ cdef class FunctionFieldElement(FieldElement): Traceback (most recent call last): ... TypeError: in_dict must be a dict + + sage: # needs sage.rings.function_field sage: R. = K[] sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) sage: f = x + y @@ -234,6 +237,7 @@ cdef class FunctionFieldElement(FieldElement): We can also substitute using dictionary syntax:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) @@ -249,6 +253,7 @@ cdef class FunctionFieldElement(FieldElement): Check that we correctly handle extension fields:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) @@ -270,6 +275,7 @@ cdef class FunctionFieldElement(FieldElement): Test that substitution works for rational functions:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^4 - 3) @@ -298,6 +304,7 @@ cdef class FunctionFieldElement(FieldElement): Same purpose as above but over an extension field over the rationals:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) @@ -319,6 +326,7 @@ cdef class FunctionFieldElement(FieldElement): Test proper handling of not making substitutions:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: f = x sage: f.subs() is f @@ -357,6 +365,7 @@ cdef class FunctionFieldElement(FieldElement): Test error handling with ambiguously named generators:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] sage: L. = K.extension(x^3 - x) diff --git a/src/sage/rings/function_field/order_basis.py b/src/sage/rings/function_field/order_basis.py index e4fa3dfe39e..1cf89e3f966 100644 --- a/src/sage/rings/function_field/order_basis.py +++ b/src/sage/rings/function_field/order_basis.py @@ -102,7 +102,7 @@ def __init__(self, basis, check=True): if check: if self._module.rank() != field.degree(): raise ValueError("basis {} is not linearly independent".format(basis)) - if not to_V(field(1)) in self._module: + if to_V(field(1)) not in self._module: raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) if not all(to_V(a*b) in self._module for a in basis for b in basis): raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) @@ -399,7 +399,7 @@ def __init__(self, basis, check=True): if check: if self._module.rank() != field.degree(): raise ValueError("basis {} is not linearly independent".format(basis)) - if not W.coordinate_vector(to(field(1))) in self._module: + if W.coordinate_vector(to(field(1))) not in self._module: raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) if not all(W.coordinate_vector(to(a*b)) in self._module for a in basis for b in basis): raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) @@ -431,7 +431,7 @@ def _element_constructor_(self, f): V, fr_V, to_V = F.vector_space() W = self._ambient_space - if not W.coordinate_vector(to_V(f)) in self._module: + if W.coordinate_vector(to_V(f)) not in self._module: raise TypeError("{} is not an element of {}".format(f, self)) return f diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 2ebb062a432..16ff2d8f2ae 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -888,7 +888,7 @@ def ideal(self, *gens): """ if len(gens) == 1: gens = gens[0] - if not type(gens) in (list,tuple): + if type(gens) not in (list,tuple): gens = (gens,) mgens = [g * b for g in gens for b in self._basis] return self.ideal_with_gens_over_base(mgens) diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py index 87cbbdb4858..d90124c6800 100644 --- a/src/sage/rings/function_field/order_rational.py +++ b/src/sage/rings/function_field/order_rational.py @@ -83,7 +83,7 @@ def _element_constructor_(self, f): except TypeError: raise TypeError("unable to convert to an element of {}".format(F)) - if not f.denominator() in self.function_field().constant_base_field(): + if f.denominator() not in self.function_field().constant_base_field(): raise TypeError("%r is not an element of %r" % (f,self)) return f diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 3ec83de9b11..1603cba334e 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1896,10 +1896,14 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: TestSuite(L).run() # needs sage.libs.singular sage: L.category() Category of infinite commutative no zero divisors algebras over - (unique factorization domains and commutative algebras over + (unique factorization domains and algebras with basis over (Dedekind domains and euclidean domains - and noetherian rings - and infinite enumerated sets and metric spaces) + and noetherian rings + and infinite enumerated sets and metric spaces) + and commutative algebras over + (Dedekind domains and euclidean domains + and noetherian rings + 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 68ee2d5ee8e..594e2ff0a3e 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2277,7 +2277,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): """ P = self.codomain() try: - return P(dict([(a, self._underlying(b)) for a,b in x.dict().items()])) + return P({a: self._underlying(b) for a, b in x.monomial_coefficients().items()}) except Exception: pass try: diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 89fa8548f5d..35c4d4beb32 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -213,8 +213,8 @@ from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base from sage.rings.polynomial.term_order import TermOrder from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic -from sage.rings.ring import CommutativeRing from sage.structure.nonexact import Nonexact +from sage.structure.parent import Parent from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() @@ -389,8 +389,9 @@ def __init__(self, base_ring, num_gens, name_list, # Multivariate power series rings inherit from power series rings. But # apparently we can not call their initialisation. Instead, initialise # CommutativeRing and Nonexact: - CommutativeRing.__init__(self, base_ring, name_list, category=_IntegralDomains if base_ring in - _IntegralDomains else _CommutativeRings) + Parent.__init__(self, base=base_ring, names=name_list, + category=_IntegralDomains if base_ring in + _IntegralDomains else _CommutativeRings) Nonexact.__init__(self, default_prec) # underlying polynomial ring in which to represent elements @@ -1005,7 +1006,7 @@ def gen(self, n=0): if n < 0 or n >= self._ngens: raise ValueError("Generator not defined.") #return self(self._poly_ring().gens()[int(n)]) - return self.element_class(parent=self,x=self._poly_ring().gens()[int(n)], is_gen=True) + return self.element_class(parent=self, x=self._poly_ring().gens()[int(n)], is_gen=True) def ngens(self): """ @@ -1019,6 +1020,18 @@ def ngens(self): """ return self._ngens + def gens(self) -> tuple: + """ + Return the generators of this ring. + + EXAMPLES:: + + sage: M = PowerSeriesRing(ZZ, 3, 'v') + sage: M.gens() + (v0, v1, v2) + """ + return tuple(self.gen(i) for i in range(self._ngens)) + def prec_ideal(self): """ Return the ideal which determines precision; this is the ideal diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index b7731d59f74..76d81db6997 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -567,7 +567,7 @@ def _subs_formal(self, *x, **kwds): base_map = kwds.get('base_map') if base_map is None: base_map = lambda t: t - for m, c in self.dict().items(): + for m, c in self.monomial_coefficients().items(): y += base_map(c)*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) if self.prec() == infinity: return y @@ -1099,7 +1099,7 @@ def __mod__(self, other): return self.change_ring(Zmod(other)) raise NotImplementedError("Mod on multivariate power series ring elements not defined except modulo an integer.") - def dict(self): + def monomial_coefficients(self): """ Return underlying dictionary with keys the exponents and values the coefficients of this power series. @@ -1116,6 +1116,14 @@ def dict(self): sage: m = 2/3*t0*t1^15*t3^48 - t0^15*t1^21*t2^28*t3^5 sage: m2 = 1/2*t0^12*t1^29*t2^46*t3^6 - 1/4*t0^39*t1^5*t2^23*t3^30 + M.O(100) sage: s = m + m2 + sage: s.monomial_coefficients() + {(1, 15, 0, 48): 2/3, + (12, 29, 46, 6): 1/2, + (15, 21, 28, 5): -1, + (39, 5, 23, 30): -1/4} + + ``dict`` is an alias:: + sage: s.dict() {(1, 15, 0, 48): 2/3, (12, 29, 46, 6): 1/2, @@ -1124,9 +1132,11 @@ def dict(self): """ out_dict = {} for j in self._bg_value.coefficients(): - out_dict.update(j.dict()) + out_dict.update(j.monomial_coefficients()) return out_dict + dict = monomial_coefficients + def polynomial(self): """ Return the underlying polynomial of ``self`` as an element of @@ -1224,7 +1234,7 @@ def coefficients(self): True """ if self.is_sparse(): - return self.dict() + return self.monomial_coefficients() tmp = {} for j in self._bg_value.coefficients(): for m in j.monomials(): @@ -1727,7 +1737,7 @@ def _integral(self, xx): xxe = xx.exponents()[0] pos = [i for i, c in enumerate(xxe) if c != 0][0] # get the position of the variable res = {mon.eadd(xxe): R(co / (mon[pos]+1)) - for mon, co in self.dict().items()} + for mon, co in self.monomial_coefficients().items()} return P( res ).add_bigoh(self.prec()+1) def ogf(self): diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index d62ae34fb95..3686840ccba 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -2161,7 +2161,7 @@ def reduce(self, f): R = self.number_field().maximal_order() - if not (f in R): + if f not in R: raise TypeError("reduce only defined for integral elements") Rbasis = R.basis() diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index e54d2f454af..55db3bbbcbf 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -894,7 +894,7 @@ def ring_generators(self): gens.append(g) n.append(g.absolute_minpoly().degree()) W = A.span([to_V(x) for x in monomials(gens, n)]) - remaining = [x for x in remaining if not to_V(x) in W] + remaining = [x for x in remaining if to_V(x) not in W] return Sequence(gens, immutable=True) @cached_method @@ -1590,7 +1590,7 @@ def _element_constructor_(self, x): if not isinstance(x, Element) or x.parent() is not self._K: x = self._K(x) V, _, embedding = self._K.vector_space() - if not embedding(x) in self._module_rep: + if embedding(x) not in self._module_rep: raise TypeError("Not an element of the order.") return self._element_type(self, x) diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index 61459d8d50d..b8f2b62d5a7 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -124,7 +124,7 @@ def __pow__(self, other): sage: a^2 9 """ - if not (other in PariRing()): + if other not in PariRing(): other = Pari(other) return self.__class__(self.__x ** other.__x, parent=_inst) diff --git a/src/sage/rings/polynomial/flatten.py b/src/sage/rings/polynomial/flatten.py index ac0e594ab3c..6354bbcd382 100644 --- a/src/sage/rings/polynomial/flatten.py +++ b/src/sage/rings/polynomial/flatten.py @@ -224,13 +224,13 @@ def _call_(self, p): if isinstance(ring, PolynomialRing_general): for mon, pp in p.items(): assert pp.parent() is ring - for i, j in pp.dict().items(): - new_p[(i,)+(mon)] = j + for i, j in pp.monomial_coefficients().items(): + new_p[(i,) + (mon)] = j elif isinstance(ring, MPolynomialRing_base): for mon, pp in p.items(): assert pp.parent() is ring - for mmon, q in pp.dict().items(): - new_p[tuple(mmon)+mon] = q + for mmon, q in pp.monomial_coefficients().items(): + new_p[tuple(mmon) + mon] = q else: raise RuntimeError p = new_p @@ -642,7 +642,7 @@ def _call_(self, p): # apply _sub_specialization to each coefficient # in the flattened polynomial tmp = {} - for exponent, coefficient in flat.dict().items(): + for exponent, coefficient in flat.monomial_coefficients().items(): # Fix the type of exponent from (a,) to a # (necessary for R(tmp) later) if isinstance(exponent, ETuple) and len(exponent) == 1: diff --git a/src/sage/rings/polynomial/laurent_polynomial.pxd b/src/sage/rings/polynomial/laurent_polynomial.pxd index 8e9107aeb47..1937490caac 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pxd +++ b/src/sage/rings/polynomial/laurent_polynomial.pxd @@ -7,11 +7,11 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): cpdef _mul_(self, other) cpdef _floordiv_(self, other) cpdef long number_of_terms(self) except -1 - cpdef dict dict(self) + cpdef dict monomial_coefficients(self) + cdef class LaurentPolynomial_univariate(LaurentPolynomial): cdef ModuleElement __u cdef long __n cpdef _normalize(self) cpdef _unsafe_mutate(self, i, value) - diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 70cec491cae..0fed2467c05 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -218,7 +218,7 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): """ return self.number_of_terms() - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Abstract ``dict`` method. @@ -226,13 +226,15 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): sage: R. = LaurentPolynomialRing(ZZ) sage: from sage.rings.polynomial.laurent_polynomial import LaurentPolynomial - sage: LaurentPolynomial.dict(x) + sage: LaurentPolynomial.monomial_coefficients(x) Traceback (most recent call last): ... NotImplementedError """ raise NotImplementedError + dict = monomial_coefficients + def map_coefficients(self, f, new_base_ring=None): """ Apply ``f`` to the coefficients of ``self``. @@ -294,7 +296,8 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R(dict([(k, f(v)) for (k, v) in self.dict().items()])) + return R(dict([(k, f(v)) + for k, v in self.monomial_coefficients().items()])) cdef class LaurentPolynomial_univariate(LaurentPolynomial): @@ -838,7 +841,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): d = {repr(g): R.var(g) for g in self._parent.gens()} return self.subs(**d) - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Return a dictionary representing ``self``. @@ -848,11 +851,18 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: Q. = LaurentPolynomialRing(R) sage: f = (x^3 + y/t^3)^3 + t^2; f y^3*t^-9 + 3*x^3*y^2*t^-6 + 3*x^6*y*t^-3 + x^9 + t^2 + sage: f.monomial_coefficients() + {-9: y^3, -6: 3*x^3*y^2, -3: 3*x^6*y, 0: x^9, 2: 1} + + ``dict`` is an alias:: + sage: f.dict() {-9: y^3, -6: 3*x^3*y^2, -3: 3*x^6*y, 0: x^9, 2: 1} """ - cdef dict d = self.__u.dict() - return {k+self.__n: d[k] for k in d} + cdef dict d = self.__u.monomial_coefficients() + return {k + self.__n: d[k] for k in d} + + dict = monomial_coefficients def coefficients(self): """ @@ -1126,7 +1136,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ return self.__u.is_monomial() - def __pow__(_self, r, dummy): + def __pow__(_self, r, mod): """ EXAMPLES:: @@ -1145,9 +1155,21 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): x^-8 sage: (5*x^-4)^-3 5*x^12 + + Check that using third argument raises an error:: + + sage: L. = LaurentPolynomialRing(R) + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef LaurentPolynomial_univariate self = _self cdef long right = r + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if right != r: raise ValueError("exponent must be an integer") try: @@ -2124,7 +2146,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): Multivariate Polynomial Ring in t, tinv over Rational Field """ dres = {} - for (e, c) in self.dict().items(): + for e, c in self.monomial_coefficients().items(): if e > 0: dres[(e, 0)] = c else: diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index e6ea53e1756..17caf047da0 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -108,7 +108,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if isinstance(x, dict): self._mon = ETuple({}, int(parent.ngens())) D = {} - for k, x_k in x.iteritems(): # ETuple-ize keys, set _mon + for k, x_k in x.items(): # ETuple-ize keys, set _mon if not isinstance(k, (tuple, ETuple)) or len(k) != parent.ngens(): self._mon = ETuple({}, int(parent.ngens())) break @@ -119,7 +119,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): else: x = D if not self._mon.is_constant(): # factor out _mon - x = {k.esub(self._mon): x_k for k, x_k in x.iteritems()} + x = {k.esub(self._mon): x_k for k, x_k in x.items()} elif (isinstance(x, LaurentPolynomial_mpair) and parent.variable_names() == x.parent().variable_names()): self._mon = ( < LaurentPolynomial_mpair > x)._mon @@ -281,7 +281,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): # self._parent.variable_names(), # self._parent.base_ring() # ) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.monomial_coefficients() cdef ETuple e if i is None: @@ -309,12 +309,12 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: L. = LaurentPolynomialRing(QQ) sage: a = w^2*z^-1 +3 - sage: a.dict() # indirect doctest + sage: a.monomial_coefficients() # indirect doctest {(0, 0): 3, (2, -1): 1} """ # cdef dict D = self._poly._mpoly_dict_recursive(self._parent.variable_names(), # self._parent.base_ring()) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.monomial_coefficients() cdef dict DD if self._mon.is_constant(): self._prod = PolyDict(D) @@ -448,7 +448,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ cdef ETuple e if self._poly.is_term(): - (e, c), = self.dict().items() + (e, c), = self.monomial_coefficients().items() e = e.emul(-1) P = self._parent try: @@ -460,7 +460,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): return P({e: c}) return super().__invert__() - def __pow__(LaurentPolynomial_mpair self, n, m): + def __pow__(LaurentPolynomial_mpair self, n, mod): """ EXAMPLES:: @@ -480,8 +480,20 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: f = (x+y+z^-1)^2 sage: f.substitute(z=1) x^2 + 2*x*y + y^2 + 2*x + 2*y + 1 + + Check that using third argument raises an error:: + + sage: L. = LaurentPolynomialRing(R) + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef LaurentPolynomial_mpair ans + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if n < 0: return ~(self ** -n) ans = self._new_c() @@ -574,7 +586,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): Multivariate Polynomial Ring in t1, t1inv, t2, t2inv over Rational Field """ dres = {} - for (e, c) in self.dict().items(): + for e, c in self.monomial_coefficients().items(): exps = [] for t in e: if t > 0: @@ -742,7 +754,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): self._compute_polydict() if m._prod is None: m._compute_polydict() - return self._parent(self._prod.coefficient(m.dict())) + return self._parent(self._prod.coefficient(m.monomial_coefficients())) def coefficients(self): """ @@ -781,7 +793,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: f.variables(sort=False) #random (y, z, x) """ - cdef dict d = self.dict() + cdef dict d = self.monomial_coefficients() cdef tuple g = self._parent.gens() cdef Py_ssize_t nvars = len(g) cdef set vars = set() @@ -794,7 +806,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): v.sort() return tuple(v) - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Return ``self`` represented as a ``dict``. @@ -802,6 +814,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: L. = LaurentPolynomialRing(QQ) sage: f = 4*x^7*z^-1 + 3*x^3*y + 2*x^4*z^-2 + x^6*y^-7 + sage: sorted(f.monomial_coefficients().items()) + [((3, 1, 0), 3), ((4, 0, -2), 2), ((6, -7, 0), 1), ((7, 0, -1), 4)] + + ``dict`` is an alias:: + sage: sorted(f.dict().items()) [((3, 1, 0), 3), ((4, 0, -2), 2), ((6, -7, 0), 1), ((7, 0, -1), 4)] """ @@ -809,6 +826,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): self._compute_polydict() return < dict > self._prod.dict() + dict = monomial_coefficients + def _fraction_pair(self): """ Return one representation of ``self`` as a pair @@ -1558,11 +1577,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): except ValueError: # call _derivative() recursively on coefficients return P({m: c._derivative(var) - for (m, c) in self.dict().iteritems()}) + for m, c in self.monomial_coefficients().items()}) # compute formal derivative with respect to generator cdef dict d = {} - for m, c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): if m[index] != 0: new_m = [u for u in m] new_m[index] += -1 @@ -1643,7 +1662,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if R is None: R = LaurentPolynomialRing(self.base_ring(), x) - return R({m[i]: c for m, c in self.dict().iteritems()}) + return R({m[i]: c for m, c in self.monomial_coefficients().items()}) def monomial_reduction(self): """ @@ -1713,7 +1732,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): cdef list f = [] cdef dict d for t in pf: - d = (t[0].dict()) + d = (t[0].monomial_coefficients()) if len(d) == 1: # monomials are units u *= self.parent(d) ** t[1] else: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 680c4c0138c..40b09bbfb6e 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -388,7 +388,8 @@ def value(d, R): return {k: value(v, P.base_ring()) for k, v in D.items()} except (ValueError, TypeError): pass - return sum(P({k: 1}) * value(v, P) for k, v in D.items()).dict() + return sum(P({k: 1}) * value(v, P) + for k, v in D.items()).monomial_coefficients() def from_fraction_field(L, x): r""" @@ -444,6 +445,8 @@ def __init__(self, R): if R.ngens() != 1: raise ValueError("must be 1 generator") LaurentPolynomialRing_generic.__init__(self, R) + from sage.rings.integer_ring import IntegerRing + self._indices = IntegerRing() Element = LaurentPolynomial_univariate @@ -535,9 +538,9 @@ def _element_constructor_(self, x): P = x.parent() if set(self.variable_names()) & set(P.variable_names()): if isinstance(x, LaurentPolynomial_univariate): - d = {(k,): v for k, v in x.dict().items()} + d = {(k,): v for k, v in x.monomial_coefficients().items()} else: - d = x.dict() + d = x.monomial_coefficients() x = _split_laurent_polynomial_dict_(self, P, d) x = {k[0]: v for k, v in x.items()} elif P is self.base_ring(): @@ -545,7 +548,7 @@ def _element_constructor_(self, x): elif x.is_constant() and self.has_coerce_map_from(x.parent().base_ring()): return self(x.constant_coefficient()) elif len(self.variable_names()) == len(P.variable_names()): - x = x.dict() + x = x.monomial_coefficients() elif isinstance(x, FractionFieldElement): # since the field of fraction of self is defined corresponding to @@ -560,6 +563,12 @@ def _element_constructor_(self, x): return self.element_class(self, x) + def monomial(self, arg): + r""" + Return the monomial with the given exponent. + """ + return self.element_class(self, {arg: self.base_ring().one()}) + def __reduce__(self): """ Used in pickling. @@ -590,6 +599,9 @@ def __init__(self, R): if not R.base_ring().is_integral_domain(): raise ValueError("base ring must be an integral domain") LaurentPolynomialRing_generic.__init__(self, R) + from sage.modules.free_module import FreeModule + from sage.rings.integer_ring import IntegerRing + self._indices = FreeModule(IntegerRing(), R.ngens()) Element = LazyImport('sage.rings.polynomial.laurent_polynomial_mpair', 'LaurentPolynomial_mpair') @@ -604,7 +616,7 @@ def _repr_(self): """ return "Multivariate Laurent Polynomial Ring in %s over %s" % (", ".join(self._R.variable_names()), self._R.base_ring()) - def monomial(self, *args): + def monomial(self, *exponents): r""" Return the monomial whose exponents are given in argument. @@ -628,14 +640,27 @@ def monomial(self, *args): sage: L.monomial(1, 2, 3) # needs sage.modules Traceback (most recent call last): ... - TypeError: tuple key must have same length as ngens - """ - if len(args) != self.ngens(): - raise TypeError("tuple key must have same length as ngens") + TypeError: tuple key (1, 2, 3) must have same length as ngens (= 2) + + We also allow to specify the exponents in a single tuple:: + + sage: L.monomial((-1, 2)) # needs sage.modules + x0^-1*x1^2 + sage: L.monomial((-1, 2, 3)) # needs sage.modules + Traceback (most recent call last): + ... + TypeError: tuple key (-1, 2, 3) must have same length as ngens (= 2) + """ from sage.rings.polynomial.polydict import ETuple - m = ETuple(args, int(self.ngens())) - return self.element_class(self, self.polynomial_ring().one(), m) + if len(exponents) == 1 and isinstance((e := exponents[0]), (tuple, ETuple)): + exponents = e + + if len(exponents) != self.ngens(): + raise TypeError(f"tuple key {exponents} must have same length as ngens (= {self.ngens()})") + + m = ETuple(exponents, int(self.ngens())) + return self.element_class(self, self.polynomial_ring().base_ring().one(), m) def _element_constructor_(self, x, mon=None): """ @@ -762,9 +787,9 @@ def _element_constructor_(self, x, mon=None): pass elif set(self.variable_names()) & set(P.variable_names()): if isinstance(x, LaurentPolynomial_univariate): - d = {(k,): v for k, v in x.dict().items()} + d = {(k,): v for k, v in x.monomial_coefficients().items()} else: - d = x.dict() + d = x.monomial_coefficients() x = _split_laurent_polynomial_dict_(self, P, d) elif P is self.base_ring(): from sage.rings.polynomial.polydict import ETuple @@ -773,7 +798,7 @@ def _element_constructor_(self, x, mon=None): elif x.is_constant() and self.has_coerce_map_from(P.base_ring()): return self(x.constant_coefficient()) elif len(self.variable_names()) == len(P.variable_names()): - x = x.dict() + x = x.monomial_coefficients() elif isinstance(x, FractionFieldElement): # since the field of fraction of self is defined corresponding to diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py index 71eb20cd701..b4f4f13289e 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py @@ -42,6 +42,8 @@ class LaurentPolynomialRing_generic(CommutativeRing, Parent): sage: R. = LaurentPolynomialRing(QQ) sage: R.category() Join of Category of unique factorization domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index e116272db94..7c5246a2b36 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -243,7 +243,7 @@ cdef class MPolynomial(CommutativePolynomial): - Didier Deshommes """ - d = self.dict() + d = self.monomial_coefficients() return [d[i] for i in self.exponents()] def truncate(self, var, n): @@ -260,8 +260,8 @@ cdef class MPolynomial(CommutativePolynomial): ind = Z.index(var) except ValueError: raise ValueError("var must be one of the generators of the parent polynomial ring.") - d = self.dict() - return R(dict([(k, c) for k, c in d.iteritems() if k[ind] < n])) + return R({k: c for k, c in self.monomial_coefficients().items() + if k[ind] < n}) def _fast_callable_(self, etb): r""" @@ -299,9 +299,9 @@ cdef class MPolynomial(CommutativePolynomial): n = len(x) expr = etb.constant(self.base_ring().zero()) - for (m, c) in self.dict().iteritems(): - monom = prod([ x[i]**m[i] for i in range(n) if m[i] != 0], - etb.constant(c)) + for m, c in self.monomial_coefficients().items(): + monom = prod([x[i] ** m[i] for i in range(n) if m[i] != 0], + etb.constant(c)) expr = expr + monom return expr @@ -426,12 +426,12 @@ cdef class MPolynomial(CommutativePolynomial): d = self.degree(var) B = ring.base_ring() w = {remove_from_tuple(e, ind): val - for e, val in self.dict().iteritems() if not e[ind]} + for e, val in self.monomial_coefficients().items() if not e[ind]} v = [B(w)] # coefficients that don't involve var z = var for i in range(1,d+1): - c = self.coefficient(z).dict() - w = {remove_from_tuple(e, ind): val for e, val in c.iteritems()} + c = self.coefficient(z).monomial_coefficients() + w = {remove_from_tuple(e, ind): val for e, val in c.items()} v.append(B(w)) z *= var return ring(v) @@ -483,7 +483,7 @@ cdef class MPolynomial(CommutativePolynomial): vars = self._parent.variable_names_recursive() cdef tuple my_vars = self._parent.variable_names() if vars == my_vars: - return self.dict() + return self.monomial_coefficients() elif my_vars[-1] not in vars: x = base_ring(self) if base_ring is not None else self const_ix = ETuple((0,)*len(vars)) @@ -511,13 +511,13 @@ cdef class MPolynomial(CommutativePolynomial): new_map[k] -= m tmp = [0] * (len(vars) - m) try: - for ix,a in self.dict().iteritems(): + for ix, a in self.monomial_coefficients().items(): for k in range(len(my_vars)): tmp[new_map[k]] = ix[k] postfix = ETuple(tmp) mpoly = a._mpoly_dict_recursive(prev_vars, base_ring) - for prefix,b in mpoly.iteritems(): - D[prefix+postfix] = b + for prefix, b in mpoly.items(): + D[prefix + postfix] = b return D except AttributeError: @@ -527,7 +527,7 @@ cdef class MPolynomial(CommutativePolynomial): base_ring = None tmp = [0] * len(vars) - for ix,a in self.dict().iteritems(): + for ix, a in self.monomial_coefficients().items(): for k in range(len(my_vars)): tmp[mapping[k]] = ix[k] if base_ring is not None: @@ -578,7 +578,7 @@ cdef class MPolynomial(CommutativePolynomial): cdef long result_mon var_name_hash = [hash(v) for v in self._parent.variable_names()] cdef long c_hash - for m,c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): # I'm assuming (incorrectly) that hashes of zero indicate that the element is 0. # This assumption is not true, but I think it is true enough for the purposes and it # it allows us to write fast code that omits terms with 0 coefficients. This is @@ -913,7 +913,7 @@ cdef class MPolynomial(CommutativePolynomial): """ if isinstance(R, Map): return self.map_coefficients(R) - return self.parent().change_ring(R)(self.dict()) + return self.parent().change_ring(R)(self.monomial_coefficients()) def is_symmetric(self, group=None): r""" @@ -997,7 +997,7 @@ cdef class MPolynomial(CommutativePolynomial): raise ValueError("argument must be a permutation group") gens = [S(g) for g in gens] - cdef dict coeffs = self.dict() + cdef dict coeffs = self.monomial_coefficients() zero = self.base_ring().zero() return all(coeffs.get(g._act_on_etuple_on_position(e), zero) == coeff for e, coeff in coeffs.items() for g in gens) @@ -1360,7 +1360,7 @@ cdef class MPolynomial(CommutativePolynomial): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R(dict([(k,f(v)) for (k,v) in self.dict().items()])) + return R({k: f(v) for k, v in self.monomial_coefficients().items()}) def _norm_over_nonprime_finite_field(self): r""" @@ -2611,7 +2611,7 @@ cdef class MPolynomial(CommutativePolynomial): # K[x,y,...] as K[x][y]... if not self.constant_coefficient().is_unit(): return False - cdef dict d = self.dict() + cdef dict d = self.monomial_coefficients() cdef ETuple zero_key = ETuple({}, int(self.parent().ngens())) d.pop(zero_key, None) return all(d[k].is_nilpotent() for k in d) @@ -2646,7 +2646,7 @@ cdef class MPolynomial(CommutativePolynomial): # Section 7.3 Exercise 33). # This generalizes easily to the multivariate case, by considering # K[x,y,...] as K[x][y]... - d = self.dict() + d = self.monomial_coefficients() return all(c.is_nilpotent() for c in d.values()) def _test_subs(self, tester=None, **options): diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 65ba655b114..2f3310b3af9 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -74,7 +74,6 @@ from sage.rings.rational_field import QQ from sage.rings.fraction_field import FractionField - class MPolynomial_element(MPolynomial): r""" Generic multivariate polynomial. @@ -802,14 +801,27 @@ def monomial_coefficient(self, mon): zero = self.parent().base_ring().zero() return self.element().get(exp, zero) - def dict(self): + def monomial_coefficients(self): """ Return underlying dictionary with keys the exponents and values the coefficients of this polynomial. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = PolynomialRing(QQbar, order='lex') + sage: f = (x^1*y^5*z^2 + x^2*z + x^4*y^1*z^3) + sage: f.monomial_coefficients() + {(1, 5, 2): 1, (2, 0, 1): 1, (4, 1, 3): 1} + + ``dict`` is an alias:: + + sage: f.dict() # needs sage.rings.number_field + {(1, 5, 2): 1, (2, 0, 1): 1, (4, 1, 3): 1} """ return self.element().dict() - monomial_coefficients = dict + dict = monomial_coefficients def __iter__(self): """ @@ -1856,12 +1868,12 @@ def _floordiv_(self, right): """ # handle division by monomials without using Singular - if len(right.dict()) == 1: + if len(right.monomial_coefficients()) == 1: P = self.parent() ret = P(0) - denC,denM = next(iter(right)) - for c,m in self: - t = c*m + denC, denM = next(iter(right)) + for c, m in self: + t = c * m if denC.divides(c) and P.monomial_divides(denM, m): ret += P.monomial_quotient(t, right, coeff=True) return ret @@ -1917,7 +1929,8 @@ def _derivative(self, var=None): if index == -1: # var is not a generator; do term-by-term differentiation recursively # var may be, for example, a generator of the base ring - d = dict([(e, x._derivative(var)) for (e, x) in self.dict().items()]) + d = dict([(e, x._derivative(var)) + for e, x in self.monomial_coefficients().items()]) d = polydict.PolyDict(d, check=False) d.remove_zeros() return MPolynomial_polydict(P, d) @@ -2014,7 +2027,7 @@ def integral(self, var=None): # var is not a generator; do term-by-term integration recursively # var may be, for example, a generator of the base ring d = {e: x.integral(var) - for e, x in self.dict().items()} + for e, x in self.monomial_coefficients().items()} d = polydict.PolyDict(d, check=False) d.remove_zeros() else: diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index d177889bd6e..d05760d84cd 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -855,7 +855,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if element.parent() == self: bucket = sBucketCreate(_ring) try: - for (m,c) in element.element().dict().iteritems(): + for m, c in element.element().dict().items(): mon = p_Init(_ring) p_SetCoeff(mon, sa2si(c, _ring), _ring) for pos in m.nonzero_positions(): @@ -893,7 +893,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if element.parent().ngens() <= self.ngens(): bucket = sBucketCreate(_ring) try: - for (m,c) in element.element().dict().iteritems(): + for m, c in element.element().dict().items(): if check: c = base_ring(c) if not c: @@ -923,7 +923,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): else: bucket = sBucketCreate(_ring) try: - for (m,c) in element.iteritems(): + for m, c in element.items(): if check: c = base_ring(c) if not c: @@ -2088,10 +2088,11 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): except TypeError: # give up, evaluate functional sage_res = parent.base_ring().zero() - for m, c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): sage_res += c * mul([x[i] ** m[i] for i in m.nonzero_positions()]) else: - singular_polynomial_call(&res, self._poly, _ring, coerced_x, MPolynomial_libsingular_get_element) + singular_polynomial_call(&res, self._poly, _ring, coerced_x, + MPolynomial_libsingular_get_element) if res == NULL: return res_parent(0) @@ -2350,7 +2351,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): else: return (left._parent).fraction_field()(left,right_ringelement) - def __pow__(MPolynomial_libsingular self, exp, ignored): + def __pow__(MPolynomial_libsingular self, exp, mod): """ Return ``self**(exp)``. @@ -2413,7 +2414,20 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): Traceback (most recent call last): ... TypeError: R is neither an integer nor a rational + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(ZZ) + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + if type(exp) is not Integer: try: exp = Integer(exp) @@ -2983,7 +2997,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): return self._parent._base._zero_element - def dict(self): + def monomial_coefficients(self): """ Return a dictionary representing ``self``. This dictionary is in the same format as the generic MPolynomial: The dictionary @@ -2992,7 +3006,12 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): EXAMPLES:: sage: R. = QQ[] - sage: f=2*x*y^3*z^2 + 1/7*x^2 + 2/3 + sage: f = 2*x*y^3*z^2 + 1/7*x^2 + 2/3 + sage: f.monomial_coefficients() + {(0, 0, 0): 2/3, (1, 3, 2): 2, (2, 0, 0): 1/7} + + ``dict`` is an alias:: + sage: f.dict() {(0, 0, 0): 2/3, (1, 3, 2): 2, (2, 0, 0): 1/7} """ @@ -3016,6 +3035,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): p = pNext(p) return pd + dict = monomial_coefficients + def iterator_exp_coeff(self, as_ETuples=True): """ Iterate over ``self`` as pairs of ((E)Tuple, coefficient). @@ -3607,8 +3628,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): for m, v in items: gg[g.index(m)] = v y = parent.base_ring().zero() - for (m,c) in self.dict().items(): - y += c*mul([ gg[i]**m[i] for i in m.nonzero_positions()]) + for m, c in self.monomial_coefficients().items(): + y += c*mul([gg[i] ** m[i] for i in m.nonzero_positions()]) return y _f = ( v)._poly if p_IsConstant(_f, _ring): @@ -5222,7 +5243,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): sage: parent(h(R.0,0)) Univariate Polynomial Ring in x over Rational Field """ - return unpickle_MPolynomial_libsingular, (self._parent, self.dict()) + return unpickle_MPolynomial_libsingular, (self._parent, + self.monomial_coefficients()) def _im_gens_(self, codomain, im_gens, base_map=None): """ @@ -5263,8 +5285,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): if base_map is None: # Just use conversion base_map = codomain - for (m,c) in self.dict().iteritems(): - y += base_map(c)*mul([ im_gens[i]**m[i] for i in range(n) if m[i]]) + for m, c in self.monomial_coefficients().items(): + y += base_map(c) * mul([im_gens[i] ** m[i] for i in range(n) if m[i]]) return y def _derivative(self, MPolynomial_libsingular var): @@ -5391,7 +5413,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): v = ETuple({index: 1}, len(gens)) _p = p_ISet(0, _ring) - for (exp, coeff) in self.dict().iteritems(): + for exp, coeff in self.monomial_coefficients().items(): nexp = exp.eadd(v) # new exponent mon = p_Init(_ring) p_SetCoeff(mon, sa2si(coeff / (1 + exp[index]), _ring), _ring) @@ -5897,9 +5919,9 @@ def unpickle_MPolynomial_libsingular(MPolynomialRing_libsingular R, d): rChangeCurrRing(r) bucket = sBucketCreate(r) try: - for mon,c in d.iteritems(): + for mon, c in d.items(): m = p_Init(r) - for i,e in mon.sparse_iter(): + for i, e in mon.sparse_iter(): _i = i if _i >= r.N: p_Delete(&m, r) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 588358ab3d9..41cc1fb7c87 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -430,13 +430,16 @@ def __call__(self, x=0, check=True): if P is self: return x - elif P == self: + + if P == self: return MPolynomial_polydict(self, x.element().dict()) - elif self.base_ring().has_coerce_map_from(P): + + if self.base_ring().has_coerce_map_from(P): # it might be in the base ring (i.e. a poly ring over a poly ring) c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple: c}) - elif len(P.variable_names()) == len(self.variable_names()): + + if len(P.variable_names()) == len(self.variable_names()): # Map the variables in some crazy way (but in order, # of course). This is here since R(blah) is supposed # to be "make an element of R if at all possible with @@ -446,63 +449,76 @@ def __call__(self, x=0, check=True): for i, a in D.items(): D[i] = K(a) return MPolynomial_polydict(self, D) - elif set(P.variable_names()).issubset(set(self.variable_names())) and self.base_ring().has_coerce_map_from(P.base_ring()): + + if (set(P.variable_names()).issubset(set(self.variable_names())) + and self.base_ring().has_coerce_map_from(P.base_ring())): # If the named variables are a superset of the input, map the variables by name return MPolynomial_polydict(self, self._extract_polydict(x)) - else: - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) + + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) elif isinstance(x, MPolynomial_libsingular): P = x.parent() if P == self: - return MPolynomial_polydict(self, x.dict()) - elif self.base_ring().has_coerce_map_from(P): + return MPolynomial_polydict(self, x.monomial_coefficients()) + + if self.base_ring().has_coerce_map_from(P): # it might be in the base ring (i.e. a poly ring over a poly ring) c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple: c}) - elif len(P.variable_names()) == len(self.variable_names()): + + if len(P.variable_names()) == len(self.variable_names()): # Map the variables in some crazy way (but in order, # of course). This is here since R(blah) is supposed # to be "make an element of R if at all possible with # no guarantees that this is mathematically solid." K = self.base_ring() - D = x.dict() + D = x.monomial_coefficients() for i, a in D.items(): D[i] = K(a) return MPolynomial_polydict(self, D) - elif set(P.variable_names()).issubset(set(self.variable_names())) and self.base_ring().has_coerce_map_from(P.base_ring()): + + if (set(P.variable_names()).issubset(set(self.variable_names())) + and self.base_ring().has_coerce_map_from(P.base_ring())): # If the named variables are a superset of the input, map the variables by name return MPolynomial_polydict(self, self._extract_polydict(x)) - else: - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) - elif isinstance(x, polynomial_element.Polynomial): - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) - elif isinstance(x, PolyDict): + if isinstance(x, polynomial_element.Polynomial): + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) + + if isinstance(x, PolyDict): return MPolynomial_polydict(self, x) - elif isinstance(x, dict): + if isinstance(x, dict): K = self.base_ring() return MPolynomial_polydict(self, {i: K(a) for i, a in x.items()}) - elif isinstance(x, fraction_field_element.FractionFieldElement) and x.parent().ring() == self: + if (isinstance(x, fraction_field_element.FractionFieldElement) + and x.parent().ring() == self): if x.denominator() == 1: return x.numerator() - else: - raise TypeError("unable to coerce since the denominator is not 1") - elif isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: + raise TypeError("unable to coerce since the denominator is not 1") + + if isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: self._singular_().set_ring() try: return x.sage_poly(self) except TypeError: raise TypeError("unable to coerce singular object") - elif hasattr(x, '_polynomial_'): + if hasattr(x, '_polynomial_'): return x._polynomial_(self) - elif isinstance(x, str): + if isinstance(x, str): from sage.misc.sage_eval import sage_eval try: x = sage_eval(x, self.gens_dict_recursive()) @@ -510,7 +526,7 @@ def __call__(self, x=0, check=True): raise TypeError("unable to evaluate {!r} in {}".format(x, self)) return self(x) - elif isinstance(x, sage.interfaces.abc.Macaulay2Element): + if isinstance(x, sage.interfaces.abc.Macaulay2Element): try: s = x.sage_polystring() if len(s) == 0: @@ -524,7 +540,7 @@ def __call__(self, x=0, check=True): raise TypeError("Unable to coerce macaulay2 object") return MPolynomial_polydict(self, x) - elif isinstance(x, pari_gen) and x.type() == 't_POL': + if isinstance(x, pari_gen) and x.type() == 't_POL': # This recursive approach is needed because PARI # represents multivariate polynomials as iterated # univariate polynomials. Below, v is the variable @@ -538,9 +554,9 @@ def __call__(self, x=0, check=True): if isinstance(x, dict): return MPolynomial_polydict(self, x) - else: - c = self.base_ring()(x) - return MPolynomial_polydict(self, {self._zero_tuple: c}) + + c = self.base_ring()(x) + return MPolynomial_polydict(self, {self._zero_tuple: c}) # The following methods are handy for implementing Groebner # basis algorithms. They do only superficial type/sanity checks @@ -627,8 +643,8 @@ def monomial_quotient(self, f, g, coeff=False): if not g: raise ZeroDivisionError - fd = f.dict() - gd = g.dict() + fd = f.monomial_coefficients() + gd = g.monomial_coefficients() if not coeff: f = next(iter(fd)) @@ -682,8 +698,8 @@ def monomial_lcm(self, f, g): """ one = self.base_ring().one() - f, = f.dict() - g, = g.dict() + f, = f.monomial_coefficients() + g, = g.monomial_coefficients() length = len(f) @@ -785,8 +801,8 @@ def monomial_divides(self, a, b): if not a: raise ZeroDivisionError - a, = a.dict() - b, = b.dict() + a, = a.monomial_coefficients() + b, = b.monomial_coefficients() return all(b[i] >= a[i] for i in b.common_nonzero_positions(a)) @@ -879,7 +895,7 @@ def addwithcarry(tempvector, maxvector, pos): one = self.base_ring().one() M = list() - v, = t.dict() + v, = t.monomial_coefficients() maxvector = list(v) tempvector = [0] * len(maxvector) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd index 4ce9033dadb..322e25e363b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd @@ -4,6 +4,7 @@ from sage.structure.parent cimport Parent cdef class MPolynomialRing_base(CommutativeRing): cdef object _ngens cdef object _term_order + cdef public object _indices cdef public object _has_singular cdef public object _magma_gens cdef public dict _magma_cache diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 19a74c9d540..19c2ddac7b1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -26,7 +26,7 @@ from sage.rings.polynomial import polynomial_ring from sage.rings.polynomial.term_order import TermOrder from sage.rings.polynomial.polynomial_ring_constructor import (PolynomialRing, polynomial_default_category) -from sage.rings.polynomial.polydict import ETuple +from sage.rings.polynomial.polydict cimport ETuple def is_MPolynomialRing(x): @@ -99,6 +99,8 @@ cdef class MPolynomialRing_base(CommutativeRing): else: category = polynomial_default_category(base_ring.category(), n) Ring.__init__(self, base_ring, names, category=category) + from sage.combinat.integer_vector import IntegerVectors + self._indices = IntegerVectors(self._ngens) def is_integral_domain(self, proof=True): """ @@ -619,12 +621,14 @@ cdef class MPolynomialRing_base(CommutativeRing): """ # This is probably horribly inefficient other_vars = list(x.parent().variable_names()) - name_mapping = [(other_vars.index(var) if var in other_vars else -1) for var in self.variable_names()] + name_mapping = [(other_vars.index(var) if var in other_vars else -1) + for var in self.variable_names()] K = self.base_ring() D = {} var_range = range(len(self.variable_names())) - for ix, a in x.dict().iteritems(): - ix = ETuple([0 if name_mapping[t] == -1 else ix[name_mapping[t]] for t in var_range]) + for ix, a in x.monomial_coefficients().items(): + ix = ETuple([0 if name_mapping[t] == -1 else ix[name_mapping[t]] + for t in var_range]) D[ix] = K(a) return D diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index 20a61096548..797200f4e40 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -311,7 +311,7 @@ def MacMahonOmega(var, expression, denominator=None, op=operator.ge, decoded_factors = [] for factor in factors_denominator: factor = L(factor) - D = factor.dict() + D = factor.monomial_coefficients() if not D: raise ZeroDivisionError('Denominator contains a factor 0.') elif len(D) == 1: @@ -331,7 +331,7 @@ def MacMahonOmega(var, expression, denominator=None, op=operator.ge, numerator = L(numerator) / prod(to_numerator) result_numerator, result_factors_denominator = \ - _Omega_(numerator.dict(), decoded_factors) + _Omega_(numerator.monomial_coefficients(), decoded_factors) if result_numerator == 0: return Factorization([], unit=result_numerator) @@ -589,7 +589,7 @@ def subs_power(expression, var, exponent): It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ - p = tuple(var.dict().popitem()[0]).index(1) # var is the p-th generator + p = tuple(var.monomial_coefficients().popitem()[0]).index(1) # var is the p-th generator def subs_e(e): e = list(e) @@ -597,7 +597,8 @@ def subs_e(e): e[p] = e[p] // exponent return tuple(e) parent = expression.parent() - result = parent({subs_e(e): c for e, c in expression.dict().items()}) + result = parent({subs_e(e): c + for e, c in expression.monomial_coefficients().items()}) return result def de_power(expression): diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pxd b/src/sage/rings/polynomial/ore_polynomial_element.pxd index f38bcb0f0c5..771c4d6325a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pxd +++ b/src/sage/rings/polynomial/ore_polynomial_element.pxd @@ -17,7 +17,7 @@ cdef class OrePolynomial(AlgebraElement): cpdef bint is_zero(self) noexcept cpdef bint is_one(self) noexcept - + cdef _left_quo_rem(self, OrePolynomial other) cdef _right_quo_rem(self, OrePolynomial other) cdef OrePolynomial _left_lcm_cofactor(self, OrePolynomial other) @@ -38,7 +38,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef list _mul_list(self, list A) cpdef _mul_(self, other) - cpdef dict dict(self) + cpdef dict monomial_coefficients(self) cpdef list list(self, bint copy=*) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 8307bf9694a..768565840f6 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -2481,7 +2481,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): else: return (self)._coeffs - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): r""" Return a dictionary representation of ``self``. @@ -2491,6 +2491,11 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: a = x^2012 + t*x^1006 + t^3 + 2*t + sage: a.monomial_coefficients() + {0: t^3 + 2*t, 1006: t, 2012: 1} + + ``dict`` is an alias:: + sage: a.dict() {0: t^3 + 2*t, 1006: t, 2012: 1} """ @@ -2503,6 +2508,8 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): X[i] = c return X + dict = monomial_coefficients + cpdef Integer degree(self): r""" Return the degree of ``self``. diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 604764eff35..668f66fe574 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -51,7 +51,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, ab Check that :issue:`13620` has been fixed:: sage: f = R.zero() - sage: R(f.dict()) + sage: R(f.monomial_coefficients()) 0 Check that :issue:`29829` has been fixed:: diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index 5292050d6a7..9ec3a57bb4c 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -79,7 +79,7 @@ def eliminate(polys, on_the_fly=False, prot=False, reduction_function=None, lm = p.lex_lead() if lm.deg() == 1: - if not (lm in linear_leading_monomials): + if lm not in linear_leading_monomials: linear_leading_monomials.add(lm) linear_leads.append(p) else: diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index eb6b091c683..35de7feefe5 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -1403,7 +1403,7 @@ def unpickle_NCPolynomial_plural(NCPolynomialRing_plural R, d): cdef int _i, _e p = p_ISet(0,r) rChangeCurrRing(r) - for mon,c in d.iteritems(): + for mon, c in d.items(): m = p_Init(r) for i,e in mon.sparse_iter(): _i = i @@ -1671,7 +1671,7 @@ cdef class NCPolynomial_plural(RingElement): else: return (left._parent).fraction_field()(left,right) - def __pow__(NCPolynomial_plural self, exp, ignored): + def __pow__(NCPolynomial_plural self, exp, mod): """ Return ``self**(exp)``. @@ -1697,7 +1697,22 @@ cdef class NCPolynomial_plural(RingElement): Traceback (most recent call last): .... OverflowError: exponent overflow (2147483648) + + Check that using third argument raises an error:: + + sage: A. = FreeAlgebra(QQ, 3) + sage: P = A.g_algebra(relations={y*x:-x*y + z}, order='lex') + sage: P.inject_variables() + Defining x, z, y + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if type(exp) is not Integer: try: exp = Integer(exp) @@ -2203,6 +2218,9 @@ cdef class NCPolynomial_plural(RingElement): sage: f = (2*x*y^3*z^2 + (7)*x^2 + (3)) sage: f.dict() {(0, 0, 0): 3, (1, 2, 3): 2, (2, 0, 0): 7} + + sage: f.monomial_coefficients() + {(0, 0, 0): 3, (1, 2, 3): 2, (2, 0, 0): 7} """ cdef poly *p cdef ring *r diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 3ce764d6f01..2449b3208ae 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -76,7 +76,6 @@ from sage.arith.power cimport generic_power from sage.arith.misc import crt from sage.arith.long cimport pyobject_to_long from sage.misc.mrange import cartesian_product_iterator -from sage.misc.superseded import deprecated_function_alias from sage.structure.factorization import Factorization from sage.structure.richcmp cimport (richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn) @@ -174,7 +173,6 @@ cpdef is_Polynomial(f): sage: is_Polynomial(f) False """ - from sage.misc.superseded import deprecation deprecation(32709, "the function is_Polynomial is deprecated; use isinstance(x, sage.rings.polynomial.polynomial_element.Polynomial) instead") return isinstance(f, Polynomial) @@ -862,7 +860,7 @@ cdef class Polynomial(CommutativePolynomial): return R(pol) if pol._parent.is_sparse(): coeff_sparse = {} - coeff_dict = pol.dict() + coeff_dict = pol.monomial_coefficients() for i in coeff_dict: coeff_sparse[i * d] = coeff_dict[i] return R(coeff_sparse, check=False) @@ -883,7 +881,7 @@ cdef class Polynomial(CommutativePolynomial): if a.is_monomial(): etup = ETuple(( a).degrees()) if pol._parent.is_sparse(): - coeff_dict = pol.dict() + coeff_dict = pol.monomial_coefficients() return R({etup.emul(key): coeff_dict[key] for key in coeff_dict}) # Dense version return R({etup.emul(i): c for i, c in enumerate(pol)}) @@ -2648,7 +2646,6 @@ cdef class Polynomial(CommutativePolynomial): # 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_equal_deg`.") degree = -degree assume_equal_deg = True @@ -2931,7 +2928,7 @@ cdef class Polynomial(CommutativePolynomial): q = right sparse = self.parent().is_sparse() if sparse: - d = self.dict() + d = self.monomial_coefficients() else: c = self.list(copy=False) while q > 0: @@ -3883,7 +3880,7 @@ cdef class Polynomial(CommutativePolynomial): cdef dict D = {} cdef tuple leftovers = (0,) * (len(variables) - len(prev_variables) - 1) for k in range(len(mpolys)): - for i,a in mpolys[k].iteritems(): + for i, a in mpolys[k].items(): j = ETuple((k,) + leftovers) D[i + j] = a @@ -4464,7 +4461,7 @@ cdef class Polynomial(CommutativePolynomial): if self.get_unsafe(n) else zero for n in range(self.degree() + 1)] return S(p) - def dict(self): + def monomial_coefficients(self): """ Return a sparse dictionary representation of this univariate polynomial. @@ -4473,6 +4470,11 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = QQ[] sage: f = x^3 + -1/7*x + 13 + sage: f.monomial_coefficients() + {0: 13, 1: -1/7, 3: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 13, 1: -1/7, 3: 1} """ @@ -4485,7 +4487,7 @@ cdef class Polynomial(CommutativePolynomial): X[i] = c return X - monomial_coefficients = dict + dict = monomial_coefficients def factor(self, **kwargs): r""" @@ -10597,7 +10599,7 @@ cdef class Polynomial(CommutativePolynomial): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R({k: f(v) for k, v in self.dict().items()}) + return R({k: f(v) for k, v in self.monomial_coefficients().items()}) def is_cyclotomic(self, certificate=False, algorithm='pari'): r""" @@ -12685,11 +12687,11 @@ cpdef list _dict_to_list(dict x, zero): if isinstance(n, tuple): # a mpoly dict n = n[0] v = [zero] * (n+1) - for i, z in x.iteritems(): + for i, z in x.items(): v[i[0]] = z else: v = [zero] * (n+1) - for i, z in x.iteritems(): + for i, z in x.items(): v[i] = z return v diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 3e8a6b45ee1..c3df26200e2 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -39,16 +39,16 @@ except ImportError: pari_gen = () -from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn from sage.structure.element import coerce_binop, parent +from sage.structure.factorization import Factorization +from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn from sage.rings.infinity import infinity, Infinity from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer -from sage.structure.factorization import Factorization - from sage.rings.padics.precision_error import PrecisionError + class Polynomial_generic_sparse(Polynomial): """ A generic sparse polynomial. @@ -99,18 +99,18 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): R = parent.base_ring() if isinstance(x, Polynomial): if x.parent() == self.parent(): - x = dict(x.dict()) + x = x.monomial_coefficients() elif x.parent() == R: - x = {0:x} + x = {0: x} else: w = {} - for n, c in x.dict().items(): + for n, c in x.monomial_coefficients().items(): w[n] = R(c) # The following line has been added in github issue #9944. # Apparently, the "else" case has never occurred before. x = w elif isinstance(x, (list, tuple)): - x = dict((i, c) for (i, c) in enumerate(x) if c) + x = {i: c for i, c in enumerate(x) if c} elif isinstance(x, pari_gen): y = {} for i in range(len(x)): @@ -118,7 +118,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): x = y check = True elif not isinstance(x, dict): - x = {0:x} # constant polynomials + x = {0: x} # constant polynomials if check: self.__coeffs = {} for i, z in x.items(): @@ -128,7 +128,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): if check: self.__normalize() - def dict(self): + def monomial_coefficients(self): """ Return a new copy of the dict of the underlying elements of ``self``. @@ -138,14 +138,21 @@ def dict(self): sage: R. = PolynomialRing(Integers(8), sparse=True) sage: f = 5 + w^1997 - w^10000; f 7*w^10000 + w^1997 + 5 - sage: d = f.dict(); d + sage: d = f.monomial_coefficients(); d {0: 5, 1997: 1, 10000: 7} sage: d[0] = 10 + sage: f.monomial_coefficients() + {0: 5, 1997: 1, 10000: 7} + + ``dict`` is an alias:: + sage: f.dict() {0: 5, 1997: 1, 10000: 7} """ return dict(self.__coeffs) + dict = monomial_coefficients + def coefficients(self, sparse=True): """ Return the coefficients of the monomials appearing in ``self``. diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 8aa1d13b5c1..fc2ab9b584e 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -1035,7 +1035,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - def __pow__(Polynomial_integer_dense_flint self, exp, ignored): + def __pow__(Polynomial_integer_dense_flint self, exp, mod): """ EXAMPLES:: @@ -1114,10 +1114,23 @@ cdef class Polynomial_integer_dense_flint(Polynomial): ... TypeError: no canonical coercion from Univariate Polynomial Ring in R over Integer Ring to Rational Field + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(ZZ, implementation='FLINT') + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef long nn cdef Polynomial_integer_dense_flint res + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + try: nn = pyobject_to_long(exp) except TypeError: diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index df25358ff3b..ceaa9d7b2d5 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -1160,7 +1160,7 @@ cdef class Polynomial_rational_flint(Polynomial): if do_sig: sig_off() return res - def __pow__(Polynomial_rational_flint self, exp, ignored): + def __pow__(Polynomial_rational_flint self, exp, mod): """ Return ``self`` raised to the power of ``exp``. @@ -1266,10 +1266,23 @@ cdef class Polynomial_rational_flint(Polynomial): ... TypeError: no canonical coercion from Univariate Polynomial Ring in R over Rational Field to Rational Field + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(QQ) + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef Polynomial_rational_flint res cdef long n + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + try: n = pyobject_to_long(exp) except TypeError: diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 165c7fb72a4..2bda9f6f9c3 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -256,15 +256,25 @@ 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 algebras with basis over + (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets + and metric spaces) and Category of commutative algebras over (Dedekind domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) and Category of infinite sets + sage: category(GF(7)['x']) Join of Category of euclidean domains - and Category of commutative algebras over - (finite enumerated fields and subquotients of monoids and quotients of semigroups) and Category of infinite sets + and Category of algebras with basis over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of commutative algebras over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of infinite sets TESTS: @@ -314,6 +324,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, self.__cyclopoly_cache = {} self._has_singular = False Ring.__init__(self, base_ring, names=name, normalize=True, category=category) + from sage.rings.semirings.non_negative_integer_semiring import NonNegativeIntegerSemiring + self._indices = NonNegativeIntegerSemiring() self._populate_coercion_lists_(convert_method_name='_polynomial_') def __reduce__(self): diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index a135fe8de6c..5cbc74dd1d5 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -916,23 +916,30 @@ def polynomial_default_category(base_ring_category, n_variables): EXAMPLES:: sage: from sage.rings.polynomial.polynomial_ring_constructor import polynomial_default_category - sage: polynomial_default_category(Rings(),1) is Algebras(Rings()).Infinite() + sage: polynomial_default_category(Rings(), 1) + Category of infinite algebras with basis over rings + sage: polynomial_default_category(Rings().Commutative(), 1) + Category of infinite commutative algebras with basis + over commutative rings + sage: polynomial_default_category(Fields(), 1) + Join of Category of euclidean domains + and Category of algebras with basis over fields + and Category of commutative algebras over fields + and Category of infinite sets + sage: polynomial_default_category(Fields(), 2) + Join of Category of unique factorization domains + and Category of algebras with basis over fields + and Category of commutative algebras over fields + and Category of infinite sets + + sage: QQ['t'].category() is EuclideanDomains() & CommutativeAlgebras(QQ.category()).WithBasis().Infinite() True - sage: polynomial_default_category(Rings().Commutative(),1) is Algebras(Rings().Commutative()).Commutative().Infinite() + sage: QQ['s','t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ.category()).WithBasis().Infinite() True - sage: polynomial_default_category(Fields(),1) is EuclideanDomains() & Algebras(Fields()).Infinite() - True - sage: polynomial_default_category(Fields(),2) is UniqueFactorizationDomains() & CommutativeAlgebras(Fields()).Infinite() - True - - sage: QQ['t'].category() is EuclideanDomains() & CommutativeAlgebras(QQ.category()).Infinite() - True - sage: QQ['s','t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ.category()).Infinite() - True - sage: QQ['s']['t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ['s'].category()).Infinite() + sage: QQ['s']['t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ['s'].category()).WithBasis().Infinite() True """ - category = Algebras(base_ring_category) + category = Algebras(base_ring_category).WithBasis() if n_variables: # here we assume the base ring to be nonzero @@ -1034,4 +1041,4 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order='lex'): ############################################################################ # END (Factory function for making polynomial rings) -############################################################################ \ No newline at end of file +############################################################################ diff --git a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx index b3ce1183e5e..087e520dc23 100644 --- a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx +++ b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx @@ -58,7 +58,7 @@ cdef class PolynomialRingHomomorphism_from_base(RingHomomorphism_from_base): P = self.codomain() f = self.underlying_map() if P.is_sparse(): - return P({a: f(b) for a, b in x.dict().iteritems()}) + return P({a: f(b) for a, b in x.monomial_coefficients().items()}) else: return P([f(b) for b in x]) @@ -88,7 +88,8 @@ cdef class PolynomialRingHomomorphism_from_base(RingHomomorphism_from_base): P = self.codomain() f = self.underlying_map() if P.is_sparse(): - return P({a: f(b) for a, b in x.dict().iteritems()}, *args, **kwds) + return P({a: f(b) for a, b in x.monomial_coefficients().items()}, + *args, **kwds) else: return P([f(b) for b in x], *args, **kwds) diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 402e6831245..b76d62a9e92 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -371,7 +371,7 @@ cdef class Polynomial_template(Polynomial): sage: f.gcd(g) Traceback (most recent call last): ... - ValueError: non-invertible elements encountered during GCD + RuntimeError: FLINT gcd calculation failed """ if celement_is_zero(&self.x, (self)._cparent): return other diff --git a/src/sage/rings/power_series_mpoly.pyx b/src/sage/rings/power_series_mpoly.pyx index 42cf467912b..741b0ad94e7 100644 --- a/src/sage/rings/power_series_mpoly.pyx +++ b/src/sage/rings/power_series_mpoly.pyx @@ -58,11 +58,11 @@ cdef class PowerSeries_mpoly(PowerSeries): d = {} if isinstance(B, MPolynomialRing_base): for i in range(len(v)): - for n, c in v[i].dict().iteritems(): + for n, c in v[i].monomial_coefficients().items(): d[tuple(n) + (i,)] = c else: for i in range(len(v)): - for n, c in v[i].dict().iteritems(): + for n, c in v[i].monomial_coefficients().items(): d[(n,i)] = c self.__f = S(d) diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index 0e491cad78a..8f7ff769dd1 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -734,7 +734,7 @@ cdef class PowerSeries_pari(PowerSeries): else: return [R(g)] + [R.zero()] * (n - 1) - def dict(self): + def monomial_coefficients(self): """ Return a dictionary of coefficients for ``self``. @@ -746,10 +746,17 @@ cdef class PowerSeries_pari(PowerSeries): sage: R. = PowerSeriesRing(ZZ, implementation='pari') sage: f = 1 + t^10 + O(t^12) + sage: f.monomial_coefficients() + {0: 1, 10: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 1, 10: 1} """ - return self.polynomial().dict() + return self.polynomial().monomial_coefficients() + + dict = monomial_coefficients def _derivative(self, var=None): """ diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index 91f790ffa16..309d72c8f6a 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -794,7 +794,7 @@ cdef class PowerSeries_poly(PowerSeries): """ return self.__f.list() - def dict(self): + def monomial_coefficients(self): """ Return a dictionary of coefficients for ``self``. @@ -806,10 +806,17 @@ cdef class PowerSeries_poly(PowerSeries): sage: R. = ZZ[[]] sage: f = 1 + t^10 + O(t^12) + sage: f.monomial_coefficients() + {0: 1, 10: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 1, 10: 1} """ - return self.__f.dict() + return self.__f.monomial_coefficients() + + dict = monomial_coefficients def _derivative(self, var=None): """ diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 3ebcf680801..e6c932aadff 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -44,7 +44,8 @@ x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 + O(x^10) sage: R. = PowerSeriesRing(QQ, default_prec=15) sage: sin(x) - x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 - 1/39916800*x^11 + 1/6227020800*x^13 + O(x^15) + x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 - 1/39916800*x^11 + + 1/6227020800*x^13 + O(x^15) An iterated example:: @@ -139,8 +140,6 @@ from sage.misc.lazy_import import lazy_import from sage.rings import ( integer, - laurent_series_ring, - laurent_series_ring_element, power_series_mpoly, power_series_poly, power_series_ring_element, @@ -153,6 +152,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.category_object import normalize_names from sage.structure.element import Expression, parent +from sage.structure.parent import Parent from sage.structure.nonexact import Nonexact from sage.structure.unique_representation import UniqueRepresentation @@ -355,7 +355,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, * :func:`sage.misc.defaults.set_series_precision` """ - #multivariate case: + # multivariate case: # examples for first case: # PowerSeriesRing(QQ,'x,y,z') # PowerSeriesRing(QQ,['x','y','z']) @@ -364,7 +364,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, names = name if isinstance(names, (tuple, list)) and len(names) > 1 or (isinstance(names, str) and ',' in names): return _multi_variate(base_ring, num_gens=arg2, names=names, - order=order, default_prec=default_prec, sparse=sparse) + order=order, default_prec=default_prec, sparse=sparse) # examples for second case: # PowerSeriesRing(QQ,3,'t') if arg2 is None and num_gens is not None: @@ -373,7 +373,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, if (isinstance(arg2, str) and isinstance(names, (int, integer.Integer))): return _multi_variate(base_ring, num_gens=names, names=arg2, - order=order, default_prec=default_prec, sparse=sparse) + order=order, default_prec=default_prec, sparse=sparse) # univariate case: the arguments to PowerSeriesRing used to be # (base_ring, name=None, default_prec=20, names=None, sparse=False), @@ -385,11 +385,10 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, elif arg2 is not None: default_prec = arg2 - ## too many things (padics, elliptic curves) depend on this behavior, - ## so no warning for now. - ## + # ## too many things (padics, elliptic curves) depend on this behavior, + # ## so no warning for now. - # if isinstance(name, (int,integer.Integer)) or isinstance(arg2,(int,integer.Integer)): + # if isinstance(name, (int, integer.Integer)) or isinstance(arg2, (int, integer.Integer)): # deprecation(issue_number, "This behavior of PowerSeriesRing is being deprecated in favor of constructing multivariate power series rings. (See Github issue #1956.)") # the following is the original, univariate-only code @@ -427,8 +426,9 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, raise TypeError("base_ring must be a commutative ring") return R + def _multi_variate(base_ring, num_gens=None, names=None, - order='negdeglex', default_prec=None, sparse=False): + order='negdeglex', default_prec=None, sparse=False): """ Construct multivariate power series ring. """ @@ -436,7 +436,7 @@ def _multi_variate(base_ring, num_gens=None, names=None, raise TypeError("you must specify a variable name or names") if num_gens is None: - if isinstance(names,str): + if isinstance(names, str): num_gens = len(names.split(',')) elif isinstance(names, (list, tuple)): num_gens = len(names) @@ -458,6 +458,7 @@ def _multi_variate(base_ring, num_gens=None, names=None, def _single_variate(): pass + def is_PowerSeriesRing(R): """ Return ``True`` if this is a *univariate* power series ring. This is in @@ -489,7 +490,8 @@ def is_PowerSeriesRing(R): else: return False -class PowerSeriesRing_generic(UniqueRepresentation, ring.CommutativeRing, Nonexact): + +class PowerSeriesRing_generic(UniqueRepresentation, Parent, Nonexact): """ A power series ring. """ @@ -592,9 +594,9 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, else: raise ValueError('unknown power series implementation: %r' % implementation) - ring.CommutativeRing.__init__(self, base_ring, names=name, - category=getattr(self, '_default_category', - _CommutativeRings)) + Parent.__init__(self, base=base_ring, names=name, + category=getattr(self, '_default_category', + _CommutativeRings)) Nonexact.__init__(self, default_prec) if implementation == 'pari': self.__generator = self.element_class(self, R.gen().__pari__()) @@ -832,7 +834,7 @@ def _element_constructor_(self, f, prec=infinity, check=True): elif isinstance(f, LaurentSeries) and f.parent().power_series_ring() is self: return self(f.power_series(), prec, check=check) elif isinstance(f, MagmaElement) and str(f.Type()) == 'RngSerPowElt': - v = sage_eval(f.Eltseq()) + v = sage_eval(f.Eltseq()) # could use .sage() ? return self(v) * (self.gen(0)**f.Valuation()) elif isinstance(f, FractionFieldElement): if self.base_ring().has_coerce_map_from(f.parent()): @@ -846,7 +848,8 @@ def _element_constructor_(self, f, prec=infinity, check=True): if isinstance(f, SymbolicSeries): if str(f.default_variable()) == self.variable_name(): return self.element_class(self, f.list(), - f.degree(f.default_variable()), check=check) + f.degree(f.default_variable()), + check=check) else: raise TypeError("Can only convert series into ring with same variable name.") else: @@ -1097,6 +1100,18 @@ def gen(self, n=0): raise IndexError("generator n>0 not defined") return self.__generator + def gens(self) -> tuple: + """ + Return the generators of this ring. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(ZZ) + sage: R.gens() + (t,) + """ + return (self.__generator,) + def uniformizer(self): """ Return a uniformizer of this power series ring if it is @@ -1357,6 +1372,7 @@ def _get_action_(self, other, op, self_is_left): return BaseRingFloorDivAction(other, self, is_left=False) return super()._get_action_(other, op, self_is_left) + class PowerSeriesRing_over_field(PowerSeriesRing_domain): _default_category = CompleteDiscreteValuationRings() diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 34e84ddbc5f..b1ca6ea4ab1 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1045,7 +1045,7 @@ cdef class PowerSeries(AlgebraElement): else: n = int(n) v = {} - for k, x in self.dict().iteritems(): + for k, x in self.monomial_coefficients().items(): if k >= n: v[k-n] = x return self._parent(v, self.prec()-n) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 47365a9a8c6..300d64b8279 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -981,9 +981,9 @@ def _factor_multivariate_polynomial(self, f, proof=True): numfield_polynomial_flat = numfield.polynomial()(nf_gen) - polynomial_flat = sum(flat_ring({(0,)+tuple(k):1}) + polynomial_flat = sum(flat_ring({(0,) + tuple(k): 1}) * v.polynomial()(nf_gen) - for k,v in numfield_f.dict().items()) + for k, v in numfield_f.monomial_coefficients().items()) norm_flat = polynomial_flat.resultant(numfield_polynomial_flat, nf_gen) norm_f = norm_flat((0,)+norm_ring.gens()) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 59cf58e3513..d139c1449c5 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -155,6 +155,7 @@ cdef class Ring(ParentWithGens): Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass + running ._test_monomial_coefficients() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass @@ -184,20 +185,29 @@ cdef class Ring(ParentWithGens): Test against another bug fixed in :issue:`9944`:: sage: QQ['x'].category() - Join of Category of euclidean domains and Category of commutative algebras over - (number fields and quotient fields and metric spaces) and Category of infinite sets + Join of Category of euclidean domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) + and Category of commutative algebras + over (number fields and quotient fields and metric spaces) + and Category of infinite sets sage: QQ['x','y'].category() Join of Category of unique factorization domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets sage: PolynomialRing(MatrixSpace(QQ, 2),'x').category() # needs sage.modules - Category of infinite algebras over (finite dimensional algebras with basis over - (number fields and quotient fields and metric spaces) and infinite sets) + Category of infinite algebras with basis + over (finite dimensional algebras with basis + over (number fields and quotient fields and metric spaces) + and infinite sets) sage: PolynomialRing(SteenrodAlgebra(2),'x').category() # needs sage.combinat sage.modules - Category of infinite algebras over (super Hopf algebras with basis - over Finite Field of size 2 and supercocommutative super coalgebras - over Finite Field of size 2) + Category of infinite algebras with basis + over (super Hopf algebras with basis over Finite Field of size 2 + and supercocommutative super coalgebras + over Finite Field of size 2) TESTS:: diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 0fa89866a12..d8df667530b 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -401,7 +401,7 @@ def _repr_(self): sage: x + R(-1)*y + R(-3) 0*x + (-1)*y + (-3) """ - if not self.dict(): + if not self.monomial_coefficients(): return str(self.parent().base().zero()) s = super()._repr_() if self.monomials()[-1].is_constant(): @@ -425,7 +425,7 @@ def _latex_(self): sage: latex(R.zero()) \infty """ - if not self.dict(): + if not self.monomial_coefficients(): return self.parent().base().zero()._latex_() s = super()._latex_() if self.monomials()[-1].is_constant(): @@ -531,7 +531,7 @@ def _element_constructor_(self, x): raise ValueError(f"can not convert {x} to {self}") if isinstance(x, MPolynomial): if x.parent().variable_names() == self.variable_names(): - x = x.dict() + x = x.monomial_coefficients() else: raise ValueError(f"can not convert {x} to {self}") elif (x in self.base().base_ring()) or (x in self.base()): @@ -630,9 +630,8 @@ def random_element(self, degree=2, terms=None, choose_degree=False, R = PolynomialRing(self.base().base_ring(), self.variable_names()) f = R.random_element(degree=degree, terms=terms, choose_degree=choose_degree, *args, **kwargs) - new_dict = {} - for key, value in f.dict().items(): - new_dict[key] = self.base()(value) + new_dict = {key: self.base()(value) + for key, value in f.monomial_coefficients().items()} return self.element_class(self, new_dict) def gen(self, n=0): diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index b2d595e7e0a..74125633a22 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -207,7 +207,7 @@ def roots(self): """ from itertools import combinations tropical_roots = [] - data = self.dict() + data = self.monomial_coefficients() R = self.parent().base() if len(data) == 1: exponent = next(iter(data)) @@ -279,7 +279,7 @@ def split_form(self): """ roots = self.roots() R = self.parent() - poly = R(self.dict()[self.degree()].lift()) + poly = R(self.monomial_coefficients()[self.degree()].lift()) for root in roots: linear = R([root, 0]) poly *= linear @@ -321,7 +321,7 @@ def factor(self): (3) * 0 """ from sage.structure.factorization import Factorization - unit = self.dict()[self.degree()] + unit = self.monomial_coefficients()[self.degree()] if self != self.split_form() or not self.roots(): factor = [(self * self.parent(-unit.lift()), 1)] return Factorization(factor, unit=unit) @@ -374,7 +374,7 @@ def piecewise_function(self): from sage.sets.real_set import RealSet x = SR.var('x') - data = self.dict() + data = self.monomial_coefficients() R = self.parent().base() if not self.roots(): f = data[0].lift() @@ -537,7 +537,7 @@ def _repr_(self): (-1)*x^3 + 2*x^2 + (-1)*x + (-3) """ import re - if not self.dict(): + if not self.monomial_coefficients(): return str(self.parent().base().zero()) def replace_negatives(expr): @@ -844,7 +844,7 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self.base().base_ring(), self.variable_names()) f = R.random_element(degree=degree, monic=monic, *args, **kwds) - new_dict = f.dict() + new_dict = f.monomial_coefficients() if monic: new_dict[f.degree()] = 0 return self.element_class(self, new_dict) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index e65b2408904..3eb04372d13 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -201,7 +201,7 @@ def __init__(self, poly): # Convert each term to its linear function linear_eq = {} - pd = poly.dict() + pd = poly.monomial_coefficients() for key in pd: eq = sum(variables[i] * e for i, e in enumerate(key)) eq += pd[key].lift() diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 1c435a2ccec..022b5d1c880 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -1074,7 +1074,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): else: try: poly = parent._polynomial_ring(x) - self._poly = PolyDict(poly.dict(), None) + self._poly = PolyDict(poly.monomial_coefficients(), None) except TypeError: # last chance: we first try to convert to the rational Tate series if parent._integral: @@ -2192,7 +2192,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): """ return [ t.monomial() for t in self.terms() ] - def dict(self): + def monomial_coefficients(self): """ Return a dictionary whose keys are the exponents and whose values are the corresponding coefficients of this series. @@ -2202,12 +2202,19 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): sage: R = Zp(2, prec=10, print_mode='digits') sage: A. = TateAlgebra(R) sage: f = 2*x^2 + x + sage: f.monomial_coefficients() + {(1, 0): ...0000000001, (2, 0): ...00000000010} + + ``dict`` is an alias:: + sage: f.dict() {(1, 0): ...0000000001, (2, 0): ...00000000010} """ self._normalize() return dict(self._poly.__repn) + dict = monomial_coefficients + def coefficient(self, exponent): r""" Return the coefficient corresponding to the given exponent. @@ -2364,7 +2371,9 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): return elt.lift_to_precision(prec) except PrecisionError: return elt.lift_to_precision() - ans._poly = PolyDict({ e: lift_without_error(c) for (e,c) in self._poly.__repn.iteritems() }, None) + ans._poly = PolyDict({e: lift_without_error(c) + for e, c in self._poly.__repn.items()}, + None) if prec is None: prec = self._parent.precision_cap() ans._prec = max(self._prec, prec) diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index ff35d1166e9..b9f0229f787 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1390,7 +1390,7 @@ def lift(self, F, report_coefficients=False): F = self.residue_ring().coerce(F) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if F.is_constant(): @@ -1481,7 +1481,7 @@ def lift_to_key(self, F, check=True): F = self.residue_ring().coerce(F) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if check: diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 9646cf5e429..470e4990bed 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1375,7 +1375,7 @@ def minimal_representative(self, f): f = self.domain().coerce(f) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if f.is_zero(): diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d98209bdba1..0b647c8411b 100755 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -579,7 +579,7 @@ def multiplicity(self, P): TypeError: (=(1, 1)) is not a point on (=Affine Plane Curve over Rational Field defined by x^6 - x^3 + y^3) """ - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("curve must be defined over a field") # Check whether P is a point on this curve @@ -866,7 +866,7 @@ def __init__(self, A, X): """ super().__init__(A, X) - if not A.base_ring() in Fields(): + if A.base_ring() not in Fields(): raise TypeError("curve not defined over a field") d = super(Curve_generic, self).dimension() @@ -1320,7 +1320,7 @@ def blowup(self, P=None): self(P) except TypeError: raise TypeError("(=%s) must be a point on this curve" % P) - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("the base ring of this curve must be a field") if not self.is_irreducible(): raise TypeError("this curve must be irreducible") diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 7c8cf62c5ea..77c94b61c04 100755 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1105,7 +1105,7 @@ def quadratic_transform(self): for i in range(len(L)): degs[i] = min(F.degree(L[i]), degs[i]) T = [] - for item in G.dict().items(): + for item in G.monomial_coefficients().items(): tup = tuple([item[0][i] - degs[i] for i in range(len(L))]) T.append((tup, item[1])) G = R(dict(T)) @@ -1280,11 +1280,11 @@ def excellent_position(self, Q): if j == 0: div_pow = min(e[1] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0, v1 - div_pow, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.monomial_coefficients().items()}) else: div_pow = min(e[0] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0 - div_pow, v1, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.monomial_coefficients().items()}) # check the degree again if npoly.degree() != d - r: need_continue = True @@ -1466,7 +1466,7 @@ def extension(self): # make sure the defining polynomial variable names are the same for K, N N = NumberField(K.defining_polynomial().parent()(F.defining_polynomial()), str(K.gen())) return N.composite_fields(K, both_maps=True)[0][1]*F.embeddings(N)[0] - if not self.base_ring() in NumberFields(): + if self.base_ring() not in NumberFields(): raise NotImplementedError("the base ring of this curve must be a number field") if not self.is_irreducible(): raise TypeError("this curve must be irreducible") @@ -1499,7 +1499,7 @@ def extension(self): try: temp_pt = (temp_qua*temp_exc)(temp_exc.domain()(pts[i])) pts.pop(i) - if not PP(list(temp_pt)) in [PP(list(tpt)) for tpt in pts]: + if PP(list(temp_pt)) not in [PP(list(tpt)) for tpt in pts]: pts.append(temp_pt) except (TypeError, ValueError): pass @@ -1519,7 +1519,7 @@ def extension(self): newpts = [PP(list(pt) + [0]) for pt in X.rational_points()] # avoid duplicates for pt in newpts: - if not PP(list(pt)) in [PP(list(tpt)) for tpt in pts]: + if PP(list(pt)) not in [PP(list(tpt)) for tpt in pts]: pts.append(pt) return phi @@ -1604,7 +1604,7 @@ def __init__(self, A, X, category=None): """ super().__init__(A, X, category=category) - if not A.base_ring() in Fields(): + if A.base_ring() not in Fields(): raise TypeError("curve not defined over a field") d = super(Curve_generic, self).dimension() @@ -2296,7 +2296,7 @@ def __init__(self, A, f): ideal = self.defining_ideal() gs = self.ambient_space().gens() for i in range(self.ngens()): - if not gs[i] in ideal: + if gs[i] not in ideal: self._open_affine = self.affine_patch(i) self._open_affine_index = i break @@ -2733,7 +2733,7 @@ def places_on(self, point): # determine the affine patch where the point lies S = prime.ring() for i in range(S.ngens()): - if not S.gen(i) in prime: + if S.gen(i) not in prime: break phi = self._map_to_function_field diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 925d1d9f2d0..f410a73f8fc 100755 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -732,7 +732,7 @@ def roots_interval(f, x0): diam = min((CF(r) - CF(r0)).abs() for r0 in roots[:i] + roots[i + 1:]) / divisor envelop = IF(diam) * IF((-1, 1), (-1, 1)) - while not newton(fx, r, r + envelop) in r + envelop: + while newton(fx, r, r + envelop) not in r + envelop: prec += 53 IF = ComplexIntervalField(prec) CF = ComplexField(prec) @@ -1302,7 +1302,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): g = f.parent()(prod(glist)) d = g.degree(y) if not arrangement_v: # change of coordinates only if indices_v is empty - while not g.coefficient(y**d) in F: + while g.coefficient(y**d) not in F: g = g.subs({x: x + y}) d = g.degree(y) arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h) @@ -1752,7 +1752,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=True): x, y = g.parent().gens() F = g.parent().base_ring() d = g.degree(y) - while not g.coefficient(y**d) in F: + while g.coefficient(y**d) not in F: g = g.subs({x: x + y}) d = g.degree(y) if projective: diff --git a/src/sage/schemes/elliptic_curves/BSD.py b/src/sage/schemes/elliptic_curves/BSD.py index 25d21fbd1a2..7b64cd63883 100755 --- a/src/sage/schemes/elliptic_curves/BSD.py +++ b/src/sage/schemes/elliptic_curves/BSD.py @@ -95,6 +95,7 @@ def simon_two_descent_work(E, two_tor_rk): ... DeprecationWarning: Use E.rank(algorithm="pari") instead, as this script has been ported over to pari. See https://github.com/sagemath/sage/issues/35621 for details. + ... (0, 0, 0, 0, []) sage: E = EllipticCurve('37a') sage: simon_two_descent_work(E, E.two_torsion_rank()) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 65b49cfe1bd..50106558570 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2565,10 +2565,12 @@ def is_j_supersingular(j, proof=True): return E.trace_of_frobenius() % p == 0 -def special_supersingular_curve(F, *, endomorphism=False): +def special_supersingular_curve(F, q=None, *, endomorphism=False): r""" - Given a finite field ``F``, construct a "special" supersingular - elliptic curve `E` defined over ``F``. + Given a finite field ``F`` of characteristic `p`, and optionally + a positive integer `q` such that the Hilbert conductor of `-q` + and `-p` equals `p`, construct a "special" supersingular elliptic + curve `E` defined over ``F``. Such a curve @@ -2577,21 +2579,32 @@ def special_supersingular_curve(F, *, endomorphism=False): - has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and `E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`; - - has an endomorphism `\vartheta` of small degree `q` that + - has an endomorphism `\vartheta` of degree `q` that anticommutes with the `\mathbb F_p`-Frobenius on `E`. (The significance of `\vartheta` is that any such endomorphism, together with the `\mathbb F_p`-Frobenius, generates the endomorphism algebra `\mathrm{End}(E) \otimes \QQ`.) + The complexity grows exponentially in `\log(q)`. Automatically + chosen values of `q` lie in `O((\log p)^2)` assuming GRH. + INPUT: - - ``F`` -- finite field `\mathbb F_{p^r}`; + - ``F`` -- finite field `\mathbb F_{p^r}` + + - ``q`` -- positive integer (optional, default ``None``) - ``endomorphism`` -- boolean (default: ``False``); when set to ``True``, it is required that `2 \mid r`, and the function then additionally returns `\vartheta` + .. WARNING:: + + Due to :issue:`38481`, calling this function with a value of `q` + larger than approximately `p/4` may currently fail. This failure + will not occur for automatically chosen values of `q`. + EXAMPLES:: sage: special_supersingular_curve(GF(1013^2), endomorphism=True) @@ -2604,8 +2617,8 @@ def special_supersingular_curve(F, *, endomorphism=False): Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0)) sage: special_supersingular_curve(GF(1021^2), endomorphism=True) - (Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2, - Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2) + (Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2, + Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2) sage: special_supersingular_curve(GF(1031^2), endomorphism=True) (Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2, @@ -2630,6 +2643,20 @@ def special_supersingular_curve(F, *, endomorphism=False): Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2 Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0)) + We can also supply a suitable value of `q` ourselves:: + + sage: special_supersingular_curve(GF(1019), q=99) + Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019 + + sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True) + (Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2, + Isogeny of degree 99 from Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2 to Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2) + + sage: special_supersingular_curve(GF(1013), q=99) + Traceback (most recent call last): + ... + ValueError: invalid choice of q + TESTS:: sage: p = random_prime(1000) @@ -2678,6 +2705,35 @@ def special_supersingular_curve(F, *, endomorphism=False): sage: pi * endo == -endo * pi True + Also try it when `q` is given: + + sage: p = random_prime(300, lbound=10) + sage: k = ZZ(randrange(1, 5)) + sage: while True: + ....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481 + ....: if QuaternionAlgebra(-q, -p).discriminant() == p: + ....: break + sage: E = special_supersingular_curve(GF((p, k)), q) + sage: E.is_supersingular() + True + sage: F. = GF((p, 2*k)) + sage: E, endo = special_supersingular_curve(F, q, endomorphism=True) + sage: E.is_supersingular() + True + sage: E.j_invariant() in GF(p) + True + sage: endo.domain() is endo.codomain() is E + True + sage: endo.degree() == q + True + sage: endo.trace() + 0 + sage: pi = E.frobenius_isogeny() + sage: pi.codomain() is pi.domain() is E + True + sage: pi * endo == -endo * pi + True + .. NOTE:: This function makes no guarantees about the distribution of @@ -2694,42 +2750,49 @@ def special_supersingular_curve(F, *, endomorphism=False): if endomorphism and deg % 2: raise ValueError('endomorphism was requested but is not defined over given field') - E = None + if q is not None: + from sage.arith.misc import hilbert_conductor + if p.divides(q) or hilbert_conductor(-q, -p) != p: + raise ValueError('invalid choice of q') # first find the degree q of our special endomorphism - if p == 2: - q = 3 - E = EllipticCurve(F, [0,0,1,0,0]) - - elif p % 4 == 3: - q = 1 - E = EllipticCurve(F, [1,0]) - - elif p % 3 == 2: - q = 3 - E = EllipticCurve(F, [0,1]) - - elif p % 8 == 5: - q = 2 - E = EllipticCurve(F, [-4320, 96768]) - - else: - from sage.arith.misc import legendre_symbol - for q in map(ZZ, range(3,p,4)): - if not q.is_prime(): - continue - if legendre_symbol(-q, p) == -1: - break + if q is None: + if p == 2: + q = 3 + elif p % 4 == 3: + q = 1 + elif p % 3 == 2: + q = 3 + elif p % 8 == 5: + q = 2 else: - assert False # should never happen + from sage.arith.misc import legendre_symbol + for q in map(ZZ, range(3,p,4)): + if not q.is_prime(): + continue + if legendre_symbol(-q, p) == -1: + break + else: # should never happen + assert False, 'bug in special_supersingular_curve()' + q = ZZ(q) - if E is None: - from sage.arith.misc import fundamental_discriminant - from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial - H = hilbert_class_polynomial(fundamental_discriminant(-q)) - j = H.change_ring(GF(p)).any_root() + from sage.arith.misc import fundamental_discriminant + from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial + H = hilbert_class_polynomial(fundamental_discriminant(-q)) + j = H.change_ring(GF(p)).any_root() + if j.is_zero(): + if p == 2: + ainvs = [0,0,1,0,0] + elif p == 3: + ainvs = [1,0] + else: + ainvs = [0,1] + elif j == 1728: + ainvs = [1,0] + else: a = 27 * j / (4 * (1728-j)) - E = EllipticCurve(F, [a,-a]) + ainvs = [a,-a] + E = EllipticCurve(F, ainvs) if ZZ(2).divides(deg): k = deg//2 @@ -2740,23 +2803,26 @@ def special_supersingular_curve(F, *, endomorphism=False): if not endomorphism: return E - if q == 1 or p <= 13: - if q == 1: - endos = E.automorphisms() - else: - endos = (iso*phi for phi in E.isogenies_prime_degree(q) - for iso in phi.codomain().isomorphisms(E)) - endo = next(endo for endo in endos if endo.trace().is_zero()) - + if q.is_one(): + endo = next(auto for auto in E.automorphisms() if auto.trace().is_zero()) else: - from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - iso = WeierstrassIsomorphism(None, (F(-q).sqrt(),0,0,0), E) - if q == 3 and E.a_invariants() == (0,0,0,0,1): - # workaround for #21883 - endo = E.isogeny(E(0,1)) - else: - endo = E.isogeny(None, iso.domain(), degree=q) - endo = iso * endo + iso = E.isomorphism(F(-q).sqrt(), is_codomain=True) + try: + endo = iso * E.isogeny(None, iso.domain(), degree=q) + except (NotImplementedError, ValueError): #FIXME catching ValueError here is a workaround for #38481 + #FIXME this code could be simplified/optimized after #37388 and/or #35949 + def _isogs(E, d): + if d.is_one(): + yield E.identity_morphism() + return + l = d.prime_factors()[-1] + for phi in E.isogenies_prime_degree(l): + for psi in _isogs(phi.codomain(), d//l): + yield psi * phi + endos = (iso*phi for phi in _isogs(E, q) for iso in phi.codomain().isomorphisms(E)) +# endos = (iso*phi for phi in E.isogenies_degree(q) +# for iso in phi.codomain().isomorphisms(E)) + endo = next(endo for endo in endos if endo.trace().is_zero()) endo._degree = ZZ(q) endo.trace.set_cache(ZZ.zero()) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 8ccdbcd452d..eea2e5cc243 100755 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -250,15 +250,6 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, listpoints = [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7), 1]] (1, 1, [(1/2*a + 3/2 : -a - 2 : 1)]) - sage: v = E.simon_two_descent(verbose=2) - K = bnfinit(y^2 + 7); - a = Mod(y,K.pol); - bnfellrank(K, [0, 0, 0, 1, a], [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]); - ... - v = [1, 1, [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]] - sage: v - (1, 1, [(1/2*a + 3/2 : -a - 2 : 1)]) - A curve with 2-torsion:: sage: K. = NumberField(x^2 + 7) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 1b7a52b0ed0..0bc71b09d42 100755 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1837,6 +1837,10 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, ... DeprecationWarning: Use E.rank(algorithm="pari") instead, as this script has been ported over to pari. See https://github.com/sagemath/sage/issues/35621 for details. + doctest:warning + ... + DeprecationWarning: please use the 2-descent algorithm over QQ inside pari + See https://github.com/sagemath/sage/issues/38461 for details. (0, 0, []) sage: E = EllipticCurve('37a1') sage: E.simon_two_descent() @@ -1937,7 +1941,7 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, return rank_low_bd, two_selmer_rank, pts - two_descent_simon = simon_two_descent + two_descent_simon = simon_two_descent # deprecated in #35621 def three_selmer_rank(self, algorithm='UseSUnits'): r""" @@ -2388,21 +2392,9 @@ def _compute_gens(self, proof, TESTS:: sage: P = E.lift_x(611429153205013185025/9492121848205441) - sage: set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) <= set([P+T for T - ....: in E.torsion_points()] + [-P+T for T in E.torsion_points()]) - True - - sage: E = EllipticCurve([-157^2,0]) - sage: E.gens(use_database=False, algorithm='pari') - Traceback (most recent call last): - ... - RuntimeError: generators could not be determined. So far we found []. Hint: increase pari_effort. - sage: ge = E.gens(use_database=False, algorithm='pari',pari_effort=10) - sage: ge #random - [(-166136231668185267540804/2825630694251145858025 : 167661624456834335404812111469782006/150201095200135518108761470235125 : 1)] - sage: P = E.lift_x(-166136231668185267540804/2825630694251145858025) - sage: set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) <= set([P+T for T - ....: in E.torsion_points()] + [-P+T for T in E.torsion_points()]) + sage: ge = set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) + sage: ge <= set([P+T for T in E.torsion_points()] + ....: + [-P+T for T in E.torsion_points()]) True """ # If the optional extended database is installed and an @@ -6209,10 +6201,11 @@ def point_preprocessing(free,tor): e1,e2,e3 = ei if r >= 1: #preprocessing of mw_base only necessary if rank > 0 mw_base = point_preprocessing(mw_base, tors_points) - #at most one point in E^{egg} + # at most one point in E^{egg} - elif disc < 0: # one real component => 1 root in RR (=: e3), - # 2 roots in C (e1,e2) + elif disc < 0: + # one real component => 1 root in RR (=: e3), + # 2 roots in C (e1,e2) roots = pol.roots(C,multiplicities=False) e3 = pol.roots(R,multiplicities=False)[0] roots.remove(e3) @@ -6693,16 +6686,16 @@ def test_with_T(R): for T in tors_points: test(R+T) - # For small rank and small H_q perform simple search + # For small rank and small H_q perform simple search if r == 1 and N <= 10: for P in multiples(mw_base[0],N+1): test_with_T(P) return xs - # explicit computation and testing linear combinations - # ni loops through all tuples (n_1,...,n_r) with |n_i| <= N - # stops when (0,0,...,0) is reached because after that, only inverse points of - # previously tested points would be tested + # explicit computation and testing linear combinations + # ni loops through all tuples (n_1,...,n_r) with |n_i| <= N + # stops when (0,0,...,0) is reached because after that, only inverse points of + # previously tested points would be tested E0 = E(0) ni = [-N for i in range(r)] @@ -6855,13 +6848,14 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): prec *= 2 RR = RealField(prec) ei = pol.roots(RR,multiplicities=False) - e1,e2,e3 = ei - elif disc < 0: # one real component => 1 root in RR (=: e3), - # 2 roots in C (e1,e2) + e1, e2, e3 = ei + elif disc < 0: + # one real component => 1 root in RR (=: e3), + # 2 roots in C (e1,e2) roots = pol.roots(C,multiplicities=False) e3 = pol.roots(R,multiplicities=False)[0] roots.remove(e3) - e1,e2 = roots + e1, e2 = roots len_tors = len(tors_points) n = r + 1 @@ -6894,10 +6888,10 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): if verbose: print('k1,k2,k3,k4', k1, k2, k3, k4) sys.stdout.flush() - #H_q -> [PZGH]:N_0 (due to consistency to integral_points()) + # H_q -> [PZGH]:N_0 (due to consistency to integral_points()) H_q = R(((k1/2+k2)/lamda).sqrt()) - #computation of logs + # computation of logs mw_base_log = [(pts.elliptic_logarithm().abs())*(len_tors/w1) for pts in mw_base] mw_base_p_log = [] beta = [] @@ -6907,7 +6901,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): Np = E.Np(p) cp = E.tamagawa_exponent(p) mp_temp = Z(len_tors).lcm(cp*Np) - mp.append(mp_temp) #only necessary because of verbose below + mp.append(mp_temp) # only necessary because of verbose below p_prec = 30+E.discriminant().valuation(p) p_prec_ok = False while not p_prec_ok: @@ -6918,7 +6912,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): p_prec_ok = True except ValueError: p_prec *= 2 - #reorder mw_base_p: last value has minimal valuation at p + # reorder mw_base_p: last value has minimal valuation at p mw_base_p_log_val = [mw_base_p_log[tmp][i].valuation() for i in range(r)] if verbose: print("mw_base_p_log_val = ",mw_base_p_log_val) @@ -6928,7 +6922,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): print("min_psi = ", min_psi) mw_base_p_log[tmp].remove(min_psi) mw_base_p_log[tmp].append(min_psi) - #beta needed for reduction at p later on + # beta needed for reduction at p later on try: beta.append([-mw_base_p_log[tmp][j]/min_psi for j in range(r)]) except ValueError: @@ -6943,7 +6937,8 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): print('mw_base_p_log', mw_base_p_log) sys.stdout.flush() - #constants in reduction (not needed to be computed every reduction step) + # constants in reduction + # (not needed to be computed every reduction step) k5 = R((2*len_tors)/(3*w1)) k6 = R((k2/len_S).exp()) k7 = R(lamda/len_S) @@ -6954,20 +6949,20 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): break_cond = 0 M = MatrixSpace(Z,n) - #Reduction of initial bound + # Reduction of initial bound if verbose: print('initial bound', H_q) sys.stdout.flush() while break_cond < 0.9: - #reduction at infinity + # reduction at infinity bound_list = [] c = R((H_q**n)*100) m = copy(M.identity_matrix()) for i in range(r): m[i, r] = R(c*mw_base_log[i]).round() m[r,r] = max(Z(1), R(c*w1).round()) - #LLL - implemented in sage - operates on rows not on columns + # LLL - implemented in sage - operates on rows not on columns m_LLL = m.LLL() m_gram = m_LLL.gram_schmidt()[0] b1_norm = R(m_LLL.row(0).norm()) diff --git a/src/sage/schemes/elliptic_curves/gp_simon.py b/src/sage/schemes/elliptic_curves/gp_simon.py index 729380dccf1..6be377e2f74 100644 --- a/src/sage/schemes/elliptic_curves/gp_simon.py +++ b/src/sage/schemes/elliptic_curves/gp_simon.py @@ -16,33 +16,20 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from pathlib import Path -from sage.structure.parent_gens import localvars +from cypari2.handle_error import PariError -from sage.interfaces.gp import Gp -from sage.misc.sage_eval import sage_eval +from sage.env import SAGE_EXTCODE +from sage.libs.pari import pari from sage.misc.randstate import current_randstate +from sage.misc.superseded import deprecation from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ +from sage.structure.parent_gens import localvars -gp = None - - -def init(): - """ - Function to initialize the gp process - """ - global gp - if gp is None: - import os - from sage.env import DOT_SAGE - logfile = os.path.join(DOT_SAGE, 'gp-simon.log') - gp = Gp(script_subdirectory='simon', logfile=logfile) - gp.read("ellQ.gp") - gp.read("ell.gp") - gp.read("qfsolve.gp") - gp.read("resultant3.gp") +simon_dir = Path(SAGE_EXTCODE) / 'pari' / 'simon' def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, @@ -59,6 +46,9 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, sage: import sage.schemes.elliptic_curves.gp_simon sage: E = EllipticCurve('389a1') sage: sage.schemes.elliptic_curves.gp_simon.simon_two_descent(E) + doctest:warning...: + DeprecationWarning: please use the 2-descent algorithm over QQ inside pari + See https://github.com/sagemath/sage/issues/38461 for details. (2, 2, [(5/4 : 5/8 : 1), (-3/4 : 7/8 : 1)]) TESTS:: @@ -94,37 +84,38 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, sage: E.simon_two_descent() # long time (0, 2, []) """ - init() + pari.read(simon_dir / "ellQ.gp") + pari.read(simon_dir / "ell.gp") + pari.read(simon_dir / "qfsolve.gp") + pari.read(simon_dir / "resultant3.gp") - current_randstate().set_seed_gp(gp) + current_randstate().set_seed_pari() K = E.base_ring() K_orig = K + E_orig = E + # The following is to correct the bug at #5204: the gp script # fails when K is a number field whose generator is called 'x'. # It also deals with relative number fields. - E_orig = E + if K is not QQ: K = K_orig.absolute_field('a') + y = K.gen() from_K, to_K = K.structure() E = E_orig.change_ring(to_K) - known_points = [P.change_ring(to_K) for P in known_points] - # Simon's program requires that this name be y. with localvars(K.polynomial().parent(), 'y'): - gp.eval("K = bnfinit(%s);" % K.polynomial()) - if verbose >= 2: - print("K = bnfinit(%s);" % K.polynomial()) - gp.eval("%s = Mod(y,K.pol);" % K.gen()) - if verbose >= 2: - print("%s = Mod(y,K.pol);" % K.gen()) + # Simon's program requires that this name be y. + K_pari = pari.bnfinit(K.polynomial()) + known_points = [P.change_ring(to_K) for P in known_points] else: + deprecation(38461, "please use the 2-descent algorithm over QQ inside pari") from_K = lambda x: x - to_K = lambda x: x - # The block below mimics the defaults in Simon's scripts, and needs to be changed - # when these are updated. + # The block below mimics the defaults in Simon's scripts. + # They need to be changed when these are updated. if K is QQ: - cmd = 'ellQ_ellrank(%s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) + over_QQ = True if lim1 is None: lim1 = 5 if lim3 is None: @@ -132,7 +123,7 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, if limtriv is None: limtriv = 3 else: - cmd = 'bnfellrank(K, %s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) + over_QQ = False if lim1 is None: lim1 = 2 if lim3 is None: @@ -140,29 +131,21 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, if limtriv is None: limtriv = 2 - gp('DEBUGLEVEL_ell=%s; LIM1=%s; LIM3=%s; LIMTRIV=%s; MAXPROB=%s; LIMBIGPRIME=%s;' % ( - verbose, lim1, lim3, limtriv, maxprob, limbigprime)) - - if verbose >= 2: - print(cmd) - s = gp.eval('ans=%s;' % cmd) - if s.find(" *** ") != -1: - raise RuntimeError("\n%s\nAn error occurred while running Simon's 2-descent program" % s) - if verbose > 0: - print(s) - v = gp.eval('ans') - if v == 'ans': # then the call to ellQ_ellrank() or bnfellrank() failed - raise RuntimeError("An error occurred while running Simon's 2-descent program") - if verbose >= 2: - print("v = %s" % v) - - # pari represents field elements as Mod(poly, defining-poly) - # so this function will return the respective elements of K - def _gp_mod(*args): - return args[0] - ans = sage_eval(v, {'Mod': _gp_mod, 'y': K.gen(0)}) - lower = ZZ(ans[0]) - upper = ZZ(ans[1]) - points = [E_orig([from_K(c) for c in list(P)]) for P in ans[2]] + pari('DEBUGLEVEL_ell=%s; LIM1=%s; LIM3=%s; LIMTRIV=%s; MAXPROB=%s; LIMBIGPRIME=%s;' % ( + verbose, lim1, lim3, limtriv, maxprob, limbigprime)) + + try: + if over_QQ: + ans = pari("ellQ_ellrank")(E, known_points) + else: + ans = pari("bnfellrank")(K_pari, E, known_points) + except PariError as err: + raise RuntimeError("an error occurred while running Simon's 2-descent program") from err + + loc = {} if over_QQ else {'y': y} + lower, upper, pts = ans.sage(locals=loc) + lower = ZZ(lower) + upper = ZZ(upper) + points = [E_orig([from_K(c) for c in P]) for P in pts] points = [P for P in points if P.has_infinite_order()] return lower, upper, points diff --git a/src/sage/schemes/plane_conics/con_rational_function_field.py b/src/sage/schemes/plane_conics/con_rational_function_field.py index 6ac78706107..26522878918 100755 --- a/src/sage/schemes/plane_conics/con_rational_function_field.py +++ b/src/sage/schemes/plane_conics/con_rational_function_field.py @@ -503,12 +503,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(B): lastpoly = (lastpoly * t) % p - phi_p[A + 2 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + 2 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[A + B + 2] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 2] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(C): lastpoly = (lastpoly * t) % p - phi_p[A + B + 3 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 3 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) for (i, p) in enumerate(supports[1]): @@ -525,12 +525,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(C): lastpoly = (lastpoly * t) % p - phi_p[A + B + 3 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 3 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[0] = vector(F, d, lastpoly.dict()) + phi_p[0] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(A): lastpoly = (lastpoly * t) % p - phi_p[1 + n] = vector(F, d, lastpoly.dict()) + phi_p[1 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) for (i, p) in enumerate(supports[2]): @@ -547,12 +547,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(A): lastpoly = (lastpoly * t) % p - phi_p[1 + n] = vector(F, d, lastpoly.dict()) + phi_p[1 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[A + 1] = vector(F, d, lastpoly.dict()) + phi_p[A + 1] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(B): lastpoly = (lastpoly * t) % p - phi_p[A + 2 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + 2 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) if case == 0: diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0c3e2724229..701f6523065 100755 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -358,7 +358,7 @@ def differential_basis_baker(f): return None from sage.geometry.polyhedron.constructor import Polyhedron - D = {(k[0], k[1]): v for k, v in f.dict().items()} + D = {(k[0], k[1]): v for k, v in f.monomial_coefficients().items()} P = Polyhedron(D) kT = k["t"] # here we check the additional genericity conditions: that the polynomials @@ -3044,7 +3044,7 @@ def initialise(z, i): A = PolynomialRing(self._CC, "xyz") aes = [] for mp in mp_list: - d = mp.dict() + d = mp.monomial_coefficients() mp = sum( [ d[k] * CCzg.gen(0)**k[0] * CCzg.gen(1)**k[1] diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index 93a28beafe8..4c171595e6a 100755 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -407,7 +407,7 @@ def subtract(e, power): return tuple([l[0] - power] + l[1:]) def divide_by_x_n(p): - d_old = p.dict() + d_old = p.monomial_coefficients() power = min(e[0] for e in d_old) d_new = {subtract(exponent, power): coefficient for exponent, coefficient in d_old.items()} diff --git a/src/sage/schemes/toric/weierstrass_covering.py b/src/sage/schemes/toric/weierstrass_covering.py index 87d414d3464..82c300c1f7a 100755 --- a/src/sage/schemes/toric/weierstrass_covering.py +++ b/src/sage/schemes/toric/weierstrass_covering.py @@ -255,9 +255,9 @@ def homogenize(inhomog, degree): result = vector(ZZ, result) result.set_immutable() return result - X_dict = {homogenize(e, 2): v for e, v in X.dict().items()} - Y_dict = {homogenize(e, 3): v for e, v in Y.dict().items()} - Z_dict = {homogenize(e, 1): v for e, v in Z.dict().items()} + X_dict = {homogenize(e, 2): v for e, v in X.monomial_coefficients().items()} + Y_dict = {homogenize(e, 3): v for e, v in Y.monomial_coefficients().items()} + Z_dict = {homogenize(e, 1): v for e, v in Z.monomial_coefficients().items()} # shift to nonnegative exponents if necessary min_deg = [0] * R.ngens() for var in variables: diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index 7a832104295..ddd9a95a310 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -62,7 +62,10 @@ from sage.rings.integer cimport Integer from sage.structure.sage_object cimport SageObject from cpython.object cimport PyObject_RichCompare from sage.groups.perm_gps.partn_ref.data_structures cimport * -from sage.combinat.set_partition import SetPartition +from sage.misc.lazy_import import LazyImport + +SetPartition = LazyImport('sage.combinat.set_partition', 'SetPartition') + cpdef DisjointSet(arg): r""" diff --git a/src/sage/sets/image_set.py b/src/sage/sets/image_set.py index 8691edbcd01..a518dc58f93 100644 --- a/src/sage/sets/image_set.py +++ b/src/sage/sets/image_set.py @@ -25,7 +25,6 @@ from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity from sage.rings.integer import Integer -from sage.modules.free_module import FreeModule from sage.structure.element import Expression from sage.structure.parent import Parent @@ -91,6 +90,7 @@ def __init__(self, map, domain_subset, *, category=None, is_injective=None, inve if isinstance(map, Expression) and map.is_callable(): domain = map.parent().base() if len(map.arguments()) != 1: + from sage.modules.free_module import FreeModule domain = FreeModule(domain, len(map.arguments())) function = map diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 44c23ff8fb0..8e8940b5846 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1680,7 +1680,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): ....: ....: def _act_(self, g, a): ....: D = {} - ....: for k, v in a.dict().items(): + ....: for k, v in a.monomial_coefficients().items(): ....: nk = [0]*len(k) ....: for i in range(len(k)): ....: nk[g(i+1)-1] = k[i] diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index a8678861b91..3a7b7f553a6 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -213,11 +213,11 @@ def unify_arguments(self, x): temp = set() # Sorting remaining variables. for j in range(i, len(a)): - if not a[j] in temp: + if a[j] not in temp: temp.add(a[j]) for j in range(i, len(b)): - if not b[j] in temp: + if b[j] not in temp: temp.add(b[j]) new_list.extend(sorted(temp, key=repr)) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 1001cbda600..ca523ee9a95 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5846,12 +5846,12 @@ cdef class Expression(Expression_abc): if kwds: # Ensure that the keys are symbolic variables. - varkwds = {self._parent.var(k): v for k,v in kwds.iteritems()} + varkwds = {self._parent.var(k): v for k,v in kwds.items()} # Check for duplicate _dict_update_check_duplicate(sdict, varkwds) cdef GExMap smap - for k, v in sdict.iteritems(): + for k, v in sdict.items(): smap.insert(make_pair((self.coerce_in(k))._gobj, (self.coerce_in(v))._gobj)) res = self._gobj.subs_map(smap, 0) @@ -5999,7 +5999,7 @@ cdef class Expression(Expression_abc): if kwds: # Ensure that the keys are functions. - funkwds = {_find_func(k): v for k,v in kwds.iteritems()} + funkwds = {_find_func(k): v for k,v in kwds.items()} # Check for duplicate _dict_update_check_duplicate(sdict, funkwds) @@ -7503,7 +7503,7 @@ cdef class Expression(Expression_abc): [[-5, 0], [6, 2]] sage: g.polynomial(QQ).list() [-5, 0, 6] - sage: g.polynomial(QQ).dict() + sage: g.polynomial(QQ).monomial_coefficients() {0: -5, 2: 6} :: diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 968d0e7e7c3..5f647228564 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -285,7 +285,15 @@ def libgiac_integrator(expression, v, a=None, b=None): sage: (F.derivative(x) - f).simplify_trig() 0 """ - from sage.libs.giac import libgiac + try: + from sage.libs.giac import libgiac + except ImportError: + # If libgiac isn't available, return a symbolic answer + # (without actually integrating anything). This is essentially + # the failure case of any integration: see below for what we + # do if libgiac is *available* but unable to do much. + return expression.integrate(v, a, b, hold=True) + from sage.libs.giac.giac import Pygen # We call Pygen on first argument because otherwise some expressions # involving derivatives result in doctest failures in interfaces/sympy.py diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 5c7e5db192f..6bb2592a668 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -83,6 +83,10 @@ def __init__(self): # in the given order. This is an attribute of the class instead of # a global variable in this module to enable customization by # creating a subclasses which define a different set of integrators + # + # The libgiac integrator may immediately return a symbolic + # (unevaluated) answer if libgiac is unavailable. This essentially + # causes it to be skipped. self.integrators = [external.maxima_integrator, external.libgiac_integrator, external.sympy_integrator] diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 241b56a0ff6..54166cf4f1f 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -3707,7 +3707,7 @@ def stellar_subdivision(self, simplex, inplace=False, is_mutable=True): if inplace and self._is_immutable: raise ValueError("this simplicial complex is not mutable") - if not Simplex(simplex) in self: + if Simplex(simplex) not in self: raise ValueError("the face to subdivide is not a face of self") if inplace: diff --git a/src/sage/version.py b/src/sage/version.py index 635791563a7..b26f1f87f03 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.5.beta6' -date = '2024-09-29' -banner = 'SageMath version 10.5.beta6, Release Date: 2024-09-29' +version = '10.5.beta7' +date = '2024-10-12' +banner = 'SageMath version 10.5.beta7, Release Date: 2024-10-12' diff --git a/src/sage_docbuild/builders.py b/src/sage_docbuild/builders.py index 871cc4705a2..ab39d93c280 100644 --- a/src/sage_docbuild/builders.py +++ b/src/sage_docbuild/builders.py @@ -1054,7 +1054,7 @@ def get_modules(self, filename): try: tag_name = line[line.index('feature_') + 8:].strip() for feature in all_features(): - if tag_name == feature.name.replace('.', '_'): + if tag_name == feature.name.replace('.', '_') and feature.is_present(): break else: skip = True diff --git a/src/tox.ini b/src/tox.ini index 094c1f54806..42f976e070d 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -180,7 +180,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,E221,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,E275,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] @@ -291,7 +291,6 @@ passenv = RUFF_OUTPUT_FORMAT # 116 PLR0402 [*] Use `from matplotlib import cm` in lieu of alias # 111 PLW0603 [ ] Using the global statement to update `AA_0` is discouraged # 78 F841 [*] Local variable `B` is assigned to but never used -# 64 E713 [*] Test for membership should be `not in` # 48 PLW0602 [ ] Using global for `D` but no assignment is done # 33 PLR1711 [*] Useless `return` statement at end of function # 24 E714 [*] Test for object identity should be `is not` @@ -315,7 +314,7 @@ passenv = RUFF_OUTPUT_FORMAT # 1 F402 [ ] Import `factor` from line 259 shadowed by loop variable # 1 PLC0208 [*] Use a sequence type instead of a `set` when iterating over values # -commands = ruff check --ignore PLR2004,I001,F401,E741,F821,PLR0912,PLR0913,E402,PLR0915,PLW2901,PLR5501,PLR0911,E731,F405,PLR1714,PLR1736,F403,PLR0402,PLW0603,F841,E713,PLW0602,PLW0642,PLR1711,E714,SIM101,PLR1704,PLW3301,PLW1510,E721,PLW0211,PLW0120,F811,PLC2401,PLC0414,E743,PLE0101,PLR0124,PLW0127,F541,PLW1508,PLC3002,E742,PLE0302,PLW0129,F402,PLC0208 {posargs:{toxinidir}/sage/} +commands = ruff check --ignore PLR2004,I001,F401,E741,F821,PLR0912,PLR0913,E402,PLR0915,PLW2901,PLR5501,PLR0911,E731,F405,PLR1714,PLR1736,F403,PLR0402,PLW0603,F841,PLW0602,PLW0642,PLR1711,E714,SIM101,PLR1704,PLW3301,PLW1510,E721,PLW0211,PLW0120,F811,PLC2401,PLC0414,E743,PLE0101,PLR0124,PLW0127,F541,PLW1508,PLC3002,E742,PLE0302,PLW0129,F402,PLC0208 {posargs:{toxinidir}/sage/} [flake8] rst-roles =