diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index 40322ca86b9..2dc3ca61a41 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -51,7 +51,7 @@ diffParagraphs.forEach(paragraph => { EOF echo '' >> CHANGES.html echo '' >> CHANGES.html -(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- *.html) > diff.txt +(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- "*.html") > diff.txt python3 - << EOF import os, re, html with open('diff.txt', 'r') as f: diff --git a/.ci/docker-exec-script.sh b/.ci/docker-exec-script.sh new file mode 100755 index 00000000000..60bbd70aee7 --- /dev/null +++ b/.ci/docker-exec-script.sh @@ -0,0 +1,14 @@ +#!/bin/sh -x +if [ $# -lt 3 ]; then + echo >&2 "usage: docker-exec-script.sh CONTAINER WORKDIR [VAR=VALUE...] SCRIPT" + exit 1 +fi +CONTAINER=$1 +WORKDIR=$2 +shift 2 +(echo "cd \"$WORKDIR\""; + while [ $# -gt 1 ]; do + echo "export \"$1\"" + shift + done; + cat "$1") | docker exec -i $CONTAINER bash -ex diff --git a/.ci/merge-fixes.sh b/.ci/merge-fixes.sh index 13350018221..9d36e40d72b 100755 --- a/.ci/merge-fixes.sh +++ b/.ci/merge-fixes.sh @@ -1,5 +1,8 @@ #!/bin/sh -# Apply open PRs labeled "blocker" from sagemath/sage as patches. +# Apply open PRs labeled "p: CI Fix" from sagemath/sage as patches. +# (policy set by vote in 2024-03, +# https://groups.google.com/g/sage-devel/c/OKwwUGyKveo/m/vpyCXYBqAAAJ) +# # This script is invoked by various workflows in .github/workflows # # The repository variable SAGE_CI_FIXES_FROM_REPOS can be set @@ -20,15 +23,15 @@ for REPO in ${SAGE_CI_FIXES_FROM_REPOSITORIES:-sagemath/sage}; do echo "Nothing to do for 'none' in SAGE_CI_FIXES_FROM_REPOSITORIES" ;; */*) - echo "Getting open PRs with 'blocker' status from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+blocker+%2F+1%22" + echo "Getting open PRs with 'p: CI Fix' label from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+CI+Fix%22" GH="gh -R $REPO" REPO_FILE="upstream/ci-fixes-${REPO%%/*}-${REPO##*/}" - PRs="$($GH pr list --label "p: blocker / 1" --json number --jq '.[].number' | tee $REPO_FILE)" + PRs="$($GH pr list --label "p: CI Fix" --json number --jq '.[].number' | tee $REPO_FILE)" date -u +"%Y-%m-%dT%H:%M:%SZ" > $REPO_FILE.date # Record the date, for future reference if [ -z "$PRs" ]; then - echo "Nothing to do: Found no open PRs with 'blocker' status in $REPO." + echo "Nothing to do: Found no open PRs with 'p: CI Fix' label in $REPO." else - echo "Found open PRs with 'blocker' status in $REPO: $(echo $PRs)" + echo "Found open PRs with 'p: CI Fix' label in $REPO: $(echo $PRs)" git tag -f test_base git commit -q -m "Uncommitted changes" --no-allow-empty -a for a in $PRs; do diff --git a/build/bin/write-dockerfile.sh b/.ci/write-dockerfile.sh similarity index 79% rename from build/bin/write-dockerfile.sh rename to .ci/write-dockerfile.sh index 3c62d6082e4..0aa53414553 100755 --- a/build/bin/write-dockerfile.sh +++ b/.ci/write-dockerfile.sh @@ -27,13 +27,13 @@ for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} " fi done -echo "# Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh" +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" RUN=RUN cat < /dev/null; then \ + (yes | unminimize) || echo "(ignored)"; \ + rm -f "$(command -v unminimize)"; \ + fi EOF if [ -n "$DIST_UPGRADE" ]; then cat <> .gitignore && \ + ./.ci/retrofit-worktree.sh worktree-image /sage); \ + else \ + 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 -$ADD Makefile VERSION.txt COPYING.txt condarc.yml README.md bootstrap bootstrap-conda configure.ac sage .homebrew-build-env tox.ini Pipfile.m4 ./ -$ADD config/config.rpath config/config.rpath -$ADD src/doc/bootstrap src/doc/bootstrap -$ADD src/bin src/bin -$ADD src/Pipfile.m4 src/pyproject.toml.m4 src/requirements.txt.m4 src/setup.cfg.m4 src/VERSION.txt src/ -$ADD m4 ./m4 -$ADD pkgs pkgs -$ADD build ./build -$ADD .upstream.d ./.upstream.d -ARG BOOTSTRAP=./bootstrap + +ARG BOOTSTRAP=${BOOTSTRAP-./bootstrap} $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 -ARG EXTRA_CONFIGURE_ARGS="" +ARG EXTRA_CONFIGURE_ARGS="${CONFIGURE_ARGS}" EOF if [ ${WITH_SYSTEM_SPKG} = "force" ]; then cat <&1 | tee upstream/ci_fixes.log env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact - run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream - build: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:${{ github.event.inputs.docker_tag || 'dev'}} - needs: [get_ci_fixes] - steps: - - name: Checkout - id: checkout - uses: actions/checkout@v4 + # Building - - name: Update system packages - id: prepare + - name: Generate Dockerfile + # From docker.yml run: | - export PATH="build/bin:$PATH" - eval $(sage-print-system-package-command auto update) - eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git) + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Add prebuilt tree as a worktree - id: worktree - run: | - set -ex - git config --global --add safe.directory $(pwd) - .ci/retrofit-worktree.sh worktree-image /sage + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Download upstream artifact - uses: actions/download-artifact@v3 + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 with: - path: upstream - name: upstream + # push and load may not be set together at the moment + # + # We are using "push" (to the local registry) because it was + # more reliable than "load", for which we observed random failure + # conditions in which the built image could not be found. + # + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. + - name: Start container run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh - - name: Incremental build - id: incremental + # Testing + + - name: Check that all modules can be imported run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + # Increase the length of the lines in the "short summary" + export COLUMNS=120 + # The following command checks that all modules can be imported. + # The output also includes a long list of modules together with the number of tests in each module. + # This can be ignored. + ./sage -python -m pip install pytest-xdist + ./sage -python -m pytest -c tox.ini -qq --doctest --collect-only || true + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Build modularized distributions - if: (success() || failure()) && steps.worktree.outcome == 'success' - run: make V=0 tox && make SAGE_CHECK=no pypi-wheels - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + - name: Test changed files (sage -t --new) + run: | + export MAKE="make -j2 --output-sync=recurse" SAGE_NUM_THREADS=4 + # We run tests with "sage -t --new"; this only tests the uncommitted changes. + ./sage -t --new -p4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Static code check with pyright - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 - with: - version: 1.1.332 - # Many warnings issued by pyright are not yet helpful because there is not yet enough type information. - no-comments: true - working-directory: ./worktree-image - env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 - - - name: Static code check with pyright (annotated) - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 + test-mod: + runs-on: ubuntu-latest + needs: [test-new] + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 + strategy: + fail-fast: false + matrix: + targets: + - sagemath_categories-check + steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 with: - version: 1.1.332 - # Issue errors - no-comments: false - level: error - working-directory: ./worktree-image + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + 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 + # so that multi-stage builds can work correctly. + run: | + .ci/merge-fixes.sh env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 + GH_TOKEN: ${{ github.token }} + + # Building - - name: Clean (fallback to non-incremental) - id: clean - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' + - name: Generate Dockerfile + # From docker.yml run: | - set -ex - ./bootstrap && make doc-clean doc-uninstall sagelib-clean && git clean -fx src/sage && ./config.status - working-directory: ./worktree-image + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . env: - MAKE: make -j4 - SAGE_NUM_THREADS: 4 + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Build - # This step is needed because building the modularized distributions installs some optional packages, - # so the editable install of sagelib needs to build the corresponding optional extension modules. - id: build - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.clean.outcome == 'success') + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback + + - name: Start container run: | - make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh # Testing - - name: Test changed files (sage -t --new) - if: (success() || failure()) && steps.build.outcome == 'success' + - name: Test modularized distributions run: | - # We run tests with "sage -t --new"; this only tests the uncommitted changes. - ./sage -t --new -p2 - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + export MAKE="make -j2 --output-sync=recurse" SAGE_NUM_THREADS=4 + make V=0 tox-ensure && make ${{ matrix.targets }} + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Test modularized distributions - if: (success() || failure()) && steps.build.outcome == 'success' - run: make V=0 tox && make pypi-wheels-check - working-directory: ./worktree-image + test-long: + runs-on: ubuntu-latest + needs: [test-new] + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 + steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + 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 + # so that multi-stage builds can work correctly. + run: | + .ci/merge-fixes.sh env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + GH_TOKEN: ${{ github.token }} - - name: Check that all modules can be imported + # Building + + - name: Generate Dockerfile + # From docker.yml run: | - # The following command checks that all modules can be imported. - # The output also includes a long list of modules together with the number of tests in each module. - # This can be ignored. - ../sage -python -m pip install pytest-xdist - ../sage -python -m pytest -c tox.ini -qq --doctest --collect-only || true - working-directory: ./worktree-image/src + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . env: - # Increase the length of the lines in the "short summary" - COLUMNS: 120 + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Pytest - if: contains(github.ref, 'pytest') + - name: Start container + id: container run: | - ../sage -python -m pip install coverage pytest-xdist - ../sage -python -m coverage run -m pytest -c tox.ini --doctest || true - working-directory: ./worktree-image/src - env: - # Increase the length of the lines in the "short summary" - COLUMNS: 120 + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh + + # Testing - name: Test all files (sage -t --all --long) - if: (success() || failure()) && steps.build.outcome == 'success' run: | ./sage -python -m pip install coverage - ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p2 --format github --random-seed=286735480429121101562228604801325644303 - working-directory: ./worktree-image + ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p4 --format github --random-seed=286735480429121101562228604801325644303 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Prepare coverage results - if: (success() || failure()) && steps.build.outcome == 'success' + - name: Copy coverage results + if: (success() || failure()) && steps.container.outcome == 'success' run: | ./sage -python -m coverage combine --rcfile=src/tox.ini ./sage -python -m coverage xml --rcfile=src/tox.ini mkdir -p coverage-report mv coverage.xml coverage-report/ - working-directory: ./worktree-image + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Upload coverage to codecov - if: (success() || failure()) && steps.build.outcome == 'success' - uses: codecov/codecov-action@v3 + if: (success() || failure()) && steps.container.outcome == 'success' + uses: codecov/codecov-action@v4 with: - directory: ./worktree-image/coverage-report + directory: ./coverage-report diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml index fd1c6442a79..8b8e8e88a16 100644 --- a/.github/workflows/ci-conda.yml +++ b/.github/workflows/ci-conda.yml @@ -61,7 +61,7 @@ jobs: ${{ runner.os }}-conda-${{ hashFiles('src/environment-3.11.yml') }} - name: Setup Conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v3 with: python-version: ${{ matrix.python }} miniforge-version: latest diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 82b14472c55..88a31a5ce42 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -19,7 +19,12 @@ on: pull_request: paths: - 'build/pkgs/**' - - 'pkgs/**' + - '!build/pkgs/sage_conf/**' + - '!build/pkgs/sage_docbuild/**' + - '!build/pkgs/sage_setup/**' + - '!build/pkgs/sage_sws2rst/**' + - '!build/pkgs/sagelib/**' + - '!build/pkgs/sagemath_*/**' workflow_dispatch: concurrency: @@ -42,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - name: Get all packages that have changed id: changed-packages - uses: tj-actions/changed-files@v42 + uses: tj-actions/changed-files@v44 with: files_yaml: | configures: diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 4726969a45f..7e3f6b6ad62 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -37,17 +37,38 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) - name: make dist (--disable-download-from-upstream-url) + id: make_dist run: | ./bootstrap -D && ./configure --disable-download-from-upstream-url && make dist env: MAKE: make -j8 + - name: make download (--disable-download-from-upstream-url) + id: make_download + run: | + make -k download DOWNLOAD_PACKAGES=":all: --no-file huge" + env: + MAKE: make -j8 + - name: Reconfigure with --enable-download-from-upstream-url + if: (success() || failure()) && (steps.make_dist.outcome != 'success' || steps.make_download.outcome != 'success') + run: | + ./configure - name: make dist (--enable-download-from-upstream-url) - if: failure() + if: (success() || failure()) && steps.make_dist.outcome != 'success' run: | - ./configure && make dist + make dist env: MAKE: make -j8 - - uses: actions/upload-artifact@v3 + - name: make download (--enable-download-from-upstream-url) + if: (success() || failure()) && steps.make_download.outcome != 'success' + run: | + make -k download DOWNLOAD_PACKAGES=":all: --no-file huge --allow-upstream" + env: + MAKE: make -j8 + - name: Remove what cannot be distributed + if: success() || failure() + run: | + rm -f upstream/*do-not-distribute* + - uses: actions/upload-artifact@v4 if: success() || failure() with: path: | @@ -61,10 +82,10 @@ jobs: runs-on: ubuntu-latest if: (success() || failure()) && github.repository == 'sagemath/sage' && startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: release_dist - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 with: generate_release_notes: true files: | @@ -91,7 +112,7 @@ jobs: make pypi-sdists V=0 (mkdir dist && mv upstream/sage*.tar.gz dist/) ls -l dist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: "dist/*.tar.gz" name: dist @@ -103,6 +124,36 @@ jobs: verbose: true if: env.CAN_DEPLOY == 'true' + noarch_wheels_for_pypi: + + runs-on: ubuntu-latest + env: + CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} + steps: + - uses: actions/checkout@v4 + - name: Install bootstrap prerequisites + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) + - name: make pypi-noarch-wheels + run: | + ./bootstrap + ./configure + make pypi-noarch-wheels V=0 + (mkdir dist && mv venv/var/lib/sage/wheels/sage*-none-any.whl dist/) + ls -l dist + - uses: actions/upload-artifact@v4 + with: + path: "dist/*.whl" + name: noarch-wheels + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.SAGEMATH_PYPI_API_TOKEN }} + skip-existing: true + verbose: true + if: env.CAN_DEPLOY == 'true' + build_wheels: name: Build wheels on ${{ matrix.os }}, arch ${{ matrix.arch }} runs-on: ${{ matrix.os }} @@ -120,6 +171,7 @@ jobs: - os: macos-14 arch: arm64 env: + CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} # SPKGs to install as system packages SPKGS: _bootstrap _prereq # Non-Python packages to install as spkgs @@ -138,7 +190,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: dist path: dist @@ -170,23 +222,11 @@ jobs: "${{ steps.python.outputs.python-path }}" -m pipx run cibuildwheel==2.17.0 unpacked/$pkg* done - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: wheels + name: ${{ matrix.os }}-${{ matrix.arch }}-wheels path: ./wheelhouse/*.whl - upload_wheels: - needs: build_wheels - runs-on: ubuntu-latest - env: - CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} - steps: - - - uses: actions/download-artifact@v3 - with: - name: wheels - path: wheelhouse - - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml index ee90f3a42fc..d4cc6ebc6ca 100644 --- a/.github/workflows/doc-build-pdf.yml +++ b/.github/workflows/doc-build-pdf.yml @@ -20,13 +20,44 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Same as in build.yml + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" + FROM_DOCKER_TARGET: "with-targets" + FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + jobs: - get_ci_fixes: + build-docs-pdf: runs-on: ubuntu-latest + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h - name: Merge CI fixes from sagemath/sage run: | mkdir -p upstream @@ -34,82 +65,64 @@ jobs: env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact - run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream - build-docs-pdf: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:${{ github.event.inputs.docker_tag || 'dev'}} - needs: [get_ci_fixes] - steps: - - name: Checkout - uses: actions/checkout@v4 + # Building - - name: Update system packages + - name: Generate Dockerfile + # From docker.yml run: | - export PATH="build/bin:$PATH" - eval $(sage-print-system-package-command auto update) - eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) - eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy) + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Add prebuilt tree as a worktree - id: worktree - run: | - git config --global --add safe.directory $(pwd) - git config --global user.email "ci-sage@example.com" - git config --global user.name "Build & Test workflow" - .ci/retrofit-worktree.sh worktree-image /sage + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Download upstream artifact - uses: actions/download-artifact@v3 + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 with: - path: upstream - name: upstream + # push and load may not be set together at the moment + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. + - name: Start container run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh - - name: Incremental build - id: incremental - run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + # Docs - - name: Build (fallback to non-incremental) - id: build - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' + - name: Update system packages run: | - set -ex - make sagelib-clean && git clean -fx src/sage && ./config.status && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + export PATH="build/bin:$PATH" + eval $(sage-print-system-package-command auto update) + eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) + eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy) + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Build docs (PDF) id: docbuild - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.build.outcome == 'success') run: | + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 make doc-clean doc-uninstall; make sagemath_doc_html-build-deps sagemath_doc_pdf-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Copy docs id: copy @@ -121,6 +134,7 @@ jobs: cp -r -L /sage/local/share/doc/sage/pdf ./docs # Zip everything for increased performance zip -r docs-pdf.zip docs + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Upload docs if: (success() || failure()) && steps.copy.outcome == 'success' diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index b3c275d8e0a..2666a393f66 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -9,19 +9,59 @@ on: - develop workflow_dispatch: # Allow to run manually + inputs: + platform: + description: 'Platform' + required: true + default: 'ubuntu-jammy-standard' + docker_tag: + description: 'Docker tag' + required: true + default: 'dev' concurrency: # Cancel previous runs of this workflow for the same branch group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Same as in build.yml + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" + FROM_DOCKER_TARGET: "with-targets" + FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + jobs: - get_ci_fixes: + build-docs: runs-on: ubuntu-latest + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h - name: Merge CI fixes from sagemath/sage run: | mkdir -p upstream @@ -29,120 +69,101 @@ jobs: env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact + + # Building + + - name: Generate Dockerfile + # From docker.yml run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - build-docs: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-ubuntu-jammy-standard-with-targets:dev - needs: [get_ci_fixes] - steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Update system packages + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + # push and load may not be set together at the moment + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback + + - name: Start container run: | - apt-get update && apt-get install -y git zip + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh + + # Docs - - name: Add prebuilt tree as a worktree + - name: Store old docs id: worktree run: | git config --global --add safe.directory $(pwd) git config --global user.email "ci-sage@example.com" - git config --global user.name "Build & Test workflow" - # mathjax path in old doc - mathjax_path_from=$(SAGE_USE_CDNS=no /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") - .ci/retrofit-worktree.sh worktree-image /sage + git config --global user.name "Build documentation workflow" + # mathjax path in old doc (regex) + mathjax_path_from="[-./A-Za-z_]*/tex-chtml[.]js?v=[0-9a-f]*" # mathjax path in new doc - mathjax_path_to=$(SAGE_USE_CDNS=yes /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") - new_version=$(cat src/VERSION.txt) + mathjax_path_to=$(docker exec -e SAGE_USE_CDNS=yes BUILD /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") + new_version=$(docker exec BUILD cat src/VERSION.txt) + mkdir -p docs/ + docker cp BUILD:/sage/local/share/doc/sage/html docs/ # Wipe out chronic diffs between old doc and new doc - (cd /sage/local/share/doc/sage/html && \ + (cd docs && \ find . -name "*.html" | xargs sed -i -e '/class="sidebar-brand-text"/ s/Sage [0-9a-z.]* /Sage '"$new_version"' /' \ -e 's;'"$mathjax_path_from"';'"$mathjax_path_to"';' \ -e '\;; d') # Create git repo from old doc - DOC_DIR=/sage/local/share/doc/sage/html - (cd $DOC_DIR && git init && \ + (cd docs && \ + git init && \ (echo "*.svg binary"; echo "*.pdf binary") >> .gitattributes && \ (echo ".buildinfo"; echo '*.inv'; echo '.git*'; echo '*.svg'; echo '*.pdf'; echo '*.png'; echo 'searchindex.js') > .gitignore; \ git add -A && git commit --quiet -m "old") - - name: Download upstream artifact - uses: actions/download-artifact@v3 - with: - path: upstream - name: upstream - - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. - run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi - - - name: Incremental build - id: incremental - run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make sagemath_doc_html-build-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 - - - name: Build (fallback to non-incremental) - id: build - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' - run: | - set -ex - make sagelib-clean && git clean -fx src/sage && ./config.status && make sagemath_doc_html-build-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 - - name: Build docs id: docbuild - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.build.outcome == 'success') # Always non-incremental because of the concern that # incremental docbuild may introduce broken links (inter-file references) though build succeeds run: | - set -ex - DOC_DIR=/sage/local/share/doc/sage/html - mv $DOC_DIR/.git /sage/.git-doc + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 make doc-clean doc-uninstall - mkdir -p $DOC_DIR/ && mv /sage/.git-doc $DOC_DIR/.git export SAGE_USE_CDNS=yes ./config.status && make sagemath_doc_html-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Copy docs id: copy if: (success() || failure()) && steps.docbuild.outcome == 'success' run: | set -ex - DOC_DIR=/sage/local/share/doc/sage/html - (cd $DOC_DIR && git commit -a -m 'new') - ls -l /sage/venv/bin - PATH=/sage/venv/bin:$PATH .ci/create-changes-html.sh $(cd $DOC_DIR; git rev-parse HEAD^) $DOC_DIR - (cd $DOC_DIR && rm -rf .git) # We copy everything to a local folder + docker cp BUILD:/sage/local/share/doc/sage/html docs + docker cp BUILD:/sage/local/share/doc/sage/index.html docs + (cd docs && git commit -a -m 'new') + .ci/create-changes-html.sh $(cd docs && git rev-parse HEAD^) docs + (cd docs && rm -rf .git) + mv CHANGES.html docs # We also need to replace the symlinks because netlify is not following them - mkdir -p ./docs - mv CHANGES.html ./docs - cp -r -L $DOC_DIR ./docs - cp $DOC_DIR/../index.html ./docs + # CHECK IF STILL NEEDED + #cp -r -L $DOC_DIR ./docs # Zip everything for increased performance zip -r docs.zip docs @@ -157,7 +178,7 @@ jobs: id: buildlivedoc if: (success() || failure()) && steps.copy.outcome == 'success' && github.repository == 'sagemath/sage' && github.ref == 'refs/heads/develop' run: | - set -ex + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 export PATH="build/bin:$PATH" eval $(sage-print-system-package-command auto update) eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) @@ -167,21 +188,18 @@ jobs: export SAGE_JUPYTER_SERVER=binder:sagemath/sage-binder-env/dev make doc-clean doc-uninstall ./config.status && make sagemath_doc_html-no-deps sagemath_doc_pdf-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD ./worktree-image {0} - name: Copy live doc id: copylivedoc if: (success() || failure()) && steps.buildlivedoc.outcome == 'success' run: | - set -ex mkdir -p ./livedoc cp -r -L /sage/local/share/doc/sage/html ./livedoc cp -r -L /sage/local/share/doc/sage/pdf ./livedoc cp /sage/local/share/doc/sage/index.html ./livedoc zip -r livedoc.zip livedoc + shell: sh .ci/docker-exec-script.sh BUILD . {0} - name: Upload live doc if: (success() || failure()) && steps.copylivedoc.outcome == 'success' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ccc2b07a02f..52288013188 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -183,8 +183,10 @@ jobs: sudo apt-get clean df -h - name: Update Sage packages from upstream artifact + # Handle both the old and new location of write-dockerfile.sh, + # because docker.yml is a reusable workflow. run: | - (export PATH=$(pwd)/build/bin:$PATH; (cd upstream && bash -x update-pkgs.sh) && sed -i.bak '/upstream/d' .dockerignore && echo "/:toolchain:/i ADD upstream upstream" | sed -i.bak -f - build/bin/write-dockerfile.sh && git diff) + (export PATH=$(pwd)/build/bin:$PATH; (cd upstream && bash -x update-pkgs.sh) && sed -i.bak '/upstream/d' .dockerignore; for a in build/bin/write-dockerfile.sh .ci/write-dockerfile.sh; do if [ -r $a ]; then echo "/:toolchain:/i ADD upstream upstream" | sed -i.bak -f - $a; fi; done; git diff) if: inputs.upstream_artifact - name: Try to login to ghcr.io diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5d444594877..7c108f7eb2c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,11 +1,11 @@ name: Lint -on: +on: push: branches: - master - develop - pull_request: + pull_request: merge_group: concurrency: @@ -29,7 +29,7 @@ jobs: SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml new file mode 100644 index 00000000000..cafe75db728 --- /dev/null +++ b/.github/workflows/pyright.yml @@ -0,0 +1,84 @@ +name: Static check with Pyright + +on: + pull_request: + merge_group: + push: + branches: + - master + - develop + workflow_dispatch: + # Allow to run manually + +concurrency: + # Cancel previous runs of this workflow for the same branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + pyright: + runs-on: ubuntu-latest + container: ghcr.io/sagemath/sage/sage-ubuntu-jammy-standard-with-targets:dev + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Update system packages + id: prepare + run: | + export PATH="build/bin:$PATH" + eval $(sage-print-system-package-command auto update) + eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git) + + - name: Install GH CLI + uses: dev-hanz-ops/install-gh-cli-action@v0.1.0 + with: + gh-cli-version: 2.32.0 + + - name: Merge CI fixes from sagemath/sage + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + .ci/merge-fixes.sh + + - name: Add prebuilt tree as a worktree + id: worktree + run: | + set -ex + .ci/retrofit-worktree.sh worktree-image /sage + + - name: Incremental build (sagelib deps) + id: incremental + run: | + # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. + # pyright does not need a built sagelib; it only needs + # the libraries from which sagelib imports. + ./bootstrap && make sagelib-build-deps + working-directory: ./worktree-image + env: + MAKE: make -j2 --output-sync=recurse + SAGE_NUM_THREADS: 2 + + - name: Static code check with pyright + uses: jakebailey/pyright-action@v1 + with: + version: 1.1.332 + # Many warnings issued by pyright are not yet helpful because there is not yet enough type information. + no-comments: true + working-directory: ./worktree-image + env: + # To avoid out of memory errors + NODE_OPTIONS: --max-old-space-size=8192 + + - name: Static code check with pyright (annotated) + if: (success() || failure()) && steps.incremental.outcome == 'success' + uses: jakebailey/pyright-action@v1 + with: + version: 1.1.332 + # Issue errors + no-comments: false + level: error + working-directory: ./worktree-image + env: + # To avoid out of memory errors + NODE_OPTIONS: --max-old-space-size=8192 diff --git a/.gitignore b/.gitignore index 43f58abcafe..7c6e7b55356 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,6 @@ /src/setup.cfg /src/requirements.txt -/src/pyproject.toml /src/Pipfile /src/Pipfile.lock /Pipfile @@ -186,6 +185,19 @@ __pycache__/ build/temp.*/ build/bin/sage-build-env-config +# Generated files in build +build/pkgs/cypari/version_requirements.txt +build/pkgs/cysignals/version_requirements.txt +build/pkgs/cython/version_requirements.txt +build/pkgs/gmpy2/version_requirements.txt +build/pkgs/jupyter_core/version_requirements.txt +build/pkgs/memory_allocator/version_requirements.txt +build/pkgs/numpy/version_requirements.txt +build/pkgs/pkgconfig/version_requirements.txt +build/pkgs/pplpy/version_requirements.txt +build/pkgs/setuptools/version_requirements.txt +build/pkgs/wheel/version_requirements.txt + # Generated files in the top-level source trees /pkgs/*/build /pkgs/*/dist @@ -223,16 +235,16 @@ build/bin/sage-build-env-config /pkgs/sagemath-categories/pyproject.toml /pkgs/sagemath-environment/pyproject.toml /pkgs/sagemath-repl/pyproject.toml -/pkgs/sagemath-objects/requirements.txt -/pkgs/sagemath-bliss/requirements.txt -/pkgs/sagemath-coxeter3/requirements.txt -/pkgs/sagemath-mcqd/requirements.txt -/pkgs/sagemath-meataxe/requirements.txt -/pkgs/sagemath-sirocco/requirements.txt -/pkgs/sagemath-tdlib/requirements.txt -/pkgs/sagemath-categories/requirements.txt -/pkgs/sagemath-environment/requirements.txt -/pkgs/sagemath-repl/requirements.txt +/pkgs/sagemath-objects/requirements*.txt +/pkgs/sagemath-bliss/requirements*.txt +/pkgs/sagemath-coxeter3/requirements*.txt +/pkgs/sagemath-mcqd/requirements*.txt +/pkgs/sagemath-meataxe/requirements*.txt +/pkgs/sagemath-sirocco/requirements*.txt +/pkgs/sagemath-tdlib/requirements*.txt +/pkgs/sagemath-categories/requirements*.txt +/pkgs/sagemath-environment/requirements*.txt +/pkgs/sagemath-repl/requirements*.txt /pkgs/sagemath-categories/MANIFEST.in # same for old locations - before Issue #31577 diff --git a/CITATION.cff b/CITATION.cff index 894be5d6a9e..ed836e58f83 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.4.beta4 -doi: 10.5281/zenodo.593563 -date-released: 2024-04-27 +version: 10.4.beta6 +doi: 10.5281/zenodo.8042260 +date-released: 2024-05-12 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/CITATION.cff.in b/CITATION.cff.in index bf7d5d3e58c..a3d4e1a35bd 100644 --- a/CITATION.cff.in +++ b/CITATION.cff.in @@ -5,7 +5,7 @@ abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" version: ${SAGE_VERSION} -doi: 10.5281/zenodo.593563 +doi: 10.5281/zenodo.8042260 date-released: ${SAGE_RELEASE_DATE} repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/Makefile b/Makefile index 4dadbf823b8..8c9a4504b49 100644 --- a/Makefile +++ b/Makefile @@ -79,14 +79,23 @@ reconfigure: fi # Preemptively download all source tarballs of normal packages. +DOWNLOAD_PACKAGES=:all: download: export SAGE_ROOT=$$(pwd) && \ export PATH=$$SAGE_ROOT/build/bin:$$PATH && \ - sage-package download :all: + sage-package download $(DOWNLOAD_PACKAGES) dist: build/make/Makefile ./sage --sdist +ci-build-with-fallback: + $(MAKE) build && $(MAKE) SAGE_CHECK=no pypi-wheels; \ + if [ $$? != 0 ]; then \ + echo "Incremental build failed, falling back"; \ + $(MAKE) doc-clean doc-uninstall sagelib-clean; \ + $(MAKE) build && $(MAKE) SAGE_CHECK=no pypi-wheels; \ + fi + ############################################################################### # Cleaning up ############################################################################### @@ -167,9 +176,19 @@ bootstrap-clean: rm -rf src/doc/en/reference/spkg/*.rst for a in environment environment-optional src/environment src/environment-dev src/environment-optional; do rm -f $$a.yml $$a-3.[89].yml $$a-3.1[0-9].yml; done rm -f src/Pipfile - rm -f src/pyproject.toml rm -f src/requirements.txt rm -f src/setup.cfg + rm -f build/pkgs/cypari/version_requirements.txt + rm -f build/pkgs/cysignals/version_requirements.txt + rm -f build/pkgs/cython/version_requirements.txt + rm -f build/pkgs/gmpy2/version_requirements.txt + rm -f build/pkgs/jupyter_core/version_requirements.txt + rm -f build/pkgs/memory_allocator/version_requirements.txt + rm -f build/pkgs/numpy/version_requirements.txt + rm -f build/pkgs/pkgconfig/version_requirements.txt + rm -f build/pkgs/pplpy/version_requirements.txt + rm -f build/pkgs/setuptools/version_requirements.txt + rm -f build/pkgs/wheel/version_requirements.txt # Remove absolutely everything which isn't part of the git repo maintainer-clean: distclean bootstrap-clean diff --git a/VERSION.txt b/VERSION.txt index c62fa370476..356733d937f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.4.beta4, Release Date: 2024-04-27 +SageMath version 10.4.beta6, Release Date: 2024-05-12 diff --git a/bootstrap b/bootstrap index 14eb56c9981..eb5bcd732b6 100755 --- a/bootstrap +++ b/bootstrap @@ -35,6 +35,15 @@ CONFVERSION=$(cat $PKG/package-version.txt) bootstrap () { + for pkgname in cypari cysignals cython gmpy2 jupyter_core memory_allocator numpy pkgconfig pplpy setuptools wheel; do + # Write the version_requirements.txt files for dependencies declared in pyproject.toml + target=build/pkgs/${pkgname}/version_requirements.txt + if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo "bootstrap:$LINENO: installing '"$target"'" + fi + echo "# Generated by SAGE_ROOT/bootstrap based on src/pyproject.toml; do not edit directly" > $target + sage-get-system-packages install-requires ${pkgname} >> $target + done for a in m4/sage_spkg_configures.m4 m4/sage_spkg_versions.m4 m4/sage_spkg_versions_toml.m4; do if [ "${BOOTSTRAP_QUIET}" = "no" ]; then echo "bootstrap:$LINENO: installing '"$a"'" @@ -233,7 +242,18 @@ save () { src/Pipfile \ src/pyproject.toml \ src/requirements.txt \ - src/setup.cfg + src/setup.cfg \ + build/pkgs/cypari/version_requirements.txt \ + build/pkgs/cysignals/version_requirements.txt \ + build/pkgs/cython/version_requirements.txt \ + build/pkgs/gmpy2/version_requirements.txt \ + build/pkgs/jupyter_core/version_requirements.txt \ + build/pkgs/memory_allocator/version_requirements.txt \ + build/pkgs/numpy/version_requirements.txt \ + build/pkgs/pkgconfig/version_requirements.txt \ + build/pkgs/pplpy/version_requirements.txt \ + build/pkgs/setuptools/version_requirements.txt \ + build/pkgs/wheel/version_requirements.txt # Update version echo "$NEWCONFVERSION" >$PKG/package-version.txt diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index c1eb4dfe101..183eba2e3dc 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -60,6 +60,8 @@ export SAGE_CONFIGURE_FFLAS_FFPACK="@SAGE_CONFIGURE_FFLAS_FFPACK@" export SAGE_HAVE_LIBJPEG="@SAGE_HAVE_LIBJPEG@" +export SAGE_FRICAS_LISP="@SAGE_FRICAS_LISP@" + export CONFIGURED_SAGE_EDITABLE="@SAGE_EDITABLE@" export CONFIGURED_SAGE_WHEELS="@SAGE_WHEELS@" diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index f8be31174ad..d7ef0a81a8b 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -69,11 +69,6 @@ # # Runs `pip uninstall` with the given arguments. If unsuccessful, it displays a warning. # -# - eval sdh_prefix_args PREFIX [...] -# -# Helper function for transforming build options so that they can be passed -# through "pip". -# # - sdh_cmake [...] # # Runs `cmake` in the current directory with the given arguments, as well as @@ -220,24 +215,17 @@ sdh_setup_bdist_wheel() { "$@" || sdh_die "Error building a wheel for $PKG_NAME" } -sdh_prefix_args () { - prefix="$1" - shift - while [ $# -gt 0 ]; do - # Quoted quotes because the result is to be run through eval - echo "$prefix" \"$1\" - shift - done -} - sdh_pip_install() { echo "Installing $PKG_NAME" mkdir -p dist rm -f dist/*.whl + export PIP_NO_INDEX=1 install_options="" build_options="" # pip has --no-build-isolation but no flag that turns the default back on... - build_isolation_option="--find-links=$SAGE_SPKG_WHEELS" + build_isolation_option="" + export PIP_FIND_LINKS="$SAGE_SPKG_WHEELS" + unset PIP_NO_BINARY while [ $# -gt 0 ]; do case "$1" in --build-isolation) @@ -245,17 +233,21 @@ sdh_pip_install() { # its build environment using the stored wheels. # We pass --find-links. # The SPKG needs to declare "setuptools" as a dependency. - build_isolation_option="--find-links=$SAGE_SPKG_WHEELS" + build_isolation_option="" + export PIP_FIND_LINKS="$SAGE_SPKG_WHEELS" + unset PIP_NO_BINARY ;; --no-build-isolation) # Use --no-binary, so that no wheels from caches are used. - build_isolation_option="--no-build-isolation --no-binary :all:" + unset PIP_FIND_LINKS + export PIP_NO_BINARY=:all: + build_isolation_option="--no-isolation --skip-dependency-check" ;; --no-deps) install_options="$install_options $1" ;; -C|--config-settings) - build_options="$build_options $1" + build_options="$build_options --config-setting" shift build_options="$build_options $1" ;; @@ -265,18 +257,20 @@ sdh_pip_install() { esac shift done - if python3 -m pip wheel --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option $build_options "$@"; then + if python3 -m build --wheel --outdir=dist $build_isolation_option $build_options "$@"; then : # successful else case $build_isolation_option in - *--no-build-isolation*) + *--no-isolation*) sdh_die "Error building a wheel for $PKG_NAME" ;; *) - echo >&2 "Warning: building with \"python3 -m pip wheel --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option\" failed." - build_isolation_option="--no-build-isolation --no-binary :all:" - echo >&2 "Retrying with \"python3 -m pip wheel --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option\"." - if python3 -m pip wheel --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option $build_options "$@"; then + echo >&2 "Warning: building with \"python3 -m build --wheel --outdir=dist $build_isolation_option $build_options $@\" failed." + unset PIP_FIND_LINKS + export PIP_NO_BINARY=:all: + build_isolation_option="--no-isolation --skip-dependency-check" + echo >&2 "Retrying with \"python3 -m build --wheel --outdir=dist $build_isolation_option $build_options $@\"." + if python3 -m build --wheel --outdir=dist $build_isolation_option $build_options "$@"; then echo >&2 "Warning: Wheel building needed to use \"$build_isolation_option\" to succeed. This means that a dependencies file in build/pkgs/ needs to be updated. Please report this to sage-devel@googlegroups.com, including the build log of this package." else sdh_die "Error building a wheel for $PKG_NAME" @@ -284,6 +278,9 @@ sdh_pip_install() { ;; esac fi + unset PIP_FIND_LINKS + unset PIP_NO_BINARY + unset PIP_NO_INDEX sdh_store_and_pip_install_wheel $install_options . } diff --git a/build/bin/sage-get-system-packages b/build/bin/sage-get-system-packages index 5e455c6daa3..0a90232ed6e 100755 --- a/build/bin/sage-get-system-packages +++ b/build/bin/sage-get-system-packages @@ -14,22 +14,25 @@ fi case "$SYSTEM" in install-requires) - # Collect version_requirements.txt (falling back to requirements.txt) and output it in the format + # Collect from src/pyproject.toml or from version_requirements.txt (falling back to requirements.txt) and output it in the format # needed by setup.cfg [options] version_requirements= SYSTEM_PACKAGES_FILE_NAMES="version_requirements.txt requirements.txt" STRIP_COMMENTS="sed s/#.*//;/^[[:space:]]*$/d;" + FROM_PYPROJECT_TOML=1 COLLECT= ;; install-requires-toml) - # Collect version_requirements.txt (falling back to requirements.txt) and output it in the format + # Collect from src/pyproject.toml or from version_requirements.txt (falling back to requirements.txt) and output it in the format # needed by pyproject.toml [build-system] requires= SYSTEM_PACKAGES_FILE_NAMES="version_requirements.txt requirements.txt" STRIP_COMMENTS="sed s/#.*//;/^[[:space:]]*$/d;s/^/'/;s/$/',/;" + FROM_PYPROJECT_TOML=1 COLLECT= ;; pip) SYSTEM_PACKAGES_FILE_NAMES="requirements.txt version_requirements.txt" STRIP_COMMENTS='sed s/#.*//;s/[[:space:]]//g;' + FROM_PYPROJECT_TOML=1 COLLECT=echo ;; *) @@ -42,11 +45,30 @@ case "$SYSTEM" in fi SYSTEM_PACKAGES_FILE_NAMES="distros/$SYSTEM.txt" STRIP_COMMENTS="sed s/#.*//;s/\${PYTHON_MINOR}/${PYTHON_MINOR}/g" + FROM_PYPROJECT_TOML=0 COLLECT=echo ;; esac -for PKG_BASE in $SPKGS; do +case "$SPKGS" in + *pkg:*|pypi/*|generic/*) + PATH="${SAGE_ROOT}/build/bin:$PATH" SPKGS=$(sage-package list $SPKGS) + ;; +esac + +for PKG_BASE in $SPKGS; do + if [ $FROM_PYPROJECT_TOML -eq 1 ]; then + if [ -f "$SAGE_ROOT/src/pyproject.toml" ]; then + # Extract from the "requires" block in src/pyproject.toml + # Packages are in the format "'sage-conf ~= 10.3b3'," + PACKAGE_INFO=$(sed -n '/requires *= *\[/,/^\]/s/^ *'\''\('$PKG_BASE'.*\)'\'',/\1/p' "$SAGE_ROOT/src/pyproject.toml") + if [ -n "$PACKAGE_INFO" ]; then + echo "$PACKAGE_INFO" | ${STRIP_COMMENTS} + continue + fi + fi + fi + case "$SYSTEM:$ENABLE_SYSTEM_SITE_PACKAGES" in install-requires*|pip*) # This is output for installation of packages into a Python environment. @@ -73,12 +95,12 @@ for PKG_BASE in $SPKGS; do for NAME in $SYSTEM_PACKAGES_FILE_NAMES; do SYSTEM_PACKAGES_FILE="$SAGE_ROOT"/build/pkgs/$PKG_BASE/$NAME if [ -f $SYSTEM_PACKAGES_FILE ]; then - if [ -z "$COLLECT" ]; then - ${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE - else - $COLLECT $(${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE) - fi - break + if [ -z "$COLLECT" ]; then + ${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE + else + $COLLECT $(${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE) + fi + break fi done done diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 2856fd71ade..7a13e5a9e23 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -130,11 +130,14 @@ PIP_PACKAGES = @SAGE_PIP_PACKAGES@ # Packages that use the 'script' package build rules SCRIPT_PACKAGES = @SAGE_SCRIPT_PACKAGES@ -# Packages for which we build wheels for PyPI -PYPI_WHEEL_PACKAGES = \ +# Packages for which we build platform-independent wheels for PyPI +PYPI_NOARCH_WHEEL_PACKAGES = \ sage_sws2rst \ sage_setup \ sagemath_environment \ + +# Packages for which we build wheels for PyPI +PYPI_WHEEL_PACKAGES = $(PYPI_NOARCH_WHEEL_PACKAGES) \ sagemath_objects \ sagemath_repl \ sagemath_categories \ @@ -222,7 +225,7 @@ SAGE_I_TARGETS = sagelib doc # Tell make not to look for files with these names: .PHONY: all all-sage all-toolchain all-build all-sageruntime \ all-start build-start base toolchain toolchain-deps base-toolchain \ - pypi-sdists pypi-wheels wheels \ + pypi-sdists pypi-noarch-wheels pypi-wheels wheels \ sagelib \ doc doc-html doc-html-jsmath doc-html-mathjax doc-pdf \ doc-uninstall \ @@ -326,7 +329,7 @@ all-toolchain: base-toolchain # Shorthand for a list of packages sufficient for building and installing # typical Python packages from source. Wheel packages only need pip. -PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel flit_core hatchling +PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel flit_core hatchling python_build # Issue #32056: Avoid installed setuptools leaking into the build of python3 by uninstalling it. # It will have to be reinstalled anyway because of its dependency on $(PYTHON). @@ -455,6 +458,13 @@ pypi-sdists: $(PYPI_SDIST_PACKAGES:%=%-sdist) # Ensuring wheels are present, even for packages that may have been installed # as editable. Until we have better uninstallation of script packages, we # just remove the timestamps, which will lead to rebuilds of the packages. +pypi-noarch-wheels: + for a in $(PYPI_NOARCH_WHEEL_PACKAGES); do \ + rm -f $(SAGE_VENV)/var/lib/sage/installed/$$a-*; \ + done + $(MAKE_REC) SAGE_EDITABLE=no SAGE_WHEELS=yes $(PYPI_NOARCH_WHEEL_PACKAGES) + @echo "Built wheels are in venv/var/lib/sage/wheels/" + pypi-wheels: for a in $(PYPI_WHEEL_PACKAGES); do \ rm -f $(SAGE_VENV)/var/lib/sage/installed/$$a-*; \ diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 5a209c1c1bc..3222e604fa8 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=2d6779beb2e69f0f7bddc2edc44ad275442ffd29 -md5=789344e03a6b57ba1538c0c760449720 -cksum=3806733369 +sha1=dca19f73642b76f1f96b860bb6603499f586380d +md5=cdfc81ecaa40045a7827959e9f52e226 +cksum=1337053183 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 6035b129425..8da0a664f61 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -0a7b2513287af1124a358c2494b8bef4668a1882 +f5e78840b108412e1e26382910af993f874c6933 diff --git a/build/pkgs/cypari/version_requirements.txt b/build/pkgs/cypari/version_requirements.txt deleted file mode 100644 index a10714cc59f..00000000000 --- a/build/pkgs/cypari/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cypari2 >=2.1.1 diff --git a/build/pkgs/cysignals/version_requirements.txt b/build/pkgs/cysignals/version_requirements.txt deleted file mode 100644 index ed9219d6ee5..00000000000 --- a/build/pkgs/cysignals/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cysignals >=1.10.2 diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index 27694df1a8e..6fcab0bf060 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,5 +1,5 @@ tarball=Cython-VERSION.tar.gz -sha1=48f0535ce0b05e0e4ae4daa6a597a2cdd76274f5 -md5=94ab8466d9350a31cfef3a0853c2fea5 -cksum=2507297160 +sha1=83d6428e3bb7869f44f92ed75d7dff867c2a38ce +md5=0110d7adac5ebb6ae65d8b71a72b664e +cksum=1043698601 upstream_url=https://pypi.io/packages/source/C/Cython/Cython-VERSION.tar.gz diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 2451c27caf7..a909317fe5a 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -3.0.7 +3.0.10 diff --git a/build/pkgs/cython/version_requirements.txt b/build/pkgs/cython/version_requirements.txt deleted file mode 100644 index 74073ecb57f..00000000000 --- a/build/pkgs/cython/version_requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -cython >=3.0, != 3.0.3, <4.0 - -# Exclude 3.0.3 because of https://github.com/cython/cython/issues/5748 diff --git a/build/pkgs/deprecation/spkg-configure.m4 b/build/pkgs/deprecation/spkg-configure.m4 deleted file mode 100644 index 306d7c5cd1e..00000000000 --- a/build/pkgs/deprecation/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([deprecation], [SAGE_PYTHON_PACKAGE_CHECK([deprecation])]) diff --git a/build/pkgs/editables/checksums.ini b/build/pkgs/editables/checksums.ini index 52c7fa0b03b..a3628c1c005 100644 --- a/build/pkgs/editables/checksums.ini +++ b/build/pkgs/editables/checksums.ini @@ -1,5 +1,5 @@ -tarball=editables-VERSION.tar.gz -sha1=90efed858e78bf6276d1a5959ec6692e11a6bce9 -md5=520de8c3a9dc5dfb2b365d104541c9de -cksum=3074203672 -upstream_url=https://pypi.io/packages/source/e/editables/editables-VERSION.tar.gz +tarball=editables-VERSION-py3-none-any.whl +sha1=7aa90de86b05d6dc1a04c219b01ca7eab09de113 +md5=5de129d3a039b26b7f6798a4002acdf6 +cksum=24000838 +upstream_url=https://pypi.io/packages/py3/e/editables/editables-VERSION-py3-none-any.whl diff --git a/build/pkgs/editables/dependencies b/build/pkgs/editables/dependencies index e0e94942dba..644ad35f773 100644 --- a/build/pkgs/editables/dependencies +++ b/build/pkgs/editables/dependencies @@ -1,4 +1,4 @@ - | flit_core $(PYTHON) + | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/editables/spkg-install.in b/build/pkgs/editables/spkg-install.in deleted file mode 100644 index 37ac1a53437..00000000000 --- a/build/pkgs/editables/spkg-install.in +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_pip_install . diff --git a/build/pkgs/fflas_ffpack/checksums.ini b/build/pkgs/fflas_ffpack/checksums.ini index 89023b57d1e..1a5cdcee74b 100644 --- a/build/pkgs/fflas_ffpack/checksums.ini +++ b/build/pkgs/fflas_ffpack/checksums.ini @@ -1,4 +1,5 @@ tarball=fflas_ffpack-VERSION.tar.bz2 -sha1=c221513710b98e0e62153f424a9725c5be2ff62a -md5=05c77ea30394cacd53b7aed6ffba1e7b -cksum=3775757878 +sha1=7c5faa81abc2b88ec24cec373b5e44cbaa7844dd +md5=d8b7c113951a2a3f498a3aaadbe5620f +cksum=3321469120 +upstream_url=https://github.com/linbox-team/fflas-ffpack/releases/download/vVERSION/fflas_ffpack-VERSION.tar.bz2 \ No newline at end of file diff --git a/build/pkgs/fflas_ffpack/package-version.txt b/build/pkgs/fflas_ffpack/package-version.txt index e0af9dd584a..437459cd94c 100644 --- a/build/pkgs/fflas_ffpack/package-version.txt +++ b/build/pkgs/fflas_ffpack/package-version.txt @@ -1 +1 @@ -2.4.3.p0 +2.5.0 diff --git a/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch b/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch deleted file mode 100644 index 9351b03a941..00000000000 --- a/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch +++ /dev/null @@ -1,93 +0,0 @@ -From d72a7643b7f8a1dedd12eadf89690c07ff6eed6e Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Mon, 1 Mar 2021 09:23:50 -0800 -Subject: [PATCH] Do not use variable names B0, B1 to avoid clash with - sys/termio.h macros (again) - ---- - .../fflas/fflas_igemm/igemm_kernels.inl | 50 +++++++++---------- - 1 file changed, 25 insertions(+), 25 deletions(-) - -diff --git a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -index c69d32c6..0ca12110 100644 ---- a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -+++ b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -@@ -403,21 +403,21 @@ namespace FFLAS { namespace details { /* kernels */ - vect_t R0; - R0 = simd::set(r0[0], r1[0], r2[0], r3[0]); // could be done with a gather (marginally faster?) - for(k=0;k +Date: Fri, 17 Dec 2021 10:27:02 +0100 +Subject: [PATCH] Fix SimdChooser: on not x86_64 machines its value could be an + nonexistant struct + +--- + fflas-ffpack/fflas/fflas_simd.h | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/fflas-ffpack/fflas/fflas_simd.h b/fflas-ffpack/fflas/fflas_simd.h +index bf5d30211..84ced0fd8 100644 +--- a/fflas-ffpack/fflas/fflas_simd.h ++++ b/fflas-ffpack/fflas/fflas_simd.h +@@ -384,6 +384,20 @@ struct SimdChooser // integral number + #endif + }; + ++#ifndef __x86_64__ ++template <> ++struct SimdChooser ++{ ++ using value = NoSimd; ++}; ++ ++template <> ++struct SimdChooser ++{ ++ using value = NoSimd; ++}; ++#endif ++ + template using Simd = typename SimdChooser::value; + + // template struct SimdChooser { diff --git a/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch b/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index fdaed2eebee..00000000000 --- a/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 33a5ec4977f36ce3a24c9ee824d9dd053b8cea04 Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 15:55:27 +0100 -Subject: [PATCH 1/1] remove 1st and last file in .pc file - -this causes problem if building with ksh, as they remain, causing a broken .pc file. ---- - fflas-ffpack.pc.in | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/fflas-ffpack.pc.in b/fflas-ffpack.pc.in -index a2618d6..e34a744 100644 ---- a/fflas-ffpack.pc.in -+++ b/fflas-ffpack.pc.in -@@ -1,4 +1,3 @@ --/------------------ fflas-ffpack.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@prefix@/lib -@@ -11,4 +10,3 @@ Version: @VERSION@ - Requires: givaro >= 4.0.3 - Libs: @PARLIBS@ @PRECOMPILE_LIBS@ @BLAS_LIBS@ - Cflags: -I@includedir@ @BLAS_CFLAGS@ @PARFLAGS@ @PRECOMPILE_FLAGS@ @REQUIRED_FLAGS@ --\------------------------------------------------------- -\ No newline at end of file --- -2.26.2 - diff --git a/build/pkgs/fflas_ffpack/spkg-configure.m4 b/build/pkgs/fflas_ffpack/spkg-configure.m4 index 646cfb527e4..4e99de570e3 100644 --- a/build/pkgs/fflas_ffpack/spkg-configure.m4 +++ b/build/pkgs/fflas_ffpack/spkg-configure.m4 @@ -4,7 +4,7 @@ SAGE_SPKG_CONFIGURE([fflas_ffpack], [ # the system fflas-ffpack, too. Use pkg-config to find a # recentish version, if there is one. PKG_CHECK_MODULES([FFLAS_FFPACK], - [fflas-ffpack >= 2.4.0],dnl The version test is refined in linbox/spkg-configure.m4 + [fflas-ffpack >= 2.5.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_fflas_ffpack=no], [sage_spkg_install_fflas_ffpack=yes]) ]) diff --git a/build/pkgs/flint/spkg-configure.m4 b/build/pkgs/flint/spkg-configure.m4 index ddf60b596fd..4c1a347152d 100644 --- a/build/pkgs/flint/spkg-configure.m4 +++ b/build/pkgs/flint/spkg-configure.m4 @@ -3,12 +3,12 @@ SAGE_SPKG_CONFIGURE([flint], [ AC_CHECK_HEADER(flint/flint.h, [dnl dnl gr_get_fexpr appears in Flint 3.0 AC_SEARCH_LIBS([gr_get_fexpr], [flint], [dnl - dnl Flint 3.1 is too new - AC_MSG_CHECKING([whether FLINT version is >= 3.1.0]) + dnl Assume Flint 3.2 is too new + AC_MSG_CHECKING([whether FLINT version is >= 3.2.0]) AC_COMPILE_IFELSE([dnl AC_LANG_PROGRAM([[#include - #if __FLINT_RELEASE >= 30100 - # error "FLINT 3.1 is too new" + #if __FLINT_RELEASE >= 30200 + # error "FLINT 3.2 is too new" #endif ]]) ], [dnl diff --git a/build/pkgs/fricas/checksums.ini b/build/pkgs/fricas/checksums.ini index 965d2f17f01..a06136db04e 100644 --- a/build/pkgs/fricas/checksums.ini +++ b/build/pkgs/fricas/checksums.ini @@ -1,5 +1,5 @@ tarball=fricas-VERSION-full.tar.bz2 -sha1=6f2c1ae5eb71daab871d1814b26f596363c8e925 -md5=504b431c39e498527e6f9c739c973488 -cksum=2469663675 +sha1=2f1e1bbbad7e04a7114ffbd93eeedadc5db32272 +md5=d2ecd6f8c45cfc41c407b7d5f6eaae07 +cksum=1256005675 upstream_url=https://github.com/fricas/fricas/releases/download/VERSION/fricas-VERSION-full.tar.bz2 diff --git a/build/pkgs/fricas/package-version.txt b/build/pkgs/fricas/package-version.txt index 99e2eaffb13..0c00f610817 100644 --- a/build/pkgs/fricas/package-version.txt +++ b/build/pkgs/fricas/package-version.txt @@ -1 +1 @@ -1.3.8.p1 +1.3.10 diff --git a/build/pkgs/fricas/patches/extdecls.patch b/build/pkgs/fricas/patches/extdecls.patch deleted file mode 100644 index 848da9c02aa..00000000000 --- a/build/pkgs/fricas/patches/extdecls.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/src/include/cfuns-c.H1 b/src/include/cfuns-c.H1 -index af23933a..8e458c6c 100644 ---- a/src/include/cfuns-c.H1 -+++ b/src/include/cfuns-c.H1 -@@ -1,2 +1,4 @@ - extern int directoryp(char *); - extern int writeablep(char *); -+extern int remove_directory(char *); -+extern int makedir(char *); diff --git a/build/pkgs/fricas/patches/macos_lisp.patch b/build/pkgs/fricas/patches/macos_lisp.patch deleted file mode 100644 index c1be0ac46fd..00000000000 --- a/build/pkgs/fricas/patches/macos_lisp.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/src/lisp/Makefile.in b/src/lisp/Makefile.in -index 30f615096..f0b3f0bd4 100644 ---- a/src/lisp/Makefile.in -+++ b/src/lisp/Makefile.in -@@ -118,6 +118,8 @@ do_it.ecl: fricas-lisp.lisp fricas-package.lisp fricas-config.lisp \ - fricas-lisp.o primitives.o) ")))" \ - >> fricas-ecl.lisp - echo "(defvar *fricas-initial-lisp-forms* nil)" >> fricas-ecl.lisp -+ echo "(require :cmp)" -+ echo "(setf c::*user-cc-flags* (concatenate 'string c::*user-cc-flags* \" -I$(BASE)/$(fricas_src_srcdir)/include/ -I$(BASE)/$(fricas_src_srcdir)/../config/\"))" >> fricas-ecl.lisp - echo '(load "fricas-package.lisp")' \ - '(load "fricas-config.lisp")' \ - '(load "fricas-ecl.lisp")' \ -diff --git a/src/lisp/fricas-lisp.lisp b/src/lisp/fricas-lisp.lisp -index d6c7484df..f99e2e75b 100644 ---- a/src/lisp/fricas-lisp.lisp -+++ b/src/lisp/fricas-lisp.lisp -@@ -609,6 +609,13 @@ with this hack and will try to convince the GCL crowd to fix this. - #+(and :clisp :ffi) `(defun clisp-init-foreign-calls () ,@arguments) - ) - -+#+:ecl -+(ext:with-backend :c/c++ -+ (ffi:clines -+ "#include " -+ "#include " -+ "#include ")) -+ - (foreign-defs - - (fricas-foreign-call |writeablep| "writeablep" int diff --git a/build/pkgs/fricas/spkg-install.in b/build/pkgs/fricas/spkg-install.in index 847d88cba9d..dfae6f54edb 100644 --- a/build/pkgs/fricas/spkg-install.in +++ b/build/pkgs/fricas/spkg-install.in @@ -3,6 +3,6 @@ cd src # Use newer version of config.guess and config.sub (see Issue #23847) cp "$SAGE_ROOT"/config/config.* config -sdh_configure --with-lisp=ecl --enable-case-insensitive-file-system-check=no +sdh_configure --with-lisp="$SAGE_FRICAS_LISP" --enable-case-insensitive-file-system-check=no sdh_make sdh_make_install -j1 diff --git a/build/pkgs/gambit/SPKG.rst b/build/pkgs/gambit/SPKG.rst deleted file mode 100644 index f266379f6ff..00000000000 --- a/build/pkgs/gambit/SPKG.rst +++ /dev/null @@ -1,30 +0,0 @@ -gambit: Computations on finite, noncooperative games -==================================================== - -Description ------------ - -Gambit is a set of software tools for doing computation on finite, -noncooperative games. The Gambit Project was founded in the mid-1980s by -Richard McKelvey at the California Institute of Technology. - -License -------- - -GPL v2+ - - -Upstream Contact ----------------- - -- Website: http://www.gambit-project.org/ -- Mailing List: http://sourceforge.net/p/gambit/mailman/gambit-devel/ - -Dependencies ------------- - -- python -- cython -- setuptools -- IPython -- scipy diff --git a/build/pkgs/gambit/checksums.ini b/build/pkgs/gambit/checksums.ini deleted file mode 100644 index 132796d9573..00000000000 --- a/build/pkgs/gambit/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=gambit-VERSION.tar.gz -sha1=603dd52e8c0c2881bc2fdc8523bd8cbd9106b36f -md5=db47a02f66644806dbd43f77dc41ebeb -cksum=2352708160 diff --git a/build/pkgs/gambit/dependencies b/build/pkgs/gambit/dependencies deleted file mode 100644 index e026bfaacf9..00000000000 --- a/build/pkgs/gambit/dependencies +++ /dev/null @@ -1,4 +0,0 @@ -cython | $(PYTHON_TOOLCHAIN) $(PYTHON) - ----------- -All lines of this file are ignored except the first. diff --git a/build/pkgs/gambit/distros/homebrew.txt b/build/pkgs/gambit/distros/homebrew.txt deleted file mode 100644 index c08942b85ca..00000000000 --- a/build/pkgs/gambit/distros/homebrew.txt +++ /dev/null @@ -1 +0,0 @@ -gambit diff --git a/build/pkgs/gambit/distros/repology.txt b/build/pkgs/gambit/distros/repology.txt deleted file mode 100644 index 748786b4f51..00000000000 --- a/build/pkgs/gambit/distros/repology.txt +++ /dev/null @@ -1 +0,0 @@ -gambit-game-theory diff --git a/build/pkgs/gambit/package-version.txt b/build/pkgs/gambit/package-version.txt deleted file mode 100644 index 1b67f294f5f..00000000000 --- a/build/pkgs/gambit/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -15.1.1.p0 diff --git a/build/pkgs/gambit/patches/b91115633dbf6f64745fda2db7fbb83f918dd500.patch b/build/pkgs/gambit/patches/b91115633dbf6f64745fda2db7fbb83f918dd500.patch deleted file mode 100644 index f99d88daa7f..00000000000 --- a/build/pkgs/gambit/patches/b91115633dbf6f64745fda2db7fbb83f918dd500.patch +++ /dev/null @@ -1,24 +0,0 @@ -From b91115633dbf6f64745fda2db7fbb83f918dd500 Mon Sep 17 00:00:00 2001 -From: Ted Turocy -Date: Fri, 7 Jul 2017 12:56:56 +0100 -Subject: [PATCH] Const-correctness fix in shared_ptr.h - -This corrects the parameter to weak_ptr::swap(), which had been -incorrectly labeled as const. ---- - src/libgambit/shared_ptr.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/libgambit/shared_ptr.h b/src/libgambit/shared_ptr.h -index 959510e5..4379840e 100644 ---- a/src/libgambit/shared_ptr.h -+++ b/src/libgambit/shared_ptr.h -@@ -131,7 +131,7 @@ template class weak_ptr { - long use_count(void) const { return *m_count; } - bool expired(void) const { return *m_count == 0; } - -- void swap(const weak_ptr &other) // never throws -+ void swap(weak_ptr &other) // never throws - { - std::swap(m_ptr, other.m_ptr); - std::swap(m_count, other.m_count); diff --git a/build/pkgs/gambit/spkg-install.in b/build/pkgs/gambit/spkg-install.in deleted file mode 100644 index 61b6e43c0d4..00000000000 --- a/build/pkgs/gambit/spkg-install.in +++ /dev/null @@ -1,15 +0,0 @@ -cd src - -sdh_configure --disable-gui -sdh_make -sdh_make_install - - -cd src/python - -# Remove outdated source file (https://github.com/gambitproject/gambit/pull/232) -rm gambit/lib/libgambit.cpp - -# pip doesn't work (https://github.com/gambitproject/gambit/issues/207) -sdh_setup_bdist_wheel -sdh_store_and_pip_install_wheel . diff --git a/build/pkgs/gambit/type b/build/pkgs/gambit/type deleted file mode 100644 index 9839eb20815..00000000000 --- a/build/pkgs/gambit/type +++ /dev/null @@ -1 +0,0 @@ -experimental diff --git a/build/pkgs/gc/checksums.ini b/build/pkgs/gc/checksums.ini index 828d7e35d93..81825b73703 100644 --- a/build/pkgs/gc/checksums.ini +++ b/build/pkgs/gc/checksums.ini @@ -1,5 +1,5 @@ tarball=gc-VERSION.tar.gz -sha1=41c88cbc4bc9bf76e1a95a1500ea5b0360bc4f55 -md5=8901a6ed29ac35842420054772ea3441 -cksum=4201205407 +sha1=3f543532c47e592a8f5ea6f7a529c8ed7465a5c7 +md5=fc5351214bc2e854070ee3319181a467 +cksum=2567761321 upstream_url=https://github.com/ivmai/bdwgc/releases/download/vVERSION/gc-VERSION.tar.gz diff --git a/build/pkgs/gc/package-version.txt b/build/pkgs/gc/package-version.txt index 11cb5b746c0..d00d9ba64a3 100644 --- a/build/pkgs/gc/package-version.txt +++ b/build/pkgs/gc/package-version.txt @@ -1 +1 @@ -8.2.4 +8.2.6 diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 8b5ff54fe8e..9b8c8dc44ce 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -165,8 +165,8 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Install our own GCC if the system-provided one is older than gcc 8.4 SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]) ], - [1[[4-9]].*], [ - # Install our own GCC if the system-provided one is newer than 13.x. + [1[[5-9]].*], [ + # Install our own GCC if the system-provided one is newer than 14.x. # See https://github.com/sagemath/sage/issues/29456 SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) ]) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index e6e396deaab..a8248a26aa5 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -86,8 +86,8 @@ SAGE_SPKG_CONFIGURE([gfortran], [ # Install our own gfortran if the system-provided one is older than gcc-4.8. SAGE_SHOULD_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is quite old]) ], - [1[[4-9]].*], [ - # Install our own gfortran if the system-provided one is newer than 13.x. + [1[[5-9]].*], [ + # Install our own gfortran if the system-provided one is newer than 14.x. # See https://github.com/sagemath/sage/issues/29456, https://github.com/sagemath/sage/issues/31838 SAGE_MUST_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is too recent for this version of Sage]) ]) diff --git a/build/pkgs/givaro/checksums.ini b/build/pkgs/givaro/checksums.ini index 27466082a40..8d20cfdafa5 100644 --- a/build/pkgs/givaro/checksums.ini +++ b/build/pkgs/givaro/checksums.ini @@ -1,4 +1,5 @@ tarball=givaro-VERSION.tar.gz -sha1=2e7af1537d6f8325578a54d5b8092c990028863d -md5=b27c7713fcdced257df5f17b7bec8fd5 -cksum=731010730 +sha1=73ef15ca34c6f1c9f61013d2bd7d4d547e3ace14 +md5=d03ca4ba1e4a44c20935cf2adfcb520b +cksum=3088182773 +upstream_url=https://github.com/linbox-team/givaro/releases/download/vVERSION/givaro-VERSION.tar.gz \ No newline at end of file diff --git a/build/pkgs/givaro/package-version.txt b/build/pkgs/givaro/package-version.txt index 627a3f43a64..6aba2b245a8 100644 --- a/build/pkgs/givaro/package-version.txt +++ b/build/pkgs/givaro/package-version.txt @@ -1 +1 @@ -4.1.1 +4.2.0 diff --git a/build/pkgs/givaro/patches/197.patch b/build/pkgs/givaro/patches/197.patch new file mode 100644 index 00000000000..bafaff3efbd --- /dev/null +++ b/build/pkgs/givaro/patches/197.patch @@ -0,0 +1,28 @@ +From ab3d332508c21daff41fb64a8658cdc7cc74fc47 Mon Sep 17 00:00:00 2001 +From: Cyril Bouvier +Date: Thu, 16 Dec 2021 17:12:25 +0100 +Subject: [PATCH] dom_power argument is now an uint64_t to avoid problem with + 32bit machine + +--- + src/kernel/system/givpower.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/kernel/system/givpower.h b/src/kernel/system/givpower.h +index eb784872..5644264d 100644 +--- a/src/kernel/system/givpower.h ++++ b/src/kernel/system/givpower.h +@@ -71,11 +71,11 @@ namespace Givaro { + + //! dom_power + template +- TT& dom_power(TT& res, const TT& n, long l, const D& F) ++ TT& dom_power(TT& res, const TT& n, uint64_t l, const D& F) + { + if (l == 0) return F.assign(res,F.one) ; + +- unsigned long p = (unsigned long) l ; ++ uint64_t p = l; + bool is_assg = false ; + + TT puiss; F.init(puiss); F.assign(puiss,n) ; diff --git a/build/pkgs/givaro/patches/226.patch b/build/pkgs/givaro/patches/226.patch new file mode 100644 index 00000000000..0459dc6f36c --- /dev/null +++ b/build/pkgs/givaro/patches/226.patch @@ -0,0 +1,30 @@ +From 20caba1b549fe46b483f120f8eec6ec4e9f4572d Mon Sep 17 00:00:00 2001 +From: "Benjamin A. Beasley" +Date: Thu, 25 Jan 2024 08:29:17 -0500 +Subject: [PATCH] Temporary GCC 14 workaround +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes https://github.com/linbox-team/givaro/issues/226 “GCC 14: No match +for operator= for Givaro::ZRing” + +Recommended in +https://github.com/linbox-team/givaro/issues/226#issuecomment-1908853755 +--- + src/kernel/integer/random-integer.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/kernel/integer/random-integer.h b/src/kernel/integer/random-integer.h +index f9361d33..ea189a36 100644 +--- a/src/kernel/integer/random-integer.h ++++ b/src/kernel/integer/random-integer.h +@@ -87,7 +87,6 @@ namespace Givaro + if (this != &R) { + _bits = R._bits; + _integer = R._integer; +- const_cast(_ring)=R._ring; + } + return *this; + } +-- diff --git a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch b/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index 28fd95088c8..00000000000 --- a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 91dcba743e15288abe69966a5f71704d9adcc57c Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 10:22:57 +0100 -Subject: [PATCH 1/1] remove 1st and last lines in givaro.pc.in - ---- - givaro.pc.in | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/givaro.pc.in b/givaro.pc.in -index 285b854..af38bf3 100644 ---- a/givaro.pc.in -+++ b/givaro.pc.in -@@ -1,4 +1,3 @@ --/------------------ givaro.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@prefix@/lib -@@ -11,4 +10,3 @@ Version: @VERSION@ - Requires: - Libs: -L@libdir@ -lgivaro @LIBS@ - Cflags: -I@includedir@ @REQUIRED_FLAGS@ --\------------------------------------------------------- -\ No newline at end of file --- -2.26.2 - diff --git a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch b/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch deleted file mode 100644 index 3d7e100ad09..00000000000 --- a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch +++ /dev/null @@ -1,2501 +0,0 @@ -diff --git a/src/kernel/recint/reclonglong.h b/src/kernel/recint/reclonglong.h -index cab889b..1b1e07e 100644 ---- a/src/kernel/recint/reclonglong.h -+++ b/src/kernel/recint/reclonglong.h -@@ -41,7 +41,6 @@ - */ - - /* longlong.h may already be included from another library (e.g. flint) */ --#ifndef add_ssaaaa - - #define __BITS4 (W_TYPE_SIZE / 4) - #define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) -@@ -56,14 +55,14 @@ - - /* Define auxiliary asm macros. - -- 1) umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two -+ 1) recint_umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two - UWtype integers MULTIPLIER and MULTIPLICAND, and generates a two UWtype - word product in HIGH_PROD and LOW_PROD. - - 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a -- UDWtype product. This is just a variant of umul_ppmm. -+ UDWtype product. This is just a variant of recint_umul_ppmm. - -- 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -+ 3) recint_udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, - denominator) divides a UDWtype, composed by the UWtype integers - HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient - in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less -@@ -72,24 +71,24 @@ - UDIV_NEEDS_NORMALIZATION is defined to 1. - - 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -- denominator). Like udiv_qrnnd but the numbers are signed. The quotient -+ denominator). Like recint_udiv_qrnnd but the numbers are signed. The quotient - is rounded towards 0. - -- 5) count_leading_zeros(count, x) counts the number of zero-bits from the -+ 5) recint_count_leading_zeros(count, x) counts the number of zero-bits from the - msb to the first non-zero bit in the UWtype X. This is the number of - steps X needs to be shifted left to set the msb. Undefined for X == 0, -- unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. -+ unless the symbol RECINT_COUNT_LEADING_ZEROS_0 is defined to some value. - -- 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts -+ 6) recint_count_trailing_zeros(count, x) like recint_count_leading_zeros, but counts - from the least significant end. - -- 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, -+ 7) recint_add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, - high_addend_2, low_addend_2) adds two UWtype integers, composed by - HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 - respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow - (i.e. carry out) is not stored anywhere, and is lost. - -- 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, -+ 8) recint_sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, - high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, - composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and - LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE -@@ -102,7 +101,7 @@ - - Notes: - -- For add_ssaaaa the two high and two low addends can both commute, but -+ For recint_add_ssaaaa the two high and two low addends can both commute, but - unfortunately gcc only supports one "%" commutative in each asm block. - This has always been so but is only documented in recent versions - (eg. pre-release 3.3). Having two or more "%"s can cause an internal -@@ -123,9 +122,9 @@ - for the CPUs below! */ - - --/* count_leading_zeros_gcc_clz is count_leading_zeros implemented with gcc -+/* recint_count_leading_zeros_gcc_clz is recint_count_leading_zeros implemented with gcc - 3.4 __builtin_clzl or __builtin_clzll, according to our limb size. -- Similarly count_trailing_zeros_gcc_ctz using __builtin_ctzl or -+ Similarly recint_count_trailing_zeros_gcc_ctz using __builtin_ctzl or - __builtin_ctzll. - - These builtins are only used when we check what code comes out, on some -@@ -137,1697 +136,39 @@ - usage. We keep an asm block for use on prior versions of gcc though. - - For reference, __builtin_ffs existed in gcc prior to __builtin_clz, but -- it's not used (for count_leading_zeros) because it generally gives extra -+ it's not used (for recint_count_leading_zeros) because it generally gives extra - code to ensure the result is 0 when the input is 0, which we don't need - or want. */ - - #ifdef _LONG_LONG_LIMB --#define count_leading_zeros_gcc_clz(count,x) \ -+#define recint_count_leading_zeros_gcc_clz(count,x) \ - do { \ - (count) = __builtin_clzll (x); \ - } while (0) - #else --#define count_leading_zeros_gcc_clz(count,x) \ -+#define recint_count_leading_zeros_gcc_clz(count,x) \ - do { \ - (count) = __builtin_clzl (x); \ - } while (0) - #endif - - #ifdef _LONG_LONG_LIMB --#define count_trailing_zeros_gcc_ctz(count,x) \ -+#define recint_count_trailing_zeros_gcc_ctz(count,x) \ - do { \ - (count) = __builtin_ctzll (x); \ - } while (0) - #else --#define count_trailing_zeros_gcc_ctz(count,x) \ -+#define recint_count_trailing_zeros_gcc_ctz(count,x) \ - do { \ - (count) = __builtin_ctzl (x); \ - } while (0) - #endif - - --/* FIXME: The macros using external routines like __MPN(count_leading_zeros) -- don't need to be under !NO_ASM */ --#if ! defined (NO_ASM) -- --#if defined (__alpha) && W_TYPE_SIZE == 64 --/* Most alpha-based machines, except Cray systems. */ --#if defined (__GNUC__) --#if __GMP_GNUC_PREREQ (3,3) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = __builtin_alpha_umulh (__m0, __m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#else --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("umulh %r1,%2,%0" \ -- : "=r" (ph) \ -- : "%rJ" (m0), "rI" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 18 --#else /* ! __GNUC__ */ --#include --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = __UMULH (m0, m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 220 --#endif /* LONGLONG_STANDALONE */ -- --/* clz_tab is required in all configurations, since mpn/alpha/cntlz.asm -- always goes into libgmp.so, even when not actually used. */ --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB -- --#if defined (__GNUC__) && HAVE_HOST_CPU_alpha_CIX --#define count_leading_zeros(COUNT,X) \ -- __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X)) --#define count_trailing_zeros(COUNT,X) \ -- __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X)) --#endif /* clz/ctz using cix */ -- --#if ! defined (count_leading_zeros) \ -- && defined (__GNUC__) && ! defined (LONGLONG_STANDALONE) -- /* ALPHA_CMPBGE_0 gives "cmpbge $31,src,dst", ie. test src bytes == 0. -- "$31" is written explicitly in the asm, since an "r" constraint won't -- select reg 31. There seems no need to worry about "r31" syntax for cray, -- since gcc itself (pre-release 3.4) emits just $31 in various places. */ --#define ALPHA_CMPBGE_0(dst, src) \ -- do { asm ("cmpbge $31, %1, %0" : "=r" (dst) : "r" (src)); } while (0) -- /* Zero bytes are turned into bits with cmpbge, a __clz_tab lookup counts -- them, locating the highest non-zero byte. A second __clz_tab lookup -- counts the leading zero bits in that byte, giving the result. */ --#define count_leading_zeros(count, x) \ -- do { \ -- UWtype __clz__b, __clz__c, __clz__x = (x); \ -- ALPHA_CMPBGE_0 (__clz__b, __clz__x); /* zero bytes */ \ -- __clz__b = __clz_tab [(__clz__b >> 1) ^ 0x7F]; /* 8 to 1 byte */ \ -- __clz__b = __clz__b * 8 - 7; /* 57 to 1 shift */ \ -- __clz__x >>= __clz__b; \ -- __clz__c = __clz_tab [__clz__x]; /* 8 to 1 bit */ \ -- __clz__b = 65 - __clz__b; \ -- (count) = __clz__b - __clz__c; \ -- } while (0) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#endif /* clz using cmpbge */ -- --#if ! defined (count_leading_zeros) && ! defined (LONGLONG_STANDALONE) --#if HAVE_ATTRIBUTE_CONST --long __MPN(count_leading_zeros) (UDItype) __attribute__ ((const)); --#else --long __MPN(count_leading_zeros) (UDItype); --#endif --#define count_leading_zeros(count, x) \ -- ((count) = __MPN(count_leading_zeros) (x)) --#endif /* clz using mpn */ --#endif /* __alpha */ -- --#if defined (__AVR) && W_TYPE_SIZE == 8 --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- unsigned short __p = (unsigned short) (m0) * (m1); \ -- (ph) = __p >> 8; \ -- (pl) = __p; \ -- } while (0) --#endif /* AVR */ -- --#if defined (_CRAY) && W_TYPE_SIZE == 64 --#include --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 220 --long __MPN(count_leading_zeros) (UDItype); --#define count_leading_zeros(count, x) \ -- ((count) = _leadz ((UWtype) (x))) --#if defined (_CRAYIEEE) /* I.e., Cray T90/ieee, T3D, and T3E */ --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = _int_mult_upper (m0, m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#endif /* LONGLONG_STANDALONE */ --#endif /* _CRAYIEEE */ --#endif /* _CRAY */ -- --#if defined (__ia64) && W_TYPE_SIZE == 64 -- /* This form encourages gcc (pre-release 3.4 at least) to emit predicated -- "sub r=r,r" and "sub r=r,r,1", giving a 2 cycle latency. The generic -- code using "al>= _c; \ -- if (_x >= 1 << 4) \ -- _x >>= 4, _c += 4; \ -- if (_x >= 1 << 2) \ -- _x >>= 2, _c += 2; \ -- _c += _x >> 1; \ -- (count) = W_TYPE_SIZE - 1 - _c; \ -- } while (0) -- /* similar to what gcc does for __builtin_ffs, but 0 based rather than 1 -- based, and we don't need a special case for x==0 here */ --#define count_trailing_zeros(count, x) \ -- do { \ -- UWtype __ctz_x = (x); \ -- __asm__ ("popcnt %0 = %1" \ -- : "=r" (count) \ -- : "r" ((__ctz_x-1) & ~__ctz_x)); \ -- } while (0) --#endif --#if defined (__INTEL_COMPILER) --#include --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UWtype _m0 = (m0), _m1 = (m1); \ -- ph = _m64_xmahu (_m0, _m1, 0); \ -- pl = _m0 * _m1; \ -- } while (0) --#endif --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#endif --#define UDIV_TIME 220 --#endif -- -- --#if defined (__GNUC__) -- -- /* We sometimes need to clobber "cc" with gcc2, but that would not be -- understood by gcc1. Use cpp to avoid major code duplication. */ --#if __GNUC__ < 2 --#define __CLOBBER_CC --#define __AND_CLOBBER_CC --#else /* __GNUC__ >= 2 */ --#define __CLOBBER_CC : "cc" --#define __AND_CLOBBER_CC , "cc" --#endif /* __GNUC__ < 2 */ -- --#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add %1,%4,%5\n\taddc %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub %1,%4,%5\n\tsubc %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl)) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("multiplu %0,%1,%2" \ -- : "=r" (xl) \ -- : "r" (__m0), "r" (__m1)); \ -- __asm__ ("multmu %0,%1,%2" \ -- : "=r" (xh) \ -- : "r" (__m0), "r" (__m1)); \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("dividu %0,%3,%4" \ -- : "=r" (q), "=q" (r) \ -- : "1" (n1), "r" (n0), "r" (d)) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) \ -- : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#endif /* __a29k__ */ -- --#if defined (__arc__) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add.f\t%1, %4, %5\n\tadc\t%0, %2, %3" \ -- : "=r" (sh), \ -- "=&r" (sl) \ -- : "r" ((USItype) (ah)), \ -- "rIJ" ((USItype) (bh)), \ -- "%r" ((USItype) (al)), \ -- "rIJ" ((USItype) (bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub.f\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), \ -- "=&r" (sl) \ -- : "r" ((USItype) (ah)), \ -- "rIJ" ((USItype) (bh)), \ -- "r" ((USItype) (al)), \ -- "rIJ" ((USItype) (bl))) --#endif -- --#if defined (__arm__) && !defined (__thumb__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("adds\t%1, %4, %5\n\tadc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (al)) \ -- { \ -- if (__builtin_constant_p (ah)) \ -- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("rsbs\t%1, %5, %4\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- } \ -- else if (__builtin_constant_p (ah)) \ -- { \ -- if (__builtin_constant_p (bl)) \ -- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- } \ -- else if (__builtin_constant_p (bl)) \ -- { \ -- if (__builtin_constant_p (bh)) \ -- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- } \ -- else /* only bh might be a constant */ \ -- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- } while (0) --#if 1 || defined (__arm_m__) /* `M' series has widening multiply support */ --#define umul_ppmm(xh, xl, a, b) \ -- __asm__ ("umull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b)) --#define UMUL_TIME 5 --#define smul_ppmm(xh, xl, a, b) \ -- __asm__ ("smull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b)) --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 70 --#endif /* LONGLONG_STANDALONE */ --#else --#define umul_ppmm(xh, xl, a, b) \ -- __asm__ ("%@ Inlined umul_ppmm\n" \ -- " mov %|r0, %2, lsr #16\n" \ -- " mov %|r2, %3, lsr #16\n" \ -- " bic %|r1, %2, %|r0, lsl #16\n" \ -- " bic %|r2, %3, %|r2, lsl #16\n" \ -- " mul %1, %|r1, %|r2\n" \ -- " mul %|r2, %|r0, %|r2\n" \ -- " mul %|r1, %0, %|r1\n" \ -- " mul %0, %|r0, %0\n" \ -- " adds %|r1, %|r2, %|r1\n" \ -- " addcs %0, %0, #65536\n" \ -- " adds %1, %1, %|r1, lsl #16\n" \ -- " adc %0, %0, %|r1, lsr #16" \ -- : "=&r" (xh), "=r" (xl) \ -- : "r" (a), "r" (b) \ -- : "r0", "r1", "r2") --#define UMUL_TIME 20 --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __r; \ -- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \ -- (r) = __r; \ -- } while (0) --extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype); --#define UDIV_TIME 200 --#endif /* LONGLONG_STANDALONE */ --#endif -- /* This is a bizarre test, but GCC doesn't define useful common symbol. */ --#if defined (__ARM_ARCH_5__) || defined (__ARM_ARCH_5T__) || \ -- defined (__ARM_ARCH_5E__) || defined (__ARM_ARCH_5TE__)|| \ -- defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) || \ -- defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6Z__) || \ -- defined (__ARM_ARCH_6ZK__)|| defined (__ARM_ARCH_6T2__)|| \ -- defined (__ARM_ARCH_6M__) || defined (__ARM_ARCH_7__) || \ -- defined (__ARM_ARCH_7A__) || defined (__ARM_ARCH_7R__) || \ -- defined (__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#endif --#endif /* __arm__ */ -- --#if defined (__aarch64__) && W_TYPE_SIZE == 64 -- /* FIXME: Extend the immediate range for the low word by using both -- ADDS and SUBS, since they set carry in the same way. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("adds\t%1, %x4, %5\n\tadc\t%0, %x2, %x3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rZ" (ah), "rZ" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subs\t%1, %x4, %5\n\tsbc\t%0, %x2, %x3" \ -- : "=r,r" (sh), "=&r,&r" (sl) \ -- : "rZ,rZ" (ah), "rZ,rZ" (bh), "r,Z" (al), "rI,r" (bl) __CLOBBER_CC) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("umulh\t%0, %1, %2" : "=r" (ph) : "r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 64 --#endif /* __aarch64__ */ -- --#if defined (__clipper__) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("mulwux %2,%0" \ -- : "=r" (__x.__ll) \ -- : "%0" ((USItype)(u)), "r" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define smul_ppmm(w1, w0, u, v) \ -- ({union {DItype __ll; \ -- struct {SItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("mulwx %2,%0" \ -- : "=r" (__x.__ll) \ -- : "%0" ((SItype)(u)), "r" ((SItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("mulwux %2,%0" \ -- : "=r" (__w) : "%0" ((USItype)(u)), "r" ((USItype)(v))); \ -- __w; }) --#endif /* __clipper__ */ -- --/* Fujitsu vector computers. */ --#if defined (__uxp__) && W_TYPE_SIZE == 32 --#define umul_ppmm(ph, pl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mult.lu %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \ -- (ph) = __x.__i.__h; \ -- (pl) = __x.__i.__l; \ -- } while (0) --#define smul_ppmm(ph, pl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mult.l %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \ -- (ph) = __x.__i.__h; \ -- (pl) = __x.__i.__l; \ -- } while (0) --#endif -- --#if defined (__gmicro__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add.w %5,%1\n\taddx %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub.w %5,%1\n\tsubx %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define umul_ppmm(ph, pl, m0, m1) \ -- __asm__ ("mulx %3,%0,%1" \ -- : "=g" (ph), "=r" (pl) \ -- : "%0" ((USItype)(m0)), "g" ((USItype)(m1))) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- __asm__ ("divx %4,%0,%1" \ -- : "=g" (q), "=r" (r) \ -- : "1" ((USItype)(nh)), "0" ((USItype)(nl)), "g" ((USItype)(d))) --#define count_leading_zeros(count, x) \ -- __asm__ ("bsch/1 %1,%0" \ -- : "=g" (count) : "g" ((USItype)(x)), "0" ((USItype)0)) --#endif -- --#if defined (__hppa) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%I5 %5,%r4,%1\n\taddc %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%I4 %4,%r5,%1\n\tsubb %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl)) --#if defined (_PA_RISC1_1) --#define umul_ppmm(wh, wl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("xmpyu %1,%2,%0" : "=*f" (__x.__ll) : "*f" (u), "*f" (v)); \ -- (wh) = __x.__i.__h; \ -- (wl) = __x.__i.__l; \ -- } while (0) --#define UMUL_TIME 8 --#define UDIV_TIME 60 --#else --#define UMUL_TIME 40 --#define UDIV_TIME 80 --#endif --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __tmp; \ -- __asm__ ( \ -- "ldi 1,%0\n" \ -- " extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ -- " extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \ -- " ldo 16(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ -- " extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \ -- " ldo 8(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ -- " extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \ -- " ldo 4(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ -- " extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \ -- " ldo 2(%0),%0 ; Yes. Perform add.\n" \ -- " extru %1,30,1,%1 ; Extract bit 1.\n" \ -- " sub %0,%1,%0 ; Subtract it.\n" \ -- : "=r" (count), "=r" (__tmp) : "1" (x)); \ -- } while (0) --#endif /* hppa */ -- -- /* These macros are for ABI=2.0w. In ABI=2.0n they can't be used, since GCC -- (3.2) puts longlong into two adjacent 32-bit registers. Presumably this -- is just a case of no direct support for 2.0n but treating it like 1.0. */ --#if defined (__hppa) && W_TYPE_SIZE == 64 && ! defined (_LONG_LONG_LIMB) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%I5 %5,%r4,%1\n\tadd,dc %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%I4 %4,%r5,%1\n\tsub,db %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl)) --#endif /* hppa */ -- --#if (defined (__i370__) || defined (__s390__) || defined (__mvs__)) && W_TYPE_SIZE == 32 --#if defined (__zarch__) || defined (HAVE_HOST_CPU_s390_zarch) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- /* if (__builtin_constant_p (bl)) \ -- __asm__ ("alfi\t%1,%o5\n\talcr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "%1" (al), "n" (bl) __CLOBBER_CC); \ -- else \ -- */ __asm__ ("alr\t%1,%5\n\talcr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "%1" (al), "r" (bl)__CLOBBER_CC); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- /* if (__builtin_constant_p (bl)) \ -- __asm__ ("slfi\t%1,%o5\n\tslbr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "1" (al), "n" (bl) __CLOBBER_CC); \ -- else \ -- */ __asm__ ("slr\t%1,%5\n\tslbr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "1" (al), "r" (bl) __CLOBBER_CC); \ -- } while (0) --#if __GMP_GNUC_PREREQ (4,5) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__ll = (UDItype) (m0) * (UDItype) (m1); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#else --#if 0 -- /* FIXME: this fails if gcc knows about the 64-bit registers. Use only -- with a new enough processor pretending we have 32-bit registers. */ --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mlr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" (m0), "r" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#else --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- /* When we have 64-bit regs and gcc is aware of that, we cannot simply use -- DImode for the product, since that would be allocated to a single 64-bit -- register, whereas mlr uses the low 32-bits of an even-odd register pair. -- */ \ --register USItype __r0 __asm__ ("0"); \ --register USItype __r1 __asm__ ("1") = (m0); \ --__asm__ ("mlr\t%0,%3" \ --: "=r" (__r0), "=r" (__r1) \ -- : "r" (__r1), "r" (m1)); \ --(xh) = __r0; (xl) = __r1; \ --} while (0) --#endif /* if 0 */ --#endif --#if 0 --/* FIXME: this fails if gcc knows about the 64-bit registers. Use only -- with a new enough processor pretending we have 32-bit registers. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dlr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" (d)); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#else --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- register USItype __r0 __asm__ ("0") = (n1); \ -- register USItype __r1 __asm__ ("1") = (n0); \ -- __asm__ ("dlr\t%0,%4" \ -- : "=r" (__r0), "=r" (__r1) \ -- : "r" (__r0), "r" (__r1), "r" (d)); \ -- (q) = __r1; (r) = __r0; \ -- } while (0) --#endif /* if 0 */ --#else /* if __zarch__ */ --/* FIXME: this fails if gcc knows about the 64-bit registers. */ --#define smul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {DItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" (m0), "r" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --/* FIXME: this fails if gcc knows about the 64-bit registers. */ --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {DItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" (d)); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#endif /* if __zarch__ */ --#endif -- --#if defined (__s390x__) && W_TYPE_SIZE == 64 --/* We need to cast operands with register constraints, otherwise their types -- will be assumed to be SImode by gcc. For these machines, such operations -- will insert a value into the low 32 bits, and leave the high 32 bits with -- garbage. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- __asm__ ("algr\t%1,%5\n\talcgr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \ -- "%1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- __asm__ ("slgr\t%1,%5\n\tslbgr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \ -- "1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \ -- } while (0) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mlgr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" ((UDItype)(m0)), "r" ((UDItype)(m1))); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dlgr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" ((UDItype)(d))); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#if 0 /* FIXME: Enable for z10 (?) */ --#define count_leading_zeros(cnt, x) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __clr_cnt; \ -- __asm__ ("flogr\t%0,%1" \ -- : "=r" (__clr_cnt.__ll) \ -- : "r" (x) __CLOBBER_CC); \ -- (cnt) = __clr_cnt.__i.__h; \ -- } while (0) --#endif --#endif -- --#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addl %5,%k1\n\tadcl %3,%k0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subl %5,%k1\n\tsbbl %3,%k0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mull %3" \ -- : "=a" (w0), "=d" (w1) \ -- : "%0" ((USItype)(u)), "rm" ((USItype)(v))) --#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \ -- __asm__ ("divl %4" /* stringification in K&R C */ \ -- : "=a" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "rm" ((USItype)(dx))) -- --#if HAVE_HOST_CPU_i586 || HAVE_HOST_CPU_pentium || HAVE_HOST_CPU_pentiummmx --/* Pentium bsrl takes between 10 and 72 cycles depending where the most -- significant 1 bit is, hence the use of the following alternatives. bsfl -- is slow too, between 18 and 42 depending where the least significant 1 -- bit is, so let the generic count_trailing_zeros below make use of the -- count_leading_zeros here too. */ -- --#if HAVE_HOST_CPU_pentiummmx && ! defined (LONGLONG_STANDALONE) --/* The following should be a fixed 14 or 15 cycles, but possibly plus an L1 -- cache miss reading from __clz_tab. For P55 it's favoured over the float -- below so as to avoid mixing MMX and x87, since the penalty for switching -- between the two is about 100 cycles. -- -- The asm block sets __shift to -3 if the high 24 bits are clear, -2 for -- 16, -1 for 8, or 0 otherwise. This could be written equivalently as -- follows, but as of gcc 2.95.2 it results in conditional jumps. -- -- __shift = -(__n < 0x1000000); -- __shift -= (__n < 0x10000); -- __shift -= (__n < 0x100); -- -- The middle two sbbl and cmpl's pair, and with luck something gcc -- generates might pair with the first cmpl and the last sbbl. The "32+1" -- constant could be folded into __clz_tab[], but it doesn't seem worth -- making a different table just for that. */ -- --#define count_leading_zeros(c,n) \ -- do { \ -- USItype __n = (n); \ -- USItype __shift; \ -- __asm__ ("cmpl $0x1000000, %1\n" \ -- "sbbl %0, %0\n" \ -- "cmpl $0x10000, %1\n" \ -- "sbbl $0, %0\n" \ -- "cmpl $0x100, %1\n" \ -- "sbbl $0, %0\n" \ -- : "=&r" (__shift) : "r" (__n)); \ -- __shift = __shift*8 + 24 + 1; \ -- (c) = 32 + 1 - __shift - __clz_tab[__n >> __shift]; \ -- } while (0) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#define COUNT_LEADING_ZEROS_0 31 /* n==0 indistinguishable from n==1 */ -- --#else /* ! pentiummmx || LONGLONG_STANDALONE */ --/* The following should be a fixed 14 cycles or so. Some scheduling -- opportunities should be available between the float load/store too. This -- sort of code is used in gcc 3 for __builtin_ffs (with "n&-n") and is -- apparently suggested by the Intel optimizing manual (don't know exactly -- where). gcc 2.95 or up will be best for this, so the "double" is -- correctly aligned on the stack. */ --#define count_leading_zeros(c,n) \ -- do { \ -- union { \ -- double d; \ -- unsigned a[2]; \ -- } __u; \ -- __u.d = (UWtype) (n); \ -- (c) = 0x3FF + 31 - (__u.a[1] >> 20); \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 (0x3FF + 31) --#endif /* pentiummx */ -- --#else /* ! pentium */ -- --#if __GMP_GNUC_PREREQ (3,4) /* using bsrl */ --#define count_leading_zeros(count,x) count_leading_zeros_gcc_clz(count,x) --#endif /* gcc clz */ -- --/* On P6, gcc prior to 3.0 generates a partial register stall for -- __cbtmp^31, due to using "xorb $31" instead of "xorl $31", the former -- being 1 code byte smaller. "31-__cbtmp" is a workaround, probably at the -- cost of one extra instruction. Do this for "i386" too, since that means -- generic x86. */ --#if ! defined (count_leading_zeros) && __GNUC__ < 3 \ -- && (HAVE_HOST_CPU_i386 \ -- || HAVE_HOST_CPU_i686 \ -- || HAVE_HOST_CPU_pentiumpro \ -- || HAVE_HOST_CPU_pentium2 \ -- || HAVE_HOST_CPU_pentium3) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ -- (count) = 31 - __cbtmp; \ -- } while (0) --#endif /* gcc<3 asm bsrl */ -- --#ifndef count_leading_zeros --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#endif /* asm bsrl */ -- --#if __GMP_GNUC_PREREQ (3,4) /* using bsfl */ --#define count_trailing_zeros(count,x) count_trailing_zeros_gcc_ctz(count,x) --#endif /* gcc ctz */ -- --#ifndef count_trailing_zeros --#define count_trailing_zeros(count, x) \ -- do { \ -- __asm__ ("bsfl %1,%k0" : "=r" (count) : "rm" ((USItype)(x))); \ -- } while (0) --#endif /* asm bsfl */ -- --#endif /* ! pentium */ -- --#ifndef UMUL_TIME --#define UMUL_TIME 10 --#endif --#ifndef UDIV_TIME --#define UDIV_TIME 40 --#endif --#endif /* 80x86 */ -- --#if defined (__amd64__) && W_TYPE_SIZE == 64 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addq %5,%q1\n\tadcq %3,%q0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \ -- "%1" ((UDItype)(al)), "rme" ((UDItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subq %5,%q1\n\tsbbq %3,%q0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \ -- "1" ((UDItype)(al)), "rme" ((UDItype)(bl))) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulq %3" \ -- : "=a" (w0), "=d" (w1) \ -- : "%0" ((UDItype)(u)), "rm" ((UDItype)(v))) --#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \ -- __asm__ ("divq %4" /* stringification in K&R C */ \ -- : "=a" (q), "=d" (r) \ -- : "0" ((UDItype)(n0)), "1" ((UDItype)(n1)), "rm" ((UDItype)(dx))) --/* bsrq destination must be a 64-bit register, hence UDItype for __cbtmp. */ --#define count_leading_zeros(count, x) \ -- do { \ -- UDItype __cbtmp; \ -- __asm__ ("bsrq %1,%0" : "=r" (__cbtmp) : "rm" ((UDItype)(x))); \ -- (count) = __cbtmp ^ 63; \ -- } while (0) --/* bsfq destination must be a 64-bit register, "%q0" forces this in case -- count is only an int. */ --#define count_trailing_zeros(count, x) \ -- do { \ -- __asm__ ("bsfq %1,%q0" : "=r" (count) : "rm" ((UDItype)(x))); \ -- } while (0) --#endif /* x86_64 */ -- --#if defined (__i860__) && W_TYPE_SIZE == 32 --#define rshift_rhlc(r,h,l,c) \ -- __asm__ ("shr %3,r0,r0\;shrd %1,%2,%0" \ -- "=r" (r) : "r" (h), "r" (l), "rn" (c)) --#endif /* i860 */ -- --#if defined (__i960__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("cmpo 1,0\;addc %5,%4,%1\;addc %3,%2,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "dI" (ah), "dI" (bh), "%dI" (al), "dI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("cmpo 0,0\;subc %5,%4,%1\;subc %3,%2,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "dI" (ah), "dI" (bh), "dI" (al), "dI" (bl)) --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("emul %2,%1,%0" \ -- : "=d" (__x.__ll) : "%dI" (u), "dI" (v)); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("emul %2,%1,%0" : "=d" (__w) : "%dI" (u), "dI" (v)); \ -- __w; }) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __nn; \ -- __nn.__i.__h = (nh); __nn.__i.__l = (nl); \ -- __asm__ ("ediv %d,%n,%0" \ -- : "=d" (__rq.__ll) : "dI" (__nn.__ll), "dI" (d)); \ -- (r) = __rq.__i.__l; (q) = __rq.__i.__h; \ -- } while (0) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("scanbit %1,%0" : "=r" (__cbtmp) : "r" (x)); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 (-32) /* sic */ --#if defined (__i960mx) /* what is the proper symbol to test??? */ --#define rshift_rhlc(r,h,l,c) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __nn; \ -- __nn.__i.__h = (h); __nn.__i.__l = (l); \ -- __asm__ ("shre %2,%1,%0" : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \ -- } --#endif /* i960mx */ --#endif /* i960 */ -- --#if (defined (__mc68000__) || defined (__mc68020__) || defined(mc68020) \ -- || defined (__m68k__) || defined (__mc5200__) || defined (__mc5206e__) \ -- || defined (__mc5307__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \ -- : "=d" (sh), "=&d" (sl) \ -- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \ -- : "=d" (sh), "=&d" (sl) \ -- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) -- /* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */ --#if defined (__mc68020__) || defined(mc68020) \ -- || defined (__mc68030__) || defined (mc68030) \ -- || defined (__mc68040__) || defined (mc68040) \ -- || defined (__mcpu32__) || defined (mcpu32) \ -- || defined (__NeXT__) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulu%.l %3,%1:%0" \ -- : "=d" (w0), "=d" (w1) \ -- : "%0" ((USItype)(u)), "dmi" ((USItype)(v))) --#define UMUL_TIME 45 --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("divu%.l %4,%1:%0" \ -- : "=d" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d))) --#define UDIV_TIME 90 --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("divs%.l %4,%1:%0" \ -- : "=d" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d))) --#else /* for other 68k family members use 16x16->32 multiplication */ --#define umul_ppmm(xh, xl, a, b) \ -- do { USItype __umul_tmp1, __umul_tmp2; \ -- __asm__ ("| Inlined umul_ppmm\n" \ -- " move%.l %5,%3\n" \ -- " move%.l %2,%0\n" \ -- " move%.w %3,%1\n" \ -- " swap %3\n" \ -- " swap %0\n" \ -- " mulu%.w %2,%1\n" \ -- " mulu%.w %3,%0\n" \ -- " mulu%.w %2,%3\n" \ -- " swap %2\n" \ -- " mulu%.w %5,%2\n" \ -- " add%.l %3,%2\n" \ -- " jcc 1f\n" \ -- " add%.l %#0x10000,%0\n" \ -- "1: move%.l %2,%3\n" \ -- " clr%.w %2\n" \ -- " swap %2\n" \ -- " swap %3\n" \ -- " clr%.w %3\n" \ -- " add%.l %3,%1\n" \ -- " addx%.l %2,%0\n" \ -- " | End inlined umul_ppmm" \ -- : "=&d" (xh), "=&d" (xl), \ -- "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \ -- : "%2" ((USItype)(a)), "d" ((USItype)(b))); \ -- } while (0) --#define UMUL_TIME 100 --#define UDIV_TIME 400 --#endif /* not mc68020 */ -- /* The '020, '030, '040 and '060 have bitfield insns. -- GCC 3.4 defines __mc68020__ when in CPU32 mode, check for __mcpu32__ to -- exclude bfffo on that chip (bitfield insns not available). */ --#if (defined (__mc68020__) || defined (mc68020) \ -- || defined (__mc68030__) || defined (mc68030) \ -- || defined (__mc68040__) || defined (mc68040) \ -- || defined (__mc68060__) || defined (mc68060) \ -- || defined (__NeXT__)) \ -- && ! defined (__mcpu32__) --#define count_leading_zeros(count, x) \ -- __asm__ ("bfffo %1{%b2:%b2},%0" \ -- : "=d" (count) \ -- : "od" ((USItype) (x)), "n" (0)) --#define COUNT_LEADING_ZEROS_0 32 --#endif --#endif /* mc68000 */ -- --#if defined (__m88000__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rJ" (bh), "%rJ" (al), "rJ" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rJ" (bh), "rJ" (al), "rJ" (bl)) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("ff1 %0,%1" : "=r" (__cbtmp) : "r" (x)); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 63 /* sic */ --#if defined (__m88110__) --#define umul_ppmm(wh, wl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \ -- (wh) = __x.__i.__h; \ -- (wl) = __x.__i.__l; \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- ({union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x, __q; \ -- __x.__i.__h = (n1); __x.__i.__l = (n0); \ -- __asm__ ("divu.d %0,%1,%2" \ -- : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \ -- (r) = (n0) - __q.__l * (d); (q) = __q.__l; }) --#define UMUL_TIME 5 --#define UDIV_TIME 25 --#else --#define UMUL_TIME 17 --#define UDIV_TIME 150 --#endif /* __m88110__ */ --#endif /* __m88000__ */ -- --#if defined (__mips) && W_TYPE_SIZE == 32 --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- UDItype __ll = (UDItype)(u) * (v); \ -- w1 = __ll >> 32; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("multu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v)) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("multu %2,%3\n\tmflo %0\n\tmfhi %1" \ -- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v)) --#endif --#define UMUL_TIME 10 --#define UDIV_TIME 100 --#endif /* __mips */ -- --#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64 --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \ -- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \ -- w1 = __ll >> 64; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmultu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v)) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmultu %2,%3\n\tmflo %0\n\tmfhi %1" \ -- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v)) --#endif --#define UMUL_TIME 20 --#define UDIV_TIME 140 --#endif /* __mips */ -- --#if defined (__mmix__) && W_TYPE_SIZE == 64 --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("MULU %0,%2,%3" : "=r" (w0), "=z" (w1) : "r" (u), "r" (v)) --#endif -- --#if defined (__ns32000__) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("meid %2,%0" \ -- : "=g" (__x.__ll) \ -- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("meid %2,%0" \ -- : "=g" (__w) \ -- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \ -- __w; }) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __x.__i.__h = (n1); __x.__i.__l = (n0); \ -- __asm__ ("deid %2,%0" \ -- : "=g" (__x.__ll) \ -- : "0" (__x.__ll), "g" ((USItype)(d))); \ -- (r) = __x.__i.__l; (q) = __x.__i.__h; }) --#define count_trailing_zeros(count,x) \ -- do { \ -- __asm__ ("ffsd %2,%0" \ -- : "=r" (count) \ -- : "0" ((USItype) 0), "r" ((USItype) (x))); \ -- } while (0) --#endif /* __ns32000__ */ -- -- /* In the past we had a block of various #defines tested -- _ARCH_PPC - AIX -- _ARCH_PWR - AIX -- __powerpc__ - gcc -- __POWERPC__ - BEOS -- __ppc__ - Darwin -- PPC - old gcc, GNU/Linux, SysV -- The plain PPC test was not good for vxWorks, since PPC is defined on all -- CPUs there (eg. m68k too), as a constant one is expected to compare -- CPU_FAMILY against. -- -- At any rate, this was pretty unattractive and a bit fragile. The use of -- HAVE_HOST_CPU_FAMILY is designed to cut through it all and be sure of -- getting the desired effect. -- -- ENHANCE-ME: We should test _IBMR2 here when we add assembly support for -- the system vendor compilers. (Is that vendor compilers with inline asm, -- or what?) */ -- --#if (HAVE_HOST_CPU_FAMILY_power || HAVE_HOST_CPU_FAMILY_powerpc) \ -- && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else \ -- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else \ -- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -- } while (0) --#define count_leading_zeros(count, x) \ -- __asm__ ("cntlzw %0,%1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#if HAVE_HOST_CPU_FAMILY_powerpc --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- UDItype __ll = (UDItype)(u) * (v); \ -- w1 = __ll >> 32; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 15 --#define smul_ppmm(ph, pl, m0, m1) \ -- do { \ -- SItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define SMUL_TIME 14 --#define UDIV_TIME 120 --#else --#define UMUL_TIME 8 --#define smul_ppmm(xh, xl, m0, m1) \ -- __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1)) --#define SMUL_TIME 4 --#define sdiv_qrnnd(q, r, nh, nl, d) \ -- __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d)) --#define UDIV_TIME 100 --#endif --#endif /* 32-bit POWER architecture variants. */ -- -- /* We should test _IBMR2 here when we add assembly support for the system -- vendor compilers. */ --#if HAVE_HOST_CPU_FAMILY_powerpc && W_TYPE_SIZE == 64 --#if !defined (_LONG_LONG_LIMB) -- /* _LONG_LONG_LIMB is ABI=mode32 where adde operates on 32-bit values. So -- use adde etc only when not _LONG_LONG_LIMB. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else \ -- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -- } while (0) -- /* We use "*rI" for the constant operand here, since with just "I", gcc barfs. -- This might seem strange, but gcc folds away the dead code late. */ --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bl) && bl > -0x8000 && bl <= 0x8000) { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("addic %1,%3,%4\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ -- __asm__ ("addic %1,%3,%4\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("addic %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("addic %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \ -- else \ -- __asm__ ("addic %1,%4,%5\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "*rI" (-bl)); \ -- } else { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else \ -- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -- } \ -- } while (0) --#endif /* ! _LONG_LONG_LIMB */ --#define count_leading_zeros(count, x) \ -- __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 64 --#if 0 && __GMP_GNUC_PREREQ (4,4) /* Disable, this results in libcalls! */ --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \ -- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \ -- w1 = __ll >> 64; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 15 --#define smul_ppmm(ph, pl, m0, m1) \ -- do { \ -- DItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define SMUL_TIME 14 /* ??? */ --#define UDIV_TIME 120 /* ??? */ --#endif /* 64-bit PowerPC. */ -- --#if defined (__pyr__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addw %5,%1\n\taddwc %3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subw %5,%1\n\tsubwb %3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) -- /* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */ --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("movw %1,%R0\n\tuemul %2,%0" \ -- : "=&r" (__x.__ll) \ -- : "g" ((USItype) (u)), "g" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#endif /* __pyr__ */ -- --#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("a %1,%5\n\tae %0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "r" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("s %1,%5\n\tse %0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "r" ((USItype)(bl))) --#define smul_ppmm(ph, pl, m0, m1) \ -- __asm__ ( \ -- "s r2,r2\n" \ -- " mts r10,%2\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " cas %0,r2,r0\n" \ -- " mfs r10,%1" \ -- : "=r" (ph), "=r" (pl) \ -- : "%r" ((USItype)(m0)), "r" ((USItype)(m1)) \ -- : "r2") --#define UMUL_TIME 20 --#define UDIV_TIME 200 --#define count_leading_zeros(count, x) \ -- do { \ -- if ((x) >= 0x10000) \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) : "r" ((USItype)(x) >> 16)); \ -- else \ -- { \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) : "r" ((USItype)(x))); \ -- (count) += 16; \ -- } \ -- } while (0) --#endif /* RT/ROMP */ -- --#if (defined (__SH2__) || defined (__SH3__) || defined (__SH4__)) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \ -- : "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "macl", "mach") --#define UMUL_TIME 5 --#endif -- --#if defined (__sparc__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh),"%rJ" (al), "rI" (bl) \ -- __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl) \ -- __CLOBBER_CC) -- /* FIXME: When gcc -mcpu=v9 is used on solaris, gcc/config/sol2-sld-64.h -- doesn't define anything to indicate that to us, it only sets __sparcv8. */ --#if defined (__sparc_v9__) || defined (__sparcv9) -- /* Perhaps we should use floating-point operations here? */ --#if 0 -- /* Triggers a bug making mpz/tests/t-gcd.c fail. -- Perhaps we simply need explicitly zero-extend the inputs? */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulx %2,%3,%%g1; srl %%g1,0,%1; srlx %%g1,32,%0" : \ -- "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "g1") --#else -- /* Use v8 umul until above bug is fixed. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#endif -- /* Use a plain v8 divide for v9. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- USItype __q; \ -- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \ -- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \ -- (r) = (n0) - __q * (d); \ -- (q) = __q; \ -- } while (0) --#else --#if defined (__sparc_v8__) /* gcc normal */ \ -- || defined (__sparcv8) /* gcc solaris */ \ -- || HAVE_HOST_CPU_supersparc -- /* Don't match immediate range because, 1) it is not often useful, -- 2) the 'I' flag thinks of the range as a 13 bit signed interval, -- while we want to match a 13 bit interval, sign extended to 32 bits, -- but INTERPRETED AS UNSIGNED. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#define UMUL_TIME 5 -- --#if HAVE_HOST_CPU_supersparc --#define UDIV_TIME 60 /* SuperSPARC timing */ --#else -- /* Don't use this on SuperSPARC because its udiv only handles 53 bit -- dividends and will trap to the kernel for the rest. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- USItype __q; \ -- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \ -- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \ -- (r) = (n0) - __q * (d); \ -- (q) = __q; \ -- } while (0) --#define UDIV_TIME 25 --#endif /* HAVE_HOST_CPU_supersparc */ -- --#else /* ! __sparc_v8__ */ --#if defined (__sparclite__) -- /* This has hardware multiply but not divide. It also has two additional -- instructions scan (ffs from high bit) and divscc. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#define UMUL_TIME 5 --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("! Inlined udiv_qrnnd\n" \ -- " wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \ -- " tst %%g0\n" \ -- " divscc %3,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%0\n" \ -- " rd %%y,%1\n" \ -- " bl,a 1f\n" \ -- " add %1,%4,%1\n" \ -- "1: ! End of inline udiv_qrnnd" \ -- : "=r" (q), "=r" (r) : "r" (n1), "r" (n0), "rI" (d) \ -- : "%g1" __AND_CLOBBER_CC) --#define UDIV_TIME 37 --#define count_leading_zeros(count, x) \ -- __asm__ ("scan %1,1,%0" : "=r" (count) : "r" (x)) -- /* Early sparclites return 63 for an argument of 0, but they warn that future -- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 -- undefined. */ --#endif /* __sparclite__ */ --#endif /* __sparc_v8__ */ --#endif /* __sparc_v9__ */ -- /* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */ --#ifndef umul_ppmm --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("! Inlined umul_ppmm\n" \ -- " wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n" \ -- " sra %3,31,%%g2 ! Don't move this insn\n" \ -- " and %2,%%g2,%%g2 ! Don't move this insn\n" \ -- " andcc %%g0,0,%%g1 ! Don't move this insn\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,0,%%g1\n" \ -- " add %%g1,%%g2,%0\n" \ -- " rd %%y,%1" \ -- : "=r" (w1), "=r" (w0) : "%rI" (u), "r" (v) \ -- : "%g1", "%g2" __AND_CLOBBER_CC) --#define UMUL_TIME 39 /* 39 instructions */ --#endif --#ifndef udiv_qrnnd --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __r; \ -- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \ -- (r) = __r; \ -- } while (0) -- extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype); --#ifndef UDIV_TIME --#define UDIV_TIME 140 --#endif --#endif /* LONGLONG_STANDALONE */ --#endif /* udiv_qrnnd */ --#endif /* __sparc__ */ -- --#if defined (__sparc__) && W_TYPE_SIZE == 64 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ( \ -- "addcc %r4,%5,%1\n" \ -- " addccc %r6,%7,%%g0\n" \ -- " addc %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "%rJ" (al), "rI" (bl), \ -- "%rJ" ((al) >> 32), "rI" ((bl) >> 32) \ -- __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ( \ -- "subcc %r4,%5,%1\n" \ -- " subccc %r6,%7,%%g0\n" \ -- " subc %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl), \ -- "rJ" ((al) >> 32), "rI" ((bl) >> 32) \ -- __CLOBBER_CC) --#endif -- --#if (defined (__vax) || defined (__vax__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define smul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("emul %1,%2,$0,%0" \ -- : "=g" (__x.__ll) : "g" (__m0), "g" (__m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {DItype __ll; \ -- struct {SItype __l, __h;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("ediv %3,%2,%0,%1" \ -- : "=g" (q), "=g" (r) : "g" (__x.__ll), "g" (d)); \ -- } while (0) --#if 0 -- /* FIXME: This instruction appears to be unimplemented on some systems (vax -- 8800 maybe). */ --#define count_trailing_zeros(count,x) \ -- do { \ -- __asm__ ("ffs 0, 31, %1, %0" \ -- : "=g" (count) \ -- : "g" ((USItype) (x))); \ -- } while (0) --#endif --#endif /* vax */ -- --#if defined (__z8000__) && W_TYPE_SIZE == 16 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \ -- "%1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \ -- "1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl))) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {long int __ll; \ -- struct {unsigned int __h, __l;} __i; \ -- } __x; \ -- unsigned int __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mult %S0,%H3" \ -- : "=r" (__x.__i.__h), "=r" (__x.__i.__l) \ -- : "%1" (m0), "rQR" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- (xh) += ((((signed int) __m0 >> 15) & __m1) \ -- + (((signed int) __m1 >> 15) & __m0)); \ -- } while (0) --#endif /* __z8000__ */ -- --#endif /* __GNUC__ */ -- --#endif /* NO_ASM */ -- - - /* FIXME: "sidi" here is highly doubtful, should sometimes be "diti". */ --#if !defined (umul_ppmm) && defined (__umulsidi3) --#define umul_ppmm(ph, pl, m0, m1) \ -+#if !defined (recint_umul_ppmm) && defined (__umulsidi3) -+#define recint_umul_ppmm(ph, pl, m0, m1) \ - { \ - UDWtype __ll = __umulsidi3 (m0, m1); \ - ph = (UWtype) (__ll >> W_TYPE_SIZE); \ -@@ -1838,75 +179,75 @@ __asm__ ("mlr\t%0,%3" \ - #if !defined (__umulsidi3) - #define __umulsidi3(u, v) \ - ({UWtype __hi, __lo; \ -- umul_ppmm (__hi, __lo, u, v); \ -+ recint_umul_ppmm (__hi, __lo, u, v); \ - ((UDWtype) __hi << W_TYPE_SIZE) | __lo; }) - #endif - - -- /* Use mpn_umul_ppmm or mpn_udiv_qrnnd functions, if they exist. The "_r" -+ /* Use mpn_recint_umul_ppmm or mpn_recint_udiv_qrnnd functions, if they exist. The "_r" - forms have "reversed" arguments, meaning the pointer is last, which - sometimes allows better parameter passing, in particular on 64-bit - hppa. */ - --#define mpn_umul_ppmm __MPN(umul_ppmm) --extern UWtype mpn_umul_ppmm (UWtype *, UWtype, UWtype); -+#define mpn_recint_umul_ppmm __MPN(recint_umul_ppmm) -+extern UWtype mpn_recint_umul_ppmm (UWtype *, UWtype, UWtype); - --#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm \ -+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm \ - && ! defined (LONGLONG_STANDALONE) --#define umul_ppmm(wh, wl, u, v) \ -+#define recint_umul_ppmm(wh, wl, u, v) \ - do { \ -- UWtype __umul_ppmm__p0; \ -- (wh) = mpn_umul_ppmm (&__umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \ -- (wl) = __umul_ppmm__p0; \ -+ UWtype __recint_umul_ppmm__p0; \ -+ (wh) = mpn_recint_umul_ppmm (&__recint_umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \ -+ (wl) = __recint_umul_ppmm__p0; \ - } while (0) - #endif - --#define mpn_umul_ppmm_r __MPN(umul_ppmm_r) --extern UWtype mpn_umul_ppmm_r (UWtype, UWtype, UWtype *); -+#define mpn_recint_umul_ppmm_r __MPN(recint_umul_ppmm_r) -+extern UWtype mpn_recint_umul_ppmm_r (UWtype, UWtype, UWtype *); - --#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm_r \ -+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm_r \ - && ! defined (LONGLONG_STANDALONE) --#define umul_ppmm(wh, wl, u, v) \ -+#define recint_umul_ppmm(wh, wl, u, v) \ - do { \ -- UWtype __umul_ppmm__p0; \ -- (wh) = mpn_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__umul_ppmm__p0); \ -- (wl) = __umul_ppmm__p0; \ -+ UWtype __recint_umul_ppmm__p0; \ -+ (wh) = mpn_recint_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__recint_umul_ppmm__p0); \ -+ (wl) = __recint_umul_ppmm__p0; \ - } while (0) - #endif - --#define mpn_udiv_qrnnd __MPN(udiv_qrnnd) --extern UWtype mpn_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype); -+#define mpn_recint_udiv_qrnnd __MPN(recint_udiv_qrnnd) -+extern UWtype mpn_recint_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype); - --#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd \ -+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd \ - && ! defined (LONGLONG_STANDALONE) --#define udiv_qrnnd(q, r, n1, n0, d) \ -+#define recint_udiv_qrnnd(q, r, n1, n0, d) \ - do { \ -- UWtype __udiv_qrnnd__r; \ -- (q) = mpn_udiv_qrnnd (&__udiv_qrnnd__r, \ -+ UWtype __recint_udiv_qrnnd__r; \ -+ (q) = mpn_recint_udiv_qrnnd (&__recint_udiv_qrnnd__r, \ - (UWtype) (n1), (UWtype) (n0), (UWtype) d); \ -- (r) = __udiv_qrnnd__r; \ -+ (r) = __recint_udiv_qrnnd__r; \ - } while (0) - #endif - --#define mpn_udiv_qrnnd_r __MPN(udiv_qrnnd_r) --extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); -+#define mpn_recint_udiv_qrnnd_r __MPN(recint_udiv_qrnnd_r) -+extern UWtype mpn_recint_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - --#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd_r \ -+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd_r \ - && ! defined (LONGLONG_STANDALONE) --#define udiv_qrnnd(q, r, n1, n0, d) \ -+#define recint_udiv_qrnnd(q, r, n1, n0, d) \ - do { \ -- UWtype __udiv_qrnnd__r; \ -- (q) = mpn_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \ -- &__udiv_qrnnd__r); \ -- (r) = __udiv_qrnnd__r; \ -+ UWtype __recint_udiv_qrnnd__r; \ -+ (q) = mpn_recint_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \ -+ &__recint_udiv_qrnnd__r); \ -+ (r) = __recint_udiv_qrnnd__r; \ - } while (0) - #endif - - - /* If this machine has no inline assembler, use C macros. */ - --#if !defined (add_ssaaaa) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+#if !defined (recint_add_ssaaaa) -+#define recint_add_ssaaaa(sh, sl, ah, al, bh, bl) \ - do { \ - UWtype __x; \ - __x = (al) + (bl); \ -@@ -1915,8 +256,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - --#if !defined (sub_ddmmss) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+#if !defined (recint_sub_ddmmss) -+#define recint_sub_ddmmss(sh, sl, ah, al, bh, bl) \ - do { \ - UWtype __x; \ - __x = (al) - (bl); \ -@@ -1925,20 +266,20 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - -- /* If we lack umul_ppmm but have smul_ppmm, define umul_ppmm in terms of -- smul_ppmm. */ --#if !defined (umul_ppmm) && defined (smul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -+ /* If we lack recint_umul_ppmm but have recint_smul_ppmm, define recint_umul_ppmm in terms of -+ recint_smul_ppmm. */ -+#if !defined (recint_umul_ppmm) && defined (recint_smul_ppmm) -+#define recint_umul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __w1; \ - UWtype __xm0 = (u), __xm1 = (v); \ -- smul_ppmm (__w1, w0, __xm0, __xm1); \ -+ recint_smul_ppmm (__w1, w0, __xm0, __xm1); \ - (w1) = __w1 + (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \ - + (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \ - } while (0) - #endif - -- /* If we still don't have umul_ppmm, define it using plain C. -+ /* If we still don't have recint_umul_ppmm, define it using plain C. - - For reference, when this code is used for squaring (ie. u and v identical - expressions), gcc recognises __x1 and __x2 are the same and generates 3 -@@ -1948,8 +289,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - performance problems than a couple of extra instructions on the diagonal - of sqr_basecase. */ - --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -+#if !defined (recint_umul_ppmm) -+#define recint_umul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __x0, __x1, __x2, __x3; \ - UHWtype __ul, __vl, __uh, __vh; \ -@@ -1975,21 +316,21 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - -- /* If we don't have smul_ppmm, define it using umul_ppmm (which surely will -+ /* If we don't have recint_smul_ppmm, define it using recint_umul_ppmm (which surely will - exist in one form or another. */ --#if !defined (smul_ppmm) --#define smul_ppmm(w1, w0, u, v) \ -+#if !defined (recint_smul_ppmm) -+#define recint_smul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __w1; \ - UWtype __xm0 = (u), __xm1 = (v); \ -- umul_ppmm (__w1, w0, __xm0, __xm1); \ -+ recint_umul_ppmm (__w1, w0, __xm0, __xm1); \ - (w1) = __w1 - (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \ - - (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \ - } while (0) - #endif - - /* Define this unconditionally, so it can be used for debugging. */ --#define __udiv_qrnnd_c(q, r, n1, n0, d) \ -+#define __recint_udiv_qrnnd_c(q, r, n1, n0, d) \ - do { \ - UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ - \ -@@ -2026,26 +367,27 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - (r) = __r0; \ - } while (0) - -- /* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through -+ /* If the processor has no recint_udiv_qrnnd but sdiv_qrnnd, go through - __udiv_w_sdiv (defined in libgcc or elsewhere). */ --#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- do { \ -- UWtype __r; \ -- (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \ -- (r) = __r; \ -- } while (0) --UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); --#endif -- -- /* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ --#if !defined (udiv_qrnnd) -+/* -+#if !defined (recint_udiv_qrnnd) && defined (sdiv_qrnnd) -+ #define recint_udiv_qrnnd(q, r, nh, nl, d) \ -+ do { \ -+ UWtype __r; \ -+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \ -+ (r) = __r; \ -+ } while (0) -+ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); -+ #endif -+*/ -+ /* If recint_udiv_qrnnd was not defined for this processor, use __recint_udiv_qrnnd_c. */ -+#if !defined (recint_udiv_qrnnd) - #define UDIV_NEEDS_NORMALIZATION 1 --#define udiv_qrnnd __udiv_qrnnd_c -+#define recint_udiv_qrnnd __recint_udiv_qrnnd_c - #endif - --#if !defined (count_leading_zeros) --#define count_leading_zeros(count, x) \ -+#if !defined (recint_count_leading_zeros) -+#define recint_count_leading_zeros(count, x) \ - do { \ - UWtype __xr = (x); \ - UWtype __a; \ -@@ -2068,35 +410,35 @@ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); - (count) = W_TYPE_SIZE + 1 - __a - __clz_tab[__xr >> __a]; \ - } while (0) - /* This version gives a well-defined value for zero. */ --#define COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#define COUNT_LEADING_ZEROS_SLOW -+#define RECINT_COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1) -+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#define RECINT_COUNT_LEADING_ZEROS_SLOW - #endif - - /* clz_tab needed by mpn/x86/pentium/mod_1.asm in a fat binary */ - #if HAVE_HOST_CPU_FAMILY_x86 && WANT_FAT_BINARY --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB - #endif - --#ifdef COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#ifdef RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB - extern const unsigned char __clz_tab[129]; - #endif - --#if !defined (count_trailing_zeros) --#if !defined (COUNT_LEADING_ZEROS_SLOW) -- /* Define count_trailing_zeros using an asm count_leading_zeros. */ --#define count_trailing_zeros(count, x) \ -+#if !defined (recint_count_trailing_zeros) -+#if !defined (RECINT_COUNT_LEADING_ZEROS_SLOW) -+ /* Define recint_count_trailing_zeros using an asm recint_count_leading_zeros. */ -+#define recint_count_trailing_zeros(count, x) \ - do { \ - UWtype __ctz_x = (x); \ - UWtype __ctz_c; \ -- count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ -+ recint_count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ - (count) = W_TYPE_SIZE - 1 - __ctz_c; \ - } while (0) - #else -- /* Define count_trailing_zeros in plain C, assuming small counts are common. -- We use clz_tab without ado, since the C count_leading_zeros above will have -+ /* Define recint_count_trailing_zeros in plain C, assuming small counts are common. -+ We use clz_tab without ado, since the C recint_count_leading_zeros above will have - pulled it in. */ --#define count_trailing_zeros(count, x) \ -+#define recint_count_trailing_zeros(count, x) \ - do { \ - UWtype __ctz_x = (x); \ - int __ctz_c; \ -@@ -2122,7 +464,7 @@ extern const unsigned char __clz_tab[129]; - #define UDIV_NEEDS_NORMALIZATION 0 - #endif - -- /* Whether udiv_qrnnd is actually implemented with udiv_qrnnd_preinv, and -+ /* Whether recint_udiv_qrnnd is actually implemented with recint_udiv_qrnnd_preinv, and - that hence the latter should always be used. */ - #ifndef UDIV_PREINV_ALWAYS - #define UDIV_PREINV_ALWAYS 0 -@@ -2138,4 +480,3 @@ extern const unsigned char __clz_tab[129]; - #endif - /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s --#endif -diff --git a/src/kernel/recint/ruadd.h b/src/kernel/recint/ruadd.h -index 01d4073..deed001 100644 ---- a/src/kernel/recint/ruadd.h -+++ b/src/kernel/recint/ruadd.h -@@ -190,7 +190,7 @@ namespace RecInt - template<> - inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - r = (a < bp); - } - #endif -@@ -219,7 +219,7 @@ namespace RecInt - template<> - inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - r = (a < bp); - } - #endif -@@ -245,7 +245,7 @@ namespace RecInt - #else - template<> - inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - } - #endif - template<> -@@ -268,7 +268,7 @@ namespace RecInt - #else - template<> - inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> -@@ -288,7 +288,7 @@ namespace RecInt - // TODO Use __RECINT_USE_FAST_128 here too - template - inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c)); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c)); - r = (a < c); - } - template -@@ -306,7 +306,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b)); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b)); - r = (a < b); - } - template -@@ -345,7 +345,7 @@ namespace RecInt - #else - template<> - inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - r = (a == 0); - } - #endif -@@ -371,7 +371,7 @@ namespace RecInt - #else - template<> - inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a == 0); - } - #endif -@@ -396,7 +396,7 @@ namespace RecInt - #else - template<> - inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -419,7 +419,7 @@ namespace RecInt - #else - template<> - inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -452,9 +452,9 @@ namespace RecInt - template<> - inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - if (cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a <= bp); - } else { - r = (a < bp); -@@ -496,9 +496,9 @@ namespace RecInt - template<> - inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - if (cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a <= bp); - } else { - r = (a < bp); -@@ -533,8 +533,8 @@ namespace RecInt - #else - template<> - inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- if (cy) add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ if (cy) recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -557,8 +557,8 @@ namespace RecInt - #else - template<> - inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); - } - #endif - template<> -diff --git a/src/kernel/recint/ruaddmul.h b/src/kernel/recint/ruaddmul.h -index e3c8154..365340e 100644 ---- a/src/kernel/recint/ruaddmul.h -+++ b/src/kernel/recint/ruaddmul.h -@@ -115,8 +115,8 @@ namespace RecInt - template <> - inline void laddmul(bool& r, ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) { - auto dp(d.Value); -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); - r = ((ah.Value == 0) && (al.Value < dp)); - } - -@@ -156,8 +156,8 @@ namespace RecInt - template <> - inline void laddmul(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) { - auto dp(d.Value); -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); - } - - // a = b*c + d (r stores the carry) -@@ -202,8 +202,8 @@ namespace RecInt - auto dph(d.High.Value); - auto dpl(d.Low.Value); - -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl); - r = ((ah.Value < dph) || ((ah.Value == dph) && (al.Value < dpl))); - } - -diff --git a/src/kernel/recint/rudiv.h b/src/kernel/recint/rudiv.h -index 83d2129..6151e22 100644 ---- a/src/kernel/recint/rudiv.h -+++ b/src/kernel/recint/rudiv.h -@@ -211,7 +211,7 @@ namespace RecInt - bool ret = false; - - if (a2.Value < b1.Value) { -- udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value); -+ recint_udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value); - } else { - q.Value = __RECINT_MINUSONE; - c = a1.Value + b1.Value; -@@ -219,8 +219,8 @@ namespace RecInt - ret = true; - } - -- umul_ppmm(d1, d0, q.Value, b0.Value); -- sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0); -+ recint_umul_ppmm(d1, d0, q.Value, b0.Value); -+ recint_sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0); - - if (!ret && ((d1 > c) || ((d1 == c) && (d0 > a0.Value)))) { - q.Value--; -@@ -253,7 +253,7 @@ namespace RecInt - inline void div_2_1(ruint<__RECINT_LIMB_SIZE>& q, ruint<__RECINT_LIMB_SIZE>& r, - const ruint<__RECINT_LIMB_SIZE>& ah, const ruint<__RECINT_LIMB_SIZE>& al, - const ruint<__RECINT_LIMB_SIZE>& b) { -- udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value); -+ recint_udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value); - } - - // computes (q, r) such that a = q*b + r (0 <= r < b) -diff --git a/src/kernel/recint/ruint.h b/src/kernel/recint/ruint.h -index 1fc930b..e95d254 100644 ---- a/src/kernel/recint/ruint.h -+++ b/src/kernel/recint/ruint.h -@@ -80,19 +80,7 @@ knowledge of the CeCILL-B license and that you accept its terms. - /* GMP conversion system */ - #include "ruconvert.h" - --// Cleaning up MACROS defined in reclonglong.h (to avoid collision with FLINT's longlong.h) --#undef add_ssaaaa --#undef count_leading_zeros --#undef count_trailing_zeros --#undef smul_ppmm --#undef sub_ddmmss --#undef umul_ppmm --#undef udiv_qrnnd --#undef __BITS4 --#undef __ll_B --#undef __ll_lowpart --#undef __ll_highpart --#endif -+#endif // RUINT_H - - /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s -diff --git a/src/kernel/recint/rumul.h b/src/kernel/recint/rumul.h -index 10edcf8..d7d33db 100644 ---- a/src/kernel/recint/rumul.h -+++ b/src/kernel/recint/rumul.h -@@ -174,7 +174,7 @@ namespace RecInt - - template<> - inline void lmul_naive(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c) { -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); - } - template - inline void lmul_naive(ruint& a, const ruint& b, const ruint& c) { -@@ -243,7 +243,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) lmul(limb& ret, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -- umul_ppmm(ret, a.Value, b.Value, limb(c)); -+ recint_umul_ppmm(ret, a.Value, b.Value, limb(c)); - } - - // a = b*c -@@ -352,7 +352,7 @@ namespace RecInt - } - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& lsquare(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE>& b) { -- umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value); -+ recint_umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value); - return a; - } - -diff --git a/src/kernel/recint/rusub.h b/src/kernel/recint/rusub.h -index 74a2e39..0b91088 100644 ---- a/src/kernel/recint/rusub.h -+++ b/src/kernel/recint/rusub.h -@@ -187,7 +187,7 @@ namespace RecInt - template<> - inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { - r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - } - #endif - template<> -@@ -213,7 +213,7 @@ namespace RecInt - template<> - inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> -@@ -239,7 +239,7 @@ namespace RecInt - #else - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - return a; - } - #endif -@@ -266,7 +266,7 @@ namespace RecInt - #else - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - return a; - - } -@@ -290,7 +290,7 @@ namespace RecInt - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { - r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -@@ -308,7 +308,7 @@ namespace RecInt - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { - r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const T& b) { -@@ -325,7 +325,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -@@ -341,7 +341,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const T& b) { -@@ -367,7 +367,7 @@ namespace RecInt - template<> - inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - r = (b == 0); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -393,7 +393,7 @@ namespace RecInt - template<> - inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) { - r = (a == 0); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -417,7 +417,7 @@ namespace RecInt - #else - template<> - inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -440,7 +440,7 @@ namespace RecInt - #else - template<> - inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -469,8 +469,8 @@ namespace RecInt - inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { - if (cy) r = (b <= c); - else r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -499,8 +499,8 @@ namespace RecInt - inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { - if (cy) r = (a <= b); - else r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -525,8 +525,8 @@ namespace RecInt - #else - template<> - inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); - } - #endif - template<> -@@ -549,8 +549,8 @@ namespace RecInt - #else - template<> - inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4 index 36cfab37081..18eb1d30195 100644 --- a/build/pkgs/givaro/spkg-configure.m4 +++ b/build/pkgs/givaro/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([givaro], [ PKG_CHECK_MODULES([GIVARO], - [givaro >= 4.1.1],dnl The version test is refined in linbox/spkg-configure.m4 + [givaro >= 4.2.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_givaro=no], [sage_spkg_install_givaro=yes]) ]) diff --git a/build/pkgs/gmpy2/version_requirements.txt b/build/pkgs/gmpy2/version_requirements.txt deleted file mode 100644 index 11116398187..00000000000 --- a/build/pkgs/gmpy2/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -gmpy2 ~=2.1.b999 diff --git a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini index 6da100dad5a..067609dd4fc 100644 --- a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini +++ b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini @@ -1,5 +1,5 @@ tarball=hatch_fancy_pypi_readme-VERSION-py3-none-any.whl -sha1=4076ea14577b3c711a8345498d8f91b1c8a13d09 -md5=d7acd13333f6c71dcbfa62420c7f257b -cksum=1527082323 +sha1=25cd6749c20a6803cbf1b6c4d29338c344a8f09c +md5=a38ee7191a80ebdbbf0f126f7dff7e46 +cksum=1509914432 upstream_url=https://pypi.io/packages/py3/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION-py3-none-any.whl diff --git a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt index f8aed3e0b7a..7c974b0f495 100644 --- a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt +++ b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt @@ -1 +1 @@ -23.1.0 +24.1.0 diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index 5d8b02696c5..1d5f9b819ce 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,5 +1,5 @@ tarball=hatchling-VERSION-py3-none-any.whl -sha1=aa9d69b9dd820716440252d737a4aeaf9b4e541f -md5=20e5ea4deea21f91759fb2269b71f0dd -cksum=446304413 +sha1=2212af13a26dbaea72c7a4ecbdb950c05f6e7c00 +md5=0301aa5bc8739e9f4d58e29d285fb2f7 +cksum=1232194081 upstream_url=https://pypi.io/packages/py3/h/hatchling/hatchling-VERSION-py3-none-any.whl diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt index 39893559155..da9594fd66f 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.20.0 +1.22.5 diff --git a/build/pkgs/importlib_metadata/dependencies b/build/pkgs/importlib_metadata/dependencies index 3c8d3aeb24e..5f8dc152989 100644 --- a/build/pkgs/importlib_metadata/dependencies +++ b/build/pkgs/importlib_metadata/dependencies @@ -1,4 +1,4 @@ - zipp typing_extensions | $(PYTHON_TOOLCHAIN) tomli $(PYTHON) + zipp typing_extensions | pip tomli $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jupyter_core/version_requirements.txt b/build/pkgs/jupyter_core/version_requirements.txt deleted file mode 100644 index a0e9af65ff5..00000000000 --- a/build/pkgs/jupyter_core/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -jupyter_core >=4.6.3 diff --git a/build/pkgs/linbox/checksums.ini b/build/pkgs/linbox/checksums.ini index 23f1a33a68f..397bf97190f 100644 --- a/build/pkgs/linbox/checksums.ini +++ b/build/pkgs/linbox/checksums.ini @@ -1,4 +1,5 @@ tarball=linbox-VERSION.tar.gz -sha1=9268e21b5aecbbfc45204b25195b786f80b769bc -md5=1e90e300c7a324a7b6cece7c605b7a4e -cksum=1921179523 +sha1=24e8bdbd16fe3dedce0dd343398999a4aed7c02c +md5=1e1b95f12f015815a0194eac0cb611d0 +cksum=253115750 +upstream_url=https://github.com/linbox-team/linbox/releases/download/vVERSION/linbox-VERSION.tar.gz \ No newline at end of file diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt index 8ecda4f1393..bd8bf882d06 100644 --- a/build/pkgs/linbox/package-version.txt +++ b/build/pkgs/linbox/package-version.txt @@ -1 +1 @@ -1.6.3.p1 +1.7.0 diff --git a/build/pkgs/linbox/patches/292.patch b/build/pkgs/linbox/patches/292.patch new file mode 100644 index 00000000000..d3333cc23d0 --- /dev/null +++ b/build/pkgs/linbox/patches/292.patch @@ -0,0 +1,24 @@ +From 49b9cccd0286980c1c1811c3b03df883ef0164df Mon Sep 17 00:00:00 2001 +From: Doug Torrance +Date: Tue, 14 Dec 2021 16:22:33 -0500 +Subject: [PATCH] Only register uint128_t as a TypeName when it's available. + +Otherwise, test-fft will fail when it isn't. +--- + tests/test-fft.C | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/test-fft.C b/tests/test-fft.C +index d19184c2d..5811ebc5d 100644 +--- a/tests/test-fft.C ++++ b/tests/test-fft.C +@@ -55,7 +55,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + diff --git a/build/pkgs/linbox/patches/294.patch b/build/pkgs/linbox/patches/294.patch new file mode 100644 index 00000000000..7c645f165d3 --- /dev/null +++ b/build/pkgs/linbox/patches/294.patch @@ -0,0 +1,38 @@ +From f81a1f4a5e0835b7a0f3bb88a0fcbbaa32174cfa Mon Sep 17 00:00:00 2001 +From: Cyril Bouvier +Date: Wed, 15 Dec 2021 16:00:39 +0100 +Subject: [PATCH] Only register uint128_t as a TypeName when it's available + +--- + benchmarks/benchmark-fft.C | 2 ++ + benchmarks/benchmark-polynomial-matrix-mul-fft.C | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/benchmarks/benchmark-fft.C b/benchmarks/benchmark-fft.C +index 39b86c9d9..59a8be57c 100644 +--- a/benchmarks/benchmark-fft.C ++++ b/benchmarks/benchmark-fft.C +@@ -54,7 +54,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + +diff --git a/benchmarks/benchmark-polynomial-matrix-mul-fft.C b/benchmarks/benchmark-polynomial-matrix-mul-fft.C +index e9b184bcf..7bf17f33e 100644 +--- a/benchmarks/benchmark-polynomial-matrix-mul-fft.C ++++ b/benchmarks/benchmark-polynomial-matrix-mul-fft.C +@@ -65,7 +65,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + diff --git a/build/pkgs/linbox/patches/310-backport.patch b/build/pkgs/linbox/patches/310-backport.patch new file mode 100644 index 00000000000..c0c44bc1ec7 --- /dev/null +++ b/build/pkgs/linbox/patches/310-backport.patch @@ -0,0 +1,69 @@ +From b8f2d4ccdc0af4418d14f72caf6c4d01969092a3 Mon Sep 17 00:00:00 2001 +From: Jean-Guillaume Dumas +Date: Fri, 26 Jan 2024 16:31:56 +0100 +Subject: [PATCH] const_cast missing faster empty init + +--- + linbox/algorithms/gauss/gauss-nullspace.inl | 10 +- + .../matrix/sparsematrix/sparse-ell-matrix.h | 8 +- + .../matrix/sparsematrix/sparse-ellr-matrix.h | 18 +-- + linbox/ring/ntl/ntl-lzz_p.h | 11 +- + linbox/ring/ntl/ntl-lzz_pe.h | 143 +++++++++--------- + linbox/ring/ntl/ntl-zz_px.h | 6 + + 6 files changed, 104 insertions(+), 92 deletions(-) + +diff --git a/linbox/matrix/sparsematrix/sparse-ell-matrix.h b/linbox/matrix/sparsematrix/sparse-ell-matrix.h +index 59006d6c5f..2604f47b81 100644 +--- a/linbox/matrix/sparsematrix/sparse-ell-matrix.h ++++ b/linbox/matrix/sparsematrix/sparse-ell-matrix.h +@@ -1210,10 +1210,10 @@ namespace LinBox + _colid_beg = iter._colid_beg ; + _colid_it = iter._colid_it ; + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end) = iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast(ld) = iter._ld ; + _row = iter._row ; + + return *this; +diff --git a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h +index 498a5525db..a60943868b 100644 +--- a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h ++++ b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h +@@ -1102,11 +1102,11 @@ namespace LinBox + _Iterator &operator = (const _Iterator &iter) + { + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _rowid = iter._rowid; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end)= iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast&>(_rowid) = iter._rowid; ++ const_cast(ld) = iter._ld ; + _row = iter._row ; + + return *this; +@@ -1252,10 +1252,10 @@ namespace LinBox + _colid_beg = iter._colid_beg ; + _colid_it = iter._colid_it ; + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end) = iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast(ld)= iter._ld ; + _row = iter._row ; + + return *this; diff --git a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch b/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index e0cb575b1a1..00000000000 --- a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 52c78df67a08de074991a93b57946b7bd5ea7196 Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 15:53:25 +0100 -Subject: [PATCH 1/1] remove redundant 1st and last lines - -they remain if the script is run under ksh, leading to broken .pc file ---- - linbox.pc.in | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/linbox.pc.in b/linbox.pc.in -index f54285e..eb6835b 100644 ---- a/linbox.pc.in -+++ b/linbox.pc.in -@@ -1,4 +1,3 @@ --/------------------ linbox.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@libdir@ -@@ -11,4 +10,4 @@ Version: @VERSION@ - Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 - Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ - Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ --\------------------------------------------------------- -+ --- -2.26.2 - diff --git a/build/pkgs/linbox/patches/linbox-pr-256.patch b/build/pkgs/linbox/patches/linbox-pr-256.patch deleted file mode 100644 index 9ca03791d30..00000000000 --- a/build/pkgs/linbox/patches/linbox-pr-256.patch +++ /dev/null @@ -1,47 +0,0 @@ -Extracted from the following patch: - -From 27811e0021c73de26bf2cff5105c763e163ca8b7 Mon Sep 17 00:00:00 2001 -From: Jean-Guillaume Dumas -Date: Fri, 26 Jun 2020 09:54:38 +0200 -Subject: [PATCH 1/3] iterators may have different types - ---- - linbox/vector/blas-subvector.h | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -From 4ff828e20053ab2ef9adc4ce6931d159fd513cef Mon Sep 17 00:00:00 2001 -From: Jean-Guillaume Dumas -Date: Fri, 26 Jun 2020 09:54:57 +0200 -Subject: [PATCH 2/3] incompatible const & with modifiers - ---- - examples/Makefile.am | 3 +- - examples/ratdet.C | 96 ++++++++++++++++++++++++++++++++ - linbox/algorithms/det-rational.h | 4 +- - 3 files changed, 100 insertions(+), 3 deletions(-) - create mode 100644 examples/ratdet.C - -diff --git a/linbox/algorithms/det-rational.h b/linbox/algorithms/det-rational.h -index 327b4710f..1943876c1 100644 ---- a/linbox/algorithms/det-rational.h -+++ b/linbox/algorithms/det-rational.h -@@ -79,8 +79,8 @@ namespace LinBox - struct MyRationalModularDet { - const Blackbox &A; - const MyMethod &M; -- const Integer &mul;//multiplicative prec; -- const Integer ÷ -+ Integer mul;//multiplicative prec; -+ Integer div; - - MyRationalModularDet(const Blackbox& b, const MyMethod& n, - const Integer & p1, const Integer & p2) : - -From 567f727aa42de6678433591d731b275ea55fa8ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Cl=C3=A9ment=20Pernet?= -Date: Fri, 26 Jun 2020 17:11:37 +0200 -Subject: [PATCH 3/3] untabify + auto-indent - ---- - examples/ratdet.C | 66 +++++++++++++++++++++++------------------------ - 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch b/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch deleted file mode 100644 index c93915fb1b0..00000000000 --- a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch +++ /dev/null @@ -1,23 +0,0 @@ -Backported from: - -From 426eb97ba762c7663884f57ead0909f2aa3cd6a5 Mon Sep 17 00:00:00 2001 -From: Cyril Bouvier -Date: Thu, 17 Jan 2019 16:32:19 +0100 -Subject: [PATCH] Remove @LINBOXSAGE_LIBS@ from linbox.pc.in - ---- - linbox.pc.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/linbox.pc.in b/linbox.pc.in -index 278f127e4..c6b8091eb 100644 ---- a/linbox.pc.in -+++ b/linbox.pc.in -@@ -9,6 +9,6 @@ Description: Exact Linear Algebra library - URL: http://github.com/linbox-team/linbox - Version: @VERSION@ - Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 --Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ -+Libs: -L${libdir} -llinbox @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ - Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ - \------------------------------------------------------- diff --git a/build/pkgs/linbox/spkg-configure.m4 b/build/pkgs/linbox/spkg-configure.m4 index 8c5b85ef7fd..49cc871a85c 100644 --- a/build/pkgs/linbox/spkg-configure.m4 +++ b/build/pkgs/linbox/spkg-configure.m4 @@ -1,10 +1,10 @@ SAGE_SPKG_CONFIGURE([linbox], [ SAGE_SPKG_DEPCHECK([fflas_ffpack flint fplll givaro gmp iml m4ri m4rie mpfr ntl], [ PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching old versions - [linbox >= 1.6.3 linbox <= 1.6.4 fflas-ffpack >= 2.4.0 fflas-ffpack < 2.5.0 givaro >= 4.1.1 givaro < 4.2.0], + [linbox >= 1.7.0 linbox < 1.8.0 fflas-ffpack >= 2.5.0 fflas-ffpack < 2.6.0 givaro >= 4.2.0 givaro < 4.3.0], [sage_spkg_install_linbox=no], [PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching new versions - [linbox >= 1.7.0 linbox <= 1.7.0 fflas-ffpack >= 2.5.0 givaro >= 4.2.0 givaro < 4.3.0], + [linbox >= 1.8.0 linbox <= 1.9.0 fflas-ffpack >= 2.6.0 givaro >= 4.3.0 givaro < 4.4.0], [sage_spkg_install_linbox=no], [sage_spkg_install_linbox=yes])]) ]) diff --git a/build/pkgs/linbox/spkg-install.in b/build/pkgs/linbox/spkg-install.in index 8d415e81fd8..7acc5547e9f 100644 --- a/build/pkgs/linbox/spkg-install.in +++ b/build/pkgs/linbox/spkg-install.in @@ -15,7 +15,7 @@ export CPPFLAGS="$CPPFLAGS -DDISABLE_COMMENTATOR" # If SAGE_FAT_BINARY is set, disable dependency that be discovered on the building system. if [ "$SAGE_FAT_BINARY" = yes ]; then - LINBOX_CONFIGURE="--disable-sse --disable-sse2 --disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE" + LINBOX_CONFIGURE="--disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE" fi # Disable fplll as version 5.x is not supported by linbox <= 1.5.0. diff --git a/build/pkgs/memory_allocator/version_requirements.txt b/build/pkgs/memory_allocator/version_requirements.txt deleted file mode 100644 index fad07b22ebe..00000000000 --- a/build/pkgs/memory_allocator/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -memory_allocator diff --git a/build/pkgs/networkx/version_requirements.txt b/build/pkgs/networkx/version_requirements.txt index 5a7e22a60db..2ff86e7c737 100644 --- a/build/pkgs/networkx/version_requirements.txt +++ b/build/pkgs/networkx/version_requirements.txt @@ -1 +1 @@ -networkx >=2.4, <3.3 +networkx >=2.4 diff --git a/build/pkgs/numpy/version_requirements.txt b/build/pkgs/numpy/version_requirements.txt deleted file mode 100644 index 45127ae0a86..00000000000 --- a/build/pkgs/numpy/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -numpy >=1.19 diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index 3ee369817f0..69a230aa11c 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -1,5 +1,5 @@ tarball=packaging-VERSION-py3-none-any.whl -sha1=d3fb436d835b252ea884a5d172d7265220127f95 -md5=f6e9c6af858bd34eff07b407d3f650a1 -cksum=3531019080 +sha1=21573cef174a05ac2794b34f3841d6f9ea9fa507 +md5=8b7ed65f4b1a2175ccab25317f2efccc +cksum=4283692602 upstream_url=https://pypi.io/packages/py3/p/packaging/packaging-VERSION-py3-none-any.whl diff --git a/build/pkgs/packaging/package-version.txt b/build/pkgs/packaging/package-version.txt index 3c8ce91a469..d9133a54b63 100644 --- a/build/pkgs/packaging/package-version.txt +++ b/build/pkgs/packaging/package-version.txt @@ -1 +1 @@ -23.2 +24.0 diff --git a/build/pkgs/pcre/distros/alpine.txt b/build/pkgs/pcre/distros/alpine.txt deleted file mode 100644 index a16e4119734..00000000000 --- a/build/pkgs/pcre/distros/alpine.txt +++ /dev/null @@ -1 +0,0 @@ -pcre-dev diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 0558c5caa45..ebfbc1a26a1 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION-py3-none-any.whl -sha1=4b2baddc0673f73017e531648a9ee27e47925e7a -md5=5d2d058044a3ae2800d18e358ddc72ca -cksum=1470281176 +sha1=e44313ae1e6af3c2bd3b60ab2fa8c34308d00555 +md5=74e3c5e4082113b1239ca0e9abfd1e82 +cksum=88131429 upstream_url=https://pypi.io/packages/py3/p/pip/pip-VERSION-py3-none-any.whl diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index a9a57c82265..d9133a54b63 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -23.3.1 +24.0 diff --git a/build/pkgs/pkgconfig/version_requirements.txt b/build/pkgs/pkgconfig/version_requirements.txt deleted file mode 100644 index 549fd1bf164..00000000000 --- a/build/pkgs/pkgconfig/version_requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pkgconfig diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini index 3757f701faf..f8770e91289 100644 --- a/build/pkgs/platformdirs/checksums.ini +++ b/build/pkgs/platformdirs/checksums.ini @@ -1,5 +1,5 @@ tarball=platformdirs-VERSION-py3-none-any.whl -sha1=cafa761738da959f2df0a8a92da4c72fd8eaf93e -md5=487007776ff343efc509b68d08cd7fd7 -cksum=162426958 +sha1=487a4610a037c90b242aafbe1e3f8b6ebb3ba1c8 +md5=99200c4e22d44a64a9c3ad0c72a317af +cksum=1011122610 upstream_url=https://pypi.io/packages/py3/p/platformdirs/platformdirs-VERSION-py3-none-any.whl diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/platformdirs/package-version.txt +++ b/build/pkgs/platformdirs/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/pplpy/version_requirements.txt b/build/pkgs/pplpy/version_requirements.txt deleted file mode 100644 index bd048ecc980..00000000000 --- a/build/pkgs/pplpy/version_requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Issue #30922: pplpy 0.8.4 and earlier do not declare dependencies correctly -pplpy >=0.8.6 diff --git a/build/pkgs/pycygwin/spkg-configure.m4 b/build/pkgs/pycygwin/spkg-configure.m4 deleted file mode 100644 index 7876a693d7b..00000000000 --- a/build/pkgs/pycygwin/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([pycygwin], [SAGE_PYTHON_PACKAGE_CHECK([pycygwin])]) diff --git a/build/pkgs/pyproject_hooks/dependencies b/build/pkgs/pyproject_hooks/dependencies index 47296a7bace..644ad35f773 100644 --- a/build/pkgs/pyproject_hooks/dependencies +++ b/build/pkgs/pyproject_hooks/dependencies @@ -1,4 +1,4 @@ - | $(PYTHON_TOOLCHAIN) $(PYTHON) + | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/python3/distros/fedora.txt b/build/pkgs/python3/distros/fedora.txt index ba7b5ea9b21..ab50f2d9b07 100644 --- a/build/pkgs/python3/distros/fedora.txt +++ b/build/pkgs/python3/distros/fedora.txt @@ -1,2 +1,2 @@ python3-devel -python-setuptools +python3-setuptools diff --git a/build/pkgs/python_build/dependencies b/build/pkgs/python_build/dependencies index af994ec1059..4d5609413be 100644 --- a/build/pkgs/python_build/dependencies +++ b/build/pkgs/python_build/dependencies @@ -1,4 +1,4 @@ -tomli packaging pyproject_hooks importlib_metadata | $(PYTHON_TOOLCHAIN) $(PYTHON) +tomli packaging pyproject_hooks importlib_metadata | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 1e512338617..858e8da5968 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.4b4 +sage-conf ~= 10.4b6 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index 005cc4306ac..0005ac13ace 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.4b4 +sage-docbuild ~= 10.4b6 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index fc721f1c1da..4efa9ed072c 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.4b4 +sage-setup ~= 10.4b6 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index 9ec4b186ac9..a44b4f816f4 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.4b4 +sage-sws2rst ~= 10.4b6 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 9c1daa7372c..43ee6e618ef 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.4b4 +sagemath-standard ~= 10.4b6 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index ba9e1b2081d..11ff86c39e6 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.4b4 +sagemath-bliss ~= 10.4b6 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 6155490b8ef..4bf63679c82 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.4b4 +sagemath-categories ~= 10.4b6 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index 932bad03329..4d8933d8f37 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.4b4 +sagemath-coxeter3 ~= 10.4b6 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 475da537a2b..c97a53a9437 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.4b4 +sagemath-environment ~= 10.4b6 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index 0631db2f93a..697eed8cf48 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.4b4 +sagemath-mcqd ~= 10.4b6 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index fa695fc4e4a..8e7db0b81a8 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.4b4 +sagemath-meataxe ~= 10.4b6 diff --git a/build/pkgs/sagemath_objects/spkg-check b/build/pkgs/sagemath_objects/spkg-check index 87cea0fc38f..99a8528e0da 100755 --- a/build/pkgs/sagemath_objects/spkg-check +++ b/build/pkgs/sagemath_objects/spkg-check @@ -1,15 +1,45 @@ #!/usr/bin/env bash cd src +if [ ! -r tox.ini ]; then + echo "Not testing the package because there is no tox.ini" + exit 0 +fi + +for lib in "$SAGE_SRC/bin/sage-src-env-config" "$SAGE_SRC/bin/sage-env-config" "$SAGE_SRC/bin/sage-env" "$SAGE_ROOT/build/bin/sage-build-env-config" "$SAGE_ROOT/build/bin/sage-build-env"; do + source "$lib" + if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 + fi +done + export PIP_NO_INDEX=true export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" -export TOX_PARALLEL_NO_SPINNER=1 +unset tox_args + wheel="$(sed -n '1s,.*@ file://,,p' $SAGE_SPKG_SCRIPTS/$PKG_BASE/spkg-requirements.txt)" -echo Running "tox -r -p auto -v --installpkg $wheel" -tox -r -p auto -v --installpkg "$wheel" +if [ -n "$wheel" ]; then + tox_envs=$(tox -l) + tox_args="-r -p auto -v --installpkg $wheel" +elif [ "$SAGE_EDITABLE" = yes ]; then + tox_envs=$(tox -l | sed s/norequirements/editable/) + # FIXME: Should use -r if sage_setup or another build requirement changes + tox_args="-r -v -v -v -v -e $(echo $tox_envs | sed 's/ /,/g')" +else + echo "Not testing the package because SAGE_WHEELS=$SAGE_WHEELS and SAGE_EDITABLE=$SAGE_EDITABLE" + exit 0 +fi + +export TOX_PARALLEL_NO_SPINNER=1 + +echo Running "tox $tox_args" +tox $tox_args status=$? case $status:$SAGE_CHECK:$([ -r known-test-failures.json ]; echo $?) in + 0:no:*) echo "Not testing the package because SAGE_CHECK=no";; 0:*:0) echo "Passed the test suite (modulo baseline known-test-failures*.json)";; 0:*:*) echo "Passed the test suite";; *:warn:0) echo "Warning: New failures (not in baseline known-test-failures*.json (ignored)"; status=0;; @@ -18,10 +48,12 @@ case $status:$SAGE_CHECK:$([ -r known-test-failures.json ]; echo $?) in *:yes:*) echo "Failures testing the package";; esac # Show summaries of failures (suppress lines ending with '[failed in baseline]') -for f in $(pwd)/.tox/sagepython-sagewheels-nopypi-norequirements*/log/*-command*.log; do - if [ -r "$f" ]; then - echo "$f" - grep '^sage -t.*#[^]]*$' "$f" - fi +for e in $tox_envs; do + for f in $(pwd)/.tox/$e/log/*-command*.log; do + if [ -r "$f" ]; then + echo "$f" + grep '^sage -t.*#[^]]*$' "$f" + fi + done done exit $status diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 38fc3c4dec3..92bb9707da1 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.4b4 +sagemath-objects ~= 10.4b6 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index 9b039bdd460..2b2a5e66ef5 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.4b4 +sagemath-repl ~= 10.4b6 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index 09d50bb08f7..106893032ab 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.4b4 +sagemath-sirocco ~= 10.4b6 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 5f55b35c539..71dc621822a 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.4b4 +sagemath-tdlib ~= 10.4b6 diff --git a/build/pkgs/sbcl/SPKG.rst b/build/pkgs/sbcl/SPKG.rst new file mode 100644 index 00000000000..e3a383239c7 --- /dev/null +++ b/build/pkgs/sbcl/SPKG.rst @@ -0,0 +1,24 @@ +sbcl: a lisp compiler and runtime system +===================================================================== + +Description +----------- + +Steel Bank Common Lisp (SBCL) is a high performance Common Lisp compiler. It is +open source / free software, with a permissive license (see https://www.sbcl.org/history.html). +In addition to the compiler and runtime system for ANSI Common Lisp, it provides an interactive +environment including a debugger, a statistical profiler, a code coverage tool, +and many other extensions. + +(taken from https://www.sbcl.org) + +License +------- + +- a mix of BSD-style and public domain + + +Upstream Contact +---------------- + +- https://www.sbcl.org diff --git a/build/pkgs/sbcl/distros/alpine.txt b/build/pkgs/sbcl/distros/alpine.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/alpine.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/arch.txt b/build/pkgs/sbcl/distros/arch.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/arch.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/conda.txt b/build/pkgs/sbcl/distros/conda.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/conda.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/cygwin.txt b/build/pkgs/sbcl/distros/cygwin.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/cygwin.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/debian.txt b/build/pkgs/sbcl/distros/debian.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/debian.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/fedora.txt b/build/pkgs/sbcl/distros/fedora.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/fedora.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/freebsd.txt b/build/pkgs/sbcl/distros/freebsd.txt new file mode 100644 index 00000000000..51399eefa59 --- /dev/null +++ b/build/pkgs/sbcl/distros/freebsd.txt @@ -0,0 +1 @@ +lang/sbcl diff --git a/build/pkgs/sbcl/distros/gentoo.txt b/build/pkgs/sbcl/distros/gentoo.txt new file mode 100644 index 00000000000..e02e0542b58 --- /dev/null +++ b/build/pkgs/sbcl/distros/gentoo.txt @@ -0,0 +1 @@ +dev-lisp/sbcl diff --git a/build/pkgs/sbcl/distros/homebrew.txt b/build/pkgs/sbcl/distros/homebrew.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/homebrew.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/macports.txt b/build/pkgs/sbcl/distros/macports.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/macports.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/nix.txt b/build/pkgs/sbcl/distros/nix.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/nix.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/openbsd.txt b/build/pkgs/sbcl/distros/openbsd.txt new file mode 100644 index 00000000000..51399eefa59 --- /dev/null +++ b/build/pkgs/sbcl/distros/openbsd.txt @@ -0,0 +1 @@ +lang/sbcl diff --git a/build/pkgs/sbcl/distros/opensuse.txt b/build/pkgs/sbcl/distros/opensuse.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/opensuse.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/repology.txt b/build/pkgs/sbcl/distros/repology.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/repology.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/sbcl/distros/void.txt b/build/pkgs/sbcl/distros/void.txt new file mode 100644 index 00000000000..0e94cb07231 --- /dev/null +++ b/build/pkgs/sbcl/distros/void.txt @@ -0,0 +1 @@ +sbcl diff --git a/build/pkgs/gambit/math b/build/pkgs/sbcl/math similarity index 100% rename from build/pkgs/gambit/math rename to build/pkgs/sbcl/math diff --git a/build/pkgs/sbcl/spkg-configure.m4 b/build/pkgs/sbcl/spkg-configure.m4 new file mode 100644 index 00000000000..aef49ce770b --- /dev/null +++ b/build/pkgs/sbcl/spkg-configure.m4 @@ -0,0 +1,20 @@ +SAGE_SPKG_CONFIGURE([sbcl], [dnl + m4_pushdef([SAGE_SBCL_MINVER], ["1.4.16"]) + AC_CACHE_CHECK([for sbcl >= SAGE_SBCL_MINVER], [ac_cv_path_SBCL], [ + AC_PATH_PROGS_FEATURE_CHECK([SBCL], [sbcl], [ + sbcl_version=`$ac_path_SBCL --version 2>&1 \ + | $SED -n -e 's/SBCL *\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\)/\1/p'` + AS_IF([test -n "$sbcl_version"], [ + AX_COMPARE_VERSION([$sbcl_version], [ge], [SAGE_SBCL_MINVER], [ + ac_cv_path_SBCL="$ac_path_SBCL" + ac_path_SBCL_found=: + ]) + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_SBCL" ], [ + sage_spkg_install_sbcl=yes + AC_SUBST(SAGE_FRICAS_LISP, ecl)], [ + AC_SUBST(SAGE_FRICAS_LISP, "sbcl --dynamic-space-size 4Gb")]) + m4_popdef([SAGE_SBCL_MINVER]) +]) diff --git a/build/pkgs/sbcl/type b/build/pkgs/sbcl/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sbcl/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 1a88d2a7464..bcd55128882 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools-VERSION-py3-none-any.whl -sha1=4227225bb193e3a45542f45966caf777d4c913e8 -md5=f096ed836f4036a11aa277fa16dc09ff -cksum=263664173 +sha1=49841be6743b2d129d01d02d5fd339dd693c99dc +md5=1555b24e28b53f3342e557500dedf8f3 +cksum=3445997019 upstream_url=https://pypi.io/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl diff --git a/build/pkgs/setuptools/distros/fedora.txt b/build/pkgs/setuptools/distros/fedora.txt index e1ad17860cd..1c0901c0374 100644 --- a/build/pkgs/setuptools/distros/fedora.txt +++ b/build/pkgs/setuptools/distros/fedora.txt @@ -1 +1 @@ -python-setuptools +python3-setuptools diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 2c021f541a8..2a93f495d25 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -69.0.2 +69.5.1 diff --git a/build/pkgs/setuptools/version_requirements.txt b/build/pkgs/setuptools/version_requirements.txt deleted file mode 100644 index c12b5900873..00000000000 --- a/build/pkgs/setuptools/version_requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -# 68.1.0 Promote pyproject.toml's [tool.setuptools] out of beta. -# 68.1.1 Fix editable install finder handling of nested packages -setuptools >= 68.1.1 diff --git a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt b/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt deleted file mode 100644 index c08bc88e16c..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt +++ /dev/null @@ -1 +0,0 @@ -py3-setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt b/build/pkgs/setuptools_scm_git_archive/distros/arch.txt deleted file mode 100644 index bc2a39f97a3..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt +++ /dev/null @@ -1 +0,0 @@ -python-setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt b/build/pkgs/setuptools_scm_git_archive/distros/debian.txt deleted file mode 100644 index 538474ff946..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt +++ /dev/null @@ -1 +0,0 @@ -setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt b/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt deleted file mode 100644 index ada37357769..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt +++ /dev/null @@ -1 +0,0 @@ -python-setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt b/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt deleted file mode 100644 index 2ace76a2af0..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt +++ /dev/null @@ -1 +0,0 @@ -devel/py-setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt b/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt deleted file mode 100644 index fb7388e3dd7..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt +++ /dev/null @@ -1 +0,0 @@ -dev-python/setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 b/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 deleted file mode 100644 index 0da3db40d22..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([setuptools_scm_git_archive], [SAGE_PYTHON_PACKAGE_CHECK([setuptools_scm_git_archive])]) diff --git a/build/pkgs/singular/checksums.ini b/build/pkgs/singular/checksums.ini index 40ba7b3153f..3273f5f7055 100644 --- a/build/pkgs/singular/checksums.ini +++ b/build/pkgs/singular/checksums.ini @@ -1,5 +1,5 @@ tarball=singular-VERSION.tar.gz -sha1=0dd736f26935ed72999bb9a4bbb98c6df18ab9ea -md5=0f9368193bad9a0c3dc84545b2404761 -cksum=1698641648 -upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/4-3-2/singular-VERSION.tar.gz +sha1=1f678e1cc756fd8dc29dcdef5ae67441b6bcc779 +md5=09382cdacbfe67b4099056b65c2ec016 +cksum=244065751 +upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/${VERSION_MAJOR}-${VERSION_MINOR}-${VERSION_MICRO}/singular-VERSION.tar.gz diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 7801ff9a882..fdc6698807a 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.3.2p8 +4.4.0 diff --git a/build/pkgs/trove_classifiers/checksums.ini b/build/pkgs/trove_classifiers/checksums.ini index 7350f444667..8bd0eca4933 100644 --- a/build/pkgs/trove_classifiers/checksums.ini +++ b/build/pkgs/trove_classifiers/checksums.ini @@ -1,5 +1,5 @@ tarball=trove_classifiers-VERSION-py3-none-any.whl -sha1=c341abee77b5c87d913b86dc587e544553f0658c -md5=78e67f128f53b8417134429192810701 -cksum=3034057088 +sha1=36240d053d16400380aee01f0879785693008a96 +md5=02b3e7b2eb81c3656fa859a87482f120 +cksum=1500381935 upstream_url=https://pypi.io/packages/py3/t/trove_classifiers/trove_classifiers-VERSION-py3-none-any.whl diff --git a/build/pkgs/trove_classifiers/package-version.txt b/build/pkgs/trove_classifiers/package-version.txt index a33bd2f9968..c296ac66b65 100644 --- a/build/pkgs/trove_classifiers/package-version.txt +++ b/build/pkgs/trove_classifiers/package-version.txt @@ -1 +1 @@ -2023.11.29 +2024.4.10 diff --git a/build/pkgs/uri_template/dependencies b/build/pkgs/uri_template/dependencies index 47296a7bace..644ad35f773 100644 --- a/build/pkgs/uri_template/dependencies +++ b/build/pkgs/uri_template/dependencies @@ -1,4 +1,4 @@ - | $(PYTHON_TOOLCHAIN) $(PYTHON) + | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini index 4f2b8c5c534..6e28451ce44 100644 --- a/build/pkgs/wheel/checksums.ini +++ b/build/pkgs/wheel/checksums.ini @@ -1,5 +1,5 @@ tarball=wheel-VERSION-py3-none-any.whl -sha1=fcf4ad8d5d8216d661bc98eede0d9210cbc5b697 -md5=779d91395ceb12e15e3a585b30b53f9f -cksum=1421399426 +sha1=71a83a2237cb57ab45bdafed364564e36ca5dc95 +md5=e65b1197e1dfc6bbc8df362935f5943d +cksum=1664872683 upstream_url=https://pypi.io/packages/py3/w/wheel/wheel-VERSION-py3-none-any.whl diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt index 787ffc30a81..8298bb08b2d 100644 --- a/build/pkgs/wheel/package-version.txt +++ b/build/pkgs/wheel/package-version.txt @@ -1 +1 @@ -0.42.0 +0.43.0 diff --git a/build/pkgs/wheel/version_requirements.txt b/build/pkgs/wheel/version_requirements.txt deleted file mode 100644 index 43f74ab0144..00000000000 --- a/build/pkgs/wheel/version_requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -# https://github.com/sagemath/sage/issues/31050 - version constraint for macOS Big Sur support -wheel >=0.36.2 diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 96bca3a6d8c..cf11e3c4da7 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -10,7 +10,10 @@ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2024 Matthias Koeppe +# 2022 Thierry Monteil +# 2024 Marc Culler # # 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 @@ -21,6 +24,7 @@ import os +import re import logging log = logging.getLogger() @@ -36,6 +40,10 @@ from sage_bootstrap.env import SAGE_DISTFILES +# Approximation of https://peps.python.org/pep-0508/#names dependency specification +dep_re = re.compile('^ *([-A-Z0-9._]+)', re.IGNORECASE) + + class Application(object): def config(self): @@ -88,7 +96,7 @@ def properties(self, *package_classes, **kwds): source_maxima='normal' trees_maxima='SAGE_LOCAL' """ - props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees']) + props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees', 'purl']) format = kwds.pop('format', 'plain') log.debug('Looking up properties') pc = PackageClass(*package_classes) @@ -256,6 +264,9 @@ def update_latest(self, package_name, commit=False): Update a package to the latest version. This modifies the Sage sources. """ pkg = Package(package_name) + if pkg.source not in ['normal', 'wheel']: + log.debug('update_latest can only update normal and wheel packages; %s is a %s package' % (pkg, pkg.source)) + return dist_name = pkg.distribution_name if dist_name is None: log.debug('%s does not have Python distribution info in version_requirements.txt' % pkg) @@ -304,11 +315,14 @@ def download(self, package_name, allow_upstream=False): package.tarball.download(allow_upstream=allow_upstream) print(package.tarball.upstream_fqn) - def download_cls(self, package_name_or_class, allow_upstream=False, on_error='stop'): + def download_cls(self, *package_classes, **kwds): """ Download a package or a class of packages """ - pc = PackageClass(package_name_or_class, has_files=['checksums.ini']) + allow_upstream = kwds.pop('allow_upstream', False) + on_error = kwds.pop('on_error', 'stop') + has_files = list(kwds.pop('has_files', [])) + pc = PackageClass(*package_classes, has_files=has_files + ['checksums.ini'], **kwds) def download_with_args(package): try: @@ -380,7 +394,8 @@ def fix_checksum(self, package_name): update.fix_checksum() def create(self, package_name, version=None, tarball=None, pkg_type=None, upstream_url=None, - description=None, license=None, upstream_contact=None, pypi=False, source=None): + description=None, license=None, upstream_contact=None, pypi=False, source=None, + dependencies=None): """ Create a package @@ -392,7 +407,12 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre $ sage --package create jupyterlab_markup --pypi --source wheel --type optional """ - if '-' in package_name: + if package_name.startswith('pypi/'): + package_name = 'pkg:' + package_name + if package_name.startswith('pkg:pypi/'): + pypi = True + package_name = package_name[len('pkg:pypi/'):].lower().replace('-', '_').replace('.', '_') + elif '-' in package_name: raise ValueError('package names must not contain dashes, use underscore instead') if pypi: if source is None: @@ -420,6 +440,24 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre raise ValueError('Only platform-independent wheels can be used for wheel packages, got {0}'.format(tarball)) if not version: version = pypi_version.version + if dependencies is None: + requires_dist = pypi_version.requires_dist + if requires_dist: + dependencies = [] + for item in requires_dist: + if "extra ==" in item: + continue + try: + dep = dep_re.match(item).groups()[0].strip() + except Exception: + continue + dep = 'pkg:pypi/' + dep + try: + dep = Package(dep).name + except ValueError: + self.create(dep, pkg_type=pkg_type) + dep = Package(dep).name + dependencies.append(dep) upstream_url = 'https://pypi.io/packages/{2}/{0:1.1}/{0}/{1}'.format(package_name, tarball, pypi_version.python_version) if not description: description = pypi_version.summary @@ -444,7 +482,8 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre if description or license or upstream_contact: creator.set_description(description, license, upstream_contact) if pypi or source == 'pip': - creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source) + creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source, + dependencies=dependencies) if tarball: creator.set_tarball(tarball, upstream_url) if upstream_url and version: diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 3ed185a9185..ff11315aa51 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -12,7 +12,9 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2020-2024 Matthias Koeppe +# 2022 Thierry Monteil # # 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 @@ -270,18 +272,19 @@ def make_parser(): parser_config = subparsers.add_parser( 'config', epilog=epilog_config, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print the configuration') + help='print the configuration') parser_list = subparsers.add_parser( 'list', epilog=epilog_list, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print a list of packages known to Sage') + help='print a list of packages known to Sage') parser_list.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, default=[':all-or-nothing:'], nargs='*', - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:); ' - 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given')) + 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given)')) parser_list.add_argument( '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files', help=('only include packages that have this file in their metadata directory ' @@ -303,11 +306,12 @@ def make_parser(): parser_properties = subparsers.add_parser( 'properties', epilog=epilog_properties, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print properties of given packages') + help='print properties of given packages') parser_properties.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, nargs='+', - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:)')) parser_properties.add_argument( '--format', type=str, default='plain', @@ -316,7 +320,7 @@ def make_parser(): parser_dependencies = subparsers.add_parser( 'dependencies', epilog=epilog_dependencies, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print the list of packages that are dependencies of given packages') + help='print the list of packages that are dependencies of given packages') parser_dependencies.add_argument( 'package_class', metavar='[package_name|:package_type:]', type=str, nargs='+', @@ -341,121 +345,136 @@ def make_parser(): parser_name = subparsers.add_parser( 'name', epilog=epilog_name, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find the package name given a tarball filename') - parser_name.add_argument('tarball_filename', type=str, help='Tarball filename') + help='find the package name given a tarball filename') + parser_name.add_argument('tarball_filename', type=str, help='tarball filename') parser_tarball = subparsers.add_parser( 'tarball', epilog=epilog_tarball, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find the tarball filename given a package name') - parser_tarball.add_argument('package_name', type=str, help='Package name') + help='find the tarball filename given a package name') + parser_tarball.add_argument('package_name', type=str, help='package name') parser_apropos = subparsers.add_parser( 'apropos', epilog=epilog_apropos, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find up to 5 package names that are close to the given name') + help='find up to 5 package names that are close to the given name') parser_apropos.add_argument( 'incorrect_name', type=str, - help='Fuzzy name to search for') + help='fuzzy name to search for') parser_update = subparsers.add_parser( 'update', epilog=epilog_update, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Update a package. This modifies the Sage sources.') + help='update a package, modifying the Sage sources') parser_update.add_argument( - 'package_name', type=str, help='Package name') + 'package_name', type=str, help='package name') parser_update.add_argument( - 'new_version', type=str, help='New version') + 'new_version', type=str, help='new version') parser_update.add_argument( - '--url', type=str, default=None, help='Download URL') + '--url', type=str, default=None, help='download URL') parser_update.add_argument( '--commit', action="store_true", - help='Whether to run "git commit"') + help='whether to run "git commit"') parser_update_latest = subparsers.add_parser( 'update-latest', epilog=epilog_update_latest, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Update a package to the latest version. This modifies the Sage sources.') + help='update a package to the latest version, modifying the Sage sources') parser_update_latest.add_argument( - 'package_name', type=str, help='Package name (:all: for all packages)') + 'package_name', type=str, help='package name (:all: for all packages)') parser_update_latest.add_argument( '--commit', action="store_true", - help='Whether to run "git commit"') + help='whether to run "git commit"') parser_download = subparsers.add_parser( 'download', epilog=epilog_download, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Download tarball') + help='download tarball') parser_download.add_argument( - 'package_name', type=str, help='Package name or :type:') + 'package_class', metavar='[package_name|:package_type:]', + type=str, nargs='+', + help=('package name or designator for all packages of a given type ' + '(one of :all:, :standard:, :optional:, and :experimental:)')) + parser_download.add_argument( + '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files', + help=('only include packages that have this file in their metadata directory ' + '(examples: SPKG.rst, spkg-configure.m4, distros/debian.txt, spkg-install|spkg-install.in)')) + parser_download.add_argument( + '--no-file', action='append', default=[], metavar='FILENAME', dest='no_files', + help=('only include packages that do not have this file in their metadata directory ' + '(examples: huge, patches, huge|has_nonfree_dependencies)')) + parser_download.add_argument( + '--exclude', nargs='*', action='append', default=[], metavar='PACKAGE_NAME', + help='exclude package from list') parser_download.add_argument( '--allow-upstream', action="store_true", - help='Whether to fall back to downloading from the upstream URL') + help='whether to fall back to downloading from the upstream URL') parser_download.add_argument( '--on-error', choices=['stop', 'warn'], default='stop', - help='What to do if the tarball cannot be downloaded') + help='what to do if the tarball cannot be downloaded') parser_download.add_argument( '--no-check-certificate', action='store_true', - help='Do not check SSL certificates for https connections') + help='do not check SSL certificates for https connections') parser_upload = subparsers.add_parser( 'upload', epilog=epilog_upload, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Upload tarball to Sage mirrors') + help='upload tarball to Sage mirrors') parser_upload.add_argument( - 'package_name', type=str, help='Package name or :type:') + 'package_name', type=str, help='package name or :type:') parser_fix_checksum = subparsers.add_parser( 'fix-checksum', epilog=epilog_fix_checksum, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Fix the checksum of normal packages.') + help='fix the checksum of normal packages') parser_fix_checksum.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, default=[':all:'], nargs='*', - help=('package name or designator for all packages of a given type ' - '(one of :all:, :standard:, :optional:, and :experimental:); ' - 'default: :all:')) + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' + '(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)')) parser_create = subparsers.add_parser( 'create', epilog=epilog_create, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Create or overwrite package.') + help='create or overwrite a package') parser_create.add_argument( 'package_name', default=None, type=str, - help='Package name.') + help='package name') parser_create.add_argument( - '--source', type=str, default=None, help='Package source (one of normal, wheel, script, pip); default depends on provided arguments') + '--source', type=str, default=None, help='package source (one of normal, wheel, script, pip); default depends on provided arguments') parser_create.add_argument( - '--version', type=str, default=None, help='Package version') + '--version', type=str, default=None, help='package version') parser_create.add_argument( - '--tarball', type=str, default=None, help='Tarball filename pattern, e.g. Foo-VERSION.tar.bz2') + '--tarball', type=str, default=None, help='tarball filename pattern, e.g. Foo-VERSION.tar.bz2') parser_create.add_argument( - '--type', type=str, default=None, help='Package type') + '--type', type=str, default=None, help='package type') parser_create.add_argument( - '--url', type=str, default=None, help='Download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2') + '--url', type=str, default=None, help='download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2') parser_create.add_argument( - '--description', type=str, default=None, help='Short description of the package (for SPKG.rst)') + '--description', type=str, default=None, help='short description of the package (for SPKG.rst)') parser_create.add_argument( - '--license', type=str, default=None, help='License of the package (for SPKG.rst)') + '--license', type=str, default=None, help='license of the package (for SPKG.rst)') parser_create.add_argument( - '--upstream-contact', type=str, default=None, help='Upstream contact (for SPKG.rst)') + '--upstream-contact', type=str, default=None, help='upstream contact (for SPKG.rst)') parser_create.add_argument( '--pypi', action="store_true", - help='Create a package for a Python package available on PyPI') + help='create a package for a Python package available on PyPI') parser_clean = subparsers.add_parser( 'clean', epilog=epilog_clean, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Remove outdated source tarballs from the upstream/ directory') + help='remove outdated source tarballs from the upstream/ directory') parser_metrics = subparsers.add_parser( 'metrics', epilog=epilog_metrics, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print metrics of given packages') + help='print metrics of given packages') parser_metrics.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, nargs='*', default=[':all:'], - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)')) return parser @@ -517,7 +536,9 @@ def run(): ssl._create_default_https_context = ssl._create_unverified_context except ImportError: pass - app.download_cls(args.package_name, + app.download_cls(*args.package_class, + has_files=args.has_files, no_files=args.no_files, + exclude=args.exclude, allow_upstream=args.allow_upstream, on_error=args.on_error) elif args.subcommand == 'create': diff --git a/build/sage_bootstrap/creator.py b/build/sage_bootstrap/creator.py index a738d772215..e16002f12f9 100644 --- a/build/sage_bootstrap/creator.py +++ b/build/sage_bootstrap/creator.py @@ -4,7 +4,8 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2020-2024 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -105,7 +106,7 @@ def _remove_files(self, files): except OSError: pass - def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): + def set_python_data_and_scripts(self, pypi_package_name=None, source='normal', dependencies=None): """ Write the file ``dependencies`` and other files for Python packages. @@ -121,7 +122,15 @@ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): if pypi_package_name is None: pypi_package_name = self.package_name with open(os.path.join(self.path, 'dependencies'), 'w+') as f: - f.write(' | $(PYTHON_TOOLCHAIN) $(PYTHON)\n\n') + if dependencies: + dependencies = ' '.join(dependencies) + else: + dependencies = '' + if source == 'wheel': + dependencies_order_only = 'pip $(PYTHON)' + else: + dependencies_order_only = '$(PYTHON_TOOLCHAIN) $(PYTHON)' + f.write(dependencies + ' | ' + dependencies_order_only + '\n\n') f.write('----------\nAll lines of this file are ignored except the first.\n') if source == 'normal': with open(os.path.join(self.path, 'spkg-install.in'), 'w+') as f: diff --git a/build/sage_bootstrap/download/cmdline.py b/build/sage_bootstrap/download/cmdline.py index a6afb4f92be..594cc773b38 100644 --- a/build/sage_bootstrap/download/cmdline.py +++ b/build/sage_bootstrap/download/cmdline.py @@ -6,7 +6,9 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2015 Jeroen Demeyer +# 2020 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/sage_bootstrap/download/mirror_list.py b/build/sage_bootstrap/download/mirror_list.py index 4cab19f5d64..f464d87ffbd 100644 --- a/build/sage_bootstrap/download/mirror_list.py +++ b/build/sage_bootstrap/download/mirror_list.py @@ -4,7 +4,9 @@ """ #***************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2014-2016 Volker Braun +# 2015 Jeroen Demeyer +# 2023 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py index c7fdb308bd3..c5bab8a313e 100644 --- a/build/sage_bootstrap/expand_class.py +++ b/build/sage_bootstrap/expand_class.py @@ -4,7 +4,8 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2024 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -52,12 +53,21 @@ def included_in_filter(pkg): self._init_optional(predicate=included_in_filter) elif package_name_or_class == ':experimental:': self._init_experimental(predicate=included_in_filter) + elif any(package_name_or_class.startswith(prefix) + for prefix in ["pkg:", "pypi/", "generic"]): + self.__names.add(Package(package_name_or_class).name) else: if ':' in package_name_or_class: - raise ValueError('a colon may only appear in designators of package types, ' + raise ValueError('a colon may only appear in a PURL such as ' + 'pkg:pypi/DISTRIBUTION-NAME ' + 'and in designators of package types, ' 'which must be one of ' ':all:, :standard:, :optional:, or :experimental:' 'got {}'.format(package_name_or_class)) + if '-' in package_name_or_class: + raise ValueError('dashes may only appear in a PURL such as ' + 'pkg:pypi/DISTRIBUTION-NAME; ' + 'SPKG names use underscores') self.__names.add(package_name_or_class) def include_recursive_dependencies(names, package_name): diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index aea31e4b6a4..176e842e43b 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -4,7 +4,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2018 Jeroen Demeyer +# 2020-2024 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,6 +27,33 @@ class Package(object): + def __new__(cls, package_name): + if package_name.startswith("pypi/") or package_name.startswith("generic/"): + package_name = "pkg:" + package_name + if package_name.startswith("pkg:"): + package_name = package_name.replace('_', '-') + if package_name.startswith("pkg:generic/"): # fast path + try: + pkg = cls(package_name[len("pkg:generic/"):].replace('-', '_')) + if pkg.purl == package_name: + return pkg # assume unique + except Exception: + pass + elif package_name.startswith("pkg:pypi/"): # fast path + try: + pkg = cls(package_name[len("pkg:pypi/"):].replace('-', '_')) + if pkg.purl == package_name: + return pkg # assume unique + except Exception: + pass + for pkg in cls.all(): + if pkg.purl == package_name: + return pkg # assume unique + raise ValueError('no package for PURL {0}'.format(package_name)) + self = object.__new__(cls) + self.__init__(package_name) + return self + def __init__(self, package_name): """ Sage Package @@ -41,12 +70,22 @@ def __init__(self, package_name): -- ``package_name`` -- string. Name of the package. The Sage convention is that all package names are lower case. """ + if any(package_name.startswith(prefix) + for prefix in ["pkg:", "pypi/", "generic"]): + # Already initialized + return + if package_name != package_name.lower(): + raise ValueError('package names should be lowercase, got {0}'.format(package_name)) + if '-' in package_name: + raise ValueError('package names use underscores, not dashes, got {0}'.format(package_name)) + self.__name = package_name self.__tarball = None self._init_checksum() self._init_version() self._init_type() self._init_version_requirements() + self._init_requirements() self._init_dependencies() self._init_trees() @@ -323,7 +362,7 @@ def source(self): """ Return the package source type """ - if self.has_file('requirements.txt'): + if self.__requirements is not None: return 'pip' if self.tarball_filename: if self.tarball_filename.endswith('.whl'): @@ -346,15 +385,39 @@ def trees(self): return self.__trees if self.__version_requirements is not None: return 'SAGE_VENV' - if self.has_file('requirements.txt'): + if self.__requirements is not None: return 'SAGE_VENV' return 'SAGE_LOCAL' + @property + def purl(self): + """ + Return a PURL (Package URL) for the package + + OUTPUT: + + A string in the format ``SCHEME:TYPE/NAMESPACE/NAME``, + i.e., without components for version, qualifiers, and subpath. + See https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#package-url-specification-v10x + for details + """ + dist = self.distribution_name + if dist: + return 'pkg:pypi/' + dist.lower().replace('_', '-') + return 'pkg:generic/' + self.name.replace('_', '-') + @property def distribution_name(self): """ Return the Python distribution name or ``None`` for non-Python packages """ + if self.__requirements is not None: + for line in self.__requirements.split('\n'): + line = line.strip() + if line.startswith('#'): + continue + for part in line.split(): + return part if self.__version_requirements is None: return None for line in self.__version_requirements.split('\n'): @@ -420,7 +483,7 @@ def all(cls): continue try: yield cls(subdir) - except BaseException: + except Exception: log.error('Failed to open %s', subdir) raise @@ -517,6 +580,13 @@ def _init_version_requirements(self): except IOError: self.__version_requirements = None + def _init_requirements(self): + try: + with open(os.path.join(self.path, 'requirements.txt')) as f: + self.__requirements = f.read().strip() + except IOError: + self.__requirements = None + def _init_dependencies(self): try: with open(os.path.join(self.path, 'dependencies')) as f: diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py index e3ca4e560c7..9427e9c8808 100644 --- a/build/sage_bootstrap/pypi.py +++ b/build/sage_bootstrap/pypi.py @@ -5,7 +5,8 @@ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2023 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -106,6 +107,13 @@ def summary(self): """ return self.json['info']['summary'] + @property + def requires_dist(self): + """ + Return the dependencies + """ + return self.json['info']['requires_dist'] + def update(self, package=None): if package is None: package = Package(self.name) diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py index 441d97cb3c6..c974d8ac310 100644 --- a/build/sage_bootstrap/tarball.py +++ b/build/sage_bootstrap/tarball.py @@ -4,7 +4,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2014-2015 Volker Braun +# 2017 Jeroen Demeyer +# 2020 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/sage_bootstrap/uninstall.py b/build/sage_bootstrap/uninstall.py index 08ce337386d..25496246ac7 100644 --- a/build/sage_bootstrap/uninstall.py +++ b/build/sage_bootstrap/uninstall.py @@ -24,7 +24,9 @@ are also removed. """ # **************************************************************************** -# Copyright (C) 2017 Erik M. Bray +# Copyright (C) 2017-2018 Erik M. Bray +# 2019 Jeroen Demeyer +# 2021-2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sage-conf_pypi/tox.ini b/pkgs/sage-conf_pypi/tox.ini index fadf9a9cc6c..7160d4db678 100644 --- a/pkgs/sage-conf_pypi/tox.ini +++ b/pkgs/sage-conf_pypi/tox.ini @@ -1,6 +1,10 @@ [tox] envlist = py39, py310, py311, py39-user, py310-user, py311-user +requires = + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 + tox<4.14.1 + [testenv:.pkg] basepython = py311 passenv = diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sage-docbuild/tox.ini b/pkgs/sage-docbuild/tox.ini index 77da5a78ede..efa222028ff 100644 --- a/pkgs/sage-docbuild/tox.ini +++ b/pkgs/sage-docbuild/tox.ini @@ -8,6 +8,10 @@ # [tox] +requires = + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 + tox<4.14.1 + [testenv] deps = -rrequirements.txt diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sage-setup/tox.ini b/pkgs/sage-setup/tox.ini index fd935f2e978..e8a22f03101 100644 --- a/pkgs/sage-setup/tox.ini +++ b/pkgs/sage-setup/tox.ini @@ -12,6 +12,10 @@ # [tox] +requires = + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 + tox<4.14.1 + [testenv] deps = -rrequirements.txt diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-categories/known-test-failures.json b/pkgs/sagemath-categories/known-test-failures.json index ddae185a7d9..80705635b7e 100644 --- a/pkgs/sagemath-categories/known-test-failures.json +++ b/pkgs/sagemath-categories/known-test-failures.json @@ -26,7 +26,7 @@ "ntests": 28 }, "sage.categories.affine_weyl_groups": { - "ntests": 14 + "ntests": 15 }, "sage.categories.algebra_ideals": { "failed": true, @@ -38,7 +38,7 @@ }, "sage.categories.algebras": { "failed": true, - "ntests": 20 + "ntests": 25 }, "sage.categories.algebras_with_basis": { "failed": true, @@ -126,7 +126,7 @@ }, "sage.categories.commutative_rings": { "failed": true, - "ntests": 39 + "ntests": 41 }, "sage.categories.complete_discrete_valuation": { "failed": true, @@ -148,15 +148,19 @@ }, "sage.categories.coxeter_groups": { "failed": true, - "ntests": 362 + "ntests": 392 }, "sage.categories.cw_complexes": { "failed": true, "ntests": 36 }, + "sage.categories.dedekind_domains": { + "failed": true, + "ntests": 30 + }, "sage.categories.discrete_valuation": { "failed": true, - "ntests": 23 + "ntests": 28 }, "sage.categories.distributive_magmas_and_additive_magmas": { "failed": true, @@ -169,9 +173,6 @@ "failed": true, "ntests": 7 }, - "sage.categories.drinfeld_modules": { - "ntests": 2 - }, "sage.categories.dual": { "failed": true, "ntests": 1 @@ -297,18 +298,18 @@ }, "sage.categories.filtered_modules_with_basis": { "failed": true, - "ntests": 65 + "ntests": 74 }, "sage.categories.finite_complex_reflection_groups": { "failed": true, - "ntests": 178 + "ntests": 189 }, "sage.categories.finite_coxeter_groups": { "ntests": 6 }, "sage.categories.finite_dimensional_algebras_with_basis": { "failed": true, - "ntests": 87 + "ntests": 91 }, "sage.categories.finite_dimensional_bialgebras_with_basis": { "failed": true, @@ -328,11 +329,11 @@ }, "sage.categories.finite_dimensional_lie_algebras_with_basis": { "failed": true, - "ntests": 34 + "ntests": 73 }, "sage.categories.finite_dimensional_modules_with_basis": { "failed": true, - "ntests": 55 + "ntests": 89 }, "sage.categories.finite_dimensional_nilpotent_lie_algebras_with_basis": { "failed": true, @@ -493,10 +494,6 @@ "failed": true, "ntests": 13 }, - "sage.categories.inner_product_spaces": { - "failed": true, - "ntests": 9 - }, "sage.categories.integral_domains": { "failed": true, "ntests": 22 @@ -530,7 +527,7 @@ }, "sage.categories.lie_algebras": { "failed": true, - "ntests": 125 + "ntests": 135 }, "sage.categories.lie_algebras_with_basis": { "ntests": 19 @@ -586,7 +583,7 @@ }, "sage.categories.modules_with_basis": { "failed": true, - "ntests": 221 + "ntests": 240 }, "sage.categories.monoid_algebras": { "failed": true, @@ -598,7 +595,11 @@ }, "sage.categories.morphism": { "failed": true, - "ntests": 99 + "ntests": 126 + }, + "sage.categories.noetherian_rings": { + "failed": true, + "ntests": 19 }, "sage.categories.number_fields": { "failed": true, @@ -668,14 +669,14 @@ }, "sage.categories.rings": { "failed": true, - "ntests": 141 + "ntests": 146 }, "sage.categories.rngs": { "ntests": 6 }, "sage.categories.schemes": { "failed": true, - "ntests": 23 + "ntests": 41 }, "sage.categories.semigroups": { "failed": true, @@ -711,7 +712,7 @@ }, "sage.categories.simplicial_sets": { "failed": true, - "ntests": 57 + "ntests": 98 }, "sage.categories.subobjects": { "ntests": 2 @@ -765,7 +766,7 @@ }, "sage.categories.unital_algebras": { "failed": true, - "ntests": 39 + "ntests": 37 }, "sage.categories.vector_spaces": { "failed": true, @@ -783,7 +784,7 @@ }, "sage.cpython.debug": { "failed": true, - "ntests": 14 + "ntests": 13 }, "sage.cpython.dict_del_by_value": { "failed": true, @@ -808,30 +809,28 @@ }, "sage.doctest.control": { "failed": true, - "ntests": 0 + "ntests": 230 }, "sage.doctest.external": { "ntests": 42 }, "sage.doctest.fixtures": { - "failed": true, "ntests": 59 }, "sage.doctest.forker": { "failed": true, - "ntests": 433 + "ntests": 413 }, "sage.doctest.parsing": { "failed": true, - "ntests": 321 + "ntests": 313 }, "sage.doctest.reporting": { - "failed": true, - "ntests": 124 + "ntests": 115 }, "sage.doctest.sources": { "failed": true, - "ntests": 378 + "ntests": 343 }, "sage.doctest.test": { "failed": true, @@ -846,7 +845,7 @@ "ntests": 41 }, "sage.features": { - "ntests": 145 + "ntests": 143 }, "sage.features.all": { "ntests": 14 @@ -865,17 +864,23 @@ }, "sage.features.databases": { "failed": true, - "ntests": 26 + "ntests": 38 }, "sage.features.dvipng": { "ntests": 4 }, + "sage.features.ecm": { + "ntests": 4 + }, "sage.features.ffmpeg": { "ntests": 4 }, "sage.features.four_ti_2": { "ntests": 6 }, + "sage.features.fricas": { + "ntests": 6 + }, "sage.features.gap": { "ntests": 6 }, @@ -901,7 +906,11 @@ "sage.features.internet": { "ntests": 5 }, + "sage.features.jmol": { + "ntests": 4 + }, "sage.features.join_feature": { + "failed": true, "ntests": 25 }, "sage.features.kenzo": { @@ -960,7 +969,7 @@ }, "sage.features.sagemath": { "failed": true, - "ntests": 147 + "ntests": 151 }, "sage.features.singular": { "ntests": 4 @@ -968,9 +977,16 @@ "sage.features.sphinx": { "ntests": 4 }, + "sage.features.symengine_py": { + "ntests": 4 + }, "sage.features.tdlib": { "ntests": 2 }, + "sage.features.threejs": { + "failed": true, + "ntests": 6 + }, "sage.misc.abstract_method": { "failed": true, "ntests": 33 @@ -1001,7 +1017,7 @@ }, "sage.misc.decorators": { "failed": true, - "ntests": 126 + "ntests": 125 }, "sage.misc.fast_methods": { "failed": true, @@ -1059,7 +1075,7 @@ }, "sage.misc.package_dir": { "failed": true, - "ntests": 29 + "ntests": 35 }, "sage.misc.persist": { "failed": true, @@ -1091,7 +1107,7 @@ }, "sage.misc.sageinspect": { "failed": true, - "ntests": 329 + "ntests": 322 }, "sage.misc.superseded": { "failed": true, @@ -1157,7 +1173,7 @@ }, "sage.repl.interpreter": { "failed": true, - "ntests": 118 + "ntests": 114 }, "sage.repl.ipython_extension": { "failed": true, @@ -1165,13 +1181,14 @@ }, "sage.repl.ipython_kernel.install": { "failed": true, - "ntests": 40 + "ntests": 35 }, "sage.repl.ipython_kernel.interact": { "failed": true, "ntests": 42 }, "sage.repl.ipython_kernel.kernel": { + "failed": true, "ntests": 12 }, "sage.repl.ipython_kernel.widgets": { @@ -1188,7 +1205,7 @@ }, "sage.repl.load": { "failed": true, - "ntests": 42 + "ntests": 38 }, "sage.repl.preparse": { "failed": true, @@ -1206,7 +1223,7 @@ }, "sage.repl.rich_output.backend_ipython": { "failed": true, - "ntests": 78 + "ntests": 75 }, "sage.repl.rich_output.buffer": { "failed": true, @@ -1214,7 +1231,7 @@ }, "sage.repl.rich_output.display_manager": { "failed": true, - "ntests": 88 + "ntests": 86 }, "sage.repl.rich_output.output_basic": { "ntests": 47 @@ -1251,7 +1268,7 @@ }, "sage.rings.ring": { "failed": true, - "ntests": 337 + "ntests": 298 }, "sage.sets.pythonclass": { "failed": true, @@ -1263,7 +1280,7 @@ }, "sage.structure.coerce": { "failed": true, - "ntests": 314 + "ntests": 310 }, "sage.structure.coerce_actions": { "failed": true, @@ -1298,7 +1315,7 @@ }, "sage.structure.factorization": { "failed": true, - "ntests": 203 + "ntests": 200 }, "sage.structure.factorization_integer": { "failed": true, @@ -1315,9 +1332,6 @@ "sage.structure.global_options": { "ntests": 145 }, - "sage.structure.graphics_file": { - "ntests": 8 - }, "sage.structure.indexed_generators": { "failed": true, "ntests": 90 @@ -1345,7 +1359,7 @@ }, "sage.structure.parent": { "failed": true, - "ntests": 300 + "ntests": 303 }, "sage.structure.parent_gens": { "failed": true, @@ -1418,4 +1432,4 @@ "sage.typeset.unicode_characters": { "ntests": 27 } -} \ No newline at end of file +} diff --git a/pkgs/sagemath-categories/requirements-editable.txt.m4 b/pkgs/sagemath-categories/requirements-editable.txt.m4 new file mode 100644 index 00000000000..1c0abbe40ae --- /dev/null +++ b/pkgs/sagemath-categories/requirements-editable.txt.m4 @@ -0,0 +1,7 @@ +include(`sage_spkg_versions.m4')dnl +dnl Same as setup.cfg.m4 install_requires; FIXME: should pin to built wheels. +SPKG_INSTALL_REQUIRES_gmpy2 +SPKG_INSTALL_REQUIRES_cysignals +SPKG_INSTALL_REQUIRES_memory_allocator +-e ../sagemath-environment +-e ../sagemath-objects diff --git a/pkgs/sagemath-categories/tox.ini b/pkgs/sagemath-categories/tox.ini index b4bb49d132a..841209e88f1 100644 --- a/pkgs/sagemath-categories/tox.ini +++ b/pkgs/sagemath-categories/tox.ini @@ -13,7 +13,9 @@ envlist = requires = # Auto-provision a modern tox. # [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 tox>=4.2 + tox<4.14.1 [pkgenv] # Environment in which to build the sdist. @@ -49,6 +51,11 @@ passenv = {[pkgenv]passenv} setenv = {[pkgenv]setenv} # Sage scripts such as sage-runtests like to use $HOME/.sage HOME={envdir} + # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL + SAGE_DOC=/doesnotexist + KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json + # See src/bin/sage-env + PYDEVD_DISABLE_FILE_VALIDATION=1 allowlist_externals = bash @@ -61,7 +68,7 @@ commands = {envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); from sage.categories.all import *; SimplicialComplexes(); FunctionFields()' bash -c 'cd $(python -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") \ - && sage-runtests -p --initial --environment=sage.all__sagemath_categories --probe all --baseline-stats-path={toxinidir}/known-test-failures.json --optional=sage --installed' + && sage-runtests -p --force-lib --initial --environment=sage.all__sagemath_categories --probe all --baseline-stats-path=$KNOWN_TEST_FAILURES --optional=sage --installed' [testenv:.tox] # Allow access to PyPI for auto-provisioning a suitable tox version @@ -84,6 +91,11 @@ setenv = {[pkgenv]setenv} basepython = {env:SAGE_VENV}/bin/python3 +[testenv:.pkg-sagepython-sagewheels-nopypi-editable] +config_settings_build_editable = + editable_mode = strict + + [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython @@ -92,10 +104,6 @@ package_env = .pkg-sagepython basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython-sagewheels-nopypi -[testenv:sagepython-sagewheels-nopypi-norequirements] -basepython = {env:SAGE_VENV}/bin/python3 -package_env = .pkg-sagepython-sagewheels-nopypi - [testenv:sagepython-sagewheels] basepython = {env:SAGE_VENV}/bin/python package_env = .pkg-sagepython @@ -103,3 +111,16 @@ package_env = .pkg-sagepython [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython + + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi + +[testenv:sagepython-sagewheels-nopypi-editable] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi-editable +package = editable +deps = -r requirements-editable.txt +config_settings_build_editable = + editable_mode = strict diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-environment/requirements-editable.txt.m4 b/pkgs/sagemath-environment/requirements-editable.txt.m4 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkgs/sagemath-environment/tox.ini b/pkgs/sagemath-environment/tox.ini index 6bf1f2a6ebb..5d741bd0b48 100644 --- a/pkgs/sagemath-environment/tox.ini +++ b/pkgs/sagemath-environment/tox.ini @@ -13,7 +13,9 @@ envlist = requires = # Auto-provision a modern tox. # [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 tox>=4.2 + tox<4.14.1 [pkgenv] # Environment in which to build the sdist. @@ -49,6 +51,9 @@ passenv = {[pkgenv]passenv} setenv = {[pkgenv]setenv} # Sage scripts such as sage-runtests like to use $HOME/.sage HOME={envdir} + # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL + SAGE_DOC=/doesnotexist + KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json allowlist_externals = bash @@ -78,6 +83,11 @@ setenv = {[pkgenv]setenv} basepython = {env:SAGE_VENV}/bin/python3 +[testenv:.pkg-sagepython-sagewheels-nopypi-editable] +config_settings_build_editable = + editable_mode = strict + + [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython @@ -98,3 +108,11 @@ package_env = .pkg-sagepython [testenv:sagepython-sagewheels-nopypi-norequirements] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython-sagewheels-nopypi + +[testenv:sagepython-sagewheels-nopypi-editable] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi-editable +package = editable +deps = -r requirements-editable.txt +config_settings_build_editable = + editable_mode = strict diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-objects/requirements-editable.txt.m4 b/pkgs/sagemath-objects/requirements-editable.txt.m4 new file mode 100644 index 00000000000..df9b22b43aa --- /dev/null +++ b/pkgs/sagemath-objects/requirements-editable.txt.m4 @@ -0,0 +1,4 @@ +include(`sage_spkg_versions.m4')dnl +dnl Same as setup.cfg.m4 install_requires; FIXME: should pin to built wheels. +SPKG_INSTALL_REQUIRES_gmpy2 +SPKG_INSTALL_REQUIRES_cysignals diff --git a/pkgs/sagemath-objects/tox.ini b/pkgs/sagemath-objects/tox.ini index a8f5a6d6a76..a7b91f55990 100644 --- a/pkgs/sagemath-objects/tox.ini +++ b/pkgs/sagemath-objects/tox.ini @@ -13,7 +13,9 @@ envlist = requires = # Auto-provision a modern tox. # [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 tox>=4.2 + tox<4.14.1 [pkgenv] # Environment in which to build the sdist. @@ -49,6 +51,9 @@ passenv = {[pkgenv]passenv} setenv = {[pkgenv]setenv} # Sage scripts such as sage-runtests like to use $HOME/.sage HOME={envdir} + # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL + SAGE_DOC=/doesnotexist + KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json allowlist_externals = bash @@ -82,6 +87,11 @@ setenv = {[pkgenv]setenv} basepython = {env:SAGE_VENV}/bin/python3 +[testenv:.pkg-sagepython-sagewheels-nopypi-editable] +config_settings_build_editable = + editable_mode = strict + + [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython @@ -102,3 +112,11 @@ package_env = .pkg-sagepython [testenv:sagepython-sagewheels-nopypi-norequirements] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython-sagewheels-nopypi + +[testenv:sagepython-sagewheels-nopypi-editable] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi-editable +package = editable +deps = -r requirements-editable.txt +config_settings_build_editable = + editable_mode = strict diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-repl/pyproject.toml.m4 b/pkgs/sagemath-repl/pyproject.toml.m4 index 2d2f13008be..459a599f1fb 100644 --- a/pkgs/sagemath-repl/pyproject.toml.m4 +++ b/pkgs/sagemath-repl/pyproject.toml.m4 @@ -13,8 +13,10 @@ description = "Sage: Open Source Mathematics Software: IPython kernel, Sage prep dependencies = [ SPKG_INSTALL_REQUIRES_sagemath_objects SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_ipykernel SPKG_INSTALL_REQUIRES_ipython SPKG_INSTALL_REQUIRES_ipywidgets + SPKG_INSTALL_REQUIRES_jupyter_client ] dynamic = ["version"] include(`pyproject_toml_metadata.m4')dnl' diff --git a/pkgs/sagemath-repl/requirements-editable.txt.m4 b/pkgs/sagemath-repl/requirements-editable.txt.m4 new file mode 100644 index 00000000000..fa89ade7621 --- /dev/null +++ b/pkgs/sagemath-repl/requirements-editable.txt.m4 @@ -0,0 +1,11 @@ +include(`sage_spkg_versions.m4')dnl +dnl Same as setup.cfg.m4 install_requires (+ their install-requires) +dnl FIXME: should pin to built wheels. +SPKG_INSTALL_REQUIRES_gmpy2 +SPKG_INSTALL_REQUIRES_cysignals +SPKG_INSTALL_REQUIRES_memory_allocator +SPKG_INSTALL_REQUIRES_ipython +SPKG_INSTALL_REQUIRES_ipywidgets +dnl To be added when ready for editable: +-e ../sagemath-environment +-e ../sagemath-objects diff --git a/pkgs/sagemath-repl/tox.ini b/pkgs/sagemath-repl/tox.ini index 679153a2947..d7b557761ac 100644 --- a/pkgs/sagemath-repl/tox.ini +++ b/pkgs/sagemath-repl/tox.ini @@ -13,7 +13,9 @@ envlist = requires = # Auto-provision a modern tox. # [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 tox>=4.2 + tox<4.14.1 [pkgenv] # Environment in which to build the sdist. @@ -49,6 +51,11 @@ passenv = {[pkgenv]passenv} setenv = {[pkgenv]setenv} # Sage scripts such as sage-runtests like to use $HOME/.sage HOME={envdir} + # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL + SAGE_DOC=/doesnotexist + KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json + # See src/bin/sage-env + PYDEVD_DISABLE_FILE_VALIDATION=1 allowlist_externals = bash @@ -57,7 +64,7 @@ commands = # Beware of the treacherous non-src layout. "./sage/" shadows the installed sage package. {envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); import sage.repl.all; import sage.doctest.all' - bash -c 'cd $({envpython} -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") && sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path={toxinidir}/known-test-failures.json --initial --optional=sage sage/repl sage/doctest sage/misc/sage_input.py sage/misc/sage_eval.py' + bash -c 'cd $({envpython} -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") && sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path=$KNOWN_TEST_FAILURES --initial --optional=sage sage/repl sage/doctest sage/misc/sage_input.py sage/misc/sage_eval.py' [testenv:.tox] # Allow access to PyPI for auto-provisioning a suitable tox version @@ -80,6 +87,11 @@ setenv = {[pkgenv]setenv} basepython = {env:SAGE_VENV}/bin/python3 +[testenv:.pkg-sagepython-sagewheels-nopypi-editable] +config_settings_build_editable = + editable_mode = strict + + [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython @@ -88,10 +100,6 @@ package_env = .pkg-sagepython basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython-sagewheels-nopypi -[testenv:sagepython-sagewheels-nopypi-norequirements] -basepython = {env:SAGE_VENV}/bin/python3 -package_env = .pkg-sagepython-sagewheels-nopypi - [testenv:sagepython-sagewheels] basepython = {env:SAGE_VENV}/bin/python package_env = .pkg-sagepython @@ -99,3 +107,16 @@ package_env = .pkg-sagepython [testenv:sagepython-norequirements] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython + + +[testenv:sagepython-sagewheels-nopypi-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi + +[testenv:sagepython-sagewheels-nopypi-editable] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-sagewheels-nopypi-editable +package = editable +deps = -r requirements-editable.txt +config_settings_build_editable = + editable_mode = strict diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/pkgs/sagemath-standard/pyproject.toml.m4 b/pkgs/sagemath-standard/pyproject.toml.m4 deleted file mode 120000 index 25dbae84866..00000000000 --- a/pkgs/sagemath-standard/pyproject.toml.m4 +++ /dev/null @@ -1 +0,0 @@ -../../src/pyproject.toml.m4 \ No newline at end of file diff --git a/pkgs/sagemath-standard/tox.ini b/pkgs/sagemath-standard/tox.ini index 6aae1ef1bfa..c197f49d9be 100644 --- a/pkgs/sagemath-standard/tox.ini +++ b/pkgs/sagemath-standard/tox.ini @@ -62,7 +62,9 @@ envlist = requires = # Auto-provision a modern tox. # [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance + # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1 tox>=4.2 + tox<4.14.1 [pkgenv] # Environment in which to build the sdist. diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/src/VERSION.txt b/src/VERSION.txt index 957ecf95b0a..54ab6979ef2 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.4.beta4 +10.4.beta6 diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index 8f7687adcc5..bd898b2ae54 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -7,6 +7,8 @@ import ast import argparse import logging import textwrap +from contextlib import contextmanager + logging.basicConfig() logger = logging.getLogger() @@ -19,7 +21,6 @@ _system_jupyter_url = "https://doc.sagemath.org/html/en/installation/launching.h class NotebookJupyter(): def print_banner(self): - banner() print('Please wait while the Sage Jupyter Notebook server starts...') @classmethod @@ -50,7 +51,6 @@ class NotebookJupyter(): class NotebookJupyterlab(): def print_banner(self): - banner() print('Please wait while the Jupyterlab server starts...') @classmethod @@ -77,7 +77,6 @@ class NotebookJupyterlab(): class SageNBExport(NotebookJupyter): def print_banner(self): - banner() print('Please wait while the SageNB export server starts...') @classmethod @@ -131,7 +130,6 @@ EXAMPLES: """ - notebook_launcher = { 'default': NotebookJupyter, # change this to change the default 'ipython': NotebookJupyter, @@ -140,7 +138,6 @@ notebook_launcher = { 'export': SageNBExport, } - notebook_names = ', '.join(notebook_launcher.keys()) @@ -187,6 +184,32 @@ def trac_23428_browser_workaround(): os.environ['BROWSER'] = 'open' +@contextmanager +def sage_doc_server(): + from functools import partial + from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer + from threading import Thread + from sage.env import SAGE_DOC, SAGE_DOC_LOCAL_PORT as port + + server = ThreadingHTTPServer(('127.0.0.1', int(port)), + partial(SimpleHTTPRequestHandler, directory=SAGE_DOC)) + + if port == '0': + port = str(server.server_address[1]) + os.environ['SAGE_DOC_LOCAL_PORT'] = port + + server_thread = Thread(target=server.serve_forever, name="sage_doc_server") + server_thread.start() + print(f'Sage doc server started running at http://127.0.0.1:{port}') + + try: + yield + finally: + server.shutdown() + server_thread.join() + print(f'Sage doc server stopped runnning at http://127.0.0.1:{port}') + + if __name__ == '__main__': parser = make_parser() args, unknown = parser.parse_known_args(sys.argv[1:]) @@ -227,4 +250,19 @@ if __name__ == '__main__': launcher.print_help() sys.exit(0) - launcher(unknown) + banner() + + # Start a Sage doc server if the Sage documentation is available locally. + # See the corresponding code in src/sage/repl/ipython_kernel/kernel.py. + + from sage.env import SAGE_DOC_SERVER_URL + from sage.features.sagemath import sagemath_doc_html + + if SAGE_DOC_SERVER_URL: + print(f'Sage doc server is running at {SAGE_DOC_SERVER_URL}') + launcher(unknown) + elif sagemath_doc_html().is_present(): + with sage_doc_server(): + launcher(unknown) + else: + launcher(unknown) diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index c2aa055eac9..bab5675baae 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.4.beta4' -SAGE_RELEASE_DATE='2024-04-27' -SAGE_VERSION_BANNER='SageMath version 10.4.beta4, Release Date: 2024-04-27' +SAGE_VERSION='10.4.beta6' +SAGE_RELEASE_DATE='2024-05-12' +SAGE_VERSION_BANNER='SageMath version 10.4.beta6, Release Date: 2024-05-12' diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index c586b030ed8..8522fca3b9f 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -1303,11 +1303,11 @@ framework. Here is a comprehensive list: that is, commas, hyphens, semicolons, ..., after the first word ends the list of packages. Hyphens or colons between the word ``optional`` and the first package name are allowed. Therefore, - you should not write ``# optional - depends on package CHomP`` but simply - ``# optional - CHomP``. + you should not write ``# optional - depends on package bliss`` but simply + ``# optional - bliss``. - Optional tags are case-insensitive, so you could also write ``# optional - - chOMP``. + Bliss``. If ``# optional`` or ``# needs`` is placed right after the ``sage:`` prompt, it is a block-scoped tag, which applies to all doctest lines until diff --git a/src/doc/en/developer/coding_in_cython.rst b/src/doc/en/developer/coding_in_cython.rst index 829b8234e7b..d0c7b0d521f 100644 --- a/src/doc/en/developer/coding_in_cython.rst +++ b/src/doc/en/developer/coding_in_cython.rst @@ -191,3 +191,23 @@ original object. As an example, the following code snippet is the .. _python pickling documentation: http://docs.python.org/library/pickle.html#pickle-protocol +Deprecation +=========== + +When making a **backward-incompatible** modification in Sage, the old code should +keep working and display a message indicating how it should be updated/written +in the future. We call this a *deprecation*. + +.. NOTE:: + + Deprecated code can only be removed one year after the first + stable release in which it appeared. + +Each deprecation warning contains the number of the GitHub PR that defines +it. We use 666 in the example below. + +.. CODE-BLOCK:: cython + + from sage.misc.superseded import deprecation_cython + deprecation_cython(666, "Do not use your computer to compute 1+1. Use your brain.") + diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index 37552e5bcfe..82dda02f209 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -748,6 +748,13 @@ documentation for more information on its behaviour and optional arguments. from sage.misc.superseded import deprecation deprecation(666, "Do not use your computer to compute 1+1. Use your brain.") +Note that these decorators only work for (pure) Python. There is no implementation +of decorators in Cython. Hence, when in need to rename a keyword/function/method/... +in a Cython (.pyx) file and/or to deprecate something, forget about decorators and +just use :func:`~sage.misc.superseded.deprecation_cython` instead. The usage of +:func:`~sage.misc.superseded.deprecation_cython` is exactly the same as +:func:`~sage.misc.superseded.deprecation`. + Experimental/unstable code -------------------------- diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 679d12f8be7..6a778c3a71a 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -14,7 +14,7 @@ The installation of packages is done through a bash script located in :sage_root:`build/bin/sage-spkg`. This script is typically invoked by giving the command:: - [alice@localhost sage]$ sage -i ... + [alice@localhost sage]$ ./sage -i ... options can be: @@ -640,6 +640,8 @@ and upper bounds). The constraints are in the format of the `_ or `setup.py `_. +An exception are build time dependencies of Sage library, which should instead +be declared in the ``requires`` block of ``pyproject.toml``. Sage uses these version constraints for two purposes: @@ -1047,10 +1049,10 @@ Creating packages Assuming that you have downloaded ``$SAGE_ROOT/upstream/FoO-1.3.tar.gz``, you can use:: - [alice@localhost sage]$ sage --package create foo \ - --version 1.3 \ - --tarball FoO-VERSION.tar.gz \ - --type experimental + [alice@localhost sage]$ ./sage --package create foo \ + --version 1.3 \ + --tarball FoO-VERSION.tar.gz \ + --type experimental to create ``$SAGE_ROOT/build/pkgs/foo/package-version.txt``, ``checksums.ini``, and ``type`` in one step. @@ -1059,36 +1061,47 @@ You can skip the manual downloading of the upstream tarball by using the additional argument ``--upstream-url``. This command will also set the ``upstream_url`` field in ``checksums.ini`` described above. -For Python packages available from PyPI, you can use:: +For Python packages available from PyPI, use a PURL (Package URL, +see `PEP 725 `_):: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --type optional -This automatically downloads the most recent version from PyPI and also -obtains most of the necessary information by querying PyPI. In particular, -the ``SPKG.rst`` file is created as a copy of the package's README file. +An equivalent command uses the SPKG name of the new package:: + [alice@localhost sage]$ ./sage --package create scikit_spatial --pypi \ + --type optional -The ``dependencies`` file may need editing (watch out for warnings regarding -``--no-deps`` that Sage issues during installation of the package!). +Either of these two commands automatically downloads the most recent version +from PyPI and also obtains most of the necessary information by querying PyPI. +In particular, the ``SPKG.rst`` file is created as a copy of the package's +README file. + +By default, when the package is available as a platform-independent +wheel, the ``sage --package`` creates a ``wheel`` package. In this case, +the ``dependencies`` file is automatically generated from the information +on PyPI, but may still need some manual editing. + +For ``normal`` and ``pip`` packages, the ``dependencies`` file is initialized +to the bare minimum and will need manual editing. (Watch out for warnings +regarding ``--no-deps`` that Sage issues during installation of the package!) Also you may want to set lower and upper bounds for acceptable package versions in the file ``version_requirements.txt``. (Make sure that the version in ``package-version.txt`` falls within this acceptable version range!) -By default, when the package is available as a platform-independent -wheel, the ``sage --package`` creates a wheel package. To create a normal package -instead (for example, when the package requires patching), you can use:: +To create a ``normal`` package instead of a ``wheel`` package (for example, when the +package requires patching), you can use:: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --source normal \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --source normal \ + --type optional -To create a pip package rather than a normal or wheel package, you can use:: +To create a ``pip`` package rather than a ``normal`` or ``wheel`` package, you can use:: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --source pip \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --source pip \ + --type optional When the package already exists, ``sage --package create`` overwrites it. @@ -1099,14 +1112,14 @@ Updating packages to a new version A package that has the ``upstream_url`` information can be updated by simply typing:: - [alice@localhost sage]$ sage --package update numpy 3.14.59 + [alice@localhost sage]$ ./sage --package update openblas 0.3.79 which will automatically download the archive and update the -information in ``build/pkgs/numpy/``. +information in ``build/pkgs/openblas/``. For Python packages available from PyPI, there is another shortcut:: - [alice@localhost sage]$ sage --package update-latest matplotlib + [alice@localhost sage]$ ./sage --package update-latest pkg:pypi/matplotlib Updating matplotlib: 3.3.0 -> 3.3.1 Downloading tarball to ...matplotlib-3.3.1.tar.bz2 [...............................................................] @@ -1120,10 +1133,10 @@ version range! If you pass the switch ``--commit``, the script will run ``git commit`` for you. -If you prefer to make update a package ``foo`` by making manual +If you prefer to update a package ``foo`` by making manual changes to the files in ``build/pkgs/foo``, you will need to run:: - [alice@localhost sage]$ sage --package fix-checksum foo + [alice@localhost sage]$ ./sage --package fix-checksum foo which will modify the ``checksums.ini`` file with the correct checksums. @@ -1136,7 +1149,7 @@ The command ``sage --package metrics`` computes machine-readable aggregated metrics for all packages in the Sage distribution or a given list of packages:: - [alice@localhost sage]$ sage --package metrics + [alice@localhost sage]$ ./sage --package metrics has_file_distros_arch_txt=181 has_file_distros_conda_txt=289 has_file_distros_debian_txt=172 @@ -1210,20 +1223,20 @@ Sage (``FoO-1.3.tar.gz`` in the example of section Now you can install the package using:: - [alice@localhost sage]$ sage -i package_name + [alice@localhost sage]$ ./sage -i package_name or:: - [alice@localhost sage]$ sage -f package_name + [alice@localhost sage]$ ./sage -f package_name to force a reinstallation. If your package contains a ``spkg-check`` script (see :ref:`section-spkg-check`) it can be run with:: - [alice@localhost sage]$ sage -i -c package_name + [alice@localhost sage]$ ./sage -i -c package_name or:: - [alice@localhost sage]$ sage -f -c package_name + [alice@localhost sage]$ ./sage -f -c package_name If all went fine, open a PR with the code under :sage_root:`build/pkgs`. diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst index c7925b3392a..55cefd0bf60 100644 --- a/src/doc/en/developer/portability_testing.rst +++ b/src/doc/en/developer/portability_testing.rst @@ -349,7 +349,7 @@ Generating dockerfiles Sage also provides a script for generating a ``Dockerfile``, which is a recipe for automatically building a new image:: - [mkoeppe@sage sage]$ build/bin/write-dockerfile.sh debian ":standard: :optional:" > Dockerfile + [mkoeppe@sage sage]$ .ci/write-dockerfile.sh debian ":standard: :optional:" > Dockerfile (The second argument is passed to ``sage -package list`` to find packages for the listed package types.) @@ -360,7 +360,7 @@ new Docker image. Let us take a quick look at the generated file; this is slightly simplified:: [mkoeppe@sage sage]$ cat Dockerfile - # Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh + # Automatically generated by SAGE_ROOT/.ci/write-dockerfile.sh # the :comments: separate the generated file into sections # to simplify writing scripts that customize this file ... diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index 354df395e99..f5457ddca0a 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -334,8 +334,10 @@ Here is a BibTeX entry for Sage: } Adjust version/year as needed. You might also like to use DOI for Sage, -as the note entry in the above record, or directly as DOI record. - +as the ``note`` entry in the above record, or directly as DOI record. +The DOI :doi:`10.5281/zenodo.8042260` represents all versions of Sage; +clicking on it will show the DOI for the latest Sage version +(at the time of writing, 10.3, see :doi:`10.5281/zenodo.10841614`). If you happen to use the Sage interface to PARI, GAP or Singular, you should definitely reference them as well. Likewise, if you use @@ -403,4 +405,4 @@ What are DOI records for Sage? e.g. see `record for Sage 9.5 `_. The corresponding :doi:`10.5281/zenodo.6259615`. -There is also DOI for the latest version, :doi:`10.5281/zenodo.593563`. +There is also DOI for the latest version, :doi:`10.5281/zenodo.8042260`. diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index 583b331ea28..fbe7626b4e5 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -13,6 +13,7 @@ Curves sage/schemes/curves/constructor sage/schemes/curves/curve sage/schemes/curves/affine_curve + sage/schemes/curves/plane_curve_arrangement sage/schemes/curves/projective_curve sage/schemes/curves/point sage/schemes/curves/closed_point diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 7c0fb57bb1e..9dde6700341 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -12,6 +12,7 @@ Hyperplane arrangements :maxdepth: 1 sage/geometry/hyperplane_arrangement/arrangement + sage/geometry/hyperplane_arrangement/ordered_arrangement sage/geometry/hyperplane_arrangement/library sage/geometry/hyperplane_arrangement/hyperplane sage/geometry/hyperplane_arrangement/affine_subspace @@ -79,6 +80,7 @@ Toric geometry sage/geometry/toric_lattice sage/geometry/cone sage/geometry/cone_catalog + sage/geometry/cone_critical_angles sage/geometry/fan sage/geometry/fan_morphism sage/geometry/point_collection diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index eb7dab7193c..8b2fe6f250b 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -8,7 +8,6 @@ Game Theory sage/game_theory/matching_game sage/game_theory/normal_form_game sage/game_theory/catalog_normal_form_games - sage/game_theory/gambit_docs sage/game_theory/parser .. include:: ../footer.txt diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index 8188233a52b..e1f2adabb5e 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -19,6 +19,5 @@ computing homology groups. sage/homology/algebraic_topological_model sage/homology/homology_morphism sage/homology/matrix_utils - sage/interfaces/chomp .. include:: ../footer.txt diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 0b660cd9732..bdc30927a55 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -219,7 +219,6 @@ Distribution :maxdepth: 1 sage/misc/package - sage/misc/dist Credits diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f69da256651..44c22814471 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4217,6 +4217,10 @@ REFERENCES: .. [Lei2013] Tom Leinster, *The magnitude of metric spaces*. Doc. Math. 18 (2013), 857-905. +.. [Ler2022] Antonin Leroux: *Quaternion algebras and isogeny-based cryptography*, + PhD Thesis, 2022. + https://www.lix.polytechnique.fr/Labo/Antonin.LEROUX/manuscrit_these.pdf + .. [Lev2014] Lionel Levine. *Threshold state and a conjecture of Poghosyan, Poghosyan, Priezzhev and Ruelle*, Communications in Mathematical Physics. @@ -4999,6 +5003,11 @@ REFERENCES: .. [Nie] Johan S. R. Nielsen, Codinglib, https://bitbucket.org/jsrn/codinglib/. +.. [Niez1998] Marek Niezgoda. + Group majorization and Schur type inequalities. + Linear Algebra and its Applications, 268(1):9-30, 1998. + :doi:`10.1016/S0024-3795(97)89322-6`. + .. [NW1978] \A. Nijenhuis and H. Wilf, Combinatorial Algorithms, Academic Press (1978). @@ -5111,6 +5120,13 @@ REFERENCES: Electronic Journal of Linear Algebra, 34:444-458, 2018, :doi:`10.13001/1081-3810.3782`. +.. [Or2020] \M. Orlitzky. When a maximal angle among cones is nonobtuse. + Computational and Applied Mathematics 39(2), 2020. + :doi:`10.1007/s40314-020-1115-y`. + +.. [Or2024] \M. Orlitzky. Continuity of the conic hull. + Journal of Convex Analysis 31(1):255-264, 2024. + .. [ORS2013] Peter Ozsvath, Jacob Rasmussen, Zoltan Szabo, *Odd Khovanov homology*, Algebraic & Geometric Topology 13 (2013) 1465–1488, @@ -6158,6 +6174,9 @@ REFERENCES: :doi: `10.1017/fms.2022.38`. :arxiv:`2106.11141`. +.. [Ryom2015] Steen Ryom-Hansen. *Projective modules for the symmetric group and + Young's seminormal form*. J. Algebra **439** (2015) pp. 515-541. + .. [SV1970] \H. Schneider and M. Vidyasagar. Cross-positive matrices. SIAM Journal on Numerical Analysis, 7:508-519, 1970. diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index afa83bfa754..0c6be456348 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -116,7 +116,6 @@ This base class provides a lot more methods than a general parent:: '_coerce_impl', '_default_category', '_gens', - '_ideal_class_', '_ideal_monoid', '_latex_names', '_list', @@ -127,8 +126,6 @@ This base class provides a lot more methods than a general parent:: '_zero_ideal', 'algebraic_closure', 'base_extend', - 'class_group', - 'content', 'derivation', 'derivation_module', 'divides', @@ -136,7 +133,6 @@ This base class provides a lot more methods than a general parent:: 'extension', 'fraction_field', 'frobenius_endomorphism', - 'gcd', 'gen', 'gens', 'ideal', @@ -144,7 +140,6 @@ This base class provides a lot more methods than a general parent:: 'integral_closure', 'is_commutative', 'is_field', - 'is_integral_domain', 'is_integrally_closed', 'is_noetherian', 'is_prime_field', @@ -858,7 +853,9 @@ The four axioms requested for coercions rational field is a homomorphism of euclidean domains:: sage: QQ.coerce_map_from(ZZ).category_for() - Join of Category of euclidean domains and Category of infinite sets + Join of Category of euclidean domains + and Category of noetherian rings + and Category of infinite sets and Category of metric spaces .. end of output diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 9a3cb8e8ee7..f5c17d619d9 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -118,6 +118,7 @@ implemented in Sage as well: sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 41ec9264ae6..e19487c0cbd 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -119,6 +119,7 @@ par ailleurs les catégories en tant que telles : sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst index 6aaf7aa2911..f13b48c782b 100644 --- a/src/doc/ja/tutorial/tour_coercion.rst +++ b/src/doc/ja/tutorial/tour_coercion.rst @@ -101,6 +101,7 @@ Sageのクラス階層と圏の階層構造にはそれなりに類似が見ら sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/pt/tutorial/tour_coercion.rst b/src/doc/pt/tutorial/tour_coercion.rst index b5eeaa85a9f..94efa8cec15 100644 --- a/src/doc/pt/tutorial/tour_coercion.rst +++ b/src/doc/pt/tutorial/tour_coercion.rst @@ -125,6 +125,7 @@ categorias matemáticas também são implementadas no Sage: sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/pyproject.toml b/src/pyproject.toml new file mode 100644 index 00000000000..f70304534d9 --- /dev/null +++ b/src/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + # 68.1.0 Promote pyproject.toml's [tool.setuptools] out of beta. + # 68.1.1 Fix editable install finder handling of nested packages + 'setuptools >= 68.1.1', + # version constraint for macOS Big Sur support (see https://github.com/sagemath/sage/issues/31050) + 'wheel >=0.36.2', + 'cypari2 >=2.1.1', + 'cysignals >=1.10.2', + # Exclude 3.0.3 because of https://github.com/cython/cython/issues/5748 + 'cython >=3.0, != 3.0.3, <4.0', + 'gmpy2 ~=2.1.b999', + 'memory_allocator', + 'numpy >=1.19', + 'pkgconfig', +] +build-backend = "setuptools.build_meta" + +[tool.conda-lock] +platforms = [ + 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64' +] diff --git a/src/pyproject.toml.m4 b/src/pyproject.toml.m4 deleted file mode 100644 index 26de30ea108..00000000000 --- a/src/pyproject.toml.m4 +++ /dev/null @@ -1,23 +0,0 @@ -[build-system] -# Minimum requirements for the build system to execute. -requires = [ - esyscmd(`sage-get-system-packages install-requires-toml \ - setuptools \ - wheel \ - cypari \ - cysignals \ - cython \ - gmpy2 \ - jinja2 \ - jupyter_core \ - numpy \ - pkgconfig \ - pplpy \ - memory_allocator \ - ')] -build-backend = "setuptools.build_meta" - -[tool.conda-lock] -platforms = [ - 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64' -] diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index f87d52c94ce..05a686f95d0 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -514,6 +514,7 @@ def __init__(self, Q, names, category=None): sage: Cl.category() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: TestSuite(Cl).run() @@ -1093,6 +1094,7 @@ def lift_module_morphism(self, m, names=None): sage: phi.category_for() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 7 -3 -7 0] @@ -1177,6 +1179,7 @@ def lift_isometry(self, m, names=None): sage: phi.category_for() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 1 2 5 0] diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index e84a6b03f24..9564869df42 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -376,6 +376,10 @@ def is_FreeAlgebra(x) -> bool: sage: from sage.algebras.free_algebra import is_FreeAlgebra sage: is_FreeAlgebra(5) + doctest:warning... + DeprecationWarning: the function is_FreeAlgebra is deprecated; + use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead + See https://github.com/sagemath/sage/issues/37896 for details. False sage: is_FreeAlgebra(ZZ) False @@ -387,6 +391,8 @@ def is_FreeAlgebra(x) -> bool: ....: degrees=list(range(1,11)))) True """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_FreeAlgebra is deprecated; use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead") return isinstance(x, (FreeAlgebra_generic, FreeAlgebra_letterplace)) @@ -742,7 +748,7 @@ def _coerce_map_from_(self, R): return True # free algebras in the same variable over any base that coerces in: - if is_FreeAlgebra(R): + if isinstance(R, (FreeAlgebra_generic, FreeAlgebra_letterplace)): if R.variable_names() == self.variable_names(): return self.base_ring().has_coerce_map_from(R.base_ring()) if isinstance(R, PBWBasisOfFreeAlgebra): diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index 94be310a0b1..df19a18e3e8 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -60,13 +60,16 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.algebras.free_algebra import is_FreeAlgebra +from sage.algebras.free_algebra import FreeAlgebra_generic from sage.algebras.free_algebra_quotient_element import FreeAlgebraQuotientElement from sage.categories.algebras import Algebras +from sage.misc.lazy_import import lazy_import from sage.modules.free_module import FreeModule from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent +lazy_import('sage.algebras.letterplace.free_algebra_letterplace', 'FreeAlgebra_letterplace') + class FreeAlgebraQuotient(UniqueRepresentation, Parent): @staticmethod @@ -153,7 +156,7 @@ def __init__(self, A, mons, mats, names): sage: TestSuite(H2).run() """ - if not is_FreeAlgebra(A): + if not isinstance(A, (FreeAlgebra_generic, FreeAlgebra_letterplace)): raise TypeError("argument A must be a free algebra") R = A.base_ring() n = A.ngens() diff --git a/src/sage/algebras/lie_algebras/quotient.py b/src/sage/algebras/lie_algebras/quotient.py index e744cfbd252..db9831cbdd4 100644 --- a/src/sage/algebras/lie_algebras/quotient.py +++ b/src/sage/algebras/lie_algebras/quotient.py @@ -218,8 +218,10 @@ def __classcall_private__(cls, I, ambient=None, names=None, index_set = [i for i in sorted_indices if i not in I_supp] if names is None: - amb_names = dict(zip(sorted_indices, ambient.variable_names())) - names = [amb_names[i] for i in index_set] + if ambient._names is not None: + # ambient has assigned variable names, so use those + amb_names = dict(zip(sorted_indices, ambient.variable_names())) + names = [amb_names[i] for i in index_set] elif isinstance(names, str): if len(index_set) == 1: names = [names] @@ -252,7 +254,7 @@ def __init__(self, I, L, names, index_set, category=None): B = L.basis() sm = L.module().submodule_with_basis([I.reduce(B[i]).to_vector() for i in index_set]) - SB = sm.basis() + SB = [L.from_vector(b) for b in sm.basis()] # compute and normalize structural coefficients for the quotient s_coeff = {} @@ -260,7 +262,7 @@ def __init__(self, I, L, names, index_set, category=None): for j in range(i + 1, len(index_set)): ind_j = index_set[j] - brkt = I.reduce(L.bracket(SB[i], SB[j])) + brkt = I.reduce(SB[i].bracket(SB[j])) brktvec = sm.coordinate_vector(brkt.to_vector()) s_coeff[(ind_i, ind_j)] = dict(zip(index_set, brktvec)) s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff( @@ -291,11 +293,26 @@ def _repr_(self): L: General linear Lie algebra of rank 2 over Rational Field I: Ideal ([0 0] [0 1]) + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.gens() + sage: I = L.ideal([a + 2*b, b + 3*c]) + sage: Q = L.quotient(I) + sage: Q + Lie algebra quotient L/I of dimension 1 over Rational Field where + L: An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field + I: Ideal ((1, 0, -6), (0, 1, 3)) """ + try: + ideal_repr = self._I._repr_short() + except AttributeError: + ideal_repr = repr(tuple(self._I.gens())) + return ("Lie algebra quotient L/I of dimension %s" " over %s where\nL: %s\nI: Ideal %s" % ( self.dimension(), self.base_ring(), - self.ambient(), self._I._repr_short())) + self.ambient(), ideal_repr)) def _repr_generator(self, i): r""" diff --git a/src/sage/algebras/lie_algebras/subalgebra.py b/src/sage/algebras/lie_algebras/subalgebra.py index aee197edbbb..b7b5e2f9866 100644 --- a/src/sage/algebras/lie_algebras/subalgebra.py +++ b/src/sage/algebras/lie_algebras/subalgebra.py @@ -888,71 +888,6 @@ def is_ideal(self, A): return True return super().is_ideal(A) - def reduce(self, X): - r""" - Reduce an element of the ambient Lie algebra modulo the - ideal ``self``. - - INPUT: - - - ``X`` -- an element of the ambient Lie algebra - - OUTPUT: - - An element `Y` of the ambient Lie algebra that is contained in a fixed - complementary submodule `V` to ``self`` such that `X = Y` mod ``self``. - - When the base ring of ``self`` is a field, the complementary submodule - `V` is spanned by the elements of the basis that are not the leading - supports of the basis of ``self``. - - EXAMPLES: - - An example reduction in a 6 dimensional Lie algebra:: - - sage: sc = {('a','b'): {'d': 1}, ('a','c'): {'e': 1}, - ....: ('b','c'): {'f': 1}} - sage: L. = LieAlgebra(QQ, sc) - sage: I = L.ideal(c) - sage: I.reduce(a + b + c + d + e + f) - a + b + d - - The reduction of an element is zero if and only if the - element belongs to the subalgebra:: - - sage: I.reduce(c + e) - 0 - sage: c + e in I - True - - Over non-fields, the complementary submodule may not be spanned by - a subset of the basis of the ambient Lie algebra:: - - sage: L. = LieAlgebra(ZZ, {('X','Y'): {'Z': 3}}) - sage: I = L.ideal(Y) - sage: I.basis() - Family (Y, 3*Z) - sage: I.reduce(3*Z) - 0 - sage: I.reduce(Y + 14*Z) - 2*Z - """ - R = self.base_ring() - for Y in self.basis(): - Y = self.lift(Y) - k, c = Y.leading_item(key=self._order) - - if R.is_field(): - X = X - X[k] / c * Y - else: - try: - q, _ = X[k].quo_rem(c) - X = X - q * Y - except AttributeError: - pass - - return X - class Element(LieSubalgebraElementWrapper): def adjoint_matrix(self, sparse=False): """ diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 71c0379f4f6..5d9ca10e9f0 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -307,10 +307,16 @@ def is_QuaternionAlgebra(A): EXAMPLES:: sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(QuaternionAlgebra(QQ,-1,-1)) + doctest:warning... + DeprecationWarning: the function is_QuaternionAlgebra is deprecated; + use 'isinstance(..., QuaternionAlgebra_abstract)' instead + See https://github.com/sagemath/sage/issues/37896 for details. True sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(ZZ) False """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_QuaternionAlgebra is deprecated; use 'isinstance(..., QuaternionAlgebra_abstract)' instead") return isinstance(A, QuaternionAlgebra_abstract) @@ -515,7 +521,7 @@ def is_integral_domain(self, proof=True) -> bool: def is_noetherian(self) -> bool: """ - Return ``True`` always, since any quaternion algebra is a noetherian + Return ``True`` always, since any quaternion algebra is a Noetherian ring (because it is a finitely generated module over a field). EXAMPLES:: @@ -3383,6 +3389,195 @@ def multiply_by_conjugate(self, J): R = self.quaternion_algebra() return R.ideal(basis, check=False) + def pushforward(self, J, side=None): + """ + Compute the ideal which is the pushforward of ``self`` through an ideal ``J``. + + Uses Lemma 2.1.7 of [Ler2022]_. Only works for integral ideals. + + INPUT: + + - ``J`` -- a fractional quaternion ideal with norm coprime to ``self`` and either + the same left order or right order as ``self`` + + - ``side`` -- string (optional, default ``None``) set to ``"left"`` or ``"right"`` to + perform pushforward of left or right ideals respectively. If ``None`` the side + is determined by the matching left or right orders + + OUTPUT: a fractional quaternion ideal + + EXAMPLES:: + + sage: B = QuaternionAlgebra(419) + sage: i,j,k = B.gens() + sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k]) + sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k]) + sage: I1.left_order() == I2.left_order() + True + sage: I1.pushforward(I2, side="left") + Fractional ideal (1/2 + 3/2*j + 5*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k) + + TESTS:: + + sage: B = QuaternionAlgebra(419) + sage: i,j,k = B.gens() + sage: O0 = B.maximal_order() + sage: O0.unit_ideal().pushforward(O0.unit_ideal()) + Traceback (most recent call last): + ... + ValueError: self and J have same left and right orders, side of pushforward must be specified + sage: O0.unit_ideal().pushforward(O0.unit_ideal(), "left") + Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k) + sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k]) + sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k]) + sage: I1.pushforward(I2) + Fractional ideal (1/2 + 3/2*j + 5*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k) + sage: I1.pushforward(I2, side="right") + Traceback (most recent call last): + ... + ValueError: self and J must have the same right orders + sage: I1.conjugate().pushforward(I2.conjugate()) + Fractional ideal (1/2 + 3/2*j + 10*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k) + sage: I1.conjugate().pushforward(I2.conjugate(), side="left") + Traceback (most recent call last): + ... + ValueError: self and J must have the same left orders + sage: I1.pushforward(I1, side="left") + Traceback (most recent call last): + ... + ValueError: self and J must have coprime norms + sage: I3 = B.ideal([1/2 + 13/2*j + 6*k, 1/2*i + 3*j + 13/2*k, 9*j, 9*k]) + sage: I3.pushforward(I3*(1/3), side="left") + Traceback (most recent call last): + ... + NotImplementedError: quaternion ideal pushforward not implemented for non-integral ideals + """ + if not isinstance(J, QuaternionFractionalIdeal_rational): + raise TypeError("can only pushforward through a quaternion ideal") + + if side == "left": + if self.left_order() != J.left_order(): + raise ValueError("self and J must have the same left orders") + if not self.is_integral() or not J.is_integral(): + raise NotImplementedError("quaternion ideal pushforward not implemented for non-integral ideals") + Jnorm = J.norm() + if gcd(self.norm(), Jnorm) != 1: + raise ValueError("self and J must have coprime norms") + return (1 / Jnorm) * (J.conjugate() * self.intersection(J)) + + if side == "right": + if self.right_order() != J.right_order(): + raise ValueError("self and J must have the same right orders") + return self.conjugate().pushforward(J.conjugate(), side="left").conjugate() + + if side is None: + same_left_order = bool(self.left_order() == J.left_order()) + same_right_order = bool(self.right_order() == J.right_order()) + if not same_left_order and not same_right_order: + raise ValueError("self and J must share a left or right order") + if same_left_order and same_right_order: + raise ValueError("self and J have same left and right orders, side of pushforward must be specified") + if same_left_order: + return self.pushforward(J, side="left") + return self.pushforward(J, side="right") + + raise ValueError('side must be "left", "right" or None') + + def pullback(self, J, side=None): + """ + Compute the ideal which is the pullback of ``self`` through an ideal ``J``. + + Uses Lemma 2.1.7 of [Ler2022]_. Only works for integral ideals. + + INPUT: + + - ``J`` -- a fractional quaternion ideal with norm coprime to ``self`` and either + left order equal to the right order of ``self``, or vice versa + + - ``side`` -- string (optional, default ``None``) set to ``"left"`` or ``"right"`` to + perform pullback of left or right ideals respectively. If ``None`` the side + is determined by the matching left and right orders + + OUTPUT: a fractional quaternion ideal + + EXAMPLES:: + + sage: B = QuaternionAlgebra(419) + sage: i,j,k = B.gens() + sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k]) + sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k]) + sage: I3 = I1.pushforward(I2, side="left") + sage: I3.left_order() == I2.right_order() + True + sage: I3.pullback(I2, side="left") == I1 + True + + TESTS:: + + sage: B = QuaternionAlgebra(419) + sage: i,j,k = B.gens() + sage: O0 = B.maximal_order() + sage: O0.unit_ideal().pullback(O0.unit_ideal()) + Traceback (most recent call last): + ... + ValueError: self and J have same left and right orders, side of pullback must be specified + sage: O0.unit_ideal().pullback(O0.unit_ideal(), "left") + Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k) + sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k]) + sage: I2 = B.ideal([1/2 + 15/2*j + 2*k, 1/6*i + 43/3*j + 5/2*k, 15*j, 5*k]) + sage: I2.pullback(I1) + Fractional ideal (1/2 + 5/2*j + 2*k, 1/2*i + 3*j + 5/2*k, 5*j, 5*k) + sage: I2.pullback(I1, side="right") + Traceback (most recent call last): + ... + ValueError: right order of self should be left order of J + sage: I2.conjugate().pullback(I1.conjugate(), side="right") + Fractional ideal (1/2 + 5/2*j + 3*k, 1/2*i + 3*j + 5/2*k, 5*j, 5*k) + sage: I2.conjugate().pullback(I1.conjugate(), side="left") + Traceback (most recent call last): + ... + ValueError: left order of self should be right order of J + sage: I1.pullback(I1.conjugate(), side="left") + Traceback (most recent call last): + ... + ValueError: self and J must have coprime norms + sage: I3 = B.ideal([1/2 + 13/2*j + 6*k, 1/2*i + 3*j + 13/2*k, 9*j, 9*k]) + sage: I3.pullback(I3.conjugate()*(1/3), side="left") + Traceback (most recent call last): + ... + NotImplementedError: quaternion ideal pullback not implemented for non-integral ideals + """ + if not isinstance(J, QuaternionFractionalIdeal_rational): + raise TypeError("can only pullback through a quaternion ideal") + + if side == "left": + if self.left_order() != J.right_order(): + raise ValueError("left order of self should be right order of J") + if not self.is_integral() or not J.is_integral(): + raise NotImplementedError("quaternion ideal pullback not implemented for non-integral ideals") + N = self.norm() + if gcd(N, J.norm()) != 1: + raise ValueError("self and J must have coprime norms") + return J*self + N*J.left_order() + + if side == "right": + if self.right_order() != J.left_order(): + raise ValueError("right order of self should be left order of J") + return self.conjugate().pullback(J.conjugate(), side="left").conjugate() + + if side is None: + is_side_left = bool(self.left_order() == J.right_order()) + is_side_right = bool(self.right_order() == J.left_order()) + if not is_side_left and not is_side_right: + raise ValueError("left order of self must equal right order of J, or vice versa") + if is_side_left and is_side_right: + raise ValueError("self and J have same left and right orders, side of pullback must be specified") + if is_side_left: + return self.pullback(J, side="left") + return self.pullback(J, side="right") + + raise ValueError('side must be "left", "right" or None') + def is_equivalent(self, J, B=10, certificate=False, side=None): r""" Checks whether ``self`` and ``J`` are equivalent as ideals. diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 3f9153cf962..f3f5447245f 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -3054,7 +3054,7 @@ def is_integral_domain(self, proof=True): def is_noetherian(self): """ - This algebra is noetherian if and only if it is finite. + This algebra is Noetherian if and only if it is finite. EXAMPLES:: diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index 109de21a5d9..d457c448dd1 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -215,8 +215,8 @@ def integral(f, *args, **kwds): symbolically:: sage: f(x) = 1/(sqrt(2*pi)) * e^(-x^2/2) - sage: P = plot(f, -4, 4, hue=0.8, thickness=2) - sage: P.show(ymin=0, ymax=0.4) + sage: P = plot(f, -4, 4, hue=0.8, thickness=2) # needs sage.plot + sage: P.show(ymin=0, ymax=0.4) # needs sage.plot sage: numerical_integral(f, -4, 4) # random output (0.99993665751633376, 1.1101527003413533e-14) sage: integrate(f, x) diff --git a/src/sage/calculus/interpolators.pyx b/src/sage/calculus/interpolators.pyx index 0e1f5fc7209..221b52369e2 100644 --- a/src/sage/calculus/interpolators.pyx +++ b/src/sage/calculus/interpolators.pyx @@ -50,13 +50,14 @@ def polygon_spline(pts): sage: ps = polygon_spline(pts) sage: fx = lambda x: ps.value(x).real sage: fy = lambda x: ps.value(x).imag - sage: show(parametric_plot((fx, fy), (0, 2*pi))) # needs sage.plot + sage: show(parametric_plot((fx, fy), (0, 2*pi))) # needs sage.plot sage.symbolic sage: m = Riemann_Map([lambda x: ps.value(real(x))], ....: [lambda x: ps.derivative(real(x))], 0) sage: show(m.plot_colored() + m.plot_spiderweb()) # needs sage.plot Polygon approximation of a circle:: + sage: # needs sage.symbolic sage: pts = [e^(I*t / 25) for t in range(25)] sage: ps = polygon_spline(pts) sage: ps.derivative(2) diff --git a/src/sage/calculus/riemann.pyx b/src/sage/calculus/riemann.pyx index a4f9545bffb..9527736872d 100644 --- a/src/sage/calculus/riemann.pyx +++ b/src/sage/calculus/riemann.pyx @@ -408,7 +408,7 @@ cdef class Riemann_Map: sage: m = Riemann_Map([f], [fprime], 0) sage: sz = m.get_szego(boundary=0) sage: points = m.get_szego(absolute_value=True) - sage: list_plot(points) + sage: list_plot(points) # needs sage.plot Graphics object consisting of 1 graphics primitive Extending the points by a spline:: @@ -416,7 +416,7 @@ cdef class Riemann_Map: sage: s = spline(points) sage: s(3*pi / 4) 0.0012158... - sage: plot(s,0,2*pi) # plot the kernel + sage: plot(s,0,2*pi) # plot the kernel # needs sage.plot Graphics object consisting of 1 graphics primitive The unit circle with a small hole:: @@ -483,7 +483,7 @@ cdef class Riemann_Map: sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t) sage: m = Riemann_Map([f], [fprime], 0) sage: points = m.get_theta_points() - sage: list_plot(points) + sage: list_plot(points) # needs sage.plot Graphics object consisting of 1 graphics primitive Extending the points by a spline:: @@ -740,12 +740,12 @@ cdef class Riemann_Map: Default plot:: - sage: m.plot_boundaries() + sage: m.plot_boundaries() # needs sage.plot Graphics object consisting of 1 graphics primitive Big blue collocation points:: - sage: m.plot_boundaries(plotjoined=False, rgbcolor=[0,0,1], thickness=6) + sage: m.plot_boundaries(plotjoined=False, rgbcolor=[0,0,1], thickness=6) # needs sage.plot Graphics object consisting of 1 graphics primitive """ from sage.plot.all import list_plot @@ -905,17 +905,18 @@ cdef class Riemann_Map: Default plot:: - sage: m.plot_spiderweb() + sage: m.plot_spiderweb() # needs sage.plot Graphics object consisting of 21 graphics primitives Simplified plot with many discrete points:: - sage: m.plot_spiderweb(spokes=4, circles=1, pts=400, linescale=0.95, plotjoined=False) + sage: m.plot_spiderweb(spokes=4, circles=1, pts=400, # needs sage.plot + ....: linescale=0.95, plotjoined=False) Graphics object consisting of 6 graphics primitives Plot with thick, red lines:: - sage: m.plot_spiderweb(rgbcolor=[1,0,0], thickness=3) + sage: m.plot_spiderweb(rgbcolor=[1,0,0], thickness=3) # needs sage.plot Graphics object consisting of 21 graphics primitives To generate the unit circle map, it's helpful to see what the @@ -924,7 +925,7 @@ cdef class Riemann_Map: sage: f(t) = e^(I*t) sage: fprime(t) = I*e^(I*t) sage: m = Riemann_Map([f], [fprime], 0, 1000) - sage: m.plot_spiderweb() + sage: m.plot_spiderweb() # needs sage.plot Graphics object consisting of 21 graphics primitives A multiply connected region with corners. We set ``min_mag`` higher @@ -934,8 +935,10 @@ cdef class Riemann_Map: sage: z1 = lambda t: ps.value(t); z1p = lambda t: ps.derivative(t) sage: z2(t) = -2+exp(-I*t); z2p(t) = -I*exp(-I*t) sage: z3(t) = 2+exp(-I*t); z3p(t) = -I*exp(-I*t) - sage: m = Riemann_Map([z1,z2,z3],[z1p,z2p,z3p],0,ncorners=4) # long time - sage: p = m.plot_spiderweb(withcolor=True,plot_points=500, thickness = 2.0, min_mag=0.1) # long time + sage: m = Riemann_Map([z1,z2,z3], [z1p,z2p,z3p], 0, # long time + ....: ncorners=4) + sage: p = m.plot_spiderweb(withcolor=True, plot_points=500, # long time, needs sage.plot + ....: thickness=2.0, min_mag=0.1) """ from sage.plot.complex_plot import ComplexPlot from sage.plot.all import list_plot, Graphics @@ -1028,17 +1031,17 @@ cdef class Riemann_Map: sage: f(t) = e^(I*t) - 0.5*e^(-I*t) sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t) sage: m = Riemann_Map([f], [fprime], 0) - sage: m.plot_colored() + sage: m.plot_colored() # needs sage.plot Graphics object consisting of 1 graphics primitive Plot zoomed in on a specific spot:: - sage: m.plot_colored(plot_range=[0,1,.25,.75]) + sage: m.plot_colored(plot_range=[0,1,.25,.75]) # needs sage.plot Graphics object consisting of 1 graphics primitive High resolution plot:: - sage: m.plot_colored(plot_points=1000) # long time (29s on sage.math, 2012) + sage: m.plot_colored(plot_points=1000) # long time (29s on sage.math, 2012), needs sage.plot Graphics object consisting of 1 graphics primitive To generate the unit circle map, it's helpful to see what the @@ -1047,7 +1050,7 @@ cdef class Riemann_Map: sage: f(t) = e^(I*t) sage: fprime(t) = I*e^(I*t) sage: m = Riemann_Map([f], [fprime], 0, 1000) - sage: m.plot_colored() + sage: m.plot_colored() # needs sage.plot Graphics object consisting of 1 graphics primitive """ from sage.plot.complex_plot import ComplexPlot @@ -1081,7 +1084,7 @@ cdef comp_pt(clist, loop=True): sage: f(t) = e^(I*t) - 0.5*e^(-I*t) sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t) sage: m = Riemann_Map([f], [fprime], 0) - sage: m.plot_spiderweb() + sage: m.plot_spiderweb() # needs sage.plot Graphics object consisting of 21 graphics primitives """ list2 = [(c.real, c.imag) for c in clist] diff --git a/src/sage/calculus/transforms/dft.py b/src/sage/calculus/transforms/dft.py index 1b165fa6503..cd0c76bfc07 100644 --- a/src/sage/calculus/transforms/dft.py +++ b/src/sage/calculus/transforms/dft.py @@ -294,13 +294,15 @@ def dft(self, chi=None): Indexed sequence: [6, 0, 0, 0, 0, 0] indexed by [0, 1, 2, 3, 4, 5] - sage: # needs sage.groups + sage: # needs sage.combinat sage.groups sage: G = SymmetricGroup(3) sage: J = G.conjugacy_classes_representatives() sage: s = IndexedSequence([1,2,3], J) # 1,2,3 are the values of a class fcn on G sage: s.dft() # the "scalar-valued Fourier transform" of this class fcn Indexed sequence: [8, 2, 2] indexed by [(), (1,2), (1,2,3)] + + sage: # needs sage.rings.number_field sage: J = AbelianGroup(2, [2,3], names='ab') sage: s = IndexedSequence([1,2,3,4,5,6], J) sage: s.dft() # the precision of output is somewhat random and architecture dependent. @@ -311,6 +313,8 @@ def dft(self, chi=None): -0.00000000000000976996261670137 - 0.0000000000000159872115546022*I, -0.00000000000000621724893790087 - 0.0000000000000106581410364015*I] indexed by Multiplicative Abelian group isomorphic to C2 x C3 + + sage: # needs sage.groups sage.rings.number_field sage: J = CyclicPermutationGroup(6) sage: s = IndexedSequence([1,2,3,4,5,6], J) sage: s.dft() # the precision of output is somewhat random and architecture dependent. diff --git a/src/sage/calculus/transforms/fft.pyx b/src/sage/calculus/transforms/fft.pyx index 363fa65a836..ca7fd73cb64 100644 --- a/src/sage/calculus/transforms/fft.pyx +++ b/src/sage/calculus/transforms/fft.pyx @@ -243,7 +243,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): EXAMPLES:: sage: a = FastFourierTransform(4) - sage: a._plot_polar(0,2) # needs sage.plot + sage: a._plot_polar(0,2) # needs sage.plot sage.symbolic Graphics object consisting of 2 graphics primitives """ @@ -304,9 +304,9 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): - ``style`` -- Style of the plot, options are ``"rect"`` or ``"polar"`` - - ``rect`` -- height represents real part, color represents + - ``"rect"`` -- height represents real part, color represents imaginary part. - - ``polar`` -- height represents absolute value, color + - ``"polar"`` -- height represents absolute value, color represents argument. - ``xmin`` -- The lower bound of the slice to plot. 0 by default. @@ -319,13 +319,14 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): EXAMPLES:: + sage: # needs sage.plot sage: a = FastFourierTransform(16) sage: for i in range(16): a[i] = (random(),random()) - sage: A = plot(a) # needs sage.plot - sage: B = plot(a, style='polar') # needs sage.plot - sage: type(A) # needs sage.plot + sage: A = plot(a) + sage: B = plot(a, style='polar') # needs sage.symbolic + sage: type(A) - sage: type(B) # needs sage.plot + sage: type(B) # needs sage.symbolic sage: a = FastFourierTransform(125) sage: b = FastFourierTransform(125) @@ -333,7 +334,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): sage: for i in range(1, 60): b[i]=1 sage: a.forward_transform() sage: a.inverse_transform() - sage: a.plot() + b.plot() # needs sage.plot + sage: a.plot() + b.plot() Graphics object consisting of 250 graphics primitives """ diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index 4e92f890cd0..f45f42c52dc 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -77,6 +77,7 @@ def _make_named_class_key(self, name): and Category of metric spaces, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) @@ -85,6 +86,7 @@ def _make_named_class_key(self, name): (Category of fields, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index b71331c5ab5..ce316e50138 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1228,7 +1228,7 @@ def structure(self): sage: structure(Rings()) (Category of unital magmas, Category of additive unital additive magmas) sage: structure(Fields()) - (Category of euclidean domains,) + (Category of euclidean domains, Category of noetherian rings) sage: structure(Algebras(QQ)) (Category of unital magmas, Category of right modules over Rational Field, @@ -2840,6 +2840,7 @@ def _make_named_class_key(self, name): sage: Algebras(ZZ)._make_named_class_key("parent_class") Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces @@ -2852,6 +2853,7 @@ def _make_named_class_key(self, name): and Category of metric spaces, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) @@ -2979,6 +2981,7 @@ def _make_named_class_key(self, name): sage: Modules(ZZ)._make_named_class_key('element_class') Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index d80f5aa7bac..46736d03620 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -226,7 +226,8 @@ def _make_named_class_key(self, name): sage: Modules(ZZ)._make_named_class_key('element_class') Join of Category of Dedekind domains - and Category of euclidean domains + and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index 4e63eb8b594..38ae0d22ada 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -65,6 +65,39 @@ def is_commutative(self) -> bool: """ return True + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + commutative ring. + + This class can depend on `n`, the number of generators of the ideal. + The default input of `n=0` indicates an unspecified number of generators, + in which case a class that works for any number of generators is returned. + + EXAMPLES:: + + sage: ZZ._ideal_class_() + + sage: RR._ideal_class_() + + sage: R. = GF(5)[] + sage: R._ideal_class_(1) + + sage: S = R.quo(x^3 - y^2) + sage: S._ideal_class_(1) + + sage: S._ideal_class_(2) + + sage: T. = S[] # needs sage.libs.singular + sage: T._ideal_class_(5) # needs sage.libs.singular + + sage: T._ideal_class_(1) # needs sage.libs.singular + + """ + # One might need more than just n + from sage.rings.ideal import Ideal_generic, Ideal_principal + return Ideal_principal if n == 1 else Ideal_generic + def _test_divides(self, **options): r""" Run generic tests on the method :meth:`divides`. @@ -248,6 +281,18 @@ class Finite(CategoryWithAxiom): ....: GF(5)]) in Rings().Commutative().Finite() True """ + def extra_super_categories(self): + r""" + Let Sage know that finite commutative rings are Noetherian. + + EXAMPLES:: + + sage: CommutativeRings().Finite().extra_super_categories() + [Category of noetherian rings] + """ + from sage.categories.noetherian_rings import NoetherianRings + return [NoetherianRings()] + class ParentMethods: def cyclotomic_cosets(self, q, cosets=None): r""" @@ -367,7 +412,7 @@ def cyclotomic_cosets(self, q, cosets=None): try: ~q except ZeroDivisionError: - raise ValueError("%s is not invertible in %s" % (q,self)) + raise ValueError("%s is not invertible in %s" % (q, self)) if cosets is None: rest = set(self) @@ -378,7 +423,7 @@ def cyclotomic_cosets(self, q, cosets=None): while rest: x0 = rest.pop() o = [x0] - x = q*x0 + x = q * x0 while x != x0: o.append(x) rest.discard(x) diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 08150e61360..513ef129c8a 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -990,8 +990,12 @@ def map(self, f, name=None, *, is_injective=True): ....: '_test_enumerated_set_contains', ....: '_test_some_elements']) """ - from sage.combinat.combinat import MapCombinatorialClass - return MapCombinatorialClass(self, f, name, is_injective=is_injective) + from sage.sets.image_set import ImageSubobject + + image = ImageSubobject(f, self, is_injective=is_injective) + if name: + image.rename(name) + return image # # Consistency test suite for an enumerated set: diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py index e8db64d11a9..452cf12aa1e 100644 --- a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py @@ -95,6 +95,10 @@ def __init__(self, R, n=None, M=None, ambient=None): self._ambient = ambient Parent.__init__(self, base=R, category=cat) + from sage.categories.lie_algebras import LiftMorphism + self._lift_uea = LiftMorphism(self, self._construct_UEA()) + self._lift_uea.register_as_coercion() + def _repr_(self): """ EXAMPLES:: @@ -135,6 +139,57 @@ def _element_constructor_(self, x): x = x.value return self.element_class(self, self._M(x)) + def lift(self, x): + r""" + Return the lift of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.gens() + sage: L.lift(a) + b0 + sage: L.lift(b).parent() is L.universal_enveloping_algebra() + True + + sage: I = L.ideal([a + 2*b, b + 3*c]) + sage: I.lift(I.basis()[0]) + (1, 0, -6) + """ + # FIXME: This method can likely be simplified or removed once we + # disentangle the UEA lift from the generic lift + A = self._ambient + if A is self: + return self._lift_uea(x) + return A.element_class(A, A._M(x.value)) + + def universal_enveloping_algebra(self): + r""" + Return the universal enveloping algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.universal_enveloping_algebra() + Noncommutative Multivariate Polynomial Ring in b0, b1, b2 + over Rational Field, nc-relations: {} + """ + # FIXME: This method can likely be removed once we + # disentangle the UEA lift from the generic lift + return self._lift_uea.codomain() + + def _order(self, x): + r""" + Return a key for sorting for the index ``x``. + + TESTS:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L._order(2) + 2 + """ + return x + @cached_method def zero(self): """ @@ -194,6 +249,8 @@ def subalgebra(self, gens): [ 1 0 -1/2] [ 0 1 1] """ + if isinstance(gens, AbelianLieAlgebra): + gens = [self(g) for g in gens.gens()] N = self._M.subspace([g.value for g in gens]) return AbelianLieAlgebra(self.base_ring(), M=N, ambient=self._ambient) @@ -252,7 +309,7 @@ def gens(self) -> tuple: sage: L.gens() ((1, 0, 0), (0, 1, 0), (0, 0, 1)) """ - return tuple(self._M.basis()) + return tuple([self.element_class(self, b) for b in self._M.basis()]) def module(self): """ @@ -302,7 +359,36 @@ def from_vector(self, v, order=None): """ return self.element_class(self, self._M(v)) + def leading_monomials(self): + r""" + Return the set of leading monomials of the basis of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: I = L.ideal([2*a + b, b + c]) + sage: I.leading_monomials() + ((1, 0, 0), (0, 1, 0)) + """ + # for free modules, the leading monomial is actually the trailing monomial + return tuple([self._ambient._M(b.value).trailing_monomial() + for b in self.basis()]) + class Element(BaseExample.Element): + def __init__(self, parent, value): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: TestSuite(a).run() + """ + value.set_immutable() + super().__init__(parent, value) + def __iter__(self): """ Iterate over ``self`` by returning pairs ``(i, c)`` where ``i`` diff --git a/src/sage/categories/examples/lie_algebras.py b/src/sage/categories/examples/lie_algebras.py index 135c122efa2..15ace1987db 100644 --- a/src/sage/categories/examples/lie_algebras.py +++ b/src/sage/categories/examples/lie_algebras.py @@ -198,6 +198,20 @@ def __ne__(self, rhs): """ return not self.__eq__(rhs) + def __hash__(self): + r""" + Return the hash of ``self``. + + EXAMPLES:: + + sage: # needs sage.combinat sage.groups + sage: L = LieAlgebras(QQ).example() + sage: x, y = L.lie_algebra_generators() + sage: hash(x) == hash(x.value) + True + """ + return hash(self.value) + def __bool__(self) -> bool: """ Check non-zero. diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 7407632a93f..e0b03fa0901 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -18,6 +18,7 @@ from sage.categories.category_singleton import Category_contains_method_by_parent_class from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.division_rings import DivisionRings +from sage.categories.noetherian_rings import NoetherianRings from sage.structure.element import coerce_binop @@ -33,7 +34,9 @@ class Fields(CategoryWithAxiom): sage: K Category of fields sage: Fields().super_categories() - [Category of euclidean domains, Category of division rings] + [Category of euclidean domains, + Category of division rings, + Category of noetherian rings] sage: K(IntegerRing()) Rational Field @@ -54,10 +57,9 @@ def extra_super_categories(self): EXAMPLES:: sage: Fields().extra_super_categories() - [Category of euclidean domains] - + [Category of euclidean domains, Category of noetherian rings] """ - return [EuclideanDomains()] + return [EuclideanDomains(), NoetherianRings()] def __contains__(self, x): """ @@ -165,7 +167,9 @@ def _call_(self, x): sage: K Category of fields sage: Fields().super_categories() - [Category of euclidean domains, Category of division rings] + [Category of euclidean domains, + Category of division rings, + Category of noetherian rings] sage: K(IntegerRing()) # indirect doctest Rational Field diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 4c8e00b9fc2..4477401a909 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -123,12 +123,21 @@ def _construct_UEA(self): Universal enveloping algebra of The 6-Witt Lie algebra over Ring of integers modulo 6 in the Poincare-Birkhoff-Witt basis + + Corner case for the trivial (0-dimensional) Lie algebra:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: I = L.product_space(L) + sage: I._construct_UEA() + Free Algebra on 0 generators () over Rational Field """ from sage.algebras.free_algebra import FreeAlgebra # Create the UEA relations # We need to get names for the basis elements, not just the generators I = self._basis_ordering + if not I: # trivial Lie algebra + return FreeAlgebra(self.base_ring(), []) try: names = [str(x) for x in I] @@ -522,6 +531,7 @@ def centralizer(self, S): """ return self.subalgebra(self.centralizer_basis(S)) + @cached_method def center(self): """ Return the center of ``self``. @@ -1065,14 +1075,19 @@ def derived_series(self): sage: # needs sage.combinat sage.modules sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) - sage: L.derived_series() # not implemented + sage: L.derived_series() (Lie algebra on 2 generators (x, y) over Rational Field, - Subalgebra generated of - Lie algebra on 2 generators (x, y) over Rational Field - with basis: (x,), - Subalgebra generated of - Lie algebra on 2 generators (x, y) over Rational Field - with basis: ()) + Ideal (x) of Lie algebra on 2 generators (x, y) over Rational Field, + Ideal () of Lie algebra on 2 generators (x, y) over Rational Field) + + sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1}, + ....: ('b','d'): {'b':1}, ('b','e'): {'a':1}, + ....: ('d','e'): {'c':1}} + sage: L. = LieAlgebra(QQ, scoeffs) + sage: L.derived_series() + (Lie algebra on 5 generators (a, b, c, d, e) over Rational Field, + Ideal (a, b, c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field, + Ideal () of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field) """ L = [self] while L[-1].dimension() > 0: @@ -1135,11 +1150,18 @@ def lower_central_series(self, submodule=False): sage: # needs sage.combinat sage.modules sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) - sage: L.lower_central_series() # not implemented + sage: L.lower_central_series() (Lie algebra on 2 generators (x, y) over Rational Field, - Subalgebra generated of - Lie algebra on 2 generators (x, y) over Rational Field - with basis: (x,)) + Ideal (x) of Lie algebra on 2 generators (x, y) over Rational Field) + + sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1}, + ....: ('b','d'): {'b':1}, ('b','e'): {'a':1}, + ....: ('d','e'): {'c':1}} + sage: L. = LieAlgebra(QQ, scoeffs) + sage: L.lower_central_series() + (Lie algebra on 5 generators (a, b, c, d, e) over Rational Field, + Ideal (a, b, c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field, + Ideal (a, b) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field) """ if submodule: L = [self.module()] @@ -1152,6 +1174,82 @@ def lower_central_series(self, submodule=False): L.append(s) return tuple(L) + @cached_method + def upper_central_series(self): + r""" + Return the upper central series `(Z_i(\mathfrak{g}))_i` + of ``self`` where the rightmost + `Z_k(\mathfrak{g}) = Z_{k+1}(\mathfrak{g}) = \cdots`. + + The *upper central series* of a Lie algebra `\mathfrak{g}` is + defined recursively by `Z_0(\mathfrak{g}) := Z(\mathfrak{g})` and + + .. MATH:: + + Z_{k+1}(\mathfrak{g}) / Z_k(\mathfrak{g}) + = Z(\mathfrak{g} / Z_k(\mathfrak{g}), + + and recall that `Z(\mathfrak{g})` is the :meth:`center` + of `\mathfrak{g}`. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.upper_central_series() + [An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field] + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: L.upper_central_series() + [Subalgebra generated by () of Lie algebra on 2 generators (x, y) over Rational Field] + + sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1}, + ....: ('b','d'): {'b':1}, ('b','e'): {'a':1}, + ....: ('d','e'): {'c':1}} + sage: L. = LieAlgebra(QQ, scoeffs) + sage: L.upper_central_series() + [Subalgebra generated by (c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field] + + sage: L = lie_algebras.Heisenberg(QQ, 3) + sage: L.upper_central_series() + [Subalgebra generated by (z) of Heisenberg algebra of rank 3 over Rational Field, + Heisenberg algebra of rank 3 over Rational Field] + """ + I = self.center() + if I.dimension() == 0: + return [I] + ret = [I] + dim = self.dimension() + while True: + Q = self.quotient(I) + Z = Q.center() + if not Z.dimension(): # we did not add anything + return ret + new_gens = [Q.lift(b.value) for b in Z.basis()] + I = self.ideal(list(I.basis()) + new_gens) + if I.dimension() == dim: + ret.append(self) + return ret + ret.append(I) + + def hypercenter(self): + r""" + Return the hypercenter of ``self``. + + EXAMPLES:: + + sage: SGA3 = SymmetricGroup(3).algebra(QQ) + sage: L = LieAlgebra(associative=SGA3) + sage: L.hypercenter() + Subalgebra generated by ((), (1,2,3) + (1,3,2), (2,3) + (1,2) + (1,3)) + of Lie algebra of Symmetric group algebra of order 3 over Rational Field + + sage: L = lie_algebras.Heisenberg(QQ, 3) + sage: L.hypercenter() + Heisenberg algebra of rank 3 over Rational Field + """ + return self.upper_central_series()[-1] + def is_abelian(self): """ Return if ``self`` is an abelian Lie algebra. @@ -2254,3 +2352,71 @@ def basis_matrix(self): [ 1 0 -1/2] [ 0 1 1] """ + + def reduce(self, X): + r""" + Reduce an element of the ambient Lie algebra modulo the + ideal ``self``. + + INPUT: + + - ``X`` -- an element of the ambient Lie algebra + + OUTPUT: + + An element `Y` of the ambient Lie algebra that is contained + in a fixed complementary submodule `V` to ``self`` such that + `X = Y` mod ``self``. + + When the base ring of ``self`` is a field, the complementary + submodule `V` is spanned by the elements of the basis that + are not the leading supports of the basis of ``self``. + + EXAMPLES: + + An example reduction in a 6 dimensional Lie algebra:: + + sage: sc = {('a','b'): {'d': 1}, ('a','c'): {'e': 1}, + ....: ('b','c'): {'f': 1}} + sage: L. = LieAlgebra(QQ, sc) + sage: I = L.ideal(c) + sage: I.reduce(a + b + c + d + e + f) + a + b + d + + The reduction of an element is zero if and only if the + element belongs to the subalgebra:: + + sage: I.reduce(c + e) + 0 + sage: c + e in I + True + + Over non-fields, the complementary submodule may not be spanned + by a subset of the basis of the ambient Lie algebra:: + + sage: L. = LieAlgebra(ZZ, {('X','Y'): {'Z': 3}}) + sage: I = L.ideal(Y) + sage: I.basis() + Family (Y, 3*Z) + sage: I.reduce(3*Z) + 0 + sage: I.reduce(Y + 14*Z) + 2*Z + """ + R = self.base_ring() + from sage.categories.fields import Fields + is_field = R in Fields() + for Y in self.basis(): + Y = self.lift(Y) + k, c = Y.leading_item(key=self._order) + + if is_field: + X -= (X[k] / c) * Y + else: + try: + q, _ = X[k].quo_rem(c) + X -= q * Y + except AttributeError: + break + + return X diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 5208522a11a..58916af2aaf 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -679,6 +679,96 @@ def matrix(self, base_ring=None, side="left"): m.set_immutable() return m + def _repr_matrix(self): + r""" + Return a string representation of this morphism (as a matrix). + + EXAMPLES:: + + sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]], + ....: column_keys=['a', 'b', 'c'], + ....: row_keys=['v', 'w']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'v', 'w'} over Integer Ring + sage: M._repr_ = M._repr_matrix + sage: M # indirect doctest + a b c + v[1 0 0] + w[0 1 0] + """ + matrix = self.matrix() + + from sage.matrix.constructor import options + + if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols(): + return matrix.str(top_border=self.domain().basis().keys(), + left_border=self.codomain().basis().keys()) + + return repr(matrix) + + def _ascii_art_matrix(self): + r""" + Return an ASCII art representation of this morphism (as a matrix). + + EXAMPLES:: + + sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]], + ....: column_keys=['a', 'b', 'c'], + ....: row_keys=['v', 'w']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'v', 'w'} over Integer Ring + sage: M._ascii_art_ = M._ascii_art_matrix + sage: ascii_art(M) # indirect doctest + a b c + v[1 0 0] + w[0 1 0] + """ + matrix = self.matrix() + + from sage.matrix.constructor import options + + if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols(): + return matrix.str(character_art=True, + top_border=self.domain().basis().keys(), + left_border=self.codomain().basis().keys()) + + from sage.typeset.ascii_art import AsciiArt + + return AsciiArt(repr(self).splitlines()) + + def _unicode_art_matrix(self): + r""" + Return a unicode art representation of this morphism (as a matrix). + + EXAMPLES:: + + sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]], + ....: column_keys=['a', 'b', 'c'], + ....: row_keys=['v', 'w']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'v', 'w'} over Integer Ring + sage: M._unicode_art_ = M._unicode_art_matrix + sage: unicode_art(M) # indirect doctest + a b c + v⎛1 0 0⎞ + w⎝0 1 0⎠ + """ + matrix = self.matrix() + + from sage.matrix.constructor import options + + if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols(): + return matrix.str(unicode=True, character_art=True, + top_border=self.domain().basis().keys(), + left_border=self.codomain().basis().keys()) + + from sage.typeset.unicode_art import UnicodeArt + + return UnicodeArt(repr(self).splitlines()) + def __invert__(self): """ Return the inverse morphism of ``self``. diff --git a/src/sage/categories/group_algebras.py b/src/sage/categories/group_algebras.py index c5cceb53633..6732a49b150 100644 --- a/src/sage/categories/group_algebras.py +++ b/src/sage/categories/group_algebras.py @@ -358,7 +358,7 @@ def is_integral_domain(self, proof=True): return ans # I haven't written is_noetherian(), because I don't know when group - # algebras are noetherian, and I haven't written is_prime_field(), because + # algebras are Noetherian, and I haven't written is_prime_field(), because # I don't know if that means "is canonically isomorphic to a prime field" # or "is identical to a prime field". diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index deb489a9042..29f4c7df69a 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -1245,7 +1245,7 @@ def reversed(self): the principal ideal domain Integer Ring to Ambient free module of rank 3 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over (Dedekind - domains and euclidean domains + domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) sage: type(H) @@ -1254,7 +1254,7 @@ def reversed(self): the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over (Dedekind - domains and euclidean domains + domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) sage: type(H.reversed()) diff --git a/src/sage/categories/integral_domains.py b/src/sage/categories/integral_domains.py index b6e6f59a196..d29b2b159f0 100644 --- a/src/sage/categories/integral_domains.py +++ b/src/sage/categories/integral_domains.py @@ -103,6 +103,8 @@ def is_integral_domain(self, proof=True): EXAMPLES:: + sage: ZZ.is_integral_domain() + True sage: QQ.is_integral_domain() True sage: Parent(QQ, category=IntegralDomains()).is_integral_domain() @@ -113,6 +115,9 @@ def is_integral_domain(self, proof=True): True sage: L.is_integral_domain(proof=True) # needs sage.combinat True + + sage: ZZ['x'].is_integral_domain() + True """ return True diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index c5b1aa2afb5..952ca09fbde 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -885,7 +885,8 @@ def __init_extra__(self): sage: M.category() # needs sage.modules Category of Cartesian products of modules with basis over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) + and noetherian rings + and infinite enumerated sets and metric spaces) sage: M.base_ring() # needs sage.modules Integer Ring diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index d4b412dd126..57e00943eda 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -179,9 +179,12 @@ cdef class Morphism(Map): sage: f.category() Category of endsets of unital magmas and right modules over (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + and left modules over + (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) - and left modules over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) sage: # needs sage.rings.number_field sage: K = CyclotomicField(12) diff --git a/src/sage/categories/noetherian_rings.py b/src/sage/categories/noetherian_rings.py new file mode 100644 index 00000000000..bdffb796243 --- /dev/null +++ b/src/sage/categories/noetherian_rings.py @@ -0,0 +1,86 @@ +r""" +Noetherian rings + +EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: GF(4, "a") in NoetherianRings() # needs sage.rings.finite_rings + True + sage: QQ in NoetherianRings() + True + sage: ZZ in NoetherianRings() + True + sage: IntegerModRing(4) in NoetherianRings() + True + sage: IntegerModRing(5) in NoetherianRings() + True +""" +# **************************************************************************** +# Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) +# 2012 Nicolas M. Thiery +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.categories.category import Category +from sage.categories.commutative_rings import CommutativeRings + + +class NoetherianRings(Category): + """ + The category of Noetherian rings + + A Noetherian ring is a commutative ring in which + every ideal is finitely generated. + + See :wikipedia:`Noetherian ring` + + EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: C = NoetherianRings(); C + Category of noetherian rings + sage: sorted(C.super_categories(), key=str) + [Category of commutative rings] + + TESTS:: + + sage: TestSuite(C).run() + """ + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: NoetherianRings().super_categories() + [Category of commutative rings] + """ + return [CommutativeRings()] + + class ParentMethods: + def is_noetherian(self, proof=True): + r""" + Return ``True``, since this in an object of the category + of Noetherian rings. + + EXAMPLES:: + + sage: ZZ.is_noetherian() + True + sage: QQ.is_noetherian() + True + sage: ZZ['x'].is_noetherian() + True + sage: R. = PolynomialRing(QQ) + sage: R.is_noetherian() + True + + sage: L. = LazyLaurentSeriesRing(QQ) # needs sage.combinat + sage: L.is_noetherian() # needs sage.combinat + True + """ + return True + + class ElementMethods: + pass diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index c7339b712e3..aa47130b205 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -352,18 +352,20 @@ sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.categories() [Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces, Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, Category of gcd domains, - Category of integral domains, Category of domains, + Category of integral domains, Category of domains, ... Category of commutative rings, Category of rings, ... Category of magmas and additive magmas, ... Category of monoids, Category of semigroups, diff --git a/src/sage/categories/principal_ideal_domains.py b/src/sage/categories/principal_ideal_domains.py index f020cfb383d..6118e06b1c7 100644 --- a/src/sage/categories/principal_ideal_domains.py +++ b/src/sage/categories/principal_ideal_domains.py @@ -1,16 +1,17 @@ r""" Principal ideal domains """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.category_singleton import Category_singleton from sage.categories.unique_factorization_domains import UniqueFactorizationDomains + class PrincipalIdealDomains(Category_singleton): """ The category of (constructive) principal ideal domains @@ -94,33 +95,175 @@ def _test_gcd_vs_xgcd(self, **options): # there are some strange things in Sage doctests... so it is better # to cut the list in order to avoid lists of size 531441. elts = elts[:10] - pairs = [(x,y) for x in elts for y in elts] + pairs = [(x, y) for x in elts for y in elts] try: - xgcds = [x.xgcd(y) for x,y in pairs] - except (AttributeError,NotImplementedError): + xgcds = [x.xgcd(y) for x, y in pairs] + except (AttributeError, NotImplementedError): return has_gcd = True try: - gcds = [x.gcd(y) for x,y in pairs] - except (AttributeError,NotImplementedError): + gcds = [x.gcd(y) for x, y in pairs] + except (AttributeError, NotImplementedError): has_gcd = False tester.assertTrue(has_gcd, "The ring {} provides a xgcd but no gcd".format(self)) - for (x,y),gcd,xgcd in zip(pairs,gcds,xgcds): + for (x, y), gcd, xgcd in zip(pairs, gcds, xgcds): tester.assertTrue(gcd.parent() == self, "The parent of the gcd is {} for element of {}".format( gcd.parent(), self)) tester.assertTrue(xgcd[0].parent() == self and - xgcd[1].parent() == self and xgcd[2].parent() == self, - "The parent of output in xgcd is different from " - "the parent of input for elements in {}".format(self)) + xgcd[1].parent() == self == xgcd[2].parent(), + "The parent of output in xgcd is different from " + "the parent of input for elements in {}".format(self)) tester.assertTrue(gcd == xgcd[0], "The methods gcd and xgcd disagree on {}:\n" " gcd({},{}) = {}\n" - " xgcd({},{}) = {}\n".format(self,x,y,gcd,x,y,xgcd)) + " xgcd({},{}) = {}\n".format(self, x, y, gcd, x, y, xgcd)) + + def is_noetherian(self) -> bool: + """ + Every principal ideal domain is Noetherian, so we return ``True``. + + EXAMPLES:: + + sage: Zp(5).is_noetherian() # needs sage.rings.padics + True + """ + return True + + def class_group(self): + """ + Return the trivial group, since the class group of a PID is trivial. + + EXAMPLES:: + + sage: QQ.class_group() # needs sage.groups + Trivial Abelian group + """ + from sage.groups.abelian_gps.abelian_group import AbelianGroup + return AbelianGroup([]) + + def gcd(self, x, y, coerce=True): + r""" + Return the greatest common divisor of ``x`` and ``y``, as elements + of ``self``. + + EXAMPLES: + + The integers are a principal ideal domain and hence a GCD domain:: + + sage: ZZ.gcd(42, 48) + 6 + sage: 42.factor(); 48.factor() + 2 * 3 * 7 + 2^4 * 3 + sage: ZZ.gcd(2^4*7^2*11, 2^3*11*13) + 88 + sage: 88.factor() + 2^3 * 11 + + In a field, any nonzero element is a GCD of any nonempty set + of nonzero elements. In previous versions, Sage used to return + 1 in the case of the rational field. However, since :issue:`10771`, + the rational field is considered as the + *fraction field* of the integer ring. For the fraction field + of an integral domain that provides both GCD and LCM, it is + possible to pick a GCD that is compatible with the GCD of the + base ring:: + + sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48))) + 6 + + sage: QQ.gcd(1/2, 1/3) + 1/6 + + Polynomial rings over fields are GCD domains as well. Here is a simple + example over the ring of polynomials over the rationals as well as + over an extension ring. Note that ``gcd`` requires x and y to be + coercible:: + + sage: # needs sage.rings.number_field + sage: R. = PolynomialRing(QQ) + sage: S. = NumberField(x^2 - 2, 'a') + sage: f = (x - a)*(x + a); g = (x - a)*(x^2 - 2) + sage: print(f); print(g) + x^2 - 2 + x^3 - a*x^2 - 2*x + 2*a + sage: f in R + True + sage: g in R + False + sage: R.gcd(f, g) + Traceback (most recent call last): + ... + TypeError: Unable to coerce 2*a to a rational + sage: R.base_extend(S).gcd(f,g) + x^2 - 2 + sage: R.base_extend(S).gcd(f, (x - a)*(x^2 - 3)) + x - a + """ + if coerce: + x = self(x) + y = self(y) + return x.gcd(y) + + def content(self, x, y, coerce=True): + r""" + Return the content of `x` and `y`. + + This is the unique element `c` of + ``self`` such that `x/c` and `y/c` + are coprime and integral. + + EXAMPLES:: + + sage: QQ.content(ZZ(42), ZZ(48)); type(QQ.content(ZZ(42), ZZ(48))) + 6 + + sage: QQ.content(1/2, 1/3) + 1/6 + sage: factor(1/2); factor(1/3); factor(1/6) + 2^-1 + 3^-1 + 2^-1 * 3^-1 + sage: a = (2*3)/(7*11); b = (13*17)/(19*23) + sage: factor(a); factor(b); factor(QQ.content(a,b)) + 2 * 3 * 7^-1 * 11^-1 + 13 * 17 * 19^-1 * 23^-1 + 7^-1 * 11^-1 * 19^-1 * 23^-1 + + Note the changes to the second entry:: + + sage: c = (2*3)/(7*11); d = (13*17)/(7*19*23) + sage: factor(c); factor(d); factor(QQ.content(c,d)) + 2 * 3 * 7^-1 * 11^-1 + 7^-1 * 13 * 17 * 19^-1 * 23^-1 + 7^-1 * 11^-1 * 19^-1 * 23^-1 + sage: e = (2*3)/(7*11); f = (13*17)/(7^3*19*23) + sage: factor(e); factor(f); factor(QQ.content(e,f)) + 2 * 3 * 7^-1 * 11^-1 + 7^-3 * 13 * 17 * 19^-1 * 23^-1 + 7^-3 * 11^-1 * 19^-1 * 23^-1 + """ + if coerce: + x = self(x) + y = self(y) + return x.content(y) + + def _ideal_class_(self, n=0): + """ + Ideals in PIDs have their own special class. + + EXAMPLES:: + + sage: ZZ._ideal_class_() + + """ + from sage.rings.ideal import Ideal_pid + return Ideal_pid class ElementMethods: pass diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index e8eee82998d..61f0f841720 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -35,7 +35,7 @@ lazy_import('sage.categories.commutative_rings', 'CommutativeRings') lazy_import('sage.categories.groups', 'Groups') lazy_import('sage.categories.objects', 'Objects') -lazy_import('sage.categories.rings', 'Rings', at_startup=True) +lazy_import('sage.categories.rings', 'Rings') # TODO, think through the rankings, and override pushout where necessary. diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 9685fb80dd4..02e81ff1f63 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -339,6 +339,101 @@ def is_commutative(self) -> bool: """ return False + def is_integral_domain(self, proof=True) -> bool: + """ + Return ``True`` if this ring is an integral domain. + + INPUT: + + - ``proof`` -- (default: ``True``) Determines what to do in unknown + cases + + ALGORITHM: + + If the parameter ``proof`` is set to ``True``, the returned value is + correct but the method might throw an error. Otherwise, if it is set + to ``False``, the method returns ``True`` if it can establish that ``self`` + is an integral domain and ``False`` otherwise. + + EXAMPLES:: + + sage: QQ.is_integral_domain() + True + sage: ZZ.is_integral_domain() + True + sage: ZZ['x,y,z'].is_integral_domain() + True + sage: Integers(8).is_integral_domain() + False + sage: Zp(7).is_integral_domain() # needs sage.rings.padics + True + sage: Qp(7).is_integral_domain() # needs sage.rings.padics + True + sage: R. = QQ[] + sage: S. = R.quo((b^3)) # needs sage.libs.singular + sage: S.is_integral_domain() # needs sage.libs.singular + False + sage: R = ZZ.quotient(ZZ.ideal(10)); R.is_integral_domain() + False + + This illustrates the use of the ``proof`` parameter:: + + sage: R. = ZZ[] + sage: S. = R.quo((b^3)) # needs sage.libs.singular + sage: S.is_integral_domain(proof=True) # needs sage.libs.singular + Traceback (most recent call last): + ... + NotImplementedError + sage: S.is_integral_domain(proof=False) # needs sage.libs.singular + False + + TESTS: + + Make sure :issue:`10481` is fixed:: + + sage: x = polygen(ZZ, 'x') + sage: R. = ZZ['x'].quo(x^2) # needs sage.libs.pari + sage: R.fraction_field() # needs sage.libs.pari + Traceback (most recent call last): + ... + TypeError: self must be an integral domain. + sage: R.is_integral_domain() # needs sage.libs.pari + False + + Forward the proof flag to ``is_field``, see :issue:`22910`:: + + sage: # needs sage.libs.singular + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2 + x + 1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2 + x + 1) + sage: F2.is_integral_domain(False) + False + """ + if self.is_field(proof): + return True + + if self.is_zero(): + return False + + if proof: + raise NotImplementedError + + return False + + def is_noetherian(self): + """ + Return ``True`` if this ring is Noetherian. + + EXAMPLES:: + + sage: QQ.is_noetherian() + True + sage: ZZ.is_noetherian() + True + """ + return False + def is_zero(self) -> bool: """ Return ``True`` if this is the zero ring. @@ -579,6 +674,26 @@ def ideal_monoid(self): from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + ring. + + EXAMPLES:: + + sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules + sage: MS._ideal_class_() # needs sage.modules + + + Since :issue:`7797`, non-commutative rings have ideals as well:: + + sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules + sage: A._ideal_class_() # needs sage.combinat sage.modules + + """ + from sage.rings.noncommutative_ideals import Ideal_nc + return Ideal_nc + def characteristic(self): """ Return the characteristic of this ring. @@ -734,62 +849,6 @@ def ideal(self, *args, **kwds): gens = gens[0] return C(self, gens, **kwds) - def _ideal_class_(self, n=0): - """ - Return the class that is used to implement ideals of this ring. - - .. NOTE:: - - We copy the code from :class:`~sage.rings.ring.Ring`. This is - necessary because not all rings inherit from that class, such - as matrix algebras. - - INPUT: - - - ``n`` (optional integer, default 0): The number of generators - of the ideal to be created. - - OUTPUT: - - The class that is used to implement ideals of this ring with - ``n`` generators. - - .. NOTE:: - - Often principal ideals (``n==1``) are implemented via - a different class. - - EXAMPLES:: - - sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules - sage: MS._ideal_class_() # needs sage.modules - - - We do not know of a commutative ring in Sage that does not inherit - from the base class of rings. So, we need to cheat in the next - example:: - - sage: super(Ring,QQ)._ideal_class_.__module__ - 'sage.categories.rings' - sage: super(Ring,QQ)._ideal_class_() - - sage: super(Ring,QQ)._ideal_class_(1) - - sage: super(Ring,QQ)._ideal_class_(2) - - """ - from sage.rings.noncommutative_ideals import Ideal_nc - try: - if not self.is_commutative(): - return Ideal_nc - except (NotImplementedError, AttributeError): - return Ideal_nc - from sage.rings.ideal import Ideal_generic, Ideal_principal - if n == 1: - return Ideal_principal - return Ideal_generic - - ## # Quotient rings def quotient(self, I, names=None, **kwds): """ diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 193d1f1c966..3cdff883922 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -2244,7 +2244,7 @@ def one(self): True sage: TestSuite(A).run() """ - return self([i for i in range(1,self.k+2)]) + return self(list(range(1, self.k + 2))) #------------------------ #Type-unique methods. diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index d1f391013e2..4ab35568bb4 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -56,8 +56,7 @@ from sage.misc.lazy_import import lazy_import -from .combinat import (CombinatorialClass, CombinatorialObject, - MapCombinatorialClass, +from .combinat import (CombinatorialObject, bell_number, bell_polynomial, bernoulli_polynomial, catalan_number, euler_number, fibonacci, fibonacci_sequence, fibonacci_xrange, @@ -66,12 +65,6 @@ polygonal_number, stirling_number1, stirling_number2, tuples, unordered_tuples) -lazy_import('sage.combinat.combinat', - ('InfiniteAbstractCombinatorialClass', 'UnionCombinatorialClass', - 'FilteredCombinatorialClass'), - deprecation=(31545, 'this class is deprecated, do not use')) - - from .expnums import expnums from sage.combinat.chas.all import * diff --git a/src/sage/combinat/cartesian_product.py b/src/sage/combinat/cartesian_product.py index c5a16d3f8ee..ef151e55ffa 100644 --- a/src/sage/combinat/cartesian_product.py +++ b/src/sage/combinat/cartesian_product.py @@ -112,6 +112,16 @@ def iterfunc(): category=category, cache=False) + def __hash__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.cartesian_product import CartesianProduct_iters + sage: cp = CartesianProduct_iters((1,2), (3,4)) + sage: hash(cp) == CartesianProduct_iters((1,2), (3,4)) + """ + return hash(tuple(self.iters)) + def __contains__(self, x): """ EXAMPLES:: diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 3fc3499ef67..37103b623f4 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -787,9 +787,8 @@ def qmu_save(self, filename=None): string.append('1') string.append('//Matrix') string.append(str(m) + ' ' + str(m)) - for i in range(m): - string.append(' '.join(str(M[i, j]) - for j in range(m))) + string.extend(' '.join(str(M[i, j]) for j in range(m)) + for i in range(m)) string.append('//Points') for i in range(m): diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 38348c41c1d..382c915db77 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -180,8 +180,6 @@ from sage.misc.lazy_import import lazy_import from sage.misc.lazy_attribute import lazy_attribute from .combinat_cython import _stirling_number2 -from sage.categories.enumerated_sets import EnumeratedSets -from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.element import Element @@ -1581,1011 +1579,6 @@ def __init__(self, parent, *args, **kwds): super().__init__(L) super(CombinatorialObject, self).__init__(parent) - -class CombinatorialClass(Parent, metaclass=ClasscallMetaclass): - """ - This class is deprecated, and will disappear as soon as all derived - classes in Sage's library will have been fixed. Please derive - directly from Parent and use the category :class:`EnumeratedSets`, - :class:`FiniteEnumeratedSets`, or :class:`InfiniteEnumeratedSets`, as - appropriate. - - For examples, see:: - - sage: FiniteEnumeratedSets().example() - An example of a finite enumerated set: {1,2,3} - sage: InfiniteEnumeratedSets().example() - An example of an infinite enumerated set: the non negative integers - """ - - def __init__(self, category=None): - """ - TESTS:: - - sage: C = sage.combinat.combinat.CombinatorialClass() - sage: C.category() - Category of enumerated sets - sage: C.__class__ - - sage: isinstance(C, Parent) - True - sage: C = sage.combinat.combinat.CombinatorialClass(category = FiniteEnumeratedSets()) - sage: C.category() - Category of finite enumerated sets - """ - Parent.__init__(self, category=EnumeratedSets().or_subcategory(category)) - - def is_finite(self) -> bool: - """ - Return whether ``self`` is finite or not. - - EXAMPLES:: - - sage: Partitions(5).is_finite() # needs sage.combinat - True - sage: Permutations().is_finite() - False - """ - return self.cardinality() != infinity - - def __getitem__(self, i): - """ - Return the combinatorial object of rank i. - - EXAMPLES:: - - sage: class C(CombinatorialClass): - ....: def __iter__(self): - ....: return iter([1,2,3]) - sage: c = C() - sage: c[0] - 1 - sage: c[2] - 3 - sage: c[4] - Traceback (most recent call last): - ... - ValueError: the value must be between 0 and 2 inclusive - """ - return self.unrank(i) - - def __str__(self) -> str: - """ - Return a string representation of self. - - EXAMPLES:: - - sage: str(Partitions(5)) # needs sage.combinat - 'Partitions of the integer 5' - """ - return repr(self) - - def _repr_(self) -> str: - """ - EXAMPLES:: - - sage: repr(Partitions(5)) # indirect doctest # needs sage.combinat - 'Partitions of the integer 5' - """ - if hasattr(self, '_name') and self._name: - return self._name - else: - return "Combinatorial Class -- REDEFINE ME!" - - def __contains__(self, x) -> bool: - """ - Test whether or not the combinatorial class contains the object x. - This raises a NotImplementedError as a default since _all_ - subclasses of CombinatorialClass should override this. - - Note that we could replace this with a default implementation that - just iterates through the elements of the combinatorial class and - checks for equality. However, since we use __contains__ for - type checking, this operation should be cheap and should be - implemented manually for each combinatorial class. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: x in C # needs sage.symbolic - Traceback (most recent call last): - ... - NotImplementedError - """ - raise NotImplementedError - - def __eq__(self, other): - """ - Compare two different combinatorial classes. - - For now, the comparison is done just on their repr's. - - EXAMPLES:: - - sage: # needs sage.combinat - sage: p5 = Partitions(5) - sage: p6 = Partitions(6) - sage: repr(p5) == repr(p6) - False - sage: p5 == p6 - False - """ - return repr(self) == repr(other) - - def __ne__(self, other): - """ - Test unequality of ``self`` and ``other``. - - EXAMPLES:: - - sage: p5 = Partitions(5) # needs sage.combinat - sage: p6 = Partitions(6) # needs sage.combinat - sage: p5 != p6 # needs sage.combinat - True - """ - return not (self == other) - - def __hash__(self): - """ - Create a hash value. This is based on the string representation. - - Note that in Python 3 objects that define __eq__ do not inherit their __hash__ - function. Without an explicit __hash__ they are no longer hashable. - - TESTS:: - - sage: C = CombinatorialClass() - sage: hash(C) == hash(repr(C)) - True - """ - return hash(repr(self)) - - def __cardinality_from_iterator(self) -> Integer | infinity: - """ - Default implementation of cardinality which just goes through the iterator - of the combinatorial class to count the number of objects. - - EXAMPLES:: - - sage: class C(CombinatorialClass): - ....: def __iter__(self): - ....: return iter([1,2,3]) - sage: C().cardinality() #indirect doctest - 3 - """ - c = Integer(0) - one = Integer(1) - for _ in self: - c += one - return c - - cardinality = __cardinality_from_iterator - - # __call__, element_class, and _element_constructor_ are poor - # man's versions of those from Parent. This is for transition, - # until all combinatorial classes are proper parents (in Parent) - # and use coercion, etcc - - def __call__(self, x): - """ - Return x as an element of the combinatorial class's object class. - - EXAMPLES:: - - sage: # needs sage.combinat - sage: p5 = Partitions(5) - sage: a = [2,2,1] - sage: type(a) - - sage: a = p5(a) - sage: type(a) - - sage: p5([2,1]) - Traceback (most recent call last): - ... - ValueError: [2, 1] is not an element of Partitions of the integer 5 - """ - if x in self: - return self._element_constructor_(x) - else: - raise ValueError("%s not in %s" % (x, self)) - - Element = CombinatorialObject # mostly for backward compatibility - - @lazy_attribute - def element_class(self): - """ - This function is a temporary helper so that a CombinatorialClass - behaves as a parent for creating elements. This will disappear when - combinatorial classes will be turned into actual parents (in the - category EnumeratedSets). - - TESTS:: - - sage: P5 = Partitions(5) # needs sage.combinat - sage: P5.element_class # needs sage.combinat - - """ - # assert not isinstance(self, Parent) # Raises an alert if we override the proper definition from Parent - return self.Element - - def _element_constructor_(self, x): - """ - This function is a temporary helper so that a CombinatorialClass - behaves as a parent for creating elements. This will disappear when - combinatorial classes will be turned into actual parents (in the - category EnumeratedSets). - - TESTS:: - - sage: P5 = Partitions(5) # needs sage.combinat - sage: p = P5([3,2]) # indirect doctest # needs sage.combinat - sage: type(p) # needs sage.combinat - - """ - # assert not isinstance(self, Parent) # Raises an alert if we override the proper definition from Parent - return self.element_class(x) - - def __list_from_iterator(self): - """ - The default implementation of list which builds the list from the - iterator. - - EXAMPLES:: - - sage: class C(CombinatorialClass): - ....: def __iter__(self): - ....: return iter([1,2,3]) - sage: C().list() #indirect doctest - [1, 2, 3] - """ - return [x for x in self] - - # Set list to the default implementation - list = __list_from_iterator - - # Set the default object class to be CombinatorialObject - Element = CombinatorialObject - - def __iterator_from_next(self) -> Iterator: - """ - An iterator to use when the .first() and .next(x) methods are provided. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.first = lambda: 0 - sage: C.next = lambda c: c+1 - sage: it = iter(C) # indirect doctest - sage: [next(it) for _ in range(4)] - [0, 1, 2, 3] - """ - f = self.first() - yield f - while True: - try: - f = self.next(f) - except (TypeError, ValueError): - break - - if f is None or f is False: - break - else: - yield f - - def __iterator_from_previous(self): - """ - An iterator to use when .last() and .previous() are provided. Note - that this requires the combinatorial class to be finite. It is not - recommended to implement combinatorial classes using last and - previous. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.last = lambda: 4 - sage: def prev(c): - ....: if c <= 1: - ....: return None - ....: else: - ....: return c-1 - sage: C.previous = prev - sage: it = iter(C) # indirect doctest - sage: [next(it) for _ in range(4)] - [1, 2, 3, 4] - """ - l = self.last() - li = [l] - while True: - try: - l = self.previous(l) - except (TypeError, ValueError): - break - - if l is None: - break - else: - li.append(l) - return reversed(li) - - def __iterator_from_unrank(self) -> Iterator: - """ - An iterator to use when .unrank() is provided. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: l = [1,2,3] - sage: C.unrank = lambda c: l[c] - sage: list(C) # indirect doctest - [1, 2, 3] - """ - r = 0 - u = self.unrank(r) - yield u - while True: - r += 1 - try: - u = self.unrank(r) - except (TypeError, ValueError, IndexError): - break - - if u is None: - break - else: - yield u - - def __iterator_from_list(self) -> Iterator: - """ - An iterator to use when .list() is provided() - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1, 2, 3] - sage: list(C) # indirect doctest - [1, 2, 3] - """ - yield from self.list() - - def __iter__(self): - """ - Allows the combinatorial class to be treated as an iterator. Default - implementation. - - EXAMPLES:: - - sage: p5 = Partitions(5) # needs sage.combinat - sage: [i for i in p5] # needs sage.combinat - [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] - sage: C = CombinatorialClass() - sage: iter(C) - Traceback (most recent call last): - ... - NotImplementedError: iterator called but not implemented - """ - # Check whether .first() and .next(x) are overridden in the subclass - if (self.first != self.__first_from_iterator and - self.next != self.__next_from_iterator): - return self.__iterator_from_next() - # Check whether .last() and .previous() are overridden in the subclass - elif (self.last != self.__last_from_iterator and - self.previous != self.__previous_from_iterator): - return self.__iterator_from_previous() - # Check whether .unrank() is overridden in the subclass - elif self.unrank != self.__unrank_from_iterator: - return self.__iterator_from_unrank() - # Check whether .list() is overridden in the subclass - elif self.list != self.__list_from_iterator: - return self.__iterator_from_list() - else: - raise NotImplementedError("iterator called but not implemented") - - def __unrank_from_iterator(self, r): - """ - Default implementation of unrank which goes through the iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.unrank(1) # indirect doctest - 2 - """ - counter = 0 - for u in self: - if counter == r: - return u - counter += 1 - raise ValueError("the value must be between %s and %s inclusive" % (0, counter - 1)) - - # Set the default implementation of unrank - unrank = __unrank_from_iterator - - def __random_element_from_unrank(self): - """ - Default implementation of random which uses unrank. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.random_element() # random # indirect doctest - 1 - """ - c = self.cardinality() - r = randint(0, c - 1) - return self.unrank(r) - - # Set the default implementation of random - random_element = __random_element_from_unrank - - def __rank_from_iterator(self, obj): - """ - Default implementation of rank which uses iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.rank(3) # indirect doctest - 2 - """ - r = 0 - for i in self: - if i == obj: - return r - r += 1 - raise ValueError - - rank = __rank_from_iterator - - def __first_from_iterator(self): - """ - Default implementation for first which uses iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.first() # indirect doctest - 1 - """ - for i in self: - return i - - first = __first_from_iterator - - def __last_from_iterator(self): - """ - Default implementation for first which uses iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.last() # indirect doctest - 3 - """ - for i in self: - pass - return i - - last = __last_from_iterator - - def __next_from_iterator(self, obj): - """ - Default implementation for next which uses iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.next(2) # indirect doctest - 3 - """ - found = False - for i in self: - if found: - return i - if i == obj: - found = True - return None - - next = __next_from_iterator - - def __previous_from_iterator(self, obj): - """ - Default implementation for next which uses iterator. - - EXAMPLES:: - - sage: C = CombinatorialClass() - sage: C.list = lambda: [1,2,3] - sage: C.previous(2) # indirect doctest - 1 - """ - prev = None - for i in self: - if i == obj: - break - prev = i - return prev - - previous = __previous_from_iterator - - def filter(self, f, name=None): - """ - Return the combinatorial subclass of f which consists of the - elements x of ``self`` such that f(x) is ``True``. - - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - sage: P.list() # needs sage.combinat - [[3, 2, 1]] - """ - return FilteredCombinatorialClass(self, f, name=name) - - def union(self, right_cc, name=None): - """ - Return the combinatorial class representing the union of ``self`` and - ``right_cc``. - - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(2).union(Permutations_CC(1)) - sage: P.list() - [[1, 2], [2, 1], [1]] - """ - if not isinstance(right_cc, CombinatorialClass): - raise TypeError("right_cc must be a CombinatorialClass") - return UnionCombinatorialClass(self, right_cc, name=name) - - def map(self, f, name=None, *, is_injective=True): - r""" - Return the image `\{f(x) | x \in \text{self}\}` of this combinatorial - class by `f`, as a combinatorial class. - - INPUT: - - - ``is_injective`` -- boolean (default: ``True``) whether to assume - that ``f`` is injective. - - EXAMPLES:: - - sage: R = Permutations(3).map(attrcall('reduced_word')); R - Image of Standard permutations of 3 by - The map *.reduced_word() from Standard permutations of 3 - sage: R.cardinality() - 6 - sage: R.list() - [[], [2], [1], [1, 2], [2, 1], [2, 1, 2]] - sage: [ r for r in R] - [[], [2], [1], [1, 2], [2, 1], [2, 1, 2]] - - If the function is not injective, then there may be repeated elements:: - - sage: P = Partitions(4) # needs sage.combinat - sage: P.list() # needs sage.combinat - [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] - sage: P.map(len).list() # needs sage.combinat - [1, 2, 2, 3, 4] - - Use ``is_injective=False`` to get a correct result in this case:: - - sage: P.map(len, is_injective=False).list() # needs sage.combinat - [1, 2, 3, 4] - - TESTS:: - - sage: R = Permutations(3).map(attrcall('reduced_word')) - sage: R == loads(dumps(R)) - True - """ - return MapCombinatorialClass(self, f, name, is_injective=is_injective) - - -class FilteredCombinatorialClass(CombinatorialClass): - def __init__(self, combinatorial_class, f, name=None): - """ - A filtered combinatorial class F is a subset of another - combinatorial class C specified by a function f that takes in an - element c of C and returns True if and only if c is in F. - - TESTS:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - Filtered subclass of Standard permutations of 3 - """ - self.f = f - self.combinatorial_class = combinatorial_class - self._name = name - - def __repr__(self): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - sage: P.__repr__() - 'Filtered subclass of Standard permutations of 3' - sage: P._name = 'Permutations avoiding [1, 2]' - sage: P.__repr__() - 'Permutations avoiding [1, 2]' - """ - if self._name: - return self._name - else: - return "Filtered subclass of " + repr(self.combinatorial_class) - - def __contains__(self, x) -> bool: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - sage: 'cat' in P - False - sage: [4,3,2,1] in P - False - sage: Permutation([1,2,3]) in P # needs sage.combinat - False - sage: Permutation([3,2,1]) in P # needs sage.combinat - True - """ - return x in self.combinatorial_class and self.f(x) - - def cardinality(self) -> Integer: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - sage: P.cardinality() # needs sage.combinat - 1 - """ - c = 0 - for _ in self: - c += 1 - return c - - def __iter__(self) -> Iterator: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2])) - sage: list(P) # needs sage.combinat - [[3, 2, 1]] - """ - for x in self.combinatorial_class: - if self.f(x): - yield x - - -class UnionCombinatorialClass(CombinatorialClass): - def __init__(self, left_cc, right_cc, name=None): - """ - A UnionCombinatorialClass is a union of two other combinatorial - classes. - - TESTS:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P == loads(dumps(P)) - True - """ - self.left_cc = left_cc - self.right_cc = right_cc - self._name = name - - def __repr__(self) -> str: - """ - TESTS:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: print(repr(Permutations_CC(3).union(Permutations_CC(2)))) - Union combinatorial class of - Standard permutations of 3 - and - Standard permutations of 2 - """ - if self._name: - return self._name - else: - return "Union combinatorial class of \n %s\nand\n %s" % (self.left_cc, self.right_cc) - - def __contains__(self, x) -> bool: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: [1,2] in P - True - sage: [3,2,1] in P - True - sage: [1,2,3,4] in P - False - """ - return x in self.left_cc or x in self.right_cc - - def cardinality(self) -> Integer | infinity: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.cardinality() - 8 - """ - return self.left_cc.cardinality() + self.right_cc.cardinality() - - def list(self): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.list() - [[1, 2, 3], - [1, 3, 2], - [2, 1, 3], - [2, 3, 1], - [3, 1, 2], - [3, 2, 1], - [1, 2], - [2, 1]] - """ - return self.left_cc.list() + self.right_cc.list() - - def __iter__(self) -> Iterator: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: list(P) - [[1, 2, 3], - [1, 3, 2], - [2, 1, 3], - [2, 3, 1], - [3, 1, 2], - [3, 2, 1], - [1, 2], - [2, 1]] - """ - for x in self.left_cc: - yield x - for x in self.right_cc: - yield x - - def first(self): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.first() - [1, 2, 3] - """ - return self.left_cc.first() - - def last(self): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.last() - [2, 1] - """ - return self.right_cc.last() - - def rank(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.rank(Permutation([2,1])) - 7 - sage: P.rank(Permutation([1,2,3])) - 0 - """ - try: - return self.left_cc.rank(x) - except (TypeError, ValueError): - return self.left_cc.cardinality() + self.right_cc.rank(x) - - def unrank(self, x): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3).union(Permutations_CC(2)) - sage: P.unrank(7) - [2, 1] - sage: P.unrank(0) - [1, 2, 3] - """ - try: - return self.left_cc.unrank(x) - except (TypeError, ValueError): - return self.right_cc.unrank(x - self.left_cc.cardinality()) - - -class Permutations_CC(CombinatorialClass): - """ - A testing class for :class:`CombinatorialClass` since :class:`Permutations` - no longer inherits from :class:`CombinatorialClass` in :issue:`14772`. - """ - - def __init__(self, n): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(4) - sage: loads(dumps(P)) == P - True - """ - from sage.combinat.permutation import StandardPermutations_n - self._permutations = StandardPermutations_n(n) - - def __repr__(self) -> str: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: Permutations_CC(3) - Standard permutations of 3 - """ - return repr(self._permutations) - - def __contains__(self, x) -> bool: - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3) - sage: [1, 3, 2] in P - True - """ - return x in self._permutations - - def __iter__(self): - """ - EXAMPLES:: - - sage: from sage.combinat.combinat import Permutations_CC - sage: P = Permutations_CC(3) - sage: P.list() - [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] - """ - return iter(self._permutations) - - -############################################################################## -from sage.sets.image_set import ImageSubobject - - -class MapCombinatorialClass(ImageSubobject, CombinatorialClass): - r""" - The image of a combinatorial class through a function. - - INPUT: - - - ``is_injective`` -- boolean (default: ``True``) whether to assume - that ``f`` is injective. - - See :meth:`CombinatorialClass.map` for examples - - EXAMPLES:: - - sage: # needs sage.groups - sage: R = SymmetricGroup(10).map(attrcall('reduced_word')) - sage: R.an_element() - [9, 8, 7, 6, 5, 4, 3, 2] - sage: R.cardinality() - 3628800 - sage: i = iter(R) - sage: next(i), next(i), next(i) - ([], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1]) - """ - - def __init__(self, cc, f, name=None, *, is_injective=True): - """ - TESTS:: - - sage: Partitions(3).map(attrcall('conjugate')) # needs sage.combinat - Image of Partitions of the integer 3 by The map *.conjugate() - from Partitions of the integer 3 - """ - ImageSubobject.__init__(self, f, cc, is_injective=is_injective) - self.cc = cc - self.f = f - if name: - self.rename(name) - - -############################################################################## -class InfiniteAbstractCombinatorialClass(CombinatorialClass): - r""" - This is an internal class that should not be used directly. A class which - inherits from InfiniteAbstractCombinatorialClass inherits the standard - methods list and count. - - If self._infinite_cclass_slice exists then self.__iter__ returns an - iterator for self, otherwise raise NotImplementedError. The method - self._infinite_cclass_slice is supposed to accept any integer as an - argument and return something which is iterable. - """ - - def cardinality(self) -> Integer | infinity: - """ - Count the elements of the combinatorial class. - - EXAMPLES:: - - sage: R = InfiniteAbstractCombinatorialClass() - doctest:warning... - DeprecationWarning: this class is deprecated, do not use - See https://github.com/sagemath/sage/issues/31545 for details. - - sage: R.cardinality() - +Infinity - """ - return infinity - - def list(self): - """ - Return an error since ``self`` is an infinite combinatorial class. - - EXAMPLES:: - - sage: R = InfiniteAbstractCombinatorialClass() - sage: R.list() - Traceback (most recent call last): - ... - NotImplementedError: infinite list - """ - raise NotImplementedError("infinite list") - - def __iter__(self) -> Iterator: - """ - Return an iterator for the infinite combinatorial class ``self`` if - possible or raise a NotImplementedError. - - EXAMPLES:: - - sage: R = InfiniteAbstractCombinatorialClass() - sage: next(iter(R)) - Traceback (most recent call last): - ... - NotImplementedError - - sage: c = iter(Compositions()) # indirect doctest - sage: next(c), next(c), next(c), next(c), next(c), next(c) - ([], [1], [1, 1], [2], [1, 1, 1], [1, 2]) - sage: next(c), next(c), next(c), next(c), next(c), next(c) - ([2, 1], [3], [1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3]) - """ - try: - finite = self._infinite_cclass_slice - except AttributeError: - raise NotImplementedError - i = 0 - while True: - yield from finite(i) - i += 1 - - ##################################################### # combinatorial sets/lists diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py index 0ad3151925e..652ddb4b898 100644 --- a/src/sage/combinat/constellation.py +++ b/src/sage/combinat/constellation.py @@ -400,7 +400,7 @@ def __copy__(self): sage: c is copy(c) False """ - return self.parent()([gg for gg in self._g], + return self.parent()(list(self._g), check=False, mutable=self._mutable) @@ -417,7 +417,7 @@ def mutable_copy(self): sage: d.is_mutable() True """ - return self.parent()([gg for gg in self._g], + return self.parent()(list(self._g), check=False, mutable=True) diff --git a/src/sage/combinat/crystals/affine_factorization.py b/src/sage/combinat/crystals/affine_factorization.py index b1d820032af..81fa6a74d0a 100644 --- a/src/sage/combinat/crystals/affine_factorization.py +++ b/src/sage/combinat/crystals/affine_factorization.py @@ -323,7 +323,7 @@ def bracketing(self, i): right_n.remove(min(l)) else: left_unbracketed += [m] - return [[j for j in left_unbracketed],[j for j in right_n]] + return [list(left_unbracketed), list(right_n)] def to_tableau(self): """ @@ -473,16 +473,16 @@ def _call_(self, x): """ p = [] q = [] - for i,factor in enumerate(reversed(x.value)): + for i, factor in enumerate(reversed(x.value)): word = factor.reduced_word() - p += [i+1]*len(word) + p += [i + 1] * len(word) # We sort for those pesky commutative elements # The word is most likely in reverse order to begin with q += sorted(reversed(word)) C = self.codomain() return C(RSK(p, q, insertion=RSK.rules.EG)[1]) - def is_isomorphism(self): + def is_isomorphism(self) -> bool: """ Return ``True`` as this is an isomorphism. diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py index aebc499e9fb..fe32db2a419 100644 --- a/src/sage/combinat/crystals/fast_crystals.py +++ b/src/sage/combinat/crystals/fast_crystals.py @@ -154,14 +154,14 @@ def __init__(self, ct, shape, format): self.shape = shape for i in range(self.size): - target = [x for x in self.delpat[i]] + target = list(self.delpat[i]) target[0] = target[0]-1 e1 = None if target not in self.delpat else self.delpat.index(target) target[0] = target[0]+1+1 f1 = None if target not in self.delpat else self.delpat.index(target) - target = [x for x in self.gampat[i]] + target = list(self.gampat[i]) target[0] = target[0]-1 e2 = None if target not in self.gampat else self.gampat.index(target) target[0] = target[0]+1+1 diff --git a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py index e63e1a4a3ce..d11846d7766 100644 --- a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py +++ b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py @@ -728,8 +728,8 @@ def bracketing(self, i): m = P.factors L = list(self.value[m-i-1]) R = list(self.value[m-i]) - right_n = [j for j in R] - left_n = [j for j in L] + right_n = list(R) + left_n = list(L) left_unbracketed = [] while left_n: m = max(left_n) @@ -739,7 +739,7 @@ def bracketing(self, i): right_n.remove(min(l)) else: left_unbracketed += [m] - return [[j for j in left_unbracketed], [j for j in right_n]] + return [list(left_unbracketed), list(right_n)] #################### diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index 6219da38391..8b2053bec8b 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -250,7 +250,7 @@ def module_generator(self): [[1, 1, 1], [2, 2], [3]] """ n = self._cartan_type.rank() - p = Partition([x for x in reversed(range(1, n+1))]) + p = Partition(list(reversed(range(1, n + 1)))) # The column canonical tableau, read by columns module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n)]) return self(list=[self.letters(x) for x in module_generator]) @@ -624,7 +624,7 @@ def module_generator(self): [[1, 1, 1], [2, 2], [3]] """ n = self._cartan_type.rank() - p = Partition([x for x in reversed(range(1, n))]) + p = Partition(list(reversed(range(1, n)))) # The column canonical tableau, read by columns module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n-1)]) return self(list=[self.letters(x) for x in module_generator]) diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 1e58c8ead35..c1872c793be 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -3138,7 +3138,7 @@ def promotion_on_highest_weight_vectors(self): ind.remove(1) C = T.cartan_type() n = C.n - sh = [i for i in T.shapes[0]] + sh = list(T.shapes[0]) sh[n-1] = -sh[n-1] T_dual = CrystalOfTableaux(C, shape=sh) hw = [t for t in T if t.is_highest_weight(index_set=ind)] @@ -3997,7 +3997,7 @@ def horizontal_dominoes_removed(r, s): sage: sage.combinat.crystals.kirillov_reshetikhin.horizontal_dominoes_removed(3,2) [[], [2], [2, 2], [2, 2, 2]] """ - ulist = [ [y for y in x] + [0]*(r-x.length()) for x in partitions_in_box(r, s//2) ] + ulist = [list(x) + [0]*(r-x.length()) for x in partitions_in_box(r, s//2)] two = lambda x : 2 * (x - s // 2) + s return [Partition([two(y) for y in x]) for x in ulist] diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 726c974e781..e0482ec74eb 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -583,8 +583,6 @@ def __iter__(self): for x in self.cartesian_product: yield self(*x) -# list = CombinatorialClass._CombinatorialClass__list_from_iterator - def cardinality(self): """ Return the cardinality of ``self``. diff --git a/src/sage/combinat/decorated_permutation.py b/src/sage/combinat/decorated_permutation.py index f687818ef25..817be65223d 100644 --- a/src/sage/combinat/decorated_permutation.py +++ b/src/sage/combinat/decorated_permutation.py @@ -189,7 +189,7 @@ def __contains__(self, pi): if isinstance(pi, DecoratedPermutation): return len(pi) == self._n - values = [v for v in pi] + values = list(pi) if len(values) != self._n: return False abs_values = [abs(v) for v in values] diff --git a/src/sage/combinat/derangements.py b/src/sage/combinat/derangements.py index e9e1de647e8..6b2d868b90d 100644 --- a/src/sage/combinat/derangements.py +++ b/src/sage/combinat/derangements.py @@ -422,7 +422,7 @@ def cardinality(self): R = PolynomialRing(QQ, 'x', len(A)) S = sum(R.gens()) e = prod((S - x)**y for (x, y) in zip(R.gens(), A)) - return Integer(e.coefficient(dict([(x, y) for (x, y) in zip(R.gens(), A)]))) + return Integer(e.coefficient(dict(zip(R.gens(), A)))) return self._count_der(len(self._set)) def _rand_der(self): diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index e4d80442f3c..943f7c9ba22 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4118,7 +4118,7 @@ def RBIBD_120_8_1(): if p in B: equiv.append([x for x in B if x not in hyperoval]) else: - new_BIBD.append([x for x in B]) + new_BIBD.append(list(B)) BIBD = new_BIBD @@ -4658,7 +4658,7 @@ def BIBD_79_13_2(): permAction = libgap.Action(G, points, action) - baseBlocks = [libgap.Set(list(map(lambda p: libgap.Position(points, p), B))) for B in [B1, B2, B3, B4]] + baseBlocks = [libgap.Set([libgap.Position(points, p) for p in B]) for B in [B1, B2, B3, B4]] B3Orbit = libgap.Orbit(permAction, baseBlocks[2], libgap.OnSets) B4Orbit = libgap.Orbit(permAction, baseBlocks[3], libgap.OnSets) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 8c2b2b0b01c..a569a722104 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -2936,7 +2936,7 @@ def complementary_difference_setsII(n, check=True): if t % 2 == 0: rho = G.multiplicative_generator() C0 = list({el**8 for el in G if el != 0}) - C1, C2, C3, C6, C7 = map(lambda i: [rho**i * el for el in C0], [1, 2, 3, 6, 7]) + C1, C2, C3, C6, C7 = ([rho**i * el for el in C0] for i in [1, 2, 3, 6, 7]) A = C0 + C1 + C2 + C3 B = C0 + C1 + C6 + C7 else: diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index 2227a1e9f63..75df1889c41 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -922,7 +922,7 @@ def _end_element(self, name): self.block_design_proc(self.current_node[2][0]) if self.save_designs: init_bd = XTree(self.current_node[2][0]) - self.list_of_designs.append((init_bd.v, [b for b in init_bd.blocks])) + self.list_of_designs.append((init_bd.v, list(init_bd.blocks))) #print_subxt(self.current_node[2][0], level=2, outf=self.outf) self._init() elif name == 'info': diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index b4a14b0e7b6..4ba9b60ecc2 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1563,7 +1563,7 @@ def OA_relabel(OA, k, n, blocks=tuple(), matrix=None, symbol_list=None): OA = [[matrix[i][j] if j is not None else None for i,j in enumerate(R)] for R in OA] if symbol_list: - mapping = {index: symbol for index, symbol in enumerate(symbol_list)} + mapping = dict(enumerate(symbol_list)) OA = [[mapping[element] for element in row] for row in OA] return OA diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 0f82ee78430..ce1758b15c4 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1069,7 +1069,7 @@ def bijection_on_free_nodes(self, two_line=False): sage: elm2.bijection_on_free_nodes(two_line=True) [[1, 2, 3], [-2, -3, -1]] """ - terms = sorted(sorted(list(v), reverse=True) for v in self.diagram() + terms = sorted(sorted(v, reverse=True) for v in self.diagram() if max(v) > 0 and min(v) < 0) if two_line: terms = [[t[i] for t in terms] for i in range(2)] diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 12bbd4385db..069ebb88fb6 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1381,7 +1381,7 @@ def __init__(self, modules, **options): """ self._sets = modules indices = CartesianProduct_iters(*[module.basis().keys() - for module in modules]).map(tuple) + for module in modules]).map(tuple, is_injective=True) CombinatorialFreeModule.__init__(self, modules[0].base_ring(), indices, **options) # the following is not the best option, but it's better than nothing. if 'tensor_symbol' in options: diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index 7540aca9a07..9a28d632c85 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -240,7 +240,7 @@ def __iter__(self): perm = Word(self._weights).standard_permutation() perm = [len(self._weights) - i for i in perm] - l = [x for x in sorted(self._weights, reverse=True)] + l = sorted(self._weights, reverse=True) for x in iterator_fast(self._n, l): yield self.element_class(self, [x[i] for i in perm]) # .action(x) diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index 1c0215fa093..b8b62c6a440 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -817,8 +817,8 @@ def __repr__(self) -> str: Nablas : [a\b/c, b\c/a, c\a/b] Deltas : [a/c\b, b/a\c, c/b\a] """ - s = "Nablas : %s\n" % sorted([p for p in self._nabla_pieces], key=str) - s += "Deltas : %s" % sorted([p for p in self._delta_pieces], key=str) + s = "Nablas : %s\n" % sorted(self._nabla_pieces, key=str) + s += "Deltas : %s" % sorted(self._delta_pieces, key=str) return s def delta_pieces(self): diff --git a/src/sage/combinat/lr_tableau.py b/src/sage/combinat/lr_tableau.py index a94f576a134..364291cee7c 100644 --- a/src/sage/combinat/lr_tableau.py +++ b/src/sage/combinat/lr_tableau.py @@ -304,5 +304,5 @@ def _tableau_join(t1, t2, shift=0): sage: _tableau_join([[1,2]],[[None,None,2],[3]],shift=5) [[1, 2, 7], [8]] """ - return [[e1 for e1 in row1] + [e2+shift for e2 in row2 if e2 is not None] + return [list(row1) + [e2+shift for e2 in row2 if e2 is not None] for (row1, row2) in zip_longest(t1, t2, fillvalue=[])] diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 14d6a30a8f5..8c6b2f10395 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -592,7 +592,7 @@ def pmtoZ(s): if n not in db: raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n) - a, b, c, d = map(lambda s: vector(pmtoZ(s)), db[n]) + a, b, c, d = (vector(pmtoZ(s)) for s in db[n]) return a, b, c, d @@ -736,7 +736,7 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): def autocorrelation(seq, j): return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) for j in range(1, n): - assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0 T1 = X + Z T2 = X + [-z for z in Z] @@ -812,7 +812,7 @@ def autocorrelation(seq, j): return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) for j in range(1, n): - assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0 def alternate(seq1, seq2): return [seq1[i//2] if i % 2 == 0 else seq2[(i-1)//2] for i in range(len(seq1)+len(seq2))] diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 967f6ad93af..55d6a9aeaf9 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -246,9 +246,9 @@ def _repr_normal(self): """ # TODO: simplify if/once ``_repr_`` method for ``Set`` sorts its elements. if self._n: - string_parts = map(lambda k: str(sorted(k)), self) + string_parts = (str(sorted(k)) for k in self) else: - string_parts = map(lambda k: str(sorted(k, key=str)), self) + string_parts = (str(sorted(k, key=str)) for k in self) string_parts = ", ".join(string_parts).replace("[","{").replace("]","}") return "[" + string_parts + "]" diff --git a/src/sage/combinat/nu_dyck_word.py b/src/sage/combinat/nu_dyck_word.py index ddc54a03612..bdc2c0f6e3a 100644 --- a/src/sage/combinat/nu_dyck_word.py +++ b/src/sage/combinat/nu_dyck_word.py @@ -829,7 +829,7 @@ def _latex_(self): if latex_options['show_grid']: grid = [((0, 0), (self.width(), self.height()))] for v1, v2 in grid: - res += " \\draw[dotted] %s grid %s;" % (str(v1), str(v2)) + res += f" \\draw[dotted] {v1} grid {v2};" res += "\n" # Add points if wanted @@ -838,8 +838,8 @@ def _latex_(self): radius = 0.15 + .03 * latex_options['line width'] for v in self.points(): res += " \\draw[line width=2," - res += "color=%s,fill=%s]" % (pt_color, pt_color) - res += "%s circle (%s);" % (str(v), str(radius)) + res += f"color={pt_color},fill={pt_color}]" + res += f"{v} circle ({radius});" res += "\n" # Add nu if wanted @@ -853,7 +853,7 @@ def _latex_(self): res += ";\n" # setup Path - res += " \\draw[rounded corners=1, color=%s, line width=%s]" % ( + res += " \\draw[rounded corners=1, color={}, line width={}]".format( latex_options['color'], str(latex_options['line width']) ) @@ -1210,46 +1210,46 @@ class options(GlobalOptions): """ NAME = 'NuDyckWords' module = 'sage.combinat.nu_dyck_path' - display = dict(default="list", - description='Specifies how nu Dyck words should be printed', - values=dict(list='displayed as a list', - lattice='displayed on the lattice defined by ``diagram_style``'), - case_sensitive=False) - ascii_art = dict(default="pretty_output", - description='Specifies how the ascii art of nu Dyck words should be printed', - values=dict(pretty_output="Using pretty printing"), - alias=dict(pretty_print="pretty_output",), - case_sensitive=False) - diagram_style = dict(default="grid", - values=dict( - grid='printing as paths on a grid using N and E steps',), - alias={'N-E': 'grid'}, - case_sensitive=False) - latex_tikz_scale = dict(default=1, - description='The default value for the tikz scale when latexed', - checker=lambda x: True) # More trouble than it's worth to check - latex_line_width_scalar = dict(default=2, - description='The default value for the line width as a ' - 'multiple of the tikz scale when latexed', - checker=lambda x: True) # More trouble than it's worth to check - latex_color = dict(default="black", - description='The default value for the color when latexed', - checker=lambda x: isinstance(x, str)) - latex_show_points = dict(default=False, - description='The default value for showing points', - checker=lambda x: isinstance(x, bool)) - latex_points_color = dict(default='black', - description='The default value for path color.', - checker=lambda x: isinstance(x, str)) - latex_show_grid = dict(default=True, - description='The default value for showing grid', - checker=lambda x: isinstance(x, bool)) - latex_show_nu = dict(default=True, - description='The default value for showing nu', - checker=lambda x: isinstance(x, bool)) - latex_nu_options = dict(default='rounded corners=1, color=red, line width=1', - description='The default value for options for nu path', - checker=lambda x: isinstance(x, str)) + display = {'default': "list", + 'description': 'Specifies how nu Dyck words should be printed', + 'values': {'list': 'displayed as a list', + 'lattice': 'displayed on the lattice defined by ``diagram_style``'}, + 'case_sensitive': False} + ascii_art = {'default': "pretty_output", + 'description': 'Specifies how the ascii art of nu Dyck words should be printed', + 'values': {'pretty_output': "Using pretty printing"}, + 'alias': {'pretty_print': "pretty_output"}, + 'case_sensitive': False} + diagram_style = {'default': "grid", + 'values': { + 'grid': 'printing as paths on a grid using N and E steps'}, + 'alias': {'N-E': 'grid'}, + 'case_sensitive': False} + latex_tikz_scale = {'default': 1, + 'description': 'The default value for the tikz scale when latexed', + 'checker': lambda x: True} # More trouble than it's worth to check + latex_line_width_scalar = {'default': 2, + 'description': 'The default value for the line width as a ' + 'multiple of the tikz scale when latexed', + 'checker': lambda x: True} # More trouble than it's worth to check + latex_color = {'default': "black", + 'description': 'The default value for the color when latexed', + 'checker': lambda x: isinstance(x, str)} + latex_show_points = {'default': False, + 'description': 'The default value for showing points', + 'checker': lambda x: isinstance(x, bool)} + latex_points_color = {'default': 'black', + 'description': 'The default value for path color.', + 'checker': lambda x: isinstance(x, str)} + latex_show_grid = {'default': True, + 'description': 'The default value for showing grid', + 'checker': lambda x: isinstance(x, bool)} + latex_show_nu = {'default': True, + 'description': 'The default value for showing nu', + 'checker': lambda x: isinstance(x, bool)} + latex_nu_options = {'default': 'rounded corners=1, color=red, line width=1', + 'description': 'The default value for options for nu path', + 'checker': lambda x: isinstance(x, str)} def _element_constructor_(self, word): """ diff --git a/src/sage/combinat/nu_tamari_lattice.py b/src/sage/combinat/nu_tamari_lattice.py index d28faf6c7ed..56cfa020194 100644 --- a/src/sage/combinat/nu_tamari_lattice.py +++ b/src/sage/combinat/nu_tamari_lattice.py @@ -263,7 +263,7 @@ def AltNuTamariLattice(nu, delta=None): deltamax = [len(a) for a in nu.split(sep='1')[1:]] if delta is None: delta = deltamax - elif len(delta) != len(deltamax) or any([delta[i] > deltamax[i] for i in range(len(delta))]): + elif len(delta) != len(deltamax) or any(delta[i] > deltamax[i] for i in range(len(delta))): raise ValueError("delta is not a valid increment vector") def covers(p): diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index cb65aec8aec..539d63a867b 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -713,8 +713,8 @@ def _repr_exp_low(self): if not self._list: return '-' exp = self.to_exp() - return '%s' % ', '.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ', '.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_exp_high(self): """ @@ -733,7 +733,7 @@ def _repr_exp_high(self): return '-' exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return ', '.join('%s%s' % (M - m, '' if e == 1 else '^%s' % e) + return ', '.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e) for m, e in enumerate(exp) if e > 0) def _repr_compact_low(self): @@ -751,8 +751,8 @@ def _repr_compact_low(self): if not self._list: return '-' exp = self.to_exp() - return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_compact_high(self): """ @@ -770,8 +770,8 @@ def _repr_compact_high(self): return '-' exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_diagram(self): r""" @@ -945,8 +945,8 @@ def _latex_exp_low(self): if not self._list: return "{\\emptyset}" exp = self.to_exp() - return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^{%s}' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^{%s}' % e) + for (m,e) in enumerate(exp) if e > 0) def _latex_exp_high(self): r""" @@ -963,8 +963,8 @@ def _latex_exp_high(self): return "{\\emptyset}" exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^{%s}' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^{%s}' % e) + for (m,e) in enumerate(exp) if e > 0) def ferrers_diagram(self): r""" @@ -1851,7 +1851,7 @@ def down_list(self): sage: Partition([]).down_list() #checks :issue:`11435` [] """ - return [p for p in self.down()] + return list(self.down()) @combinatorial_map(name="cell poset") def cell_poset(self, orientation="SE"): @@ -2339,11 +2339,7 @@ def cells(self): sage: Partition([3,2]).cells() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1)] """ - res = [] - for i in range(len(self)): - for j in range(self[i]): - res.append( (i,j) ) - return res + return [(i, j) for i, si in enumerate(self) for j in range(si)] def generalized_pochhammer_symbol(self, a, alpha): r""" @@ -2925,7 +2921,7 @@ def top_garnir_tableau(self,e,cell): """ (row,col) = cell if row+1 >= len(self) or col >= self[row+1]: - raise ValueError('(%s,%s)=(row+1,col) must be inside the diagram' % (row+1,col)) + raise ValueError(f'({row+1},{col})=(row+1,col) must be inside the diagram') g = self.garnir_tableau(cell) # start with the Garnir tableau and modify @@ -2945,6 +2941,110 @@ def top_garnir_tableau(self,e,cell): t[row+1][col-b+1:col+1] = [m+a+col-b+1+i for i in range(b)] return tableau.StandardTableau(t) + def ladder_tableau(self, e, ladder_lengths=False): + r""" + Return the ladder tableau of shape ``self``. + + The `e`-*ladder tableau* is the standard Young tableau obtained + by reading the *ladders*, the set of cells `(i, j)` that differ + from `(i+e-1, j-1)`, of the partition `\lambda` from left-to-right. + + INPUT: + + - ``e`` -- a nonnegative integer; ``0`` is considered as `\infty` + (analogous to the characteristic of a ring) + - ``ladder_sizes`` -- (default: ``False``) if ``True``, also return + the sizes of the ladders + + .. SEEALSO:: + + :meth:`ladders` + + EXAMPLES:: + + sage: la = Partition([6, 5, 3, 1]) + sage: ascii_art(la.ladder_tableau(3)) + 1 2 3 5 7 10 + 4 6 8 11 13 + 9 12 14 + 15 + sage: la.ladder_tableau(3, ladder_lengths=True)[1] + [1, 1, 2, 2, 3, 3, 3] + + sage: ascii_art(la.ladder_tableau(0)) + 1 2 3 4 5 6 + 7 8 9 10 11 + 12 13 14 + 15 + sage: all(ll == 1 for ll in la.ladder_tableau(0, ladder_lengths=True)[1]) + True + """ + Tlad = [[None] * val for val in self] + counter = 0 + start = 0 + n = sum(self) + sizes = [] + e = e - 1 if e > 0 else n # change to the slope + while counter < n: + cur = start + size = 0 + for i, val in enumerate(self): + if cur < 0: + break + if cur < val: + counter += 1 + Tlad[i][cur] = counter + size += 1 + cur -= e + if ladder_lengths and size: + sizes.append(size) + start += 1 + ret = tableau.StandardTableaux(self)(Tlad) + if ladder_lengths: + return (ret, sizes) + return ret + + def ladders(self, e): + r""" + Return a dictionary containing the ladders in the diagram of ``self``. + + For `e > 0`, a node `(i, j)` in a partition belongs to the `l`-th + `e`-ladder if `l = (e - 1) r + c`. + + INPUT: + + - ``e`` -- a nonnegative integer; if ``0``, then we + set ``e = self.size() + 1`` + + EXAMPLES:: + + sage: Partition([3, 2]).ladders(3) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2), (1, 0)], 3: [(1, 1)]} + + When ``e`` is ``0``, the cells are in bijection with the ladders, + but the index of the ladder depends on the size of the partition:: + + sage: Partition([3, 2]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 6: [(1, 1)]} + sage: Partition([3, 2, 1]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 6: [(1, 0)], 7: [(1, 1)], + 12: [(2, 0)]} + sage: Partition([3, 1, 1]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 10: [(2, 0)]} + sage: Partition([1, 1, 1]).ladders(0) + {0: [(0, 0)], 3: [(1, 0)], 6: [(2, 0)]} + """ + if e == 0: + e = sum(self) + 1 + ladders = {} + for row, val in enumerate(self): + for col in range(val): + ell = col + row * (e - 1) + if ell not in ladders: + ladders[ell] = [] + ladders[ell].append((row, col)) + return ladders + @cached_method def young_subgroup(self): r""" @@ -3336,16 +3436,16 @@ def attacking_pairs(self): attacking_pairs = [] for i, r in enumerate(self): for j in range(r): - #c is in position (i,j) - #Find the d that satisfy condition 1 - for k in range(j+1, r): - attacking_pairs.append( ((i,j),(i,k)) ) + # c is in position (i,j) + # Find the d that satisfy condition 1 + attacking_pairs.extend(((i, j), (i, k)) + for k in range(j + 1, r)) - #Find the d that satisfy condition 2 + # Find the d that satisfy condition 2 if i == 0: continue - for k in range(j): - attacking_pairs.append( ((i,j),(i-1,k)) ) + attacking_pairs.extend(((i, j), (i - 1, k)) + for k in range(j)) return attacking_pairs @@ -4017,7 +4117,7 @@ def is_restricted(self, e, multicharge=(0,)): return (not self or ( self[-1] < e and all(self[r]-self[r+1] < e for r in range(len(self)-1)) )) - def is_regular(self, e, multicharge=(0,)): + def is_regular(self, e, multicharge=(0,)) -> bool: """ Return ``True`` is this is an ``e``-regular partition. @@ -4049,9 +4149,9 @@ def conjugacy_class_size(self): sage: Partition([2,1,1]).conjugacy_class_size() 6 """ - return factorial(sum(self))/self.centralizer_size() + return factorial(sum(self)) / self.centralizer_size() - def corners(self): + def corners(self) -> list: r""" Return a list of the corners of the partition ``self``. @@ -4224,9 +4324,8 @@ def rim(self): p = self res = [] prevLen = 1 - for i in range(len(p)-1, -1, -1): - for c in range(prevLen-1, p[i]): - res.append((i,c)) + for i in range(len(p) - 1, -1, -1): + res.extend((i, c) for c in range(prevLen - 1, p[i])) prevLen = p[i] return res @@ -4263,9 +4362,8 @@ def outer_rim(self): p = self res = [] prevLen = 0 - for i in range(len(p)-1, -1, -1): - for c in range(prevLen, p[i]+1): - res.append((i+1,c)) + for i in range(len(p) - 1, -1, -1): + res.extend((i + 1, c) for c in range(prevLen, p[i] + 1)) prevLen = p[i] res.append((0, prevLen)) return res @@ -4316,7 +4414,7 @@ def zero_one_sequence(self): sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) == mu for n in range(10) for mu in Partitions(n)) True """ - tmp = set(self[i] - i for i in range(len(self))) + tmp = {si - i for i, si in enumerate(self)} return [Integer(i not in tmp) for i in range(-len(self) + 1, self.get_part(0) + 1)] @@ -4958,7 +5056,7 @@ def horizontal_border_strip_cells(self, k): [(3, 0), (3, 1)]] """ if k == 0: - return list() + return [] L = self._list shelf = [k] # the number of boxes which will fit in a row @@ -5099,11 +5197,8 @@ def atom(self): sage: Partition([3,2,1]).atom() [[[1, 2, 3, 6], [4, 5]], [[1, 2, 3], [4, 5], [6]]] """ - res = [] - for tab in tableau.StandardTableaux_size(self.size()): - if tab.atom() == self: - res.append(tab) - return res + return [tab for tab in tableau.StandardTableaux_size(self.size()) + if tab.atom() == self] def k_atom(self, k): r""" @@ -6059,7 +6154,7 @@ def __classcall_private__(cls, n=None, **kwargs): return RestrictedPartitions_n(n, kwargs['restricted']) # FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute - kwargs['name'] = "Partitions of the integer %s satisfying constraints %s" % (n, ", ".join( ["%s=%s" % (key, kwargs[key]) for key in sorted(kwargs)] )) + kwargs['name'] = "Partitions of the integer {} satisfying constraints {}".format(n, ", ".join( ["{}={}".format(key, kwargs[key]) for key in sorted(kwargs)] )) # min_part is at least 1, and it is 1 by default kwargs['min_part'] = max(1, kwargs.get('min_part', 1)) @@ -6195,34 +6290,34 @@ class options(GlobalOptions): """ NAME = 'Partitions' module = 'sage.combinat.partition' - display = dict(default="list", - description='Specifies how partitions should be printed', - values=dict(list='displayed as a list', - exp_low='in exponential form (lowest first)', - exp_high='in exponential form (highest first)', - diagram='as a Ferrers diagram', - compact_low='compact form of ``exp_low``', - compact_high='compact form of ``exp_high``'), - alias=dict(exp="exp_low", compact="compact_low", array="diagram", - ferrers_diagram="diagram", young_diagram="diagram"), - case_sensitive=False) - latex = dict(default="young_diagram", - description='Specifies how partitions should be latexed', - values=dict(diagram='latex as a Ferrers diagram', - young_diagram='latex as a Young diagram', - list='latex as a list', - exp_high='latex as a list in exponential notation (highest first)', - exp_low='as a list latex in exponential notation (lowest first)'), - alias=dict(exp="exp_low", array="diagram", ferrers_diagram="diagram"), - case_sensitive=False) - diagram_str = dict(default="*", - description='The character used for the cells when printing Ferrers diagrams', - checker=lambda char: isinstance(char,str)) - latex_diagram_str = dict(default="\\ast", - description='The character used for the cells when latexing Ferrers diagrams', - checker=lambda char: isinstance(char,str)) - convention = dict(link_to=(tableau.Tableaux.options,'convention')) - notation = dict(alt_name='convention') + display = {'default': "list", + 'description': 'Specifies how partitions should be printed', + 'values': {'list': 'displayed as a list', + 'exp_low': 'in exponential form (lowest first)', + 'exp_high': 'in exponential form (highest first)', + 'diagram': 'as a Ferrers diagram', + 'compact_low': 'compact form of ``exp_low``', + 'compact_high': 'compact form of ``exp_high``'}, + 'alias': {'exp': "exp_low", 'compact': "compact_low", 'array': "diagram", + 'ferrers_diagram': "diagram", 'young_diagram': "diagram"}, + 'case_sensitive': False} + latex = {'default': "young_diagram", + 'description': 'Specifies how partitions should be latexed', + 'values': {'diagram': 'latex as a Ferrers diagram', + 'young_diagram': 'latex as a Young diagram', + 'list': 'latex as a list', + 'exp_high': 'latex as a list in exponential notation (highest first)', + 'exp_low': 'as a list latex in exponential notation (lowest first)'}, + 'alias': {'exp': "exp_low", 'array': "diagram", 'ferrers_diagram': "diagram"}, + 'case_sensitive': False} + diagram_str = {'default': "*", + 'description': 'The character used for the cells when printing Ferrers diagrams', + 'checker': lambda char: isinstance(char,str)} + latex_diagram_str = {'default': "\\ast", + 'description': 'The character used for the cells when latexing Ferrers diagrams', + 'checker': lambda char: isinstance(char,str)} + convention = {'link_to': (tableau.Tableaux.options,'convention')} + notation = {'alt_name': 'convention'} def __reversed__(self): """ @@ -6282,7 +6377,7 @@ def _element_constructor_(self, lst): # trailing zeros are removed in Partition.__init__ return self.element_class(self, lst) - raise ValueError('%s is not an element of %s' % (lst, self)) + raise ValueError(f'{lst} is not an element of {self}') def __contains__(self, x): """ @@ -7122,7 +7217,7 @@ def _repr_(self): sage: Partitions(5, length=2) # indirect doctest Partitions of the integer 5 of length 2 """ - return "Partitions of the integer {} of length {}".format(self.n, self.k) + return f"Partitions of the integer {self.n} of length {self.k}" def _an_element_(self): """ @@ -7344,7 +7439,7 @@ def _repr_(self): sage: Partitions(5, parts_in=[1,2,3]) # indirect doctest Partitions of the integer 5 with parts in [1, 2, 3] """ - return "Partitions of the integer %s with parts in %s" % (self.n, self.parts) + return f"Partitions of the integer {self.n} with parts in {self.parts}" def cardinality(self): r""" @@ -7877,7 +7972,7 @@ def _repr_(self): sage: PartitionsInBox(2,2) # indirect doctest Integer partitions which fit in a 2 x 2 box """ - return "Integer partitions which fit in a %s x %s box" % (self.h, self.w) + return f"Integer partitions which fit in a {self.h} x {self.w} box" def __contains__(self, x): """ @@ -8161,7 +8256,7 @@ def _repr_(self): sage: RegularPartitions_all(3) 3-Regular Partitions """ - return "{}-Regular Partitions".format(self._ell) + return f"{self._ell}-Regular Partitions" def __iter__(self): """ @@ -8251,7 +8346,7 @@ def _repr_(self): sage: RegularPartitions_truncated(4, 3) 4-Regular Partitions with max length 3 """ - return "{}-Regular Partitions with max length {}".format(self._ell, self._max_len) + return f"{self._ell}-Regular Partitions with max length {self._max_len}" def __iter__(self): """ @@ -8369,7 +8464,7 @@ def _repr_(self): sage: RegularPartitions_bounded(4, 3) 4-Regular 3-Bounded Partitions """ - return "{}-Regular {}-Bounded Partitions".format(self._ell, self.k) + return f"{self._ell}-Regular {self.k}-Bounded Partitions" def __iter__(self): """ @@ -8432,7 +8527,7 @@ def _repr_(self): sage: RegularPartitions_n(3, 5) 5-Regular Partitions of the integer 3 """ - return "{}-Regular Partitions of the integer {}".format(self._ell, self.n) + return f"{self._ell}-Regular Partitions of the integer {self.n}" def __contains__(self, x): """ @@ -8724,7 +8819,7 @@ def _repr_(self): sage: PartitionsGreatestLE(10, 2) # indirect doctest Partitions of 10 having parts less than or equal to 2 """ - return "Partitions of %s having parts less than or equal to %s" % (self.n, self.k) + return f"Partitions of {self.n} having parts less than or equal to {self.k}" def cardinality(self): """ @@ -8814,7 +8909,7 @@ def _repr_(self): sage: PartitionsGreatestEQ(10, 2) # indirect doctest Partitions of 10 having greatest part equal to 2 """ - return "Partitions of %s having greatest part equal to %s" % (self.n, self.k) + return f"Partitions of {self.n} having greatest part equal to {self.k}" def cardinality(self): """ @@ -8996,7 +9091,7 @@ def _repr_(self): sage: RestrictedPartitions_all(3) 3-Restricted Partitions """ - return "{}-Restricted Partitions".format(self._ell) + return f"{self._ell}-Restricted Partitions" def __iter__(self): """ @@ -9051,7 +9146,7 @@ def _repr_(self): sage: RestrictedPartitions_n(3, 5) 5-Restricted Partitions of the integer 3 """ - return "{}-Restricted Partitions of the integer {}".format(self._ell, self.n) + return f"{self._ell}-Restricted Partitions of the integer {self.n}" def __contains__(self, x): """ diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 182c0b8813d..b4d15d2dc55 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -767,7 +767,7 @@ def components(self): *** ** """ - return [t for t in self] + return list(self) def diagram(self): r""" @@ -883,7 +883,7 @@ def up(self): """ for c in range(len(self)): for nu in self[c].up(): - up = [tau for tau in self] + up = list(self) up[c] = nu yield PartitionTuple(up) @@ -900,7 +900,7 @@ def up_list(self): [([1], [], [], []), ([], [1], [], []), ([], [], [1], []), ([], [], [], [1])] """ - return [mu for mu in self.up()] + return list(self.up()) def down(self): r""" @@ -917,7 +917,7 @@ def down(self): """ for c in range(len(self)): for nu in self[c].down(): - down = [tau for tau in self] + down = list(self) down[c] = nu yield PartitionTuple(down) @@ -933,7 +933,7 @@ def down_list(self): sage: PartitionTuple([[],[],[]]).down_list() [] """ - return [mu for mu in self.down()] + return list(self.down()) def cells(self): """ @@ -1515,7 +1515,7 @@ def young_subgroup_generators(self): m = 0 for comp in self: for row in comp: - gens.extend([c for c in range(m + 1, m + row)]) + gens.extend(list(range(m + 1, m + row))) m += row return gens diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 3919d9866da..0ec9451a9a9 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -239,7 +239,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator import itertools import operator @@ -680,9 +680,9 @@ def _latex_(self): redword = self.reduced_word() if not redword: return self.parent().options.latex_empty_str - return " ".join("%s_{%s}" % (let, i) for i in redword) + return " ".join(f"{let}_{{{i}}}" for i in redword) if display == "twoline": - return "\\begin{pmatrix} %s \\\\ %s \\end{pmatrix}" % ( + return r"\begin{{pmatrix}} {} \\ {} \end{{pmatrix}}".format( " & ".join("%s" % i for i in range(1, len(self._list)+1)), " & ".join("%s" % i for i in self._list)) if display == "list": @@ -1406,7 +1406,7 @@ def __call__(self, i): """ if isinstance(i, (int, Integer)) and 1 <= i <= len(self): return self[i - 1] - raise TypeError("i (= %s) must be between 1 and %s" % (i, len(self))) + raise TypeError(f"i (= {i}) must be between 1 and {len(self)}") ######## # Rank # @@ -2780,7 +2780,7 @@ def destandardize(self, weight, ordered_alphabet=None): for a in weight: partial.append(partial[-1]+a) if not set(ides).issubset(set(partial)): - raise ValueError("Standardization with weight {} is not possible!".format(weight)) + raise ValueError(f"Standardization with weight {weight} is not possible!") if ordered_alphabet is None: ordered_alphabet = list(range(1,len(weight)+1)) else: @@ -4221,7 +4221,7 @@ def right_permutohedron_interval_iterator(self, other): [2, 1, 5, 4, 3], [2, 5, 1, 4, 3], [2, 5, 4, 1, 3]] """ if len(self) != len(other): - raise ValueError("len({}) and len({}) must be equal".format(self, other)) + raise ValueError(f"len({self}) and len({other}) must be equal") if not self.permutohedron_lequal(other): raise ValueError("{} must be lower or equal than {} for the right permutohedron order".format(self, other)) d = DiGraph() @@ -6079,7 +6079,7 @@ def _repr_(self) -> str: sage: Permutations(3,2) Permutations of {1,...,3} of length 2 """ - return "Permutations of {1,...,%s} of length %s" % (self.n, self._k) + return f"Permutations of {{1,...,{self.n}}} of length {self._k}" def __iter__(self) -> Iterator[Permutation]: """ @@ -6710,7 +6710,7 @@ def _repr_(self): sage: Permutations([1,2,2], 2) Permutations of the multi-set [1, 2, 2] of length 2 """ - return "Permutations of the multi-set %s of length %s" % (list(self.mset), self._k) + return f"Permutations of the multi-set {list(self.mset)} of length {self._k}" def cardinality(self): """ @@ -6797,8 +6797,7 @@ def _repr_(self): sage: repr(Permutations([1,2,4],2)) 'Permutations of the set [1, 2, 4] of length 2' """ - return "Permutations of the set %s of length %s" % (list(self._set), - self._k) + return f"Permutations of the set {list(self._set)} of length {self._k}" def __iter__(self): """ @@ -6914,7 +6913,7 @@ def _repr_(self): sage: Arrangements([1,2,2],2) Arrangements of the multi-set [1, 2, 2] of length 2 """ - return "Arrangements of the multi-set %s of length %s" % (list(self.mset), self._k) + return f"Arrangements of the multi-set {list(self.mset)} of length {self._k}" class Arrangements_setk(Arrangements, Permutations_setk): @@ -6929,8 +6928,7 @@ def _repr_(self): sage: Arrangements([1,2,3],2) Arrangements of the set [1, 2, 3] of length 2 """ - return "Arrangements of the set %s of length %s" % (list(self._set), - self._k) + return f"Arrangements of the set {list(self._set)} of length {self._k}" ############################################################### # Standard permutations @@ -7420,8 +7418,8 @@ def element_in_conjugacy_classes(self, nu): from sage.combinat.partition import Partition nu = Partition(nu) if nu.size() > self.n: - raise ValueError("the size of the partition (=%s) should be at most" - " the size of the permutations (=%s)" % (nu.size(), self.n)) + raise ValueError("the size of the partition (={}) should be at most" + " the size of the permutations (={})".format(nu.size(), self.n)) l = [] i = 0 for nui in nu: @@ -8307,7 +8305,7 @@ def _repr_(self): sage: Permutations(descents=([1,0,4,8],12)) Standard permutations of 12 with descents [0, 1, 4, 8] """ - return "Standard permutations of %s with descents %s" % (self.n, list(self._d)) + return f"Standard permutations of {self.n} with descents {list(self._d)}" def cardinality(self): """ @@ -9239,8 +9237,7 @@ def _repr_(self): sage: CyclicPermutationsOfPartition([[1,2,3,4],[5,6,7]]) Cyclic permutations of partition [[1, 2, 3, 4], [5, 6, 7]] """ - return "Cyclic permutations of partition {}".format( - [list(_) for _ in self.partition]) + return f"Cyclic permutations of partition {[list(_) for _ in self.partition]}" def __iter__(self, distinct=False): """ @@ -9498,7 +9495,7 @@ def _repr_(self): sage: Permutations(3, avoiding=[[2, 1, 3],[1,2,3]]) Standard permutations of 3 avoiding [[2, 1, 3], [1, 2, 3]] """ - return "Standard permutations of %s avoiding %s" % (self.n, list(self._a)) + return f"Standard permutations of {self.n} avoiding {list(self._a)}" def __iter__(self): """ diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 33de9927c12..535787875b5 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -1549,7 +1549,7 @@ def to_virtual(self, rc): for a,rp in enumerate(rc): g = gamma[a+1] for i in sigma[a+1]: - partitions[i-1] = RiggedPartition([row_len for row_len in rp._list], + partitions[i-1] = RiggedPartition(list(rp._list), [rig_val*g for rig_val in rp.rigging], [vac_num*g for vac_num in rp.vacancy_numbers]) return self.virtual.element_class(self.virtual, partitions, use_vacancy_numbers=True) @@ -1585,7 +1585,7 @@ def from_virtual(self, vrc): for a in range(n): rp = vrc[sigma[a+1][0] - 1] g = gamma[a+1] - partitions[a] = RiggedPartition([row_len for row_len in rp._list], + partitions[a] = RiggedPartition(list(rp._list), [rig_val//g for rig_val in rp.rigging], [vac_val//g for vac_val in rp.vacancy_numbers]) return self.element_class(self, partitions, use_vacancy_numbers=True) @@ -1874,7 +1874,7 @@ def to_virtual(self, rc): for a,rp in enumerate(rc): g = gammatilde[a+1] for i in sigma[a+1]: - partitions[i-1] = RiggedPartition([row_len for row_len in rp._list], + partitions[i-1] = RiggedPartition(list(rp._list), [rig_val*g for rig_val in rp.rigging]) return self.virtual.element_class(self.virtual, partitions) @@ -1910,7 +1910,7 @@ def from_virtual(self, vrc): for a in range(n): rp = vrc[sigma[a+1][0] - 1] g = gammatilde[a+1] - partitions[a] = RiggedPartition([row_len for row_len in rp._list], + partitions[a] = RiggedPartition(list(rp._list), [rig_val/g for rig_val in rp.rigging]) return self.element_class(self, partitions) diff --git a/src/sage/combinat/root_system/fundamental_group.py b/src/sage/combinat/root_system/fundamental_group.py index 8905471a5ab..6dc13e302ec 100644 --- a/src/sage/combinat/root_system/fundamental_group.py +++ b/src/sage/combinat/root_system/fundamental_group.py @@ -393,7 +393,7 @@ def leading_support(beta): cartan_type = cartan_type.dual() if cartan_type.is_untwisted_affine(): cartan_type_classical = cartan_type.classical() - I = [i for i in cartan_type_classical.index_set()] + I = list(cartan_type_classical.index_set()) Q = RootSystem(cartan_type_classical).root_lattice() alpha = Q.simple_roots() omega = RootSystem(cartan_type_classical).weight_lattice().fundamental_weights() diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index 1dc8d16ad59..d5eb99adbb7 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -637,8 +637,8 @@ def Y_lambdacheck(self, lambdacheck): # works for our two main examples (action of affine W on W, # and Macdonald polynomials) if self._side == "left": - word = tuple([x for x in reversed(word)]) - signs = tuple([x for x in reversed(signs)]) + word = tuple(reversed(word)) + signs = tuple(reversed(signs)) # The power of q implements the fact that Y^\deltacheck = 1/q. # The classical simple coroots have no \deltacheck term. # alpha[0] has a \deltacheck with coefficient one diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py index 5d591febe43..483c510eb61 100644 --- a/src/sage/combinat/root_system/plot.py +++ b/src/sage/combinat/root_system/plot.py @@ -1412,7 +1412,7 @@ def cone(self, rays=[], lines=[], color="black", thickness=1, alpha=1, wireframe # TODO: we currently convert lines into rays, which simplify a # bit the calculation of the intersection. But it would be # nice to benefit from the new ``lines`` option of Polyhedra - rays = list(rays) + [ray for ray in lines] + [-ray for ray in lines] + rays = list(rays) + list(lines) + [-ray for ray in lines] # Compute the intersection at level 1, if needed if self.level: diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 0b80aa73a71..3287b516d08 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -499,7 +499,7 @@ def distinguished_reflections(self): """ # makes sure that the simple reflections come first gens = self.gens() - R = [t for t in gens] + R = list(gens) # Then import all distinguished reflections from gap, # the Set is used as every such appears multiple times. for r in self._gap_group.Reflections(): diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index ea1f4d285b2..f03d1d44e7f 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -18,6 +18,7 @@ from sage.misc.lazy_import import LazyImport from sage.categories.coxeter_groups import CoxeterGroups from sage.categories.category_types import Category_over_base_ring +from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.modules_with_basis import ModulesWithBasis from sage.structure.element import Element from sage.sets.family import Family @@ -715,7 +716,8 @@ def positive_roots(self, index_set=None): index_set = tuple(self.cartan_type().index_set()) return RecursivelyEnumeratedSet([self.simple_root(i) for i in index_set], attrcall('pred', index_set=index_set), - structure='graded', enumeration='breadth') + structure='graded', enumeration='breadth', + category=EnumeratedSets().Finite()) @cached_method def nonparabolic_positive_roots(self, index_set=None): @@ -1229,7 +1231,7 @@ def negative_roots(self): """ if not self.cartan_type().is_finite(): raise ValueError("%s is not a finite Cartan type" % self.cartan_type()) - return self.positive_roots().map(attrcall('__neg__')) + return self.positive_roots().map(attrcall('__neg__'), is_injective=True) ########################################################################## # coroots @@ -4279,7 +4281,7 @@ def weyl_action(self, element, inverse=False): # TODO, some day: accept an iterator if isinstance(element, (tuple, list, range)): # Action by a (reduced) word - the_word = [x for x in element] + the_word = list(element) I = self.parent().index_set() if not all(i in I for i in the_word): raise ValueError("Not all members of %s are in the index set of the %s" % (element, self.parent())) diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index a375b3b8074..54d3c3d71f2 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -443,7 +443,7 @@ def max_quantum_element(self): word = [] while self != Qvee.zero(): beta = self.max_coroot_le() - word += [x for x in beta.associated_reflection()] + word += list(beta.associated_reflection()) self = self - beta.associated_coroot() W = self.parent().weyl_group() return (W.demazure_product(word)).reduced_word() diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index d2d9d6385e5..aab1cf6d2bb 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -246,6 +246,10 @@ def is_SymmetricFunctionAlgebra(x): sage: from sage.combinat.sf.sfa import is_SymmetricFunctionAlgebra sage: is_SymmetricFunctionAlgebra(5) + doctest:warning... + DeprecationWarning: the function is_SymmetricFunctionAlgebra is deprecated; + use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead + See https://github.com/sagemath/sage/issues/37896 for details. False sage: is_SymmetricFunctionAlgebra(ZZ) False @@ -258,6 +262,8 @@ def is_SymmetricFunctionAlgebra(x): sage: is_SymmetricFunctionAlgebra(SymmetricFunctions(FractionField(QQ['q','t'])).macdonald().P()) True """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_SymmetricFunctionAlgebra is deprecated; use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead") return isinstance(x, SymmetricFunctionAlgebra_generic) diff --git a/src/sage/combinat/sf/witt.py b/src/sage/combinat/sf/witt.py index c856e8fe399..cf14fa826c6 100644 --- a/src/sage/combinat/sf/witt.py +++ b/src/sage/combinat/sf/witt.py @@ -666,7 +666,7 @@ def _omega_even(self, lam_even): True """ dct = self._h(self.monomial(lam_even)).monomial_coefficients(copy=False) - eelt = self._e.element_class(self._e, {mu: c for mu, c in dct.items()}) + eelt = self._e.element_class(self._e, dict(dct.items())) return self(eelt) class Element(multiplicative.SymmetricFunctionAlgebra_multiplicative.Element): diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index a05a8b0bee8..b678c84171a 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -357,7 +357,7 @@ def _repr_list(self): sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list() "[(None, None, 2', 3), (2, 2)]" """ - return repr([row for row in self]) + return repr(list(self)) def _repr_tab(self): """ diff --git a/src/sage/combinat/species/structure.py b/src/sage/combinat/species/structure.py index b3003ed0f13..c5629212f5d 100644 --- a/src/sage/combinat/species/structure.py +++ b/src/sage/combinat/species/structure.py @@ -37,8 +37,10 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.combinat.combinat import CombinatorialClass, CombinatorialObject +from sage.categories.enumerated_sets import EnumeratedSets +from sage.combinat.combinat import CombinatorialObject from sage.rings.integer import Integer +from sage.structure.parent import Parent from copy import copy @@ -326,7 +328,7 @@ def change_labels(self, labels): ############################################################## -class SpeciesWrapper(CombinatorialClass): +class SpeciesWrapper(Parent): def __init__(self, species, labels, iterator, generating_series, name, structure_class): """ This is a abstract base class for the set of structures of a @@ -350,6 +352,7 @@ def __init__(self, species, labels, iterator, generating_series, name, structure sage: S.cardinality() 1 """ + Parent.__init__(self, category=EnumeratedSets().Finite()) self._species = species self._labels = labels self._iterator = iterator @@ -357,6 +360,46 @@ def __init__(self, species, labels, iterator, generating_series, name, structure self._name = "%s for %s with labels %s" % (name, species, labels) self._structure_class = structure_class if structure_class is not None else species._default_structure_class + def __eq__(self, other) -> bool: + r""" + EXAMPLES:: + + sage: from sage.combinat.species.structure import SpeciesWrapper + sage: F = species.SetSpecies() + sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None) + sage: S == SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None) + True + """ + return ((self._species, self._labels, + self._iterator, self._generating_series, + self._name, self._structure_class) == (other._species, other._labels, + other._iterator, other._generating_series, + other._name, other._structure_class)) + + def __ne__(self, other) -> bool: + r""" + EXAMPLES:: + + sage: from sage.combinat.species.structure import SpeciesWrapper + sage: F = species.SetSpecies() + sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None) + sage: S != SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None) + False + """ + return not (self == other) + + def _repr_(self) -> str: + """ + EXAMPLES:: + + sage: from sage.combinat.species.structure import SpeciesWrapper + sage: F = species.SetSpecies() + sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None) + sage: repr(S) # indirect doctest + 'Structures for Set species with labels [1, 2, 3]' + """ + return self._name + def labels(self): """ Returns the labels used on these structures. If `X` is the diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index 3b7673a5425..f79413e2be3 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -1244,6 +1244,100 @@ def _blocks_dictionary(self): blocks[c] = [la] return blocks + def ladder_idemponent(self, la): + r""" + Return the ladder idempontent of ``self``. + + Let `F` be a field of characteristic `p`. The *ladder idempotent* + of shape `\lambda` is the idempotent of `F[S_n]` defined as follows. + Let `T` be the :meth:`ladder tableau + ` of shape `\lambda`. + Let `[T]` be the set of standard tableaux whose residue sequence + is the same as for `T`. Let `\alpha` be the sizes of the ladders + of `\lambda`. Then the ladder idempontent is constructed as + + .. MATH:: + + \widetilde{e}_{\lambda} := \frac{1}{\alpha!} + \left( \sum_{\sigma \in S_{\alpha}} \sigma \right) + \left( \overline{\sum_{U \in [T]} E_U} \right), + + where `E_{UU}` is the :meth:`seminormal_basis` element over `\QQ` + and we project the sum to `F`, `S_{\alpha}` is the Young subgroup + corresponding to `\alpha`, and `\alpha! = \alpha_1! \cdots \alpha_k!`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: for la in Partitions(SGA.n): + ....: idem = SGA.ladder_idemponent(la) + ....: print(la) + ....: print(idem) + ....: assert idem^2 == idem + [4] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1] + [3, 1] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + [3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + [4, 3, 2, 1] + [2, 2] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1] + [2, 1, 1] + 2*[1, 2, 3, 4] + [1, 2, 4, 3] + 2*[1, 3, 2, 4] + [1, 3, 4, 2] + + [1, 4, 2, 3] + 2*[1, 4, 3, 2] + 2*[2, 1, 3, 4] + [2, 1, 4, 3] + + 2*[2, 3, 1, 4] + [2, 3, 4, 1] + [2, 4, 1, 3] + 2*[2, 4, 3, 1] + + 2*[3, 1, 2, 4] + [3, 1, 4, 2] + 2*[3, 2, 1, 4] + [3, 2, 4, 1] + + [4, 1, 2, 3] + 2*[4, 1, 3, 2] + [4, 2, 1, 3] + 2*[4, 2, 3, 1] + [1, 1, 1, 1] + 2*[1, 2, 3, 4] + [1, 2, 4, 3] + [2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + 2*[4, 3, 2, 1] + + When `p = 0`, these idempotents will generate all of the simple + modules (which are the :meth:`Specht modules ` + and also projective modules):: + + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: for la in Partitions(SGA.n): + ....: idem = SGA.ladder_idemponent(la) + ....: assert idem^2 == idem + ....: print(la, SGA.principal_ideal(idem).dimension()) + [5] 1 + [4, 1] 4 + [3, 2] 5 + [3, 1, 1] 6 + [2, 2, 1] 5 + [2, 1, 1, 1] 4 + [1, 1, 1, 1, 1] 1 + sage: [StandardTableaux(la).cardinality() for la in Partitions(SGA.n)] + [1, 4, 5, 6, 5, 4, 1] + + REFERENCES: + + - [Ryom2015]_ + """ + R = self.base_ring() + p = R.characteristic() + n = self.n + if not p: + p = n + 1 + la = _Partitions(la) + if sum(la) != n: + raise ValueError(f"{la} is not a partition of {n}") + Tlad, alpha = la.ladder_tableau(p, ladder_lengths=True) + if not all(val < p for val in alpha): + raise ValueError(f"{la} is not {p}-ladder restricted") + Tclass = Tlad.residue_sequence(p).standard_tableaux() + Elad = sum(epsilon_ik(T, T) for T in Tclass) + Elad = self.element_class(self, {sigma: R(c) for sigma, c in Elad._monomial_coefficients.items()}) + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + YG = SymmetricGroup(n).young_subgroup(alpha) + coeff = ~R.prod(factorial(val) for val in alpha) + G = self.group() + eprod = self.element_class(self, {G(list(elt.tuple())): coeff + for elt in YG}) + return Elad * eprod + @cached_method def algebra_generators(self): r""" diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index bb1aeaf28be..fda9458f0f2 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1381,7 +1381,7 @@ def schuetzenberger_involution(self, n=None, check=True): t = t.bump(k) if isinstance(self, StandardTableau): return StandardTableau(list(t)) - elif isinstance(self, SemistandardTableau): + if isinstance(self, SemistandardTableau): return SemistandardTableau(list(t)) return t @@ -1910,7 +1910,7 @@ def vertical_flip(self): if not self.is_rectangular(): raise TypeError("the tableau must be rectangular to use vertical_flip()") - return Tableau([row for row in reversed(self)]) + return Tableau(list(reversed(self))) def rotate_180(self): """ @@ -1926,7 +1926,7 @@ def rotate_180(self): if not self.is_rectangular(): raise TypeError("the tableau must be rectangular to use rotate_180()") - return Tableau([[rline for rline in reversed(row)] for row in reversed(self)]) + return Tableau([list(reversed(row)) for row in reversed(self)]) def cells(self): """ @@ -2447,7 +2447,7 @@ def insert_word(self, w, left=False): [[1, 3], [2, 5], [4]] """ if left: - w = [i for i in reversed(w)] + w = list(reversed(w)) res = self for i in w: res = res.schensted_insert(i, left=left) @@ -3891,7 +3891,7 @@ def flush(self): for s in S: if (s[0][0] != len(self)-1 and s[1] == len(self[s[0][0]+1]) and self[s[0][0]+1][-1] <= s[0][1]) \ - or (s[0][0] == len(self)-1 and s[1] == 0): + or (s[0][0] == len(self)-1 and s[1] == 0): f += 1 else: for t in S: @@ -4491,7 +4491,7 @@ def __classcall_private__(self, t): """ if isinstance(t, SemistandardTableau): return t - elif t in SemistandardTableaux(): + if t in SemistandardTableaux(): return SemistandardTableaux_all().element_class(SemistandardTableaux_all(), t) # t is not a semistandard tableau so we give an appropriate error message @@ -4676,9 +4676,9 @@ def check(self): super().check() # We have checked that t is tableau, so it remains to check that # the entries of t are positive integers that increase along rows. - flatx = sorted(sum((list(row) for row in self), [])) + flatx = sorted(c for row in self for c in row) if (flatx != list(range(1, len(flatx)+1)) - or any(row[i] >= row[i+1] for row in self for i in range(len(row)-1))): + or any(row[i] >= row[i+1] for row in self for i in range(len(row)-1))): raise ValueError("the entries in a row standard tableau must increase" " along rows and contain the numbers 1,2,...,n") @@ -5657,13 +5657,13 @@ class options(GlobalOptions): alias=dict(array="diagram", ferrers_diagram="diagram", young_diagram="diagram"), case_sensitive=False) convention = dict(default="English", - description='Sets the convention used for displaying tableaux and partitions', - values=dict( - English='use the English convention', - French='use the French convention', - Russian='use the Russian convention', - ), - case_sensitive=False) + description='Sets the convention used for displaying tableaux and partitions', + values=dict( + English='use the English convention', + French='use the French convention', + Russian='use the Russian convention', + ), + case_sensitive=False) notation = dict(alt_name="convention") def _element_constructor_(self, t): @@ -5726,7 +5726,7 @@ def __contains__(self, x): from sage.combinat.partition import _Partitions if isinstance(x, Tableau): return True - elif isinstance(x, list): + if isinstance(x, list): try: for row in x: iter(row) @@ -6062,7 +6062,7 @@ def __classcall_private__(cls, *args, **kwargs): if size is not None: if not isinstance(size, (int, Integer)): raise ValueError("size must be an integer") - elif size < 0: + if size < 0: raise ValueError("size must be non-negative") if shape is not None: @@ -6269,9 +6269,9 @@ def __contains__(self, t): return (self.max_entry is None or len(t) == 0 or max(max(row) for row in t) <= self.max_entry) - elif not t: + if not t: return True - elif Tableaux.__contains__(self, t): + if Tableaux.__contains__(self, t): for row in t: if not all(c > 0 for c in row): return False @@ -6648,7 +6648,7 @@ def random_element(self): tot += weights[pos] # we now have pos elements over the diagonal and n - 2 * pos on it m = diagonal_matrix(list(IntegerVectors(self.size - 2 * pos, - self.max_entry).random_element())) + self.max_entry).random_element())) above_diagonal = list(IntegerVectors(pos, kchoose2m1 + 1).random_element()) index = 0 for i in range(self.max_entry - 1): @@ -7309,10 +7309,10 @@ def __contains__(self, x): """ if isinstance(x, RowStandardTableau): return True - elif Tableaux.__contains__(self, x): - flatx = sorted(sum((list(row) for row in x), [])) + if Tableaux.__contains__(self, x): + flatx = sorted(c for row in x for c in row) return (flatx == list(range(1, len(flatx)+1)) - and all(row[i] < row[i+1] for row in x for i in range(len(row)-1))) + and all(row[i] < row[i+1] for row in x for i in range(len(row)-1))) return False @@ -7561,7 +7561,7 @@ def cardinality(self): sage: RowStandardTableaux([]).cardinality() 1 """ - return Integer(multinomial([m for m in self.shape])) + return Integer(multinomial(list(self.shape))) ######################## @@ -7710,12 +7710,12 @@ def __contains__(self, x): if isinstance(x, StandardTableau): return True elif Tableaux.__contains__(self, x): - flatx = sorted(sum((list(row) for row in x), [])) + flatx = sorted(c for row in x for c in row) return flatx == list(range(1, len(flatx)+1)) and (len(x) == 0 or (all(row[i] < row[i+1] for row in x for i in range(len(row)-1)) and - all(x[r][c] < x[r+1][c] for r in range(len(x)-1) - for c in range(len(x[r+1]))) - )) + all(x[r][c] < x[r+1][c] for r in range(len(x)-1) + for c in range(len(x[r+1]))) + )) return False @@ -7936,7 +7936,7 @@ def random_element(self): # We add the number of involutions with ``fixed_point_number`` # fixed points. partial_sum += binomial(self.size, fixed_point_number) * \ - prod(range(1, self.size - fixed_point_number, 2)) + prod(range(1, self.size - fixed_point_number, 2)) # If the partial sum is greater than the involution index, # then the random involution that we want to generate has # ``fixed_point_number`` fixed points. @@ -7954,7 +7954,7 @@ def random_element(self): matching = PerfectMatchings(set(range(1, self.size + 1)) - set(fixed_point_positions)).random_element() permutation_cycle_rep = ([(fixed_point,) for fixed_point in fixed_point_positions] - + [(a, b) for a, b in matching]) + + [tuple(ab) for ab in matching]) return from_cycles(self.size, permutation_cycle_rep).robinson_schensted()[0] @@ -8198,7 +8198,7 @@ def list(self): [[1, 2, 4], [3, 5], [6]], [[1, 2, 3], [4, 5], [6]]] """ - return [y for y in self] + return list(self) def random_element(self): """ diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 1bdd38e92c7..1b5dcb3b03e 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -293,7 +293,7 @@ def _repr_(self): if word_options['old_repr']: if word_options['truncate'] and \ self.length() > word_options['truncate_length']: - return "Finite word of length %s over %s" % (self.length(), str(self.parent().alphabet())[17:]) + return "Finite word of length {} over {}".format(self.length(), str(self.parent().alphabet())[17:]) return word_options['identifier'] + self.string_rep() def coerce(self, other): @@ -331,7 +331,7 @@ def coerce(self, other): self = other.parent()(self) self.parent()._check(self, length=None) except Exception: - raise TypeError("no coercion rule between %r and %r" % (self.parent(), other.parent())) + raise TypeError("no coercion rule between {!r} and {!r}".format(self.parent(), other.parent())) return self, other def __hash__(self): @@ -1261,7 +1261,7 @@ def number_of_factors(self, n=None, algorithm='suffix tree'): elif algorithm == 'naive': return ZZ(len(self.factor_set(n, algorithm='naive'))) else: - raise ValueError('Unknown algorithm (={})'.format(algorithm)) + raise ValueError(f'Unknown algorithm (={algorithm})') def factor_iterator(self, n=None): r""" @@ -1437,7 +1437,7 @@ def factor_set(self, n=None, algorithm='suffix tree'): S.add(self[i:i+n]) return Set(S) else: - raise ValueError('Unknown algorithm (={})'.format(algorithm)) + raise ValueError(f'Unknown algorithm (={algorithm})') def topological_entropy(self, n): r""" @@ -2067,8 +2067,7 @@ def _conjugates_list(self): [word: a] """ S = [self] - for i in range(1, self.length()): - S.append(self.conjugate(i)) + S.extend(self.conjugate(i) for i in range(1, self.length())) return S def conjugates_iterator(self): @@ -2853,9 +2852,9 @@ def length_maximal_palindrome(self, j, m=None, f=None): # Initialize the next (left) position to check i = (jj - m - 1) / 2 if not i.is_integer(): - raise ValueError("(2*j-m-1)/2(={}) must be an integer, i.e., " - "2*j(={}) and m(={}) can't " - "have the same parity".format(i, jj, m)) + raise ValueError(f"(2*j-m-1)/2(={i}) must be an integer, i.e., " + f"2*j(={jj}) and m(={m}) can't " + "have the same parity") i = Integer(i) # Compute @@ -2970,8 +2969,7 @@ def lps_lengths(self, f=None): for j in range(1, 2 * len(self) + 1): Nj = j + LPC[j] if Nj > Nk: - for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2): - LPS.append(i - j) + LPS.extend(i - j for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2)) Nk = Nj return LPS @@ -3858,8 +3856,7 @@ def subword_complementaries(self, other): if len(m) == 1: temp.append([j]+m[0]) if len(m) > 1: - for sw in m: - temp.append([j]+sw) + temp.extend([j] + sw for sw in m) Mpos[i][j] = temp # Create the list of positions for occurrences of `self` as a subword @@ -3875,7 +3872,7 @@ def subword_complementaries(self, other): comp_words.append(Word([other[i] for i in comp_pos])) return comp_words - def is_lyndon(self): + def is_lyndon(self) -> bool: r""" Return ``True`` if ``self`` is a Lyndon word, and ``False`` otherwise. diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 9424a3370b7..46207beab1c 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -883,18 +883,15 @@ def _latex_(self): A = self.domain().alphabet() latex_layout = self.latex_layout() if latex_layout == 'oneliner': - L = [r"%s \mapsto %s" % (a, self.image(a)) for a in A] - return LatexExpr(r','.join(L)) - elif latex_layout == 'array': + lines = (fr"{a} \mapsto {self.image(a)}" for a in A) + return LatexExpr(r','.join(lines)) + if latex_layout == 'array': s = r"\begin{array}{l}" + '\n' - lines = [] - for a in A: - lines.append(r"%s \mapsto %s" % (a, self.image(a))) + lines = (fr"{a} \mapsto {self.image(a)}" for a in A) s += '\\\\\n'.join(lines) s += '\n' + r"\end{array}" return LatexExpr(s) - else: - raise ValueError('unknown latex_layout(=%s)' % latex_layout) + raise ValueError('unknown latex_layout(=%s)' % latex_layout) def __mul__(self, other): r""" @@ -1693,8 +1690,7 @@ def is_prolongable(self, letter): TypeError: codomain of self must be an instance of Words """ if letter not in self.domain().alphabet(): - raise TypeError("letter (=%s) is not in the domain alphabet (=%s)" - % (letter, self.domain().alphabet())) + raise TypeError("letter (={}) is not in the domain alphabet (={})".format(letter, self.domain().alphabet())) image = self.image(letter) return not image.is_empty() and letter == image[0] @@ -1893,11 +1889,9 @@ def fixed_points(self): [] """ - L = [] - for letter in self.domain().alphabet(): - if self.is_prolongable(letter=letter): - L.append(self.fixed_point(letter=letter)) - return L + return [self.fixed_point(letter=letter) + for letter in self.domain().alphabet() + if self.is_prolongable(letter=letter)] def periodic_point(self, letter): r""" diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index f5506152d44..21709124f64 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -1489,11 +1489,11 @@ def is_tangent(self): class FiniteWordPath_2d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red', thickness=3), - fill=True, filloptions=dict(rgbcolor='red', alpha=0.2), - startpoint=True, startoptions=dict(rgbcolor='red', pointsize=100), - endarrow=True, arrowoptions=dict(rgbcolor='red', arrowsize=20, width=3), - gridlines=False, gridoptions=dict()): + def plot(self, pathoptions={"rgbcolor": 'red', "thickness": 3}, + fill=True, filloptions={"rgbcolor": 'red', "alpha": 0.2}, + startpoint=True, startoptions={"rgbcolor": 'red', "pointsize": 100}, + endarrow=True, arrowoptions={"rgbcolor": 'red', "arrowsize": 20, "width": 3}, + gridlines=False, gridoptions={}): r""" Return a 2d Graphics illustrating the path. @@ -1698,7 +1698,7 @@ def animate(self): return animate(images, **kwds) - def plot_directive_vector(self, options=dict(rgbcolor='blue')): + def plot_directive_vector(self, options={"rgbcolor": 'blue'}): r""" Return an arrow 2d graphics that goes from the start of the path to the end. @@ -2003,8 +2003,8 @@ def ymax(self): class FiniteWordPath_3d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red', arrow_head=True, thickness=3), - startpoint=True, startoptions=dict(rgbcolor='red', size=10)): + def plot(self, pathoptions={"rgbcolor": 'red', "arrow_head": True, "thickness": 3}, + startpoint=True, startoptions={"rgbcolor": 'red', "size": 10}): r""" INPUT: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 8c56ba3d4b3..c2bda4caf96 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -1740,7 +1740,7 @@ def treat_node(current_node, parent): D = self.transition_function_dictionary() string_depth = {0: 0} n = len(self.word()) - labeling = dict() + labeling = {} treat_node(0, None) return labeling @@ -1827,7 +1827,7 @@ def treat_node(current_node, i, j): walk_chain(current_node, child, l, square_start) prelabeling = self._partial_labeling() - labeling = dict() + labeling = {} D = self.transition_function_dictionary() treat_node(0, 0, 0) return labeling diff --git a/src/sage/combinat/yang_baxter_graph.py b/src/sage/combinat/yang_baxter_graph.py index f2fdb7a5a47..6b7684e077a 100644 --- a/src/sage/combinat/yang_baxter_graph.py +++ b/src/sage/combinat/yang_baxter_graph.py @@ -166,7 +166,7 @@ def _successors(self, u): if v != u: yield (v, op) - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -176,7 +176,7 @@ def __repr__(self): sage: Y.__repr__() 'Yang-Baxter graph with root vertex (1, 2, 3)' """ - return "Yang-Baxter graph with root vertex %s" % (self._root,) + return f"Yang-Baxter graph with root vertex {self._root}" @lazy_attribute def _digraph(self): @@ -216,7 +216,7 @@ def __hash__(self): # used in containers but are mutable. return hash(self._digraph.copy(immutable=True)) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" EXAMPLES:: @@ -238,7 +238,7 @@ def __eq__(self, other): """ return type(self) is type(other) and self._digraph == other._digraph - def __ne__(self, other): + def __ne__(self, other) -> bool: r""" Test non-equality. @@ -369,7 +369,7 @@ def root(self): """ return self._root - def successors(self, v): + def successors(self, v) -> list: r""" Return the successors of the vertex ``v``. @@ -405,7 +405,7 @@ def plot(self, *args, **kwds): kwds["vertex_labels"] = True return self._digraph.plot(*args, **kwds) - def vertices(self, sort=False): + def vertices(self, sort=False) -> list: r""" Return the vertices of ``self``. @@ -439,7 +439,7 @@ def edges(self): """ return self._digraph.edges(sort=True) - def vertex_relabelling_dict(self, v, relabel_operator): + def vertex_relabelling_dict(self, v, relabel_operator) -> dict: r""" Return a dictionary pairing vertices ``u`` of ``self`` with the object obtained from ``v`` by applying the @@ -590,7 +590,7 @@ def __init__(self, partition): for i in range(sum(partition) - 1)] super().__init__(root, operators) - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -598,7 +598,7 @@ def __repr__(self): sage: Y.__repr__() # needs sage.combinat 'Yang-Baxter graph of [3, 2], with top vertex (1, 0, 2, 1, 0)' """ - return "Yang-Baxter graph of %s, with top vertex %s" % (self._partition, self._root) + return f"Yang-Baxter graph of {self._partition}, with top vertex {self._root}" def __copy__(self): r""" @@ -695,7 +695,7 @@ def _swap_operator(self, operator, u): """ return operator(u) - def vertex_relabelling_dict(self, v): + def vertex_relabelling_dict(self, v) -> dict: r""" Return a dictionary pairing vertices ``u`` of ``self`` with the object obtained from ``v`` by applying transpositions corresponding to the @@ -792,7 +792,7 @@ def __hash__(self): """ return hash(self._position) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" Compare two swap operators. @@ -811,7 +811,7 @@ def __eq__(self, other): return False return self._position == other._position - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Check whether ``self`` is not equal to ``other``. @@ -826,7 +826,7 @@ def __ne__(self, other): """ return not (self == other) - def __repr__(self): + def __repr__(self) -> str: r""" Representation string. @@ -840,7 +840,7 @@ def __repr__(self): pos = self._position return f"Swap positions {pos} and {pos + 1}" - def __str__(self): + def __str__(self) -> str: r""" A short str representation (used, for example, in labelling edges of graphs). @@ -889,7 +889,7 @@ def position(self): class SwapIncreasingOperator(SwapOperator): - def __repr__(self): + def __repr__(self) -> str: r""" Representation string. diff --git a/src/sage/data_structures/bitset.pyx b/src/sage/data_structures/bitset.pyx index 8a5e91c0a9a..e90ffea8fca 100644 --- a/src/sage/data_structures/bitset.pyx +++ b/src/sage/data_structures/bitset.pyx @@ -72,7 +72,7 @@ cdef class FrozenBitset: - string -- If a nonempty string, then the bitset is initialized by including an element if the index of the string is ``1``. If the - string is empty, then raise a ``ValueError``. + string is empty, then raise a :class:`ValueError`. - iterable -- If an iterable, then it is assumed to contain a list of nonnegative integers and those integers are placed in the set. @@ -1798,8 +1798,10 @@ cdef class Bitset(FrozenBitset): cpdef remove(self, unsigned long n): """ - Update the bitset by removing ``n``. Raises ``KeyError`` if ``n`` is - not contained in the bitset. + Update the bitset by removing ``n``. + + This raises a :class:`KeyError` if ``n`` is not contained + in the bitset. EXAMPLES:: @@ -1871,8 +1873,9 @@ cdef class Bitset(FrozenBitset): cpdef pop(self): """ - Remove and return an arbitrary element from the set. Raises - ``KeyError`` if the set is empty. + Remove and return an arbitrary element from the set. + + This raises a :class:`KeyError` if the set is empty. EXAMPLES:: @@ -1899,7 +1902,7 @@ cdef class Bitset(FrozenBitset): cpdef clear(self): """ - Removes all elements from the bitset. + Remove all elements from the bitset. EXAMPLES:: diff --git a/src/sage/data_structures/bitset_base.pxd b/src/sage/data_structures/bitset_base.pxd index df40d666d86..f8949d05e78 100644 --- a/src/sage/data_structures/bitset_base.pxd +++ b/src/sage/data_structures/bitset_base.pxd @@ -455,7 +455,9 @@ cdef inline bint bitset_not_in(fused_bitset_t bits, mp_bitcnt_t n) noexcept: cdef inline bint bitset_remove(fused_bitset_t bits, mp_bitcnt_t n) except -1: """ - Remove n from bits. Raise KeyError if n is not contained in bits. + Remove ``n`` from ``bits``. + + This raises a :class:`KeyError` if ``n`` is not contained in ``bits``. """ if not bitset_in(bits, n): raise KeyError(n) @@ -558,8 +560,9 @@ cdef inline long bitset_first_in_complement(fused_bitset_t a) noexcept: cdef inline long bitset_pop(fused_bitset_t a) except -1: """ - Remove and return an arbitrary element from the set. Raise - KeyError if the set is empty. + Remove and return an arbitrary element from the set. + + This raises a :class:`KeyError` if the set is empty. """ cdef long i = bitset_first(a) if i == -1: diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 03b5a7019c2..72ec82d3bb4 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2299,21 +2299,21 @@ def remove(self, key, raise_key_error=True): - ``key`` -- the key of an object. - ``raise_key_error`` -- (default: ``True``) switch raising - ``KeyError`` on and off. + :class:`KeyError` on and off. OUTPUT: Nothing. If the element is not a member and ``raise_key_error`` is set - (default), raise a ``KeyError``. + (default), raise a :class:`KeyError`. .. NOTE:: As with Python's ``set``, the methods :meth:`remove` and :meth:`discard` only differ in their behavior when an element is not contained in the poset: :meth:`remove` - raises a ``KeyError`` whereas :meth:`discard` does not + raises a :class:`KeyError` whereas :meth:`discard` does not raise any exception. This default behavior can be overridden with the @@ -2479,21 +2479,21 @@ def discard(self, key, raise_key_error=False): - ``key`` -- the key of an object. - ``raise_key_error`` -- (default: ``False``) switch raising - ``KeyError`` on and off. + :class:`KeyError` on and off. OUTPUT: Nothing. If the element is not a member and ``raise_key_error`` is set - (not default), raise a ``KeyError``. + (not default), raise a :class:`KeyError`. .. NOTE:: As with Python's ``set``, the methods :meth:`remove` and :meth:`discard` only differ in their behavior when an element is not contained in the poset: :meth:`remove` - raises a ``KeyError`` whereas :meth:`discard` does not + raises a :class:`KeyError` whereas :meth:`discard` does not raise any exception. This default behavior can be overridden with the diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index b1f76292ce8..54912a2471c 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -138,11 +138,16 @@ def __len__(self): """ Return the number of polynomials in this database. - TESTS:: + TESTS: + + The database currently contains `35357` polynomials, but due to + :issue:`35357` it will be extended by Conway polynomials of + degrees `1`, `2` and `3` for primes between `65537` and `110000`, + thus leading to a new total of `47090` entries:: sage: c = ConwayPolynomials() - sage: len(c) - 35357 + sage: len(c) in [35357, 47090] + True """ try: return self._len diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 172d212fc97..169f2e7c31a 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -953,13 +953,14 @@ def data_from_coefficients(self, ainvs): def elliptic_curve_from_ainvs(self, ainvs): """ - Return the elliptic curve in the database of with minimal - ``ainvs``, if it exists, or raises a ``RuntimeError`` exception - otherwise. + Return the elliptic curve in the database of with minimal ``ainvs`` + if it exists. + + This raises a :class:`RuntimeError` exception otherwise. INPUT: - - ``ainvs`` - list (5-tuple of int's); the minimal + - ``ainvs`` -- list (5-tuple of int's); the minimal Weierstrass model for an elliptic curve OUTPUT: EllipticCurve diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 767a9978a87..ba78f9bc2a6 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -608,8 +608,8 @@ def second_on_modern_computer(self): Float. The wall time on your computer that would be equivalent to one second on a modern computer. Unless you have kick-ass - hardware this should always be >= 1.0. Raises a - ``RuntimeError`` if there are no stored timings to use as + hardware this should always be >= 1.0. This raises a + :class:`RuntimeError` if there are no stored timings to use as benchmark. EXAMPLES:: @@ -1577,8 +1577,9 @@ def run(self): self.log("Features detected for doctesting: " + ','.join(available_software.seen())) if self.options.hidden_features: - features_hidden = [f.name for f in self.options.hidden_features if f.unhide()] - self.log("Features that have been hidden: " + ','.join(features_hidden)) + for f in self.options.hidden_features: + f.unhide() + self.log("Features that have been hidden: " + ','.join(available_software.hidden())) self.cleanup() return self.reporter.error_status diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 85eefc27119..4628db45f5c 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -429,6 +429,7 @@ def __init__(self): self._features = sorted(features, key=lambda feature: feature.name) self._indices = {feature.name: idx for idx, feature in enumerate(self._features)} self._seen = Array('i', len(self._features)) # initialized to zeroes + self._hidden = Array('i', len(self._features)) # initialized to zeroes def __contains__(self, item): """ @@ -444,19 +445,26 @@ def __contains__(self, item): idx = self._indices[item] except KeyError: return False - if not self._seen[idx]: - if not self._allow_external and self._features[idx] in self._external_features: - self._seen[idx] = -1 # not available - elif self._features[idx].is_present(): - self._seen[idx] = 1 # available + feature = self._features[idx] + if feature.is_hidden(): + if not self._hidden[idx]: + self._hidden[idx] = 1 + available = False # a hidden feature is considered to be not available + else: + if not self._allow_external and feature in self._external_features: + # an external feature is considered to be not available + # if this is not allowed + available = False + elif feature.is_present(): + available = True else: - self._seen[idx] = -1 # not available - if self._seen[idx] == 1: + available = False + if available: + if not self._seen[idx]: + self._seen[idx] = 1 return True - elif self._seen[idx] == -1: - return False else: - raise AssertionError("Invalid value for self.seen") + return False def issuperset(self, other): """ @@ -495,5 +503,29 @@ def seen(self): for feature, seen in zip(self._features, self._seen) if seen > 0] + def hidden(self): + """ + Return the list of detected hidden external software. + + EXAMPLES:: + + sage: # needs conway_polynomials database_cremona_mini_ellcurve database_ellcurves database_graphs + sage: from sage.doctest.external import available_software + sage: from sage.features.databases import all_features + sage: for f in all_features(): + ....: f.hide() + ....: if f._spkg_type() == 'standard': + ....: test = f.name in available_software + ....: f.unhide() + sage: sorted(available_software.hidden()) + [...'conway_polynomials',... + 'database_cremona_mini_ellcurve',... + 'database_ellcurves',... + 'database_graphs'...] + """ + return [feature.name + for feature, hidden in zip(self._features, self._hidden) + if hidden > 0] + available_software = AvailableSoftware() diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 600a18b0e8d..68da2754fc9 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -41,6 +41,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +import re from sys import stdout from signal import (SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM) @@ -145,7 +146,8 @@ def were_doctests_with_optional_tag_run(self, tag): When latex is available, doctests marked with optional tag ``latex`` are run by default since :issue:`32174`:: - sage: filename = os.path.join(SAGE_SRC,'sage','misc','latex.py') + sage: # needs SAGE_SRC + sage: filename = os.path.join(SAGE_SRC, 'sage', 'misc', 'latex.py') sage: DC = DocTestController(DocTestDefaults(), [filename]) sage: DTR = DocTestReporter(DC) sage: DTR.were_doctests_with_optional_tag_run('latex') # optional - latex @@ -217,6 +219,69 @@ def report_head(self, source, fail_msg=None): cmd += f" [failed in baseline: {failed}]" return cmd + def _log_failure(self, source, fail_msg, event, output=None): + r""" + Report on the result of a failed doctest run. + + INPUT: + + - ``source`` -- a source from :mod:`sage.doctest.sources` + + - ``fail_msg`` -- a string + + - ``event`` -- a string + + - ``output`` -- optional string + + EXAMPLES:: + + sage: from sage.doctest.reporting import DocTestReporter + sage: from sage.doctest.control import DocTestController, DocTestDefaults + sage: from sage.doctest.sources import FileDocTestSource + sage: from sage.env import SAGE_SRC + sage: import os + sage: filename = os.path.join(SAGE_SRC, 'sage', 'doctest', 'reporting.py') + sage: DD = DocTestDefaults() + sage: FDS = FileDocTestSource(filename, DD) + sage: DC = DocTestController(DD,[filename]) + sage: DTR = DocTestReporter(DC) + sage: DTR._log_failure(FDS, "Timed out", "process (pid=1234) timed out", "Output so far...") + Timed out + ********************************************************************** + Tests run before process (pid=1234) timed out: + Output so far... + ********************************************************************** + """ + log = self.controller.log + format = self.controller.options.format + if format == 'sage': + stars = "*" * 70 + log(f" {fail_msg}\n{stars}\n") + if output: + log(f"Tests run before {event}:") + log(output) + log(stars) + elif format == 'github': + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions + command = f'::error title={fail_msg}' + command += f',file={source.printpath}' + if output: + if m := re.search("## line ([0-9]+) ##\n-{40,100}\n(.*)", output, re.MULTILINE | re.DOTALL): + lineno = m.group(1) + message = m.group(2) + command += f',line={lineno}' + else: + message = output + # Urlencoding trick for multi-line annotations + # https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448 + message = message.replace('\n', '%0A') + else: + message = "" + command += f'::{message}' + log(command) + else: + raise ValueError(f'unknown format option: {format}') + def report(self, source, timeout, return_code, results, output, pid=None): """ Report on the result of running doctests on a given source. @@ -434,9 +499,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): fail_msg += " (and interrupt failed)" else: fail_msg += " (with %s after interrupt)" % signal_name(sig) - log(" %s\n%s\nTests run before %s timed out:" % (fail_msg, "*"*70, process_name)) - log(output) - log("*"*70) + self._log_failure(source, fail_msg, f"{process_name} timed out", output) postscript['lines'].append(self.report_head(source, fail_msg)) stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests} if not baseline.get('failed', False): @@ -448,9 +511,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): fail_msg = "Killed due to %s" % signal_name(-return_code) if ntests > 0: fail_msg += " after testing finished" - log(" %s\n%s\nTests run before %s failed:" % (fail_msg,"*"*70, process_name)) - log(output) - log("*"*70) + self._log_failure(source, fail_msg, f"{process_name} failed", output) postscript['lines'].append(self.report_head(source, fail_msg)) stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests} if not baseline.get('failed', False): @@ -465,15 +526,11 @@ def report(self, source, timeout, return_code, results, output, pid=None): else: cpu = 1e6 if result_dict.err == 'badresult': - log(" Error in doctesting framework (bad result returned)\n%s\nTests run before error:" % ("*"*70)) - log(output) - log("*"*70) + self._log_failure(source, "Error in doctesting framework (bad result returned)", "error", output) postscript['lines'].append(self.report_head(source, "Testing error: bad result")) self.error_status |= 64 elif result_dict.err == 'noresult': - log(" Error in doctesting framework (no result returned)\n%s\nTests run before error:" % ("*"*70)) - log(output) - log("*"*70) + self._log_failure(source, "Error in doctesting framework (no result returned)", "error", output) postscript['lines'].append(self.report_head(source, "Testing error: no result")) self.error_status |= 64 elif result_dict.err == 'tab': @@ -499,11 +556,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): else: err = repr(result_dict.err) fail_msg = "%s in doctesting framework" % err - - log(" %s\n%s" % (fail_msg, "*"*70)) - if output: - log("Tests run before doctest exception:\n" + output) - log("*"*70) + self._log_failure(source, fail_msg, "exception", output) postscript['lines'].append(self.report_head(source, fail_msg)) if hasattr(result_dict, 'tb'): log(result_dict.tb) diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 353eb1ea041..4b42ad87b1f 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -6056,8 +6056,7 @@ def reduced_form(self, **kwds): sage: f.reduced_form(prec=50, smallest_coeffs=False) # this needs 2 periodic Traceback (most recent call last): ... - ValueError: accuracy of Newton's root not within tolerance(0.000066... > 1e-06), - increase precision + ValueError: accuracy of Newton's root not within tolerance(0.00006... > 1e-06), increase precision sage: f.reduced_form(smallest_coeffs=False) ( Dynamical System of Projective Space of dimension 1 over Rational Field @@ -6107,7 +6106,7 @@ def reduced_form(self, **kwds): sage: f.reduced_form(prec=30, smallest_coeffs=False) Traceback (most recent call last): ... - ValueError: accuracy of Newton's root not within tolerance(0.00008... > 1e-06), increase precision + ValueError: accuracy of Newton's root not within tolerance(0.00009... > 1e-06), increase precision sage: f.reduced_form(smallest_coeffs=False) ( Dynamical System of Projective Space of dimension 1 over Rational Field diff --git a/src/sage/env.py b/src/sage/env.py index bb228e7f041..3d336aa18ea 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -187,6 +187,11 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st SAGE_PKGS = var("SAGE_PKGS", join(SAGE_ROOT, "build", "pkgs")) SAGE_ROOT_GIT = var("SAGE_ROOT_GIT", join(SAGE_ROOT, ".git")) +# Sage doc server (local server with PORT if URL is not given) +SAGE_DOC_SERVER_URL = var("SAGE_DOC_SERVER_URL") +# The default port is 0 so that the system will assign a random unused port > 1024 +SAGE_DOC_LOCAL_PORT = var("SAGE_DOC_LOCAL_PORT", "0") + # ~/.sage DOT_SAGE = var("DOT_SAGE", join(os.environ.get("HOME"), ".sage")) SAGE_STARTUP_FILE = var("SAGE_STARTUP_FILE", join(DOT_SAGE, "init.sage")) diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx index 52fbc8f7406..3db7d6fa1dc 100644 --- a/src/sage/ext/fast_eval.pyx +++ b/src/sage/ext/fast_eval.pyx @@ -31,20 +31,20 @@ AUTHORS: from sage.ext.fast_callable import fast_callable, Wrapper -def fast_float(f, *vars, old=None, expect_one_var=False): +def fast_float(f, *vars, expect_one_var=False): """ - Tries to create a function that evaluates f quickly using - floating-point numbers, if possible. There are two implementations - of fast_float in Sage; by default we use the newer, which is - slightly faster on most tests. + Try to create a function that evaluates f quickly using + floating-point numbers, if possible. On failure, returns the input unchanged. + This is an alternative interface to :func:`sage.ext.fast_callable.fast_callable`. + :issue:`32268` proposes to deprecate this function. + INPUT: - ``f`` -- an expression - ``vars`` -- the names of the arguments - - ``old`` -- deprecated, do not use - ``expect_one_var`` -- don't give deprecation warning if ``vars`` is omitted, as long as expression has only one var @@ -67,12 +67,6 @@ def fast_float(f, *vars, old=None, expect_one_var=False): sage: f(1,2) 1.0 """ - if old: - raise ValueError("the old implementation of fast_float has been removed") - if old is not None: - from sage.misc.superseded import deprecation - deprecation(32234, "passing old=False to fast_float is deprecated") - if isinstance(f, (tuple, list)): return tuple([fast_float(x, *vars, expect_one_var=expect_one_var) for x in f]) diff --git a/src/sage/ext/memory_allocator.pxd b/src/sage/ext/memory_allocator.pxd deleted file mode 100644 index 5fc54a1cd27..00000000000 --- a/src/sage/ext/memory_allocator.pxd +++ /dev/null @@ -1,145 +0,0 @@ -cimport cython -from libc.stdint cimport uintptr_t - - -cdef extern from *: - int unlikely(int) nogil # defined by Cython - - -cdef inline void* align(void* ptr, size_t alignment) noexcept: - """ - Round up ``ptr`` to the nearest multiple of ``alignment``, which - must be a power of 2 - """ - cdef uintptr_t x = ptr - x = (x + alignment - 1) & ~(alignment - 1) - return x - - -@cython.final -cdef class MemoryAllocator: - cdef size_t n - cdef size_t size - cdef void** pointers - cdef void* static_pointers[16] # If n <= 16, store pointers here - - cdef void* malloc(self, size_t size) except? NULL - cdef void* calloc(self, size_t nmemb, size_t size) except? NULL - cdef void* allocarray(self, size_t nmemb, size_t size) except? NULL - cdef void* realloc(self, void* ptr, size_t size) except? NULL - cdef void* reallocarray(self, void* ptr, size_t nmemb, - size_t size) except? NULL - - cdef int resize(self, size_t new_size) except -1 - cdef void** find_pointer(self, void* ptr) except NULL - - cdef inline int enlarge_if_needed(self) except -1: - r""" - Enlarge the list of pointers if needed such that there is at - least one free entry. - """ - if unlikely(self.n >= self.size): - return self.resize(self.size * 2) - - cdef inline void* aligned_malloc(self, size_t alignment, - size_t size) except? NULL: - r""" - Returns new aligned pointer. Stores it to be automatically freed later. - - Alignment must be a power of two. - - .. NOTE:: - - If you want to allocate multiple (small) aligned arrays with the - same alignment and really want to be memory efficient, you can - allocate one large aligned array instead. - - TESTS:: - - sage: cython( # needs sage.misc.cython - ....: ''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for i in range(12): - ....: ptr = mem.aligned_malloc(2**i, 4048) - ....: assert ptr == ( ptr) & ~(2**i-1) - ....: ''') - doctest:...: DeprecationWarning: this class is deprecated; - use the class from the python package `memory_allocator` - See https://github.com/sagemath/sage/issues/31591 for details. - """ - cdef size_t extra = alignment - 1 - return align(self.malloc(size + extra), alignment) - - cdef inline void* aligned_calloc(self, size_t alignment, size_t nmemb, - size_t size) except? NULL: - r""" - Returns new aligned pointer. Stores it to be automatically freed later. - - Alignment must be a power of two. - - .. NOTE:: - - If you want to allocate multiple (small) aligned arrays with the - same alignment and really want to be memory efficient, you can - allocate one large aligned array instead. - - TESTS:: - - sage: cython( # needs sage.misc.cython - ....: ''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: def foo(): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for i in range(12): - ....: ptr = mem.aligned_calloc(2**i, i, 2**i) - ....: assert ptr == ( ptr) & ~(2**i-1) - ....: ''') - sage: foo() # needs sage.misc.cython - doctest:...: DeprecationWarning: this class is deprecated; - use the class from the python package `memory_allocator` - See https://github.com/sagemath/sage/issues/31591 for details. - """ - # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1 - # ⇔ extra * size >= alignment - 1 - # ⇔ extra >= ceil( (alignment - 1) / size) - # ⇔ extra >= (alignment - 1 + size - 1) // size - cdef size_t extra = (alignment + size - 2) // size - return align(self.calloc(nmemb + extra, size), alignment) - - cdef inline void* aligned_allocarray(self, size_t alignment, size_t nmemb, - size_t size) except? NULL: - r""" - Returns new aligned pointer. Stores it to be automatically freed later. - - Alignment must be a power of two. - - .. NOTE:: - - If you want to allocate multiple (small) aligned arrays with the - same alignment and really want to be memory efficient, you can - allocate one large aligned array instead. - - TESTS:: - - sage: cython( # needs sage.misc.cython - ....: ''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: def foo(): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for i in range(12): - ....: ptr = mem.aligned_allocarray(2**i, i, 2**i) - ....: assert ptr == ( ptr) & ~(2**i-1) - ....: ''') - sage: foo() # random # might raise deprecation warning # needs sage.misc.cython - sage: foo() # needs sage.misc.cython - """ - # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1 - # ⇔ extra * size >= alignment - 1 - # ⇔ extra >= ceil( (alignment - 1) / size) - # ⇔ extra >= (alignment - 1 + size - 1) // size - cdef size_t extra = (alignment + size - 2) // size - return align(self.allocarray(nmemb + extra, size), alignment) diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx deleted file mode 100644 index 0c2814658e9..00000000000 --- a/src/sage/ext/memory_allocator.pyx +++ /dev/null @@ -1,180 +0,0 @@ -# sage.doctest: needs sage.misc.cython - -from cysignals.memory cimport * -from sage.misc.superseded import deprecation - - -cdef class MemoryAllocator: - r""" - An object for memory allocation, whose resources are freed upon - ``__dealloc__``. - - EXAMPLES:: - - sage: cython( - ....: ''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for n in range(100): - ....: ptr = mem.malloc(n) - ....: mem.realloc(ptr, 2*n) - ....: mem.calloc(n, n) - ....: ptr = mem.allocarray(n, n) - ....: mem.reallocarray(ptr, n + 1, n) - ....: mem.aligned_malloc(32, (n//32 + 1)*32) - ....: mem.aligned_calloc(16, n, 16) - ....: mem.aligned_allocarray(8, n, 8) - ....: ''') - doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` - See https://github.com/sagemath/sage/issues/31591 for details. - """ - def __cinit__(self): - """ - EXAMPLES:: - - sage: cython( - ....: ''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: def foo(): - ....: cdef MemoryAllocator mem = MemoryAllocator.__new__(MemoryAllocator) - ....: mem.malloc(10000) - ....: print(mem.n) - ....: print(mem.size) - ....: ''') - sage: foo() - doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` - See https://github.com/sagemath/sage/issues/31591 for details. - 1 - 16 - """ - deprecation(31591, "this class is deprecated; use the class from the python package `memory_allocator`") - self.n = 0 - self.size = 16 - self.pointers = self.static_pointers - - cdef int resize(self, size_t new_size) except -1: - r""" - Resize the list of pointers to contain ``new_size`` elements. - - It is required that ``new_size`` is at least ``self.n``, but - this condition is not checked. - """ - cdef size_t i - if self.pointers == self.static_pointers: - # Case 1: allocate pointers for the first time - self.pointers = check_allocarray(new_size, sizeof(void*)) - for i in range(self.n): - self.pointers[i] = self.static_pointers[i] - else: - # Case 2: resize pointers - self.pointers = check_reallocarray(self.pointers, new_size, sizeof(void*)) - self.size = new_size - - cdef void** find_pointer(self, void* ptr) except NULL: - r""" - Return the address in the list of stored pointers where ``ptr`` - is stored. If ``ptr`` is not found in the existing pointers and - ``ptr`` is not ``NULL``, then an exception is raised. If ``ptr`` - is ``NULL``, then we simply add ``NULL`` as an additional - pointer and return the address of that. - """ - cdef size_t i = 0 - for i in range(self.n): - if self.pointers[i] == ptr: - return &self.pointers[i] - if ptr != NULL: - raise ValueError("given pointer not found in MemoryAllocator") - self.enlarge_if_needed() - addr = &self.pointers[self.n] - self.n += 1 - return addr - - cdef void* malloc(self, size_t size) except? NULL: - r""" - Returns a new pointer and stores it to be automatically freed later. - """ - self.enlarge_if_needed() - cdef void* val = check_malloc(size) - self.pointers[self.n] = val - self.n += 1 - return val - - cdef void* calloc(self, size_t nmemb, size_t size) except? NULL: - r""" - Returns a new pointer and stores it to be automatically freed later. - """ - self.enlarge_if_needed() - cdef void* val = check_calloc(nmemb, size) - self.pointers[self.n] = val - self.n += 1 - return val - - cdef void* allocarray(self, size_t nmemb, size_t size) except? NULL: - r""" - Returns a new pointer and stores it to be automatically freed later. - """ - self.enlarge_if_needed() - cdef void* val = check_allocarray(nmemb, size) - self.pointers[self.n] = val - self.n += 1 - return val - - cdef void* realloc(self, void* ptr, size_t size) except? NULL: - r""" - Re-allocates `ptr` and automatically frees it later. - - TESTS:: - - sage: cython(''' - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: def test_realloc_good(): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: ptr = mem.malloc(20) - ....: mem.realloc(ptr, 21) - ....: def test_realloc_NULL(): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: mem.realloc(NULL, 21) - ....: def test_realloc_bad(): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef MemoryAllocator mem2 = MemoryAllocator() - ....: ptr = mem.malloc(20) - ....: mem2.realloc(ptr, 21) - ....: ''') - sage: test_realloc_good() # random # might raise deprecation warning - sage: test_realloc_good() - sage: test_realloc_NULL() - sage: test_realloc_bad() - Traceback (most recent call last): - ... - ValueError: given pointer not found in MemoryAllocator - """ - cdef void** addr = self.find_pointer(ptr) - cdef void* val = check_realloc(ptr, size) - addr[0] = val - return val - - cdef void* reallocarray(self, void* ptr, size_t nmemb, - size_t size) except? NULL: - r""" - Re-allocates `ptr` and automatically frees it later. - """ - cdef void** addr = self.find_pointer(ptr) - cdef void* val = check_reallocarray(ptr, nmemb, size) - addr[0] = val - return val - - def __dealloc__(self): - r""" - Free the allocated resources - - EXAMPLES:: - - sage: from sage.ext.memory_allocator import MemoryAllocator - sage: _ = MemoryAllocator() - """ - cdef size_t i - for i in range(self.n): - sig_free(self.pointers[i]) - if self.pointers != self.static_pointers: - sig_free(self.pointers) diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index a8b11ef7db5..86a777ee915 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -158,12 +158,6 @@ def __init__(self, name, spkg=None, url=None, description=None, type='optional') self._hidden = False self._type = type - # For multiprocessing of doctests, the data self._num_hidings should be - # shared among subprocesses. Thus we use the Value class from the - # multiprocessing module (cf. self._seen of class AvailableSoftware) - from multiprocessing import Value - self._num_hidings = Value('i', 0, lock=False) - try: from sage.misc.package import spkg_type except ImportError: # may have been surgically removed in a downstream distribution @@ -220,10 +214,6 @@ def is_present(self): self._cache_is_present = res if self._hidden: - if self._num_hidings.value > 0: - self._num_hidings.value += 1 - elif self._cache_is_present: - self._num_hidings.value = 1 return FeatureTestResult(self, False, reason="Feature `{name}` is hidden.".format(name=self.name)) return self._cache_is_present @@ -354,7 +344,6 @@ def is_standard(self): sage: from sage.features.databases import DatabaseCremona sage: DatabaseCremona().is_standard() False - """ if self.name.startswith('sage.'): return True @@ -369,7 +358,6 @@ def is_optional(self): sage: from sage.features.databases import DatabaseCremona sage: DatabaseCremona().is_optional() True - """ return self._spkg_type() == 'optional' @@ -394,9 +382,7 @@ def hide(self): Use method `unhide` to make it available again. sage: Benzene().unhide() # optional - benzene, needs sage.graphs - 1 sage: len(list(graphs.fusenes(2))) # optional - benzene, needs sage.graphs - 1 """ self._hidden = True @@ -404,8 +390,6 @@ def unhide(self): r""" Revert what :meth:`hide` did. - OUTPUT: The number of events a present feature has been hidden. - EXAMPLES: sage: from sage.features.sagemath import sage__plot @@ -413,15 +397,28 @@ def unhide(self): sage: sage__plot().is_present() FeatureTestResult('sage.plot', False) sage: sage__plot().unhide() # needs sage.plot - 1 sage: sage__plot().is_present() # needs sage.plot FeatureTestResult('sage.plot', True) """ - num_hidings = self._num_hidings.value - self._num_hidings.value = 0 self._hidden = False - return int(num_hidings) + def is_hidden(self): + r""" + Return whether ``self`` is present but currently hidden. + + EXAMPLES: + + sage: from sage.features.sagemath import sage__plot + sage: sage__plot().hide() + sage: sage__plot().is_hidden() # needs sage.plot + True + sage: sage__plot().unhide() + sage: sage__plot().is_hidden() + False + """ + if self._hidden and self._is_present(): + return True + return False class FeatureNotPresentError(RuntimeError): r""" @@ -948,7 +945,10 @@ def _is_present(self): # Available since https://setuptools.pypa.io/en/latest/history.html#v59-0-0 from setuptools.errors import CCompilerError except ImportError: - from distutils.errors import CCompilerError + try: + from distutils.errors import CCompilerError + except ImportError: + CCompilerError = () with open(tmp_filename(ext=".pyx"), 'w') as pyx: pyx.write(self.test_code) try: diff --git a/src/sage/features/join_feature.py b/src/sage/features/join_feature.py index 24c6583c123..81a5b7fb6e9 100644 --- a/src/sage/features/join_feature.py +++ b/src/sage/features/join_feature.py @@ -154,8 +154,6 @@ def unhide(self): r""" Revert what :meth:`hide` did. - OUTPUT: The number of events a present feature has been hidden. - EXAMPLES:: sage: from sage.features.sagemath import sage__groups @@ -167,14 +165,11 @@ def unhide(self): FeatureTestResult('sage.groups.perm_gps.permgroup', False) sage: f.unhide() - 4 sage: f.is_present() # optional sage.groups FeatureTestResult('sage.groups', True) sage: f._features[0].is_present() # optional sage.groups FeatureTestResult('sage.groups.perm_gps.permgroup', True) """ - num_hidings = 0 for f in self._features: - num_hidings += f.unhide() - num_hidings += super().unhide() - return num_hidings + f.unhide() + super().unhide() diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 75a925895c5..2001f793a2f 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -43,6 +43,31 @@ from .join_feature import JoinFeature +class SAGE_SRC(StaticFile): + r""" + A :class:`~sage.features.Feature` which describes the presence of the + monolithic source tree of the Sage library. + + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import SAGE_SRC + sage: isinstance(SAGE_SRC(), SAGE_SRC) + True + """ + from sage.env import SAGE_SRC + # We check the file bin/sage-src-env-config.in, which by design is: + # - never installed, + # - not included in the sagemath-standard sdist, + # - included only in one modularized sdist, of pkgs/sage-conf_pypi, + # where it appears in a subdirectory (sage_root/src/bin/) + StaticFile.__init__(self, 'SAGE_SRC', + filename='bin/sage-src-env-config.in', + search_path=(SAGE_SRC,) if SAGE_SRC else ()) + + class sagemath_doc_html(StaticFile): r""" A :class:`~sage.features.Feature` which describes the presence of the documentation @@ -1095,7 +1120,8 @@ def all_features(): sage: list(all_features()) [...Feature('sage.combinat'), ...] """ - return [sagemath_doc_html(), + return [SAGE_SRC(), + sagemath_doc_html(), sage__combinat(), sage__geometry__polyhedron(), sage__graphs(), diff --git a/src/sage/functions/error.py b/src/sage/functions/error.py index 76509ce5ef0..db6312af807 100644 --- a/src/sage/functions/error.py +++ b/src/sage/functions/error.py @@ -265,7 +265,7 @@ def _evalf_(self, x, parent=None, algorithm=None): sage: gp.set_real_precision(59) # random # needs sage.libs.pari 38 - sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)) # needs sage.libs.pari + sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)) # needs mpmath sage.libs.pari 0.84270079294971486934122063508260925929606699796630290845994 0.84270079294971486934122063508260925929606699796630290845994 diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index 913cb577bef..b5638f8d35f 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -1470,7 +1470,7 @@ def exponential_integral_1(x, n=0): EXAMPLES:: - sage: # needs sage.libs.pari + sage: # needs sage.libs.pari sage.rings.real_mpfr sage: exponential_integral_1(2) 0.0489005107080611 sage: exponential_integral_1(2, 4) # abs tol 1e-18 @@ -1519,7 +1519,7 @@ def exponential_integral_1(x, n=0): ....: if e >= c: ....: print("exponential_integral_1(%s, %s)[%s] with precision %s has error of %s >= %s"%(a, n, i, prec, e, c)) - ALGORITHM: use the PARI C-library function ``eint1``. + ALGORITHM: use the PARI C-library function :pari:`eint1`. REFERENCE: diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py index 0af99e5dc20..be2d60d0861 100644 --- a/src/sage/functions/gamma.py +++ b/src/sage/functions/gamma.py @@ -40,9 +40,9 @@ def __init__(self): EXAMPLES:: sage: from sage.functions.gamma import gamma1 - sage: gamma1(CDF(0.5, 14)) # needs sage.libs.pari + sage: gamma1(CDF(0.5, 14)) # needs sage.libs.pari sage.rings.complex_double -4.0537030780372815e-10 - 5.773299834553605e-10*I - sage: gamma1(CDF(I)) # needs sage.libs.pari sage.symbolic + sage: gamma1(CDF(I)) # needs sage.libs.pari sage.rings.complex_double sage.symbolic -0.15494982830181067 - 0.49801566811835607*I Recall that `\Gamma(n)` is `n-1` factorial:: @@ -99,7 +99,7 @@ def __init__(self): 1*x^(-2) + (-2*euler_gamma)*x^(-1) + (2*euler_gamma^2 + 1/6*pi^2) + Order(x) - To prevent automatic evaluation use the ``hold`` argument:: + To prevent automatic evaluation, use the ``hold`` argument:: sage: gamma1(1/2, hold=True) # needs sage.symbolic gamma(1/2) @@ -138,9 +138,9 @@ def __init__(self): Infinity sage: (-1.).gamma() # needs sage.rings.real_mpfr NaN - sage: CC(-1).gamma() # needs sage.libs.pari + sage: CC(-1).gamma() # needs sage.libs.pari sage.rings.real_mpfr Infinity - sage: RDF(-1).gamma() + sage: RDF(-1).gamma() # needs sage.rings.real_mpfr NaN sage: CDF(-1).gamma() # needs sage.libs.pari sage.rings.complex_double Infinity @@ -691,9 +691,9 @@ def gamma(a, *args, **kwds): :: - sage: gamma(CDF(I)) # needs sage.libs.pari sage.symbolic + sage: gamma(CDF(I)) # needs sage.libs.pari sage.rings.complex_double sage.symbolic -0.15494982830181067 - 0.49801566811835607*I - sage: gamma(CDF(0.5, 14)) # needs sage.libs.pari + sage: gamma(CDF(0.5, 14)) # needs sage.libs.pari sage.rings.complex_double -4.0537030780372815e-10 - 5.773299834553605e-10*I Use ``numerical_approx`` to get higher precision from @@ -721,7 +721,8 @@ def gamma(a, *args, **kwds): sage: gamma(i) # needs sage.rings.number_field sage.symbolic Traceback (most recent call last): ... - TypeError: cannot coerce arguments: no canonical coercion from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring + TypeError: cannot coerce arguments: no canonical coercion + from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring .. SEEALSO:: @@ -1004,9 +1005,9 @@ def __init__(self): INPUT: - - ``p`` - number or symbolic expression + - ``p`` -- number or symbolic expression - - ``q`` - number or symbolic expression + - ``q`` -- number or symbolic expression OUTPUT: number or symbolic expression (if input is symbolic) @@ -1016,18 +1017,18 @@ def __init__(self): sage: # needs sage.symbolic sage: beta(3, 2) 1/12 - sage: beta(3,1) + sage: beta(3, 1) 1/3 sage: beta(1/2, 1/2) beta(1/2, 1/2) - sage: beta(-1,1) + sage: beta(-1, 1) -1 - sage: beta(-1/2,-1/2) + sage: beta(-1/2, -1/2) 0 sage: ex = beta(x/2, 3) sage: set(ex.operands()) == set([1/2*x, 3]) True - sage: beta(.5,.5) + sage: beta(.5, .5) 3.14159265358979 sage: beta(1, 2.0+I) 0.400000000000000 - 0.200000000000000*I diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index 5e66d267828..ba8222121b9 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -290,7 +290,7 @@ class FunctionUnitStep(GinacFunction): INPUT: - - ``x`` - a real number or a symbolic expression + - ``x`` -- a real number or a symbolic expression DEFINITION: @@ -331,7 +331,7 @@ def __init__(self): INPUT: - - ``x`` - a real number or a symbolic expression + - ``x`` -- a real number or a symbolic expression EXAMPLES:: @@ -378,7 +378,7 @@ class FunctionSignum(BuiltinFunction): INPUT: - - ``x`` - a real number or a symbolic expression + - ``x`` -- a real number or a symbolic expression DEFINITION: @@ -548,8 +548,8 @@ class FunctionKroneckerDelta(BuiltinFunction): INPUT: - - ``m`` - a number or a symbolic expression - - ``n`` - a number or a symbolic expression + - ``m`` -- a number or a symbolic expression + - ``n`` -- a number or a symbolic expression DEFINITION: @@ -560,9 +560,9 @@ class FunctionKroneckerDelta(BuiltinFunction): EXAMPLES:: - sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field 0 - sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field 1 sage: m, n = var('m,n') # needs sage.symbolic sage: kronecker_delta(m, n) # needs sage.symbolic @@ -579,9 +579,9 @@ def __init__(self): EXAMPLES:: - sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field 0 - sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field 1 sage: y = var('y') # needs sage.symbolic sage: kronecker_delta(x, y)._sympy_() # needs sympy sage.symbolic @@ -598,9 +598,9 @@ def _eval_(self, m, n): EXAMPLES:: - sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field 0 - sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field + sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field 1 Kronecker delta is a symmetric function. We keep arguments sorted to diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index 7c94892ba9d..6098a6de0bb 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -53,7 +53,7 @@ def __init__(self): EXAMPLES:: - sage: sinh(3.1415) + sage: sinh(3.1415) # needs sage.rings.real_mpfr 11.5476653707437 sage: # needs sage.symbolic @@ -91,7 +91,7 @@ def __init__(self): EXAMPLES:: - sage: cosh(3.1415) + sage: cosh(3.1415) # needs sage.rings.real_mpfr 11.5908832931176 sage: # needs sage.symbolic @@ -129,9 +129,9 @@ def __init__(self): EXAMPLES:: - sage: tanh(3.1415) + sage: tanh(3.1415) # needs sage.rings.real_mpfr 0.996271386633702 - sage: tan(3.1415/4) + sage: tan(3.1415/4) # needs sage.rings.real_mpfr 0.999953674278156 sage: # needs sage.symbolic @@ -197,7 +197,7 @@ def __init__(self): EXAMPLES:: - sage: coth(3.1415) + sage: coth(3.1415) # needs sage.rings.real_mpfr 1.00374256795520 sage: coth(complex(1, 2)) # abs tol 1e-15 # needs sage.rings.complex_double (0.8213297974938518+0.17138361290918508j) @@ -256,7 +256,7 @@ def __init__(self): EXAMPLES:: - sage: sech(3.1415) + sage: sech(3.1415) # needs sage.rings.real_mpfr 0.0862747018248192 sage: # needs sage.symbolic @@ -313,7 +313,7 @@ def __init__(self): EXAMPLES:: - sage: csch(3.1415) + sage: csch(3.1415) # needs sage.rings.real_mpfr 0.0865975907592133 sage: # needs sage.symbolic @@ -375,7 +375,7 @@ def __init__(self): sage: asinh arcsinh - sage: asinh(0.5) + sage: asinh(0.5) # needs sage.rings.real_mpfr 0.481211825059603 sage: asinh(1/2) # needs sage.symbolic arcsinh(1/2) @@ -527,7 +527,7 @@ def __init__(self): EXAMPLES:: - sage: atanh(0.5) + sage: atanh(0.5) # needs sage.rings.real_mpfr 0.549306144334055 sage: atanh(1/2) # needs sage.symbolic 1/2*log(3) diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 6e2b26d284a..010c61febe0 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -14,13 +14,14 @@ Examples from :issue:`9908`:: + sage: # needs sage.symbolic sage: maxima('integrate(bessel_j(2, x), x)').sage() 1/24*x^3*hypergeometric((3/2,), (5/2, 3), -1/4*x^2) sage: sum(((2*I)^x/(x^3 + 1)*(1/4)^x), x, 0, oo) hypergeometric((1, 1, -1/2*I*sqrt(3) - 1/2, 1/2*I*sqrt(3) - 1/2),... (2, -1/2*I*sqrt(3) + 1/2, 1/2*I*sqrt(3) + 1/2), 1/2*I) sage: res = sum((-1)^x/((2*x + 1)*factorial(2*x + 1)), x, 0, oo) - sage: res # not tested - depends on maxima version + sage: res # not tested (depends on maxima version) hypergeometric((1/2,), (3/2, 3/2), -1/4) sage: res in [hypergeometric((1/2,), (3/2, 3/2), -1/4), sin_integral(1)] True @@ -28,6 +29,7 @@ Simplification (note that ``simplify_full`` does not yet call ``simplify_hypergeometric``):: + sage: # needs sage.symbolic sage: hypergeometric([-2], [], x).simplify_hypergeometric() x^2 - 2*x + 1 sage: hypergeometric([], [], x).simplify_hypergeometric() @@ -41,10 +43,10 @@ Equality testing:: - sage: bool(hypergeometric([], [], x).derivative(x) == + sage: bool(hypergeometric([], [], x).derivative(x) == # needs sage.symbolic ....: hypergeometric([], [], x)) # diff(e^x, x) == e^x True - sage: bool(hypergeometric([], [], x) == hypergeometric([], [1], x)) + sage: bool(hypergeometric([], [], x) == hypergeometric([], [1], x)) # needs sage.symbolic False Computing terms and series:: @@ -79,14 +81,14 @@ 1 + (-1/2)*z^2 + 1/24*z^4 + (-1/720)*z^6 + 1/40320*z^8 +... (-1/3628800)*z^10 + Order(z^11) - sage: hypergeometric([1], [5], x).series(x, 5) + sage: hypergeometric([1], [5], x).series(x, 5) # needs sage.symbolic 1 + 1/5*x + 1/30*x^2 + 1/210*x^3 + 1/1680*x^4 + Order(x^5) sage: sum(hypergeometric([1, 2], [3], 1/3).terms(6)).n() # needs sage.symbolic 1.29788359788360 sage: hypergeometric([1, 2], [3], 1/3).n() # needs sage.symbolic 1.29837194594696 - sage: hypergeometric([], [], x).series(x, 20)(x=1).n() == e.n() + sage: hypergeometric([], [], x).series(x, 20)(x=1).n() == e.n() # needs sage.symbolic True Plotting:: @@ -96,7 +98,7 @@ sage: plot(f, x, -30, 30) # needs sage.plot Graphics object consisting of 1 graphics primitive sage: g(x) = hypergeometric([x], [], 2) - sage: complex_plot(g, (-1, 1), (-1, 1)) + sage: complex_plot(g, (-1, 1), (-1, 1)) # needs sage.plot Graphics object consisting of 1 graphics primitive Numeric evaluation:: @@ -121,7 +123,7 @@ sage: maxima(hypergeometric([1, 1, 1], [3, 3, 3], x)) # needs sage.symbolic hypergeometric([1,1,1],[3,3,3],_SAGE_VAR_x) - sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() # needs sage.symbolic + sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() # needs sympy sage.symbolic hyper((5, 4), (4, 4), 3) sage: hypergeometric((5, 4), (4, 4), 3)._mathematica_init_() # needs sage.symbolic 'HypergeometricPFQ[{5,4},{4,4},3]' @@ -154,7 +156,7 @@ 1 + 1*x + 1/2*x^2 + Order(x^3) sage: hypergeometric_U(2, 2, x).series(x == 3, 100).subs(x=1).n() # needs sage.symbolic 0.403652637676806 - sage: hypergeometric_U(2, 2, 1).n() # needs mpmath + sage: hypergeometric_U(2, 2, 1).n() # needs mpmath sage.symbolic 0.403652637676806 """ @@ -451,7 +453,7 @@ def eliminate_parameters(self, a, b, z): sage: hypergeometric([1, 1, 2, 5], [5, 1, 4], # needs sage.symbolic ....: 1/2).eliminate_parameters() hypergeometric((1, 2), (4,), 1/2) - sage: hypergeometric([x], [x], x).eliminate_parameters() + sage: hypergeometric([x], [x], x).eliminate_parameters() # needs sage.symbolic hypergeometric((), (), x) sage: hypergeometric((5, 4), (4, 4), 3).eliminate_parameters() # needs sage.symbolic hypergeometric((5,), (4,), 3) @@ -538,11 +540,11 @@ def is_terminating(self, a, b, z): EXAMPLES:: - sage: hypergeometric([1, 2], [3, 4], x).is_terminating() + sage: hypergeometric([1, 2], [3, 4], x).is_terminating() # needs sage.symbolic False - sage: hypergeometric([1, -2], [3, 4], x).is_terminating() + sage: hypergeometric([1, -2], [3, 4], x).is_terminating() # needs sage.symbolic True - sage: hypergeometric([1, -2], [], x).is_terminating() + sage: hypergeometric([1, -2], [], x).is_terminating() # needs sage.symbolic True """ if z == 0: @@ -642,11 +644,11 @@ def terms(self, a, b, z, n=None): EXAMPLES:: - sage: list(hypergeometric([-2, 1], [3, 4], x).terms()) + sage: list(hypergeometric([-2, 1], [3, 4], x).terms()) # needs sage.symbolic [1, -1/6*x, 1/120*x^2] - sage: list(hypergeometric([-2, 1], [3, 4], x).terms(2)) + sage: list(hypergeometric([-2, 1], [3, 4], x).terms(2)) # needs sage.symbolic [1, -1/6*x] - sage: list(hypergeometric([-2, 1], [3, 4], x).terms(0)) + sage: list(hypergeometric([-2, 1], [3, 4], x).terms(0)) # needs sage.symbolic [] """ if n is None: @@ -675,8 +677,7 @@ def deflated(self, a, b, z): sage: # needs sage.symbolic sage: x = hypergeometric([6, 1], [3, 4, 5], 10) - sage: y = x.deflated() - sage: y + sage: y = x.deflated(); y 1/252*hypergeometric((4,), (7, 8), 10) + 1/12*hypergeometric((3,), (6, 7), 10) + 1/2*hypergeometric((2,), (5, 6), 10) @@ -687,8 +688,7 @@ def deflated(self, a, b, z): sage: # needs sage.symbolic sage: x = hypergeometric([6, 7], [3, 4, 5], 10) - sage: y = x.deflated() - sage: y + sage: y = x.deflated(); y 25/27216*hypergeometric((), (11,), 10) + 25/648*hypergeometric((), (10,), 10) + 265/504*hypergeometric((), (9,), 10) @@ -710,8 +710,7 @@ def _deflated(self, a, b, z): sage: # needs sage.symbolic sage: x = hypergeometric([5], [4], 3) - sage: y = x.deflated() - sage: y + sage: y = x.deflated(); y 7/4*hypergeometric((), (), 3) sage: x.n(); y.n() 35.1496896155784 @@ -958,21 +957,21 @@ class Hypergeometric_M(BuiltinFunction): EXAMPLES:: - sage: # needs mpmath + + sage: hypergeometric_M(1, 1, 1.) # needs mpmath + 2.71828182845905 + + sage: # needs sage.symbolic sage: hypergeometric_M(1, 1, 1) hypergeometric_M(1, 1, 1) - sage: hypergeometric_M(1, 1, 1.) - 2.71828182845905 - sage: hypergeometric_M(1, 1, 1).n(70) + sage: hypergeometric_M(1, 1, 1).n(70) # needs mpmath 2.7182818284590452354 sage: hypergeometric_M(1, 1, 1).simplify_hypergeometric() e sage: hypergeometric_M(1, 3/2, 1).simplify_hypergeometric() 1/2*sqrt(pi)*erf(1)*e - - sage: hypergeometric_M(1, 1/2, x).simplify_hypergeometric() # needs sage.symbolic + sage: hypergeometric_M(1, 1/2, x).simplify_hypergeometric() (-I*sqrt(pi)*x*erf(I*sqrt(-x))*e^x + sqrt(-x))/sqrt(-x) - """ def __init__(self): r""" @@ -980,7 +979,7 @@ def __init__(self): sage: maxima(hypergeometric_M(1,1,x)) # needs sage.symbolic kummer_m(1,1,_SAGE_VAR_x) - sage: latex(hypergeometric_M(1,1,x)) + sage: latex(hypergeometric_M(1,1,x)) # needs sage.symbolic M\left(1, 1, x\right) """ BuiltinFunction.__init__(self, 'hypergeometric_M', nargs=3, @@ -995,8 +994,8 @@ def _eval_(self, a, b, z, **kwargs): """ TESTS:: - sage: (a,b)=var('a,b') # needs sage.symbolic - sage: hypergeometric_M(a,b,0) # needs sage.symbolic + sage: a, b = var('a,b') # needs sage.symbolic + sage: hypergeometric_M(a, b, 0) # needs sage.symbolic 1 """ if not isinstance(z, Expression) and z == 0: @@ -1007,7 +1006,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None): """ TESTS:: - sage: hypergeometric_M(1,1,1).n() # needs mpmath + sage: hypergeometric_M(1,1,1).n() # needs mpmath sage.symbolic 2.71828182845905 """ return _mpmath_utils_call(_mpmath_hyp1f1, a, b, z, parent=parent) @@ -1016,9 +1015,9 @@ def _derivative_(self, a, b, z, diff_param): """ TESTS:: - sage: diff(hypergeometric_M(1,1,x),x,3) + sage: diff(hypergeometric_M(1, 1, x), x, 3) # needs sage.symbolic hypergeometric_M(4, 4, x) - sage: diff(hypergeometric_M(x,1,1),x,3) + sage: diff(hypergeometric_M(x, 1, 1), x, 3) # needs sage.symbolic Traceback (most recent call last): ... NotImplementedError: derivative of hypergeometric function with respect to parameters @@ -1076,7 +1075,9 @@ class Hypergeometric_U(BuiltinFunction): hypergeometric_U(1, 1, 1) sage: hypergeometric_U(1, 1, 1.) 0.596347362323194 - sage: hypergeometric_U(1, 1, 1).n(70) + + sage: # needs sage.symbolic + sage: hypergeometric_U(1, 1, 1).n(70) # needs mpmath 0.59634736232319407434 sage: hypergeometric_U(10^4, 1/3, 1).n() # needs sage.libs.pari 6.60377008885811e-35745 @@ -1092,9 +1093,9 @@ def __init__(self): r""" TESTS:: - sage: maxima(hypergeometric_U(1,1,x)) # needs sage.symbolic + sage: maxima(hypergeometric_U(1, 1, x)) # needs sage.symbolic kummer_u(1,1,_SAGE_VAR_x) - sage: latex(hypergeometric_U(1,1,x)) + sage: latex(hypergeometric_U(1, 1, x)) # needs sage.symbolic U\left(1, 1, x\right) """ BuiltinFunction.__init__(self, 'hypergeometric_U', nargs=3, @@ -1112,7 +1113,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None): """ TESTS:: - sage: hypergeometric_U(1,1,1).n() # needs mpmath + sage: hypergeometric_U(1, 1, 1).n() # needs mpmath sage.symbolic 0.596347362323194 """ return _mpmath_utils_call(_mpmath_hyperu, a, b, z, parent=parent) @@ -1121,9 +1122,9 @@ def _derivative_(self, a, b, z, diff_param): """ TESTS:: - sage: diff(hypergeometric_U(1,1,x),x,3) + sage: diff(hypergeometric_U(1, 1, x), x, 3) # needs sage.symbolic -6*hypergeometric_U(4, 4, x) - sage: diff(hypergeometric_U(x,1,1),x,3) + sage: diff(hypergeometric_U(x, 1, 1), x, 3) # needs sage.symbolic Traceback (most recent call last): ... NotImplementedError: derivative of hypergeometric function with respect to parameters @@ -1140,13 +1141,14 @@ def generalized(self, a, b, z): EXAMPLES:: - sage: var('a b z') # needs sage.symbolic + sage: # needs sage.symbolic + sage: var('a b z') (a, b, z) - sage: hypergeometric_U(a, b, z).generalized() # needs sage.symbolic + sage: hypergeometric_U(a, b, z).generalized() hypergeometric((a, a - b + 1), (), -1/z)/z^a - sage: hypergeometric_U(1, 3, 1/2).generalized() # needs mpmath + sage: hypergeometric_U(1, 3, 1/2).generalized() 2*hypergeometric((1, -1), (), -2) - sage: hypergeometric_U(3, I, 2).generalized() # needs sage.symbolic + sage: hypergeometric_U(3, I, 2).generalized() 1/8*hypergeometric((3, -I + 4), (), -1/2) """ diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 37e5f601e59..903bb7cfe75 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -63,7 +63,7 @@ class Function_exp(GinacFunction): sage: exp(float(2.5)) 12.182493960703473 - sage: exp(RDF('2.5')) + sage: exp(RDF('2.5')) # needs sage.symbolic 12.182493960703473 To prevent automatic evaluation, use the ``hold`` parameter:: diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 8976001bc62..1de970e0aa4 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -439,7 +439,7 @@ class OrthogonalFunction(BuiltinFunction): def __init__(self, name, nargs=2, latex_name=None, conversions=None): """ :class:`OrthogonalFunction` class needs the same input parameter as - it's parent class. + its parent class. EXAMPLES:: @@ -466,7 +466,7 @@ def eval_formula(self, *args): sage: from sage.functions.orthogonal_polys import OrthogonalFunction sage: P = OrthogonalFunction('testo_P') - sage: P.eval_formula(1,2.0) + sage: P.eval_formula(1, 2.0) Traceback (most recent call last): ... NotImplementedError: no explicit calculation of values implemented @@ -543,8 +543,8 @@ class ChebyshevFunction(OrthogonalFunction): """ def __call__(self, n, *args, **kwds): """ - This overides the call method from SageObject to avoid problems with coercions, - since the _eval_ method is able to handle more data types than symbolic functions + This overides the call method from :class:`SageObject` to avoid problems with coercions, + since the ``_eval_`` method is able to handle more data types than symbolic functions would normally allow. Thus we have the distinction between algebraic objects (if n is an integer), and else as symbolic function. @@ -563,7 +563,7 @@ def __call__(self, n, *args, **kwds): Univariate Polynomial Ring in x over Rational Field sage: chebyshev_T(5, 2, hold=True) # needs sage.symbolic chebyshev_T(5, 2) - sage: chebyshev_T(1,2,3) + sage: chebyshev_T(1, 2, 3) Traceback (most recent call last): ... TypeError: Symbolic function chebyshev_T takes exactly 2 arguments (3 given) @@ -599,11 +599,11 @@ def _eval_(self, n, x): chebyshev_T(3/2, x) sage: R. = QQ[] - sage: chebyshev_T(2,t) + sage: chebyshev_T(2, t) 2*t^2 - 1 - sage: chebyshev_U(2,t) + sage: chebyshev_U(2, t) 4*t^2 - 1 - sage: parent(chebyshev_T(4, RIF(5))) + sage: parent(chebyshev_T(4, RIF(5))) # needs sage.rings.real_interval_field Real Interval Field with 53 bits of precision sage: RR2 = RealField(5) # needs sage.rings.real_mpfr sage: chebyshev_T(100000, RR2(2)) # needs sage.rings.real_mpfr @@ -779,8 +779,9 @@ def _evalf_(self, n, x, **kwds): sage: chebyshev_T._evalf_(10^6, 0.1) # needs sage.rings.real_mpfr Traceback (most recent call last): ... - NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. - sage: chebyshev_T(10^6, 0.1) + NoConvergence: Hypergeometric series converges too slowly. + Try increasing maxterms. + sage: chebyshev_T(10^6, 0.1) # needs sage.rings.real_mpfr 0.636384327171504 """ try: @@ -976,9 +977,9 @@ class Func_chebyshev_U(ChebyshevFunction): EXAMPLES:: sage: R. = QQ[] - sage: chebyshev_U(2,t) + sage: chebyshev_U(2, t) 4*t^2 - 1 - sage: chebyshev_U(3,t) + sage: chebyshev_U(3, t) 8*t^3 - 4*t """ def __init__(self): @@ -1052,7 +1053,7 @@ def eval_formula(self, n, x): 1 sage: chebyshev_U.eval_formula(1, x) 2*x - sage: chebyshev_U.eval_formula(2,0.1) == chebyshev_U._evalf_(2,0.1) + sage: chebyshev_U.eval_formula(2, 0.1) == chebyshev_U._evalf_(2, 0.1) True """ if n < -1: @@ -1086,9 +1087,9 @@ def eval_algebraic(self, n, x): Ring of integers modulo 9 sage: chebyshev_U(-3, x) + chebyshev_U(1, x) # needs sage.symbolic 0 - sage: chebyshev_U(-1,Mod(5,8)) + sage: chebyshev_U(-1, Mod(5,8)) 0 - sage: parent(chebyshev_U(-1,Mod(5,8))) + sage: parent(chebyshev_U(-1, Mod(5,8))) Ring of integers modulo 8 sage: R. = ZZ[] sage: chebyshev_U.eval_algebraic(-2, t) @@ -1102,8 +1103,10 @@ def eval_algebraic(self, n, x): sage: n = 97; x = RIF(pi/n) # needs sage.symbolic sage: chebyshev_U(n - 1, cos(x)).contains_zero() # needs sage.symbolic True - sage: R. = Zp(2, 6, 'capped-abs')[] # needs sage.rings.padics - sage: chebyshev_U(10^6 + 1, t) # needs sage.rings.padics + + sage: # needs sage.rings.padics + sage: R. = Zp(2, 6, 'capped-abs')[] + sage: chebyshev_U(10^6 + 1, t) (2 + O(2^6))*t + O(2^6) """ if n == -1: @@ -1142,7 +1145,7 @@ def _evalf_(self, n, x, **kwds): EXAMPLES:: - sage: chebyshev_U(5,-4+3.*I) # needs sage.symbolic + sage: chebyshev_U(5, -4 + 3.*I) # needs sage.symbolic 98280.0000000000 - 11310.0000000000*I sage: chebyshev_U(10, 3).n(75) # needs sage.symbolic 4.661117900000000000000e7 @@ -1676,7 +1679,7 @@ class Func_assoc_legendre_P(BuiltinFunction): -sqrt(-x^2 + 1) sage: gen_legendre_P(1, 1, 0.5) # abs tol 1e-14 # needs mpmath -0.866025403784439 - sage: gen_legendre_P.eval_gen_poly(1, 1, 0.5) # abs tol 1e-14 + sage: gen_legendre_P.eval_gen_poly(1, 1, 0.5) # abs tol 1e-14 # needs sage.rings.real_mpfr -0.866025403784439 sage: gen_legendre_P._evalf_(1, 1, 0.5) # abs tol 1e-14 # needs mpmath -0.866025403784439 @@ -1831,7 +1834,7 @@ def _evalf_(self, n, m, x, parent=None, **kwds): sage: gen_legendre_P(10, 2, 3).n() # abs tol 1e-14 # needs sage.symbolic -7.19496360000000e8 - sage: gen_legendre_P(5/2,2,1.+I) # needs sage.symbolic + sage: gen_legendre_P(5/2, 2, 1. + I) # needs sage.symbolic 14.3165258449040 - 12.7850496155152*I sage: gen_legendre_P(5/2, 2, ComplexField(70)(1+I)) # needs sage.rings.real_mpfr sage.symbolic 14.316525844904028532 - 12.785049615515157033*I @@ -2145,9 +2148,9 @@ class Func_jacobi_P(OrthogonalFunction): EXAMPLES:: sage: x = PolynomialRing(QQ, 'x').gen() - sage: jacobi_P(2,0,0,x) + sage: jacobi_P(2, 0, 0, x) # needs sage.libs.flint sage.symbolic 3/2*x^2 - 1/2 - sage: jacobi_P(2,1,2,1.2) # needs sage.symbolic + sage: jacobi_P(2, 1, 2, 1.2) # needs sage.libs.flint 5.01000000000000 """ def __init__(self): @@ -2205,18 +2208,18 @@ def _eval_(self, n, a, b, x): Check that :issue:`17192` is fixed:: sage: x = PolynomialRing(QQ, 'x').gen() - sage: jacobi_P(0,0,0,x) + sage: jacobi_P(0, 0, 0, x) # needs sage.libs.flint sage.symbolic 1 - sage: jacobi_P(-1,0,0,x) + sage: jacobi_P(-1, 0, 0, x) # needs sage.libs.flint sage.symbolic 1 - sage: jacobi_P(-1,1,1,x) + sage: jacobi_P(-1, 1, 1, x) # needs sage.libs.flint sage.symbolic Traceback (most recent call last): ... ValueError: n must be greater than -1, got n = -1 - sage: jacobi_P(-7,0,0,x) + sage: jacobi_P(-7, 0, 0, x) # needs sage.libs.flint sage.symbolic 231/16*x^6 - 315/16*x^4 + 105/16*x^2 - 5/16 - sage: jacobi_P(-7,0,2,x) + sage: jacobi_P(-7, 0, 2, x) # needs sage.symbolic Traceback (most recent call last): ... ValueError: n must be greater than -1, got n = -7 @@ -2324,11 +2327,11 @@ class Func_ultraspherical(GinacFunction): sage: # needs sage.symbolic sage: gegenbauer(2, -3, x) 12*x^2 + 3 - sage: gegenbauer(120,-99/2,3) + sage: gegenbauer(120, -99/2, 3) 1654502372608570682112687530178328494861923493372493824 sage: gegenbauer(5, 9/2, x) 21879/8*x^5 - 6435/4*x^3 + 1287/8*x - sage: gegenbauer(15,3/2,5) + sage: gegenbauer(15, 3/2, 5) 3903412392243800 sage: derivative(gegenbauer(n, a, x), x) # needs sage.symbolic @@ -2484,7 +2487,7 @@ def _pol_laguerre(self, n, x): 1/24*x^4 - 2/3*x^3 + 3*x^2 - 4*x + 1 sage: laguerre(4, x + 1) # needs mpmath 1/24*(x + 1)^4 - 2/3*(x + 1)^3 + 3*(x + 1)^2 - 4*x - 3 - sage: laguerre(10,1+I) # needs sage.symbolic + sage: laguerre(10, 1 + I) # needs sage.symbolic 142511/113400*I + 95867/22680 """ if hasattr(x, 'pyobject'): @@ -2503,7 +2506,7 @@ def _evalf_(self, n, x, **kwds): sage: laguerre(100, RealField(300)(pi)) # needs sage.symbolic -0.638322077840648311606324... - sage: laguerre(10,1.+I) # needs sage.symbolic + sage: laguerre(10, 1. + I) # needs sage.symbolic 4.22694003527337 + 1.25671075837743*I sage: laguerre(-9, 2.) # needs sage.symbolic 1566.22186244286 @@ -2638,7 +2641,7 @@ def _pol_gen_laguerre(self, n, a, x): 1/24*x^4 - 7/12*x^3 + 35/16*x^2 - 35/16*x + 35/128 sage: gen_laguerre(4, -1/2, x + 1) # needs mpmath 1/24*(x + 1)^4 - 7/12*(x + 1)^3 + 35/16*(x + 1)^2 - 35/16*x - 245/128 - sage: gen_laguerre(10, 1, 1+I) # needs sage.symbolic + sage: gen_laguerre(10, 1, 1 + I) # needs sage.symbolic 25189/2100*I + 11792/2835 """ return sum(binomial(n + a, n - k) * (-1)**k / factorial(k) * x**k diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 3da7beec8ce..23450acb02e 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -173,7 +173,7 @@ def _eval_floor_ceil(self, x, method, bits=0, **kwds): These do not work but fail gracefully:: - sage: ceil(Infinity) + sage: ceil(Infinity) # needs sage.rings.real_interval_field Traceback (most recent call last): ... ValueError: Calling ceil() on infinity or NaN @@ -714,9 +714,9 @@ def __init__(self): EXAMPLES:: - sage: frac(5.4) + sage: frac(5.4) # needs sage.rings.real_mpfr 0.400000000000000 - sage: type(frac(5.4)) + sage: type(frac(5.4)) # needs sage.rings.real_mpfr sage: frac(456/123) 29/41 @@ -812,9 +812,9 @@ class Function_real_nth_root(BuiltinFunction): For numeric input, it gives a numerical approximation. :: - sage: real_nth_root(2., 3) + sage: real_nth_root(2., 3) # needs sage.rings.real_mpfr 1.25992104989487 - sage: real_nth_root(-2., 3) + sage: real_nth_root(-2., 3) # needs sage.rings.real_mpfr -1.25992104989487 Some symbolic calculus:: @@ -914,9 +914,9 @@ def _eval_(self, base, exp): sage: real_nth_root(x, 3) # needs sage.symbolic real_nth_root(x, 3) - sage: real_nth_root(RIF(2), 3) + sage: real_nth_root(RIF(2), 3) # needs sage.rings.real_interval_field 1.259921049894873? - sage: real_nth_root(RBF(2), 3) + sage: real_nth_root(RBF(2), 3) # needs sage.libs.flint [1.259921049894873 +/- 3.92e-16] """ if not isinstance(base, Expression) and not isinstance(exp, Expression): @@ -1145,9 +1145,9 @@ def __init__(self): sage: real(5/3) 5/3 sage: a = 2.5 - sage: real(a) + sage: real(a) # needs sage.rings.real_mpfr 2.50000000000000 - sage: type(real(a)) + sage: type(real(a)) # needs sage.rings.real_mpfr sage: real(1.0r) 1.0 diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index f621c324cc4..677f5a06bd4 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -113,7 +113,7 @@ def __call__(self, function_pieces, **kwds): OUTPUT: - A piecewise-defined function. A ``ValueError`` will be raised + A piecewise-defined function. A :class:`ValueError` will be raised if the domains of the pieces are not pairwise disjoint. EXAMPLES:: @@ -213,17 +213,16 @@ def _subs_(self, subs_map, options, parameters, x): piecewise(x|-->-x^sin(y) on (-2, 0), x|-->x - sin(y) on [0, 2]; x) """ point = subs_map.apply_to(x, 0) - if point == x: + if ((point.is_numeric() or point.is_constant()) and (point.is_real())): + if hasattr(point, 'pyobject'): + # unwrap any numeric values + point = point.pyobject() + elif point == x: # this comparison may be very slow (see #37925) # substitution only in auxiliary variables new_params = [] for domain, func in parameters: new_params.append((domain, subs_map.apply_to(func, 0))) return piecewise(new_params, var=x) - if ((point.is_numeric() or point.is_constant()) - and (point.is_real())): - if hasattr(point, 'pyobject'): - # unwrap any numeric values - point = point.pyobject() else: raise ValueError('substituting the piecewise variable must result in real number') @@ -291,7 +290,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds): EXAMPLES:: - sage: f = piecewise([ [(-1,1), x**2], [(1,3), x**3]]) + sage: f = piecewise([[(-1,1), x**2], [(1,3), x**3]]) sage: f.diff() piecewise(x|-->2*x on (-1, 1), x|-->3*x^2 on (1, 3); x) sage: f.diff(x,x) @@ -300,7 +299,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds): This still fails miserably:: sage: y = SR.var('y') - sage: f = piecewise([ [(-6,0), x+y], [(0,8), x*y]],var=x) + sage: f = piecewise([[(-6,0), x+y], [(0,8), x*y]],var=x) sage: f.derivative(x) # known bug piecewise(x|-->1 on (-6, 0), x|-->y on (0, 8); x) sage: f.derivative(y) # known bug @@ -308,7 +307,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds): TESTS:: - sage: f = piecewise([((-oo, -1),0), ((-1, 1),exp(-1/(1 - x^2))), ((1, oo),0)]) + sage: f = piecewise([((-oo, -1), 0), ((-1, 1), exp(-1/(1 - x^2))), ((1, oo), 0)]) sage: f.diff() piecewise(x|-->0 on (-oo, -1), x|-->-2*x*e^(1/(x^2 - 1))/(x^2 - 1)^2 on (-1, 1), x|-->0 on (1, +oo); x) """ @@ -330,7 +329,7 @@ def __pow__(self, parameters, variable, n): EXAMPLES:: sage: f1(x) = -abs(x) + 1; f2(x) = abs(x - 2) - 1 - sage: f = piecewise([ [(-1,1), f1], [(1,3), f2]]) + sage: f = piecewise([[(-1,1), f1], [(1,3), f2]]) sage: (f^2).integral(definite=True) 4/3 """ @@ -635,9 +634,9 @@ def end_points(self, parameters, variable): EXAMPLES:: sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = x^2-5 - sage: f = piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) + sage: f2(x) = 1 - x + sage: f3(x) = x^2 - 5 + sage: f = piecewise([[(0,1), f1], [(1,2), f2], [(2,3), f3]]) sage: f.end_points() [0, 1, 2, 3] sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f @@ -665,7 +664,9 @@ def piecewise_add(self, parameters, variable, other): sage: f = piecewise([([0,1], 1), ((2,3), x)]) sage: g = piecewise([((1/2, 2), x)]) sage: f.piecewise_add(g).unextend_zero() - piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) ∪ (2, 3); x) + piecewise(x|-->1 on (0, 1/2], + x|-->x + 1 on (1/2, 1], + x|-->x on (1, 2) ∪ (2, 3); x) """ points = ([minus_infinity] + sorted(set(self.end_points() + other.end_points())) + @@ -731,7 +732,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, r""" By default, return the indefinite integral of the function. - If definite=True is given, returns the definite integral. + If ``definite=True`` is given, returns the definite integral. AUTHOR: @@ -739,8 +740,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, EXAMPLES:: - sage: f1(x) = 1-x - sage: f = piecewise([((0,1),1), ((1,2),f1)]) + sage: f1(x) = 1 - x + sage: f = piecewise([((0,1), 1), ((1,2), f1)]) sage: f.integral(definite=True) 1/2 @@ -748,7 +749,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, sage: f1(x) = -1 sage: f2(x) = 2 - sage: f = piecewise([((0,pi/2),f1), ((pi/2,pi),f2)]) + sage: f = piecewise([((0,pi/2), f1), ((pi/2,pi), f2)]) sage: f.integral(definite=True) 1/2*pi @@ -764,8 +765,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, sage: f3(y) = -y - 1 sage: f4(y) = y^2 - 1 sage: f5(y) = 3 - sage: f = piecewise([[[-4,-3],f1], [(-3,-2),f2], [[-2,0],f3], - ....: [(0,2),f4], [[2,3],f5]]) + sage: f = piecewise([[[-4,-3], f1], [(-3,-2), f2], [[-2,0], f3], + ....: [(0,2), f4], [[2,3], f5]]) sage: F = f.integral(y); F piecewise(y|-->-y - 4 on [-4, -3], y|-->1/2*y^2 + 3*y + 7/2 on (-3, -2), @@ -795,7 +796,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, sage: f1(y) = (y+3)^2 sage: f2(y) = y+3 sage: f3(y) = 3 - sage: f = piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]]) + sage: f = piecewise([[(-infinity, -3), f1], [(-3, 0), f2], + ....: [(0, infinity), f3]]) sage: f.integral() piecewise(y|-->1/3*y^3 + 3*y^2 + 9*y + 9 on (-oo, -3), y|-->1/2*y^2 + 3*y + 9/2 on (-3, 0), @@ -823,7 +825,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, Verify that piecewise integrals of zero work (:issue:`10841`):: sage: f0(x) = 0 - sage: f = piecewise([[[0,1],f0]]) + sage: f = piecewise([[[0,1], f0]]) sage: f.integral(x,0,1) 0 sage: f = piecewise([[[0,1], 0]]) @@ -836,9 +838,9 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, Check that the algorithm keyword can be used:: sage: ex = piecewise([([0, 1], 1), ((1, oo), 1/x**2)]) - sage: integral(ex,x,0,100,algorithm='giac') + sage: integral(ex, x, 0, 100, algorithm='giac') 199/100 - sage: integral(ex,x,algorithm='giac') + sage: integral(ex, x, algorithm='giac') piecewise(x|-->x on [0, 1], x|-->-1/x + 2 on (1, +oo); x) """ if a is not None and b is not None: @@ -901,7 +903,7 @@ def critical_points(self, parameters, variable): sage: f1 = x^0 sage: f2 = 10*x - x^2 sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x - sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: f = piecewise([[(0,3), f1], [(3,10), f2], [(10,20), f3]]) sage: expected = [5, 12, 13, 14] sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) True @@ -914,7 +916,7 @@ def critical_points(self, parameters, variable): sage: f1 = y^0 sage: f2 = 10*y - y^2 sage: f3 = 3*y^4 - 156*y^3 + 3036*y^2 - 26208*y - sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: f = piecewise([[(0,3), f1], [(3,10), f2], [(10,20), f3]]) sage: expected = [5, 12, 13, 14] sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) True @@ -940,15 +942,22 @@ def convolution(self, parameters, variable, other): EXAMPLES:: - sage: x = PolynomialRing(QQ,'x').gen() - sage: f = piecewise([[[0,1],1]]) ## example 0 + sage: x = PolynomialRing(QQ, 'x').gen() + + Example 0:: + + sage: f = piecewise([[[0,1], 1]]) sage: g = f.convolution(f); g - piecewise(x|-->x on (0, 1], x|-->-x + 2 on (1, 2]; x) + piecewise(x|-->x on (0, 1], + x|-->-x + 2 on (1, 2]; x) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], x|-->-x^2 + 3*x - 3/2 on (1, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) - sage: f = piecewise([[(0,1),1], [(1,2),2], [(2,3),1]]) ## example 1 + + Example 1:: + + sage: f = piecewise([[(0,1), 1], [(1,2), 2], [(2,3), 1]]) sage: g = f.convolution(f) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], @@ -958,13 +967,16 @@ def convolution(self, parameters, variable, other): x|-->-2*x^2 + 15*x - 15/2 on (5, 6], x|-->2*x^2 - 33*x + 273/2 on (6, 8], x|-->1/2*x^2 - 9*x + 81/2 on (8, 9]; x) - sage: f = piecewise([[(-1,1),1]]) ## example 2 - sage: g = piecewise([[(0,3),x]]) + + Example 2:: + + sage: f = piecewise([[(-1,1), 1]]) + sage: g = piecewise([[(0,3), x]]) sage: f.convolution(g) piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 1], x|-->2*x on (1, 2], x|-->-1/2*x^2 + x + 4 on (2, 4]; x) - sage: g = piecewise([[(0,3),1], [(3,4),2]]) + sage: g = piecewise([[(0,3), 1], [(3,4), 2]]) sage: f.convolution(g) piecewise(x|-->x + 1 on (-1, 1], x|-->2 on (1, 2], @@ -1037,7 +1049,8 @@ def trapezoid(self, parameters, variable, N): EXAMPLES:: - sage: f = piecewise([[[0,1], x^2], [RealSet.open_closed(1,2), 5-x^2]]) + sage: f = piecewise([[[0,1], x^2], + ....: [RealSet.open_closed(1,2), 5 - x^2]]) sage: f.trapezoid(2) piecewise(x|-->1/2*x on (0, 1/2), x|-->3/2*x - 1/2 on (1/2, 1), @@ -1056,8 +1069,8 @@ def trapezoid(self, parameters, variable, N): sage: R. = QQ[] sage: f1 = y^2 - sage: f2 = 5-y^2 - sage: f = piecewise([[[0,1],f1], [RealSet.open_closed(1,2),f2]]) + sage: f2 = 5 - y^2 + sage: f = piecewise([[[0,1], f1], [RealSet.open_closed(1,2), f2]]) sage: f.trapezoid(2) piecewise(y|-->1/2*y on (0, 1/2), y|-->3/2*y - 1/2 on (1/2, 1), @@ -1082,14 +1095,14 @@ def func(x0, x1): def laplace(self, parameters, variable, x='x', s='t'): r""" - Returns the Laplace transform of self with respect to the variable + Return the Laplace transform of ``self`` with respect to the variable var. INPUT: - - ``x`` - variable of self + - ``x`` -- variable of ``self`` - - ``s`` - variable of Laplace transform. + - ``s`` -- variable of Laplace transform. We assume that a piecewise function is 0 outside of its domain and that the left-most endpoint of the domain is 0. @@ -1097,7 +1110,7 @@ def laplace(self, parameters, variable, x='x', s='t'): EXAMPLES:: sage: x, s, w = var('x, s, w') - sage: f = piecewise([[(0,1),1], [[1,2], 1 - x]]) + sage: f = piecewise([[(0,1), 1], [[1,2], 1 - x]]) sage: f.laplace(x, s) -e^(-s)/s + (s + 1)*e^(-2*s)/s^2 + 1/s - e^(-s)/s^2 sage: f.laplace(x, w) @@ -1116,8 +1129,8 @@ def laplace(self, parameters, variable, x='x', s='t'): sage: t = var('t') sage: f1(t) = -t sage: f2(t) = 2 - sage: f = piecewise([[[0,1],f1], [(1,infinity),f2]]) - sage: f.laplace(t,s) + sage: f = piecewise([[[0,1], f1], [(1,infinity), f2]]) + sage: f.laplace(t, s) (s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2 """ from sage.symbolic.assumptions import assume, forget @@ -1172,7 +1185,7 @@ def fourier_series_cosine_coefficient(self, parameters, A triangle wave function of period 2:: - sage: f = piecewise([((0,1), x), ((1,2), 2-x)]) + sage: f = piecewise([((0,1), x), ((1,2), 2 - x)]) sage: f.fourier_series_cosine_coefficient(0) 1 sage: f.fourier_series_cosine_coefficient(3) @@ -1203,13 +1216,13 @@ def fourier_series_cosine_coefficient(self, parameters, Other examples:: sage: f(x) = x^2 - sage: f = piecewise([[(-1,1),f]]) + sage: f = piecewise([[(-1,1), f]]) sage: f.fourier_series_cosine_coefficient(2) pi^(-2) sage: f1(x) = -1 sage: f2(x) = 2 - sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_cosine_coefficient(5,pi) + sage: f = piecewise([[(-pi, pi/2), f1], [(pi/2, pi), f2]]) + sage: f.fourier_series_cosine_coefficient(5, pi) -3/5/pi """ diff --git a/src/sage/functions/spike_function.py b/src/sage/functions/spike_function.py index 4739ca7ffb4..c2fb6113e39 100644 --- a/src/sage/functions/spike_function.py +++ b/src/sage/functions/spike_function.py @@ -71,7 +71,7 @@ def __init__(self, v, eps=0.0000001): A spike function with spikes at [-3.0, -1.0, 2.0] sage: S.height [4.0, 1.0, 3.0] - sage: S.eps + sage: S.eps # needs sage.rings.real_mpfr 0.00100000000000000 """ if not v: diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index c0debecdc0d..236646bae52 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -173,7 +173,7 @@ def __init__(self): INPUT: - - ``n`` - non-negative integer + - ``n`` -- non-negative integer EXAMPLES:: @@ -265,11 +265,11 @@ def _evalf_(self, s, x, parent=None, algorithm=None): r""" TESTS:: - sage: hurwitz_zeta(11/10, 1/2).n() # needs sage.symbolic + sage: hurwitz_zeta(11/10, 1/2).n() # needs mpmath sage.symbolic 12.1038134956837 - sage: hurwitz_zeta(11/10, 1/2).n(100) # needs sage.symbolic + sage: hurwitz_zeta(11/10, 1/2).n(100) # needs mpmath sage.symbolic 12.103813495683755105709077413 - sage: hurwitz_zeta(11/10, 1 + 1j).n() + sage: hurwitz_zeta(11/10, 1 + 1j).n() # needs mpmath sage.rings.real_mpfr 9.85014164287853 - 1.06139499403981*I """ return _mpmath_utils_call(_mpmath_zeta, s, x, parent=parent) @@ -571,48 +571,54 @@ def power_series(self, n, abs_prec): """ This function returns the power series about `n+1/2` used to evaluate Dickman's function. It is scaled such that the interval - `[n,n+1]` corresponds to x in `[-1,1]`. + `[n,n+1]` corresponds to `x` in `[-1,1]`. INPUT: - - ``n`` - the lower endpoint of the interval for which + - ``n`` -- the lower endpoint of the interval for which this power series holds - - ``abs_prec`` - the absolute precision of the + - ``abs_prec`` -- the absolute precision of the resulting power series EXAMPLES:: + sage: # needs sage.rings.real_mpfr sage: f = dickman_rho.power_series(2, 20); f - -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032 + -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 + - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 + - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032 sage: f(-1), f(0), f(1) (0.30685, 0.13032, 0.048608) - sage: dickman_rho(2), dickman_rho(2.5), dickman_rho(3) # needs sage.symbolic + sage: dickman_rho(2), dickman_rho(2.5), dickman_rho(3) (0.306852819440055, 0.130319561832251, 0.0486083882911316) """ return self._compute_power_series(n, abs_prec, cache_ring=None) def _compute_power_series(self, n, abs_prec, cache_ring=None): """ - Compute the power series giving Dickman's function on [n, n+1], by - recursion in n. For internal use; self.power_series() is a wrapper + Compute the power series giving Dickman's function on `[n, n+1]`, by + recursion in `n`. For internal use; ``self.power_series()`` is a wrapper around this intended for the user. INPUT: - - ``n`` - the lower endpoint of the interval for which + - ``n`` -- the lower endpoint of the interval for which this power series holds - - ``abs_prec`` - the absolute precision of the + - ``abs_prec`` -- the absolute precision of the resulting power series - - ``cache_ring`` - for internal use, caches the power + - ``cache_ring`` -- for internal use, caches the power series at this precision. EXAMPLES:: + sage: # needs sage.rings.real_mpfr sage: f = dickman_rho.power_series(2, 20); f - -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032 + -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 + - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 + - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032 """ if n <= 1: if n <= -1: diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index d4e723ec6e2..b767fbbd5d4 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -210,6 +210,7 @@ def __init__(self): EXAMPLES:: + sage: # needs sage.rings.real_mpfr sage: tan(3.1415) -0.0000926535900581913 sage: tan(3.1415/4) @@ -323,20 +324,20 @@ def __init__(self): TESTS:: - sage: cot(float(0)) # needs sage.symbolic + sage: # needs sage.symbolic + sage: cot(float(0)) Infinity - sage: cot(SR(0)) # needs sage.symbolic + sage: cot(SR(0)) Infinity - sage: cot(float(0.1)) # needs sage.symbolic + sage: cot(float(0.1)) 9.966644423259238 sage: type(_) <... 'float'> - - sage: cot(float(0)) # needs sage.symbolic + sage: cot(float(0)) Infinity - sage: cot(SR(0)) # needs sage.symbolic + sage: cot(SR(0)) Infinity - sage: cot(float(0.1)) # needs sage.symbolic + sage: cot(float(0.1)) 9.966644423259238 sage: type(_) <... 'float'> @@ -520,7 +521,7 @@ def __init__(self): EXAMPLES:: - sage: arcsin(0.5) + sage: arcsin(0.5) # needs sage.rings.real_mpfr 0.523598775598299 sage: arcsin(1/2) # needs sage.symbolic 1/6*pi @@ -584,7 +585,7 @@ def __init__(self): EXAMPLES:: - sage: arccos(0.5) + sage: arccos(0.5) # needs sage.rings.real_mpfr 1.04719755119660 sage: arccos(1/2) # needs sage.symbolic 1/3*pi @@ -940,7 +941,7 @@ def __init__(self): sage: maxima.atan2(1, -1) # needs sage.symbolic (3*%pi)/4 - sage: math.atan2(1,-1) + sage: math.atan2(1, -1) 2.356194490192345 More examples:: diff --git a/src/sage/functions/wigner.py b/src/sage/functions/wigner.py index 0bafe13a246..5a19010f073 100644 --- a/src/sage/functions/wigner.py +++ b/src/sage/functions/wigner.py @@ -616,11 +616,11 @@ def gaunt(l_1, l_2, l_3, m_1, m_2, m_3, prec=None): It is an error to use non-integer values for `l` or `m`:: - sage: gaunt(1.2,0,1.2,0,0,0) + sage: gaunt(1.2,0,1.2,0,0,0) # needs sage.rings.real_mpfr Traceback (most recent call last): ... TypeError: Attempt to coerce non-integral RealNumber to Integer - sage: gaunt(1,0,1,1.1,0,-1.1) + sage: gaunt(1,0,1,1.1,0,-1.1) # needs sage.rings.real_mpfr Traceback (most recent call last): ... TypeError: Attempt to coerce non-integral RealNumber to Integer diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py deleted file mode 100644 index b2d8af991e9..00000000000 --- a/src/sage/game_theory/gambit_docs.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -Using Gambit as a standalone package - -This file contains some information and tests for the use of -`Gambit `_ as a stand alone package. - -To install gambit as an optional package run (from root of Sage):: - - $ sage -i gambit - -The `python API documentation for gambit -`_ shows various examples -that can be run easily in IPython. To run the IPython packaged with Sage run -(from root of Sage):: - - $ ./sage --ipython - -Here is an example that constructs the Prisoner's Dilemma:: - - In [1]: import gambit - In [2]: g = gambit.Game.new_table([2,2]) - In [3]: g.title = "A prisoner's dilemma game" - In [4]: g.players[0].label = "Alphonse" - In [5]: g.players[1].label = "Gaston" - In [6]: g - Out[6]: - NFG 1 R "A prisoner's dilemma game" { "Alphonse" "Gaston" } - - { { "1" "2" } - { "1" "2" } - } - "" - - { - { "" 0, 0 } - { "" 0, 0 } - { "" 0, 0 } - { "" 0, 0 } - } - 1 2 3 4 - - In [7]: g.players[0].strategies - Out[7]: [, - ] - In [8]: len(g.players[0].strategies) - Out[8]: 2 - - In [9]: g.players[0].strategies[0].label = "Cooperate" - In [10]: g.players[0].strategies[1].label = "Defect" - In [11]: g.players[0].strategies - Out[11]: [, - ] - - In [12]: g[0,0][0] = 8 - In [13]: g[0,0][1] = 8 - In [14]: g[0,1][0] = 2 - In [15]: g[0,1][1] = 10 - In [16]: g[1,0][0] = 10 - In [17]: g[1,1][1] = 2 - In [18]: g[1,0][1] = 2 - In [19]: g[1,1][0] = 5 - In [20]: g[1,1][1] = 5 - -Here is a list of the various solvers available in gambit: - -- ExternalEnumPureSolver -- ExternalEnumMixedSolver -- ExternalLPSolver -- ExternalLCPSolver -- ExternalSimpdivSolver -- ExternalGlobalNewtonSolver -- ExternalEnumPolySolver -- ExternalLyapunovSolver -- ExternalIteratedPolymatrixSolver -- ExternalLogitSolver - -Here is how to use the ``ExternalEnumPureSolver``:: - - In [21]: solver = gambit.nash.ExternalEnumPureSolver() - In [22]: solver.solve(g) - Out[22]: [] - -Note that the above finds the equilibria by investigating all potential pure -pure strategy pairs. This will fail to find all Nash equilibria in certain -games. For example here is an implementation of Matching Pennies:: - - In [1]: import gambit - In [2]: g = gambit.Game.new_table([2,2]) - In [3]: g[0, 0][0] = 1 - In [4]: g[0, 0][1] = -1 - In [5]: g[0, 1][0] = -1 - In [6]: g[0, 1][1] = 1 - In [7]: g[1, 0][0] = -1 - In [8]: g[1, 0][1] = 1 - In [9]: g[1, 1][0] = 1 - In [10]: g[1, 1][1] = -1 - In [11]: solver = gambit.nash.ExternalEnumPureSolver() - In [12]: solver.solve(g) - Out[12]: [] - -If we solve this with the ``LCP`` solver we get the expected Nash equilibrium:: - - In [13]: solver = gambit.nash.ExternalLCPSolver() - In [14]: solver.solve(g) - Out[14]: [] - -Note that the above examples only show how to build and find equilibria for -two player strategic form games. Gambit supports multiple player games as well -as extensive form games: for more details see http://www.gambit-project.org/. - -If one really wants to use gambit directly in Sage (without using the -``NormalFormGame`` class as a wrapper) then integers must first be -converted to Python integers (due to the preparser). Here is an example -showing the Battle of the Sexes:: - - sage: # optional - gambit - sage: import gambit - sage: g = gambit.Game.new_table([2,2]) - sage: g[int(0), int(0)][int(0)] = int(2) - sage: g[int(0), int(0)][int(1)] = int(1) - sage: g[int(0), int(1)][int(0)] = int(0) - sage: g[int(0), int(1)][int(1)] = int(0) - sage: g[int(1), int(0)][int(0)] = int(0) - sage: g[int(1), int(0)][int(1)] = int(0) - sage: g[int(1), int(1)][int(0)] = int(1) - sage: g[int(1), int(1)][int(1)] = int(2) - sage: solver = gambit.nash.ExternalLCPSolver() - sage: solver.solve(g) - [, - , - ] - -AUTHOR: - -- Vince Knight (11-2014): Original version - -""" diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index b053f3160b8..93cfe25a883 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -236,8 +236,7 @@ `Gambit `_ [Gambit]_. At present this is the only gambit algorithm available in sage but further development will hope to implement more algorithms - (in particular for games with more than 2 players). To install it, - type ``sage -i gambit`` in the shell. + (in particular for games with more than 2 players). * ``'enumeration'``: Support enumeration for 2 player games. This algorithm is hard coded in Sage and checks through all potential @@ -648,7 +647,6 @@ from sage.matrix.constructor import vector from sage.misc.temporary_file import tmp_filename from sage.numerical.mip import MixedIntegerLinearProgram -from sage.misc.package import PackageNotFoundError from sage.cpython.string import bytes_to_str try: @@ -1705,7 +1703,7 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None): if algorithm == "LCP": if Game is None: - raise PackageNotFoundError("gambit") + raise RuntimeError("gambit not found") # should later become a FeatureNotFoundError return self._solve_LCP(maximization) if algorithm.startswith('lp'): diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index 3b7dcf756d8..e4b4d933bc4 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -16,4 +16,5 @@ lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram') lazy_import('sage.geometry.ribbon_graph', 'RibbonGraph') lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') +lazy_import('sage.geometry.hyperplane_arrangement.ordered_arrangement', 'OrderedHyperplaneArrangements') lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements') diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index fc0b3731945..0d9689b4d2e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -211,7 +211,6 @@ from sage.combinat.posets.posets import FinitePoset from sage.geometry.point_collection import PointCollection from sage.geometry.polyhedron.constructor import Polyhedron -from sage.geometry.polyhedron.base import is_Polyhedron from sage.geometry.hasse_diagram import lattice_from_incidences from sage.geometry.toric_lattice import (ToricLattice, is_ToricLattice, is_ToricLatticeQuotient) @@ -259,6 +258,9 @@ def is_Cone(x): sage: from sage.geometry.cone import is_Cone sage: is_Cone(1) + doctest:warning... + DeprecationWarning: is_Cone is deprecated, use isinstance instead + See https://github.com/sagemath/sage/issues/34307 for details. False sage: quadrant = Cone([(1,0), (0,1)]) sage: quadrant @@ -266,6 +268,8 @@ def is_Cone(x): sage: is_Cone(quadrant) True """ + from sage.misc.superseded import deprecation + deprecation(34307, "is_Cone is deprecated, use isinstance instead") return isinstance(x, ConvexRationalPolyhedralCone) @@ -439,7 +443,7 @@ def Cone(rays, lattice=None, check=True, normalize=True): 0-d cone in 2-d lattice N """ # Cone from Polyhedron - if is_Polyhedron(rays): + if isinstance(rays, sage.geometry.abc.Polyhedron): polyhedron = rays if lattice is None: lattice = ToricLattice(polyhedron.ambient_dim()) @@ -1894,7 +1898,7 @@ def cartesian_product(self, other, lattice=None): N+N(0, 1) in 2-d lattice N+N """ - assert is_Cone(other) + assert isinstance(other, sage.geometry.abc.ConvexRationalPolyhedralCone) rc = super().cartesian_product(other, lattice) return ConvexRationalPolyhedralCone(rc.rays(), rc.lattice()) @@ -1952,7 +1956,7 @@ def __richcmp__(self, right, op): sage: c2 is c3 False """ - if is_Cone(right): + if isinstance(right, sage.geometry.abc.ConvexRationalPolyhedralCone): # We don't care about particular type of right in this case return richcmp((self.lattice(), self.rays()), (right.lattice(), right.rays()), op) @@ -2413,7 +2417,7 @@ def embed(self, cone): ValueError: 2-d cone in 3-d lattice N is not a face of 3-d cone in 3-d lattice N! """ - assert is_Cone(cone) + assert isinstance(cone, sage.geometry.abc.ConvexRationalPolyhedralCone) if cone.ambient() is self: return cone if self.is_strictly_convex(): @@ -2935,7 +2939,7 @@ def facet_of(self): L = self._ambient._face_lattice_function() H = L.hasse_diagram() return self._sort_faces( - f for f in H.neighbors_out(L(self)) if is_Cone(f)) + f for f in H.neighbors_out(L(self)) if isinstance(f, sage.geometry.abc.ConvexRationalPolyhedralCone)) def facets(self): r""" @@ -6193,6 +6197,201 @@ def Z_operators_gens(self): """ return [ -cp for cp in self.cross_positive_operators_gens() ] + def max_angle(self, other=None, exact=True, epsilon=0): + r""" + Return the maximal angle between ``self`` and ``other``. + + The maximal angle between two closed convex cones is the + unique largest angle formed by any two unit-norm vectors in + those cones. In pathological cases, this computation can fail. + + If it fails when ``exact`` is ``True`` and if each of the + cones :meth:`is_strictly_convex`, then a second attempt will + be made using inexact arithmetic. (This sometimes avoids the + problem noted in [Or2024]_). If the computation fails when the + cones are not strictly convex or when ``exact`` is ``False``, + a :class:`ValueError` is raised. + + INPUT: + + - ``other`` -- (default: ``None``) a rational, polyhedral + convex cone + + - ``exact`` -- (default: ``True``) whether or not to use exact + rational arithmetic instead of floating point computations; + beware that ``True`` is not guaranteed to avoid floating + point computations if the algorithm runs into trouble in + rational arithmetic + + - ``epsilon`` -- (default: ``0``) the tolerance to use when + making comparisons + + .. WARNING:: + + Using inexact arithmetic (``exact=False``) is faster, but + this computation is only known to be stable when both of + the cones are strictly convex (or when one of them is the + entire space, but the maximal angle is obviously `\pi` in + that case). + + OUTPUT: + + A triple `\left( \theta_{\max}, u, v \right)` + containing: + + - the maximal angle `\theta_{\max}` between ``self`` and + ``other`` + + - a vector `u` in ``self`` that achieves the maximal angle + + - a vector `v` in ``other`` that achieves the maximal angle + + If ``other`` is ``None`` (the default), then the maximal angle + within this cone (between this cone and itself) is returned. + + If an eigenspace of dimension greater than one is encountered + and if the corresponding angle cannot be ruled out as a + maximum, the behavior of this function depends on + ``exact``: + + - If ``exact`` is ``True`` and if both ``self`` and ``other`` + are strictly convex, then the algorithm will fall back to + inexact arithmetic. In that case, the returned angle and + vectors will be over :class:`sage.rings.real_double.RDF`. + + - If ``exact`` is ``False`` or if either cone is not strictly + convex, then a :class:`ValueError` is raised to indicate + that we have failed; i.e. we cannot say with certainty what + the maximal angle is. + + REFERENCES: + + - [IS2005]_ + - [SS2016]_ + - [Or2020]_ + - [Or2024]_ + + ALGORITHM: + + Algorithm 3 in [Or2020]_ is used. If a potentially-maximal + angle corresponds to an eigenspace of dimension two or more, + we sometimes fall back to inexact arithmetic which has the + effect of perturbing the cones. That this will not affect the + answer too much is one conclusion of [Or2024]_. + + EXAMPLES: + + The maximal angle in a single ray is zero:: + + sage: K = random_cone(min_rays=1, max_rays=1, max_ambient_dim=5) + sage: K.max_angle()[0] + 0 + + The maximal angle in the nonnegative octant is `\pi/2`:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.max_angle()[0] + 1/2*pi + + The maximal angle between the nonnegative quintant and the + Schur cone of dimension 5 is about `0.8524 \pi`. The same + result can be obtained faster using inexact arithmetic, but + only confidently so because we already know the answer:: + + sage: # long time + sage: P = cones.nonnegative_orthant(5) + sage: Q = cones.schur(5) + sage: actual = P.max_angle(Q)[0] + sage: expected = 0.8524*pi + sage: bool( (actual - expected).abs() < 0.0001 ) + True + sage: actual = P.max_angle(Q,exact=False)[0] + sage: bool( (actual - expected).abs() < 0.0001 ) + True + + The maximal angle within the Schur cone is known explicitly + via Gourion and Seeger's Proposition 2 [GS2010]_:: + + sage: n = 3 + sage: K = cones.schur(n) + sage: bool(K.max_angle()[0] == ((n-1)/n)*pi) + True + + Sage can't prove that the actual and expected results are + equal in the next two cases without a little nudge in the + right direction, and, moreover, it's crashy about it:: + + sage: n = 4 + sage: K = cones.schur(n) + sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_() + sage: expected = ((n-1)/n)*pi + sage: bool( actual == expected ) + True + sage: n = 5 + sage: K = cones.schur(n) + sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_() + sage: expected = ((n-1)/n)*pi + sage: bool( actual == expected ) + True + + When there's a unit norm vector in ``self`` whose negation is + in ``other``, they form a maximal angle of `\pi`:: + + sage: P = Cone([(5,1), (1,-1)]) + sage: Q = Cone([(-1,0), (-1,0)]) + sage: P.max_angle(Q)[0] + pi + + TESTS: + + When ``self`` and ``-other`` have nontrivial intersection, we + expect the maximal angle to be `\pi`:: + + sage: # long time + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: expected = P.intersection(-Q).is_trivial() + sage: actual = bool(P.max_angle(Q)[0] != pi) + sage: actual == expected + True + + In particular, this should happen when either cone is the full + space:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: n = ZZ.random_element(1,5) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = cones.trivial(n, P.dual().lattice()).dual() + sage: Q.is_full_space() + True + sage: P.max_angle(Q)[0] + pi + sage: Q.max_angle(P)[0] + pi + """ + # We do the argument checking here, in the public cone method, + # because the error message should say something like "this + # cone" and not reference the argument of a function the user + # has never heard of in some internal module. + if self.is_trivial(): + raise ValueError("cone should not be trivial") + + if other is None: + other = self + else: + if (other.lattice_dim() != self.lattice_dim()): + raise ValueError("lattice dimensions of self and other " + "must agree") + if other.is_trivial(): + raise ValueError("other cone cannot be trivial") + + from sage.geometry.cone_critical_angles import max_angle + return max_angle(self, other, exact, epsilon) + def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, min_rays=0, max_rays=None, strictly_convex=None, solid=None): @@ -6387,9 +6586,8 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, It's hard to test the output of a random process, but we can at least make sure that we get a cone back:: - sage: from sage.geometry.cone import is_Cone sage: K = random_cone(max_ambient_dim=6, max_rays=10) - sage: is_Cone(K) + sage: isinstance(K, sage.geometry.abc.ConvexRationalPolyhedralCone) True The upper/lower bounds are respected:: diff --git a/src/sage/geometry/cone_catalog.py b/src/sage/geometry/cone_catalog.py index d6898d40087..a56de80b412 100644 --- a/src/sage/geometry/cone_catalog.py +++ b/src/sage/geometry/cone_catalog.py @@ -4,6 +4,7 @@ This module provides shortcut functions, grouped under the globally-available ``cones`` prefix, to create some common cones: +- The downward-monotone cone, - The nonnegative orthant, - The rearrangement cone of order ``p``, - The Schur cone, @@ -18,6 +19,15 @@ Here are some typical usage examples:: + sage: cones.downward_monotone(3).rays() + N( 1, 0, 0), + N( 1, 1, 0), + N( 1, 1, 1), + N(-1, -1, -1) + in 3-d lattice N + +:: + sage: cones.nonnegative_orthant(2).rays() N(1, 0), N(0, 1) @@ -150,6 +160,143 @@ def _preprocess_args(ambient_dim, lattice): return (ambient_dim, lattice) +def downward_monotone(ambient_dim=None, lattice=None): + r""" + The downward-monotone cone in ``ambient_dim`` dimensions, or + living in ``lattice``. + + The elements of the downward-monotone cone are vectors whose + components are arranged in non-increasing order. Vectors whose + entries are arranged in the reverse (non-decreasing) order are + sometimes called isotone vectors, and are used in statistics + for isotonic regression. + + The downward-monotone cone is the dual of the Schur cone. It + is also often referred to as the downward-monotone cone. + + INPUT: + + - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the + dimension of the ambient space + + - ``lattice`` -- a toric lattice (default: ``None``); the lattice in + which the cone will live + + If ``ambient_dim`` is omitted, then it will be inferred from the + rank of ``lattice``. If the ``lattice`` is omitted, then the + default lattice of rank ``ambient_dim`` will be used. + + A :class:`ValueError` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a :class:`ValueError` to + specify both ``ambient_dim`` and ``lattice`` unless the rank of + ``lattice`` is equal to ``ambient_dim``. + + OUTPUT: + + A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living + in ``lattice`` whose elements' entries are arranged in + nonincreasing order. Each generating ray has the integer ring as + its base ring. + + A :class:`ValueError` can be raised if the inputs are incompatible + or insufficient. See the INPUT documentation for details. + + .. SEEALSO:: + + :func:`schur` + + REFERENCES: + + - [GS2010]_, Section 3.1 + + - [Niez1998]_, Example 2.2 + + EXAMPLES: + + The entries of the elements of the downward-monotone cone are in + non-increasing order:: + + sage: ambient_dim = ZZ.random_element(10) + sage: K = cones.downward_monotone(ambient_dim) + sage: all( x[i] >= x[i + 1] + ....: for i in range(ambient_dim - 1) + ....: for x in K.rays() ) + True + sage: x = K.random_element() + sage: all( x[i] >= x[i + 1] for i in range(ambient_dim - 1) ) + True + + A nontrivial downward-monotone cone is solid but not proper, + since it contains both the vector of all ones and its negation; + that, however, is the only subspace it contains:: + + sage: ambient_dim = ZZ.random_element(1,10) + sage: K = cones.downward_monotone(ambient_dim) + sage: K.is_solid() + True + sage: K.is_proper() + False + sage: K.lineality() + 1 + + The dual of the downward-monotone cone is the Schur cone + [GS2010]_ that induces the majorization preordering:: + + sage: ambient_dim = ZZ.random_element(10) + sage: K = cones.downward_monotone(ambient_dim).dual() + sage: J = cones.schur(ambient_dim, K.lattice()) + sage: K.is_equivalent(J) + True + + TESTS: + + We can construct the trivial cone as the downward-monotone cone + in a trivial vector space:: + + sage: cones.downward_monotone(0) + 0-d cone in 0-d lattice N + + If a ``lattice`` was given, it is actually used:: + + sage: L = ToricLattice(3, 'M') + sage: cones.downward_monotone(lattice=L) + 3-d cone in 3-d lattice M + + Unless the rank of the lattice disagrees with ``ambient_dim``:: + + sage: L = ToricLattice(1, 'M') + sage: cones.downward_monotone(3, lattice=L) + Traceback (most recent call last): + ... + ValueError: lattice rank=1 and ambient_dim=3 are incompatible + + We also get an error if no arguments are given:: + + sage: cones.downward_monotone() + Traceback (most recent call last): + ... + ValueError: either the ambient dimension or the lattice must + be specified + """ + from sage.geometry.cone import Cone + from sage.matrix.constructor import matrix + from sage.rings.integer_ring import ZZ + + ambient_dim, lattice = _preprocess_args(ambient_dim, lattice) + + # The generators for this cone are mentioned in Niezgoda's + # Example 2.2 if you don't want to compute them yourself. + G = matrix.identity(ZZ, ambient_dim) + for i in range(1, ambient_dim): + G.add_multiple_of_row(i, i - 1, 1) + + if G.nrows() > 0: + # Special case for when the ambient space is trivial. + G = G.insert_row(ambient_dim, -1*G.row(-1)) + + return Cone(G.rows(), lattice) + + def nonnegative_orthant(ambient_dim=None, lattice=None): r""" The nonnegative orthant in ``ambient_dim`` dimensions, or living @@ -170,20 +317,20 @@ def nonnegative_orthant(ambient_dim=None, lattice=None): rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. - A ``ValueError`` is raised if neither ``ambient_dim`` nor - ``lattice`` are specified. It is also a ``ValueError`` to specify - both ``ambient_dim`` and ``lattice`` unless the rank of + A :class:`ValueError` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a :class:`ValueError` to + specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: - A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living in ``lattice`` - and having ``ambient_dim`` standard basis vectors as its - generators. Each generating ray has the integer ring as its + A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living + in ``lattice`` and having ``ambient_dim`` standard basis vectors + as its generators. Each generating ray has the integer ring as its base ring. - A ``ValueError`` can be raised if the inputs are incompatible or - insufficient. See the INPUT documentation for details. + A :class:`ValueError` can be raised if the inputs are incompatible + or insufficient. See the INPUT documentation for details. REFERENCES: @@ -238,7 +385,7 @@ def nonnegative_orthant(ambient_dim=None, lattice=None): from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ - (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + ambient_dim, lattice = _preprocess_args(ambient_dim, lattice) I = matrix.identity(ZZ, ambient_dim) return Cone(I.rows(), lattice) @@ -278,22 +425,22 @@ def rearrangement(p, ambient_dim=None, lattice=None): rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. - A ``ValueError`` is raised if neither ``ambient_dim`` nor - ``lattice`` are specified. It is also a ``ValueError`` to specify - both ``ambient_dim`` and ``lattice`` unless the rank of + A :class:`ValueError` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a :class:`ValueError` to + specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. - It is also a ``ValueError`` to specify a non-integer ``p``. + It is also a :class:`ValueError` to specify a non-integer ``p``. OUTPUT: - A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the - rearrangement cone of order ``p`` living in ``lattice``, with - ambient dimension ``ambient_dim``. Each generating ray has the - integer ring as its base ring. + A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` + representing the rearrangement cone of order ``p`` living in + ``lattice``, with ambient dimension ``ambient_dim``. Each + generating ray has the integer ring as its base ring. - A ``ValueError`` can be raised if the inputs are incompatible or - insufficient. See the INPUT documentation for details. + A :class:`ValueError` can be raised if the inputs are incompatible + or insufficient. See the INPUT documentation for details. ALGORITHM: @@ -464,7 +611,7 @@ def rearrangement(p, ambient_dim=None, lattice=None): from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ - (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + ambient_dim, lattice = _preprocess_args(ambient_dim, lattice) if p < 1 or p > ambient_dim or p not in ZZ: raise ValueError("order p=%s should be an integer between 1 " @@ -481,11 +628,11 @@ def schur(ambient_dim=None, lattice=None): The Schur cone in ``ambient_dim`` dimensions, or living in ``lattice``. - The Schur cone in `n` dimensions induces the majorization ordering - on the ambient space. If `\left\{e_{1}, e_{2}, \ldots, + The Schur cone in `n` dimensions induces the majorization + preordering on the ambient space. If `\left\{e_{1}, e_{2}, \ldots, e_{n}\right\}` is the standard basis for the space, then its generators are `\left\{e_{i} - e_{i+1}\ |\ 1 \le i \le - n-1\right\}`. Its dual is the downward monotonic cone. + n-1\right\}`. Its dual is the downward monotone cone. INPUT: @@ -499,19 +646,24 @@ def schur(ambient_dim=None, lattice=None): rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. - A ``ValueError`` is raised if neither ``ambient_dim`` nor - ``lattice`` are specified. It is also a ``ValueError`` to specify - both ``ambient_dim`` and ``lattice`` unless the rank of + A :class:`ValueError` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a :class:`ValueError` to + specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: - A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the Schur - cone living in ``lattice``, with ambient dimension ``ambient_dim``. - Each generating ray has the integer ring as its base ring. + A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` + representing the Schur cone living in ``lattice``, with ambient + dimension ``ambient_dim``. Each generating ray has the integer + ring as its base ring. + + A :class:`ValueError` can be raised if the inputs are incompatible + or insufficient. See the INPUT documentation for details. + + .. SEEALSO:: - A ``ValueError`` can be raised if the inputs are incompatible or - insufficient. See the INPUT documentation for details. + :func:`downward_monotone` REFERENCES: @@ -537,13 +689,13 @@ def schur(ambient_dim=None, lattice=None): sage: abs(actual - expected).n() < 1e-12 True - The dual of the Schur cone is the "downward monotonic cone" + The dual of the Schur cone is the downward-monotone cone [GS2010]_, whose elements' entries are in non-increasing order:: sage: ambient_dim = ZZ.random_element(10) sage: K = cones.schur(ambient_dim).dual() - sage: x = K.random_element() - sage: all( x[i] >= x[i+1] for i in range(ambient_dim-1) ) + sage: J = cones.downward_monotone(ambient_dim, K.lattice()) + sage: K.is_equivalent(J) True TESTS: @@ -553,21 +705,25 @@ def schur(ambient_dim=None, lattice=None): sage: cones.schur(0).is_trivial() True - The Schur cone induces the majorization ordering, as in Iusem - and Seeger's [IS2005]_ Example 7.3:: + The Schur cone induces the majorization preordering, as in Iusem + and Seeger's [IS2005]_ Example 7.3 or Niezgoda's [Niez1998]_ + Example 2.2:: + sage: ambient_dim = ZZ.random_element(10) + sage: V = VectorSpace(QQ, ambient_dim) + sage: rearrange = lambda z: V(sorted(z.list(),reverse=True)) sage: def majorized_by(x,y): + ....: x = rearrange(x) + ....: y = rearrange(y) ....: return (all(sum(x[0:i]) <= sum(y[0:i]) ....: for i in range(x.degree()-1)) ....: and sum(x) == sum(y)) - sage: ambient_dim = ZZ.random_element(10) - sage: V = VectorSpace(QQ, ambient_dim) sage: S = cones.schur(ambient_dim) sage: majorized_by(V.zero(), S.random_element()) True sage: x = V.random_element() sage: y = V.random_element() - sage: majorized_by(x,y) == ( (y-x) in S ) + sage: majorized_by(x,y) == ((rearrange(y) - rearrange(x)) in S) True If a ``lattice`` was given, it is actually used:: @@ -596,7 +752,7 @@ def schur(ambient_dim=None, lattice=None): from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ - (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + ambient_dim, lattice = _preprocess_args(ambient_dim, lattice) def _f(i,j): if i == j: @@ -629,19 +785,19 @@ def trivial(ambient_dim=None, lattice=None): rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. - A ``ValueError`` is raised if neither ``ambient_dim`` nor - ``lattice`` are specified. It is also a ``ValueError`` to specify - both ``ambient_dim`` and ``lattice`` unless the rank of + A :class:`ValueError` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a :class:`ValueError` to + specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: - A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the - trivial cone with no nonzero generators living in ``lattice``, - with ambient dimension ``ambient_dim``. + A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` + representing the trivial cone with no nonzero generators living in + ``lattice``, with ambient dimension ``ambient_dim``. - A ``ValueError`` can be raised if the inputs are incompatible or - insufficient. See the INPUT documentation for details. + A :class:`ValueError` can be raised if the inputs are incompatible + or insufficient. See the INPUT documentation for details. EXAMPLES: @@ -684,6 +840,6 @@ def trivial(ambient_dim=None, lattice=None): """ from sage.geometry.cone import Cone - (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + ambient_dim, lattice = _preprocess_args(ambient_dim, lattice) return Cone([], lattice) diff --git a/src/sage/geometry/cone_critical_angles.py b/src/sage/geometry/cone_critical_angles.py new file mode 100644 index 00000000000..3e39102426a --- /dev/null +++ b/src/sage/geometry/cone_critical_angles.py @@ -0,0 +1,1024 @@ +r""" +Find maximal angles between polyhedral convex cones + +.. WARNING:: + + This module is considered internal and its contents are subject to + change at any time without (deprecation) warning. The stable + interface is + :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`. + +Finding the maximal (or equivalently, the minimal) angle between two +polyhedral convex cones is a hard nonconvex optimization problem. The +problem for a single cone was introduced in [IS2005]_, and was later +extended in [SS2016]_ to two cones as a generalization of the +principal angle between two vector subspaces. + +Seeger and Sossa proposed an algorithm in [SS2016]_ to find maximal +angles, and [Or2020]_ elaborates on that algorithm. It is this latest +improvement that is implemented (more or less) by this module. The +fact that perturbations of pointed cones may not change the answer too +much [Or2024]_ is taken into consideration to avoid pathological +cases. + +This module is internal to SageMath; the interface presented to users +consists of a public method, +:meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle` for +polyhedral convex cones. Even though all of the functions in this +module are internal, some are more internal than others. There are a +few functions that are used only in doctests, and not by any code that +an end-user would run. Breaking somewhat with tradition, only those +methods have been prefixed with an underscore. +""" + +from sage.functions.trig import arccos, cos +from sage.matrix.constructor import matrix +from sage.misc.misc import powerset +from sage.rings.integer_ring import ZZ +from sage.rings.qqbar import AA +from sage.rings.rational_field import QQ +from sage.rings.real_double import RDF +from sage.symbolic.constants import pi + +def _normalize_gevp_solution(gevp_solution): + r""" + Normalize the results of :func:`solve_gevp_nonzero` and + :func:`_solve_gevp_naive`. + + Those two functions return solutions (pairs of vectors) to an + eigenvalue problem, but eigenvectors are only unique up to a + scalar multiple. This function normalizes those results so that + every eigenvector has a leading entry of positive one. This allows + us to identity equivalent solutions to those problems. + + INPUT: + + A quartet ``gevp_solution`` whose components are, in order: + + - ``eigenvalue`` -- ignored + + - ``xi`` -- first component of the `( \xi, \eta )` eigenvector + + - ``eta`` -- second component of the `( \xi, \eta )` eigenvector + + - ``multiplicity`` -- ignored + + OUTPUT: + + If `c` is the first nonzero component of the concatenated `( \xi, + \eta )` vector, then a quartet whose components are, in order: + + - ``eigenvalue`` -- the unmodified ``eigenvalue`` argument + + - ``xi*(1/c)`` -- the `\xi` component normalized so that the first + nonzero component of the concatenated vector + `( \xi, \eta )` is positive one + + - ``eta*(1/c)`` -- the `\eta` component normalized so that the + first nonzero component of the concatenated vector + `( \xi, \eta )` is positive one + + - ``multiplicity`` -- the unmodified ``multiplicity`` argument + + If there is no such `c` (that is, if both `\xi` and `\eta` are + zero), then the entire input quartet is returned unmodified. + + EXAMPLES:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution) + sage: s1 = (-1, vector(QQ,[0,-2]), vector(QQ,[1]), 1) + sage: _normalize_gevp_solution(s1) + (-1, (0, 1), (-1/2), 1) + sage: s2 = (1, vector(QQ,[0,0]), vector(QQ,[0,0,-1]), 2) + sage: _normalize_gevp_solution(s2) + (1, (0, 0), (0, 0, 1), 2) + """ + eigenvalue, xi, eta, multiplicity = gevp_solution + from itertools import chain + + # We'll use this default of zero as the scaling factor if we don't + # find a better one; that is, if xi = eta = 0 -- in which case the + # additional multiplication by zero is a no-op. + scale = 0 + for c in chain(xi, eta): + if c != 0: + scale = ~c + break + + xi *= scale + xi.set_immutable() + eta *= scale + eta.set_immutable() + return (eigenvalue, xi, eta, multiplicity) + + +def _random_admissible_cone(ambient_dim): + r""" + Generate a random cone in a lattice of dimension + ``ambient_dim`` that isn't trivial. + + This is a convenience method used to simplify some test cases. The + number of rays that the cone possesses is limited to two more than + ``ambient_dim``; so, for example, you will not get more than five + rays in a three-dimensional space. This limits the amount of time + spent in any one test case. In contrast with the definition in + [Or2020]_ we consider the full ambient space to be admissible (it + doesn't hurt anything, and [Or2024]_ was forced to allow it). + + INPUT: + + - ``ambient_dim`` -- a positive integer representing the dimension + of the ambient lattice in which the returned cone lives + + OUTPUT: + + A "random" nontrivial closed convex cone in a lattice of dimension + ``ambient_dim``. + + A :class:`ValueError` is raised if ``ambient_dim`` is not + positive. + + EXAMPLES: + + The result has all of the desired properties:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: K = _random_admissible_cone(5) + sage: K.lattice_dim() + 5 + sage: K.is_trivial() + False + + Unless the ``ambient_dim`` argument is nonsense:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: K = _random_admissible_cone(0) + Traceback (most recent call last): + ... + ValueError: there are no nontrivial cones in dimension 0 + """ + if ambient_dim < 1 or ambient_dim not in ZZ: + # The random_cone() method already crashes if we ask the + # impossible of it, but having this here emits a more sensible + # error message. + raise ValueError("there are no nontrivial cones in dimension %d" + % ambient_dim) + + args = { 'min_ambient_dim': ambient_dim, + 'max_ambient_dim': ambient_dim, + 'min_rays': 1, + 'max_rays': ambient_dim+2 } + + from sage.geometry.cone import random_cone + return random_cone(**args) + + return K + + +def gevp_licis(G): + r""" + Return all nonempty subsets of indices for the columns of + ``G`` that correspond to linearly independent sets (of columns of + ``G``). + + Mnemonic: linearly independent column-index subsets (LICIS). + + The returned lists are all sorted in the same (the natural) order; + and are returned as lists so that they may be used to index into + the rows/columns of matrices. + + INPUT: + + - ``G`` -- the matrix whose linearly independent column index sets + we want + + OUTPUT: + + A generator that returns sorted lists of natural numbers. Each + generated list ``I`` is a set of indices corresponding to columns + of ``G`` that, when considered as a set, is linearly independent. + + EXAMPLES: + + The linearly independent subsets of the matrix corresponding to a + line (with two generators pointing in opposite directions) are the + one-element subsets, since the only two-element subset isn't + linearly independent:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = Cone([(1,0),(-1,0)]) + sage: G = matrix.column(K.rays()) + sage: list(gevp_licis(G)) + [[0], [1]] + + The matrix for the trivial cone has no linearly independent + subsets, since we require them to be nonempty:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: trivial_cone = cones.trivial(0) + sage: trivial_cone.is_trivial() + True + sage: list(gevp_licis(matrix.column(trivial_cone.rays()))) + [] + + All rays in the nonnegative orthant of `R^{n}` are + linearly independent, so we should get back `2^{n} - 1` subsets + after accounting for the absence of the empty set:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = cones.nonnegative_orthant(3) + sage: G = matrix.column(K.rays()) + sage: len(list(gevp_licis(G))) == 2^(K.nrays()) - 1 + True + + TESTS: + + All sets corresponding to the returned indices should be linearly + independent:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = random_cone(max_rays=8) + sage: G = matrix.column(K.rays()) + sage: all( len(s) == K.rays(s).dimension() for s in gevp_licis(G) ) + True + """ + from sage.matroids.linear_matroid import LinearMatroid + + # There's a fast implementation of this for matroids, but we need + # to drop the empty set from its output and convert the rest to + # lists that are all sorted in the same order. + return map(sorted, filter(bool, LinearMatroid(G).independent_sets())) + + +def _solve_gevp_naive(GG, HH, M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ in a very naive way, by (slowly) inverting the matrices + and finding the eigenvalues in the product space. + + This is used only for testing, to ensure that the smart way of + solving the generalized eigenvalue problem via + :func:`solve_gevp_zero` and :func:`solve_gevp_nonzero` returns the + same answers as the dumb way. + + This returns a generator, like :func:`solve_gevp_zero` and + :func:`solve_gevp_nonzero`. + + INPUT: + + See the arguments for :func:`solve_gevp_nonzero`. + + ALGORITHM: + + We construct the two matrices `A` and `B` in Theorem 3 [Or2020]_ + in block form, and then use the naive "inverse" method on `B` to + move it to the left and obtain `M = B^{-1}A`. We then compute the + right eigenvectors of the whole big matrix `M`. + + EXAMPLES: + + A simple usage example, that also appears as Example 3 in + [Or2020]_:: + + sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive + sage: K = cones.nonnegative_orthant(2) + sage: G = matrix.column(K.rays()) + sage: GG = G.transpose() * G + sage: I = [0] + sage: J = [1] + sage: list(_solve_gevp_naive(GG,GG,GG,I,J)) + [(0, (1), (0), 2), (0, (0), (1), 2)] + + Check Example 4 [Or2020]_ symbolically to ensure that we get + eigenspaces of dimension `n=2` corresponding to the eigenvalues + `\cos\theta = -1` and `\cos\theta = 1`:: + + sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive + sage: g11,g12,g21,g22 = SR.var('g11,g12,g21,g22', domain='real') + sage: h11,h12,h21,h22 = SR.var('h11,h12,h21,h22', domain='real') + sage: gs = [[g11,g12], [g21,g22]] + sage: hs = [[h11,h12], [h21,h22]] + sage: G = matrix.column(gs) + sage: H = matrix.column(hs) + sage: GG = G.transpose() * G + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: I = [0, 1] + sage: J = [0, 1] + sage: all( v in [-1,1] and m == 2 + ....: for (v,_,_,m) in _solve_gevp_naive(GG,HH,M,I,J) ) + True + """ + A = matrix.block([ + [ZZ.zero(), M[I,J]], + [M.transpose()[J,I], ZZ.zero()] + ]) + B = matrix.block([ + [GG[I,I], ZZ.zero()], + [ZZ.zero(), HH[J,J]] + ]) + M = B.inverse() * A + + # We'll format the result to match the solve_gevp_nonzero() return value. + for (evalue, evectors, multiplicity) in M.eigenvectors_right(): + for z in evectors: + xi = z[0:len(I)] + xi.set_immutable() + eta = z[len(I):] + eta.set_immutable() + yield (evalue, xi, eta, multiplicity) + + +def solve_gevp_zero(M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ for a zero eigenvalue using Propositions 3 and 4 + [Or2020]_. + + INPUT: + + - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `h_{j}` as in Proposition 6 [Or2020]_ + + - ``I`` -- a linearly independent column-index set for the matrix + `G` that appears in Theorem 3 [Or2020]_ + + - ``J`` -- a linearly independent column-index set for the matrix + `H` that appears in Theorem 3 [Or2020]_ + + OUTPUT: + + A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets + where + + - ``eigenvalue`` is zero (the eigenvalue of the system) + + - ``xi`` is the first (length ``len(I)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``eta`` is the second (length ``len(J)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``multiplicity`` is the dimension of the eigenspace associated + with ``eigenvalue`` + + ALGORITHM: + + Proposition 4 in [Or2020]_ is used. + + EXAMPLES: + + This particular configuration results in the zero matrix in the + eigenvalue problem, so the only solutions correspond to the + eigenvalue zero:: + + sage: from sage.geometry.cone_critical_angles import solve_gevp_zero + sage: K = cones.nonnegative_orthant(2) + sage: G = matrix.column(K.rays()) + sage: GG = G.transpose() * G + sage: I = [0] + sage: J = [1] + sage: list(solve_gevp_zero(GG, I, J)) + [(0, (1), (0), 2), (0, (0), (1), 2)] + """ + # A Cartesian product would be more appropriate here, but Sage + # isn't smart enough to figure out a basis for the product. So, + # we use the direct sum and then chop it up. + M_IJ = M[I,J] + xi_space = M_IJ.left_kernel() + eta_space = M_IJ.right_kernel() + + fake_cartprod = xi_space.direct_sum(eta_space) + multiplicity = fake_cartprod.dimension() + + for z in fake_cartprod.basis(): + z1 = z[0:len(I)] + z1.set_immutable() + z2 = z[len(I):] + z2.set_immutable() + + # The base ring of M will either be RDF or AA, which is enough + # to contain any eigenvalues that will arise... meaning that + # if we use the corresponding "zero" here, it will match the + # field of the eigenvalues returned by the nonzero function. + yield (M.base_ring().zero(), z1, z2, multiplicity) + + +def solve_gevp_nonzero(GG, HH, M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ for a nonzero eigenvalue using Propositions 3 and 5 + [Or2020]_. + + INPUT: + + - ``GG`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `g_{j}`, which are in turn the `i`-th and `j`-th + columns of the matrix `G` in Theorem 3 [Or2020]_ + + - ``HH`` -- the matrix whose `(i,j)`-th entry is the inner product + of `h_{i}` and `h_{j}`, which are in turn the `i`-th and `j`-th + columns of the matrix `H` in Theorem 3 [Or2020]_ + + - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `h_{j}` as in Proposition 6 in [Or2020]_ + + - ``I`` -- a linearly independent column-index set for the matrix + `G` that appears in Theorem 3 [Or2020]_ + + - ``J`` -- a linearly independent column-index set for the matrix + `H` that appears in Theorem 3 [Or2020]_ + + OUTPUT: + + A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets + where + + - ``eigenvalue`` is a real eigenvalue of the system + + - ``xi`` is the first (length ``len(I)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``eta`` is the second (length ``len(J)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``multiplicity`` is the dimension of the eigenspace associated + with ``eigenvalue`` + + Note that we do not return a basis for each eigenspace along with + its eigenvalue. For the application we have in mind, an eigenspace + of dimension greater than one (so, ``multiplicity > 1``) is an + error. As such, our return value is optimized for convenience in + the non-error case, where there is only one eigenvector (spanning + a one-dimensional eigenspace) associated with each eigenvalue. + + ALGORITHM: + + According to Proposition 5 [Or2020]_, the solutions corresponding + to non-zero eigenvalues can be found by solving a smaller + eigenvalue problem in only the variable `\xi`. So, we do that, and + then solve for `\eta` in terms of `\xi` as described in the + proposition. + + EXAMPLES: + + When the zero solutions are included, this function returns the + same solutions as the naive method on the Schur cone in three + dimensions:: + + sage: from itertools import chain + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution, + ....: _solve_gevp_naive, + ....: gevp_licis, + ....: solve_gevp_nonzero, + ....: solve_gevp_zero) + sage: K = cones.schur(3) + sage: gs = [g.change_ring(AA).normalized() for g in K] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: G_index_sets = list(gevp_licis(G)) + sage: all( + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: chain( + ....: solve_gevp_zero(GG, I, J), + ....: solve_gevp_nonzero(GG, GG, GG, I, J) + ....: ) + ....: ) + ....: == + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: _solve_gevp_naive(GG,GG,GG,I,J) + ....: ) + ....: for I in G_index_sets + ....: for J in G_index_sets + ....: ) + True + + TESTS: + + This function should return the same solutions (with zero included, + of course) as the naive implementation even for random cones:: + + sage: # long time + sage: from itertools import chain + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution, + ....: _random_admissible_cone, + ....: _solve_gevp_naive, + ....: gevp_licis, + ....: solve_gevp_nonzero, + ....: solve_gevp_zero) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(AA).normalized() for g in P] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: hs = [h.change_ring(AA).normalized() for h in Q] + sage: H = matrix.column(hs) + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: G_index_sets = list(gevp_licis(G)) + sage: H_index_sets = list(gevp_licis(H)) + sage: all( + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: chain( + ....: solve_gevp_zero(M, I, J), + ....: solve_gevp_nonzero(GG, HH, M, I, J) + ....: ) + ....: ) + ....: == + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: _solve_gevp_naive(GG, HH, M, I, J) + ....: ) + ....: for I in G_index_sets + ....: for J in H_index_sets + ....: ) + True + + According to Proposition 7 [Or2020]_, the only eigenvalues that + arise when either ``G`` or ``H`` is invertible are `-1`, `0`, and + `1`:: + + sage: # long time + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone, + ....: gevp_licis, + ....: solve_gevp_nonzero) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(AA).normalized() for g in P] + sage: hs = [h.change_ring(AA).normalized() for h in Q] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: H = matrix.column(hs) + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: from itertools import product + sage: all( + ....: (v in [-1,0,1] + ....: for (v,_,_,_) in solve_gevp_nonzero(GG, HH, M, I, J)) + ....: for (I,J) in product(gevp_licis(G),gevp_licis(H)) + ....: if len(I) == n or len(J) == n ) + True + """ + if len(J) < len(I): + # We can always opt to solve the smaller problem. Reading the + # first three assignments below, you should be able to + # convince yourself that switching GG <-> HH, I <-> J, and + # transposing M does in fact switch from the "xi problem" to + # the "eta problem." + yield from ((l, xi, eta, m) + for (l, eta, xi, m) + in solve_gevp_nonzero(HH, GG, M.transpose(), J, I)) + else: + M_IJ = M[I,J] + G_I_pinv_H_J = GG[I,I].inverse_positive_definite() * M_IJ + H_J_pinv_G_I = HH[J,J].inverse_positive_definite() * M_IJ.transpose() + L = (G_I_pinv_H_J * H_J_pinv_G_I) + + for (sigma, xis, m) in L.eigenvectors_right(): + if sigma > 0: + # Avoid recomputing these for each xi in xis + sigma_sqrt = sigma.sqrt() + inv_sqrt = ~sigma_sqrt + pm_sqrt_inv_pairs = [ + (-sigma_sqrt, -inv_sqrt), + (sigma_sqrt, inv_sqrt) + ] + + for xi in xis: + for l, li in pm_sqrt_inv_pairs: + eta = li * H_J_pinv_G_I*xi + eta.set_immutable() + yield (l, xi, eta, m) + + +def compute_gevp_M(gs, hs): + r""" + Compute the matrix `M` whose `(i,j)`-th entry is the inner + product of ``gs[i]`` and ``hs[j]``. + + This is the "generalized Gram matrix" appearing in Proposition 6 + in [Or2020]_. For efficiency, we also return the minimal pair, + whose inner product is minimal among the entries of `M`. This + allows our consumer to bail out immediately (knowing the optimal + pair!) if it turns out that the maximal angle is acute; i.e. if + the smallest entry of `M` is nonnegative. + + INPUT: + + - ``gs`` -- a linearly independent list of unit-norm generators + for the cone `P` + + - ``hs`` -- a linearly independent list of unit-norm generators + for the cone `Q` + + OUTPUT: + + A tuple containing four elements, in order: + + - The matrix `M` described in Proposition 6 + + - The minimal entry in the matrix `M` + + - A vector in ``gs`` that achieves that minimal inner product + along with the next element of the tuple + + - A vector in ``hs`` that achieves the minimal inner product + along with the previous element in the tuple + + EXAMPLES:: + + sage: from sage.geometry.cone_critical_angles import compute_gevp_M + sage: P = Cone([ (1,2,0), (3,4,0) ]) + sage: Q = Cone([ (-1,4,1), (5,-2,-1), (-1,-1,5) ]) + sage: gs = [g.change_ring(QQ) for g in P] + sage: hs = [h.change_ring(QQ) for h in Q] + sage: M = compute_gevp_M(gs, hs)[0] + sage: all( M[i][j] == gs[i].inner_product(hs[j]) + ....: for i in range(P.nrays()) + ....: for j in range(Q.nrays()) ) + True + + TESTS: + + The products `(G_{I})^{T}H_{J}` correspond to + submatrices of the "generalized Gram matrix" `M` in Proposition + 6. Note that SageMath does (row,column) indexing but [Or2020]_ + does (column,row) indexing:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone, + ....: compute_gevp_M, + ....: gevp_licis) + sage: n = ZZ.random_element(1,4) + sage: n = ZZ.random_element(1,8) # long time + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(QQ) for g in P] + sage: hs = [h.change_ring(QQ) for h in Q] + sage: M = compute_gevp_M(gs,hs)[0] + sage: f = lambda i,j: gs[i].inner_product(hs[j]) + sage: expected_M = matrix(QQ, P.nrays(), Q.nrays(), f) + sage: M == expected_M + True + sage: G = matrix.column(gs) + sage: H = matrix.column(hs) + sage: def _test_indexing(I,J): + ....: G_I = G.matrix_from_columns(I) + ....: H_J = H.matrix_from_columns(J) + ....: return (G_I.transpose()*H_J == M[I,J] + ....: and + ....: H_J.transpose()*G_I == M.transpose()[J,I]) + sage: G_index_sets = list(gevp_licis(G)) + sage: H_index_sets = list(gevp_licis(H)) + sage: all( _test_indexing(I,J) for I in G_index_sets + ....: for J in H_index_sets ) + True + """ + min_u = gs[0] + min_v = hs[0] + min_ip = min_u.inner_product(min_v) + + M = [] + for g in gs: + M_i = [] + for h in hs: + val = g.inner_product(h) + M_i.append(val) + if (val < min_ip): + min_ip = val + min_u = g + min_v = h + M.append(M_i) + + return (matrix(M), min_ip, min_u, min_v) + + +def check_gevp_feasibility(cos_theta, xi, eta, G_I, G_I_c_T, + H_J, H_J_c_T, epsilon): + r""" + Determine if a solution to the generalized eigenvalue problem + in Theorem 3 [Or2020]_ is feasible. + + Implementation detail: we take four matrices that we are capable + of computing as parameters instead, because we will be called in a + nested loop "for all `I`... and for all `J`..." The data + corresponding to `I` should be computed only once, which means + that we can't do it here -- it needs to be done outside of the `J` + loop. For symmetry (and to avoid relying on too many + cross-function implementation details), we also insist that the + `J` data be passed in. + + INPUT: + + - ``cos_theta`` -- an eigenvalue corresponding to + `( \xi, \eta )` + + - ``xi`` -- first component of the `( \xi, \eta )` eigenvector + + - ``eta`` -- second component of the `( \xi, \eta )` eigenvector + + - ``G_I`` -- the submatrix of `G` with columns indexed by `I` + + - ``G_I_c_T`` -- a matrix whose rows are the non-`I` columns of `G` + + - ``H_J`` -- the submatrix of `H` with columns indexed by `J` + + - ``H_J_c_T`` -- a matrix whose rows are the non-`J` columns of `H` + + - ``epsilon`` -- the tolerance to use when making comparisons + + OUTPUT: + + A triple containing (in order), + + - a boolean, + - a vector in the cone `P` (of the same length as ``xi``), and + - a vector in the cone `Q` (of the same length as ``eta``). + + If `( \xi, \eta )` is feasible, we return ``(True, u, v)`` where `u` + and `v` are the vectors in `P` and `Q` respectively that form the + the angle `\theta`. + + If `( \xi, \eta )` is **not** feasible, then we return ``(False, 0, 0)`` + where ``0`` should be interpreted to mean the zero vector in the + appropriate space. + + EXAMPLES: + + If `\xi` has any components less than "zero," it isn't feasible:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [-1,1]) + sage: eta = vector(QQ, [1,1,1]) + sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0) + (False, (0, 0), (0, 0, 0)) + + If `\eta` has any components less than "zero," it isn't feasible:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [2]) + sage: eta = vector(QQ, [1,-4,4,5]) + sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0) + (False, (0), (0, 0, 0, 0)) + + If `\xi` and `\eta` are equal and if `G_{I}` and `H_{J}` are not, + then the copy of `\eta` that's been scaled by the norm of `G_{I}\xi` + generally won't satisfy its norm-equality constraint:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1,1]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,2) + sage: H_J = 2*G_I + sage: check_gevp_feasibility(0,xi,eta,G_I,None,H_J,None,0) + (False, (0, 0), (0, 0)) + + When `\cos\theta` is zero, the inequality (42) in Theorem 7.3 + [SS2016]_ is just an inner product with `v` which we can make + positive by ensuring that all of the entries of `H_{J}` are + positive. So, if any of the rows of ``G_I_c_T`` contain a negative + entry, (42) will fail:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,-1,0,0]]) + sage: H_J = G_I + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,None,0) + (False, (0, 0, 0, 0), (0, 0, 0, 0)) + + Likewise we can make (43) fail in exactly the same way:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,1,0,0]]) + sage: H_J = G_I + sage: H_J_c_T = matrix(QQ, [[0,-1,0,0]]) + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) + (False, (0, 0, 0, 0), (0, 0, 0, 0)) + + Finally, if we ensure that everything works, we get back a feasible + result along with the vectors (scaled `\xi` and `\eta`) that worked:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,1,0,0]]) + sage: H_J = G_I + sage: H_J_c_T = matrix(QQ, [[0,1,0,0]]) + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) + (True, (1/2, 1/2, 1/2, 1/2), (1/2, 1/2, 1/2, 1/2)) + """ + infeasible_result = (False, 0*xi, 0*eta) + if min(xi) <= -epsilon or min(eta) <= -epsilon: + # xi or eta isn't in the interior of the nonnegative orthant, + # so skip this (non-)solution. + return infeasible_result + + # Rescale xi to satisfy (44), and rescale eta by the same amount, + # because (xi,eta) needs to remain in the same one-dimensional + # eigenspace. + scale = ~((G_I*xi).norm()) + xi_hat = xi * scale + eta_hat = eta * scale + + # Now check that (45) is satisfied. + if ((H_J*eta_hat).norm() - 1).abs() > epsilon: + return infeasible_result + + # And check that (42,43) are satisfied. + v = H_J * eta_hat + rhs = v - cos_theta*G_I*xi_hat + + if any(x < -epsilon for x in G_I_c_T * rhs): + return infeasible_result + + u = G_I * xi_hat + rhs = u - cos_theta*H_J*eta_hat + if any(x < -epsilon for x in H_J_c_T * rhs): + return infeasible_result + + return (True, u, v) + + +def max_angle(P, Q, exact, epsilon): + r""" + Find the maximal angle between the cones `P` and `Q`. + + This implements + :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`, + which should be fully documented. + + EXAMPLES: + + For the sake of the user interface, the argument validation for + this function is performed in the associated cone method; we can + therefore crash it by feeding it invalid input like an + inadmissible cone:: + + sage: from sage.geometry.cone_critical_angles import max_angle + sage: K = cones.trivial(3) + sage: max_angle(K,K,True,0) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + # The lattice dimensions of P and Q are guaranteed to be equal + # because the cone method checks it before calling us. + n = P.lattice_dim() + + ring = RDF + if exact: + ring = AA + # For some reason we can go RR -> QQ -> AA, but not + # straight from RR to AA. + epsilon = QQ(epsilon) + epsilon = ring(epsilon) + + # First check if P is contained in the dual of Q. Keep track of + # the minimum inner product (and associated vectors) while doing + # so; then if P is contained in dual(Q), we just return the pair + # with the smallest inner product. + gs = [g.change_ring(ring).normalized() for g in P] + Q_is_P = (P == Q) # This is used again later + if Q_is_P: + hs = gs + else: + hs = [h.change_ring(ring).normalized() for h in Q] + + (M, min_ip, min_u, min_v) = compute_gevp_M(gs,hs) + + if min_ip >= 0: # The maximal angle is acute! + return (arccos(min_ip), min_u, min_v) + + # Also check to see if the maximal angle is pi. In particular this + # is true when either P or Q is the entire ambient space. + P_and_negative_Q = P.intersection(-Q) + if not (P_and_negative_Q.is_trivial()): + u = P_and_negative_Q.ray(0).change_ring(ring).normalized() + v = -u + return (pi, u, v) + + # When P == Q, GG and HH are both just M. We rule out the + # cardinality ``n`` in the index sets because it will eventually + # result in a GEVP whose only solutions are lambda in {-1,0,1}; + # none of which we want! We rule out 0 and 1 with the acute check, + # and -1 with the pi (P_and_negative_Q) check. + # + # It's VERY IMPORTANT that we construct lists from the index set + # generators, because we're going to use them in a nested loop! + G = matrix.column(gs) + G_index_sets = [s for s in gevp_licis(G) if not len(s) == n] + + if Q_is_P: + GG = M + H = G + HH = M + H_index_sets = G_index_sets + else: + GG = G.transpose() * G + H = matrix.column(hs) + HH = H.transpose() * H + H_index_sets = [s for s in gevp_licis(H) if not len(s) == n] + + # Keep track of the (cos-theta, xi, eta, multiplicity) tuples with + # multiplicity > 1. These are only a problem if they could + # potentially be maximal. Therefore, we want to inspect them AFTER + # we've checked all of the multiplicity=1 angles and found the + # largest. This allows us to ignore most of the problematic + # eigenspaces, independent of the order in which we run through I,J. + big_eigenspaces = [] + + for I in G_index_sets: + G_I = G.matrix_from_columns(I) + I_complement = [i for i in range(P.nrays()) if i not in I] + G_I_c_T = G.matrix_from_columns(I_complement).transpose() + + for J in H_index_sets: + J_complement = [j for j in range(Q.nrays()) if j not in J] + H_J = H.matrix_from_columns(J) + H_J_c_T = H.matrix_from_columns(J_complement).transpose() + + for (cos_theta,xi,eta,mult) in solve_gevp_nonzero(GG, HH, M, I, J): + + if cos_theta >= min_ip: + # This potential critical angle is smaller than or + # equal to one that we've already found. Why + # bother? + continue + + if cos_theta == -1: + # We already ruled this case out with the + # "P_and_negative_Q" trick. + continue + + (is_feasible, u, v) = check_gevp_feasibility(cos_theta, + xi, + eta, + G_I, + G_I_c_T, + H_J, + H_J_c_T, + epsilon) + + if is_feasible: + min_ip = cos_theta + min_u = u + min_v = v + elif mult > 1: + # Save this for later. The eigenvalue cos_theta + # might be so big that we can ignore it. + big_eigenspaces.append((cos_theta, xi, eta, mult)) + continue + + for (cos_theta, xi, eta, mult) in big_eigenspaces: + if cos_theta < min_ip: + # The existence of a big eigenspace is only a problem if + # cos_theta could actually be minimal. + + if exact and P.is_strictly_convex(): + if Q_is_P or Q.is_strictly_convex(): + # The maximal angle composed with the conic hull + # is continuous at (gs,hs), so we can retry with + # inexact arithmetic. That will "perturb" + # everything, hopefully eliminating any larger + # eigenspaces and without changing the answer too + # much. + return max_angle(P, Q, False, epsilon) + + # Either we don't know that the maximal angle of the conic + # hull is continuous at (gs,hs), or we're already using + # inexact arithmetic. There's nothing left to try. (Note + # that the case where either P or Q is the ambient space + # was handled much earlier, since in that case the maximal + # angle is obviously pi.) + raise ValueError('eigenspace of dimension %d > 1 ' + 'corresponding to eigenvalue %s' + % (mult, cos_theta)) + + return (arccos(min_ip), min_u, min_v) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 832a76bb65d..0fffcdeb58c 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -238,6 +238,8 @@ from copy import copy from warnings import warn +import sage.geometry.abc + from sage.structure.richcmp import richcmp_method, richcmp from sage.combinat.combination import Combinations from sage.combinat.posets.posets import FinitePoset @@ -245,7 +247,6 @@ Cone, ConvexRationalPolyhedralCone, IntegralRayCollection, - is_Cone, normalize_rays) from sage.geometry.hasse_diagram import lattice_from_incidences from sage.geometry.point_collection import PointCollection @@ -571,7 +572,7 @@ def result(): cones = ((), ) rays = () return result() - if is_Cone(cones[0]): + if isinstance(cones[0], sage.geometry.abc.ConvexRationalPolyhedralCone): # Construct the fan from Cone objects if lattice is None: lattice = cones[0].lattice() @@ -749,11 +750,10 @@ def FaceFan(polytope, lattice=None): ValueError: face fans are defined only for polytopes containing the origin as an interior point! """ - from sage.geometry.lattice_polytope import is_LatticePolytope interior_point_error = ValueError( "face fans are defined only for polytopes containing " "the origin as an interior point!") - if is_LatticePolytope(polytope): + if isinstance(polytope, sage.geometry.abc.LatticePolytope): if any(d <= 0 for d in polytope.distances([0] * polytope.dim())): raise interior_point_error cones = (f.ambient_vertex_indices() for f in polytope.facets()) @@ -843,8 +843,7 @@ def NormalFan(polytope, lattice=None): """ dimension_error = ValueError( 'the normal fan is only defined for full-dimensional polytopes') - from sage.geometry.lattice_polytope import is_LatticePolytope - if is_LatticePolytope(polytope): + if isinstance(polytope, sage.geometry.abc.LatticePolytope): if polytope.dim() != polytope.lattice_dim(): raise dimension_error rays = polytope.facet_normals() @@ -2425,7 +2424,7 @@ def embed(self, cone): ValueError: 2-d cone in 3-d lattice N does not belong to Rational polyhedral fan in 3-d lattice N! """ - if not is_Cone(cone): + if not isinstance(cone, sage.geometry.abc.ConvexRationalPolyhedralCone): raise TypeError("%s is not a cone!" % cone) if cone.ambient() is self: return cone diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index bb1e04efabe..19ae8698f8c 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -242,7 +242,7 @@ \chi(x) := \sum_{w\in P} \mu(w) x^{dim(w)} -where the sum is `P` is the +where `P` is the :meth:`~HyperplaneArrangementElement.intersection_poset` of the arrangement and `\mu` is the Möbius function of `P`:: @@ -335,7 +335,7 @@ arrangements. """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2013 David Perkinson # Volker Braun # @@ -344,25 +344,24 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** # Possible extensions for hyperplane_arrangement.py: # - the big face lattice # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields +from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane +from sage.matrix.constructor import matrix, vector +from sage.misc.cachefunc import cached_method +from sage.modules.free_module import VectorSpace +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.rational_field import QQ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ -from sage.misc.cachefunc import cached_method -from sage.matrix.constructor import matrix, vector -from sage.modules.free_module import VectorSpace -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - -from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane class HyperplaneArrangementElement(Element): @@ -385,10 +384,9 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): - ``hyperplanes`` -- a tuple of hyperplanes - - ``check`` -- boolean (optional; default ``True``); whether - to check input + - ``check`` -- boolean (default: ``True``); whether to check input - - ``backend`` -- string (optional; default: ``None``); the backend to + - ``backend`` -- string (optional); the backend to use for the related polyhedral objects EXAMPLES:: @@ -499,7 +497,7 @@ def hyperplanes(self): OUTPUT: - An integer. + A tuple EXAMPLES:: @@ -659,10 +657,10 @@ def union(self, other): sage: H. = HyperplaneArrangements(QQ) sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) - sage: A.union(B) - Arrangement of 8 hyperplanes of dimension 2 and rank 2 - sage: A | B # syntactic sugar + sage: C = A.union(B); C Arrangement of 8 hyperplanes of dimension 2 and rank 2 + sage: C == A | B # syntactic sugar + True A single hyperplane is coerced into a hyperplane arrangement if necessary:: @@ -678,9 +676,10 @@ def union(self, other): Arrangement of 6 hyperplanes of dimension 2 and rank 2 """ P = self.parent() - other = P(other) - hyperplanes = self._hyperplanes + other._hyperplanes - return P(*hyperplanes, backend=self._backend) + other_h = P(other) + hyperplanes = self._hyperplanes + other_h._hyperplanes + result = P(*hyperplanes, backend=self._backend) + return result add_hyperplane = union @@ -713,9 +712,10 @@ def cone(self, variable='t'): OUTPUT: - A new hyperplane arrangement. Its equations consist of - `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the - original arrangement and the equation `[0, 1, 0, \ldots, 0]`. + A new hyperplane arrangement `L`. + Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each + `[d, a_1, \ldots, a_n]` in the original arrangement and the + equation `[0, 1, 0, \ldots, 0]` (maybe not in this order). .. WARNING:: @@ -724,7 +724,8 @@ def cone(self, variable='t'): no guarantee that the order in which they appear in ``self.hyperplanes()`` will match the order in which their counterparts in ``self.cone()`` will appear in - ``self.cone().hyperplanes()``! + ``self.cone().hyperplanes()``! This warning does not apply + to ordered hyperplane arrangements. EXAMPLES:: @@ -758,7 +759,7 @@ def cone(self, variable='t'): hyperplanes.append([0, 1] + [0] * self.dimension()) P = self.parent() names = (variable,) + P._names - H = HyperplaneArrangements(self.parent().base_ring(), names=names) + H = type(P).__base__(P.base_ring(), names=names) return H(*hyperplanes, backend=self._backend) @cached_method @@ -853,16 +854,16 @@ def intersection_poset(self, element_label="int"): W = Vector space of dimension 2 over Rational Field] """ if element_label == "int": - def update(mapping, val, I): + def update(mapping, val, I0): mapping[val] = len(mapping) elif element_label == "subset": from sage.sets.set import Set - def update(mapping, val, I): + def update(mapping, val, I0): mapping[val] = Set(val) elif element_label == "subspace": - def update(mapping, val, I): - mapping[val] = I + def update(mapping, val, I0): + mapping[val] = I0 else: raise ValueError("invalid element label type") @@ -885,13 +886,13 @@ def update(mapping, val, I): for label, T in cur_level: edges = [] for i, H in enumerate(hyperplanes): - I = H.intersection(T) - if I is not None and I != T: + I0 = H.intersection(T) + if I0 is not None and I0 != T: try: - target = new_level[I] + target = new_level[I0] except KeyError: target = set(label) - new_level[I] = target + new_level[I0] = target target.add(i) edges.append(target) hasse[label] = edges @@ -1216,7 +1217,7 @@ def deletion(self, hyperplanes): raise ValueError('hyperplane is not in the arrangement') return parent(*planes, backend=self._backend) - def restriction(self, hyperplane): + def restriction(self, hyperplane, repetitions=False): r""" Return the restriction to a hyperplane. @@ -1224,10 +1225,13 @@ def restriction(self, hyperplane): - ``hyperplane`` -- a hyperplane of the hyperplane arrangement + - ``repetitions`` -- boolean (default: ``False``); eliminate + repetitions for ordered arrangements + OUTPUT: - The restriction of the hyperplane arrangement to the given - ``hyperplane``. + The restriction `\mathcal{A}_H` of the + hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`. EXAMPLES:: @@ -1236,8 +1240,12 @@ def restriction(self, hyperplane): Arrangement of 6 hyperplanes of dimension 4 and rank 3 sage: H = A[0]; H Hyperplane 0*u + 0*x + y - z + 0 - sage: R = A.restriction(H); R + sage: R = A.restriction(H); R Arrangement + sage: A.add_hyperplane(z).restriction(z) + Arrangement of 6 hyperplanes of dimension 3 and rank 3 + sage: A.add_hyperplane(u).restriction(u) + Arrangement of 6 hyperplanes of dimension 3 and rank 3 sage: D = A.deletion(H); D Arrangement of 5 hyperplanes of dimension 4 and rank 3 sage: ca = A.characteristic_polynomial() @@ -1281,8 +1289,19 @@ def restriction(self, hyperplane): hyperplanes.append([A, b]) names = list(parent._names) names.pop(pivot) - H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) - return H(*hyperplanes, signed=False, backend=self._backend) + from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements + if isinstance(parent, OrderedHyperplaneArrangements): + H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names)) + if not repetitions: + L = list(hyperplanes) + hyperplanes = () + for h in L: + if h not in hyperplanes: + hyperplanes += (h,) + else: + H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) + result = H(*hyperplanes, signed=False, backend=self._backend) + return result def change_ring(self, base_ring): """ @@ -1665,7 +1684,8 @@ def essentialization(self): OUTPUT: - The essentialization as a new hyperplane arrangement. + The essentialization `\mathcal{A}'` of `\mathcal{A}` as a + new hyperplane arrangement. EXAMPLES:: @@ -2091,7 +2111,7 @@ def regions(self): for hyperplane in self: ieq = vector(R, hyperplane.dense_coefficient_list()) - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) if not regions: # See comment above. @@ -2129,8 +2149,8 @@ def regions(self): else: # In this case, at least one of the vertices is not on the hyperplane. # So we check if any ray or line pokes the hyperplane. - if ( any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or - any(ieq[1:]*l[:] != 0 for l in region_lines)): + if (any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or + any(ieq[1:]*ll[:] != 0 for ll in region_lines)): splits = True if splits: @@ -2158,10 +2178,10 @@ def poset_of_regions(self, B=None, numbered_labels=True): INPUT: - - ``B`` -- a region (optional; default: ``None``); if ``None``, then + - ``B`` -- a region (optional); if ``None``, then an arbitrary region is chosen as the base region. - - ``numbered_labels`` -- bool (optional; default: ``True``); if ``True``, + - ``numbered_labels`` -- bool (default: ``True``); if ``True``, then the elements of the poset are numbered. Else they are labelled with the regions themselves. @@ -2345,12 +2365,13 @@ def closed_faces(self, labelled=True): ((-1, -1), A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, (-1, -2))] - sage: a = hyperplane_arrangements.braid(3) # needs sage.graphs - sage: a.hyperplanes() # needs sage.graphs + sage: # needs sage.graphs + sage: a = hyperplane_arrangements.braid(3) + sage: a.hyperplanes() (Hyperplane 0*t0 + t1 - t2 + 0, Hyperplane t0 - t1 + 0*t2 + 0, Hyperplane t0 + 0*t1 - t2 + 0) - sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] # needs sage.graphs + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] [((0, 0, 0), A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line, (0, 0, 0)), ((0, 1, 1), A 2-dimensional polyhedron in QQ^3 defined @@ -2422,7 +2443,7 @@ def closed_faces(self, labelled=True): zero_half = Polyhedron(eqns=[ieq], base_ring=R, backend=be) # ``zero_half`` is the hyperplane ``hyperplane`` itself # (viewed as a polyhedron). - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) subdivided = [] for signs, face in faces: @@ -2904,8 +2925,9 @@ def whitney_data(self): EXAMPLES:: + sage: # needs sage.combinat sage: A = hyperplane_arrangements.Shi(3) - sage: A.whitney_data() # needs sage.combinat + sage: A.whitney_data() ( [ 1 -6 9] [ 1 6 6] [ 0 6 -15] [ 0 6 15] @@ -3247,7 +3269,7 @@ def orlik_solomon_algebra(self, base_ring=None, ordering=None, **kwds): """ if base_ring is None: base_ring = self.base_ring() - return self.matroid().orlik_solomon_algebra(base_ring, ordering,**kwds) + return self.matroid().orlik_solomon_algebra(base_ring, ordering, **kwds) def orlik_terao_algebra(self, base_ring=None, ordering=None, **kwds): """ @@ -3394,9 +3416,10 @@ def derivation_module_free_chain(self): EXAMPLES:: - sage: W = WeylGroup(['A',3], prefix='s') # needs sage.combinat sage.groups - sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups - sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['A',3], prefix='s') + sage: A = W.long_element().inversion_arrangement() + sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) [ 1 0 0] [ 0 1 0] [ 0 0 a3] @@ -3539,9 +3562,10 @@ def derivation_module_basis(self, algorithm="singular"): EXAMPLES:: - sage: W = WeylGroup(['A', 2], prefix='s') # needs sage.combinat sage.groups - sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups - sage: A.derivation_module_basis() # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['A', 2], prefix='s') + sage: A = W.long_element().inversion_arrangement() + sage: A.derivation_module_basis() [(a1, a2), (0, a1*a2 + a2^2)] TESTS: @@ -3560,7 +3584,7 @@ def derivation_module_basis(self, algorithm="singular"): ....: else: ....: assert exponents(B) == exponents(Bp) """ - alg = algorithm # prevent possible changes to a global variable + alg = algorithm # prevent possible changes to a global variable if alg == "singular": # import sage.libs.singular.function_factory # syz = sage.libs.singular.function_factory.ff.syz @@ -3575,7 +3599,7 @@ def derivation_module_basis(self, algorithm="singular"): # Check using Saito's criterion if det / f in f.parent().base_ring() and not det.is_zero(): return basis.rows() - except ValueError: # Non-square matrix or det = 0 + except ValueError: # Non-square matrix or det = 0 pass # Check if it is free if not self.is_free(algorithm=alg): @@ -3586,7 +3610,7 @@ def derivation_module_basis(self, algorithm="singular"): if alg == "BC": C = self.derivation_module_free_chain() if C is not None: - if not C: # C is an empty list + if not C: # C is an empty list S = self.parent().ambient_space().symmetric_space() return matrix.identity(S, self.dimension()).rows() from sage.misc.misc_c import prod @@ -3739,15 +3763,15 @@ def _element_constructor_(self, *args, **kwds): hyperplane; alternatively, a single polytope or a single hyperplane arrangement - - ``signed`` -- boolean (optional, default: ``True``); whether to + - ``signed`` -- boolean (default: ``True``); whether to preserve signs of hyperplane equations - - ``warn_duplicates`` -- boolean (optional, default: ``False``); + - ``warn_duplicates`` -- boolean (default: ``False``); whether to issue a warning if duplicate hyperplanes were passed -- note that duplicate hyperplanes are always removed, whether or not there is a warning shown - - ``check`` -- boolean (optional, default: ``True``); whether to + - ``check`` -- boolean (default: ``True``); whether to perform argument checking. EXAMPLES:: diff --git a/src/sage/geometry/hyperplane_arrangement/hyperplane.py b/src/sage/geometry/hyperplane_arrangement/hyperplane.py index 9ca1a7c42ac..dfd1e4c6169 100644 --- a/src/sage/geometry/hyperplane_arrangement/hyperplane.py +++ b/src/sage/geometry/hyperplane_arrangement/hyperplane.py @@ -108,9 +108,10 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +import sage.geometry.abc -from sage.misc.cachefunc import cached_method from sage.geometry.linear_expression import LinearExpression, LinearExpressionModule +from sage.misc.cachefunc import cached_method class Hyperplane(LinearExpression): @@ -460,9 +461,8 @@ def intersection(self, other): sage: h.intersection(polytopes.cube()) A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices """ - from sage.geometry.polyhedron.base import is_Polyhedron from sage.geometry.polyhedron.constructor import Polyhedron - if not is_Polyhedron(other): + if not isinstance(other, sage.geometry.abc.Polyhedron): try: other = other.polyhedron() except AttributeError: diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index cdfaca2faf0..98b03ceac70 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -156,7 +156,7 @@ def bigraphical(self, G, A=None, K=QQ, names=None): for u, v in G.edge_iterator(labels=False, sort_vertices=False): i = vertex_to_int[u] j = vertex_to_int[v] - hyperplanes.append( x[i] - x[j] - A[i][j]) + hyperplanes.append(x[i] - x[j] - A[i][j]) hyperplanes.append(-x[i] + x[j] - A[j][i]) return H(*hyperplanes) @@ -794,7 +794,7 @@ def Shi(self, data, K=QQ, names=None, m=1): hyperplanes = [] for a in PR: - for const in range(-m+1,m+1): + for const in range(-m + 1, m + 1): hyperplanes.append(sum(a[j]*x[j] for j in range(d))-const) A = H(*hyperplanes) x = polygen(QQ, 'x') diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py new file mode 100644 index 00000000000..bb16768e13b --- /dev/null +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -0,0 +1,650 @@ +r""" +Ordered Hyperplane Arrangements + +The :class:`HyperplaneArrangements` orders the hyperplanes in a arrangement +independently of the way the hyperplanes are introduced. The class +:class:`OrderedHyperplaneArrangements` fixes an order specified by +the user. This can be needed for certain properties, e.g., fundamental group with +information about meridians, braid monodromy with information about the strands; +in the future, it may be useful for combinatorial properties. +There are no other differences with usual hyperplane arrangements. + +An ordered arrangement is an arrangement where the hyperplanes are sorted +by the user:: + + sage: H0. = HyperplaneArrangements(QQ) + sage: H0(t0 - t1, t1 - t2, t0 - t2) + Arrangement + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: H(t0 - t1, t1 - t2, t0 - t2) + Arrangement + +Some methods are adapted, e.g., :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.hyperplanes`, +and some new ones are created, regarding +hyperplane sections and fundamental groups:: + + sage: H. = HyperplaneArrangements(QQ) + sage: H1. = OrderedHyperplaneArrangements(QQ) + sage: A1 = H1(x, y); A = H(A1) + sage: A.hyperplanes() + (Hyperplane 0*x + y + 0, Hyperplane x + 0*y + 0) + sage: A1.hyperplanes() + (Hyperplane x + 0*y + 0, Hyperplane 0*x + y + 0) + +We see the differences in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.union`:: + + sage: H. = HyperplaneArrangements(QQ) + sage: H1. = OrderedHyperplaneArrangements(QQ) + sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) + sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) + sage: C = A.union(B) + sage: A1 = H1(A); B1 = H1(B); C1 = A1.union(B1) + sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] + [0, 5, 6, 1, 2, 3, 7, 4] + +Also in meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.cone`:: + + sage: # needs sage.combinat + sage: a. = hyperplane_arrangements.semiorder(3) + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: a1 = H(a) + sage: b = a.cone(); b1 = a1.cone() + sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] + [0, 2, 4, 6, 1, 3, 5] + +And in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.restriction`:: + + sage: # needs sage.graphs + sage: A. = hyperplane_arrangements.braid(4) + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A1 = L(A) + sage: H = A[0]; H + Hyperplane 0*u + 0*x + y - z + 0 + sage: A.restriction(H) + Arrangement + sage: A1.restriction(H) + Arrangement + sage: A1.restriction(H, repetitions=True) + Arrangement of 5 hyperplanes of dimension 3 and rank 2 + +AUTHORS: + +- Enrique Artal (2023-12): initial version + +This module adds some features to the *unordered* one for some +properties which depend on the order. +""" + +# ***************************************************************************** +# Copyright (C) 2023 Enrique Artal +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangementElement +from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements +from sage.geometry.hyperplane_arrangement.hyperplane import Hyperplane +from sage.matrix.constructor import matrix, vector +from sage.misc.misc_c import prod +from sage.groups.free_group import FreeGroup +from sage.rings.integer_ring import ZZ +from sage.rings.qqbar import QQbar +from sage.schemes.curves.plane_curve_arrangement import AffinePlaneCurveArrangements +from sage.schemes.curves.plane_curve_arrangement import ProjectivePlaneCurveArrangements + + +class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): + """ + An ordered hyperplane arrangement. + + .. WARNING:: + + You should never create + :class:`OrderedHyperplaneArrangementElement` instances directly, + always use the parent. + """ + def __init__(self, parent, hyperplanes, check=True, backend=None): + """ + Construct an ordered hyperplane arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`OrderedHyperplaneArrangements` + + - ``hyperplanes`` -- a tuple of hyperplanes + + - ``check`` -- boolean (default ``True``); whether + to check input + + - ``backend`` -- string (default: ``None``); the backend to + use for the related polyhedral objects + + EXAMPLES:: + + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement + sage: TestSuite(elt).run() + """ + super().__init__(parent, hyperplanes, check=check, backend=backend) + self._affine_fundamental_group = None + self._affine_meridians = None + self._projective_fundamental_group = None + self._projective_meridians = None + + def hyperplane_section(self, proj=True): + r""" + Compute a generic hyperplane section of ``self``. + + INPUT: + + - ``proj`` -- (default: ``True``); if the + ambient space is affine or projective + + OUTPUT: + + An arrangement `\mathcal{A}` obtained by intersecting with a + generic hyperplane + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L(x, y - 1, z).hyperplane_section() + Traceback (most recent call last): + ... + TypeError: the arrangement is not projective + + sage: # needs sage.graphs + sage: A0. = hyperplane_arrangements.braid(4); A0 + Arrangement of 6 hyperplanes of dimension 4 and rank 3 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A = L(A0) + sage: M = A.matroid() + sage: A1 = A.hyperplane_section() + sage: A1 + Arrangement of 6 hyperplanes of dimension 3 and rank 3 + sage: M1 = A1.matroid() + sage: A2 = A1.hyperplane_section(); A2 + Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: M2 = A2.matroid() + sage: T1 = M1.truncation() + sage: T1.is_isomorphic(M2) + True + sage: T1.isomorphism(M2) + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + + sage: # needs sage.combinat + sage: a0 = hyperplane_arrangements.semiorder(3); a0 + Arrangement of 6 hyperplanes of dimension 3 and rank 2 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: a = L(a0) + sage: ca = a.cone() + sage: m = ca.matroid() + sage: a1 = a.hyperplane_section(proj=False) + sage: a1 + Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: ca1 = a1.cone() + sage: m1 = ca1.matroid() + sage: m.isomorphism(m1) + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6} + sage: p0 = hyperplane_arrangements.Shi(4) + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: p = L(p0) + sage: a = p.hyperplane_section(proj=False); a + Arrangement of 12 hyperplanes of dimension 3 and rank 3 + sage: ca = a.cone() + sage: m = ca.matroid().truncation() + sage: a1 = a.hyperplane_section(proj=False); a1 + Arrangement of 12 hyperplanes of dimension 2 and rank 2 + sage: ca1 = a1.cone() + sage: m1 = ca1.matroid() + sage: m1.is_isomorphism(m, {j: j for j in range(13)}) + True + """ + if proj and not self.is_linear(): + raise TypeError('the arrangement is not projective') + n0 = self.dimension() + if not proj: + H = self.cone() + H1 = H.hyperplane_section() + mat = matrix(h.coefficients()[1:] for h in H1) + m = mat.nrows() + for j in range(mat.ncols()): + if mat[m - 1, j] != 0: + mat.swap_columns(0, j) + break + for j in range(1, mat.ncols()): + mat.add_multiple_of_column(j, 0, -mat[m - 1, j] / mat[m - 1, 0]) + vrs = H1.parent().variable_names()[1:] + A1 = OrderedHyperplaneArrangements(self.base_ring(), names=vrs) + mat_rows = mat.rows()[:-1] + H1b = A1(mat_rows) + return H1b + P = self.intersection_poset(element_label="subspace") + center = P.maximal_elements()[0].linear_part() + n1 = center.dimension() + U = [] + for p in P: + if p.dimension() == n1 + 1: + B = [u for u in p.linear_part().basis() if u not in center] + U.append(B[0]) + # U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] + U0 = sum(U) + # We look for a linear hyperplane with integer coefficients + # defining a transversal hyperplane + for v in ZZ**n0: + v1 = v + U0 + if 0 not in [w * v1 for w in U]: + break + h0 = self.parent()((0,) + tuple(v1)) + H1 = self.add_hyperplane(h0) + return H1.restriction(h0) + + def affine_fundamental_group(self): + r""" + Return the fundamental group of the complement of an affine + hyperplane arrangement in `\CC^n` whose equations have + coefficients in a subfield of `\QQbar`. + + OUTPUT: + + A finitely presented fundamental group. + + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: H.affine_fundamental_group() + Finitely presented group < x0, x1 | > + sage: L = [x, y, x + 1, y + 1, x - y] + sage: A(L).affine_fundamental_group() + Finitely presented group + < x0, x1, x2, x3, x4 | x4*x0*x4^-1*x0^-1, + x0*x2*x3*x2^-1*x0^-1*x3^-1, + x1*x2*x4*x2^-1*x1^-1*x4^-1, + x2*x3*x0*x2^-1*x0^-1*x3^-1, + x2*x4*x1*x2^-1*x1^-1*x4^-1, + x4*x1*x4^-1*x3^-1*x2^-1*x1^-1*x2*x3 > + sage: H = A(x, y, x + y) + sage: H.affine_fundamental_group() + Finitely presented group + < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > + sage: H.affine_fundamental_group() # repeat to use the attribute + Finitely presented group + < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > + sage: T. = QQ[] + sage: K. = NumberField(t^3 + t + 1) + sage: L. = OrderedHyperplaneArrangements(K) + sage: H = L(a*x + y -1, x + a*y + 1, x - 1, y - 1) + sage: H.affine_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the base field is not in QQbar + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L([t - j for j in range(4)]).affine_fundamental_group() + Finitely presented group < x0, x1, x2, x3 | > + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L(L.gens() + (x + y + z + 1,)).affine_fundamental_group().sorted_presentation() + Finitely presented group + < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, + x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, + x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > + sage: A = OrderedHyperplaneArrangements(QQ, names=()) + sage: H = A(); H + Empty hyperplane arrangement of dimension 0 + sage: H.affine_fundamental_group() + Finitely presented group < | > + """ + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + if self._affine_fundamental_group: + return self._affine_fundamental_group + n = self.dimension() + r = len(self) + if n == 0: + return FreeGroup(0) / [] + if n == 1: + G = FreeGroup(r) / [] + dic = {j: G.gen(j) for j in range(r)} + dic[r] = [prod(G.gens()) ** -1] + self._affine_fundamental_group = G + self._affine_meridians = dic + return G + if n == 2: + S = self.parent().ambient_space().symmetric_space() + coord = vector((1,) + S.gens()) + Af = AffinePlaneCurveArrangements(K, names=self.parent().variable_names()) + L = Af([vector(line.coefficients()) * coord for line in self]) + G = L.fundamental_group() + self._affine_fundamental_group = G + self._affine_meridians = L._meridians_simpl_vertical + return G + H1 = self.hyperplane_section(proj=False) + G = H1.affine_fundamental_group() + self._affine_fundamental_group = G + self._affine_meridians = H1._affine_meridians + return G + + def affine_meridians(self): + r""" + Return the meridians of each hyperplane (including the one at infinity). + + OUTPUT: + + A dictionary + + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: g = H.affine_fundamental_group() + sage: g + Finitely presented group < x0, x1 | > + sage: H.affine_meridians() + {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} + sage: H1 = H.add_hyperplane(y - x) + sage: H1.affine_meridians() + {0: [x0], 1: [x1], 2: [x2], 3: [x2^-1*x1^-1*x0^-1]} + """ + if self._affine_meridians is None: + self.affine_fundamental_group() + return dict(self._affine_meridians) + + def projective_fundamental_group(self): + r""" + Return the fundamental group of the complement of a projective + hyperplane arrangement. + + OUTPUT: + + The finitely presented group of the complement + in the projective space whose equations have + coefficients in a subfield of `\QQbar`. + + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_fundamental_group() + Finitely presented group < x0, x1 | > + + sage: # needs sirocco sage.graphs + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) + sage: G3 = H.projective_fundamental_group(); G3.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > + sage: G3.abelian_invariants() + (0, 0, 0, 0, 0) + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) + sage: G4 = H.projective_fundamental_group(); G4.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > + sage: G4.abelian_invariants() + (0, 0, 0, 0, 0) + + sage: # needs sirocco + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) + sage: H = L(H) + sage: g = H.projective_fundamental_group() + sage: g.is_abelian(), g.abelian_invariants() + (True, (0, 0, 0, 0)) + sage: L(t0, t1, t2, t3, t4, t0 - 1).projective_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the arrangement is not projective + sage: T. = QQ[] + sage: K. = NumberField(t^3 + t + 1) + sage: L. = OrderedHyperplaneArrangements(K) + sage: H = L(a*x + y - z, x + a*y + z, x - z, y - z) + sage: H.projective_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the base field is not in QQbar + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(); H + Empty hyperplane arrangement of dimension 1 + sage: H.projective_fundamental_group() + Finitely presented group < | > + """ + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + if not self.is_linear(): + raise TypeError('the arrangement is not projective') + if self._projective_fundamental_group: + return self._projective_fundamental_group + n = self.dimension() + r = len(self) + if n == 1: + return FreeGroup(0) / [] + if n == 2: + G = FreeGroup(r - 1) / [] + dic = {j: G.gen(j) for j in range(r - 1)} + dic[r - 1] = [prod(G.gens()) ** -1] + self._projective_fundamental_group = G + self._projective_meridians = dic + return G + if n == 3: + S = self.parent().ambient_space().symmetric_space() + coord = vector(S.gens()) + Proj = ProjectivePlaneCurveArrangements(K, + names=self.parent().variable_names()) + L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) + G = L.fundamental_group() + self._projective_fundamental_group = G + self._projective_meridians = L._meridians_simpl + return G + H1 = self.hyperplane_section() + G = H1.projective_fundamental_group() + self._projective_fundamental_group = G + self._projective_meridians = H1._projective_meridians + return G + + def projective_meridians(self): + r""" + Return the meridian of each hyperplane. + + OUTPUT: + + A dictionary + + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_meridians() + {0: x0, 1: x1, 2: [x1^-1*x0^-1]} + + sage: # needs sirocco sage.graphs + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) + sage: H.projective_meridians() + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], + 1: [x3], 2: [x4], 3: [x1], 4: [x2], 5: [x0]} + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) + sage: H.projective_meridians() + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], 1: [x3], + 2: [x4], 3: [x0], 4: [x2], 5: [x1]} + + sage: # needs sirocco + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) + sage: H = L(H) + sage: H.projective_meridians() + {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} + """ + if self._projective_meridians is None: + self.projective_fundamental_group() + return dict(self._projective_meridians) + + +class OrderedHyperplaneArrangements(HyperplaneArrangements): + """ + Ordered Hyperplane arrangements. + + For more information on hyperplane arrangements, see + :mod:`sage.geometry.hyperplane_arrangement.arrangement`. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: x + Hyperplane x + 0*y + 0 + sage: x + y + Hyperplane x + y + 0 + sage: H(x, y, x-1, y-1) + Arrangement + """ + Element = OrderedHyperplaneArrangementElement + + def _element_constructor_(self, *args, **kwds): + """ + Construct an element of ``self``. + + INPUT: + + - ``*args`` -- positional arguments, each defining a + hyperplane; alternatively, a single polytope or a single + hyperplane arrangement + + - ``signed`` -- boolean (default: ``True``); whether to + preserve signs of hyperplane equations + + - ``check`` -- boolean (default: ``True``); whether to + perform argument checking. + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L(x) + Arrangement + sage: L(x, y) + Arrangement + sage: L([x, y]) + Arrangement + sage: L([0, 1, 0], [0, 0, 1]) + Arrangement + sage: L([[0, 0, 1], [0, 1, 0]]) + Arrangement + + sage: L(polytopes.hypercube(2)) + Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1> + + sage: L(-x, x + y - 1, signed=False) + Arrangement + + TESTS:: + + sage: L() + Empty hyperplane arrangement of dimension 2 + sage: L(0) # zero is equivalent to no argument, Issue #8648 + Empty hyperplane arrangement of dimension 2 + sage: L(0*x) # degenerate hyperplane is NOT allowed + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + sage: L(0*x, y) # ditto + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + """ + if len(args) == 1: + arg = args[0] + if isinstance(arg, HyperplaneArrangementElement) and arg.parent() is self: + # optimization if argument is already a hyperplane arrangement + return arg + if arg == 0 and not isinstance(arg, Hyperplane): + # zero = neutral element under addition = the empty hyperplane arrangement + args = [] + # process keyword arguments + not_char2 = (self.base_ring().characteristic() != 2) + signed = kwds.pop('signed', not_char2) + check = kwds.pop('check', True) + backend = kwds.pop('backend', None) + if kwds: + raise ValueError('unknown keyword argument') + # process positional arguments + AA = self.ambient_space() + try: + hyperplanes = [AA(a) for a in args] + except (TypeError, ValueError, AttributeError): + if len(args) > 1: + raise + arg = args[0] + if hasattr(arg, 'Hrepresentation'): + hyperplanes = [AA(h) for h in arg.Hrepresentation()] + else: + hyperplanes = [AA(a) for a in arg] + hyperplanes = [h.primitive(signed) for h in hyperplanes] + if check: + if signed and not not_char2: + raise ValueError('cannot be signed in characteristic 2') + for h in hyperplanes: + if h.A() == 0: + raise ValueError('linear expression must be non-constant to define a hyperplane') + if not_char2 and -h in hyperplanes: + raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') + return self.element_class(self, tuple(hyperplanes), backend=backend) + + def _repr_(self): + """ + Return a string representation. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ); L + Ordered hyperplane arrangements in 2-dimensional linear space + over Rational Field with coordinates x, y + """ + return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space()) diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index b652e68c946..666548b9f39 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -482,6 +482,9 @@ def is_LatticePolytope(x): sage: from sage.geometry.lattice_polytope import is_LatticePolytope sage: is_LatticePolytope(1) + doctest:warning... + DeprecationWarning: is_LatticePolytope is deprecated, use isinstance instead + See https://github.com/sagemath/sage/issues/34307 for details. False sage: p = LatticePolytope([(1,0), (0,1), (-1,-1)]) sage: p # needs palp @@ -489,6 +492,8 @@ def is_LatticePolytope(x): sage: is_LatticePolytope(p) True """ + from sage.misc.superseded import deprecation + deprecation(34307, "is_LatticePolytope is deprecated, use isinstance instead") return isinstance(x, LatticePolytopeClass) @richcmp_method @@ -989,7 +994,7 @@ def _palp(self, command, reduce_dimension=False): sage: o = lattice_polytope.cross_polytope(3) sage: o._palp("poly.x -f") # needs palp 'M:7 6 N:27 8 Pic:17 Cor:0\n' - sage: print(o._palp("nef.x -f -N -p")) # random time information # needs palp + sage: print(o._palp("nef.x -f -N -p")) # random time information # needs palp M:27 8 N:7 6 codim=2 #part=5 H:[0] P:0 V:2 4 5 0sec 0cpu H:[0] P:2 V:3 4 5 0sec 0cpu @@ -1234,7 +1239,7 @@ def _read_nef_partitions(self, data): sage: o = lattice_polytope.cross_polytope(3) sage: s = o.nef_x("-p -N -Lv") # needs palp - sage: print(s) # random time values # needs palp + sage: print(s) # random time values # needs palp M:27 8 N:7 6 codim=2 #part=5 3 6 Vertices in N-lattice: 1 0 0 -1 0 0 @@ -3139,7 +3144,7 @@ def normal_form(self, algorithm="palp_native", permutation=False): M( 12, -1, -9, -6, 6), M( 12, -1, -6, -3, 3) in 5-d lattice M - sage: P.normal_form(algorithm="palp_modified") # not tested (22s; MemoryError on 32 bit), needs sage.groups + sage: P.normal_form(algorithm="palp_modified") # not tested (22s; MemoryError on 32 bit), needs sage.groups M( 6, 0, 0, 0, 0), M( -6, 0, 0, 0, 0), M( 0, 1, 0, 0, 0), @@ -5264,7 +5269,7 @@ def _read_nef_x_partitions(data): sage: o = lattice_polytope.cross_polytope(3) sage: s = o.nef_x("-N -p") # needs palp - sage: print(s) # random # needs palp + sage: print(s) # random # needs palp M:27 8 N:7 6 codim=2 #part=5 P:0 V:2 4 5 0sec 0cpu P:2 V:3 4 5 0sec 0cpu diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index 28101e70646..c8e185d01e6 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -14,6 +14,8 @@ # https://www.gnu.org/licenses/ ############################################################################# +import sage.geometry.abc + from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element import Element @@ -22,7 +24,6 @@ from sage.rings.infinity import Infinity from sage.geometry.polyhedron.constructor import Polyhedron -from sage.geometry.polyhedron.base import is_Polyhedron class NewtonPolygon_element(Element): @@ -716,7 +717,7 @@ def _element_constructor_(self, arg, sort_slopes=True, last_slope=Infinity): sage: NewtonPolygon(1) Finite Newton polygon with 1 vertex: (0, 0) """ - if is_Polyhedron(arg): + if isinstance(arg, sage.geometry.abc.Polyhedron): return self.element_class(arg, parent=self) if arg == 0: polyhedron = Polyhedron(base_ring=self.base_ring(), ambient_dim=2) diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 09200a60be9..981404eb174 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -110,9 +110,11 @@ # **************************************************************************** from copy import copy + +import sage.geometry.abc + from sage.topology.cell_complex import GenericCellComplex from sage.geometry.polyhedron.constructor import Polyhedron -from sage.geometry.polyhedron.base import is_Polyhedron from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.graphs.graph import Graph @@ -308,7 +310,7 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim() self._ambient_dim = ambient_dim self._maximal_cells = cells_dict - if not all((is_Polyhedron(cell) and + if not all((isinstance(cell, sage.geometry.abc.Polyhedron) and cell.ambient_dim() == self._ambient_dim) for cell in self.maximal_cell_iterator()): raise ValueError("the given cells are not polyhedra " + @@ -959,7 +961,7 @@ def __contains__(self, x): sage: (0, 0) in pc # not a polyhedron False """ - if not is_Polyhedron(x): + if not isinstance(x, sage.geometry.abc.Polyhedron): return False dim = x.dimension() return dim in self.cells() and x in self.cells()[dim] @@ -2028,7 +2030,7 @@ def add_cell(self, cell): """ if self._is_immutable: raise ValueError("this polyhedral complex is not mutable") - if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: + if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim: raise ValueError("the given cell is not a polyhedron " + "in the same ambient space") # if cell is already in self, do nothing. @@ -2191,7 +2193,7 @@ def remove_cell(self, cell, check=False): """ if self._is_immutable: raise ValueError("this polyhedral complex is not mutable") - if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: + if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim: raise ValueError("the given cell is not a polyhedron " + "in the same ambient space") # if cell is not in self, delete nothing. diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index e59f05d09a3..a333defe55c 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -22,9 +22,6 @@ from .base import Polyhedron_base from .base_QQ import Polyhedron_QQ -from sage.misc.lazy_import import lazy_import -lazy_import('sage.geometry.polyhedron.backend_cdd_rdf', 'Polyhedron_RDF_cdd', deprecation=32592) - class Polyhedron_cdd(Polyhedron_base): r""" diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index f9ce84be09d..82e92b72bd3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -74,10 +74,15 @@ def is_Polyhedron(X): sage: p = polytopes.hypercube(2) sage: from sage.geometry.polyhedron.base import is_Polyhedron sage: is_Polyhedron(p) + doctest:warning... + DeprecationWarning: is_Polyhedron is deprecated, use isinstance instead + See https://github.com/sagemath/sage/issues/34307 for details. True sage: is_Polyhedron(123456) False """ + from sage.misc.superseded import deprecation + deprecation(34307, "is_Polyhedron is deprecated, use isinstance instead") return isinstance(X, Polyhedron_base) diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py index d64cf38d26d..3b57c4f2055 100644 --- a/src/sage/geometry/polyhedron/base3.py +++ b/src/sage/geometry/polyhedron/base3.py @@ -372,7 +372,7 @@ def _test_combinatorial_polyhedron(self, tester=None, **options): prefix=tester._prefix+" ") tester.info(tester._prefix + " ", newline=False) - def face_generator(self, face_dimension=None, algorithm=None, **kwds): + def face_generator(self, face_dimension=None, algorithm=None): r""" Return an iterator over the faces of given dimension. @@ -590,22 +590,6 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds): sage: f.ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0,) - The ``dual`` keyword is deprecated:: - - sage: P = polytopes.hypercube(4) - sage: list(P.face_generator(dual=False))[:4] - doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead - See https://github.com/sagemath/sage/issues/33646 for details. - [A 4-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 16 vertices, - A -1-dimensional face of a Polyhedron in ZZ^4, - A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices, - A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices] - sage: list(P.face_generator(True))[:4] - [A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices] - Check that we catch incorrect algorithms:: sage: list(P.face_generator(2, algorithm='integrate'))[:4] @@ -618,19 +602,9 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds): dual = False elif algorithm == 'dual': dual = True - elif algorithm in (False, True): - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - dual = algorithm elif algorithm is not None: raise ValueError("algorithm must be 'primal', 'dual' or None") - if kwds: - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - if 'dual' in kwds and dual is None: - dual = kwds['dual'] - from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator import FaceIterator_geom return FaceIterator_geom(self, output_dimension=face_dimension, dual=dual) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index a04a1186876..dd5ee0bf472 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -34,7 +34,6 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple Vrep(self) cdef tuple facet_names(self) cdef tuple equations(self) - cdef tuple equalities(self) cdef unsigned int n_Vrepresentation(self) noexcept cdef unsigned int n_Hrepresentation(self) noexcept cdef bint is_bounded(self) noexcept diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 367049b9fc0..d21b824da0c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1359,7 +1359,7 @@ cdef class CombinatorialPolyhedron(SageObject): adjacency_matrix.set_immutable() return adjacency_matrix - def ridges(self, add_equations=False, names=True, add_equalities=False, algorithm=None): + def ridges(self, add_equations=False, names=True, algorithm=None): r""" Return the ridges. @@ -1453,21 +1453,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(polytopes.simplex()) sage: C.ridges(names=False, add_equations=True) ((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1)) - - The keyword ``add_equalities`` is deprecated:: - - sage: C = CombinatorialPolyhedron(polytopes.simplex()) - sage: r = C.ridges(add_equations=True) - sage: r1 = C.ridges(add_equalities=True) - doctest:...: DeprecationWarning: the keyword ``add_equalities`` is deprecated; use ``add_equations`` - See https://github.com/sagemath/sage/issues/31834 for details. - sage: r == r1 - True """ - if add_equalities: - from sage.misc.superseded import deprecation - deprecation(31834, "the keyword ``add_equalities`` is deprecated; use ``add_equations``", 3) - add_equations = True self._compute_ridges(self._algorithm_to_dual(algorithm)) cdef size_t n_ridges = self._ridges.length @@ -2673,7 +2659,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self.face_generator().meet_of_Hrep(*indices) - def face_generator(self, dimension=None, algorithm=None, **kwds): + def face_generator(self, dimension=None, algorithm=None): r""" Iterator over all proper faces of specified dimension. @@ -2760,16 +2746,6 @@ cdef class CombinatorialPolyhedron(SageObject): (A ray in the direction (1, 0), A vertex at (1, 0)) (A ray in the direction (0, 1), A vertex at (0, 1)) - TESTS: - - The kewword ``dual`` is deprecated:: - - sage: C = CombinatorialPolyhedron([[0,1,2],[0,1,3],[0,2,3],[1,2,3]]) - sage: it = C.face_generator(1, False) - doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead - See https://github.com/sagemath/sage/issues/33646 for details. - sage: it = C.face_generator(1, dual=True) - .. SEEALSO:: :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`, @@ -2777,18 +2753,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ cdef int dual - if algorithm in (False, True): - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - dual = int(algorithm) - else: - dual = self._algorithm_to_dual(algorithm) - - if kwds: - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - if 'dual' in kwds and dual == -1 and kwds['dual'] in (False, True): - dual = int(kwds['dual']) + dual = self._algorithm_to_dual(algorithm) if dual == -1: # Determine the faster way, to iterate through all faces. @@ -3273,11 +3238,6 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._equations - cdef tuple equalities(self): - from sage.misc.superseded import deprecation - deprecation(31834, "the method equalities of CombinatorialPolyhedron is deprecated; use equations", 3) - return self.equations() - cdef unsigned int n_Vrepresentation(self) noexcept: r""" Return the number of elements in the Vrepresentation. diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 7dafad76437..5fad17dc55a 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -9,6 +9,8 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +import sage.geometry.abc + from sage.structure.parent import Parent from sage.structure.element import get_coercion_model from sage.structure.unique_representation import UniqueRepresentation @@ -22,8 +24,6 @@ from sage.categories.fields import Fields from sage.categories.rings import Rings from sage.categories.modules import Modules - -from sage.geometry.polyhedron.base import is_Polyhedron from .representation import Inequality, Equation, Vertex, Ray, Line @@ -691,7 +691,7 @@ def convert_base_ring_Hrep(lstlst): if convert and Vrep: Vrep = [convert_base_ring(_) for _ in Vrep] return self.element_class(self, Vrep, Hrep, **kwds) - if nargs == 1 and is_Polyhedron(args[0]): + if nargs == 1 and isinstance(args[0], sage.geometry.abc.Polyhedron): copy = kwds.pop('copy', args[0].parent() is not self) mutable = kwds.pop('mutable', False) diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 0d039d8db05..02998662ba0 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -496,7 +496,7 @@ def HammingGraph(n, q, X=None): def BalancedTree(r, h): r""" - Returns the perfectly balanced tree of height `h \geq 1`, + Return the perfectly balanced tree of height `h \geq 1`, whose root has degree `r \geq 2`. The number of vertices of this graph is @@ -513,23 +513,18 @@ def BalancedTree(r, h): OUTPUT: The perfectly balanced tree of height `h \geq 1` and whose root has - degree `r \geq 2`. A :exc:`~networkx.exception.NetworkXError` is raised - if `r < 2` or `h < 1`. - - ALGORITHM: - - Uses the :ref:`NetworkX ` function - :func:`~networkx.generators.classic.balanced_tree`. + degree `r \geq 2`. EXAMPLES: A balanced tree whose root node has degree `r = 2`, and of height `h = 1`, has order 3 and size 2:: - sage: G = graphs.BalancedTree(2, 1); G # needs networkx + sage: G = graphs.BalancedTree(2, 1); G Balanced tree: Graph on 3 vertices - sage: G.order(); G.size() # needs networkx + sage: G.order() 3 + sage: G.size() 2 sage: r = 2; h = 1 sage: v = 1 + r @@ -539,8 +534,9 @@ def BalancedTree(r, h): Plot a balanced tree of height 5, whose root node has degree `r = 3`:: - sage: G = graphs.BalancedTree(3, 5) # needs networkx - sage: G.show() # long time # needs networkx sage.plot + sage: G = graphs.BalancedTree(3, 5) + sage: G.plot() # long time # needs sage.plot + Graphics object consisting of 728 graphics primitives A tree is bipartite. If its vertex set is finite, then it is planar. :: @@ -563,17 +559,40 @@ def BalancedTree(r, h): has degree `r \geq 2`, but the construction degenerates gracefully:: - sage: graphs.BalancedTree(1, 10) # needs networkx + sage: graphs.BalancedTree(1, 10) Balanced tree: Graph on 11 vertices Similarly, we usually want the tree must have height `h \geq 1` but the algorithm also degenerates gracefully here:: - sage: graphs.BalancedTree(3, 0) # needs networkx + sage: graphs.BalancedTree(3, 0) Balanced tree: Graph on 1 vertex + + The construction is the same as the one of networkx:: + + sage: # needs networkx + sage: import networkx + sage: r = randint(2, 4); h = randint(1, 5) + sage: T = graphs.BalancedTree(r, h) + sage: N = Graph(networkx.balanced_tree(r, h), name="Balanced tree") + sage: T.is_isomorphic(N) + True """ - import networkx - return Graph(networkx.balanced_tree(r, h), name="Balanced tree") + # Compute the number of vertices per level of the tree + order = [r**l for l in range(h + 1)] + # Compute the first index of the vertices of a level + begin = [0] + begin.extend(begin[-1] + val for val in order) + # The number of vertices of the tree is the first index of level h + 1 + T = Graph(begin[-1], name="Balanced tree") + + # Add edges of the r-ary tree + for level in range(h): + start = begin[level + 1] + for u in range(begin[level], begin[level + 1]): + T.add_edges((u, v) for v in range(start, start + r)) + start += r + return T def BarbellGraph(n1, n2): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 4d147efcd2f..eee4c61b69c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1900,6 +1900,72 @@ def to_dictionary(self, edge_labels=False, multiple_edges=False): return d + def _vertex_indices_and_keys(self, vertices=None, *, sort=None): + r""" + Process a ``vertices`` parameter. + + This is a helper function for :meth:`adjacency_matrix`, + :meth:`incidence_matrix`, :meth:`weighted_adjacency_matrix`, + and :meth:`kirchhoff_matrix`. + + INPUT: + + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``) + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + - ``sort`` -- boolean or ``None`` (default); passed to :meth:`vertices` + when ``vertices`` is not a list. + + OUTPUT: pair of: + + - ``vertex_indices`` -- a dictionary mapping vertices to numerical indices, + - ``keys`` -- either a tuple of basis keys (when using a + :class:`CombinatorialFreeModule`) or ``None`` (when using a + :class:`FreeModule`, :func:`matrix`). + + EXAMPLES:: + + sage: G = graphs.PathGraph(5) + sage: G.relabel(['o....', '.o...', '..o..', '...o.', '....o']) + sage: G._vertex_indices_and_keys(None) + ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4}, + None) + sage: G._vertex_indices_and_keys(None, sort=False) + ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0}, + None) + sage: G._vertex_indices_and_keys(['..o..', '.o...', '...o.', 'o....', '....o']) + ({'....o': 4, '...o.': 2, '..o..': 0, '.o...': 1, 'o....': 3}, + None) + sage: G._vertex_indices_and_keys(True) + ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0}, + ('o....', '.o...', '..o..', '...o.', '....o')) + sage: G._vertex_indices_and_keys(True, sort=True) + ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4}, + ('....o', '...o.', '..o..', '.o...', 'o....')) + """ + n = self.order() + keys = None + if vertices is True: + vertices = self.vertices(sort=sort if sort is not None else False) + keys = tuple(vertices) # tuple to make it hashable + elif vertices is None: + try: + vertices = self.vertices(sort=sort if sort is not None else True) + except TypeError: + raise TypeError("Vertex labels are not comparable. You must " + "specify an ordering using parameter 'vertices'") + elif (len(vertices) != n or + set(vertices) != set(self.vertex_iterator())): + raise ValueError("parameter 'vertices' must be a permutation of the vertices") + return {v: i for i, v in enumerate(vertices)}, keys + def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds): r""" Return the adjacency matrix of the (di)graph. @@ -1911,10 +1977,16 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds - ``sparse`` -- boolean (default: ``None``); whether to represent with a sparse matrix - - ``vertices`` -- list (default: ``None``); the ordering of - the vertices defining how they should appear in the - matrix. By default, the ordering given by - :meth:`GenericGraph.vertices` with ``sort=True`` is used. + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + If the vertices are not comparable, the keyword ``vertices`` must be used to specify an ordering, or a :class:`TypeError` exception will be raised. @@ -2025,27 +2097,45 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Creating a module endomorphism:: + + sage: # needs sage.modules + sage: D12 = posets.DivisorLattice(12).hasse_diagram() + sage: phi = D12.adjacency_matrix(vertices=True); phi + Generic endomorphism of + Free module generated by {1, 2, 3, 4, 6, 12} over Integer Ring + sage: print(phi._unicode_art_matrix()) + 1 2 3 4 6 12 + 1⎛ 0 1 1 0 0 0⎞ + 2⎜ 0 0 0 1 1 0⎟ + 3⎜ 0 0 0 0 1 0⎟ + 4⎜ 0 0 0 0 0 1⎟ + 6⎜ 0 0 0 0 0 1⎟ + 12⎝ 0 0 0 0 0 0⎠ + TESTS:: - sage: graphs.CubeGraph(8).adjacency_matrix().parent() # needs sage.modules + sage: # needs sage.modules + sage: graphs.CubeGraph(8).adjacency_matrix().parent() Full MatrixSpace of 256 by 256 dense matrices over Integer Ring - sage: graphs.CubeGraph(9).adjacency_matrix().parent() # needs sage.modules + sage: graphs.CubeGraph(9).adjacency_matrix().parent() Full MatrixSpace of 512 by 512 sparse matrices over Integer Ring - sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # needs sage.modules + sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], ....: multiedges=True).adjacency_matrix().parent() Full MatrixSpace of 501 by 501 dense matrices over Integer Ring - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # needs sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # needs sage.modules + ValueError: parameter 'vertices' must be a permutation of the vertices + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices + ValueError: parameter 'vertices' must be a permutation of the vertices + sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix() Traceback (most recent call last): ... - TypeError: Vertex labels are not comparable. You must specify an ordering using parameter ``vertices`` + TypeError: Vertex labels are not comparable. You must specify an ordering using parameter 'vertices' sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix(vertices=['John', 42, 0]) [0 1 0] [1 0 0] @@ -2056,25 +2146,17 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds sparse = True if self.has_multiple_edges() or n <= 256 or self.density() > 0.05: sparse = False + vertex_indices, keys = self._vertex_indices_and_keys(vertices) + if keys is not None: + kwds = copy(kwds) + kwds['row_keys'] = kwds['column_keys'] = keys - if vertices is None: - try: - vertices = self.vertices(sort=True) - except TypeError: - raise TypeError("Vertex labels are not comparable. You must " - "specify an ordering using parameter " - "``vertices``") - elif (len(vertices) != n or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") - - new_indices = {v: i for i, v in enumerate(vertices)} D = {} directed = self._directed multiple_edges = self.allows_multiple_edges() for u, v, l in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] if multiple_edges and (i, j) in D: D[i, j] += 1 if not directed and i != j: @@ -2126,15 +2208,23 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None - ``sparse`` -- boolean (default: ``True``); whether to use a sparse or a dense matrix - - ``vertices`` -- list (default: ``None``); when specified, the `i`-th - row of the matrix corresponds to the `i`-th vertex in the ordering of - ``vertices``, otherwise, the `i`-th row of the matrix corresponds to - the `i`-th vertex in the ordering given by method :meth:`vertices`. + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row of the matrix corresponds to the `i`-th + vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row of the matrix corresponds to + the `i`-th vertex in the ordering given by method :meth:`vertices`, + - when ``True``, construct a morphism of free modules instead of a matrix, + where the codomain's basis is indexed by the vertices. - - ``edges`` -- list (default: ``None``); when specified, the `i`-th - column of the matrix corresponds to the `i`-th edge in the ordering of - ``edges``, otherwise, the `i`-th column of the matrix corresponds to - the `i`-th edge in the ordering given by method :meth:`edge_iterator`. + - ``edges`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th column of the matrix corresponds to the `i`-th + edge in the ordering of ``edges``, + - when ``None``, the `i`-th column of the matrix corresponds to + the `i`-th edge in the ordering given by method :meth:`edge_iterator`, + - when ``True``, construct a morphism of free modules instead of a matrix, + where the domain's basis is indexed by the edges. - ``base_ring`` -- a ring (default: ``ZZ``); the base ring of the matrix space to use. @@ -2258,13 +2348,39 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Creating a module morphism:: + + sage: # needs sage.modules + sage: D12 = posets.DivisorLattice(12).hasse_diagram() + sage: phi_VE = D12.incidence_matrix(vertices=True, edges=True); phi_VE + Generic morphism: + From: Free module generated by + {(1, 2), (1, 3), (2, 4), (2, 6), (3, 6), (4, 12), (6, 12)} + over Integer Ring + To: Free module generated by {1, 2, 3, 4, 6, 12} over Integer Ring + sage: print(phi_VE._unicode_art_matrix()) + (1, 2) (1, 3) (2, 4) (2, 6) (3, 6) (4, 12) (6, 12) + 1⎛ -1 -1 0 0 0 0 0⎞ + 2⎜ 1 0 -1 -1 0 0 0⎟ + 3⎜ 0 1 0 0 -1 0 0⎟ + 4⎜ 0 0 1 0 0 -1 0⎟ + 6⎜ 0 0 0 1 1 0 -1⎟ + 12⎝ 0 0 0 0 0 1 1⎠ + sage: E = phi_VE.domain() + sage: P1 = E.monomial((2, 4)) + E.monomial((4, 12)); P1 + B[(2, 4)] + B[(4, 12)] + sage: P2 = E.monomial((2, 6)) + E.monomial((6, 12)); P2 + B[(2, 6)] + B[(6, 12)] + sage: phi_VE(P1 - P2) + 0 + TESTS:: sage: P5 = graphs.PathGraph(5) sage: P5.incidence_matrix(vertices=[1] * P5.order()) # needs sage.modules Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices + ValueError: parameter 'vertices' must be a permutation of the vertices sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # needs sage.modules Traceback (most recent call last): ... @@ -2279,27 +2395,27 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None if oriented is None: oriented = self.is_directed() - if vertices is None: - vertices = self.vertices(sort=False) - elif (len(vertices) != self.num_verts() or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") + vertex_indices, row_keys = self._vertex_indices_and_keys(vertices, sort=False) - verts = {v: i for i, v in enumerate(vertices)} - if edges is None: - edges = self.edge_iterator(labels=False) + column_keys = None + use_edge_labels = kwds.pop('use_edge_labels', False) + if edges is True: + edges = self.edges(labels=use_edge_labels) + column_keys = tuple(edges) # because an EdgesView is not hashable + elif edges is None: + edges = self.edge_iterator(labels=use_edge_labels) elif len(edges) != self.size(): raise ValueError("parameter edges must be a permutation of the edges") else: # We check that we have the same set of unlabeled edges if oriented: - i_edges = [(verts[e[0]], verts[e[1]]) for e in edges] - s_edges = [(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)] + i_edges = [(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges] + s_edges = [(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)] else: def reorder(u, v): return (u, v) if u <= v else (v, u) - i_edges = [reorder(verts[e[0]], verts[e[1]]) for e in edges] - s_edges = [reorder(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)] + i_edges = [reorder(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges] + s_edges = [reorder(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)] if sorted(i_edges) != sorted(s_edges): raise ValueError("parameter edges must be a permutation of the edges") @@ -2312,15 +2428,20 @@ def reorder(u, v): if oriented: for i, e in enumerate(edges): if e[0] != e[1]: - m[verts[e[0]], i] = -1 - m[verts[e[1]], i] = +1 + m[vertex_indices[e[0]], i] = -1 + m[vertex_indices[e[1]], i] = +1 else: for i, e in enumerate(edges): - m[verts[e[0]], i] += 1 - m[verts[e[1]], i] += 1 + m[vertex_indices[e[0]], i] += 1 + m[vertex_indices[e[1]], i] += 1 + + if row_keys is not None or column_keys is not None: + m.set_immutable() + return matrix(m, row_keys=row_keys, column_keys=column_keys) if immutable: m.set_immutable() + return m def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): @@ -2476,10 +2597,19 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, - ``sparse`` -- boolean (default: ``True``); whether to use a sparse or a dense matrix - - ``vertices`` -- list (default: ``None``); when specified, each vertex - is represented by its position in the list ``vertices``, otherwise - each vertex is represented by its position in the list returned by - method :meth:`vertices` + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + If the vertices are not comparable, the keyword ``vertices`` must be + used to specify an ordering, or a :class:`TypeError` exception will + be raised. - ``default_weight`` -- (default: ``None``); specifies the weight to replace any ``None`` edge label. When not specified an error is raised @@ -2531,6 +2661,21 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Creating a module morphism:: + + sage: # needs sage.modules + sage: G = Graph(sparse=True, weighted=True) + sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)]) + sage: phi = G.weighted_adjacency_matrix(vertices=True); phi + Generic endomorphism of + Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring + sage: print(phi._unicode_art_matrix()) + A B C D + A⎛0 1 3 4⎞ + B⎜1 0 2 0⎟ + C⎜3 2 0 0⎟ + D⎝4 0 0 0⎠ + TESTS: The following doctest verifies that :issue:`4888` is fixed:: @@ -2561,11 +2706,10 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, if self.has_multiple_edges(): raise NotImplementedError("don't know how to represent weights for a multigraph") - if vertices is None: - vertices = self.vertices(sort=True) - elif (len(vertices) != self.num_verts() or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") + vertex_indices, row_column_keys = self._vertex_indices_and_keys(vertices) + if row_column_keys is not None: + kwds = copy(kwds) + kwds['row_keys'] = kwds['column_keys'] = row_column_keys # Method for checking edge weights and setting default weight if default_weight is None: @@ -2580,18 +2724,16 @@ def func(u, v, label): return default_weight return label - new_indices = {v: i for i,v in enumerate(vertices)} - D = {} if self._directed: for u, v, label in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] D[i, j] = func(u, v, label) else: for u, v, label in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] label = func(u, v, label) D[i, j] = label D[j, i] = label @@ -2659,6 +2801,20 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl - Else, `D-M` is used in calculation of Kirchhoff matrix + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + If the vertices are not comparable, the keyword ``vertices`` must be + used to specify an ordering, or a :class:`TypeError` exception will + be raised. + Note that any additional keywords will be passed on to either the :meth:`~GenericGraph.adjacency_matrix` or :meth:`~GenericGraph.weighted_adjacency_matrix` method. @@ -2740,18 +2896,36 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # needs sage.modules sage: M.is_immutable() # needs sage.modules True + + Creating a module morphism:: + + sage: # needs sage.modules + sage: G = Graph(sparse=True, weighted=True) + sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)]) + sage: phi = G.laplacian_matrix(weighted=True, vertices=True); phi + Generic endomorphism of + Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring + sage: print(phi._unicode_art_matrix()) + A B C D + A⎛ 8 -1 -3 -4⎞ + B⎜-1 3 -2 0⎟ + C⎜-3 -2 5 0⎟ + D⎝-4 0 0 4⎠ + """ - from sage.matrix.constructor import diagonal_matrix + from sage.matrix.constructor import diagonal_matrix, matrix set_immutable = kwds.pop('immutable', False) + vertex_indices, keys = self._vertex_indices_and_keys(kwds.pop('vertices', None)) + if weighted is None: weighted = self._weighted if weighted: - M = self.weighted_adjacency_matrix(immutable=True, **kwds) + M = self.weighted_adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds) else: - M = self.adjacency_matrix(immutable=True, **kwds) + M = self.adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds) D = M.parent(0) @@ -2791,6 +2965,8 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl else: ret = D - M + if keys is not None: + return matrix(ret, row_keys=keys, column_keys=keys) if set_immutable: ret.set_immutable() return ret @@ -15671,7 +15847,7 @@ def cluster_triangles(self, nbunch=None, implementation=None): sage: F = graphs.FruchtGraph() sage: list(F.cluster_triangles().values()) - [1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0] + [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0] sage: F.cluster_triangles() {0: 1, 1: 1, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0, 9: 1, 10: 1, 11: 0} sage: F.cluster_triangles(nbunch=[0, 1, 2]) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index d4f2e18a555..d8e88b023f0 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -6917,27 +6917,29 @@ def cliques_number_of(self, vertices=None, cliques=None): EXAMPLES:: sage: C = Graph('DJ{') - sage: C.cliques_number_of() # needs networkx + sage: C.cliques_number_of() {0: 1, 1: 1, 2: 1, 3: 1, 4: 2} sage: E = C.cliques_maximal() sage: E [[0, 4], [1, 2, 3, 4]] - sage: C.cliques_number_of(cliques=E) # needs networkx + sage: C.cliques_number_of(cliques=E) {0: 1, 1: 1, 2: 1, 3: 1, 4: 2} sage: F = graphs.Grid2dGraph(2,3) - sage: F.cliques_number_of() # needs networkx + sage: F.cliques_number_of() {(0, 0): 2, (0, 1): 3, (0, 2): 2, (1, 0): 2, (1, 1): 3, (1, 2): 2} - sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)]) # needs networkx + sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)]) {(0, 1): 3, (1, 2): 2} sage: F.cliques_number_of(vertices=(0, 1)) 3 sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_number_of() # needs networkx + sage: G.cliques_number_of() {0: 2, 1: 2, 2: 1, 3: 1} """ if cliques is None: - cliques = self.cliques_maximal() + # We use IndependentSets to avoid the construction of the list of + # cliques as currently done by method cliques_maximal. + cliques = IndependentSets(self, maximal=True, complement=True) if vertices in self: # single vertex return sum(1 for c in cliques if vertices in c) @@ -6952,7 +6954,7 @@ def cliques_number_of(self, vertices=None, cliques=None): @doc_index("Clique-related methods") def cliques_get_max_clique_graph(self): - """ + r""" Return the clique graph. Vertices of the result are the maximal cliques of the graph, and edges @@ -6968,26 +6970,52 @@ def cliques_get_max_clique_graph(self): EXAMPLES:: - sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG # needs networkx + sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG Graph on 24 vertices - sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot + sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_get_max_clique_graph() # needs networkx + sage: G.cliques_get_max_clique_graph() Graph on 2 vertices - sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs networkx sage.plot + sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs sage.plot + + TESTS:: + + sage: # needs networkx + sage: import networkx + sage: CG = graphs.ChvatalGraph() + sage: S = CG.cliques_get_max_clique_graph() + sage: N = Graph(networkx.make_max_clique_graph(CG.networkx_graph(), + ....: create_using=networkx.MultiGraph()), + ....: multiedges=False) + sage: S.is_isomorphic(N) + True """ - import networkx - return Graph(networkx.make_max_clique_graph(self.networkx_graph(), create_using=networkx.MultiGraph()), - multiedges=False) + # Associate each maximal clique an integer index and record for each + # vertex of self the cliques it belongs to. + # We use IndependentSets to avoid the construction of the list of + # cliques as currently done by method cliques_maximal. + cliques_of_vertex = {u: [] for u in self} + for n, clique in enumerate(IndependentSets(self, maximal=True, complement=True)): + for u in clique: + cliques_of_vertex[u].append(n) + + # Build a graph with one vertex per maximal clique and an edge between + # cliques sharing a vertex of self + G = Graph(n, multiedges=False) + for block in cliques_of_vertex.values(): + G.add_clique(block) + return G @doc_index("Clique-related methods") def cliques_get_clique_bipartite(self, **kwds): - """ - Return a bipartite graph constructed such that maximal cliques are the - right vertices and the left vertices are retained from the given - graph. Right and left vertices are connected if the bottom vertex - belongs to the clique represented by a top vertex. + r""" + Return the vertex-clique bipartite graph of ``self``. + + In the returned bipartite graph, the ``left`` vertices are the vertices + of ``self`` and the ``right`` vertices represent the maximal cliques of + ``self``. There is an edge from vertex `v` to clique `C` in the + bipartite graph if and only if `v` belongs to `C`. .. NOTE:: @@ -6996,18 +7024,32 @@ def cliques_get_clique_bipartite(self, **kwds): EXAMPLES:: - sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG # needs networkx + sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG Bipartite graph on 36 vertices - sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot + sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_get_clique_bipartite() # needs networkx + sage: G.cliques_get_clique_bipartite() Bipartite graph on 6 vertices - sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs networkx sage.plot + sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs sage.plot + + TESTS:: + + sage: # needs networkx + sage: import networkx + sage: CG = graphs.ChvatalGraph() + sage: S = CG.cliques_get_clique_bipartite() + sage: N = BipartiteGraph(networkx.make_clique_bipartite(CG.networkx_graph())) + sage: S.is_isomorphic(N) + True """ - from .bipartite_graph import BipartiteGraph - import networkx - return BipartiteGraph(networkx.make_clique_bipartite(self.networkx_graph(), **kwds)) + G = Graph([self, []], format='vertices_and_edges') + for i, clique in enumerate(IndependentSets(self, maximal=True, complement=True)): + idx = - i - 1 + G.add_vertex(idx) + G.add_edges((u, idx) for u in clique) + from sage.graphs.bipartite_graph import BipartiteGraph + return BipartiteGraph(G, check=False) @doc_index("Algorithmically hard stuff") def independent_set(self, algorithm="Cliquer", value_only=False, reduction_rules=True, diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 6b1e825ba2e..03e9231bd65 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -371,17 +371,17 @@ def print_md_tree(root): sage: from sage.graphs.graph_decompositions.modular_decomposition import * sage: print_md_tree(modular_decomposition(graphs.IcosahedralGraph())) PRIME + 3 + 4 + 7 + 9 + 11 1 5 - 7 8 - 11 0 2 6 - 3 - 9 - 4 10 """ @@ -494,17 +494,17 @@ def habib_maurer_algorithm(graph, g_classes=None): sage: from sage.graphs.graph_decompositions.modular_decomposition import * sage: print_md_tree(habib_maurer_algorithm(graphs.IcosahedralGraph())) PRIME + 3 + 4 + 7 + 9 + 11 1 5 - 7 8 - 11 0 2 6 - 3 - 9 - 4 10 The Octahedral graph is not Prime:: diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 52941853359..ce1f8916423 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -462,6 +462,14 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv sage: g.is_isomorphic(graphs.PetersenGraph()) True + The resulting order of vertices is unspecified but deterministic:: + + sage: from sage.graphs.graph_input import from_dict_of_dicts + sage: g = Graph() + sage: from_dict_of_dicts(g, {i: {} for i in range(99, 90, -1)}) + sage: g.vertices(sort=False) + [99, 98, 97, 96, 95, 94, 93, 92, 91] + TESTS: :issue:`32831` is fixed:: @@ -493,8 +501,11 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) - verts = set().union(M.keys(), *M.values()) - G.add_vertices(verts) + # Use keys of a dictionary instead of a set, to preserve insertion order + verts = dict(M) + for d in M.values(): + verts.update(d) + G.add_vertices(verts.keys()) if convert_empty_dict_labels_to_None: def relabel(x): return x if x != {} else None @@ -504,7 +515,7 @@ def relabel(x): is_directed = G.is_directed() if not is_directed and multiedges: - v_to_id = {v: i for i, v in enumerate(verts)} + v_to_id = {v: i for i, v in enumerate(verts.keys())} for u in M: for v in M[u]: if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v: @@ -543,8 +554,18 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): sage: from_dict_of_lists(g, graphs.PetersenGraph().to_dictionary()) sage: g.is_isomorphic(graphs.PetersenGraph()) True + + The resulting order of vertices is unspecified but deterministic:: + + sage: from sage.graphs.graph_input import from_dict_of_lists + sage: g = Graph() + sage: from_dict_of_lists(g, {i: [] for i in range(99, 90, -1)}) + sage: g.vertices(sort=False) + [99, 98, 97, 96, 95, 94, 93, 92, 91] """ - verts = set().union(D.keys(), *D.values()) + # Use keys of a dictionary instead of a set, to preserve insertion order + verts = dict(D) + verts.update({v: None for l in D.values() for v in l}) if not loops: if any(u in neighb for u, neighb in D.items()): if loops is False: @@ -567,11 +588,11 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): multiedges = False G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) - G.add_vertices(verts) + G.add_vertices(verts.keys()) is_directed = G.is_directed() if not is_directed and multiedges: - v_to_id = {v: i for i, v in enumerate(verts)} + v_to_id = {v: i for i, v in enumerate(verts.keys())} for u in D: for v in D[u]: if (v_to_id[u] <= v_to_id[v] or diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 8a0c5167b16..07ca7c7444e 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -458,10 +458,16 @@ def is_AbelianGroup(x): sage: F = AbelianGroup(5,[5,5,7,8,9], names=list("abcde")); F Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 sage: is_AbelianGroup(F) + doctest:warning... + DeprecationWarning: the function is_AbelianGroup is deprecated; + use 'isinstance(..., AbelianGroup_class)' instead + See https://github.com/sagemath/sage/issues/37898 for details. True sage: is_AbelianGroup(AbelianGroup(7, [3]*7)) True """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_AbelianGroup is deprecated; use 'isinstance(..., AbelianGroup_class)' instead") return isinstance(x, AbelianGroup_class) @@ -569,7 +575,7 @@ def is_isomorphic(left, right): sage: G1.is_isomorphic(G2) True """ - if not is_AbelianGroup(right): + if not isinstance(right, AbelianGroup_class): return False return left.elementary_divisors() == right.elementary_divisors() diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 86966cf74d6..24c910d96d9 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -86,6 +86,10 @@ def is_DualAbelianGroup(x): sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde")) sage: Fd = F.dual_group() sage: is_DualAbelianGroup(Fd) + doctest:warning... + DeprecationWarning: the function is_DualAbelianGroup is deprecated; + use 'isinstance(..., DualAbelianGroup_class)' instead + See https://github.com/sagemath/sage/issues/37898 for details. True sage: F = AbelianGroup(3,[1,2,3], names='a') sage: Fd = F.dual_group() @@ -94,6 +98,8 @@ def is_DualAbelianGroup(x): sage: F.gens() (1, a1, a2) """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_DualAbelianGroup is deprecated; use 'isinstance(..., DualAbelianGroup_class)' instead") return isinstance(x, DualAbelianGroup_class) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index 0cf0e8bfb31..ddba766e94d 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -602,14 +602,11 @@ cdef class ElementLibGAP(MultiplicativeGroupElement): sage: from sage.groups.libgap_group import GroupLibGAP sage: G = GroupLibGAP(libgap.FreeGroup('a', 'b')) sage: g = G.gen(0) * G.gen(1) - sage: g._latex_() - "ab%\n" - """ - try: - return self.gap().LaTeX() - except ValueError: - from sage.misc.latex import latex - return latex(self._repr_()) + sage: latex(g) + \text{\texttt{a*b}} + """ + from sage.misc.latex import latex + return latex(self._repr_()) cpdef _mul_(left, right): """ diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 709a88a6d8e..ef05b55c190 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -78,6 +78,10 @@ def is_MatrixGroup(x): sage: from sage.groups.matrix_gps.matrix_group import is_MatrixGroup sage: is_MatrixGroup(MatrixSpace(QQ, 3)) + doctest:warning... + DeprecationWarning: the function is_MatrixGroup is deprecated; + use 'isinstance(..., MatrixGroup_base)' instead + See https://github.com/sagemath/sage/issues/37898 for details. False sage: is_MatrixGroup(Mat(QQ, 3)) False @@ -86,6 +90,8 @@ def is_MatrixGroup(x): sage: is_MatrixGroup(MatrixGroup([matrix(2, [1,1,0,1])])) True """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_MatrixGroup is deprecated; use 'isinstance(..., MatrixGroup_base)' instead") return isinstance(x, MatrixGroup_base) ################################################################### @@ -499,7 +505,7 @@ def __richcmp__(self, other, op): sage: G != H False """ - if not is_MatrixGroup(other): + if not isinstance(other, MatrixGroup_base): return NotImplemented if self is other: diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd index 408fe26a15a..a71fa3cadd8 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd @@ -114,7 +114,7 @@ cdef inline int OP_find(OrbitPartition *OP, int n) noexcept: OP.parent[n] = OP_find(OP, OP.parent[n]) return OP.parent[n] -cdef inline int OP_join(OrbitPartition *OP, int m, int n) noexcept: +cdef inline void OP_join(OrbitPartition *OP, int m, int n) noexcept: """ Join the cells containing m and n, if they are different. """ diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index e64bbf5b40e..7f7cfb5e27b 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -1141,58 +1141,6 @@ def __ne__(self, other): """ return not self == other - def _homology_chomp(self, deg, base_ring, verbose, generators): - """ - Helper function for :meth:`homology`. - - This function is deprecated. - - INPUT: - - - ``deg`` -- integer (one specific homology group) or ``None`` - (all of those that can be non-zero) - - - ``base_ring`` -- the base ring (must be the integers - or a prime field) - - - ``verbose`` -- boolean, whether to print some messages - - - ``generators`` -- boolean, whether to also return generators - for homology - - EXAMPLES:: - - sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(2)) - sage: C._homology_chomp(None, GF(2), False, False) # optional - chomp, needs sage.rings.finite_rings - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - {0: Vector space of dimension 2 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2} - - sage: D = ChainComplex({0: matrix(ZZ,1,0,[]), 1: matrix(ZZ,1,1,[0]), - ....: 2: matrix(ZZ,0,1,[])}) - sage: D._homology_chomp(None, GF(2), False, False) # optional - chomp, needs sage.rings.finite_rings - {1: Vector space of dimension 1 over Finite Field of size 2, - 2: Vector space of dimension 1 over Finite Field of size 2} - """ - deprecation(33777, "the CHomP interface is deprecated; hence so is this function") - from sage.interfaces.chomp import homchain - H = homchain(self, base_ring=base_ring, verbose=verbose, - generators=generators) - if H is None: - raise RuntimeError('ran CHomP, but no output') - if deg is None: - # all the homology groups that could be non-zero - # one has to complete the answer of chomp - result = H - for idx in self.nonzero_degrees(): - if idx not in H: - result[idx] = HomologyGroup(0, base_ring) - return result - if deg in H: - return H[deg] - else: - return HomologyGroup(0, base_ring) - def homology(self, deg=None, base_ring=None, generators=False, verbose=False, algorithm='pari'): r""" @@ -1223,7 +1171,6 @@ def homology(self, deg=None, base_ring=None, generators=False, * ``'auto'`` * ``'dhsw'`` * ``'pari'`` - * ``'chomp'`` (this option is deprecated) See below for descriptions. @@ -1254,12 +1201,6 @@ def homology(self, deg=None, base_ring=None, generators=False, forces the named algorithm to be used regardless of the size of the matrices. - Finally, if ``algorithm`` is set to ``'chomp'``, then use - CHomP. CHomP is available at the web page - http://chomp.rutgers.edu/, although the software has not been - tested recently in Sage. The use of this option is deprecated; - see :issue:`33777`. - As of this writing, ``'pari'`` is the fastest standard option. .. WARNING:: @@ -1319,10 +1260,8 @@ def homology(self, deg=None, base_ring=None, generators=False, if not (base_ring.is_field() or base_ring is ZZ): raise NotImplementedError('can only compute homology if the base ring is the integers or a field') - if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp', 'chomp']: + if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']: raise NotImplementedError('algorithm not recognized') - if algorithm == 'chomp': - return self._homology_chomp(deg, base_ring, verbose, generators) if deg is None: deg = self.nonzero_degrees() @@ -1686,76 +1625,6 @@ def shift(self, n=1): return ChainComplex({k-shift: sgn * self._diff[k] for k in self._diff}, degree_of_differential=deg) - def _chomp_repr_(self): - r""" - String representation of ``self`` suitable for use by the CHomP - program. - - This function is deprecated. - - Since CHomP can only handle chain complexes, not cochain - complexes, and since it likes its complexes to start in degree - 0, flip the complex over if necessary, and shift it to start - in degree 0. Note also that CHomP only works over the - integers or a finite prime field. - - EXAMPLES:: - - sage: C = ChainComplex({-2: matrix(ZZ, 1, 3, [3, 0, 0])}, degree=-1) - sage: C._chomp_repr_() - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - 'chain complex\n\nmax dimension = 1\n\ndimension 0\n boundary a1 = 0\n\ndimension 1\n boundary a1 = + 3 * a1 \n boundary a2 = 0\n boundary a3 = 0\n\n' - sage: C = ChainComplex({-2: matrix(ZZ, 1, 3, [3, 0, 0])}, degree=1) - sage: C._chomp_repr_() - 'chain complex\n\nmax dimension = 1\n\ndimension 0\n boundary a1 = 0\n\ndimension 1\n boundary a1 = + 3 * a1 \n boundary a2 = 0\n boundary a3 = 0\n\n' - """ - deprecation(33777, "the CHomP interface is deprecated; hence so is this function") - deg = self.degree_of_differential() - if (self.grading_group() != ZZ or - (deg != 1 and deg != -1)): - raise ValueError('CHomP only works on Z-graded chain complexes with ' - 'differential of degree 1 or -1') - base_ring = self.base_ring() - if (base_ring == QQ) or (base_ring != ZZ and not (base_ring.is_prime_field())): - raise ValueError('CHomP doesn\'t compute over the rationals, only over Z or F_p') - if deg == -1: - diffs = self.differential() - else: - diffs = self._flip_().differential() - - if len(diffs) == 0: - diffs = {0: matrix(ZZ, 0, 0)} - - maxdim = max(diffs) - mindim = min(diffs) - # will shift chain complex by subtracting mindim from - # dimensions, so its bottom dimension is zero. - s = "chain complex\n\nmax dimension = %s\n\n" % (maxdim - mindim - 1,) - - for i in range(0, maxdim - mindim): - s += "dimension %s\n" % i - mat = diffs.get(i + mindim, matrix(base_ring, 0, 0)) - for idx in range(mat.ncols()): - s += " boundary a%s = " % (idx + 1) - # construct list of bdries - col = mat.column(idx) - nonzero_pos = col.nonzero_positions() - if nonzero_pos: - for j in nonzero_pos: - entry = col[j] - if entry > 0: - sgn = "+" - else: - sgn = "-" - entry = -entry - s += "%s %s * a%s " % (sgn, entry, j+1) - else: - s += "0" - s += "\n" - s += "\n" - return s - def _repr_(self): """ Print representation. diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index 1fc391376a8..206617843a9 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -1,33 +1,10 @@ # sage.doctest: needs sage.modules """ Tests for chain complexes, simplicial complexes, etc. - -These test whether CHomP gives the same answers as Sage's built-in -homology calculator. - -Since the CHomP interface has been deprecated --- see :issue:`33777` ---- so are many of the functions in is this module. - -TESTS:: - - sage: from sage.homology.tests import test_random_chain_complex - sage: test_random_chain_complex(trials=20) # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - sage: test_random_chain_complex(level=2, trials=20) # optional - CHomP - sage: test_random_chain_complex(level=3, trials=20) # long time # optional - CHomP - - sage: from sage.homology.tests import test_random_simplicial_complex - sage: test_random_simplicial_complex(level=1, trials=20) # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - sage: test_random_simplicial_complex(level=2, trials=20) # optional - CHomP - sage: test_random_simplicial_complex(level=5/2, trials=10) # long time # optional - CHomP """ from sage.misc.random_testing import random_testing from sage.misc.prandom import randint -from sage.misc.superseded import deprecation from sage.matrix.constructor import random_matrix from sage.homology.chain_complex import ChainComplex from sage.rings.integer_ring import ZZ @@ -66,43 +43,6 @@ def random_chain_complex(level=1): return ChainComplex({dim: mat}, degree=deg) -@random_testing -def test_random_chain_complex(level=1, trials=1, verbose=False): - """ - Compute the homology of a random chain complex with and without - CHomP, and compare the results. If they are not the same, raise - an error. - - This function is deprecated: see :issue:`33777`. - - :param level: measure of complexity of the chain complex -- see - :func:`random_chain_complex` - :type level: positive integer; optional, default 1 - :param trials: number of trials to conduct - :type trials: positive integer; optional, default 1 - :param verbose: if ``True``, print verbose messages - :type verbose: boolean; optional, default ``False`` - - EXAMPLES:: - - sage: from sage.homology.tests import test_random_chain_complex - sage: test_random_chain_complex(trials=2) # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - """ - deprecation(33777, 'the CHomP interface is deprecated; hence so is this function') - for i in range(trials): - C = random_chain_complex(level=level) - for d in C.differential(): - chomp = C.homology(d, verbose=verbose) - no_chomp = C.homology(d, algorithm='no_chomp', verbose=verbose) - if chomp != no_chomp: - print("Homology in dimension %s according to CHomP: %s" % (d, chomp)) - print("Homology in dimension %s according to Sage: %s" % (d, no_chomp)) - print("Chain complex: %s" % C.differential()) - raise ValueError - - def random_simplicial_complex(level=1, p=0.5): """ Return a random simplicial complex. @@ -118,7 +58,7 @@ def random_simplicial_complex(level=1, p=0.5): sage: from sage.homology.tests import random_simplicial_complex sage: X = random_simplicial_complex() - sage: X # random + sage: X # random Simplicial complex with vertex set (0, 1, 2, 3, 4, 5, 6, 7) and 31 facets sage: X.dimension() < 11 True @@ -126,40 +66,3 @@ def random_simplicial_complex(level=1, p=0.5): n = randint(2, 4 * level) dim = randint(1, n) return RandomComplex(n, dim, p) - - -@random_testing -def test_random_simplicial_complex(level=1, trials=1, verbose=False): - """ - Compute the homology of a random simplicial complex with and - without CHomP, and compare the results. If they are not the same, - raise an error. - - :param level: measure of complexity of the simplicial complex -- - see :func:`random_simplicial_complex` - :type level: positive integer; optional, default 1 - :param trials: number of trials to conduct - :type trials: positive integer; optional, default 1 - :param verbose: if ``True``, print verbose messages - :type verbose: boolean; optional, default ``False`` - - This gets pretty slow if ``level`` is more than 3. - - EXAMPLES:: - - sage: from sage.homology.tests import test_random_simplicial_complex - sage: test_random_simplicial_complex(trials=2) # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - """ - deprecation(33777, 'the CHomP interface is deprecated; hence so is this function') - for i in range(trials): - X = random_simplicial_complex(level=level) - chomp = X.homology(verbose=verbose) - no_chomp = X.homology(algorithm='no_chomp', verbose=verbose) - if chomp != no_chomp: - print("Homology according to CHomP: %s" % chomp) - print("Homology according to Sage: %s" % no_chomp) - print("Simplicial complex: %s" % X) - print("Its chain complex: %s" % X.chain_complex()) - raise ValueError diff --git a/src/sage/interfaces/chomp.py b/src/sage/interfaces/chomp.py deleted file mode 100644 index ad3bf9c734e..00000000000 --- a/src/sage/interfaces/chomp.py +++ /dev/null @@ -1,923 +0,0 @@ -r""" -Interface to CHomP - -This module is deprecated: see :issue:`33777`. - -CHomP stands for "Computation Homology Program", and is good at -computing homology of simplicial complexes, cubical complexes, and -chain complexes. It can also compute homomorphisms induced on -homology by maps. See the CHomP web page http://chomp.rutgers.edu/ -for more information. - -AUTHOR: - -- John H. Palmieri -""" - -import re - -from sage.misc.superseded import deprecation - -_have_chomp = {} - - -def have_chomp(program='homsimpl'): - """ - Return True if this computer has ``program`` installed. - - The first time it is run, this function caches its result in the - variable ``_have_chomp`` -- a dictionary indexed by program name - -- and any subsequent time, it just checks the value of the - variable. - - This program is used in the routine CHomP.__call__. - - If this computer doesn't have CHomP installed, you may obtain it - from http://chomp.rutgers.edu/. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import have_chomp - sage: have_chomp() # random -- depends on whether CHomP is installed - doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function - See https://github.com/sagemath/sage/issues/33777 for details. - True - sage: 'homsimpl' in sage.interfaces.chomp._have_chomp - True - sage: sage.interfaces.chomp._have_chomp['homsimpl'] == have_chomp() - True - """ - deprecation(33777, "the CHomP interface is deprecated; hence so is this function") - global _have_chomp - if program not in _have_chomp: - from sage.misc.sage_ostools import have_program - _have_chomp[program] = have_program(program) - return _have_chomp[program] - - -class CHomP: - r""" - Interface to the CHomP package. - - :param program: which CHomP program to use - :type program: string - :param complex: a simplicial or cubical complex - :param subcomplex: a subcomplex of ``complex`` or None (the default) - :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`. - :type base_ring: ring; optional, default `\ZZ` - :param generators: if True, also return list of generators - :type generators: boolean; optional, default False - :param verbose: if True, print helpful messages as the computation - progresses - :type verbose: boolean; optional, default False - :param extra_opts: options passed directly to ``program`` - :type extra_opts: string - :return: homology groups as a dictionary indexed by dimension - - The programs ``homsimpl``, ``homcubes``, and ``homchain`` are - available through this interface. ``homsimpl`` computes the - relative or absolute homology groups of simplicial complexes. - ``homcubes`` computes the relative or absolute homology groups of - cubical complexes. ``homchain`` computes the homology groups of - chain complexes. For consistency with Sage's other homology - computations, the answers produced by ``homsimpl`` and - ``homcubes`` in the absolute case are converted to reduced - homology. - - Note also that CHomP can only compute over the integers or - `\GF{p}`. CHomP is fast enough, though, that if you want - rational information, you should consider using CHomP with integer - coefficients, or with mod `p` coefficients for a sufficiently - large `p`, rather than using Sage's built-in homology algorithms. - - See also the documentation for the functions :func:`homchain`, - :func:`homcubes`, and :func:`homsimpl` for more examples, - including illustrations of some of the optional parameters. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import CHomP - sage: T = cubical_complexes.Torus() - sage: CHomP()('homcubes', T) # optional - CHomP - {0: 0, 1: Z x Z, 2: Z} - - Relative homology of a segment relative to its endpoints:: - - sage: edge = simplicial_complexes.Simplex(1) - sage: ends = edge.n_skeleton(0) - sage: CHomP()('homsimpl', edge) # optional - CHomP - {0: 0} - sage: CHomP()('homsimpl', edge, ends) # optional - CHomP - {0: 0, 1: Z} - - Homology of a chain complex:: - - sage: C = ChainComplex({3: 2 * identity_matrix(ZZ, 2)}, degree=-1) - sage: CHomP()('homchain', C) # optional - CHomP - {2: C2 x C2} - """ - def __repr__(self): - """ - String representation. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import CHomP - sage: CHomP() # indirect doctest - CHomP interface - """ - return "CHomP interface" - - def __call__(self, program, complex, subcomplex=None, **kwds): - """ - Call a CHomP program to compute the homology of a chain - complex, simplicial complex, or cubical complex. - - Deprecated: see :issue:`33777`. - - See :class:`CHomP` for full documentation. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import CHomP - sage: T = cubical_complexes.Torus() - sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - {0: 0, 1: Z x Z, 2: Z} - """ - from sage.misc.temporary_file import tmp_filename - from sage.topology.cubical_complex import CubicalComplex, cubical_complexes - from sage.topology.simplicial_complex import SimplicialComplex, Simplex - from sage.homology.chain_complex import HomologyGroup - from subprocess import Popen, PIPE - from sage.rings.integer_ring import ZZ - from sage.rings.rational_field import QQ - from sage.modules.free_module import VectorSpace - from sage.modules.free_module_element import free_module_element as vector - from sage.combinat.free_module import CombinatorialFreeModule - - deprecation(33777, "the CHomP interface is deprecated") - - if not have_chomp(program): - raise OSError("Program %s not found" % program) - - verbose = kwds.get('verbose', False) - generators = kwds.get('generators', False) - extra_opts = kwds.get('extra_opts', '') - base_ring = kwds.get('base_ring', ZZ) - - if extra_opts: - extra_opts = extra_opts.split() - else: - extra_opts = [] - - # type of complex: - cubical = False - simplicial = False - chain = False - # CHomP seems to have problems with cubical complexes if the - # first interval in the first cube defining the complex is - # degenerate. So replace the complex X with [0,1] x X. - if isinstance(complex, CubicalComplex): - cubical = True - edge = cubical_complexes.Cube(1) - original_complex = complex - complex = edge.product(complex) - if verbose: - print("Cubical complex") - elif isinstance(complex, SimplicialComplex): - simplicial = True - if verbose: - print("Simplicial complex") - else: - chain = True - base_ring = kwds.get('base_ring', complex.base_ring()) - if verbose: - print("Chain complex over %s" % base_ring) - - if base_ring == QQ: - raise ValueError("CHomP doesn't compute over the rationals, only over Z or F_p.") - if base_ring.is_prime_field(): - p = base_ring.characteristic() - extra_opts.append('-p%s' % p) - mod_p = True - else: - mod_p = False - - # - # complex - # - try: - data = complex._chomp_repr_() - except AttributeError: - raise AttributeError("Complex cannot be converted to use with CHomP.") - - datafile = tmp_filename() - with open(datafile, 'w') as f: - f.write(data) - - # - # subcomplex - # - if subcomplex is None: - if cubical: - subcomplex = CubicalComplex([complex.n_cells(0)[0]]) - elif simplicial: - m = re.search(r'\(([^,]*),', data) - v = int(m.group(1)) - subcomplex = SimplicialComplex([[v]]) - else: - # replace subcomplex with [0,1] x subcomplex. - if cubical: - subcomplex = edge.product(subcomplex) - # - # generators - # - if generators: - genfile = tmp_filename() - extra_opts.append('-g%s' % genfile) - - # - # call program - # - if subcomplex is not None: - try: - sub = subcomplex._chomp_repr_() - except AttributeError: - raise AttributeError("Subcomplex cannot be converted to use with CHomP.") - subfile = tmp_filename() - with open(subfile, 'w') as f: - f.write(sub) - else: - subfile = '' - if verbose: - print("Popen called with arguments", end="") - print([program, datafile, subfile] + extra_opts) - print("") - print("CHomP output:") - print("") - # output = Popen([program, datafile, subfile, extra_opts], - cmd = [program, datafile] - if subfile: - cmd.append(subfile) - if extra_opts: - cmd.extend(extra_opts) - output = Popen(cmd, stdout=PIPE).communicate()[0] - if verbose: - print(output) - print("End of CHomP output") - print("") - if generators: - with open(genfile, 'r') as f: - gens = f.read() - if verbose: - print("Generators:") - print(gens) - # - # process output - # - if output.find('ERROR') != -1: - raise RuntimeError('error inside CHomP') - # output contains substrings of one of the forms - # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2", - # "H_1 = Z + Z_2 + Z" - if output.find('trivial') != -1: - if mod_p: - return {0: VectorSpace(base_ring, 0)} - else: - return {0: HomologyGroup(0, ZZ)} - d = {} - h = re.compile("^H_([0-9]*) = (.*)$", re.M) - tors = re.compile("Z_([0-9]*)") - # - # homology groups - # - for m in h.finditer(output): - if verbose: - print(m.groups()) - # dim is the dimension of the homology group - dim = int(m.group(1)) - # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..." - hom_str = m.group(2) - # need to read off number of summands and their invariants - if hom_str.find("0") == 0: - if mod_p: - hom = VectorSpace(base_ring, 0) - else: - hom = HomologyGroup(0, ZZ) - else: - rk = 0 - if hom_str.find("^") != -1: - rk_srch = re.search(r'\^([0-9]*)\s?', hom_str) - rk = int(rk_srch.group(1)) - rk += len(re.findall(r"(Z$)|(Z\s)", hom_str)) - if mod_p: - rk = rk if rk != 0 else 1 - if verbose: - print("dimension = %s, rank of homology = %s" % (dim, rk)) - hom = VectorSpace(base_ring, rk) - else: - n = rk - invts = [] - for t in tors.finditer(hom_str): - n += 1 - invts.append(int(t.group(1))) - for i in range(rk): - invts.append(0) - if verbose: - print("dimension = %s, number of factors = %s, invariants = %s" % (dim, n, invts)) - hom = HomologyGroup(n, ZZ, invts) - - # - # generators - # - if generators: - if cubical: - g = process_generators_cubical(gens, dim) - if verbose: - print("raw generators: %s" % g) - if g: - module = CombinatorialFreeModule(base_ring, - original_complex.n_cells(dim), - prefix="", - bracket=True) - basis = module.basis() - output = [] - for x in g: - v = module(0) - for term in x: - v += term[0] * basis[term[1]] - output.append(v) - g = output - elif simplicial: - g = process_generators_simplicial(gens, dim, complex) - if verbose: - print("raw generators: %s" % gens) - if g: - module = CombinatorialFreeModule(base_ring, - complex.n_cells(dim), - prefix="", - bracket=False) - basis = module.basis() - output = [] - for x in g: - v = module(0) - for term in x: - if complex._is_numeric(): - v += term[0] * basis[term[1]] - else: - translate = complex._translation_from_numeric() - simplex = Simplex([translate[a] for a in term[1]]) - v += term[0] * basis[simplex] - output.append(v) - g = output - elif chain: - g = process_generators_chain(gens, dim, base_ring) - if verbose: - print("raw generators: %s" % gens) - if g: - if not mod_p: - # sort generators to match up with corresponding invariant - g = [_[1] for _ in sorted(zip(invts, g), key=lambda x: x[0])] - d[dim] = (hom, g) - else: - d[dim] = hom - else: - d[dim] = hom - - if chain: - new_d = {} - diff = complex.differential() - if len(diff) == 0: - return {} - bottom = min(diff) - top = max(diff) - for dim in d: - if complex._degree_of_differential == -1: # chain complex - new_dim = bottom + dim - else: # cochain complex - new_dim = top - dim - if isinstance(d[dim], tuple): - # generators included. - group = d[dim][0] - gens = d[dim][1] - new_gens = [] - dimension = complex.differential(new_dim).ncols() - # make sure that each vector is embedded in the - # correct ambient space: pad with a zero if - # necessary. - for v in gens: - v_dict = v.dict() - if dimension - 1 not in v.dict(): - v_dict[dimension - 1] = 0 - new_gens.append(vector(base_ring, v_dict)) - else: - new_gens.append(v) - new_d[new_dim] = (group, new_gens) - else: - new_d[new_dim] = d[dim] - d = new_d - return d - - def help(self, program): - """ - Print a help message for ``program``, a program from the CHomP suite. - - :param program: which CHomP program to use - :type program: string - :return: nothing -- just print a message - - EXAMPLES:: - - sage: from sage.interfaces.chomp import CHomP - sage: CHomP().help('homcubes') # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - HOMCUBES, ver. ... Copyright (C) ... by Pawel Pilarczyk... - """ - deprecation(33777, "the CHomP interface is deprecated") - from subprocess import Popen, PIPE - print(Popen([program, '-h'], stdout=PIPE).communicate()[0]) - - -def homsimpl(complex=None, subcomplex=None, **kwds): - r""" - Compute the homology of a simplicial complex using the CHomP - program ``homsimpl``. If the argument ``subcomplex`` is present, - compute homology of ``complex`` relative to ``subcomplex``. - - This function is deprecated: see :issue:`33777`. - - :param complex: a simplicial complex - :param subcomplex: a subcomplex of ``complex`` or None (the default) - :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`. - :type base_ring: ring; optional, default `\ZZ` - :param generators: if True, also return list of generators - :type generators: boolean; optional, default False - :param verbose: if True, print helpful messages as the computation - progresses - :type verbose: boolean; optional, default False - :param help: if True, just print a help message and exit - :type help: boolean; optional, default False - :param extra_opts: options passed directly to ``program`` - :type extra_opts: string - :return: homology groups as a dictionary indexed by dimension - - EXAMPLES:: - - sage: from sage.interfaces.chomp import homsimpl - sage: T = simplicial_complexes.Torus() - sage: M8 = simplicial_complexes.MooreSpace(8) - sage: M4 = simplicial_complexes.MooreSpace(4) - sage: X = T.disjoint_union(T).disjoint_union(T).disjoint_union(M8).disjoint_union(M4) - sage: homsimpl(X)[1] # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - Z^6 x C4 x C8 - - Relative homology:: - - sage: S = simplicial_complexes.Simplex(3) - sage: bdry = S.n_skeleton(2) - sage: homsimpl(S, bdry)[3] # optional - CHomP - Z - - Generators: these are given as a list after the homology group. - Each generator is specified as a linear combination of simplices:: - - sage: homsimpl(S, bdry, generators=True)[3] # optional - CHomP - (Z, [(0, 1, 2, 3)]) - - sage: homsimpl(simplicial_complexes.Sphere(1), generators=True) # optional - CHomP - {0: 0, 1: (Z, [(0, 1) - (0, 2) + (1, 2)])} - - TESTS: - - Generators for a simplicial complex whose vertices are not integers:: - - sage: S1 = simplicial_complexes.Sphere(1) - sage: homsimpl(S1.join(S1), generators=True, base_ring=GF(2))[3][1] # optional - CHomP - [('L0', 'L1', 'R0', 'R1') + ('L0', 'L1', 'R0', 'R2') + ('L0', 'L1', 'R1', 'R2') + ('L0', 'L2', 'R0', 'R1') + ('L0', 'L2', 'R0', 'R2') + ('L0', 'L2', 'R1', 'R2') + ('L1', 'L2', 'R0', 'R1') + ('L1', 'L2', 'R0', 'R2') + ('L1', 'L2', 'R1', 'R2')] - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.topology.simplicial_complex import SimplicialComplex - help = kwds.get('help', False) - if help: - return CHomP().help('homsimpl') - # Check types here, because if you pass homsimpl a cubical - # complex, it tries to compute its homology as if it were a - # simplicial complex and gets terribly wrong answers. - if (isinstance(complex, SimplicialComplex) - and (subcomplex is None or isinstance(subcomplex, SimplicialComplex))): - return CHomP()('homsimpl', complex, subcomplex=subcomplex, **kwds) - else: - raise TypeError("Complex and/or subcomplex are not simplicial complexes.") - - -def homcubes(complex=None, subcomplex=None, **kwds): - r""" - Compute the homology of a cubical complex using the CHomP program - ``homcubes``. If the argument ``subcomplex`` is present, compute - homology of ``complex`` relative to ``subcomplex``. - - This function is deprecated: see :issue:`33777`. - - :param complex: a cubical complex - :param subcomplex: a subcomplex of ``complex`` or None (the default) - :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`. - :type base_ring: ring; optional, default `\ZZ` - :param generators: if True, also return list of generators - :type generators: boolean; optional, default False - :param verbose: if True, print helpful messages as the computation progresses - :type verbose: boolean; optional, default False - :param help: if True, just print a help message and exit - :type help: boolean; optional, default False - :param extra_opts: options passed directly to ``homcubes`` - :type extra_opts: string - :return: homology groups as a dictionary indexed by dimension - - EXAMPLES:: - - sage: from sage.interfaces.chomp import homcubes - sage: S = cubical_complexes.Sphere(3) - sage: homcubes(S)[3] # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - Z - - Relative homology:: - - sage: C3 = cubical_complexes.Cube(3) - sage: bdry = C3.n_skeleton(2) - sage: homcubes(C3, bdry) # optional - CHomP - {0: 0, 1: 0, 2: 0, 3: Z} - - Generators: these are given as a list after the homology group. - Each generator is specified as a linear combination of cubes:: - - sage: homcubes(cubical_complexes.Sphere(1), generators=True, base_ring=GF(2))[1][1] # optional - CHomP - [[[1,1] x [0,1]] + [[0,1] x [1,1]] + [[0,1] x [0,0]] + [[0,0] x [0,1]]] - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.topology.cubical_complex import CubicalComplex - help = kwds.get('help', False) - if help: - return CHomP().help('homcubes') - # Type-checking is here for the same reason as in homsimpl above. - if (isinstance(complex, CubicalComplex) - and (subcomplex is None or isinstance(subcomplex, CubicalComplex))): - return CHomP()('homcubes', complex, subcomplex=subcomplex, **kwds) - else: - raise TypeError("Complex and/or subcomplex are not cubical complexes.") - - -def homchain(complex=None, **kwds): - r""" - Compute the homology of a chain complex using the CHomP program - ``homchain``. - - This function is deprecated: see :issue:`33777`. - - :param complex: a chain complex - :param generators: if True, also return list of generators - :type generators: boolean; optional, default False - :param verbose: if True, print helpful messages as the computation progresses - :type verbose: boolean; optional, default False - :param help: if True, just print a help message and exit - :type help: boolean; optional, default False - :param extra_opts: options passed directly to ``homchain`` - :type extra_opts: string - :return: homology groups as a dictionary indexed by dimension - - EXAMPLES:: - - sage: from sage.interfaces.chomp import homchain - sage: C = cubical_complexes.Sphere(3).chain_complex() - sage: homchain(C)[3] # optional - CHomP - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - Z - - Generators: these are given as a list after the homology group. - Each generator is specified as a cycle, an element in the - appropriate free module over the base ring:: - - sage: C2 = delta_complexes.Sphere(2).chain_complex() - sage: homchain(C2, generators=True)[2] # optional - CHomP - (Z, [(1, -1)]) - sage: homchain(C2, generators=True, base_ring=GF(2))[2] # optional - CHomP - (Vector space of dimension 1 over Finite Field of size 2, [(1, 1)]) - - TESTS: - - Chain complexes concentrated in negative dimensions, cochain complexes, etc.:: - - sage: C = ChainComplex({-5: 4 * identity_matrix(ZZ, 2)}, degree=-1) - sage: homchain(C) # optional - CHomP - {-6: C4 x C4} - sage: C = ChainComplex({-5: 4 * identity_matrix(ZZ, 2)}, degree=1) - sage: homchain(C, generators=True) # optional - CHomP - {-4: (C4 x C4, [(1, 0), (0, 1)])} - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.homology.chain_complex import ChainComplex_class - help = kwds.get('help', False) - if help: - return CHomP().help('homchain') - # Type-checking just in case. - if isinstance(complex, ChainComplex_class): - return CHomP()('homchain', complex, **kwds) - else: - raise TypeError("Complex is not a chain complex.") - - -def process_generators_cubical(gen_string, dim): - r""" - Process CHomP generator information for cubical complexes. - - This function is deprecated: see :issue:`33777`. - - :param gen_string: generator output from CHomP - :type gen_string: string - :param dim: dimension in which to find generators - :type dim: integer - :return: list of generators in each dimension, as described below - - ``gen_string`` has the form :: - - The 2 generators of H_1 follow: - generator 1 - -1 * [(0,0,0,0,0)(0,0,0,0,1)] - 1 * [(0,0,0,0,0)(0,0,1,0,0)] - ... - generator 2 - -1 * [(0,1,0,1,1)(1,1,0,1,1)] - -1 * [(0,1,0,0,1)(0,1,0,1,1)] - ... - - Each line consists of a coefficient multiplied by a cube; the cube - is specified by its "bottom left" and "upper right" corners. - - For technical reasons, we remove the first coordinate of each - tuple, and using regular expressions, the remaining parts get - converted from a string to a pair ``(coefficient, Cube)``, with - the cube represented as a product of tuples. For example, the - first line in "generator 1" gets turned into :: - - (-1, [0,0] x [0,0] x [0,0] x [0,1]) - - representing an element in the free abelian group with basis given - by cubes. Each generator is a list of such pairs, representing - the sum of such elements. These are reassembled in - :meth:`CHomP.__call__` to actual elements in the free module - generated by the cubes of the cubical complex in the appropriate - dimension. - - Therefore the return value is a list of lists of pairs, one list - of pairs for each generator. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import process_generators_cubical - sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * [(0,0,0,0,0)(0,0,0,0,1)]\n1 * [(0,0,0,0,0)(0,0,1,0,0)]" - sage: process_generators_cubical(s, 1) - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - [[(-1, [0,0] x [0,0] x [0,0] x [0,1]), (1, [0,0] x [0,1] x [0,0] x [0,0])]] - sage: len(process_generators_cubical(s, 1)) # only one generator - 1 - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.topology.cubical_complex import Cube - # each dim in gen_string starts with "The generator for - # H_3 follows:". So search for "T" to find the - # end of the current list of generators. - g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim) - g = g_srch.search(gen_string) - output = [] - cubes_list = [] - if g: - g = g.group(1) - if g: - # project g to one end of the cylinder [0,1] x complex: - # - # drop the first coordinate and eliminate duplicates, at least - # in positive dimensions, drop any line containing a - # degenerate cube - g = re.sub(r'\([01],', '(', g) - if dim > 0: - lines = g.splitlines() - newlines = [] - for l in lines: - x = re.findall(r'(\([0-9,]*\))', l) - if x: - left, right = x - left = [int(a) for a in left.strip('()').split(',')] - right = [int(a) for a in right.strip('()').split(',')] - if sum([xx - yy for (xx, yy) in zip(right, left)]) == dim: - newlines.append(l) - else: # line like "generator 2" - newlines.append(l) - g = newlines - cubes_list = [] - for l in g: - cubes = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?\[\(([-0-9,]+)\)\(([-0-9,]+)\)\]', l) - if cubes: - if cubes.group(1) and re.search("-", cubes.group(1)): - sign = -1 - else: - sign = 1 - if cubes.group(2) and len(cubes.group(2)) > 0: - coeff = sign * int(cubes.group(2)) - else: - coeff = sign * 1 - cube1 = [int(a) for a in cubes.group(3).split(',')] - cube2 = [int(a) for a in cubes.group(4).split(',')] - cube = Cube(zip(cube1, cube2)) - cubes_list.append((coeff, cube)) - else: - if cubes_list: - output.append(cubes_list) - cubes_list = [] - if cubes_list: - output.append(cubes_list) - return output - else: - return None - - -def process_generators_simplicial(gen_string, dim, complex): - r""" - Process CHomP generator information for simplicial complexes. - - This function is deprecated: see :issue:`33777` - - :param gen_string: generator output from CHomP - :type gen_string: string - :param dim: dimension in which to find generators - :type dim: integer - :param complex: simplicial complex under consideration - :return: list of generators in each dimension, as described below - - ``gen_string`` has the form :: - - The 2 generators of H_1 follow: - generator 1 - -1 * (1,6) - 1 * (1,4) - ... - generator 2 - -1 * (1,6) - 1 * (1,4) - ... - - where each line contains a coefficient and a simplex. Each line - is converted, using regular expressions, from a string to a pair - ``(coefficient, Simplex)``, like :: - - (-1, (1,6)) - - representing an element in the free abelian group with basis given - by simplices. Each generator is a list of such pairs, - representing the sum of such elements. These are reassembled in - :meth:`CHomP.__call__` to actual elements in the free module - generated by the simplices of the simplicial complex in the - appropriate dimension. - - Therefore the return value is a list of lists of pairs, one list - of pairs for each generator. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import process_generators_simplicial - sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * (1,6)\n1 * (1,4)" - sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus()) - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - [[(-1, (1, 6)), (1, (1, 4))]] - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.topology.simplicial_complex import Simplex - # each dim in gen_string starts with "The generator for H_3 - # follows:". So search for "T" to find the end of the current - # list of generators. - g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim) - g = g_srch.search(gen_string) - output = [] - simplex_list = [] - if g: - g = g.group(1) - if g: - lines = g.splitlines() - for l in lines: - simplex = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?(\([0-9,]*\))', l) - if simplex: - if simplex.group(1) and re.search("-", simplex.group(1)): - sign = -1 - else: - sign = 1 - if simplex.group(2) and len(simplex.group(2)) > 0: - coeff = sign * int(simplex.group(2)) - else: - coeff = sign * 1 - simplex = Simplex([int(a) for a in simplex.group(3).strip('()').split(',')]) - simplex_list.append((coeff, simplex)) - else: - if simplex_list: - output.append(simplex_list) - simplex_list = [] - if simplex_list: - output.append(simplex_list) - return output - else: - return None - - -def process_generators_chain(gen_string, dim, base_ring=None): - r""" - Process CHomP generator information for simplicial complexes. - - This function is deprecated: see :issue:`33777`. - - :param gen_string: generator output from CHomP - :type gen_string: string - :param dim: dimension in which to find generators - :type dim: integer - :param base_ring: base ring over which to do the computations - :type base_ring: optional, default ZZ - :return: list of generators in each dimension, as described below - - ``gen_string`` has the form :: - - [H_0] - a1 - - [H_1] - a2 - a3 - - [H_2] - a1 - a2 - - For each homology group, each line lists a homology generator as a - linear combination of generators ``ai`` of the group of chains in - the appropriate dimension. The elements ``ai`` are indexed - starting with `i=1`. Each generator is converted, using regular - expressions, from a string to a vector (an element in the free - module over ``base_ring``), with ``ai`` representing the unit - vector in coordinate `i-1`. For example, the string ``a1 - a2`` - gets converted to the vector ``(1, -1)``. - - Therefore the return value is a list of vectors. - - EXAMPLES:: - - sage: from sage.interfaces.chomp import process_generators_chain - sage: s = "[H_0]\na1\n\n[H_1]\na2\na3\n" - sage: process_generators_chain(s, 1) - doctest:...: DeprecationWarning: the CHomP interface is deprecated - See https://github.com/sagemath/sage/issues/33777 for details. - [(0, 1), (0, 0, 1)] - sage: s = "[H_0]\na1\n\n[H_1]\n5 * a2 - a1\na3\n" - sage: process_generators_chain(s, 1, base_ring=ZZ) - [(-1, 5), (0, 0, 1)] - sage: process_generators_chain(s, 1, base_ring=GF(2)) - [(1, 1), (0, 0, 1)] - """ - deprecation(33777, "the CHomP interface is deprecated") - from sage.modules.free_module_element import vector - from sage.rings.integer_ring import ZZ - if base_ring is None: - base_ring = ZZ - # each dim in gens starts with a string like - # "[H_3]". So search for "[" to find the end of - # the current list of generators. - g_srch = re.compile(r'\[H_%s\]\n([^]]*)(?:\[|$)' % dim) - g = g_srch.search(gen_string) - if g: - g = g.group(1) - if g: - # each line in the string g is a linear - # combination of things like "a2", "a31", etc. - # indexing on the a's starts at 1. - lines = g.splitlines() - new_gens = [] - for l in lines: - gen = re.compile(r"([+-]?)\s?([0-9]+)?\s?[*]?\s?a([0-9]*)(?:\s|$)") - v = {} - for term in gen.finditer(l): - if term.group(1) and re.search("-", term.group(1)): - sign = -1 - else: - sign = 1 - if term.group(2) and len(term.group(2)) > 0: - coeff = sign * int(term.group(2)) - else: - coeff = sign * 1 - idx = int(term.group(3)) - v[idx-1] = coeff - if v: - new_gens.append(vector(base_ring, v)) - g = new_gens - return g diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 87feb554d16..7d4dddabba9 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -647,7 +647,8 @@ def _convert_prod(x, y): def explicitly_not_implemented(*args): raise NotImplementedError("the translation of the FriCAS Expression '%s' to sage is not yet implemented" % args) - register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}, 2) + register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}, 2) # to be removed once we fully on FriCAS 1.3.10+ + register_symbol(lambda *args: explicitly_not_implemented("FEseries"), {'fricas': 'FEseries'}, 2) register_symbol(lambda *args: explicitly_not_implemented("rootOfRec"), {'fricas': 'rootOfRec'}, 2) def set(self, var, value): @@ -802,7 +803,7 @@ def get_unparsed_InputForm(self, var): '(1..3)$Segment(PositiveInteger())' """ - return self.get_string('unparse((%s)::InputForm)' % str(var)) + return self.get_string('unparse((%s)::InputForm)' % var) def get_InputForm(self, var): """ @@ -1184,7 +1185,7 @@ def _latex_(self): \frac{{{\log \left( {{e+1}} \right)} \ {\sin \left( {{y+x}} \right)}}}{{{e} ^{z}}} sage: latex(fricas("matrix([[1,2],[3,4]])")) - \left[ \begin{array}{cc} 1 & 2 \\ 3 & 4\end{array} \right] + \left[ \begin{array}{cc} 1 & 2 \\ 3 & 4...\end{array}...\right] sage: latex(fricas("integrate(sin(x+1/x),x)")) \int ^{\displaystyle x} {{\sin \left( {{\frac{{{{ \%...} ^{2}}+1}}{ \%...}}} \right)} \ {d \%...}} @@ -1715,7 +1716,6 @@ def _sage_expression(fricas_InputForm): sage: f[1].sage() -1/2*sqrt(1/3)*sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) + 1/2*sqrt(-(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3) + 6*sqrt(1/3)/sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) - 4/3/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) - """ # a FriCAS expressions may contain implicit references to a # rootOf expression within itself, as for example in the @@ -1836,7 +1836,7 @@ def _sage_(self): sage: fricas(x+3).sage() x + 3 sage: fricas(x+3).domainOf() - Polynomial(Integer()) + Polynomial(Integer...) sage: fricas(matrix([[2,3],[4,x+5]])).diagonal().sage() (2, x + 5) @@ -1904,7 +1904,7 @@ def _sage_(self): sage: s.sage() Traceback (most recent call last): ... - NotImplementedError: the translation of the FriCAS Expression 'rootOfADE' to sage is not yet implemented + NotImplementedError: the translation of the FriCAS Expression 'FEseries' to sage is not yet implemented sage: s = fricas("series(sqrt(1+x), x=0)"); s 1 1 2 1 3 5 4 7 5 21 6 33 7 429 8 @@ -1994,19 +1994,24 @@ def _sage_(self): return R([self.coefficient(i).sage() for i in range(ZZ(self.degree()) + 1)]) - # finally translate domains with InputForm - try: - unparsed_InputForm = P.get_unparsed_InputForm(self._name) - except RuntimeError as error: - raise NotImplementedError("the translation of the FriCAS object\n\n%s\n\nto sage is not yet implemented:\n%s" % (self, error)) + # finally translate domains with InputForm - we do this + # lazily, because sometimes we can use unparse, sometimes we + # need our custom sageprint + + def unparsed_InputForm(): + try: + return P.get_unparsed_InputForm(self._name) + except RuntimeError as error: + raise NotImplementedError("the translation of the FriCAS object\n\n%s\n\nto sage is not yet implemented:\n%s" % (self, error)) + if head == "Boolean": - return unparsed_InputForm == "true" + return unparsed_InputForm() == "true" if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]: - return ZZ(unparsed_InputForm) + return ZZ(unparsed_InputForm()) if head == "String": - return unparsed_InputForm + return unparsed_InputForm() if head == "Float": # Warning: precision$Float gives the current precision, @@ -2014,22 +2019,28 @@ def _sage_(self): # self. prec = max(P.new("length mantissa(%s)" % self._name).sage(), 53) R = RealField(prec) - x, e, b = unparsed_InputForm.lstrip('float(').rstrip(')').split(',') + x, e, b = unparsed_InputForm().lstrip('float(').rstrip(')').split(',') return R(ZZ(x) * ZZ(b)**ZZ(e)) if head == "DoubleFloat": - return RDF(unparsed_InputForm) + return RDF(unparsed_InputForm()) if head == "AlgebraicNumber": - s = unparsed_InputForm[:-len("::AlgebraicNumber()")] + s = unparsed_InputForm()[:-len("::AlgebraicNumber()")] return sage_eval("QQbar(" + s + ")") if head == "IntegerMod" or head == "PrimeField": # one might be tempted not to go via InputForm here, but # it turns out to be safer to do it. - n = unparsed_InputForm[len("index("):] - n = n[:n.find(")")] - return self._get_sage_type(domain)(n) + s = unparsed_InputForm()[len("index("):] + s = s[:s.find(")")] + return self._get_sage_type(domain)(s) + + if head == 'DistributedMultivariatePolynomial': + base_ring = self._get_sage_type(domain[2]) + vars = domain[1].car() + R = PolynomialRing(base_ring, vars) + return R(unparsed_InputForm()) if head == "Polynomial": base_ring = self._get_sage_type(domain[1]) @@ -2039,29 +2050,24 @@ def _sage_(self): # the following is a bad hack, we should be getting a list here vars = P.get_unparsed_InputForm("variables(%s)" % self._name)[1:-1] + s = unparsed_InputForm() if vars == "": - return base_ring(unparsed_InputForm) - else: - R = PolynomialRing(base_ring, vars) - return R(unparsed_InputForm) + return base_ring(s) + + R = PolynomialRing(base_ring, vars) + return R(s) if head in ["OrderedCompletion", "OnePointCompletion"]: # it would be more correct to get the type parameter # (which might not be Expression Integer) and recurse return FriCASElement._sage_expression(P.get_InputForm(self._name)) - if head == "Expression" or head == "Pi": + if head == "Expression" or head == "Pi" or head == "PiDomain": # we treat Expression Integer and Expression Complex # Integer just the same return FriCASElement._sage_expression(P.get_InputForm(self._name)) - if head == 'DistributedMultivariatePolynomial': - base_ring = self._get_sage_type(domain[2]) - vars = domain[1].car() - R = PolynomialRing(base_ring, vars) - return R(unparsed_InputForm) - - raise NotImplementedError("the translation of the FriCAS object %s to sage is not yet implemented" % (unparsed_InputForm)) + raise NotImplementedError("the translation of the FriCAS object %s to sage is not yet implemented" % (unparsed_InputForm())) @instancedoc diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index ee5861ac785..2e49e4c106f 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1562,16 +1562,10 @@ def _latex_(self): sage: s = gap("[[1,2], [3/4, 5/6]]") sage: latex(s) - \left(\begin{array}{rr} 1&2\\ 3/4&\frac{5}{6}\\ \end{array}\right) + \left[\left[1, 2\right], \left[\frac{3}{4}, \frac{5}{6}\right]\right] """ - P = self._check_valid() - try: - s = P.eval('LaTeXObj(%s)' % self.name()) - s = s.replace('\\\\', '\\').replace('"', '') - s = s.replace('%\\n', ' ') - return s - except RuntimeError: - return str(self) + from sage.misc.latex import latex + return latex(self._sage_()) @cached_method def _tab_completion(self): diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py index aecddae452b..40d1b7319be 100644 --- a/src/sage/interfaces/kash.py +++ b/src/sage/interfaces/kash.py @@ -527,12 +527,7 @@ def _quit_string(self): return 'quit;' def _start(self): - try: - Expect._start(self) - except RuntimeError: - # TODO: replace this error with something more accurate. - from sage.misc.package import PackageNotFoundError - raise PackageNotFoundError("kash") + Expect._start(self) # Turn off the annoying timer. self.eval('Time(false);') diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index dedbebab99c..4697b9763d3 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1211,14 +1211,14 @@ def current_ring(self): polynomial ring, over a field, global ordering // coefficients: ZZ/127 // number of vars : 3 - // block 1 : ordering rp + // block 1 : ordering ip // : names x y z // block 2 : ordering C sage: singular.current_ring() polynomial ring, over a field, global ordering // coefficients: ZZ/127 // number of vars : 3 - // block 1 : ordering rp + // block 1 : ordering ip // : names x y z // block 2 : ordering C """ @@ -1402,6 +1402,14 @@ def _repr_(self): if self._name in s: if self.get_custom_name() is None and self.type() == 'matrix': s = self.parent().eval('pmat(%s,20)' % (self.name())) + # compatibility for singular 4.3.2p10 and before + if s.startswith("polynomial ring,"): + from sage.rings.polynomial.term_order import singular_name_mapping + from sage.repl.rich_output import get_display_manager + # this is our cue that singular uses `rp` instead of `ip` + if singular_name_mapping['invlex'] == 'rp' and 'doctest' in str(get_display_manager()): + s = re.sub('^(// .*block.* : ordering )rp$', '\\1ip', + s, 0, re.MULTILINE) return s def __copy__(self): @@ -2025,6 +2033,10 @@ def _sage_(self, R=None): sage: type(singular(int(5)).sage()) + Test that bigintvec can be coerced:: + + sage: singular('hilb((ideal(x)), 1)').sage() + (1, -1, 0, 0, -1, 1, 0) """ typ = self.type() if typ == 'poly': @@ -2040,6 +2052,9 @@ def _sage_(self, R=None): elif typ == 'intvec': from sage.modules.free_module_element import vector return vector([sage.rings.integer.Integer(str(e)) for e in self]) + elif typ == 'bigintvec': + from sage.modules.free_module_element import vector + return vector([sage.rings.rational.Rational(str(e)) for e in self]) elif typ == 'intmat': from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 7ddbe5879c3..097021e2dd4 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -3606,36 +3606,57 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, # such that the resulting regions are in fact closed regions # with straight angles, and using the minimal number of bends. regions = sorted(self.regions(), key=len) - regions = regions[:-1] edges = list(set(flatten(pd_code))) edges.sort() MLP = MixedIntegerLinearProgram(maximization=False, solver=solver) # v will be the list of variables in the MLP problem. There will be - # two variables for each edge: number of right bendings and number of - # left bendings (at the end, since we are minimizing the total, only one - # of each will be nonzero + # two variables for each edge counting the number of bendings needed. + # The one with even index corresponds to the flow of this number from + # the left-hand-side region to the right-hand-side region if the edge + # is positive oriented. The one with odd index corresponds to the + # flow in the opposite direction. For a negative oriented edge the + # same is true but with exchanged directions. At the end, since we + # are minimizing the total, only one of each will be nonzero. v = MLP.new_variable(nonnegative=True, integer=True) + + def flow_from_source(e): + r""" + Return the flow variable from the source. + """ + if e > 0: + return v[2*edges.index(e)] + else: + return v[2*edges.index(-e)+1] + + def flow_to_sink(e): + r""" + Return the flow variable to the sink. + """ + return flow_from_source(-e) + # one condition for each region - for i in range(len(regions)): - cond = 0 + lr = len(regions) + for i in range(lr): r = regions[i] - for e in r: - if e > 0: - cond = cond + v[2*edges.index(e)] - v[2*edges.index(e) + 1] - else: - cond = cond - v[2*edges.index(-e)] + v[2*edges.index(-e) + 1] - MLP.add_constraint(cond == 4 - len(r)) + if i < lr - 1: + # capacity of interior region, sink if positive, source if negative + capacity = len(r) - 4 + else: + # capacity of exterior region, only sink (added to fix :issue:`37587`). + capacity = len(r) + 4 + flow = sum(flow_to_sink(e) - flow_from_source(e) for e in r) + MLP.add_constraint(flow == capacity) # exterior region only sink + MLP.set_objective(MLP.sum(v.values())) MLP.solve() # we store the result in a vector s packing right bends as negative left ones values = MLP.get_values(v, convert=ZZ, tolerance=1e-3) - s = [values[2*i] - values[2*i + 1] - for i in range(len(edges))] + s = [values[2*i] - values[2*i + 1] for i in range(len(edges))] # segments represents the different parts of the previous edges after bending segments = {e: [(e,i) for i in range(abs(s[edges.index(e)])+1)] for e in edges} pieces = {tuple(i): [i] for j in segments.values() for i in j} nregions = [] - for r in regions: + for r in regions[:-1]: # interior regions nregion = [] for e in r: if e > 0: @@ -3688,16 +3709,16 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, pieces[tuple(badregion[b][0])].append(N2) if a < b: - r1 = badregion[:a] + [[badregion[a][0],0], [N1,1]] + badregion[b:] - r2 = badregion[a+1:b] + [[N2,1],[N1,1]] + r1 = badregion[:a] + [[badregion[a][0], 0], [N1, 1]] + badregion[b:] + r2 = badregion[a + 1:b] + [[N2, 1],[N1, 1]] else: - r1 = badregion[b:a] + [[badregion[a][0],0], [N1,1]] - r2 = badregion[:b] + [[N2,1],[N1,1]] + badregion[a+1:] + r1 = badregion[b:a] + [[badregion[a][0], 0], [N1, 1]] + r2 = badregion[:b] + [[N2, 1],[N1, 1]] + badregion[a + 1:] if otherregion: c = [x for x in otherregion if badregion[b][0] == x[0]] c = otherregion.index(c[0]) - otherregion.insert(c+1, [N2,otherregion[c][1]]) + otherregion.insert(c + 1, [N2,otherregion[c][1]]) otherregion[c][1] = 0 nregions.remove(badregion) nregions.append(r1) diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 7b2b07b1f3f..6c15997c09f 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -728,7 +728,7 @@ class mwrank_MordellWeil(SageObject): P1 = [-3:0:1] is generator number 1 saturating up to 20...Saturation index bound (for points of good reduction) = 3 Reducing saturation bound from given value 20 to computed index bound 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 7) @@ -738,7 +738,7 @@ class mwrank_MordellWeil(SageObject): P2 = [-2:3:1] is generator number 2 saturating up to 20...Saturation index bound (for points of good reduction) = 4 Reducing saturation bound from given value 20 to computed index bound 4 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation possible kernel vector = [1,1] @@ -753,7 +753,7 @@ class mwrank_MordellWeil(SageObject): P3 = [-14:25:8] is generator number 3 saturating up to 20...Saturation index bound (for points of good reduction) = 3 Reducing saturation bound from given value 20 to computed index bound 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -908,7 +908,7 @@ def process(self, v, saturation_bound=0): saturating basis...Saturation index bound (for points of good reduction) = 93 Only p-saturating for p up to given value 2. The resulting points may not be p-saturated for p between this and the computed index bound 93 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 ] Checking 2-saturation possible kernel vector = [1,0,0] @@ -930,7 +930,7 @@ def process(self, v, saturation_bound=0): saturating basis...Saturation index bound (for points of good reduction) = 46 Only p-saturating for p up to given value 3. The resulting points may not be p-saturated for p between this and the computed index bound 46 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -954,7 +954,7 @@ def process(self, v, saturation_bound=0): saturating basis...Saturation index bound (for points of good reduction) = 15 Only p-saturating for p up to given value 5. The resulting points may not be p-saturated for p between this and the computed index bound 15 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 5 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -978,7 +978,7 @@ def process(self, v, saturation_bound=0): 0.417143558758384 sage: EQ.saturate() # points are now saturated saturating basis...Saturation index bound (for points of good reduction) = 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1189,7 +1189,7 @@ def saturate(self, max_prime=-1, min_prime=2): sage: EQ.saturate() # points are now saturated saturating basis...Saturation index bound (for points of good reduction) = 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1217,7 +1217,7 @@ def saturate(self, max_prime=-1, min_prime=2): sage: EQ.saturate() saturating basis...Saturation index bound (for points of good reduction) = 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index bc475f907b6..c685c329926 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -590,7 +590,7 @@ cdef class _mw: P1 = [-3:0:1] is generator number 1 saturating up to 20...Saturation index bound (for points of good reduction) = 3 Reducing saturation bound from given value 20 to computed index bound 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 7) @@ -600,7 +600,7 @@ cdef class _mw: P2 = [-2:3:1] is generator number 2 saturating up to 20...Saturation index bound (for points of good reduction) = 4 Reducing saturation bound from given value 20 to computed index bound 4 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation possible kernel vector = [1,1] @@ -615,7 +615,7 @@ cdef class _mw: P3 = [-14:25:8] is generator number 3 saturating up to 20...Saturation index bound (for points of good reduction) = 3 Reducing saturation bound from given value 20 to computed index bound 3 - Tamagawa index primes are [ 2 ] + Tamagawa index primes are [ 2 ]... Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index e36216d6395..3affdb34e11 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -48,6 +48,13 @@ cdef extern from "factory/factory.h": cdef int SW_USE_NTL_SORT cdef extern from "singular/Singular/libsingular.h": + """ + // compatibility for singular 4.3.2p10 and before + #if SINGULAR_VERSION <= 4330 + #define ringorder_ip ringorder_rp + #define BIGINTVEC_CMD INTVEC_CMD + #endif + """ # # OPTIONS @@ -243,7 +250,7 @@ cdef extern from "singular/Singular/libsingular.h": ringorder_s ringorder_lp ringorder_dp - ringorder_rp + ringorder_ip ringorder_Dp ringorder_wp ringorder_Wp @@ -291,6 +298,10 @@ cdef extern from "singular/Singular/libsingular.h": int row int col + cdef cppclass bigintmat: + int (*length)() + number* (*get)(int i) + # omalloc bins ctypedef struct omBin "omBin_s" @@ -921,6 +932,7 @@ cdef extern from "singular/Singular/libsingular.h": cdef int MATRIX_CMD cdef int LIST_CMD cdef int INTVEC_CMD + cdef int BIGINTVEC_CMD cdef int NONE cdef int RESOLUTION_CMD cdef int PACKAGE_CMD diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 4a5ab6d78f6..5c333e8d7e6 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -98,7 +98,7 @@ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence_g from sage.libs.singular.decl cimport * from sage.libs.singular.option import opt_ctx from sage.libs.singular.polynomial cimport singular_vector_maximal_component -from sage.libs.singular.singular cimport sa2si, si2sa, si2sa_intvec +from sage.libs.singular.singular cimport sa2si, si2sa, si2sa_intvec, si2sa_bigintvec from sage.libs.singular.singular import error_messages from sage.interfaces.singular import get_docstring @@ -954,6 +954,8 @@ cdef class Converter(SageObject): return si2sa(to_convert.data, self._singular_ring, self._sage_ring.base_ring()) elif rtyp == INTVEC_CMD: return si2sa_intvec( to_convert.data) + elif rtyp == BIGINTVEC_CMD: + return si2sa_bigintvec( to_convert.data) elif rtyp == STRING_CMD: # TODO: Need to determine what kind of data can be returned by a # STRING_CMD--is it just ASCII strings or can it be an arbitrary @@ -1048,6 +1050,17 @@ cdef class LibraryCallHandler(BaseCallHandler): """ return False +# mapping int --> string for function arity +arity_dict = { + CMD_1: "CMD_1", + CMD_2: "CMD_2", + CMD_3: "CMD_3", + CMD_12: "CMD_12", + CMD_13: "CMD_13", + CMD_23: "CMD_23", + CMD_123: "CMD_123", + CMD_M: "CMD_M" +} cdef class KernelCallHandler(BaseCallHandler): """ @@ -1125,8 +1138,9 @@ cdef class KernelCallHandler(BaseCallHandler): errorreported += 1 error_messages.append( - "Wrong number of arguments (got {} arguments, arity code is {})" - .format(number_of_arguments, self.arity)) + "Wrong number of arguments (got {} arguments, arity is {})" + .format(number_of_arguments, + arity_dict.get(self.arity) or self.arity)) return NULL cdef bint free_res(self) noexcept: @@ -1231,7 +1245,7 @@ cdef class SingularFunction(SageObject): Traceback (most recent call last): ... RuntimeError: error in Singular function call 'size': - Wrong number of arguments (got 2 arguments, arity code is 302) + Wrong number of arguments (got 2 arguments, arity is CMD_1) sage: size('foobar', ring=P) 6 @@ -1634,17 +1648,17 @@ def singular_function(name): Traceback (most recent call last): ... RuntimeError: error in Singular function call 'factorize': - Wrong number of arguments (got 0 arguments, arity code is 305) + Wrong number of arguments (got 0 arguments, arity is CMD_12) sage: factorize(f, 1, 2) Traceback (most recent call last): ... RuntimeError: error in Singular function call 'factorize': - Wrong number of arguments (got 3 arguments, arity code is 305) + Wrong number of arguments (got 3 arguments, arity is CMD_12) sage: factorize(f, 1, 2, 3) Traceback (most recent call last): ... RuntimeError: error in Singular function call 'factorize': - Wrong number of arguments (got 4 arguments, arity code is 305) + Wrong number of arguments (got 4 arguments, arity is CMD_12) The Singular function ``list`` can be called with any number of arguments:: diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index 0efff45904d..b3295206e5b 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -16,7 +16,7 @@ AUTHORS: # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.cpython.string cimport str_to_bytes +from sage.cpython.string cimport str_to_bytes, bytes_to_str from sage.libs.gmp.types cimport __mpz_struct from sage.libs.gmp.mpz cimport mpz_init_set_ui @@ -24,7 +24,7 @@ from sage.libs.gmp.mpz cimport mpz_init_set_ui from sage.libs.singular.decl cimport ring, currRing from sage.libs.singular.decl cimport rChangeCurrRing, rComplete, rDelete, idInit from sage.libs.singular.decl cimport omAlloc0, omStrDup, omAlloc -from sage.libs.singular.decl cimport ringorder_dp, ringorder_Dp, ringorder_lp, ringorder_rp, ringorder_ds, ringorder_Ds, ringorder_ls, ringorder_M, ringorder_c, ringorder_C, ringorder_wp, ringorder_Wp, ringorder_ws, ringorder_Ws, ringorder_a, rRingOrder_t +from sage.libs.singular.decl cimport ringorder_dp, ringorder_Dp, ringorder_lp, ringorder_ip, ringorder_ds, ringorder_Ds, ringorder_ls, ringorder_M, ringorder_c, ringorder_C, ringorder_wp, ringorder_Wp, ringorder_ws, ringorder_Ws, ringorder_a, rRingOrder_t from sage.libs.singular.decl cimport prCopyR from sage.libs.singular.decl cimport n_unknown, n_algExt, n_transExt, n_Z, n_Zn, n_Znm, n_Z2m from sage.libs.singular.decl cimport n_coeffType @@ -60,7 +60,7 @@ order_dict = { "dp": ringorder_dp, "Dp": ringorder_Dp, "lp": ringorder_lp, - "rp": ringorder_rp, + "ip": ringorder_ip, "ds": ringorder_ds, "Ds": ringorder_Ds, "ls": ringorder_ls, @@ -71,6 +71,16 @@ order_dict = { "a": ringorder_a, } +cdef extern from "singular/Singular/libsingular.h": + cdef char * rSimpleOrdStr(rRingOrder_t) + +if bytes_to_str(rSimpleOrdStr(ringorder_ip)) == "rp": + # compatibility for singular 4.3.2p10 and before + order_dict["rp"] = ringorder_ip + # also patch term_order mappings + from sage.rings.polynomial import term_order + term_order.singular_name_mapping['invlex'] = 'rp' + term_order.inv_singular_name_mapping['rp'] = 'invlex' ############################################################################# cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: diff --git a/src/sage/libs/singular/singular.pxd b/src/sage/libs/singular/singular.pxd index 05f32b68079..ca31d02456c 100644 --- a/src/sage/libs/singular/singular.pxd +++ b/src/sage/libs/singular/singular.pxd @@ -1,4 +1,4 @@ -from sage.libs.singular.decl cimport ring, poly, number, intvec +from sage.libs.singular.decl cimport ring, poly, number, intvec, bigintmat from sage.libs.singular.function cimport Resolution from sage.rings.rational cimport Rational @@ -29,6 +29,7 @@ cdef object si2sa_ZZmod(number *n, ring *_ring, object base) cdef object si2sa_NF(number *n, ring *_ring, object base) cdef object si2sa_intvec(intvec *v) +cdef object si2sa_bigintvec(bigintmat *v) # dispatches to all the above. cdef object si2sa(number *n, ring *_ring, object base) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index e256949298e..3074b859529 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1544,7 +1544,6 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring) noexcept: sage: P(3) 3 """ - nr2mModul = d.parent().characteristic() if _ring != currRing: rChangeCurrRing(_ring) cdef number *nn @@ -1699,6 +1698,25 @@ cdef object si2sa_intvec(intvec *v): l.append(v.get(r)) return tuple(l) +cdef object si2sa_bigintvec(bigintmat *v): + r""" + create a sage tuple from a singular vector of big integers + + INPUT: + + - ``v`` -- a (pointer to) singular bigintmat + + OUTPUT: + + a sage tuple + """ + cdef int r + cdef list l = list() + for r in range(v.length()): + n = v.get(r) + l.append(si2sa_QQ(n, &n, currRing)) + return tuple(l) + # ============== # Initialisation # ============== diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index bb09ed8885d..91b0997de7b 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -127,14 +127,19 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): 4-dimensional Lorentzian manifold M sage: K.atlas() [Chart (M, (t, r, th, ph))] + + The Kerr metric in Boyer-Lindquist coordinates (cf. :wikipedia:`Kerr_metric`):: + sage: K.metric().display() g = (2*m*r/(a^2*cos(th)^2 + r^2) - 1) dt⊗dt - + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph + - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph + (a^2*cos(th)^2 + r^2)/(a^2 - 2*m*r + r^2) dr⊗dr + (a^2*cos(th)^2 + r^2) dth⊗dth - + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt + - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt + (2*a^2*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) + a^2 + r^2)*sin(th)^2 dph⊗dph + The Schwarzschild spacetime with the mass parameter set to 1:: + sage: K. = manifolds.Kerr() sage: K 4-dimensional Lorentzian manifold M @@ -144,6 +149,9 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): sage: K.default_chart().coord_range() t: (-oo, +oo); r: (0, +oo); th: (0, pi); ph: [-pi, pi] (periodic) + + The Kerr spacetime in Kerr coordinates:: + sage: m, a = var('m, a') sage: K. = manifolds.Kerr(m, a, coordinates="Kerr") sage: K @@ -205,7 +213,7 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -(1-2*m*r/rho**2), \ rho**2/(r**2-2*m*r+a**2), rho**2, \ (r**2+a**2+2*m*r*a**2/rho**2*sin(th)**2)*sin(th)**2 - g[0, 3] = 2*m*r*a*sin(th)**2/rho**2 + g[0, 3] = -2*m*r*a*sin(th)**2/rho**2 return M raise NotImplementedError("coordinates system not implemented, see help" diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py index 1a45b219872..5a3dc99ae6c 100644 --- a/src/sage/manifolds/differentiable/affine_connection.py +++ b/src/sage/manifolds/differentiable/affine_connection.py @@ -149,10 +149,10 @@ class AffineConnection(SageObject): Unset components are initialized to zero:: - sage: nab[:] # list of coefficients relative to the manifold's default vector frame + sage: nab[:] # list of coefficients relative to the manifold's default vector frame [[[0, x^2, 0], [0, 0, 0], [0, 0, 0]], - [[0, 0, 0], [0, 0, 0], [0, 0, 0]], - [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]] + [[0, 0, 0], [0, 0, 0], [0, 0, 0]], + [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]] The treatment of connection coefficients in a given vector frame is similar to that of tensor components; see therefore the class @@ -2377,7 +2377,7 @@ def curvature_form(self, i, j, frame=None): 2-form curvature (1,1) of connection nabla w.r.t. Vector frame (M, (e_1,e_2,e_3)) on the 3-dimensional differentiable manifold M sage: nab.curvature_form(1,1,e).display(e) # long time (if above is skipped) - curvature (1,1) of connection nabla w.r.t. Vector frame + curvature (1,1) of connection nabla w.r.t. Vector frame (M, (e_1,e_2,e_3)) = (y^3*z^4 + 2*x*y*z + (x*y^4 - x*y)*z^2) e^1∧e^2 + (x^4*y*z^2 - x^2*y^2) e^1∧e^3 + (x^5*y*z^3 - x*z^2) e^2∧e^3 diff --git a/src/sage/manifolds/differentiable/bundle_connection.py b/src/sage/manifolds/differentiable/bundle_connection.py index a3353b820cb..8870b408d35 100644 --- a/src/sage/manifolds/differentiable/bundle_connection.py +++ b/src/sage/manifolds/differentiable/bundle_connection.py @@ -98,11 +98,11 @@ class BundleConnection(SageObject, Mutability): sage: nab[e, 1, 1][:] = [x+z, y-z, x*y*z] sage: nab.display() connection (1,1) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = (x + z) dx + (y - z) dy + x*y*z dz - connection (1,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz - connection (2,1) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz + (E|_M, (e_1,e_2)) = (x + z) dx + (y - z) dy + x*y*z dz + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz + connection (2,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz Notice, when we omit the frame, the default frame of the vector bundle is assumed (in this case ``e``):: @@ -189,14 +189,14 @@ class BundleConnection(SageObject, Mutability): sage: nab.display(frame=f) connection (1,1) of bundle connection nabla w.r.t. Local frame (E|_M, (f_1,f_2)) = ((x^3 + x)*z + 2*x)/(x^2 + 1) dx + y*z dy + z^2 dz - connection (1,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (f_1,f_2)) = -(x^3 + x)*z dx - (x^2 + 1)*y*z dy - + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_1,f_2)) = -(x^3 + x)*z dx - (x^2 + 1)*y*z dy - (x^2 + 1)*z^2 dz - connection (2,1) of bundle connection nabla w.r.t. Local frame - (E|_M, (f_1,f_2)) = (x*z - x)/(x^2 + 1) dx - + connection (2,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_1,f_2)) = (x*z - x)/(x^2 + 1) dx - (x^2 - y*z)/(x^2 + 1) dy - (x^3 - z^2)/(x^2 + 1) dz - connection (2,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (f_1,f_2)) = -x*z dx - y*z dy - z^2 dz + connection (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_1,f_2)) = -x*z dx - y*z dy - z^2 dz The new connection 1-forms obey the defining formula, too:: @@ -214,14 +214,14 @@ class BundleConnection(SageObject, Mutability): ....: print(Omega(i ,j, e).display()) curvature (1,1) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = -(x^3 - x*y)*z dx∧dy + (-x^4*z + x*z^2) dx∧dz + - (-x^3*y*z + x^2*z^2) dy∧dz - curvature (1,2) of bundle connection nabla w.r.t. Local frame + (-x^3*y*z + x^2*z^2) dy∧dz + curvature (1,2) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = -x dx∧dz - y dy∧dz - curvature (2,1) of bundle connection nabla w.r.t. Local frame + curvature (2,1) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = 2*x dx∧dy + 3*x^2 dx∧dz - curvature (2,2) of bundle connection nabla w.r.t. Local frame + curvature (2,2) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = (x^3 - x*y)*z dx∧dy + (x^4*z - x*z^2) dx∧dz + - (x^3*y*z - x^2*z^2) dy∧dz + (x^3*y*z - x^2*z^2) dy∧dz The derived forms certainly obey the structure equations, see :meth:`curvature_form` for details:: @@ -483,17 +483,17 @@ def _new_forms(self, frame): sage: forms = nab._new_forms(e) sage: [forms[k] for k in sorted(forms)] [1-form connection (1,1) of bundle connection nabla w.r.t. Local - frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable - manifold M, - 1-form connection (1,2) of bundle connection nabla w.r.t. Local - frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable - manifold M, - 1-form connection (2,1) of bundle connection nabla w.r.t. Local - frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable - manifold M, - 1-form connection (2,2) of bundle connection nabla w.r.t. Local - frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable - manifold M] + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (1,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M] """ dom = frame._domain @@ -1307,8 +1307,8 @@ def display(self, frame=None, vector_frame=None, chart=None, sage: nab.display() connection (1,1) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = x dx + y dy + z dz - connection (2,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = x^2 dx + y^2 dy + z^2 dz + connection (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + y^2 dy + z^2 dz sage: latex(nab.display()) \begin{array}{lcl} \omega^1_{\ \, 1} = x \mathrm{d} x + y \mathrm{d} y + z \mathrm{d} z \\ \omega^2_{\ \, 2} = x^{2} diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 8a64694b38b..1c2d71d23b4 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -132,9 +132,9 @@ class is sage: Phi.display() Phi: S^2 → R^3 on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), - (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1), - -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) + -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) It is possible to create the map via the method :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_map` @@ -162,7 +162,7 @@ class is sage: Phi1.display() Phi: S^2 → R^3 on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), - (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) The definition can be completed by means of the method :meth:`~sage.manifolds.continuous_map.ContinuousMap.add_expr`:: @@ -172,9 +172,9 @@ class is sage: Phi1.display() Phi: S^2 → R^3 on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), - (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1), - -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) + -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) At this stage, ``Phi1`` and ``Phi`` are fully equivalent:: @@ -226,6 +226,14 @@ class is Basis (∂/∂X,∂/∂Y,∂/∂Z) on the Tangent space at Point Phi(N) on the 3-dimensional differentiable manifold R^3 + A convenient way to display the matrix of the differential:: + + sage: Phi.differential(np).display() + ∂/∂u ∂/∂v + ∂/∂X⎛ 2 0⎞ + ∂/∂Y⎜ 0 2⎟ + ∂/∂Z⎝ 0 0⎠ + Differentiable maps can be composed by means of the operator ``*``: let us introduce the map `\RR^3\rightarrow \RR^2` corresponding to the projection from the point `(X,Y,Z)=(0,0,1)` onto the equatorial plane diff --git a/src/sage/manifolds/differentiable/differentiable_submanifold.py b/src/sage/manifolds/differentiable/differentiable_submanifold.py index 7387ea3a44e..aec0dce99e8 100644 --- a/src/sage/manifolds/differentiable/differentiable_submanifold.py +++ b/src/sage/manifolds/differentiable/differentiable_submanifold.py @@ -69,14 +69,14 @@ class DifferentiableSubmanifold(DifferentiableManifold, TopologicalSubmanifold): - ``field`` -- field `K` on which the sub manifold is defined; allowed values are - - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for - a manifold over `\RR` - - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``) - for a manifold over `\CC` - - an object in the category of topological fields (see - :class:`~sage.categories.fields.Fields` and - :class:`~sage.categories.topological_spaces.TopologicalSpaces`) - for other types of manifolds + - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for + a manifold over `\RR` + - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``) + for a manifold over `\CC` + - an object in the category of topological fields (see + :class:`~sage.categories.fields.Fields` and + :class:`~sage.categories.topological_spaces.TopologicalSpaces`) + for other types of manifolds - ``structure`` -- manifold structure (see :class:`~sage.manifolds.structure.TopologicalStructure` or @@ -136,7 +136,7 @@ class DifferentiableSubmanifold(DifferentiableManifold, TopologicalSubmanifold): sage: phi_inv_t = M.scalar_field({CM: z-x^2-y^2}) sage: phi_inv_t.display() M → ℝ - (x, y, z) ↦ -x^2 - y^2 + z + (x, y, z) ↦ -x^2 - y^2 + z `\phi` can then be declared as an embedding `N\to M`:: diff --git a/src/sage/manifolds/differentiable/multivectorfield.py b/src/sage/manifolds/differentiable/multivectorfield.py index 9a4b03fb369..8de30fdd711 100644 --- a/src/sage/manifolds/differentiable/multivectorfield.py +++ b/src/sage/manifolds/differentiable/multivectorfield.py @@ -1040,8 +1040,7 @@ def wedge(self, other): sage: b.display() b = y^2 ∂/∂x∧∂/∂y + (x + z) ∂/∂x∧∂/∂z + z^2 ∂/∂y∧∂/∂z sage: s = a.wedge(b); s - 3-vector field a∧b on the 3-dimensional differentiable - manifold M + 3-vector field a∧b on the 3-dimensional differentiable manifold M sage: s.display() a∧b = (-x^2 + (y^3 - x - 1)*z + 2*z^2 - x) ∂/∂x∧∂/∂y∧∂/∂z diff --git a/src/sage/manifolds/differentiable/tangent_space.py b/src/sage/manifolds/differentiable/tangent_space.py index 98a60cede2a..a938d77e237 100644 --- a/src/sage/manifolds/differentiable/tangent_space.py +++ b/src/sage/manifolds/differentiable/tangent_space.py @@ -177,28 +177,32 @@ class TangentSpace(FiniteRankFreeModule): [0 1] sage: W_Tp_xy = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_xy) sage: Tp.bases()[0] - Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the 2-dimensional differentiable manifold M - sage: phi_Tp_xy = Tp.isomorphism_with_fixed_basis(Tp.bases()[0], codomain=W_Tp_xy); phi_Tp_xy + Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the + 2-dimensional differentiable manifold M + sage: phi_Tp_xy = Tp.isomorphism_with_fixed_basis(Tp.bases()[0], codomain=W_Tp_xy) + sage: phi_Tp_xy Generic morphism: - From: Tangent space at Point p on the 2-dimensional differentiable manifold M - To: Ambient quadratic space of dimension 2 over Symbolic Ring - Inner product matrix: - [1 0] - [0 1] + From: Tangent space at Point p on the 2-dimensional differentiable manifold M + To: Ambient quadratic space of dimension 2 over Symbolic Ring + Inner product matrix: + [1 0] + [0 1] sage: Q_Tp_uv = g[c_uv.frame(),:](*p.coordinates(c_uv)); Q_Tp_uv [1/2 0] [ 0 1/2] sage: W_Tp_uv = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_uv) sage: Tp.bases()[1] - Basis (∂/∂u,∂/∂v) on the Tangent space at Point p on the 2-dimensional differentiable manifold M - sage: phi_Tp_uv = Tp.isomorphism_with_fixed_basis(Tp.bases()[1], codomain=W_Tp_uv); phi_Tp_uv + Basis (∂/∂u,∂/∂v) on the Tangent space at Point p on the + 2-dimensional differentiable manifold M + sage: phi_Tp_uv = Tp.isomorphism_with_fixed_basis(Tp.bases()[1], codomain=W_Tp_uv) + sage: phi_Tp_uv Generic morphism: - From: Tangent space at Point p on the 2-dimensional differentiable manifold M - To: Ambient quadratic space of dimension 2 over Symbolic Ring - Inner product matrix: - [1/2 0] - [ 0 1/2] + From: Tangent space at Point p on the 2-dimensional differentiable manifold M + To: Ambient quadratic space of dimension 2 over Symbolic Ring + Inner product matrix: + [1/2 0] + [ 0 1/2] sage: t1, t2 = Tp.tensor((1,0)), Tp.tensor((1,0)) sage: t1[:] = (8, 15) @@ -211,7 +215,8 @@ class TangentSpace(FiniteRankFreeModule): 541 sage: Tp_xy_to_uv = M.change_of_frame(c_xy.frame(), c_uv.frame()).at(p); Tp_xy_to_uv - Automorphism of the Tangent space at Point p on the 2-dimensional differentiable manifold M + Automorphism of the Tangent space at Point p on the + 2-dimensional differentiable manifold M sage: Tp.set_change_of_basis(Tp.bases()[0], Tp.bases()[1], Tp_xy_to_uv) sage: t1[Tp.bases()[1],:] [23, -7] diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index df54270dde0..d2135c1e5a9 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -4695,6 +4695,20 @@ def apply_map(self, fun, frame=None, chart=None, sage: v.display(X.frame(), X) (x + y)*(x - y) ∂/∂x + 2*pi*(pi - 1)*x ∂/∂y + TESTS: + + Check that the cached quantities derived from the components are + erased:: + + sage: w = M.vector_field(a*x, 0) + sage: diff(w[[0]]).display() + a dx + sage: w.apply_map(lambda t: t.subs(a=-2)) + sage: w.display() + -2*x ∂/∂x + sage: diff(w[[0]]).display() + -2 dx + """ # The dictionary of components w.r.t. frame: if keep_other_components: @@ -4712,3 +4726,4 @@ def apply_map(self, fun, frame=None, chart=None, for ch, fct in scalar._express.items(): cfunc_dict[ch] = ch.function(fun(fct.expr())) scalar._express = cfunc_dict + scalar._del_derived() diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index 18d1169064d..36a3c48c712 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -768,7 +768,7 @@ def set_change_of_frame(self, frame1, frame2, change_of_frame, :class:`~sage.tensor.modules.free_module_automorphism.FreeModuleAutomorphism` describing the automorphism `P` that relates the basis `(e_i)` to the basis `(f_i)` according to `f_i = P(e_i)` - - ``compute_inverse`` (default: True) -- if set to True, the inverse + - ``compute_inverse`` (default: ``True``) -- if set to ``True``, the inverse automorphism is computed and the change from basis `(f_i)` to `(e_i)` is set to it in the internal dictionary ``self._frame_changes`` @@ -1476,7 +1476,7 @@ def ambient_domain(self): sage: Phi.display() Phi: R → M t ↦ (x, y) = (cos(t), sin(t)) - sage: PhiT11 = R.tensor_bundle(1, 1, dest_map=Phi) + sage: PhiT11 = R.tensor_bundle(1, 1, dest_map=Phi) sage: PhiT11.ambient_domain() 2-dimensional differentiable manifold M @@ -1724,15 +1724,15 @@ def orientation(self): [] sage: T11.set_orientation([c_xy.frame(), c_uv.frame()]) sage: T11.orientation() - [Coordinate frame (U, (∂/∂x,∂/∂y)), Coordinate frame - (V, (∂/∂u,∂/∂v))] + [Coordinate frame (U, (∂/∂x,∂/∂y)), + Coordinate frame (V, (∂/∂u,∂/∂v))] If the destination map is the identity, the orientation is automatically set for the manifold, too:: sage: M.orientation() - [Coordinate frame (U, (∂/∂x,∂/∂y)), Coordinate frame - (V, (∂/∂u,∂/∂v))] + [Coordinate frame (U, (∂/∂x,∂/∂y)), + Coordinate frame (V, (∂/∂u,∂/∂v))] Conversely, if one sets an orientation on the manifold, the orientation on its tensor bundles is set accordingly:: @@ -1740,8 +1740,8 @@ def orientation(self): sage: c_tz. = U.chart() sage: M.set_orientation([c_tz, c_uv]) sage: T11.orientation() - [Coordinate frame (U, (∂/∂t,∂/∂z)), Coordinate frame - (V, (∂/∂u,∂/∂v))] + [Coordinate frame (U, (∂/∂t,∂/∂z)), + Coordinate frame (V, (∂/∂u,∂/∂v))] """ if self._dest_map.is_identity(): diff --git a/src/sage/matrix/args.pxd b/src/sage/matrix/args.pxd index 9ab004e1887..8deb4d434c9 100644 --- a/src/sage/matrix/args.pxd +++ b/src/sage/matrix/args.pxd @@ -47,6 +47,7 @@ cdef class MatrixArgs: cdef public Parent space # parent of matrix cdef public Parent base # parent of entries cdef public long nrows, ncols + cdef public object row_keys, column_keys cdef public object entries cdef entries_type typ cdef public bint sparse @@ -54,6 +55,7 @@ cdef class MatrixArgs: cdef bint is_finalized cpdef Matrix matrix(self, bint convert=?) + cpdef element(self, bint immutable=?) cpdef list list(self, bint convert=?) cpdef dict dict(self, bint convert=?) @@ -89,7 +91,11 @@ cdef class MatrixArgs: raise ArithmeticError("number of columns must be non-negative") cdef long p = self.ncols if p != -1 and p != n: - raise ValueError(f"inconsistent number of columns: should be {p} but got {n}") + raise ValueError(f"inconsistent number of columns: should be {p} " + f"but got {n}") + if self.column_keys is not None and n != len(self.column_keys): + raise ValueError(f"inconsistent number of columns: should be cardinality of {self.column_keys} " + f"but got {n}") self.ncols = n cdef inline int set_nrows(self, long n) except -1: @@ -102,8 +108,23 @@ cdef class MatrixArgs: cdef long p = self.nrows if p != -1 and p != n: raise ValueError(f"inconsistent number of rows: should be {p} but got {n}") + if self.row_keys is not None and n != len(self.row_keys): + raise ValueError(f"inconsistent number of rows: should be cardinality of {self.row_keys} " + f"but got {n}") self.nrows = n + cdef inline int _ensure_nrows_ncols(self) except -1: + r""" + Make sure that the number of rows and columns is set. + If ``row_keys`` or ``column_keys`` is not finite, this can raise an exception. + """ + if self.nrows == -1: + self.nrows = len(self.row_keys) + if self.ncols == -1: + self.ncols = len(self.column_keys) + + cpdef int set_column_keys(self, column_keys) except -1 + cpdef int set_row_keys(self, row_keys) except -1 cpdef int set_space(self, space) except -1 cdef int finalize(self) except -1 diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 5613a63dfd5..4f578440df6 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -312,7 +312,8 @@ cdef class MatrixArgs: self.sparse = -1 self.kwds = {} - def __init__(self, *args, base_ring=None, nrows=None, ncols=None, entries=None, sparse=None, space=None, **kwds): + def __init__(self, *args, base_ring=None, nrows=None, ncols=None, entries=None, + sparse=None, row_keys=None, column_keys=None, space=None, **kwds): """ Parse arguments for creating a new matrix. @@ -360,6 +361,10 @@ cdef class MatrixArgs: self.entries = entries if sparse is not None: self.sparse = sparse + if row_keys is not None: + self.set_row_keys(row_keys) + if column_keys is not None: + self.set_column_keys(column_keys) if space is not None: self.set_space(space) self.kwds.update(kwds) @@ -375,15 +380,21 @@ cdef class MatrixArgs: if self.entries is None and isinstance(arg, (list, tuple, dict)): self.entries = arg argc -= 1 - if argi == argc: return + if argi == argc: + return # check for base ring argument if self.base is None and isinstance(args[argi], Parent): self.base = args[argi] argi += 1 - if argi == argc: return - - # check nrows and ncols argument + if argi == argc: + return + + # check positional nrows and ncols argument + # even if redundant with row_keys, column_keys given as keywords; + # but do not check for positional row_keys, column_keys arguments + # -- we do not allow those, as they would be too easy to + # confuse with entries cdef int k cdef long v if self.nrows == -1 and self.ncols == -1: @@ -403,7 +414,8 @@ cdef class MatrixArgs: else: self.set_ncols(v) argi += 1 - if argi == argc: return + if argi == argc: + return # check for entries argument if self.entries is None: @@ -550,12 +562,14 @@ cdef class MatrixArgs: if sparse: pass else: + self._ensure_nrows_ncols() zero = self.base.zero() for i in range(self.nrows): for j in range(self.ncols): sig_check() yield zero elif self.typ == MA_ENTRIES_SCALAR: + self._ensure_nrows_ncols() diag = self.entries if convert and self.need_to_convert(diag): diag = self.base(diag) @@ -570,6 +584,7 @@ cdef class MatrixArgs: sig_check() yield diag if (i == j) else zero elif self.typ == MA_ENTRIES_SEQ_SEQ: + self._ensure_nrows_ncols() row_iter = sized_iter(self.entries, self.nrows) for i in range(self.nrows): row = sized_iter(next(row_iter), self.ncols) @@ -583,6 +598,7 @@ cdef class MatrixArgs: else: yield x elif self.typ == MA_ENTRIES_SEQ_FLAT: + self._ensure_nrows_ncols() it = sized_iter(self.entries, self.nrows * self.ncols) for i in range(self.nrows): for j in range(self.ncols): @@ -606,8 +622,14 @@ cdef class MatrixArgs: raise TypeError("dense iteration is not supported for sparse input") elif self.typ == MA_ENTRIES_CALLABLE: f = self.entries - for i in range(self.nrows): - for j in range(self.ncols): + row_keys = self.row_keys + if row_keys is None: + row_keys = range(self.nrows) + column_keys = self.column_keys + if column_keys is None: + column_keys = range(self.ncols) + for i in row_keys: + for j in column_keys: sig_check() x = f(i, j) if convert and self.need_to_convert(x): @@ -640,6 +662,7 @@ cdef class MatrixArgs: 42 """ self.finalize() + self._ensure_nrows_ncols() return self.nrows * self.ncols cpdef Matrix matrix(self, bint convert=True): @@ -726,13 +749,67 @@ cdef class MatrixArgs: M = M.__copy__() break else: - M = self.space(self, coerce=convert) + space = self.space + if not isinstance(space, MatrixSpace): + space = space.zero().matrix(side='left').parent() + M = space(self, coerce=convert) # Also store the matrix to support multiple calls of matrix() self.entries = M self.typ = MA_ENTRIES_MATRIX return M + cpdef element(self, bint immutable=False): + r""" + Return the matrix or morphism. + + INPUT: + + - ``immutable`` -- boolean; if ``True``, the result will be immutable. + + OUTPUT: an element of ``self.space`` + + .. NOTE:: + + This may change ``self.entries``, making it unsafe to access the + ``self.entries`` attribute after calling this method. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: M = matrix(2, 3, range(6), sparse=True) + sage: ma = MatrixArgs(M); ma.finalized() + + sage: M2 = ma.element(immutable=True); M2.parent() + Full MatrixSpace of 2 by 3 sparse matrices over Integer Ring + sage: M2.is_immutable() + True + + sage: ma = MatrixArgs(M, row_keys=['u','v'], column_keys=['a','b','c']) + sage: ma.finalized() + + sage: phi = ma.element(); phi + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'u', 'v'} over Integer Ring + """ + self.finalize() + cdef Matrix M = self.matrix(convert=True) + if immutable: + M.set_immutable() + if isinstance(self.space, MatrixSpace): + return M + return self.space(matrix=M, side='left') + cpdef list list(self, bint convert=True): """ Return the entries of the matrix as a flat list of scalars. @@ -835,13 +912,86 @@ cdef class MatrixArgs: D[se.i, se.j] = x return D + cpdef int set_column_keys(self, column_keys) except -1: + """ + Set the column keys with consistency checking. + + If the value was previously set, it must remain the same. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: ma = MatrixArgs(2, 4) + sage: ma.set_column_keys('xyz') + Traceback (most recent call last): + ... + ValueError: inconsistent column keys: should be of cardinality 4 but got xyz + sage: ma.set_column_keys('abcd') + 0 + sage: ma.finalized() + + """ + if self.column_keys is not None and self.column_keys != column_keys: + raise ValueError(f"inconsistent column keys: should be {self.column_keys} " + f"but got {column_keys}") + cdef long p = self.ncols + if p != -1 and p != len(column_keys): + raise ValueError(f"inconsistent column keys: should be of cardinality {self.ncols} " + f"but got {column_keys}") + self.column_keys = column_keys + + cpdef int set_row_keys(self, row_keys) except -1: + """ + Set the row keys with consistency checking. + + If the value was previously set, it must remain the same. + + EXAMPLES:: + + sage: from sage.matrix.args import MatrixArgs + sage: ma = MatrixArgs(2, 4) + sage: ma.set_row_keys('xyz') + Traceback (most recent call last): + ... + ValueError: inconsistent row keys: should be of cardinality 2 but got xyz + sage: ma.set_row_keys(['u', 'v']) + 0 + sage: ma.finalized() + + """ + if self.row_keys is not None and self.row_keys != row_keys: + raise ValueError(f"inconsistent row keys: should be {self.row_keys} " + f"but got {row_keys}") + if self.nrows != -1 and self.nrows != len(row_keys): + raise ValueError(f"inconsistent row keys: should be of cardinality {self.nrows} " + f"but got {row_keys}") + self.row_keys = row_keys + cpdef int set_space(self, space) except -1: """ Set inputs from a given matrix space. INPUT: - - ``space`` -- a :class:`MatrixSpace` + - ``space`` -- a :class:`MatrixSpace` or a homset of modules with basis EXAMPLES:: @@ -858,12 +1008,37 @@ cdef class MatrixArgs: [0 0] sage: M.parent() is S True + + From a homset:: + + sage: C = CombinatorialFreeModule(ZZ, ['a', 'b', 'c']) + sage: R = CombinatorialFreeModule(ZZ, ['u', 'v']) + sage: S = Hom(C, R); S + Set of Morphisms + from Free module generated by {'a', 'b', 'c'} over Integer Ring + to Free module generated by {'u', 'v'} over Integer Ring + in Category of finite dimensional modules with basis over Integer Ring + sage: ma = MatrixArgs() + sage: _ = ma.set_space(S) + sage: ma.finalized() + """ + if self.space is not None: + return 0 # TODO: ?????? self.space = space - self.set_nrows(space.nrows()) - self.set_ncols(space.ncols()) - self.base = space._base - self.sparse = space.is_sparse() + try: + self.set_nrows(space.nrows()) + self.set_ncols(space.ncols()) + self.base = space._base + self.sparse = space.is_sparse() + except AttributeError: + self.set_row_keys(space.codomain().basis().keys()) + self.set_column_keys(space.domain().basis().keys()) + self.base = space.base_ring() def finalized(self): """ @@ -945,6 +1120,7 @@ cdef class MatrixArgs: # Can we assume a square matrix? if self.typ & MA_FLAG_ASSUME_SQUARE: + # TODO: Handle column_keys/row_keys if self.ncols == -1: if self.nrows != -1: self.ncols = self.nrows @@ -977,7 +1153,7 @@ cdef class MatrixArgs: # Error if size is required if self.typ & MA_FLAG_DIM_REQUIRED: - if self.nrows == -1 or self.ncols == -1: + if (self.nrows == -1 and self.row_keys is None) or (self.ncols == -1 and self.column_keys is None): raise TypeError("the dimensions of the matrix must be specified") # Determine base in easy cases @@ -993,7 +1169,9 @@ cdef class MatrixArgs: if self.base is None: raise TypeError(f"unable to determine base of {self.entries!r}") - if self.nrows == -1 or self.ncols == -1 or self.base is None: + if ((self.nrows == -1 and self.row_keys is None) + or (self.ncols == -1 and self.column_keys is None) + or self.base is None): # Determine dimensions or base in the cases where we # really need to look at the entries. if self.typ == MA_ENTRIES_SEQ_SEQ: @@ -1013,9 +1191,9 @@ cdef class MatrixArgs: self.typ = MA_ENTRIES_ZERO except Exception: # "not self.entries" has failed, self.entries cannot be determined to be zero - if self.nrows != self.ncols: + if self.nrows != self.ncols or self.row_keys != self.column_keys: raise TypeError("scalar matrix must be square if the value cannot be determined to be zero") - if self.typ == MA_ENTRIES_SCALAR and self.nrows != self.ncols: + if self.typ == MA_ENTRIES_SCALAR and (self.nrows != self.ncols or self.row_keys != self.column_keys): # self.typ is still SCALAR -> "not self.entries" has successfully evaluated, to False raise TypeError("nonzero scalar matrix must be square") @@ -1026,8 +1204,17 @@ cdef class MatrixArgs: global MatrixSpace if MatrixSpace is None: from sage.matrix.matrix_space import MatrixSpace - self.space = MatrixSpace(self.base, self.nrows, self.ncols, - sparse=self.sparse, **self.kwds) + nrows = self.nrows + if nrows == -1: + nrows = None + ncols = self.ncols + if ncols == -1: + ncols = None + self.space = MatrixSpace(self.base, nrows, ncols, + sparse=self.sparse, + row_keys=self.row_keys, + column_keys=self.column_keys, + **self.kwds) self.is_finalized = True @@ -1197,10 +1384,11 @@ cdef class MatrixArgs: e = PySequence_Fast(self.entries, "not a sequence") self.set_nrows(len(e)) if self.nrows == 0: - if self.ncols == -1: self.ncols = 0 + if self.ncols == -1 and self.column_keys is None: + self.set_ncols(0) self.setdefault_base(ZZ) return 0 - elif self.ncols != -1 and self.base is not None: + elif (self.ncols != -1 or self.column_keys is not None) and self.base is not None: # Everything known => OK return 0 @@ -1246,14 +1434,20 @@ cdef class MatrixArgs: self.nrows = 0 if self.ncols == -1: self.ncols = 0 - elif self.ncols == -1: + elif self.ncols == -1 and self.column_keys is None: if self.nrows == -1: - # Assume row matrix - self.nrows = 1 - self.ncols = N + if self.row_keys is None: + # Assume row matrix + self.nrows = 1 + self.ncols = N + else: + self.nrows = len(self.row_keys) + self.ncols = N // self.nrows else: self.ncols = N // self.nrows - elif self.nrows == -1: + elif self.nrows == -1 and self.row_keys is None: + if self.ncols == -1: + self.ncols = len(self.column_keys) self.nrows = N // self.ncols self.set_seq_flat(entries) diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 6e2e4f7dda8..f343fd82578 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -62,14 +62,17 @@ def matrix(*args, **kwds): determine this from the given entries, falling back to ``ZZ`` if no entries are given. - - ``nrows`` -- the number of rows in the matrix. + - ``nrows`` -- the number of rows in the matrix, or a finite or + enumerated family of arbitrary objects that index the rows of the matrix - - ``ncols`` -- the number of columns in the matrix. + - ``ncols`` -- the number of columns in the matrix, or a finite or + enumerated family of arbitrary objects that index the columns of the matrix - ``entries`` -- see examples below. - If either ``nrows`` or ``ncols`` is given as keyword argument, then - no positional arguments ``nrows`` and ``ncols`` may be given. + If any of ``nrows``, ``ncols``, ``row_keys``, ``column_keys`` is + given as keyword argument, then none of these may be given as + positional arguments. Keyword-only arguments: @@ -77,17 +80,22 @@ def matrix(*args, **kwds): ``True`` when the entries are given as a dictionary, otherwise defaults to ``False``. + - ``row_keys`` -- a finite or enumerated family of arbitrary objects + that index the rows of the matrix + + - ``column_keys`` -- a finite or enumerated family of arbitrary objects + that index the columns of the matrix + - ``space`` -- matrix space which will be the parent of the output - matrix. This determines ``base_ring``, ``nrows``, ``ncols`` and - ``sparse``. + matrix. This determines ``base_ring``, ``nrows``, ``row_keys``, + ``ncols``, ``column_keys``, and ``sparse``. - ``immutable`` -- (boolean) make the matrix immutable. By default, the output matrix is mutable. - OUTPUT: - - a matrix + OUTPUT: a matrix or, more generally, a homomorphism between free + modules EXAMPLES:: @@ -241,6 +249,26 @@ def matrix(*args, **kwds): ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Using ``row_keys`` and ``column_keys``:: + + sage: M = matrix([[1,2,3], [4,5,6]], + ....: column_keys=['a','b','c'], row_keys=['u','v']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'u', 'v'} over Integer Ring + sage: print(M._unicode_art_matrix()) + a b c + u⎛1 2 3⎞ + v⎝4 5 6⎠ + + It is allowed to specify dimensions redundantly:: + + sage: M = matrix(2, 3, [[1,2,3], [4,5,6]], + ....: column_keys=['a','b','c'], row_keys=['u','v']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'u', 'v'} over Integer Ring + TESTS: There are many ways to create an empty matrix:: @@ -645,10 +673,7 @@ def matrix(*args, **kwds): :class:`MatrixArgs`, see :issue:`24742` """ immutable = kwds.pop('immutable', False) - M = MatrixArgs(*args, **kwds).matrix() - if immutable: - M.set_immutable() - return M + return MatrixArgs(*args, **kwds).element(immutable=immutable) Matrix = matrix diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 3dc47b9df07..c1e538a441e 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1958,7 +1958,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: K = (A - e).kernel() sage: P = K.basis_matrix() sage: P.str() - '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]' + '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]' Use single-row delimiters where appropriate:: diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 9a7fdd393c5..e36d913bcd9 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10453,23 +10453,23 @@ cdef class Matrix(Matrix1): ....: [-1, 1, -6, -6, 5]]) sage: Q, R = A.QR() sage: Q - [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?] - [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?] - [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?] - [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?] + [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?] + [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?] + [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?] + [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?] [ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?] sage: R [ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?] [ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?] - [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?] - [ 0 0 0 1.027626039419836? -3.619300149686620?] - [ 0 0 0 0 0.024551430807012?] + [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?] + [ 0 0 0 1.027626039419836? -3.61930014968662?] + [ 0 0 0 0 0.02455143080702?] sage: Q.conjugate_transpose()*Q - [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13] - [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13] - [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13] - [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13] - [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?] + [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12] + [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12] + [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12] + [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12] + [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?] sage: Q * R == A True @@ -10485,24 +10485,24 @@ cdef class Matrix(Matrix1): sage: Q, R = A.QR() sage: Q [ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I] - [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I] - [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I] + [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I] + [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I] [ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I] sage: R [ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I] - [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] + [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] [ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I] [ 0 0 0 1.942963944258992? + 0.?e-16*I] sage: Q.conjugate_transpose()*Q [1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] [ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I] + [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I] sage: Q*R - A [ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I] [0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I] A rank-deficient rectangular matrix, with both values of the ``full`` keyword. :: @@ -12192,9 +12192,9 @@ cdef class Matrix(Matrix1): sage: # needs sage.combinat sage.libs.pari sage: _, T = A.is_similar(B, transformation=True) sage: T - [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I] - [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I] - [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I] + [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I] + [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I] + [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I] sage: T.change_ring(QQ) [ 1 0 0] [-2/3 1/6 -5/6] @@ -18431,12 +18431,13 @@ def _generic_clear_column(m): I = ideal_or_fractional(R, a[0, 0]) # need to make sure we change this when a[0,0] changes for k in range(1, a.nrows()): if a[k, 0] not in I: + new_ideal = ideal_or_fractional(R, a[0, 0], a[k, 0]) try: - v = ideal_or_fractional(R, a[0, 0], a[k, 0]).gens_reduced() + v = new_ideal.gens_reduced() except Exception as msg: raise ArithmeticError("%s\nCan't create ideal on %s and %s" % (msg, a[0, 0], a[k, 0])) if len(v) > 1: - raise ArithmeticError("Ideal %s not principal" % ideal_or_fractional(R, a[0, 0], a[k, 0])) + raise ArithmeticError("Ideal %s not principal" % new_ideal) B = v[0] # now we find c,d, using the fact that c * (a_{0,0}/B) - d * diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index f7efb5432c8..5d0db5fb742 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -58,6 +58,7 @@ from sage.features import PythonModule lazy_import('sage.matrix.matrix_gfpn_dense', ['Matrix_gfpn_dense'], feature=PythonModule('sage.matrix.matrix_gfpn_dense', spkg='meataxe')) +lazy_import('sage.groups.matrix_gps.matrix_group', ['MatrixGroup_base']) _Rings = Rings() _Fields = Fields() @@ -468,7 +469,7 @@ class MatrixSpace(UniqueRepresentation, Parent): - ``'numpy'`` -- for real and complex floating point numbers - OUTPUT: a matrix space or, more generally, a homspace between free modules. + OUTPUT: a matrix space or, more generally, a homspace between free modules This factory function creates instances of various specialized classes depending on the input. Not all combinations of options are @@ -493,10 +494,12 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: MatrixSpace(ZZ, 10, 5).category() Category of infinite enumerated finite dimensional modules with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ, 10, 10).category() Category of infinite enumerated finite dimensional algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ, 10).category() Category of infinite finite dimensional algebras with basis over @@ -554,10 +557,12 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: MatrixSpace(ZZ, 10, 5).category() Category of infinite enumerated finite dimensional modules with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ, 10, 10).category() Category of infinite enumerated finite dimensional algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ, 10).category() Category of infinite finite dimensional algebras with basis over @@ -777,6 +782,61 @@ def __classcall__(cls, base_ring, def __init__(self, base_ring, nrows, ncols, sparse, implementation): r""" + INPUT: + + - ``base_ring`` + + - ``nrows`` -- (positive integer) the number of rows + + - ``ncols`` -- (positive integer, default nrows) the number of + columns + + - ``sparse`` -- (boolean, default ``False``) whether or not matrices + are given a sparse representation + + - ``implementation`` -- (optional, a string or a matrix class) a possible + implementation. Depending on the base ring the string can be + + - ``'generic'`` -- on any base rings + + - ``'flint'`` -- for integers and rationals + + - ``'meataxe'`` -- finite fields, needs to install the optional package meataxe + + - ``m4ri`` -- for characteristic 2 using M4RI library + + - ``linbox-float`` -- for integer mod rings up to `2^8 = 256` + + - ``linbox-double`` -- for integer mod rings up to + `floor(2^26*sqrt(2) + 1/2) = 94906266` + + - ``numpy`` -- for real and complex floating point numbers + + EXAMPLES:: + + sage: MatrixSpace(QQ, 2) + Full MatrixSpace of 2 by 2 dense matrices over Rational Field + sage: MatrixSpace(ZZ, 3, 2) + Full MatrixSpace of 3 by 2 dense matrices over Integer Ring + sage: MatrixSpace(ZZ, 3, sparse=False) + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + + sage: MatrixSpace(ZZ,10,5) + Full MatrixSpace of 10 by 5 dense matrices over Integer Ring + sage: MatrixSpace(ZZ,10,5).category() + Category of infinite enumerated finite dimensional modules with basis over + (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(ZZ,10,10).category() + Category of infinite enumerated finite dimensional algebras with basis over + (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(QQ,10).category() + Category of infinite finite dimensional algebras with basis over + (number fields and quotient fields and metric spaces) + TESTS: We test that in the real or complex double dense case, @@ -1392,14 +1452,8 @@ def _coerce_map_from_(self, S): pass else: MS = meth_matrix_space() - - try: - from sage.groups.matrix_gps.matrix_group import is_MatrixGroup - except ImportError: - pass - else: - if is_MatrixGroup(S): - return self.has_coerce_map_from(MS) + if isinstance(S, MatrixGroup_base): + return self.has_coerce_map_from(MS) try: from sage.modular.arithgroup.arithgroup_generic import is_ArithmeticSubgroup diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index 30038d1d3c0..7e6d565a868 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -1923,7 +1923,7 @@ cdef class BasisExchangeMatroid(Matroid): sage: M = matroids.catalog.N1() sage: M._characteristic_setsystem() - Iterator over a system of subsets + SetSystem of 23 sets over 10 elements sage: len(M._characteristic_setsystem()) 23 """ diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 6beaf02d379..aec280a35f7 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -481,7 +481,7 @@ cdef class CircuitsMatroid(Matroid): sage: from sage.matroids.circuits_matroid import CircuitsMatroid sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) sage: M.circuits() - Iterator over a system of subsets + SetSystem of 4 sets over 4 elements sage: list(M.circuits(0)) [] sage: sorted(M.circuits(3), key=str) @@ -544,7 +544,7 @@ cdef class CircuitsMatroid(Matroid): sage: from sage.matroids.circuits_matroid import CircuitsMatroid sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) sage: M.nonspanning_circuits() - Iterator over a system of subsets + SetSystem of 0 sets over 4 elements """ cdef list NSC = [] for i in self._k_C: diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index cecfead568a..07e8597fdb8 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -188,6 +188,8 @@ def Matroid(groundset=None, data=None, **kwds): ``reduced_matrix = A`` then the matroid is represented by `[I\ \ A]` where `I` is an appropriately sized identity matrix. + - ``morphism`` -- A morphism representation of the matroid. + - ``reduced_morphism`` -- A reduced morphism representation of the matroid. - ``rank_function`` -- A function that computes the rank of each subset. Can only be provided together with a groundset. - ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C`` @@ -533,6 +535,48 @@ def Matroid(groundset=None, data=None, **kwds): sage: M.base_ring() Integer Ring + A morphism representation of a :class:`LinearMatroid` can also be used as + input:: + + sage: M = matroids.catalog.Fano() + sage: A = M.representation(order=True); A + Generic morphism: + From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'} over + Finite Field of size 2 + To: Free module generated by {0, 1, 2} over Finite Field of size 2 + sage: A._unicode_art_matrix() + a b c d e f g + 0⎛1 0 0 0 1 1 1⎞ + 1⎜0 1 0 1 0 1 1⎟ + 2⎝0 0 1 1 1 0 1⎠ + sage: N = Matroid(A); N + Binary matroid of rank 3 on 7 elements, type (3, 0) + sage: N.groundset() + frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'}) + sage: M == N + True + + The keywords ``morphism`` and ``reduced_morphism`` are also available:: + + sage: M = matroids.catalog.RelaxedNonFano("abcdefg") + sage: A = M.representation(order=True, reduced=True); A + Generic morphism: + From: Free module generated by {'d', 'e', 'f', 'g'} over + Finite Field in w of size 2^2 + To: Free module generated by {'a', 'b', 'c'} over + Finite Field in w of size 2^2 + sage: A._unicode_art_matrix() + d e f g + a⎛1 1 0 1⎞ + b⎜1 0 1 1⎟ + c⎝0 1 w 1⎠ + sage: N = Matroid(reduced_morphism=A); N + Quaternary matroid of rank 3 on 7 elements + sage: N.groundset() + frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'}) + sage: M == N + True + #. Rank function: Any function mapping subsets to integers can be used as input:: @@ -713,8 +757,8 @@ def Matroid(groundset=None, data=None, **kwds): if data is None: for k in ['bases', 'independent_sets', 'circuits', 'nonspanning_circuits', 'flats', 'graph', 'matrix', - 'reduced_matrix', 'rank_function', 'revlex', - 'circuit_closures', 'matroid']: + 'reduced_matrix', 'morphism', 'reduced_morphism', + 'rank_function', 'revlex', 'circuit_closures', 'matroid']: if k in kwds: data = kwds.pop(k) key = k @@ -732,8 +776,13 @@ def Matroid(groundset=None, data=None, **kwds): Graph = () if isinstance(data, Graph): key = 'graph' - elif is_Matrix(data): + elif is_Matrix(data) or ( + isinstance(data, tuple) and is_Matrix(data[0])): key = 'matrix' + elif isinstance(data, sage.modules.with_basis.morphism.ModuleMorphism) or ( + isinstance(data, tuple) and + isinstance(data[0], sage.modules.with_basis.morphism.ModuleMorphism)): + key = 'morphism' elif isinstance(data, sage.matroids.matroid.Matroid): key = 'matroid' elif isinstance(data, str): @@ -856,9 +905,22 @@ def Matroid(groundset=None, data=None, **kwds): M = GraphicMatroid(G, groundset=groundset) # Matrices: - elif key in ['matrix', 'reduced_matrix']: + elif key in ['matrix', 'reduced_matrix', 'morphism', 'reduced_morphism']: A = data - is_reduced = (key == 'reduced_matrix') + is_reduced = (key == 'reduced_matrix' or key == 'reduced_morphism') + if isinstance(data, tuple): + A = data[0] + if key == 'matrix' or key == 'reduced_matrix': + if groundset is None: + groundset = data[1] + if is_reduced: + groundset += data[2] + if key == 'morphism' or key == 'reduced_morphism': + if groundset is None: + groundset = list(A.domain().basis().keys()) + if is_reduced: + groundset = list(A.codomain().basis().keys()) + groundset + A = A.matrix() # Fix the representation if not is_Matrix(A): diff --git a/src/sage/matroids/graphic_matroid.pxd b/src/sage/matroids/graphic_matroid.pxd new file mode 100644 index 00000000000..ced452ad963 --- /dev/null +++ b/src/sage/matroids/graphic_matroid.pxd @@ -0,0 +1,35 @@ +from .matroid cimport Matroid +from sage.graphs.generic_graph_pyx cimport GenericGraph_pyx + +cdef class GraphicMatroid(Matroid): + cdef frozenset _groundset + cdef readonly GenericGraph_pyx _G + cdef dict _vertex_map + cdef dict _groundset_edge_map + cpdef groundset(self) + cpdef _rank(self, X) + cpdef _vertex_stars(self) + cpdef _minor(self, contractions, deletions) + cpdef _has_minor(self, N, bint certificate=*) + cpdef _corank(self, X) + cpdef _is_circuit(self, X) + cpdef _closure(self, X) + cpdef _max_independent(self, X) + cpdef _max_coindependent(self, X) + cpdef _circuit(self, X) + cpdef _coclosure(self, X) + cpdef _is_closed(self, X) + cpdef _is_isomorphic(self, other, certificate=*) + cpdef _isomorphism(self, other) + cpdef is_valid(self) + cpdef graph(self) + cpdef vertex_map(self) + cpdef groundset_to_edges(self, X) + cpdef _groundset_to_edges(self, X) + cpdef subgraph_from_set(self, X) + cpdef _subgraph_from_set(self, X) + cpdef graphic_extension(self, u, v=*, element=*) + cpdef graphic_coextension(self, u, v=*, X=*, element=*) + cpdef twist(self, X) + cpdef one_sum(self, X, u, v) + cpdef regular_matroid(self) diff --git a/src/sage/matroids/graphic_matroid.py b/src/sage/matroids/graphic_matroid.pyx similarity index 87% rename from src/sage/matroids/graphic_matroid.py rename to src/sage/matroids/graphic_matroid.pyx index 76447037974..295034259a4 100644 --- a/src/sage/matroids/graphic_matroid.py +++ b/src/sage/matroids/graphic_matroid.pyx @@ -22,9 +22,9 @@ Graphic matroids do not have a representation matrix or any of the functionality of regular matroids. It is possible to get an instance of the -:class:`~sage.matroids.linear_matroid.RegularMatroid` class -by using the ``regular`` keyword when constructing the matroid. -It is also possible to cast a GraphicMatroid as a RegularMatroid with the +:class:`~sage.matroids.linear_matroid.RegularMatroid` class by using the +``regular`` keyword when constructing the matroid. It is also possible to cast +a class:`GraphicMatroid` as a class:`RegularMatroid` with the :meth:`~sage.matroids.graphic_matroids.GraphicMatroid.regular_matroid` method:: @@ -74,10 +74,8 @@ AUTHORS: - Zachary Gershkoff (2017-07-07): initial version - -Methods -======= """ + # **************************************************************************** # Copyright (C) 2017 Zachary Gershkoff # @@ -87,32 +85,29 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from .matroid import Matroid +from .matroid cimport Matroid from copy import copy, deepcopy from .utilities import newlabel, split_vertex, sanitize_contractions_deletions from itertools import combinations from sage.rings.integer import Integer +from sage.sets.disjoint_set cimport DisjointSet_of_hashables - -class GraphicMatroid(Matroid): +cdef class GraphicMatroid(Matroid): r""" The graphic matroid class. INPUT: - - ``G`` -- a Graph - - ``groundset`` -- (optional) a list in 1-1 correspondence with - ``G.edge_iterator()`` + - ``G`` -- class:`Graph` + - ``groundset`` -- list (optional); in 1-1 correspondence with ``G.edge_iterator()`` - OUTPUT: a ``GraphicMatroid`` instance where the groundset elements are the - edges of ``G`` + OUTPUT: class:`GraphicMatroid` where the groundset elements are the edges of `G` .. NOTE:: - If a disconnected graph is given as input, the instance of - ``GraphicMatroid`` will connect the graph components and store - this as its graph. + If a disconnected graph is given as input, the instance of class:`GraphicMatroid` + will connect the graph components and store this as its graph. EXAMPLES:: @@ -153,7 +148,7 @@ class GraphicMatroid(Matroid): True """ - # Necessary: + # necessary (__init__, groundset, _rank) def __init__(self, G, groundset=None): """ @@ -164,8 +159,7 @@ def __init__(self, G, groundset=None): sage: from sage.matroids.advanced import * sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph() sage: G = G1.disjoint_union(G2) - sage: M = GraphicMatroid(G) - sage: M + sage: M = GraphicMatroid(G); M Graphic matroid of rank 5 on 8 elements sage: M.graph() Looped multi-graph on 6 vertices @@ -217,16 +211,16 @@ def __init__(self, G, groundset=None): self._vertex_map[e[1]], groundset[i])) # If the matroid is empty, have the internal graph be a single vertex if edge_list: - self._G = Graph(edge_list, loops=True, multiedges=True, weighted=True, - data_structure='static_sparse') + self._G = Graph(edge_list, loops=True, multiedges=True, + weighted=True, data_structure='static_sparse') else: - self._G = Graph(1, loops=True, multiedges=True, weighted=True, - data_structure='static_sparse') + self._G = Graph(1, loops=True, multiedges=True, + weighted=True, data_structure='static_sparse') # Map groundset elements to graph edges: # The edge labels should already be the elements. self._groundset_edge_map = ({l: (u, v) for (u, v, l) in self._G.edge_iterator()}) - def groundset(self): + cpdef groundset(self): """ Return the groundset of the matroid as a frozenset. @@ -244,12 +238,12 @@ def groundset(self): """ return self._groundset - def _rank(self, X): + cpdef _rank(self, X): """ Return the rank of a set ``X``. - This method does no checking on ``X``, and - ``X`` may be assumed to have the same interface as ``frozenset``. + This method does no checking on ``X``, and ``X`` may be assumed to have + the same interface as ``frozenset``. INPUT: @@ -277,18 +271,17 @@ def _rank(self, X): sage: M.rank([0,3]) 1 """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]).union( - [v for (u, v, l) in edges]) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set([u for (u, v, l) in edges]).union( + [v for (u, v, l) in edges]) # This counts components: - DS_vertices = DisjointSet(vertices) + DS_vertices = DisjointSet_of_hashables(vertices) for (u, v, l) in edges: DS_vertices.union(u, v) return (len(vertices) - DS_vertices.number_of_subsets()) - # Representation: + # representation: def _repr_(self): """ @@ -296,21 +289,19 @@ def _repr_(self): EXAMPLES:: - sage: M = Matroid(graphs.CompleteGraph(5)) - sage: M + sage: M = Matroid(graphs.CompleteGraph(5)); M Graphic matroid of rank 4 on 10 elements sage: G = Graph([(0, 0), (0, 1), (0, 2), (1, 1), (2, 2)], loops=True) - sage: M = Matroid(G) - sage: M + sage: M = Matroid(G); M Graphic matroid of rank 2 on 5 elements """ - self._mrank = str(self._rank(self._groundset)) - self._elts = str(len(self._groundset)) - return f'Graphic matroid of rank {self._mrank} on {self._elts} elements' + r = self._rank(self._groundset) + n = len(self._groundset) + return f'Graphic matroid of rank {r} on {n} elements' - # Comparison: + # comparison: - def _vertex_stars(self): + cpdef _vertex_stars(self): """ Compute the set of edge labels around each vertex. @@ -380,7 +371,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid OUTPUT: ``True`` if ``self`` and ``other`` have the same graph; ``False`` otherwise @@ -428,7 +419,7 @@ def __ne__(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid OUTPUT: ``False`` if ``self`` and ``other`` have the same graph; ``True`` otherwise @@ -447,7 +438,7 @@ def __ne__(self, other): """ return (not self == other) - # Copying, loading, saving: + # copying, loading, saving def __reduce__(self): """ @@ -466,30 +457,28 @@ def __reduce__(self): version = 0 return unpickle_graphic_matroid, (version, data) - # Overrides: + # overrides - def _minor(self, contractions=frozenset([]), deletions=frozenset([])): + cpdef _minor(self, contractions, deletions): """ Return a minor. INPUT: - - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be - contracted - - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be - deleted + - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be contracted + - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be deleted Assumptions: contractions are independent, deletions are coindependent, contractions and deletions are disjoint. - OUTPUT: an instance of ``GraphicMatroid`` + OUTPUT: class:`GraphicMatroid` EXAMPLES:: sage: M = matroids.CompleteGraphic(5) - sage: M._minor(deletions=frozenset([0,1,2])) + sage: M._minor(deletions=frozenset([0,1,2]), contractions=frozenset([])) Graphic matroid of rank 4 on 7 elements - sage: M._minor(contractions=frozenset([0,1,2])) + sage: M._minor(deletions=frozenset([]), contractions=frozenset([0,1,2])) Graphic matroid of rank 1 on 7 elements sage: M = Matroid(range(15), graphs.PetersenGraph()) sage: N = M._minor(deletions=frozenset([0, 3, 5, 9]), @@ -497,22 +486,21 @@ def _minor(self, contractions=frozenset([]), deletions=frozenset([])): sage: N Graphic matroid of rank 6 on 8 elements """ - g = self.graph() - cont_edges = self._groundset_to_edges(contractions) - del_edges = self._groundset_to_edges(deletions) + cdef GenericGraph_pyx g = self.graph() + cdef list cont_edges = self._groundset_to_edges(contractions) + cdef list del_edges = self._groundset_to_edges(deletions) # deletions first so contractions don't mess up the vertices g.delete_edges(del_edges) g.contract_edges(cont_edges) - return GraphicMatroid(g) - def _has_minor(self, N, certificate=False): + cpdef _has_minor(self, N, bint certificate=False): """ - Check if the matroid has a minor isomorphic to M(H). + Check if the matroid has a minor isomorphic to `M(H)`. INPUT: - - ``N`` - a matroid + - ``N`` - matroid - ``certificate`` - (default: ``False``) if ``True``, returns the certificate isomorphism from the minor of ``self`` to ``N`` @@ -558,8 +546,8 @@ def _has_minor(self, N, certificate=False): sage: N.has_minor(M, certificate=True) (False, None) - If the matroids are not 3-connected, then the default matroid algorithms - are used:: + If the matroids are not 3-connected, then the default matroid + algorithms are used:: sage: M = matroids.CompleteGraphic(6) sage: N = Matroid(graphs.CycleGraph(4)) @@ -637,7 +625,7 @@ def _has_minor(self, N, certificate=False): N = N.regular_matroid() return M._has_minor(N, certificate=certificate) - def _corank(self, X): + cpdef _corank(self, X): """ Return the corank of the set `X` in the matroid. @@ -645,7 +633,7 @@ def _corank(self, X): INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: integer @@ -657,22 +645,21 @@ def _corank(self, X): sage: M._corank([1,2,3]) 3 """ - from sage.sets.disjoint_set import DisjointSet - - all_vertices = self._G.vertices(sort=False) - not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) - DS_vertices = DisjointSet(all_vertices) + cdef DisjointSet_of_hashables DS_vertices + cdef list all_vertices = self._G.vertices(sort=False) + cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) + DS_vertices = DisjointSet_of_hashables(all_vertices) for u, v, l in not_our_edges: DS_vertices.union(u, v) return len(X) - (DS_vertices.number_of_subsets() - Integer(1)) - def _is_circuit(self, X): + cpdef _is_circuit(self, X): """ Test if input is a circuit. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: boolean @@ -686,16 +673,16 @@ def _is_circuit(self, X): sage: M._is_circuit([0,1,3]) False """ - g = self._subgraph_from_set(X) + cdef GenericGraph_pyx g = self._subgraph_from_set(X) return g.is_cycle() - def _closure(self, X): + cpdef _closure(self, X): """ Return the closure of a set. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -720,32 +707,33 @@ def _closure(self, X): sage: sorted(M._closure([4])) [0, 4, 5] """ - X = set(X) - Y = self.groundset().difference(X) - edgelist = self._groundset_to_edges(Y) - g = self._subgraph_from_set(X) - V = g.vertices(sort=False) - components = g.connected_components_number() + cdef set XX = set(X) + cdef frozenset Y = self._groundset.difference(XX) + cdef list edgelist = self._groundset_to_edges(Y) + cdef GenericGraph_pyx g = self._subgraph_from_set(XX) + cdef list V = g.vertices(sort=False) + cdef int components = g.connected_components_number() + cdef tuple e for e in edgelist: # a non-loop edge is in the closure iff both its vertices are # in the induced subgraph, and the edge doesn't connect components if e[0] in V and e[1] in V: g.add_edge(e) if g.connected_components_number() >= components: - X.add(e[2]) + XX.add(e[2]) else: g.delete_edge(e) # add all loops - X.update(set([l for (u, v, l) in self._G.loops()])) - return frozenset(X) + XX.update(set([l for (u, v, l) in self._G.loops()])) + return frozenset(XX) - def _max_independent(self, X): + cpdef _max_independent(self, X): """ Compute a maximal independent subset. INPUT: - - ``X`` -- An object with Python's ``frozenset`` interface containing + - ``X`` -- an object with Python's ``frozenset`` interface containing a subset of ``self.groundset()`` OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -765,27 +753,26 @@ def _max_independent(self, X): sage: sorted(N._max_independent(frozenset(['a']))) [] """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set([u for (u, v, l) in edges]) vertices.update([v for (u, v, l) in edges]) - our_set = set() - DS_vertices = DisjointSet(vertices) + cdef set our_set = set() + DS_vertices = DisjointSet_of_hashables(vertices) for (u, v, l) in edges: if DS_vertices.find(u) != DS_vertices.find(v): DS_vertices.union(u, v) our_set.add(l) return frozenset(our_set) - def _max_coindependent(self, X): + cpdef _max_coindependent(self, X): """ Compute a maximal coindependent subset. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -800,14 +787,13 @@ def _max_coindependent(self, X): sage: sorted(N.max_coindependent([0,1,2,5])) [1, 2, 5] """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - all_vertices = self._G.vertices(sort=False) - not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef list all_vertices = self._G.vertices(sort=False) + cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) - our_set = set() - DS_vertices = DisjointSet(all_vertices) + cdef set our_set = set() + DS_vertices = DisjointSet_of_hashables(all_vertices) for (u, v, l) in not_our_edges: DS_vertices.union(u, v) @@ -818,18 +804,16 @@ def _max_coindependent(self, X): DS_vertices.union(u, v) return frozenset(our_set) - def _circuit(self, X): + cpdef _circuit(self, X): """ Return a minimal dependent subset. INPUT: - - ``X`` -- an iterable container of ground set elements - - OUTPUT: + - ``X`` -- an iterable container of groundset elements - ``frozenset`` instance containing a subset of ``X``. - A :class:`ValueError` is raised if the set contains no circuit. + OUTPUT: ``frozenset`` instance containing a subset of ``X``; + a :class:`ValueError` is raised if the set contains no circuit EXAMPLES:: @@ -864,14 +848,19 @@ def _circuit(self, X): sage: sorted(M._circuit(M.groundset())) [4, 5] """ - from sage.sets.disjoint_set import DisjointSet + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set() + cdef list vertex_list = [] + cdef list leaves + cdef tuple leaf + cdef set edge_set = set() + cdef DisjointSet_of_hashables DS_vertices - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]).union( - set([v for (u, v, l) in edges])) - edge_set = set() - DS_vertices = DisjointSet(vertices) - for u, v, l in edges: + for (u, v, l) in edges: + vertices.add(u) + vertices.add(v) + DS_vertices = DisjointSet_of_hashables(vertices) + for (u, v, l) in edges: edge_set.add((u, v, l)) if DS_vertices.find(u) != DS_vertices.find(v): DS_vertices.union(u, v) @@ -880,7 +869,8 @@ def _circuit(self, X): else: raise ValueError("no circuit in independent set") - vertex_list = [u for u, v, l in edge_set] + [v for u, v, l in edge_set] + for (u, v, l) in edge_set: + vertex_list.extend([u, v]) leaves = [(u, v, l) for (u, v, l) in edge_set if vertex_list.count(u) == 1 or vertex_list.count(v) == 1] while leaves: @@ -893,13 +883,13 @@ def _circuit(self, X): return frozenset([l for (u, v, l) in edge_set]) - def _coclosure(self, X): + cpdef _coclosure(self, X): """ Return the coclosure of a set. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -917,19 +907,19 @@ def _coclosure(self, X): sage: sorted(N._coclosure([3])) [3, 4, 5] """ - g = self.graph() + cdef GenericGraph_pyx g = self.graph() g.delete_edges(self._groundset_to_edges(X)) - components = g.connected_components_number() - X = set(X) - Y = self.groundset().difference(X) + cdef int components = g.connected_components_number() + cdef set XX = set(X) + cdef frozenset Y = self.groundset().difference(XX) for e in self._groundset_to_edges(Y): g.delete_edge(e) if g.connected_components_number() > components: - X.add(e[2]) + XX.add(e[2]) g.add_edge(e) - return frozenset(X) + return frozenset(XX) - def _is_closed(self, X): + cpdef _is_closed(self, X): """ Test if input is a closed set. @@ -955,18 +945,19 @@ def _is_closed(self, X): # Take the set of vertices of the edges corresponding to the elements, # and check if there are other edges incident with two of those vertices. # Also, there must not be loops outside of X. - X = set(X) - loop_labels = set([l for (u, v, l) in self._G.loops()]) - if not loop_labels.issubset(X): + cdef set XX = set(X) + cdef set loop_labels = set([l for (u, v, l) in self._G.loops()]) + if not loop_labels.issubset(XX): return False - # Remove loops from input since we don't want to count them as components - X.difference_update(loop_labels) - edge_list = self._groundset_to_edges(X) + # Remove loops from input since we don't want to count them as + # components + XX.difference_update(loop_labels) + cdef list edge_list = self._groundset_to_edges(XX) - vertex_set = set() - Y = self.groundset().difference(X) - edge_list2 = self._groundset_to_edges(Y) + cdef set vertex_set = set() + cdef frozenset Y = self.groundset().difference(XX) + cdef list edge_list2 = self._groundset_to_edges(Y) for e in edge_list: vertex_set.add(e[0]) vertex_set.add(e[1]) @@ -975,13 +966,13 @@ def _is_closed(self, X): return False return True - def _is_isomorphic(self, other, certificate=False): + cpdef _is_isomorphic(self, other, certificate=False): """ Test if ``self`` is isomorphic to ``other``. INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid - ``certificate`` -- boolean OUTPUT: @@ -1064,7 +1055,7 @@ def _is_isomorphic(self, other, certificate=False): return (True, {e: iso2[iso1[e]] for e in iso1}) return M._is_isomorphic(other) - def _isomorphism(self, other): + cpdef _isomorphism(self, other): """ Return isomorphism from ``self`` to ``other``, if such an isomorphism exists. @@ -1073,9 +1064,9 @@ def _isomorphism(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid - OUTPUT: a dictionary, or ``None`` + OUTPUT: dictionary or ``None`` EXAMPLES:: @@ -1103,7 +1094,7 @@ def _isomorphism(self, other): """ return self.is_isomorphic(other, certificate=True)[1] - def is_valid(self): + cpdef is_valid(self): """ Test if the data obey the matroid axioms. @@ -1148,15 +1139,15 @@ def is_regular(self): """ return True - # Graphic methods: + # graphic methods - def graph(self): + cpdef graph(self): """ Return the graph that represents the matroid. The graph will always have loops and multiedges enabled. - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: @@ -1170,7 +1161,7 @@ def graph(self): # Return a mutable graph return self._G.copy(data_structure='sparse') - def vertex_map(self): + cpdef vertex_map(self): """ Return a dictionary mapping the input vertices to the current vertices. @@ -1180,7 +1171,7 @@ def vertex_map(self): input graph as keys, and the corresponding vertex label after any merging as values. - OUTPUT: a dictionary + OUTPUT: dictionary EXAMPLES:: @@ -1204,22 +1195,22 @@ def vertex_map(self): """ return copy(self._vertex_map) - def groundset_to_edges(self, X): + cpdef groundset_to_edges(self, X): """ Return a list of edges corresponding to a set of groundset elements. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a list of graph edges + OUTPUT: list of graph edges EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M.groundset_to_edges([2,3,4]) + sage: M.groundset_to_edges([2, 3, 4]) [(1, 2, 2), (1, 3, 3), (2, 3, 4)] - sage: M.groundset_to_edges([2,3,4,5]) + sage: M.groundset_to_edges([2, 3, 4, 5]) Traceback (most recent call last): ... ValueError: input must be a subset of the groundset @@ -1229,33 +1220,33 @@ def groundset_to_edges(self, X): raise ValueError("input must be a subset of the groundset") return self._groundset_to_edges(X) - def _groundset_to_edges(self, X): + cpdef _groundset_to_edges(self, X): """ Return a list of edges corresponding to a set of groundset elements. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a list of graph edges + OUTPUT: list of graph edges EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M._groundset_to_edges([2,3,4]) + sage: M._groundset_to_edges([2, 3, 4]) [(1, 2, 2), (1, 3, 3), (2, 3, 4)] """ return [(self._groundset_edge_map[x][0], self._groundset_edge_map[x][1], x) for x in X] - def subgraph_from_set(self, X): + cpdef subgraph_from_set(self, X): """ Return the subgraph corresponding to the matroid restricted to `X`. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: @@ -1272,28 +1263,27 @@ def subgraph_from_set(self, X): raise ValueError("input must be a subset of the groundset") return self._subgraph_from_set(X) - def _subgraph_from_set(self, X): + cpdef _subgraph_from_set(self, X): """ Return the subgraph corresponding to `M` restricted to `X`. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M._subgraph_from_set([0,1,2]) + sage: M._subgraph_from_set([0, 1, 2]) Looped multi-graph on 3 vertices """ from sage.graphs.graph import Graph - edge_list = self._groundset_to_edges(X) return Graph(edge_list, loops=True, multiedges=True) - def graphic_extension(self, u, v=None, element=None): + cpdef graphic_extension(self, u, v=None, element=None): """ Return a graphic matroid extended by a new element. @@ -1302,15 +1292,15 @@ def graphic_extension(self, u, v=None, element=None): INPUT: - - ``u`` -- a vertex in the matroid's graph + - ``u`` -- vertex in the matroid's graph - ``v`` -- (optional) another vertex - ``element`` -- (optional) the label of the new element OUTPUT: - A GraphicMatroid with the specified element added. Note that if ``v`` - is not specifies or if ``v`` is ``u``, then the new element will be a - loop. If the new element's label is not specified, it will be + A class:`GraphicMatroid` with the specified element added. Note that if + ``v`` is not specified or if ``v`` is ``u``, then the new element will + be a loop. If the new element's label is not specified, it will be generated automatically. EXAMPLES:: @@ -1380,8 +1370,8 @@ def graphic_extensions(self, element=None, vertices=None, simple=False): OUTPUT: - An iterable containing instances of ``GraphicMatroid``. If ``vertices`` - is not specified, every vertex is used. + An iterable containing instances of class:`GraphicMatroid`. If + ``vertices`` is not specified, every vertex is used. .. NOTE:: @@ -1436,7 +1426,7 @@ def graphic_extensions(self, element=None, vertices=None, simple=False): yield GraphicMatroid(G) G.delete_edge(p[0], p[1], element) - def graphic_coextension(self, u, v=None, X=None, element=None): + cpdef graphic_coextension(self, u, v=None, X=None, element=None): """ Return a matroid coextended by a new element. @@ -1455,8 +1445,8 @@ def graphic_coextension(self, u, v=None, X=None, element=None): OUTPUT: - An instance of GraphicMatroid coextended by the new element. If ``X`` - is not specified, the new element will be a coloop. + An instance of class:`GraphicMatroid` coextended by the new element. + If ``X`` is not specified, the new element will be a coloop. .. NOTE:: @@ -1589,8 +1579,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal OUTPUT: - An iterable containing instances of ``GraphicMatroid``. If ``vertices`` - is not specified, the method iterates over all vertices. + An iterable containing instances of class:`GraphicMatroid`. If + ``vertices`` is not specified, the method iterates over all vertices. EXAMPLES:: @@ -1653,7 +1643,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal non-cosimple, ie. a coloop and one for every coseries class. 12 total:: - sage: edgedict = {0:[1,2,3], 1:[2,4], 2:[3], 3:[6], 4:[5,7], 5:[6,7], 6:[7]} + sage: edgedict = {0: [1, 2, 3], 1: [2, 4], 2: [3], 3: [6], + ....: 4: [5, 7], 5: [6, 7], 6: [7]} sage: M = Matroid(range(12), Graph(edgedict)) sage: sorted(M.coclosure([4])) [4, 6] @@ -1706,7 +1697,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal # If a vertex has degree 1, or 2, or 3, we already handled it. for u in vertices: if G.degree(u) > 3: - elts_incident = [ll for (_, _, ll) in G.edges_incident(u)] + elts_incident = [l for (_, _, l) in G.edges_incident(u)] x = elts_incident.pop() for i in range(1, (len(elts_incident) - Integer(1))): groups = combinations(elts_incident, i) @@ -1716,7 +1707,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal yield self.graphic_coextension( X=g, u=u, v=v, element=element) - def twist(self, X): + cpdef twist(self, X): """ Perform a Whitney twist on the graph. @@ -1730,18 +1721,17 @@ def twist(self, X): - ``X`` -- the set of elements to be twisted with respect to the rest of the matroid - OUTPUT: - - An instance of ``GraphicMatroid`` isomorphic to this matroid but with - a graph that is not necessarily isomorphic. + OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but + with a graph that is not necessarily isomorphic EXAMPLES:: - sage: edgelist = [(0,1,0), (1,2,1), (1,2,2), (2,3,3), (2,3,4), (2,3,5), (3,0,6)] + sage: edgelist = [(0, 1, 0), (1, 2, 1), (1, 2, 2), (2, 3, 3), + ....: (2, 3, 4), (2, 3, 5), (3, 0, 6)] sage: M = Matroid(Graph(edgelist, multiedges=True)) - sage: M1 = M.twist([0,1,2]); M1.graph().edges(sort=True) + sage: M1 = M.twist([0, 1, 2]); M1.graph().edges(sort=True) [(0, 1, 1), (0, 1, 2), (0, 3, 6), (1, 2, 0), (2, 3, 3), (2, 3, 4), (2, 3, 5)] - sage: M2 = M.twist([0,1,3]) + sage: M2 = M.twist([0, 1, 3]) Traceback (most recent call last): ... ValueError: the input must display a 2-separation that is not a 1-separation @@ -1825,7 +1815,7 @@ def twist(self, X): G.add_edge(u, v, l) return GraphicMatroid(G) - def one_sum(self, X, u, v): + cpdef one_sum(self, X, u, v): """ Arrange matroid components in the graph. @@ -1837,14 +1827,12 @@ def one_sum(self, X, u, v): INPUT: - - ``X`` -- a subset of the groundset - - ``u`` -- a vertex spanned by the edges of the elements in ``X`` - - ``v`` -- a vertex spanned by the edges of the elements not in ``X`` - - OUTPUT: + - ``X`` -- subset of the groundset + - ``u`` -- vertex spanned by the edges of the elements in ``X`` + - ``v`` -- vertex spanned by the edges of the elements not in ``X`` - An instance of ``GraphicMatroid`` isomorphic to this matroid but with - a graph that is not necessarily isomorphic. + OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but + with a graph that is not necessarily isomorphic EXAMPLES:: @@ -1960,9 +1948,10 @@ def one_sum(self, X, u, v): return GraphicMatroid(G) - def regular_matroid(self): + cpdef regular_matroid(self): """ - Return an instance of RegularMatroid isomorphic to this GraphicMatroid. + Return an instance of class:`RegularMatroid` isomorphic to this + class:`GraphicMatroid`. EXAMPLES:: diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 19b87336033..0027d85073f 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -495,17 +495,26 @@ cdef class LinearMatroid(BasisExchangeMatroid): - ``B`` -- (default: ``None``) a subset of elements. When provided, the representation is such that a basis `B'` that maximally intersects `B` is an identity matrix. + - ``reduced`` -- (default: ``False``) when ``True``, return a reduced matrix `D` (so `[I\ \ D]` is a representation of the matroid). Otherwise return a full representation matrix. + - ``labels`` -- (default: ``None``) when ``True``, return additionally a list of column labels (if ``reduced=False``) or a list of row labels and a list of column labels (if ``reduced=True``). The default setting, ``None``, will not return the labels for a full matrix, but will return the labels for a reduced matrix. - - ``order`` -- (default: ``None``) an ordering of the groundset - elements. If provided, the columns (and, in case of a reduced - representation, rows) will be presented in the given order. + + - ``order`` -- sequence or ``None`` or ``True`` (default: ``None``); + + - when a sequence, it should be an ordering of the groundset + elements, and the columns (and, in case of a reduced + representation, rows) will be presented in the given order, + - when ``None``, use the same ordering that :meth:`groundset_list` + uses, + - when ``True``, return a morphism of free modules instead of a matrix. + - ``lift_map`` -- (default: ``None``) a dictionary containing the cross ratios of the representing matrix in its domain. If provided, the representation will be transformed by mapping its cross ratios according @@ -578,9 +587,47 @@ cdef class LinearMatroid(BasisExchangeMatroid): [ 1 0 0 1 0 1 1 1] [ 0 1 0 -z + 1 1 0 0 1] [ 0 0 1 0 1 -1 z - 1 0] + + As morphisms:: + + sage: M = matroids.catalog.Fano() + sage: A = M.representation(order=True); A + Generic morphism: + From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'} + over Finite Field of size 2 + To: Free module generated by {0, 1, 2} over Finite Field of size 2 + sage: print(A._unicode_art_matrix()) + a b c d e f g + 0⎛1 0 0 0 1 1 1⎞ + 1⎜0 1 0 1 0 1 1⎟ + 2⎝0 0 1 1 1 0 1⎠ + sage: A = M.representation(B='efg', order=True); A + Generic morphism: + From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'} + over Finite Field of size 2 + To: Free module generated by {0, 1, 2} over Finite Field of size 2 + sage: print(A._unicode_art_matrix()) + a b c d e f g + 0⎛1 1 0 1 1 0 0⎞ + 1⎜1 0 1 1 0 1 0⎟ + 2⎝1 1 1 0 0 0 1⎠ + sage: A = M.representation(B='abc', order=True, reduced=True); A + Generic morphism: + From: Free module generated by {'d', 'e', 'f', 'g'} + over Finite Field of size 2 + To: Free module generated by {'a', 'b', 'c'} over Finite Field of size 2 + sage: print(A._unicode_art_matrix()) + d e f g + a⎛0 1 1 1⎞ + b⎜1 0 1 1⎟ + c⎝1 1 0 1⎠ """ cdef LeanMatrix A - if order is None: + column_keys = None + if order is True: + order = self.groundset_list() + column_keys = tuple(order) + elif order is None: order = self.groundset_list() else: if not frozenset(order) == self.groundset(): @@ -607,16 +654,14 @@ cdef class LinearMatroid(BasisExchangeMatroid): B = self._subset_internal(B) A = self._basic_representation(B) A = A.matrix_from_rows_and_columns(range(A.nrows()), order_idx) - if lift_map is None: - if labels: - return (A._matrix_(), order) - else: - return A._matrix_() - else: - if labels: - return (lift_cross_ratios(A._matrix_(), lift_map), order) - else: - return lift_cross_ratios(A._matrix_(), lift_map) + Am = A._matrix_() + if lift_map is not None: + Am = lift_cross_ratios(Am, lift_map) + if column_keys is not None: + Am = matrix(Am, row_keys=range(A.nrows()), column_keys=column_keys) + if labels: + return Am, order + return Am else: if B is None: B = frozenset(self.basis()) @@ -637,16 +682,14 @@ cdef class LinearMatroid(BasisExchangeMatroid): Ci.append(C.index(e)) Cl.append(e) A = A.matrix_from_rows_and_columns(Ri, Ci) - if lift_map is None: - if labels or labels is None: - return (A._matrix_(), Rl, Cl) - else: - return A._matrix_() - else: - if labels or labels is None: - return (lift_cross_ratios(A._matrix_(), lift_map), Rl, Cl) - else: - return lift_cross_ratios(A._matrix_(), lift_map) + Am = A._matrix_() + if lift_map is not None: + Am = lift_cross_ratios(Am, lift_map) + if column_keys is not None: + Am = matrix(Am, row_keys=tuple(Rl), column_keys=tuple(Cl)) + if labels or (labels is None and column_keys is None): + return Am, Rl, Cl + return Am cpdef _current_rows_cols(self, B=None): """ diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx index d4e306abfdd..1fc0ed4546a 100644 --- a/src/sage/matroids/set_system.pyx +++ b/src/sage/matroids/set_system.pyx @@ -40,7 +40,7 @@ cdef class SetSystem: sage: M = matroids.catalog.Fano() sage: M.circuits() - Iterator over a system of subsets + SetSystem of 14 sets over 7 elements To access the sets in this structure, simply iterate over them. The simplest way must be:: @@ -75,7 +75,7 @@ cdef class SetSystem: sage: from sage.matroids.set_system import SetSystem sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]]) sage: S - Iterator over a system of subsets + SetSystem of 3 sets over 4 elements """ cdef long i if not isinstance(groundset, tuple): @@ -110,7 +110,7 @@ cdef class SetSystem: sage: from sage.matroids.set_system import SetSystem sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]]) sage: S - Iterator over a system of subsets + SetSystem of 3 sets over 4 elements sage: sorted(S[1]) [3, 4] sage: for s in S: print(sorted(s)) @@ -138,7 +138,7 @@ cdef class SetSystem: sage: from sage.matroids.set_system import SetSystem sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]]) sage: S - Iterator over a system of subsets + SetSystem of 3 sets over 4 elements sage: len(S) 3 """ @@ -196,9 +196,9 @@ cdef class SetSystem: sage: from sage.matroids.set_system import SetSystem sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]]) sage: repr(S) # indirect doctest - 'Iterator over a system of subsets' + 'SetSystem of 3 sets over 4 elements' """ - return "Iterator over a system of subsets" + return f'SetSystem of {self._len} sets over {self._groundset_size} elements' cdef copy(self): cdef SetSystem S diff --git a/src/sage/matroids/union_matroid.pyx b/src/sage/matroids/union_matroid.pyx index 817f9c4a222..12e9d1e7718 100644 --- a/src/sage/matroids/union_matroid.pyx +++ b/src/sage/matroids/union_matroid.pyx @@ -21,7 +21,7 @@ cdef class MatroidUnion(Matroid): {} Matroid of rank 1 on 2 elements with 2 bases sage: M.bases() - Iterator over a system of subsets + SetSystem of 2 sets over 5 elements sage: list(M.circuits()) [frozenset({3, 4})] diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py index a0cf14f5276..d5e680fc6b9 100644 --- a/src/sage/matroids/utilities.py +++ b/src/sage/matroids/utilities.py @@ -70,7 +70,7 @@ def setprint(X): sage: from sage.matroids.advanced import setprint sage: M = matroids.catalog.Fano().delete('efg') sage: M.bases() - Iterator over a system of subsets + SetSystem of 3 sets over 4 elements sage: setprint(M.bases()) [{'a', 'b', 'c'}, {'a', 'b', 'd'}, {'a', 'c', 'd'}] diff --git a/src/sage/misc/abstract_method.py b/src/sage/misc/abstract_method.py index 6179c5a2b8f..bd78b05c7d5 100644 --- a/src/sage/misc/abstract_method.py +++ b/src/sage/misc/abstract_method.py @@ -138,7 +138,7 @@ def abstract_method(f=None, optional=False): return AbstractMethod(f, optional) -class AbstractMethod(): +class AbstractMethod: def __init__(self, f, optional=False): """ Constructor for abstract methods diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 8aee092368c..585db5a05eb 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -131,11 +131,9 @@ lazy_import('sage.misc.dev_tools', 'runsnake', deprecation=34259) lazy_import('sage.misc.edit_module', 'set_edit_template', deprecation=34259) lazy_import('sage.misc.profiler', 'Profiler', deprecation=34259) -lazy_import('sage.misc.dist', 'install_scripts', deprecation=34259) lazy_import('sage.misc.trace', 'trace', deprecation=34259) lazy_import('sage.misc.package', ('installed_packages', 'is_package_installed', - 'standard_packages', 'optional_packages', - 'experimental_packages', 'package_versions'), + 'package_versions'), deprecation=34259) lazy_import('sage.misc.benchmark', 'benchmark', deprecation=34259) lazy_import('sage.repl.interpreter', 'logstr', deprecation=34259) diff --git a/src/sage/misc/binary_tree.pyx b/src/sage/misc/binary_tree.pyx index 8451e0b1e04..2d41ce45e8c 100644 --- a/src/sage/misc/binary_tree.pyx +++ b/src/sage/misc/binary_tree.pyx @@ -310,7 +310,7 @@ cdef class BinaryTree: sage: t = BinaryTree() sage: t.contains(1) False - sage: t.insert(1,1) + sage: t.insert(1, 1) sage: t.contains(1) True """ diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index befaa7b5b32..437a3da23cf 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -295,7 +295,7 @@ Depending on the linear extension `l` it was necessary to add between one and five bases for control; for example, `216` linear extensions required the addition of four bases:: - sage: sorted(Word(stats).evaluation_sparse()) # needs sage.graphs sage.modules + sage: sorted(Word(stats).evaluation_sparse()) # needs sage.combinat sage.graphs sage.modules [(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)] We now consider a hierarchy of categories:: @@ -319,9 +319,9 @@ For a typical category, few bases, if any, need to be added to force sage: x.mro == x.mro_standard False sage: x.all_bases_len() - 70 + 72 sage: x.all_bases_controlled_len() - 74 + 76 sage: C = GradedHopfAlgebrasWithBasis(QQ) sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key")) diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index 6de02981882..1d3055faf78 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -17,7 +17,7 @@ ############################################# # Operators ############################################# -class AttrCallObject(): +class AttrCallObject: def __init__(self, name, args, kwds): """ TESTS:: diff --git a/src/sage/misc/classgraph.py b/src/sage/misc/classgraph.py index ae33fe9277e..9db5ec99639 100644 --- a/src/sage/misc/classgraph.py +++ b/src/sage/misc/classgraph.py @@ -92,7 +92,7 @@ def class_graph(top, depth=5, name_filter=None, classes=None, as_graph=True): # (first recursive call) if classes is None: - classes = dict() + classes = {} # Build the list ``children`` of submodules (resp. base classes) # of ``top`` the function will recurse through diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 0cd7a47916e..659bf223c50 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -1,7 +1,7 @@ r""" Converting Dictionary -At the moment, the only class contained in this model is a key +At the moment, the only class contained in this module is a key converting dictionary, which applies some function (e.g. type conversion function) to all arguments used as keys. diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 93ce994c537..fbf22dea2c1 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -348,7 +348,7 @@ def cython(filename, verbose=0, compile_message=False, libraries=standard_libs, library_dirs=standard_libdirs) - directives = dict(language_level=3, cdivision=True) + directives = {'language_level': 3, 'cdivision': True} try: # Change directories to target_dir so that Cython produces the correct diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 9ded4260e3b..f0b34b8843d 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -177,7 +177,7 @@ def f(wrapper, assigned=assigned, updated=updated): # Infix operator decorator -class infix_operator(): +class infix_operator: """ A decorator for functions which allows for a hack that makes the function behave like an infix operator. @@ -259,7 +259,7 @@ def __call__(self, func): return wrapper_inst -class _infix_wrapper(): +class _infix_wrapper: function = None def __init__(self, left=None, right=None): @@ -348,7 +348,7 @@ def my_wrap(*args, **kwds): return my_wrap -class suboptions(): +class suboptions: def __init__(self, name, **options): """ A decorator for functions which collects all keywords @@ -433,7 +433,7 @@ def listForNone(l): return wrapper -class options(): +class options: def __init__(self, **options): """ A decorator for functions which allows for default options to be @@ -573,7 +573,7 @@ def reset(): return wrapper -class rename_keyword(): +class rename_keyword: def __init__(self, deprecated=None, deprecation=None, **renames): """ A decorator which renames keyword arguments and optionally diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 8a4420f2314..05d7c6b877b 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -260,7 +260,7 @@ def find_objects_from_name(name, module_name=None, include_lazy_imports=False): :class:`~sage.misc.lazy_import.LazyImport` objects that are resolving to the same object may be included in the output:: - sage: dt.find_objects_from_name('RR', include_lazy_imports=True) + sage: dt.find_objects_from_name('RR', include_lazy_imports=True) # needs sage.rings.real_mpfr [Real Field with 53 bits of precision, ... Real Field with 53 bits of precision, @@ -680,10 +680,7 @@ def is_ascii(s): # is a best one (i.e. the object "obj" is contained in the module and # has name "name") if name is not None: - good_modules = [] - for mod in modules: - if name in modules[mod]: - good_modules.append(mod) + good_modules = [mod for mod in modules if name in modules[mod]] if len(good_modules) == 1: answer[good_modules[0]].append((name, name)) @@ -732,9 +729,8 @@ def is_ascii(s): if lazy: res.append("from sage.misc.lazy_import import lazy_import") - for module_name in sorted(answer): - res.append(import_statement_string(module_name, answer[module_name], - lazy)) + res.extend(import_statement_string(module_name, answer[module_name], lazy) + for module_name in sorted(answer)) if answer_as_str: return '\n'.join(res) diff --git a/src/sage/misc/dist.py b/src/sage/misc/dist.py deleted file mode 100644 index b7c59ee77f3..00000000000 --- a/src/sage/misc/dist.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -Installing shortcut scripts -""" - -import os - -from sage.misc.superseded import deprecation - -def install_scripts(directory=None, ignore_existing=False): - r""" - This function has been deprecated. - - Running ``install_scripts(directory)`` creates scripts in the - given directory that run various software components included with - Sage. Each of these scripts essentially just runs ``sage --CMD`` - where ``CMD`` is also the name of the script: - - - 'gap' runs GAP - - 'gp' runs the PARI/GP interpreter - - 'ipython' runs IPython - - 'maxima' runs Maxima - - 'mwrank' runs mwrank - - 'R' runs R - - 'singular' runs Singular - - 'sqlite3' runs SQLite version 3 - - This command: - - - verbosely tells you which scripts it adds, and - - - will *not* overwrite any scripts you already have in the given - directory. - - INPUT: - - - ``directory`` - string; the directory into which to put the - scripts. This directory must exist and the user must have write - and execute permissions. - - - ``ignore_existing`` - bool (optional, default False): if True, - install script even if another version of the program is in your - path. - - OUTPUT: Verbosely prints what it is doing and creates files in - ``directory`` that are world executable and readable. - - .. note:: - - You may need to run ``sage`` as ``root`` in order to run - ``install_scripts`` successfully, since the user running - ``sage`` needs write permissions on ``directory``. Note - that one good candidate for ``directory`` is - ``'/usr/local/bin'``, so from the shell prompt, you could run :: - - sudo sage -c "install_scripts('/usr/local/bin')" - - .. note:: - - Running ``install_scripts(directory)`` will be most helpful if - ``directory`` is in your path. - - AUTHORS: - - - William Stein: code / design - - - Arthur Gaer: design - - - John Palmieri: revision, 2011-07 (:issue:`11602`) - - EXAMPLES:: - - sage: import tempfile - sage: from sage.misc.dist import install_scripts - sage: with tempfile.TemporaryDirectory() as d: - ....: install_scripts(d, ignore_existing=True) - doctest:warning... - the function install_scripts has been deprecated and will be removed in a future version of Sage - See https://github.com/sagemath/sage/issues/30207 for details. - Checking that Sage has the command 'gap' installed - ... - """ - deprecation(30207, 'the function install_scripts has been deprecated and ' - 'will be removed in a future version of Sage') - - if directory is None: - # We do this since the intended user of install_scripts - # will likely be pretty clueless about how to use Sage or - # its help system. - from . import sagedoc - print(sagedoc.format(install_scripts.__doc__)) - print("USAGE: install_scripts('directory')") - return - - if not os.path.exists(directory): - print(f"Error: '{directory}' does not exist.") - return - - if not os.path.isdir(directory): - print(f"Error: '{directory}' is not a directory.") - return - - if not (os.access(directory, os.W_OK) and os.access(directory, os.X_OK)): - print(f"Error: you do not have write permission for '{directory}'.") - return - - from sage.misc.sage_ostools import have_program - from sage.env import SAGE_LOCAL - - if not SAGE_LOCAL: - print(f"Error: This installation of Sage does not use SAGE_LOCAL, so install_scripts makes no sense.") - return - - script_created = False - SAGE_BIN = os.path.join(SAGE_LOCAL, 'bin') - # See if 'directory' is already in PATH, and then remove - # SAGE_LOCAL/bin from PATH so that we can later check whether - # cmd is available outside of Sage. - PATH = os.environ['PATH'].split(os.pathsep) - PATH = [d for d in PATH if os.path.exists(d)] - dir_in_path = any(os.path.samefile(directory, d) for d in PATH) - PATH = os.pathsep.join(d for d in PATH - if not os.path.samefile(d, SAGE_BIN)) - for cmd in ['gap', 'gp', 'ipython', 'maxima', - 'mwrank', 'R', 'singular', 'sqlite3']: - print(f"Checking that Sage has the command '{cmd}' installed") - # Check to see if Sage includes cmd. - cmd_inside_sage = have_program(cmd, path=SAGE_BIN) - if not cmd_inside_sage: - print(f"The command '{cmd}' is not available as part of Sage; " + - "not creating script.") - print() - continue - cmd_outside_sage = have_program(cmd, path=PATH) - if cmd_outside_sage: - print(f"The command '{cmd}' is installed outside of Sage;", end=' ') - if not ignore_existing: - print("not creating script.") - print() - continue - print("trying to create script anyway...") - else: - print(f"Creating script for '{cmd}'...") - # Install shortcut. - target = os.path.join(directory, cmd) - if os.path.exists(target): - print(f"The file '{target}' already exists; not adding script.") - else: - with open(target, 'w') as o: - o.write('#!/bin/sh\n') - o.write('exec sage --%s "$@"\n' % cmd) - print(f"Created script '{target}'") - os.system(f'chmod a+rx {target}') - script_created = True - print() - - if script_created: - print("Finished creating scripts.") - print() - print("You need not do this again even if you upgrade or move Sage.") - print("The only requirement is that your PATH contains both") - print("'{}' and the directory containing the command 'sage'.".format(directory)) - if not dir_in_path: - print() - print("Warning: '{}' is not currently in your PATH.".format(directory)) - print() - else: - print("No scripts created.") diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py index 58a3ab15a2a..63232e89a81 100644 --- a/src/sage/misc/edit_module.py +++ b/src/sage/misc/edit_module.py @@ -102,7 +102,7 @@ def file_and_line(obj): filename = sage_getfile(obj) lineno = sage_getsourcelines(obj)[1] - 1 if filename.endswith('.py'): - infile = open(filename, 'r') + infile = open(filename) infile.readline() if infile.readline().find("*autogenerated*") >= 0: filename = filename[:-3] + '.sage' @@ -171,7 +171,7 @@ def set_edit_template(template_string): if not isinstance(template_string, Template): template_string = Template(template_string) fields = set(template_fields(template_string)) - if not (fields <= set(['file', 'line']) and ('file' in fields)): + if not (fields <= {'file', 'line'} and ('file' in fields)): raise ValueError("Only ${file} and ${line} are allowed as template variables, and ${file} must occur.") edit_template = template_string diff --git a/src/sage/misc/element_with_label.py b/src/sage/misc/element_with_label.py index e7ae38264be..8ae3d4ee523 100644 --- a/src/sage/misc/element_with_label.py +++ b/src/sage/misc/element_with_label.py @@ -14,7 +14,7 @@ from sage.misc.latex import latex -class ElementWithLabel(): +class ElementWithLabel: """ Auxiliary class for showing/viewing :class:`Poset`s with non-injective labelings. diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py index a0c6323270c..fe62f5ea841 100644 --- a/src/sage/misc/explain_pickle.py +++ b/src/sage/misc/explain_pickle.py @@ -318,7 +318,7 @@ def name_is_valid(name): # This string is used as the representation of a mark. the_mark = 'mark' -class PickleObject(): +class PickleObject: r""" Pickles have a stack-based virtual machine. The :func:`explain_pickle` pickle interpreter mostly uses :class:`sage.misc.sage_input.SageInputExpression` objects @@ -374,7 +374,7 @@ def _sage_input_(self, sib, coerced): self.immutable = True return self.expression -class PickleDict(): +class PickleDict: r""" An object which can be used as the value of a :class:`PickleObject`. The items is a list of key-value pairs, where the keys and values are @@ -394,7 +394,7 @@ def __init__(self, items): """ self.items = items -class PickleInstance(): +class PickleInstance: r""" An object which can be used as the value of a :class:`PickleObject`. Unlike other possible values of a :class:`PickleObject`, a :class:`PickleInstance` doesn't represent @@ -412,7 +412,7 @@ def __init__(self, klass): """ self.klass = klass -class PickleExplainer(): +class PickleExplainer: r""" An interpreter for the pickle virtual machine, that executes symbolically and constructs :class:`SageInputExpression` objects instead of @@ -2681,7 +2681,7 @@ def __hash__(self): return 0 -class EmptyNewstyleClass(): +class EmptyNewstyleClass: r""" A featureless new-style class (inherits from object); used for testing :func:`explain_pickle`. @@ -2842,7 +2842,7 @@ def extend(self): raise NotImplementedError -class TestAppendNonlist(): +class TestAppendNonlist: r""" A list-like class, carefully designed to test exact unpickling behavior. Used for testing :func:`explain_pickle`. @@ -2926,7 +2926,7 @@ def __repr__(self): return repr(self.list) -class TestBuild(): +class TestBuild: r""" A simple class with a :meth:`__getstate__` but no :meth:`__setstate__`. Used for testing :func:`explain_pickle`. @@ -2985,7 +2985,7 @@ def __setstate__(self, state): self.x = state[1]['y'] self.y = state[0]['x'] -class TestGlobalOldName(): +class TestGlobalOldName: r""" A featureless new-style class. When you try to unpickle an instance of this class, it is redirected to create a :class:`TestGlobalNewName` instead. @@ -3000,7 +3000,7 @@ class TestGlobalOldName(): pass -class TestGlobalNewName(): +class TestGlobalNewName: r""" A featureless new-style class. When you try to unpickle an instance of :class:`TestGlobalOldName`, it is redirected to create an instance of this @@ -3032,7 +3032,7 @@ def __repr__(self): register_unpickle_override('sage.misc.explain_pickle', 'TestGlobalOldName', TestGlobalNewName, call_name=('sage.misc.explain_pickle', 'TestGlobalNewName')) -class TestGlobalFunnyName(): +class TestGlobalFunnyName: r""" A featureless new-style class which has a name that's not a legal Python identifier. diff --git a/src/sage/misc/flatten.py b/src/sage/misc/flatten.py index 5ebc8c2ceaa..889cf2e9d1d 100644 --- a/src/sage/misc/flatten.py +++ b/src/sage/misc/flatten.py @@ -23,7 +23,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize): [1, 1, 1, 2] sage: flatten([[1,2,3], (4,5), [[[1],[2]]]]) [1, 2, 3, 4, 5, 1, 2] - sage: flatten([[1,2,3], (4,5), [[[1],[2]]]],max_level=1) + sage: flatten([[1,2,3], (4,5), [[[1],[2]]]], max_level=1) [1, 2, 3, 4, 5, [[1], [2]]] sage: flatten([[[3],[]]],max_level=0) [[[3], []]] @@ -64,7 +64,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize): """ index = 0 current_level = 0 - new_list = [x for x in in_list] + new_list = list(in_list) level_list = [0] * len(in_list) while index < len(new_list): diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx index 1392fc4f2fd..5f440742dcb 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -202,10 +202,10 @@ cdef class ArgumentFixer: EXAMPLES:: sage: from sage.misc.function_mangling import ArgumentFixer - sage: def sum3(a,b,c=3,*args,**kwargs): - ....: return a+b+c + sage: def sum3(a, b, c=3, *args, **kwargs): + ....: return a + b + c sage: AF = ArgumentFixer(sum3) - sage: AF.fix_to_named(1,2,3,4,5,6,f=14,e=16) + sage: AF.fix_to_named(1, 2, 3, 4, 5, 6, f=14, e=16) ((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14))) sage: AF.fix_to_named(1,2,f=14) ((), (('a', 1), ('b', 2), ('c', 3), ('f', 14))) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 4ae9ecd6a2c..c70d78800e0 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -934,7 +934,7 @@ def krull_dimension(x): 1 sage: krull_dimension(ZZ[sqrt(5)]) # needs sage.rings.number_field sage.symbolic 1 - sage: U. = PolynomialRing(ZZ,3); U + sage: U. = PolynomialRing(ZZ, 3); U Multivariate Polynomial Ring in x, y, z over Integer Ring sage: U.krull_dimension() 4 @@ -1732,9 +1732,9 @@ def quotient(x, y, *args, **kwds): EXAMPLES:: - sage: quotient(5,6) + sage: quotient(5, 6) 5/6 - sage: quotient(5.,6.) + sage: quotient(5., 6.) 0.833333333333333 sage: R. = ZZ[]; R Univariate Polynomial Ring in x over Integer Ring diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index db573d16e66..6b9332686d0 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -2270,7 +2270,7 @@ def latex_variable_name(x, is_fname=False): return latex_varify(prefix, is_fname) -class LatexExamples(): +class LatexExamples: r""" A catalogue of Sage objects with complicated ``_latex_`` methods. Use these for testing :func:`latex`, :func:`view`, the Typeset diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index c21bdefb643..35634b16634 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -290,8 +290,8 @@ class Standalone(SageObject): """ def __init__(self, content, document_class_options=None, - standalone_config=None, usepackage=None, macros=None, - use_sage_preamble=False): + standalone_config=None, usepackage=None, macros=None, + use_sage_preamble=False): r""" See :class:`Standalone` for full information. @@ -337,10 +337,10 @@ def _latex_file_header_lines(self): lines.append(r"\documentclass[{}]{{standalone}}".format(options)) else: lines.append(r"\documentclass{standalone}") - for config in self._standalone_config: - lines.append(r"\standaloneconfig{{{}}}".format(config)) - for package in self._usepackage: - lines.append(r"\usepackage{{{}}}".format(package)) + lines.extend(r"\standaloneconfig{{{}}}".format(config) + for config in self._standalone_config) + lines.extend(r"\usepackage{{{}}}".format(package) + for package in self._usepackage) lines.extend(self._macros) return lines @@ -1029,7 +1029,7 @@ def svg(self, filename=None, view=True, program='pdftocairo'): cmd = ['pdf2svg', temp_filename_pdf, temp_filename_svg] else: raise ValueError("program(={}) should be 'pdftocairo' or" - " 'pdf2svg'".format(program)) + " 'pdf2svg'".format(program)) # convert to svg result = run(cmd, capture_output=True, text=True) @@ -1146,7 +1146,7 @@ def eps(self, filename=None, view=True, program='dvips'): cmd = ['dvips', '-E', '-o', temp_filename_eps, temp_filename_dvi] else: raise ValueError("program(={}) should be 'pdftocairo' or" - " 'dvips'".format(program)) + " 'dvips'".format(program)) # convert to eps result = run(cmd, capture_output=True, text=True) @@ -1308,6 +1308,7 @@ def save(self, filename, **kwds): raise ValueError("allowed file extensions for images are " ".pdf, .png, .svg, .eps, .dvi!") + class TikzPicture(Standalone): r""" A TikzPicture embedded in a LaTeX standalone document class. @@ -1655,8 +1656,8 @@ def from_graph(cls, graph, merge_multiedges=True, from sage.graphs.graph import Graph graph = Graph(edges, format='list_of_edges', loops=loops) - options = dict(format='dot2tex', edge_labels=True, - color_by_label=False, prog='dot', rankdir='down') + options = {'format': 'dot2tex', 'edge_labels': True, + 'color_by_label': False, 'prog': 'dot', 'rankdir': 'down'} options.update(kwds) graph.latex_options().set_options(**options) @@ -1666,7 +1667,7 @@ def from_graph(cls, graph, merge_multiedges=True, @classmethod @experimental(issue_number=20343) def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, - merge_label_function=tuple): + merge_label_function=tuple): r""" Convert a graph with positions defined for vertices to a tikzpicture. diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index d25b24a10dc..79eda9dec43 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -1076,7 +1076,8 @@ def lazy_import(module, names, as_=None, *, ....: deprecation=14275) sage: my_Qp(5) # needs sage.rings.padics doctest:...: DeprecationWarning: - Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead. + Importing my_Qp from here is deprecated; + please use "from sage.rings.padics.factory import Qp as my_Qp" instead. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20 @@ -1096,10 +1097,12 @@ def lazy_import(module, names, as_=None, *, ....: feature=PythonModule('ppl', spkg='pplpy', type='standard')) sage: equation # needs pplpy - sage: lazy_import('PyNormaliz', 'NmzListConeProperties', feature=PythonModule('PyNormaliz', spkg='pynormaliz')) # optional - pynormaliz - sage: NmzListConeProperties # optional - pynormaliz + sage: lazy_import('PyNormaliz', 'NmzListConeProperties', + ....: feature=PythonModule('PyNormaliz', spkg='pynormaliz')) + sage: NmzListConeProperties # optional - pynormaliz - sage: lazy_import('foo', 'not_there', feature=PythonModule('foo', spkg='non-existing-package')) + sage: lazy_import('foo', 'not_there', + ....: feature=PythonModule('foo', spkg='non-existing-package')) sage: not_there Failed lazy import: foo is not available. diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 39a6ecffd14..cb049665766 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -52,16 +52,6 @@ lazy_import("sage.combinat.subset", ["powerset", "subsets", "uniq"], deprecation=35564) -lazy_import("sage.misc.call", ["AttrCallObject", "attrcall", "call_method"], - deprecation=29869) - -lazy_import("sage.misc.verbose", ["verbose", "set_verbose", "set_verbose_files", - "get_verbose_files", "unset_verbose_files", "get_verbose"], - deprecation=17815) - -lazy_import("sage.misc.repr", ["coeff_repr", "repr_lincomb"], - deprecation=29892) - lazy_import("sage.misc.timing", ["cputime", "GlobalCputime", "walltime"], deprecation=35816) @@ -71,44 +61,6 @@ # File and directory utilities ################################################################# - -def sage_makedirs(dirname, mode=0o777): - """ - Python version of ``mkdir -p``: try to create a directory, and also - create all intermediate directories as necessary. Succeed silently - if the directory already exists (unlike ``os.makedirs()``). - Raise other errors (like permission errors) normally. - - This function is deprecated; use ``os.makedirs(..., exist_ok=True)`` - instead. - - EXAMPLES:: - - sage: from sage.misc.misc import sage_makedirs - sage: sage_makedirs(DOT_SAGE) # no output - doctest:warning... - DeprecationWarning: sage_makedirs is deprecated; use os.makedirs(..., exist_ok=True) instead - See https://github.com/sagemath/sage/issues/32987 for details. - - The following fails because we are trying to create a directory in - place of an ordinary file:: - - sage: filename = tmp_filename() - sage: sage_makedirs(filename) - Traceback (most recent call last): - ... - FileExistsError: [Errno ...] File exists: ... - """ - from sage.misc.superseded import deprecation - deprecation(32987, - 'sage_makedirs is deprecated; use os.makedirs(..., exist_ok=True) instead') - try: - os.makedirs(dirname) - except OSError: - if not os.path.isdir(dirname): - raise - - # We create the DOT_SAGE directory (if it does not exist yet; note in particular # that it may already have been created by the bin/sage script) with # restrictive permissions, since otherwise possibly just anybody can easily see @@ -216,79 +168,6 @@ def try_read(obj, splitlines=False): return data -################################################# -# Next we create the Sage temporary directory. -################################################# - - -@lazy_string -def SAGE_TMP(): - """ - EXAMPLES:: - - sage: from sage.misc.misc import SAGE_TMP - sage: SAGE_TMP - doctest:warning... - DeprecationWarning: SAGE_TMP is deprecated; please use python's - "tempfile" module instead. - See https://github.com/sagemath/sage/issues/33213 for details. - l'.../temp/...' - - """ - from sage.misc.superseded import deprecation - deprecation(33213, "SAGE_TMP is deprecated; please use python's \"tempfile\" module instead.") - d = os.path.join(DOT_SAGE, 'temp', HOSTNAME, str(os.getpid())) - os.makedirs(d, exist_ok=True) - return d - - -@lazy_string -def ECL_TMP(): - """ - Temporary directory that should be used by ECL interfaces launched from - Sage. - - EXAMPLES:: - - sage: from sage.misc.misc import ECL_TMP - sage: ECL_TMP - doctest:warning... - DeprecationWarning: ECL_TMP is deprecated and is no longer used - by the ECL interface in sage - See https://github.com/sagemath/sage/issues/33213 for details. - ... - - """ - from sage.misc.superseded import deprecation - deprecation(33213, "ECL_TMP is deprecated and is no longer used by the ECL interface in sage") - import atexit - import tempfile - d = tempfile.TemporaryDirectory() - result = os.path.join(d.name, 'ecl') - atexit.register(lambda: d.cleanup()) - return result - - -@lazy_string -def SPYX_TMP(): - r""" - EXAMPLES:: - - sage: from sage.misc.misc import SPYX_TMP - sage: SPYX_TMP - doctest:warning... - DeprecationWarning: SPYX_TMP is deprecated; - use sage.misc.temporary_file.spyx_tmp instead - See https://github.com/sagemath/sage/issues/33213 for details. - ... - - """ - from sage.misc.temporary_file import spyx_tmp - from sage.misc.superseded import deprecation - deprecation(33213, "SPYX_TMP is deprecated; use sage.misc.temporary_file.spyx_tmp instead") - return spyx_tmp() - - SAGE_DB = os.path.join(DOT_SAGE, 'db') os.makedirs(SAGE_DB, exist_ok=True) @@ -299,41 +178,6 @@ def SPYX_TMP(): pass -def union(x, y=None): - """ - Return the union of x and y, as a list. The resulting list need not - be sorted and can change from call to call. - - INPUT: - - - - ``x`` - iterable - - - ``y`` - iterable (may optionally omitted) - - - OUTPUT: list - - EXAMPLES:: - - sage: answer = union([1,2,3,4], [5,6]); answer - doctest:...: DeprecationWarning: sage.misc.misc.union is deprecated... - See https://github.com/sagemath/sage/issues/32096 for details. - [1, 2, 3, 4, 5, 6] - sage: union([1,2,3,4,5,6], [5,6]) == answer - True - sage: union((1,2,3,4,5,6), [5,6]) == answer - True - sage: union((1,2,3,4,5,6), set([5,6])) == answer - True - """ - from sage.misc.superseded import deprecation - deprecation(32096, "sage.misc.misc.union is deprecated, use 'list(set(x).union(y))' or a more suitable replacement") - if y is None: - return list(set(x)) - return list(set(x).union(y)) - - def exactly_one_is_true(iterable): r""" Return whether exactly one element of ``iterable`` evaluates ``True``. @@ -581,7 +425,7 @@ def __mul__(self, right): r""" EXAMPLES:: - sage: # needs sage.modules + sage: # needs scipy sage.modules sage: A = matrix(RDF, 5, 5, 2) sage: b = vector(RDF, 5, range(5)) sage: v = A \ b diff --git a/src/sage/misc/object_multiplexer.py b/src/sage/misc/object_multiplexer.py index 00fd89d6652..ceaac1b3fb2 100644 --- a/src/sage/misc/object_multiplexer.py +++ b/src/sage/misc/object_multiplexer.py @@ -21,7 +21,7 @@ # **************************************************************************** -class MultiplexFunction(): +class MultiplexFunction: """ A simple wrapper object for functions that are called on a list of objects. @@ -49,15 +49,14 @@ def __call__(self, *args, **kwds): sage: f() ('1', '1/2') """ - l = [] - for child in self.multiplexer.children: - l.append(getattr(child, self.name)(*args, **kwds)) + l = [getattr(child, self.name)(*args, **kwds) + for child in self.multiplexer.children] if all(e is None for e in l): return None return tuple(l) -class Multiplex(): +class Multiplex: """ Object for a list of children such that function calls on this new object implies that the same function is called on all diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index cc7ac5fdfb2..a97ad023590 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -178,16 +178,16 @@ def pip_installed_packages(normalization=None): sage: # optional - sage_spkg sage: from sage.misc.package import pip_installed_packages sage: d = pip_installed_packages() - sage: 'scipy' in d or 'SciPy' in d + sage: 'scipy' in d or 'SciPy' in d # needs scipy + True + sage: 'beautifulsoup4' in d # needs beautifulsoup4 + True + sage: 'prompt-toolkit' in d or 'prompt_toolkit' in d # whether - or _ appears in the name depends on the setuptools version used for building the package True - sage: d['beautifulsoup4'] # optional - beautifulsoup4 - '...' - sage: d['prompt-toolkit'] - '...' sage: d = pip_installed_packages(normalization='spkg') sage: d['prompt_toolkit'] '...' - sage: d['scipy'] + sage: d['scipy'] # needs scipy '...' """ with open(os.devnull, 'w') as devnull: @@ -228,34 +228,6 @@ def is_installed(self) -> bool: """ return self.installed_version is not None - def __getitem__(self, key: Union[int, str]): - r""" - Only for backwards compatibility to allow dict-like access. - - TESTS:: - - sage: from sage.misc.package import PackageInfo - sage: package = PackageInfo("test_package") - sage: package["name"] - doctest:warning... - dict-like access is deprecated, use pkg.name instead of pkg['name'], for example - See https://github.com/sagemath/sage/issues/31013 for details. - 'test_package' - sage: package[0] - 'test_package' - """ - if isinstance(key, str): - from sage.misc.superseded import deprecation - - if key == "installed": - deprecation(31013, "dict-like access via 'installed' is deprecated, use method is_installed instead") - return self.is_installed() - else: - deprecation(31013, "dict-like access is deprecated, use pkg.name instead of pkg['name'], for example") - return self.__getattribute__(key) - else: - return tuple.__getitem__(self, key) - def list_packages(*pkg_types: str, pkg_sources: List[str] = ['normal', 'pip', 'script'], local: bool = False, ignore_URLError: bool = False, exclude_pip: bool = False) -> Dict[str, PackageInfo]: @@ -547,109 +519,6 @@ def package_versions(package_type, local=False): return {pkg.name: (pkg.installed_version, pkg.remote_version) for pkg in list_packages(package_type, local=local).values()} -def standard_packages(): - """ - Return two lists. The first contains the installed and the second - contains the not-installed standard packages that are available - from the Sage repository. - - OUTPUT: - - - installed standard packages (as a list) - - - NOT installed standard packages (as a list) - - Run ``sage -i package_name`` from a shell to install a given - package or ``sage -f package_name`` to re-install it. - - .. SEEALSO:: :func:`sage.misc.package.list_packages` - - EXAMPLES:: - - sage: from sage.misc.package import standard_packages - sage: installed, not_installed = standard_packages() # optional - sage_spkg - doctest:...: DeprecationWarning: ... - sage: 'numpy' in installed # optional - sage_spkg - True - """ - from sage.misc.superseded import deprecation - deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages ' - 'are deprecated, use sage.features instead') - pkgs = list_packages('standard', local=True).values() - return (sorted(pkg.name for pkg in pkgs if pkg.is_installed()), - sorted(pkg.name for pkg in pkgs if not pkg.is_installed())) - - -def optional_packages(): - """ - Return two lists. The first contains the installed and the second - contains the not-installed optional packages that are available - from the Sage repository. - - OUTPUT: - - - installed optional packages (as a list) - - - NOT installed optional packages (as a list) - - Run ``sage -i package_name`` from a shell to install a given - package or ``sage -f package_name`` to re-install it. - - .. SEEALSO:: :func:`sage.misc.package.list_packages` - - EXAMPLES:: - - sage: # optional - sage_spkg - sage: from sage.misc.package import optional_packages - sage: installed, not_installed = optional_packages() - doctest:...: DeprecationWarning: ... - sage: 'biopython' in installed + not_installed - True - sage: 'biopython' in installed # optional - biopython - True - """ - from sage.misc.superseded import deprecation - deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages ' - 'are deprecated, use sage.features instead') - pkgs = list_packages('optional', local=True) - pkgs = pkgs.values() - return (sorted(pkg.name for pkg in pkgs if pkg.is_installed()), - sorted(pkg.name for pkg in pkgs if not pkg.is_installed())) - - -def experimental_packages(): - """ - Return two lists. The first contains the installed and the second - contains the not-installed experimental packages that are available - from the Sage repository. - - OUTPUT: - - - installed experimental packages (as a list) - - - NOT installed experimental packages (as a list) - - Run ``sage -i package_name`` from a shell to install a given - package or ``sage -f package_name`` to re-install it. - - .. SEEALSO:: :func:`sage.misc.package.list_packages` - - EXAMPLES:: - - sage: from sage.misc.package import experimental_packages - sage: installed, not_installed = experimental_packages() # optional - sage_spkg - doctest:...: DeprecationWarning: ... - """ - from sage.misc.superseded import deprecation - deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages ' - 'are deprecated, use sage.features instead') - pkgs = list_packages('experimental', local=True).values() - return (sorted(pkg.name for pkg in pkgs if pkg.is_installed()), - sorted(pkg.name for pkg in pkgs if not pkg.is_installed())) - def package_manifest(package): """ Return the manifest for ``package``. @@ -691,68 +560,10 @@ def package_manifest(package): pass raise RuntimeError('package manifest directory changed at runtime') -class PackageNotFoundError(RuntimeError): - """ - This class defines the exception that should be raised when a - function, method, or class cannot detect a Sage package that it - depends on. - This exception should be raised with a single argument, namely - the name of the package. - - When a ``PackageNotFoundError`` is raised, this means one of the - following: - - - The required optional package is not installed. - - - The required optional package is installed, but the relevant - interface to that package is unable to detect the package. - - Raising a ``PackageNotFoundError`` is deprecated. Use - :class:`sage.features.FeatureNotPresentError` instead. - - User code can continue to catch ``PackageNotFoundError`` exceptions - for compatibility with older versions of the Sage library. - This does not cause deprecation warnings. - - EXAMPLES:: - - sage: from sage.misc.package import PackageNotFoundError - sage: try: - ....: pass - ....: except PackageNotFoundError: - ....: pass - - """ - - def __init__(self, *args): - """ - TESTS:: - - sage: from sage.misc.package import PackageNotFoundError - sage: raise PackageNotFoundError("my_package") - Traceback (most recent call last): - ... - PackageNotFoundError: the package 'my_package' was not found. You can install it by running 'sage -i my_package' in a shell - """ - super().__init__(*args) - # We do not deprecate the whole class because we want - # to allow user code to handle this exception without causing - # a deprecation warning. - from sage.misc.superseded import deprecation - deprecation(30607, "Instead of raising PackageNotFoundError, raise sage.features.FeatureNotPresentError") - - def __str__(self): - """ - Return the actual error message. - - EXAMPLES:: - - sage: from sage.misc.package import PackageNotFoundError - sage: str(PackageNotFoundError("my_package")) - doctest:warning... - "the package 'my_package' was not found. You can install it by running 'sage -i my_package' in a shell" - """ - return ("the package {0!r} was not found. " - "You can install it by running 'sage -i {0}' in a shell" - .format(self.args[0])) +# PackageNotFoundError used to be an exception class. +# It was deprecated in #30607 and removed afterwards. +# User code can continue to use PackageNotFoundError in +# try...except statements using this definition, which +# catches no exception. +PackageNotFoundError = () diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 16c78d18462..71ec08b3ae0 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -111,6 +111,7 @@ def read_distribution(src_file): EXAMPLES:: + sage: # needs SAGE_SRC sage: from sage.env import SAGE_SRC sage: from sage.misc.package_dir import read_distribution sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'tdlib.pyx')) @@ -198,7 +199,7 @@ def update_distribution(src_file, distribution, *, verbose=False): distribution = '' directive = 'sage_setup: ' f'distribution = {distribution}'.rstrip() try: - with open(src_file, 'r') as f: + with open(src_file) as f: src_lines = f.read().splitlines() except UnicodeDecodeError: # Silently skip binary files @@ -565,7 +566,7 @@ def handle_file(root, file): distribution_underscore = distribution.replace('-', '_') try: with open(os.path.join(distribution_dir, - f'{distribution_underscore}.egg-info', 'SOURCES.txt'), "r") as f: + f'{distribution_underscore}.egg-info', 'SOURCES.txt')) as f: paths.extend(os.path.join(SAGE_ROOT, 'src', line.strip()) for line in f if line.startswith('sage/')) @@ -614,7 +615,7 @@ def handle_file(root, file): if package in ordinary_packages: pass elif ((missing_all_files := distributions_per_directives - package_distributions_per_all_files[package]) - and not (missing_all_files == set(['']) and len(distributions_per_directives) < 2)): + and not (missing_all_files == {''} and len(distributions_per_directives) < 2)): s = '' if len(missing_all_files) == 1 else 's' print(f'{package}: missing file{s} ' + ', '.join(_all_filename(distribution) for distribution in missing_all_files)) diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py index 117b7f6892b..2911f72b626 100644 --- a/src/sage/misc/replace_dot_all.py +++ b/src/sage/misc/replace_dot_all.py @@ -111,8 +111,9 @@ def find_replacements(location, package_regex=None, verbose=False): EXAMPLES:: + sage: # needs SAGE_SRC sage: from sage.misc.replace_dot_all import * - sage: location = os.path.join(sage.env.SAGE_SRC, 'sage/plot/arc.py') + sage: location = os.path.join(sage.env.SAGE_SRC, 'sage', 'plot', 'arc.py') sage: find_replacements(location, package_regex='sage[.]plot[.]all', verbose=True) [[..., ..., 'from sage.plot.graphics import Graphics']] """ @@ -122,7 +123,7 @@ def find_replacements(location, package_regex=None, verbose=False): pattern = re.compile(regex) replacements = [] global log_messages, interesting_examples - with open(location, "r") as fp: + with open(location) as fp: skip_line = False lines = fp.readlines() # read all lines using readline() row_index = 0 @@ -295,8 +296,9 @@ def process_line(location, line, replacements, row_index, verbose=False): Replacing the first line which needs a replacement in the source file with filepath ``src/sage/plot/arc.py``:: + sage: # needs SAGE_SRC sage: from sage.misc.replace_dot_all import * - sage: location = os.path.join(sage.env.SAGE_SRC, 'sage/plot/arc.py') + sage: location = os.path.join(sage.env.SAGE_SRC, 'sage', 'plot', 'arc.py') sage: replacements = find_replacements(location, package_regex='sage[.]plot[.]all', verbose=True); replacements [[477, 24, 'from sage.plot.graphics import Graphics']] sage: with open(location, "r") as file: @@ -366,7 +368,7 @@ def make_replacements_in_file(location, package_regex=None, verbose=False, outpu from sage.plot.line import line """ replacements = find_replacements(location, package_regex, verbose) - with open(location, "r") as file: + with open(location) as file: lines = file.readlines() replaced_content = "" row_index = 0 # keeps track of the line number @@ -401,6 +403,7 @@ def walkdir_replace_dot_all(dir, file_regex=r'.*[.](py|pyx|pxi)$', package_regex EXAMPLES:: + sage: # needs SAGE_SRC sage: from sage.misc.replace_dot_all import * sage: walkdir_replace_dot_all(os.path.join(sage.env.SAGE_SRC, 'sage')) # not tested """ diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index be38a27d683..fcb271a7f75 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -1207,7 +1207,7 @@ def result(self, e): _prec_atomic = 42 -class SageInputExpression(): +class SageInputExpression: r""" Subclasses of this class represent expressions for :func:`sage_input`. \sage classes should define a \method{_sage_input_} method, which diff --git a/src/sage/misc/sage_timeit.py b/src/sage/misc/sage_timeit.py index 89c813e563b..b9a6d944795 100644 --- a/src/sage/misc/sage_timeit.py +++ b/src/sage/misc/sage_timeit.py @@ -17,7 +17,7 @@ """ -class SageTimeitResult(): +class SageTimeitResult: r""" Represent the statistics of a timeit() command. diff --git a/src/sage/misc/sage_unittest.py b/src/sage/misc/sage_unittest.py index 9d3ce5db1e8..21298f2f6e0 100644 --- a/src/sage/misc/sage_unittest.py +++ b/src/sage/misc/sage_unittest.py @@ -17,7 +17,7 @@ import traceback -class TestSuite(): +class TestSuite: """ Test suites for Sage objects. @@ -576,7 +576,7 @@ def some_elements(self, S=None, repeat=None): return list(some_tuples(S, repeat, self._max_runs, self._max_samples)) -class PythonObjectWithTests(): +class PythonObjectWithTests: """ Utility class for running basis tests on a plain Python object (that is not in SageObject). More test methods can be added here. diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 308fa7d8340..68a2af4625d 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -640,7 +640,7 @@ def format(s, embedded=False): r"""noreplace Format Sage documentation ``s`` for viewing with IPython. - This calls ``detex`` on ``s`` to convert LaTeX commands to plain + This calls :func:`detex` on ``s`` to convert LaTeX commands to plain text, unless the directive ``nodetex`` is given in the first line of the string. @@ -654,13 +654,13 @@ def format(s, embedded=False): INPUT: - - ``s`` - string - - ``embedded`` - boolean (optional, default False) + - ``s`` -- string + - ``embedded`` -- boolean (optional, default ``False``) OUTPUT: string - Set ``embedded`` equal to True if formatting for use in the - notebook; this just gets passed as an argument to ``detex``. + Set ``embedded`` equal to ``True`` if formatting for use in the + notebook; this just gets passed as an argument to :func:`detex`. .. SEEALSO:: @@ -776,7 +776,7 @@ def format(s, embedded=False): except ImportError: pass - docs = set([]) + docs = set() if 'noreplace' not in directives: i_0 = 0 while True: @@ -843,7 +843,7 @@ def format_src(s): """ if not isinstance(s, str): raise TypeError("s must be a string") - docs = set([]) + docs = set() try: import sage.all @@ -1158,7 +1158,8 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0 True - sage: s = search_src('MatRiX', path_re='matrix', interact=False, ignore_case=False); s.find('x') > 0 + sage: s = search_src('MatRiX', path_re='matrix', + ....: interact=False, ignore_case=False); s.find('x') > 0 False Searches are by default restricted to single lines, but this can @@ -1170,7 +1171,8 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: len(search_src('log', 'derivative', interact=False).splitlines()) < 40 True - sage: len(search_src('log', 'derivative', interact=False, multiline=True).splitlines()) > 70 + sage: len(search_src('log', 'derivative', + ....: interact=False, multiline=True).splitlines()) > 70 True A little recursive narcissism: let's do a doctest that searches for @@ -1372,7 +1374,7 @@ def format_search_as_html(what, results, search): if not isinstance(results, list): results = results.splitlines() - files = set([]) + files = set() for L in results: filename = L.strip().split(':', 1)[0] if filename: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index f79aeafb937..d370789a67f 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -167,28 +167,6 @@ def is_function_or_cython_function(obj): return hasattr(type(obj), "__code__") -def loadable_module_extension(): - r""" - Return the filename extension of loadable modules, including the dot. - - This function is deprecated. - - EXAMPLES:: - - sage: from sage.misc.sageinspect import loadable_module_extension - sage: from importlib.machinery import EXTENSION_SUFFIXES - sage: loadable_module_extension() in EXTENSION_SUFFIXES - doctest:warning... - DeprecationWarning: loadable_module_extension is deprecated; use importlib.machinery.EXTENSION_SUFFIXES instead - See https://github.com/sagemath/sage/issues/33636 for details. - True - """ - from sage.misc.superseded import deprecation - deprecation(33636, "loadable_module_extension is deprecated; use importlib.machinery.EXTENSION_SUFFIXES instead") - # Return the full platform-specific extension module suffix - return import_machinery.EXTENSION_SUFFIXES[0] - - def isclassinstance(obj): r""" Check if argument is instance of non built-in class @@ -212,7 +190,7 @@ def isclassinstance(obj): sage: isclassinstance(myclass2) False """ - builtin_mods = set(['__builtin__', 'builtins', 'exceptions']) + builtin_mods = {'__builtin__', 'builtins', 'exceptions'} return (not inspect.isclass(obj) and hasattr(obj, '__class__') and @@ -625,10 +603,7 @@ def visit_List(self, node): [[], ['s', 't', 'u'], [['e'], [], ['pi']]] """ - t = [] - for n in node.elts: - t.append(self.visit(n)) - return t + return [self.visit(n) for n in node.elts] def visit_Tuple(self, node): """ @@ -649,10 +624,7 @@ def visit_Tuple(self, node): [(), ('x', 'y'), ('Au', 'Al', 'Cu')] """ - t = [] - for n in node.elts: - t.append(self.visit(n)) - return tuple(t) + return tuple(self.visit(n) for n in node.elts) def visit_Dict(self, node): """ @@ -1572,11 +1544,14 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return ....: cpdef meet(categories, bint as_list = False, tuple ignore_axioms=(), tuple axioms=()): pass ....: ''') sage: sage_getargspec(Foo.join) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.join) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.meet) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) Test that :issue:`17009` is fixed:: @@ -1589,7 +1564,8 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return sage: from sage.misc.nested_class import MainClass sage: sage_getargspec(MainClass.NestedClass.NestedSubClass.dummy) - FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', + defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) In :issue:`18249` was decided to return a generic signature for Python builtin functions, rather than to raise an error (which is what Python's @@ -2335,13 +2311,13 @@ def sage_getsourcelines(obj): (, ) sage: print(sage_getsource(E)) - class Element(): + class Element: "This is a dummy element class" pass sage: print(sage_getsource(P)) class TestNestedParent(UniqueRepresentation, Parent): ... - class Element(): + class Element: "This is a dummy element class" pass diff --git a/src/sage/misc/sphinxify.py b/src/sage/misc/sphinxify.py index 2fb6e95395c..8d6638eba8b 100644 --- a/src/sage/misc/sphinxify.py +++ b/src/sage/misc/sphinxify.py @@ -127,7 +127,7 @@ def sphinxify(docstring, format='html'): builtins.__dict__.pop('_', None) if os.path.exists(output_name): - with open(output_name, 'r') as f: + with open(output_name) as f: output = f.read() output = output.replace('
', '
')
 
diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py
index 4c4cddddc5c..f646c24e485 100644
--- a/src/sage/misc/superseded.py
+++ b/src/sage/misc/superseded.py
@@ -38,8 +38,8 @@ def _check_issue_number(issue_number):
 
     OUTPUT:
 
-    This function returns nothing. A ``ValueError`` or ``TypeError`` is
-    raised if the argument cannot be a valid issue number.
+    This function returns nothing. A :class:`ValueError` or :class:`TypeError`
+    is raised if the argument cannot be a valid issue number.
 
     EXAMPLES::
 
@@ -212,7 +212,7 @@ def experimental_warning(issue_number, message, stacklevel=4):
     warning(issue_number, message, FutureWarning, stacklevel)
 
 
-class experimental():
+class experimental:
     def __init__(self, issue_number, stacklevel=4):
         """
         A decorator which warns about the experimental/unstable status of
@@ -318,7 +318,7 @@ def wrapper(*args, **kwds):
         return wrapper
 
 
-class __experimental_self_test():
+class __experimental_self_test:
     r"""
     This is a class only to demonstrate with a doc-test that the @experimental
     decorator only issues a warning message once (see :issue:`20601`).
@@ -339,7 +339,7 @@ def __init__(self, x):
         print("I'm " + x)
 
 
-class DeprecatedFunctionAlias():
+class DeprecatedFunctionAlias:
     """
     A wrapper around methods or functions which automatically prints a
     deprecation message. See :func:`deprecated_function_alias`.
diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py
index daf3cbac8e7..d761fbd4cc3 100644
--- a/src/sage/misc/temporary_file.py
+++ b/src/sage/misc/temporary_file.py
@@ -134,7 +134,7 @@ def tmp_filename(name="tmp_", ext=""):
 #################################################################
 # write to a temporary file and move it in place
 #################################################################
-class atomic_write():
+class atomic_write:
     """
     Write to a given file using a temporary file and then rename it
     to the target file. This renaming should be atomic on modern
@@ -413,7 +413,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
 #################################################################
 # write to a temporary directory and move it in place
 #################################################################
-class atomic_dir():
+class atomic_dir:
     """
     Write to a given directory using a temporary directory and then rename it
     to the target directory. This is for creating a directory whose contents
diff --git a/src/sage/misc/test_class_pickling.py b/src/sage/misc/test_class_pickling.py
index f9c8003fa4a..f1074af85cb 100644
--- a/src/sage/misc/test_class_pickling.py
+++ b/src/sage/misc/test_class_pickling.py
@@ -29,7 +29,7 @@ def metaclass(name, bases):
 
     """
     print("constructing class")
-    result = Metaclass(name, bases, dict())
+    result = Metaclass(name, bases, {})
     result.reduce_args = (name, bases)
     return result
 
diff --git a/src/sage/misc/test_nested_class.py b/src/sage/misc/test_nested_class.py
index 40712bfc9fb..918acc30e11 100644
--- a/src/sage/misc/test_nested_class.py
+++ b/src/sage/misc/test_nested_class.py
@@ -150,15 +150,15 @@ class Element(ElementWrapper):
 
 
 # Class for tests:
-class B():
+class B:
     """
     A normal external class.
     """
     pass
 
 
-class ABB():
-    class B():
+class ABB:
+    class B:
         """
         This class is broken and can't be pickled.
         A warning is emmited during compilation.
@@ -166,18 +166,18 @@ class B():
         pass
 
 
-class ABL():
+class ABL:
     """
     There is no problem here.
     """
     B = B
 
 
-class ALB():
+class ALB:
     """
     There is a nested class just below. Which can't be properly sphinxed.
     """
-    class C():
+    class C:
         """
         Internal C class.
 
@@ -191,7 +191,7 @@ class C():
 
 
 class ABBMeta(metaclass=NestedClassMetaclass):
-    class B():
+    class B:
         """
         B interne
         """
@@ -206,7 +206,7 @@ class ALBMeta(metaclass=NestedClassMetaclass):
     """
     There is a nested class just below which is properly sphinxed.
     """
-    class CMeta():
+    class CMeta:
         """
         B interne
         """
@@ -223,6 +223,6 @@ class TestNestedParent(UniqueRepresentation, Parent):
     See the test in ``sage.misc.sageinspect.sage_getsourcelines``.
     """
 
-    class Element():
+    class Element:
         "This is a dummy element class"
         pass
diff --git a/src/sage/misc/timing.py b/src/sage/misc/timing.py
index 5e5c49b9594..c66c8521121 100644
--- a/src/sage/misc/timing.py
+++ b/src/sage/misc/timing.py
@@ -138,7 +138,7 @@ class GlobalCputime:
         sage: t = cputime(subprocesses=True)
         sage: P = PolynomialRing(QQ,7,'x')
         sage: I = sage.rings.ideal.Katsura(P)                                           # needs sage.libs.singular
-        sage: gb = I.groebner_basis() # calls Singular                                  # needs sage.libs.singular
+        sage: gb = I.groebner_basis()  # calls Singular                                 # needs sage.libs.singular
         sage: cputime(subprocesses=True) - t    # output random
         0.462987
 
@@ -184,7 +184,7 @@ def __add__(self, other):
             sage: t = cputime(subprocesses=True)
             sage: P = PolynomialRing(QQ,7,'x')
             sage: I = sage.rings.ideal.Katsura(P)                                       # needs sage.libs.singular
-            sage: gb = I.groebner_basis() # calls Singular                              # needs sage.libs.singular
+            sage: gb = I.groebner_basis()  # calls Singular                             # needs sage.libs.singular
             sage: cputime(subprocesses=True) + t # output random
             2.798708
         """
@@ -200,7 +200,7 @@ def __sub__(self, other):
             sage: t = cputime(subprocesses=True)
             sage: P = PolynomialRing(QQ,7,'x')
             sage: I = sage.rings.ideal.Katsura(P)                                       # needs sage.libs.singular
-            sage: gb = I.groebner_basis() # calls Singular                              # needs sage.libs.singular
+            sage: gb = I.groebner_basis()  # calls Singular                             # needs sage.libs.singular
             sage: cputime(subprocesses=True) - t # output random
             0.462987
         """
diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py
index 3fd60c06165..871275fdced 100644
--- a/src/sage/modular/arithgroup/congroup_generic.py
+++ b/src/sage/modular/arithgroup/congroup_generic.py
@@ -89,9 +89,9 @@ def CongruenceSubgroup_constructor(*args):
         TypeError: Ring of definition must be Z / NZ for some N
     """
     from sage.groups.matrix_gps.finitely_generated import MatrixGroup
-    from sage.groups.matrix_gps.matrix_group import is_MatrixGroup
+    from sage.groups.matrix_gps.matrix_group import MatrixGroup_base
 
-    if is_MatrixGroup(args[0]):
+    if isinstance(args[0], MatrixGroup_base):
         G = args[0]
 
     elif isinstance(args[0], list):
diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx
index 61c2446a16b..c9cc8059c65 100644
--- a/src/sage/modular/arithgroup/farey_symbol.pyx
+++ b/src/sage/modular/arithgroup/farey_symbol.pyx
@@ -897,7 +897,6 @@ cdef class Farey:
         sig_off()
         return result
 
-    @rename_keyword(rgbcolor='color')
     @options(alpha=1, fill=True, thickness=1, color='lightgray',
              color_even='white',
              zorder=2, linestyle='solid', show_pairing=True,
diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py
index 2f314e5d3ef..7253cf6c960 100644
--- a/src/sage/modular/hecke/algebra.py
+++ b/src/sage/modular/hecke/algebra.py
@@ -179,7 +179,8 @@ def __init__(self, M) -> None:
             M = M[0]
         from . import module
         if not module.is_HeckeModule(M):
-            raise TypeError("M (=%s) must be a HeckeModule" % M)
+            msg = f"M (={M}) must be a HeckeModule"
+            raise TypeError(msg)
         self.__M = M
         cat = Algebras(M.base_ring()).Commutative()
         CommutativeRing.__init__(self, base_ring=M.base_ring(),
@@ -198,7 +199,7 @@ def _an_element_(self):
         """
         return self.hecke_operator(self.level() + 1)
 
-    def __call__(self, x, check=True):
+    def _element_constructor_(self, x, check=True):
         r"""
         Convert x into an element of this Hecke algebra. Here x is either:
 
@@ -230,7 +231,7 @@ def __call__(self, x, check=True):
             sage: T.gen(2).matrix() in T
             Traceback (most recent call last):
             ...
-            NotImplementedError: Membership testing for '...' not implemented
+            NotImplementedError: membership testing for '...' not implemented
             sage: T(T.gen(2).matrix(), check=False)
             Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by:
             [ 3  0 -1]
@@ -245,49 +246,52 @@ def __call__(self, x, check=True):
             TypeError: Don't know how to construct an element of Anemic Hecke algebra acting on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field from Hecke operator T_11 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field
 
         """
-        from . import hecke_operator
-        try:
-            if not isinstance(x, Element):
-                x = self.base_ring()(x)
-            if x.parent() is self:
-                return x
-            elif hecke_operator.is_HeckeOperator(x):
-                if x.parent() == self \
-                        or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()) \
-                        or (self.is_anemic() and x.parent().anemic_subalgebra() == self and gcd(x.index(), self.level()) == 1):
-                    return hecke_operator.HeckeOperator(self, x.index())
-                else:
-                    raise TypeError
-            elif hecke_operator.is_HeckeAlgebraElement(x):
-                if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
-                    if x.parent().module().basis_matrix() == self.module().basis_matrix():
-                        return hecke_operator.HeckeAlgebraElement_matrix(self, x.matrix())
-                    else:
-                        A = matrix([self.module().coordinate_vector(x.parent().module().gen(i))
-                                    for i in range(x.parent().module().rank())])
-                        return hecke_operator.HeckeAlgebraElement_matrix(self, ~A * x.matrix() * A)
-                elif x.parent() == self.anemic_subalgebra():
-                    pass
+        from .hecke_operator import HeckeAlgebraElement_matrix, HeckeOperator, is_HeckeOperator, is_HeckeAlgebraElement
+
+        if not isinstance(x, Element):
+            x = self.base_ring()(x)
+
+        parent = x.parent()
+
+        if parent is self.base_ring():
+            return HeckeAlgebraElement_matrix(self, x * self.matrix_space().one())
 
+        if parent is self:
+            return x
+
+        if is_HeckeOperator(x):
+            if x.parent() == self \
+                    or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()) \
+                    or (self.is_anemic() and x.parent().anemic_subalgebra() == self and gcd(x.index(), self.level()) == 1):
+                return HeckeOperator(self, x.index())
+
+        if is_HeckeAlgebraElement(x):
+            if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
+                if x.parent().module().basis_matrix() == self.module().basis_matrix():
+                    return HeckeAlgebraElement_matrix(self, x.matrix())
                 else:
-                    raise TypeError
-            else:
-                A = self.matrix_space()(x)
-                if check:
-                    if not A.is_scalar():
-                        raise NotImplementedError("Membership testing for '%s' not implemented" % self)
-                return hecke_operator.HeckeAlgebraElement_matrix(self, A)
+                    A = matrix([self.module().coordinate_vector(x.parent().module().gen(i))
+                                for i in range(x.parent().module().rank())])
+                    return HeckeAlgebraElement_matrix(self, ~A * x.matrix() * A)
 
+        try:
+            A = self.matrix_space()(x)
+            if check:
+                if not A.is_scalar():
+                    msg = f"membership testing for '{self}' not implemented"
+                    raise NotImplementedError(msg)
+            return HeckeAlgebraElement_matrix(self, A)
         except TypeError:
             raise TypeError("Don't know how to construct an element of %s from %s" % (self, x))
 
-    def _coerce_impl(self, x):
-        r"""
-        Implicit coercion of x into this Hecke algebra. The only things that
-        coerce implicitly into self are: elements of Hecke algebras which are
-        equal to self, or to the anemic subalgebra of self if self is not
-        anemic; and elements that coerce into the base ring of self.  Bare
-        matrices do *not* coerce implicitly into self.
+    def _coerce_map_from_(self, R):
+        """
+        Coercion of a parent ``R`` into this Hecke algebra.
+
+        The parents that coerce into ``self`` are: Hecke
+        algebras which are equal to ``self``, or to the anemic subalgebra
+        of ``self`` if ``self`` is not anemic; and parents that coerce into
+        the base ring of ``self``.
 
         EXAMPLES::
 
@@ -297,9 +301,11 @@ def _coerce_impl(self, x):
             sage: F.coerce(A.2) # indirect doctest
             Hecke operator T_2 on Cuspidal subspace of dimension 3 of Modular Forms space of dimension 5 for Congruence Subgroup Gamma0(3) of weight 12 over Rational Field
         """
-        if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
-            return self(x)
-        return self(self.matrix_space()(1) * self.base_ring().coerce(x))
+        if R == self:
+            return True
+        if not self.is_anemic() and R == self.anemic_subalgebra():
+            return True
+        return self.base_ring().has_coerce_map_from(R)
 
     def gen(self, n):
         """
@@ -316,7 +322,9 @@ def gen(self, n):
     def ngens(self):
         r"""
         The size of the set of generators returned by gens(), which is clearly
-        infinity. (This is not necessarily a minimal set of generators.)
+        infinity.
+
+        (This is not necessarily a minimal set of generators.)
 
         EXAMPLES::
 
@@ -325,6 +333,23 @@ def ngens(self):
         """
         return infinity
 
+    def one(self):
+        """
+        Return the unit of the Hecke algebra.
+
+        EXAMPLES::
+
+            sage: M = ModularSymbols(11,2,1)
+            sage: H = M.hecke_algebra()
+            sage: H.one()
+            Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
+            [1 0]
+            [0 1]
+        """
+        from .hecke_operator import HeckeAlgebraElement_matrix
+        A = self.matrix_space()
+        return HeckeAlgebraElement_matrix(self, A.one())
+
     def is_noetherian(self) -> bool:
         """
         Return ``True`` if this Hecke algebra is Noetherian as a ring.
diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx
index b8c39aeefa7..3be8e4dd545 100644
--- a/src/sage/modular/hypergeometric_misc.pyx
+++ b/src/sage/modular/hypergeometric_misc.pyx
@@ -4,7 +4,7 @@ significantly from Cythonization.
 """
 from cpython cimport array
 from cysignals.signals cimport sig_check
-
+from sage.rings.integer cimport Integer
 
 cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
                  gtable, int gtable_prec, bint use_longs):
@@ -37,7 +37,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
 
     cdef int gl, j, k, l, v, gv
     cdef long long i, q1, w, w1, w2, q2, r, r1
-    cdef bint flip
+    cdef bint flip, use_longlongs
 
     q1 = p ** f - 1
     gl = len(gamma)
@@ -55,6 +55,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
     # for efficiency.
     flip = (f == 1 and prec == 1 and gtable_prec == 1)
     ans = []
+    Rz = R.zero()
     if use_longs:
         q2 = p ** prec
         try:
@@ -64,13 +65,15 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
             for r in range(q1):
                 gtab2[r] = gtable[r].lift() % q2
     else:
-        gtab2 = array.array('q', [0]) * q1
-        try:
-            for r in range(q1):
-                gtab2[r] = gtable[r]
-        except TypeError:
-            for r in range(q1):
-                gtab2[r] = gtable[r].lift()
+        use_longlongs = (Integer(p) ** prec < 2 ** 63)
+        if use_longlongs:
+            gtab2 = array.array('q', [0]) * q1
+            try:
+                for r in range(q1):
+                    gtab2[r] = gtable[r]
+            except TypeError:
+                for r in range(q1):
+                    gtab2[r] = gtable[r].lift()
     if f == 1:
         for r in range(q1):
             digit_count[r] = r
@@ -82,7 +85,6 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
                 w += r1 % p
                 r1 //= p
             digit_count[r] = w
-    Rz = R.zero()
     ans = [None] * q1
     for r in range(q1):
         sig_check()
@@ -123,7 +125,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
                     else:
                         for j in range(-gv):
                             w1 = w1 * w2 % q2
-                else:
+                elif use_longlongs:
                     w2 = gtab2[r1]
                     if gv > 0:
                         for j in range(gv):
@@ -131,6 +133,13 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D,
                     else:
                         for j in range(-gv):
                             u1 *= w2
+                else:
+                    if gv > 0:
+                        for j in range(gv):
+                            u *= gtable[r1]
+                    else:
+                        for j in range(-gv):
+                            u1 *= gtable[r1]
             if use_longs:
                 u = R(w)
                 u1 = R(w1)
diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py
index bcb719fef9b..d79307c7aa4 100644
--- a/src/sage/modular/hypergeometric_motive.py
+++ b/src/sage/modular/hypergeometric_motive.py
@@ -7,7 +7,7 @@
 (E.g., see [BeukersHeckman]_)
 
 The computation of Euler factors is currently only supported for primes `p`
-of good reduction. That is, it is required that `v_p(t) = v_p(t-1) = 0`.
+of good or tame reduction.
 
 AUTHORS:
 
@@ -50,8 +50,8 @@
 
 """
 # ****************************************************************************
-#       Copyright (C) 2017     Frédéric Chapoton
-#                              Kiran S. Kedlaya 
+#       Copyright (C) 2017--2024     Frédéric Chapoton
+#                                    Kiran S. Kedlaya 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
 #  as published by the Free Software Foundation; either version 2 of
@@ -62,8 +62,7 @@
 
 from collections import defaultdict
 from itertools import combinations
-
-from sage.arith.misc import divisors, gcd, euler_phi, moebius, is_prime
+from sage.arith.misc import divisors, gcd, euler_phi, is_prime, moebius
 from sage.arith.misc import gauss_sum, kronecker_symbol
 from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
 from sage.functions.generalized import sgn
@@ -80,6 +79,7 @@
 from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
 from sage.rings.fraction_field import FractionField
 from sage.rings.integer_ring import ZZ
+from sage.rings.padics.factory import Zp
 from sage.rings.padics.padic_generic_element import gauss_table
 from sage.rings.polynomial.polynomial_ring import polygen, polygens
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
@@ -89,7 +89,7 @@
 from sage.schemes.generic.spec import Spec
 
 
-def characteristic_polynomial_from_traces(traces, d, q, i, sign):
+def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_fe=True):
     r"""
     Given a sequence of traces `t_1, \dots, t_k`, return the
     corresponding characteristic polynomial with Weil numbers as roots.
@@ -115,10 +115,18 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
 
     - ``sign`` -- integer, the sign
 
+    - ``deg`` -- an integer or ``None``
+
+    - ``use_fe`` -- a boolean (default: ``True``)
+
     OUTPUT:
 
     a polynomial
 
+    If ``deg`` is specified, only the coefficients up to this degree (inclusive) are computed.
+
+    If ``use_fe`` is ``False``, we ignore the local functional equation.
+
     EXAMPLES::
 
         sage: from sage.modular.hypergeometric_motive import characteristic_polynomial_from_traces
@@ -137,15 +145,20 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
         sage: characteristic_polynomial_from_traces([12], 3, 13, 2, -1)
         -2197*T^3 + 156*T^2 - 12*T + 1
 
-        sage: characteristic_polynomial_from_traces([36,7620], 4, 17, 3, 1)
+        sage: characteristic_polynomial_from_traces([36, 7620], 4, 17, 3, 1)
         24137569*T^4 - 176868*T^3 - 3162*T^2 - 36*T + 1
-        sage: characteristic_polynomial_from_traces([-4,276], 4, 5, 3, 1)
+        sage: characteristic_polynomial_from_traces([-4, 276], 4, 5, 3, 1)
         15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1
-        sage: characteristic_polynomial_from_traces([4,-276], 4, 5, 3, 1)
+        sage: characteristic_polynomial_from_traces([4, -276], 4, 5, 3, 1)
         15625*T^4 - 500*T^3 + 146*T^2 - 4*T + 1
         sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1)
         -923521*T^4 + 21142*T^3 - 22*T + 1
 
+        sage: characteristic_polynomial_from_traces([22], 4, 31, 2, -1, deg=1)
+        -22*T + 1
+        sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1, deg=4)
+        -923521*T^4 + 21142*T^3 - 22*T + 1
+
     TESTS::
 
         sage: characteristic_polynomial_from_traces([-36], 4, 17, 3, 1)
@@ -153,26 +166,30 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
         ...
         ValueError: not enough traces were given
     """
-    if len(traces) < d // 2:
+    if use_fe:
+        bound = d // 2 if deg is None else min(d // 2, deg)
+    else:
+        bound = d if deg is None else min(d, deg)
+    if len(traces) < bound:
         raise ValueError('not enough traces were given')
-    if i % 2 and d % 2:
+    if i % 2 and d % 2 and use_fe:
         raise ValueError('i and d may not both be odd')
     t = PowerSeriesRing(QQ, 't').gen()
     ring = PolynomialRing(ZZ, 'T')
 
     series = sum(- api * t**(i + 1) / (i + 1) for i, api in enumerate(traces))
-    series = series.O(d // 2 + 1).exp()
+    series = series.O(bound + 1).exp()
     coeffs = list(series)
-    coeffs += [0] * max(0, d // 2 + 1 - len(coeffs))
+    coeffs += [0] * max(0, bound + 1 - len(coeffs))
 
-    data = [0 for _ in range(d + 1)]
-    for k in range(d // 2 + 1):
+    fulldeg = d if deg is None else deg
+    data = [0] * (fulldeg + 1)
+    for k in range(bound + 1):
         data[k] = coeffs[k]
-    for k in range(d // 2 + 1, d + 1):
+    for k in range(bound + 1, fulldeg + 1):
         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).
@@ -247,9 +264,9 @@ def cyclotomic_to_alpha(cyclo) -> list:
         [1/2]
         sage: cyclotomic_to_alpha([5])
         [1/5, 2/5, 3/5, 4/5]
-        sage: cyclotomic_to_alpha([1,2,3,6])
+        sage: cyclotomic_to_alpha([1, 2, 3, 6])
         [0, 1/6, 1/3, 1/2, 2/3, 5/6]
-        sage: cyclotomic_to_alpha([2,3])
+        sage: cyclotomic_to_alpha([2, 3])
         [1/3, 1/2, 2/3]
     """
     alpha = [QQ((k, d)) for d in cyclo
@@ -276,11 +293,11 @@ def alpha_to_cyclotomic(alpha) -> list:
         [1]
         sage: alpha_to_cyclotomic([1/2])
         [2]
-        sage: alpha_to_cyclotomic([1/5,2/5,3/5,4/5])
+        sage: alpha_to_cyclotomic([1/5, 2/5, 3/5, 4/5])
         [5]
         sage: alpha_to_cyclotomic([0, 1/6, 1/3, 1/2, 2/3, 5/6])
         [1, 2, 3, 6]
-        sage: alpha_to_cyclotomic([1/3,2/3,1/2])
+        sage: alpha_to_cyclotomic([1/3, 2/3, 1/2])
         [2, 3]
     """
     cyclo = []
@@ -314,7 +331,7 @@ def capital_M(n):
     EXAMPLES::
 
         sage: from sage.modular.hypergeometric_motive import capital_M
-        sage: [capital_M(i) for i in range(1,8)]
+        sage: [capital_M(i) for i in range(1, 8)]
         [1, 4, 27, 64, 3125, 432, 823543]
     """
     n = ZZ(n)
@@ -383,7 +400,7 @@ def gamma_list_to_cyclotomic(galist):
         sage: gamma_list_to_cyclotomic([-1, 2, 3, -4])
         ([3], [4])
 
-        sage: gamma_list_to_cyclotomic([8,2,2,2,-6,-4,-3,-1])
+        sage: gamma_list_to_cyclotomic([8, 2, 2, 2, -6, -4, -3, -1])
         ([2, 2, 8], [3, 3, 6])
     """
     resu = defaultdict(int)
@@ -423,15 +440,15 @@ def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([2],[1]))
+            sage: Hyp(cyclotomic=([2], [1]))
             Hypergeometric data for [1/2] and [0]
 
-            sage: Hyp(alpha_beta=([1/2],[0]))
+            sage: Hyp(alpha_beta=([1/2], [0]))
             Hypergeometric data for [1/2] and [0]
-            sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5],[0,0,0,0]))
+            sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5], [0,0,0,0]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
 
-            sage: Hyp(gamma_list=([5],[1,1,1,1,1]))
+            sage: Hyp(gamma_list=([5], [1,1,1,1,1]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
             sage: Hyp(gamma_list=([5,-1,-1,-1,-1,-1]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
@@ -509,8 +526,8 @@ def __eq__(self, other) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H1 == H1
             True
             sage: H1 == H2
@@ -526,8 +543,8 @@ def __ne__(self, other) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H1 != H1
             False
             sage: H1 != H2
@@ -542,7 +559,7 @@ def __hash__(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
             sage: h = hash(H1)
         """
         return hash((self._alpha, self._beta))
@@ -555,7 +572,7 @@ def cyclotomic_data(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).cyclotomic_data()
+            sage: Hyp(alpha_beta=([1/2], [0])).cyclotomic_data()
             ([2], [1])
         """
         return (list(self._cyclo_up), list(self._cyclo_down))
@@ -567,7 +584,7 @@ def alpha_beta(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).alpha_beta()
+            sage: Hyp(alpha_beta=([1/2], [0])).alpha_beta()
             ([1/2], [0])
         """
         return (list(self._alpha), list(self._beta))
@@ -579,7 +596,7 @@ def alpha(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).alpha()
+            sage: Hyp(alpha_beta=([1/2], [0])).alpha()
             [1/2]
         """
         return list(self._alpha)
@@ -591,7 +608,7 @@ def beta(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).beta()
+            sage: Hyp(alpha_beta=([1/2], [0])).beta()
             [0]
         """
         return list(self._beta)
@@ -603,7 +620,7 @@ def defining_polynomials(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).defining_polynomials()
+            sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).defining_polynomials()
             (x^2 + 1, x^2 - 2*x + 1)
         """
         up = prod(cyclotomic_polynomial(d) for d in self._cyclo_up)
@@ -621,9 +638,9 @@ def gamma_array(self) -> dict:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).gamma_array()
+            sage: Hyp(alpha_beta=([1/2], [0])).gamma_array()
             {1: -2, 2: 1}
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_array()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_array()
             {1: -3, 3: -1, 6: 1}
         """
         return dict(self._gamma_array)
@@ -637,13 +654,13 @@ def gamma_list(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).gamma_list()
+            sage: Hyp(alpha_beta=([1/2], [0])).gamma_list()
             [-1, -1, 2]
 
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_list()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_list()
             [-1, -1, -1, -3, 6]
 
-            sage: Hyp(cyclotomic=([3],[4])).gamma_list()
+            sage: Hyp(cyclotomic=([3], [4])).gamma_list()
             [-1, 2, 3, -4]
         """
         gamma = self.gamma_array()
@@ -659,9 +676,9 @@ def wild_primes(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).wild_primes()
+            sage: Hyp(cyclotomic=([3], [4])).wild_primes()
             [2, 3]
-            sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])).wild_primes()
+            sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9])).wild_primes()
             [2, 3, 5]
         """
         gamma = self.gamma_array()
@@ -682,10 +699,10 @@ def zigzag(self, x, flip_beta=False):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: [H.zigzag(x) for x in [0, 1/3, 1/2]]
             [0, 1, 0]
-            sage: H = Hyp(cyclotomic=([5],[1,1,1,1]))
+            sage: H = Hyp(cyclotomic=([5], [1,1,1,1]))
             sage: [H.zigzag(x) for x in [0,1/6,1/4,1/2,3/4,5/6]]
             [-4, -4, -3, -2, -1, 0]
 
@@ -707,36 +724,36 @@ def weight(self):
         With rational inputs::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).weight()
+            sage: Hyp(alpha_beta=([1/2], [0])).weight()
             0
-            sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).weight()
+            sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).weight()
             1
-            sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[0,0,1/4,3/4])).weight()
+            sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [0,0,1/4,3/4])).weight()
             1
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: H.weight()
             1
 
         With cyclotomic inputs::
 
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).weight()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).weight()
             2
-            sage: Hyp(cyclotomic=([6],[1,2])).weight()
+            sage: Hyp(cyclotomic=([6], [1,2])).weight()
             0
-            sage: Hyp(cyclotomic=([8],[1,2,3])).weight()
+            sage: Hyp(cyclotomic=([8], [1,2,3])).weight()
             0
-            sage: Hyp(cyclotomic=([5],[1,1,1,1])).weight()
+            sage: Hyp(cyclotomic=([5], [1,1,1,1])).weight()
             3
-            sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).weight()
+            sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).weight()
             1
-            sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).weight()
+            sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).weight()
             2
-            sage: Hyp(cyclotomic=([3,3],[2,2,4])).weight()
+            sage: Hyp(cyclotomic=([3,3], [2,2,4])).weight()
             1
 
         With gamma list input::
 
-            sage: Hyp(gamma_list=([8,2,2,2],[6,4,3,1])).weight()
+            sage: Hyp(gamma_list=([8,2,2,2], [6,4,3,1])).weight()
             3
         """
         alpha = self._alpha
@@ -757,15 +774,15 @@ def degree(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).degree()
+            sage: Hyp(alpha_beta=([1/2], [0])).degree()
             1
-            sage: Hyp(gamma_list=([2,2,4],[8])).degree()
+            sage: Hyp(gamma_list=([2,2,4], [8])).degree()
             4
-            sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).degree()
+            sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).degree()
             6
-            sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).degree()
+            sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).degree()
             6
-            sage: Hyp(cyclotomic=([3,3],[2,2,4])).degree()
+            sage: Hyp(cyclotomic=([3,3], [2,2,4])).degree()
             4
         """
         return self._deg
@@ -781,19 +798,19 @@ def hodge_numbers(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[6]))
+            sage: H = Hyp(cyclotomic=([3], [6]))
             sage: H.hodge_numbers()
             [1, 1]
 
-            sage: H = Hyp(cyclotomic=([4],[1,2]))
+            sage: H = Hyp(cyclotomic=([4], [1,2]))
             sage: H.hodge_numbers()
             [2]
 
-            sage: H = Hyp(gamma_list=([8,2,2,2],[6,4,3,1]))
+            sage: H = Hyp(gamma_list=([8,2,2,2], [6,4,3,1]))
             sage: H.hodge_numbers()
             [1, 2, 2, 1]
 
-            sage: H = Hyp(gamma_list=([5],[1,1,1,1,1]))
+            sage: H = Hyp(gamma_list=([5], [1,1,1,1,1]))
             sage: H.hodge_numbers()
             [1, 1, 1, 1]
 
@@ -832,10 +849,10 @@ def hodge_polynomial(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_polynomial()
             (T^3 + 2*T^2 + 2*T + 1)/T^2
-            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
+            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9]))
             sage: H.hodge_polynomial()
             (T^5 + 3*T^4 + 3*T^3 + 3*T^2 + 3*T + 1)/T^2
         """
@@ -860,7 +877,7 @@ def hodge_function(self, x):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_function(3)
             2
             sage: H.hodge_function(4)
@@ -892,10 +909,10 @@ def hodge_polygon_vertices(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_polygon_vertices()
             [(0, 0), (1, 0), (3, 2), (5, 6), (6, 9)]
-            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
+            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9]))
             sage: H.hodge_polygon_vertices()
             [(0, 0), (1, 0), (4, 3), (7, 9), (10, 18), (13, 30), (14, 35)]
         """
@@ -915,7 +932,7 @@ def E_polynomial(self, vars=None):
 
         INPUT:
 
-        - ``vars`` -- optional pair of variables (default `u,v`)
+        - ``vars`` -- optional pair of variables (default: `u,v`)
 
         REFERENCES:
 
@@ -925,7 +942,7 @@ def E_polynomial(self, vars=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData
-            sage: H = HypergeometricData(gamma_list=[-30,-1,6,10,15])
+            sage: H = HypergeometricData(gamma_list=[-30, -1, 6, 10, 15])
             sage: H.E_polynomial()
             8*u*v + 7*u + 7*v + 8
 
@@ -992,12 +1009,12 @@ def M_value(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: H.M_value()
             729/4096
-            sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2],[0,0,0,0]))).M_value()
+            sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2], [0,0,0,0]))).M_value()
             256
-            sage: Hyp(cyclotomic=([5],[1,1,1,1])).M_value()
+            sage: Hyp(cyclotomic=([5], [1,1,1,1])).M_value()
             3125
         """
         return self._M_value
@@ -1013,7 +1030,7 @@ def is_primitive(self) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).is_primitive()
+            sage: Hyp(cyclotomic=([3], [4])).is_primitive()
             True
             sage: Hyp(gamma_list=[-2, 4, 6, -8]).is_primitive()
             False
@@ -1033,7 +1050,7 @@ def primitive_index(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).primitive_index()
+            sage: Hyp(cyclotomic=([3], [4])).primitive_index()
             1
             sage: Hyp(gamma_list=[-2, 4, 6, -8]).primitive_index()
             2
@@ -1052,7 +1069,7 @@ def has_symmetry_at_one(self) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=[[1/2]*16,[0]*16]).has_symmetry_at_one()
+            sage: Hyp(alpha_beta=[[1/2]*16, [0]*16]).has_symmetry_at_one()
             True
 
         REFERENCES:
@@ -1064,18 +1081,18 @@ def has_symmetry_at_one(self) -> bool:
 
     def lfunction(self, t, prec=53):
         """
-        Return the L-function of ``self``.
+        Return the `L`-function of ``self``.
 
-        The result is a wrapper around a PARI L-function.
+        The result is a wrapper around a PARI `L`-function.
 
         INPUT:
 
-        - ``prec`` -- precision (default 53)
+        - ``prec`` -- precision (default: 53)
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: L = H.lfunction(1/64); L
             PARI L-function associated to Hypergeometric data for [1/3, 2/3] and [1/4, 3/4]
             sage: L(4)
@@ -1095,7 +1112,7 @@ def canonical_scheme(self, t=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.gamma_list()
             [-1, 2, 3, -4]
             sage: H.canonical_scheme()
@@ -1187,13 +1204,13 @@ def twist(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2],[0]))
+            sage: H = Hyp(alpha_beta=([1/2], [0]))
             sage: H.twist()
             Hypergeometric data for [0] and [1/2]
             sage: H.twist().twist() == H
             True
 
-            sage: Hyp(cyclotomic=([6],[1,2])).twist().cyclotomic_data()
+            sage: Hyp(cyclotomic=([6], [1,2])).twist().cyclotomic_data()
             ([3], [1, 2])
         """
         alpha = [x + QQ((1, 2)) for x in self._alpha]
@@ -1207,7 +1224,7 @@ def swap_alpha_beta(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2],[0]))
+            sage: H = Hyp(alpha_beta=([1/2], [0]))
             sage: H.swap_alpha_beta()
             Hypergeometric data for [0] and [1/2]
         """
@@ -1225,7 +1242,7 @@ def primitive_data(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H2 = Hyp(gamma_list=[-2, 4, 6, -8])
             sage: H2.primitive_data() == H
             True
@@ -1247,7 +1264,7 @@ def gauss_table(self, p, f, prec):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.gauss_table(2, 2, 4)
             (4, [1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3])
         """
@@ -1277,7 +1294,7 @@ def gauss_table_full(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.euler_factor(2, 7, cache_p=True)
             7*T^2 - 3*T + 1
             sage: H.gauss_table_full()[(7, 1)]
@@ -1285,12 +1302,12 @@ def gauss_table_full(self):
 
         Clearing cached values::
 
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.euler_factor(2, 7, cache_p=True)
             7*T^2 - 3*T + 1
             sage: d = H.gauss_table_full()
             sage: d.clear() # Delete all entries of this dict
-            sage: H1 = Hyp(cyclotomic=([5],[12]))
+            sage: H1 = Hyp(cyclotomic=([5], [12]))
             sage: d1 = H1.gauss_table_full()
             sage: len(d1.keys()) # No cached values
             0
@@ -1313,11 +1330,11 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         INPUT:
 
-        - `p` -- a prime number
+        - ``p`` -- a prime number
 
-        - `f` -- an integer such that `q = p^f`
+        - ``f`` -- an integer such that `q = p^f`
 
-        - `t` -- a rational parameter
+        - ``t`` -- a rational parameter
 
         - ``prec`` -- precision (optional)
 
@@ -1332,7 +1349,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
         From Benasque report [Benasque2009]_, page 8::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: [H.padic_H_value(3,i,-1) for i in range(1,3)]
             [0, -12]
             sage: [H.padic_H_value(5,i,-1) for i in range(1,3)]
@@ -1353,8 +1370,8 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check issue from :issue:`28404`::
 
-            sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: [H1.padic_H_value(5,1,i) for i in range(2,5)]
             [1, -4, -4]
             sage: [H2.padic_H_value(5,1,i) for i in range(2,5)]
@@ -1362,13 +1379,13 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check for potential overflow::
 
-            sage: H = Hyp(cyclotomic=[[10,6],[5,4]])
+            sage: H = Hyp(cyclotomic=[[10,6], [5,4]])
             sage: H.padic_H_value(101, 2, 2)
             -1560629
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5],  [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
             sage: try:
             ....:     print(H.padic_H_value(373, 4, 2))
             ....: except ValueError as s:
@@ -1377,7 +1394,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check error handling for wild and tame primes::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5],  [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
             sage: try:
             ....:     print(H.padic_H_value(5, 1, 2))
             ....: except NotImplementedError as s:
@@ -1389,6 +1406,12 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
             ....:     print(s)
             p is tame
 
+        Check that :issue:`37910` is resolved::
+
+            sage: H = Hyp(alpha_beta=[[1/2,1/2,1/2,1/2,1/2,1/3,2/3,1/6,5/6],  [0,0,0,0,0,0,0,0,0]])
+            sage: H.padic_H_value(151, 2, -512000)
+            50178940126155881
+
         REFERENCES:
 
         - [MagmaHGM]_
@@ -1400,7 +1423,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
             raise ValueError('p not prime')
         if not all(x.denominator() % p for x in self._alpha + self._beta):
             raise NotImplementedError('p is wild')
-        if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
+        if t.numerator() % p == 0 or t.denominator() % p == 0:
             raise NotImplementedError('p is tame')
 
         if 0 in alpha:
@@ -1435,6 +1458,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
             trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs)
         sigma = trcoeffs[p - 2]
         p_ring = sigma.parent()
+
         teich = p_ring.teichmuller(M / t)
         for i in range(p - 3, -1, -1):
             sigma = sigma * teich + trcoeffs[i]
@@ -1451,13 +1475,13 @@ def H_value(self, p, f, t, ring=None):
 
         INPUT:
 
-        - `p` -- a prime number
+        - ``p`` -- a prime number
 
-        - `f` -- an integer such that `q = p^f`
+        - ``f`` -- an integer such that `q = p^f`
 
-        - `t` -- a rational parameter
+        - ``t`` -- a rational parameter
 
-        - ``ring`` -- optional (default :class:`UniversalCyclotomicfield`)
+        - ``ring`` -- optional (default: :class:`UniversalCyclotomicfield`)
 
         The ring could be also ``ComplexField(n)`` or ``QQbar``.
 
@@ -1478,7 +1502,7 @@ def H_value(self, p, f, t, ring=None):
         With values in the UniversalCyclotomicField (slow)::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: [H.H_value(3,i,-1) for i in range(1,3)]
             [0, -12]
             sage: [H.H_value(5,i,-1) for i in range(1,3)]
@@ -1497,8 +1521,8 @@ def H_value(self, p, f, t, ring=None):
 
         Check issue from :issue:`28404`::
 
-            sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: [H1.H_value(5,1,i) for i in range(2,5)]
             [1, -4, -4]
             sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)]
@@ -1508,7 +1532,7 @@ def H_value(self, p, f, t, ring=None):
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.padic_H_value(373, 4, 2))
             ....: except ValueError as s:
@@ -1517,7 +1541,7 @@ def H_value(self, p, f, t, ring=None):
 
         Check error handling for wild and tame primes::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.padic_H_value(5, 1, 2))
             ....: except NotImplementedError as s:
@@ -1583,19 +1607,19 @@ def sign(self, t, p):
         Return the sign of the functional equation for the Euler factor of the motive `H_t` at the prime `p`.
 
         For odd weight, the sign of the functional equation is +1. For even
-        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_
+        weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_
         (when 0 is not in alpha).
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H.weight(), H.degree()
             (2, 3)
             sage: [H.sign(1/4,p) for p in [5,7,11,13,17,19]]
             [1, 1, -1, -1, 1, 1]
 
-            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
+            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2]))
             sage: H.weight(), H.degree()
             (2, 4)
             sage: t = -5
@@ -1604,7 +1628,7 @@ def sign(self, t, p):
 
         We check that :issue:`28404` is fixed::
 
-            sage: H = Hyp(cyclotomic=([1,1,1],[6,2]))
+            sage: H = Hyp(cyclotomic=([1,1,1], [6,2]))
             sage: [H.sign(4,p) for p in [5,7,11,13,17,19]]
             [1, 1, -1, -1, 1, 1]
 
@@ -1623,16 +1647,115 @@ def sign(self, t, p):
             sign = kronecker_symbol(t * (t - 1) * self._sign_param, p)
         return sign
 
+    def euler_factor_tame_contribution(self, t, p, mo, deg=None):
+        """
+        Return a contribution to the Euler factor of the motive `H_t` at a tame prime.
+
+        The output is only nontrivial when `t` has nonzero `p`-adic valuation.
+        The algorithm is described in Section 11.4.1 of [Watkins]_.
+
+        INPUT:
+
+        - ``t`` -- rational number, not 0 or 1
+
+        - ``p`` -- prime number of good reduction
+
+        - ``mo`` -- integer
+
+        - ``deg`` -- integer (optional)
+
+        OUTPUT:
+
+        a polynomial
+
+        If ``deg`` is specified, the output is truncated to that degree (inclusive).
+
+        EXAMPLES::
+
+            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
+            sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]])
+            sage: H.euler_factor_tame_contribution(11^2, 11, 4)
+            1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 4)
+            1331*T^2 + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 4, deg=1)
+            1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 5)
+            1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 5, deg=3)
+            161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 6)
+            1
+        """
+        t = QQ(t)
+        if t in [0, 1]:
+            raise ValueError('invalid t')
+        if not is_prime(p):
+            raise ValueError('p not prime')
+        if not all(x.denominator() % p for x in self._alpha + self._beta):
+            raise NotImplementedError('p is wild')
+
+        e = t.valuation(p)
+        t0 = t / p**e
+        if e > 0:
+            mul = self.cyclotomic_data()[1].count(mo)
+        elif e < 0:
+            mul = self.cyclotomic_data()[0].count(mo)
+        else:
+            mul = None
+        if e % mo or not mul:
+            return ZZ.one()
+        d = euler_phi(mo)
+        f = IntegerModRing(mo)(p).multiplicative_order()
+        if deg is None:
+            deg = d
+        if deg < f:
+            return ZZ.one()
+        q = p ** f
+        prec = ceil(deg*(self.weight()+1-mul)/2 + log(2*d + 1, p))
+        k = (q-1) // mo
+        flip = (f == 1 and prec == 1)
+        gtab_prec, gtab = self.gauss_table(p, f, prec)
+        try:
+            p_ring = gtab[0].parent()
+        except AttributeError:
+            p_ring = Zp(p, prec, 'fixed-mod')
+        M = self.M_value()
+        teich = p_ring.teichmuller(M / t0)
+        m = {r: self._beta.count(QQ((r, q - 1))) for r in range(q - 1)}
+        D = -min(self.zigzag(x, flip_beta=True) for x in self._alpha + self._beta)
+        gamma = self.gamma_array()
+        l = []
+        for j in range(mo):
+            if gcd(j, mo) == 1:
+                r = j * k
+                term = teich**r * ZZ(-1)**m[0]
+                ct = 0
+                for v, gv in gamma.items():
+                    r1 = v * r % (q-1)
+                    ct += gv * sum(r1.digits(p))
+                    term *= p_ring(gtab[r1]) ** (-gv if flip else gv)
+                ct //= p - 1
+                term *= ZZ(-1) ** ct
+                ct += f * (D + m[0] - m[r])
+                l.append(term * p**ct)
+        traces = [0 if j % f else sum(i ** (j//f) for i in l) for j in range(1,d+1)]
+        R = IntegerModRing(p**prec)
+        traces = [R(i).lift_centered() for i in traces]
+        return characteristic_polynomial_from_traces(traces, d, p, 0, 1, deg, use_fe=False)
+
     @cached_method
-    def euler_factor(self, t, p, cache_p=False):
+    def euler_factor(self, t, p, deg=None, cache_p=False):
         """
         Return the Euler factor of the motive `H_t` at prime `p`.
 
         INPUT:
 
-        - `t` -- rational number, not 0 or 1
+        - ``t`` -- rational number, not 0 or 1
 
-        - `p` -- prime number of good reduction
+        - ``p`` -- prime number of good reduction
+
+        - ``deg`` -- integer or ``None``
 
         OUTPUT:
 
@@ -1641,12 +1764,18 @@ def euler_factor(self, t, p, cache_p=False):
         See [Benasque2009]_ for explicit examples of Euler factors.
 
         For odd weight, the sign of the functional equation is +1. For even
-        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_.
+        weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_.
+
+        If ``deg`` is specified, then the polynomial is only computed up to degree
+        ``deg`` (inclusive).
+
+        The prime `p` may be tame, but not wild. When `v_p(t-1)` is nonzero and even,
+        the Euler factor includes a linear term described in Section 11.2 of [Watkins]_.
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: H.euler_factor(-1, 5)
             15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1
 
@@ -1662,7 +1791,7 @@ def euler_factor(self, t, p, cache_p=False):
             23*T^2 + 8*T + 1,
             29*T^2 + 2*T + 1]
 
-            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H.weight(), H.degree()
             (2, 3)
             sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]]
@@ -1673,7 +1802,7 @@ def euler_factor(self, t, p, cache_p=False):
              4913*T^3 + 323*T^2 + 19*T + 1,
              6859*T^3 - 57*T^2 - 3*T + 1]
 
-            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
+            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2]))
             sage: H.weight(), H.degree()
             (2, 4)
             sage: t = -5
@@ -1690,52 +1819,107 @@ def euler_factor(self, t, p, cache_p=False):
             sage: H = Hyp(cyclotomic=([11], [7, 12]))
             sage: H.euler_factor(2, 13)
             371293*T^10 - 85683*T^9 + 26364*T^8 + 1352*T^7 - 65*T^6 + 394*T^5 - 5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1
+            sage: H.euler_factor(2, 13, deg=4)
+            -5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1
             sage: H.euler_factor(2, 19) # long time
             2476099*T^10 - 651605*T^9 + 233206*T^8 - 77254*T^7 + 20349*T^6 - 4611*T^5 + 1071*T^4 - 214*T^3 + 34*T^2 - 5*T + 1
 
+        This is an example of tame primes::
+
+            sage: H = Hyp(cyclotomic=[[4,2,2], [3,1,1]])
+            sage: H.euler_factor(8, 7)
+            -7*T^3 + 7*T^2 - T + 1
+            sage: H.euler_factor(50, 7)
+            -7*T^3 + 7*T^2 - T + 1
+            sage: H.euler_factor(7, 7)
+            -T + 1
+            sage: H.euler_factor(1/7^2, 7)
+            T + 1
+            sage: H.euler_factor(1/7^4, 7)
+            7*T^3 + 7*T^2 + T + 1
+
         TESTS::
 
-             sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2]))
+             sage: H1 = Hyp(alpha_beta=([1,1,1], [1/2,1/2,1/2]))
              sage: H2 = H1.swap_alpha_beta()
              sage: H1.euler_factor(-1, 3)
              27*T^3 + 3*T^2 + T + 1
              sage: H2.euler_factor(-1, 3)
              27*T^3 + 3*T^2 + T + 1
-             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5]))
+             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3], [1/2,1/5,2/5,3/5,4/5]))
              sage: H.euler_factor(5,7)
              16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1
 
         Check for precision downsampling::
 
-            sage: H = Hyp(cyclotomic=[[3],[4]])
+            sage: H = Hyp(cyclotomic=[[3], [4]])
             sage: H.euler_factor(2, 11, cache_p=True)
             11*T^2 - 3*T + 1
-            sage: H = Hyp(cyclotomic=[[12],[1,2,6]])
+            sage: H = Hyp(cyclotomic=[[12], [1,2,6]])
             sage: H.euler_factor(2, 11, cache_p=True)
             -T^4 + T^3 - T + 1
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.euler_factor(2, 373))
             ....: except ValueError as s:
             ....:     print(s)
             p^f cannot exceed 2^31
 
-        Check error handling for wild and tame primes::
+        Check handling of some tame cases::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[4,2,2,2], [3,1,1,1]])
+            sage: H.euler_factor(8, 7)
+            2401*T^4 - 392*T^3 + 46*T^2 - 8*T + 1
+            sage: H.euler_factor(50, 7)
+            16807*T^5 - 343*T^4 - 70*T^3 - 10*T^2 - T + 1
+            sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]])
+            sage: H.euler_factor(11, 11)
+            1
+            sage: H.euler_factor(11**4, 11)
+            1331*T^2 + 1
+            sage: H.euler_factor(11**5, 11)
+            1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor(11**5, 11, deg=3)
+            161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor(11**-3, 11)
+            1331*T^2 + 1
+            sage: H.euler_factor(11**-7, 11)
+            2357947691*T^6 - 58564*T^3 + 1
+            sage: H = Hyp(cyclotomic=[[7], [5,1,1]])
+            sage: H.euler_factor(2, 2)
+            -T + 1
+            sage: H.euler_factor(2^-7, 2)
+            8*T^6 - 2*T^3 + 1
+            sage: H.euler_factor(3, 2)
+            4*T^5 + 4*T^4 + 2*T^3 + 2*T^2 + T + 1
+
+        More examples of tame primes from [Watkins]_::
+
+            sage: H = Hyp(cyclotomic=[[3,12], [6,6,1,1]])
+            sage: H.euler_factor(1/8, 7).factor()
+            (-1) * (7*T - 1) * (117649*T^4 + 2744*T^3 + 105*T^2 + 8*T + 1)
+            sage: H.euler_factor(1/12, 11).factor()
+            (11*T + 1) * (1771561*T^4 - 18634*T^3 + 22*T^2 - 14*T + 1)
+            sage: H = Hyp(cyclotomic=[[4,4,4],[6,2,1,1,1]])
+            sage: H.euler_factor(1/8, 7).factor()
+            (49*T + 1) * (5764801*T^4 - 86436*T^3 + 2758*T^2 - 36*T + 1)
+            sage: H.euler_factor(1/12, 11).factor()
+            (-1) * (121*T - 1) * (214358881*T^4 - 527076*T^3 + 12694*T^2 - 36*T + 1)
+            sage: H = Hyp(cyclotomic=[[10,4,2], [18,1]])
+            sage: H.euler_factor(1/14, 13)
+            -4826809*T^6 + 114244*T^5 + 2197*T^4 - 13*T^2 - 4*T + 1
+
+        Check error handling for wild primes::
+
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.euler_factor(2, 5))
             ....: except NotImplementedError as s:
             ....:     print(s)
             p is wild
-            sage: try:
-            ....:     print(H.euler_factor(3, 3))
-            ....: except NotImplementedError as s:
-            ....:     print(s)
-            p is tame
 
         REFERENCES:
 
@@ -1745,19 +1929,33 @@ def euler_factor(self, t, p, cache_p=False):
         t = QQ(t)
         if t in [0, 1]:
             raise ValueError('invalid t')
-        alpha = self._alpha
-        if 0 in alpha:
-            return self._swap.euler_factor(~t, p)
-
         if not is_prime(p):
             raise ValueError('p not prime')
         if not all(x.denominator() % p for x in self._alpha + self._beta):
             raise NotImplementedError('p is wild')
-        if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
-            raise NotImplementedError('p is tame')
-        # now p is good
-        d = self.degree()
+        if 0 in self._alpha:
+            return self._swap.euler_factor(~t, p)
+        P = PolynomialRing(ZZ, 'T')
+        if t.numerator() % p == 0 or t.denominator() % p == 0:
+            ans = P.one()
+            for m in set(j for i in self.cyclotomic_data() for j in i):
+                ans *= self.euler_factor_tame_contribution(t, p, m, deg)
+            if deg is not None:
+                ans = ans.truncate(deg + 1)
+            return ans
+        # now p is good, or p is tame and t is a p-adic unit
+        elif (t-1) % p == 0:
+            typ = "mult"
+            d = self.degree() - 1
+            if d % 2:
+                d -= 1
+        else:
+            typ = "good"
+            d = self.degree()
         bound = d // 2
+        if deg is not None:
+            bound = min(deg, bound)
+
         if p ** bound > 2 ** 31:
             raise ValueError("p^f cannot exceed 2^31")
 
@@ -1765,5 +1963,55 @@ def euler_factor(self, t, p, cache_p=False):
                   for i in range(bound)]
 
         w = self.weight()
-        sign = self.sign(t, p)
-        return characteristic_polynomial_from_traces(traces, d, p, w, sign)
+        m1 = self.cyclotomic_data()[1].count(1)
+
+        # In the multiplicative case, we sometimes need to pull out a linear factor
+        # in order to apply the functional equation.
+        if typ == "mult":
+            if self.degree() % 2 == 0:
+                sign = 1
+                if w % 2:
+                    assert m1 % 2 == 0
+                    u = (-1) ** (m1//2)
+                    u *= prod(v ** gv for v, gv in self.gamma_array().items())
+                    c = kronecker_symbol(u, p) * p**((w-1)//2)
+                else:
+                    u = (-1) ** (1 + self.degree()//2 + (m1-1)//2)
+                    num, den = self.defining_polynomials()
+                    x = num.parent().gen()
+                    num = num(-x)
+                    num /= (x-1) ** num.valuation(x-1)
+                    den /= (x-1) ** den.valuation(x-1)
+                    u *= 2 * num(1) / den(1)
+                    c = kronecker_symbol(u, p) * p**(w//2)
+                cpow = c
+                for j in range(len(traces)):
+                    traces[j] -= cpow
+                    cpow *= c
+                tmp = 1 - c*P.gen()
+            else:
+                u = (-1) ** (1+(self.degree()-1)//2)
+                num, den = self.defining_polynomials()
+                x = num.parent().gen()
+                den = den(-x)
+                num /= (x-1) ** num.valuation(x-1)
+                den /= (x-1) ** den.valuation(x-1)
+                u *= num(1) / den(1)
+                sign = kronecker_symbol(u, p)
+        else:
+            sign = self.sign(t, p)
+
+        ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg)
+
+        # In the multiplicative case, we sometimes need to add extra factors.
+        if typ == "mult":
+            if self.degree() % 2 == 0:
+                ans *= tmp
+            if w % 2 == 0 and (t-1).valuation(p) % 2 == 0:
+                K = (-1) ** ((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list())
+                t0 = (~t-1) / p**((t-1).valuation(p))
+                c = kronecker_symbol(K*t0, p) * p**(w//2)
+                ans *= 1 - c*P.gen()
+            if deg is not None:
+                ans = ans.truncate(deg + 1)
+        return ans
diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py
index dd45fca7713..3a8d445f1be 100644
--- a/src/sage/modular/quasimodform/element.py
+++ b/src/sage/modular/quasimodform/element.py
@@ -5,9 +5,11 @@
 AUTHORS:
 
 - DAVID AYOTTE (2021-03-18): initial version
+- Seewoo Lee (2023-09): coefficients method
 """
 # ****************************************************************************
 #       Copyright (C) 2021 David Ayotte
+#                     2023 Seewoo Lee 
 #
 #
 # This program is free software: you can redistribute it and/or modify
@@ -772,3 +774,73 @@ def derivative(self):
         hom_comp = self.homogeneous_components()
 
         return sum(f.serre_derivative() + R(k) * u * f * E2 for k, f in hom_comp.items())
+
+    def _compute(self, X):
+        r"""
+        Compute the coefficients of `q^n` of the q-expansion of this,
+        graded quasimodular form for `n` in the list `X`.
+
+        The results are not cached.  (Use coefficients for cached results).
+
+        EXAMPLES::
+
+            sage: E2 = QuasiModularForms(1).0
+            sage: E2.q_expansion(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)
+            sage: E2._compute([3, 6])
+            [-96, -288]
+            sage: E2._compute([])
+            []
+        """
+        if not isinstance(X, list) or not X:
+            return []
+        bound = max(X)
+        q_exp = self.q_expansion(bound + 1)
+        return [q_exp[i] for i in X]
+
+    def coefficients(self, X):
+        r"""
+        Return the coefficients of `q^n` of the q-expansion of this,
+        graded quasimodular form for `n` in the list `X`.
+
+        If X is an integer, return coefficients for indices from 1
+        to X. This method caches the result.
+
+        EXAMPLES::
+
+            sage: E2, E4 = QuasiModularForms(1).0, QuasiModularForms(1).1
+            sage: f = E2^2
+            sage: g = E2^3 * E4
+            sage: f.coefficients(10)
+            [-48, 432, 3264, 9456, 21600, 39744, 66432, 105840, 147984, 220320]
+            sage: f.coefficients([0,1])
+            [1, -48]
+            sage: f.coefficients([0,1,2,3])
+            [1, -48, 432, 3264]
+            sage: f.coefficients([2,3])
+            [432, 3264]
+            sage: g.coefficients(10)
+            [168,
+             -13608,
+             210336,
+             1805496,
+             -22562064,
+             -322437024,
+             -2063087808,
+             -9165872520,
+             -32250917496,
+             -96383477232]
+            sage: g.coefficients([3, 7])
+            [210336, -2063087808]
+        """
+        try:
+            self.__coefficients
+        except AttributeError:
+            self.__coefficients = {}
+        if isinstance(X, Integer):
+            X = list(range(1, X + 1))
+        Y = [n for n in X if n not in self.__coefficients]
+        v = self._compute(Y)
+        for i in range(len(v)):
+            self.__coefficients[Y[i]] = v[i]
+        return [self.__coefficients[x] for x in X]
diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py
index dcdf8e470c2..5a91a837ecd 100644
--- a/src/sage/modular/quasimodform/ring.py
+++ b/src/sage/modular/quasimodform/ring.py
@@ -42,6 +42,8 @@
 
     sage: QM = QuasiModularForms(1); QM
     Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field
+    sage: QM.category()
+    Category of commutative graded algebras over Rational Field
     sage: QM.gens()
     [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6),
      1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6),
@@ -251,20 +253,21 @@ def __init__(self, group=1, base_ring=QQ, name='E2'):
         """
         if not isinstance(name, str):
             raise TypeError("`name` must be a string")
-        #check if the group is SL2(Z)
+        # check if the group is SL2(Z)
         if isinstance(group, (int, Integer)):
             group = Gamma0(group)
         elif not is_CongruenceSubgroup(group):
             raise ValueError("Group (=%s) should be a congruence subgroup" % group)
 
-        #Check if the base ring is the rationnal field
+        # Check if the base ring is the rational field
         if base_ring != QQ:
             raise NotImplementedError("base ring other than Q are not yet supported for quasimodular forms ring")
 
         self.__group = group
         self.__modular_forms_subring = ModularFormsRing(group, base_ring)
         self.__polynomial_subring = self.__modular_forms_subring[name]
-        Parent.__init__(self, base=base_ring, category=GradedAlgebras(base_ring))
+        cat = GradedAlgebras(base_ring).Commutative()
+        Parent.__init__(self, base=base_ring, category=cat)
 
     def group(self):
         r"""
@@ -421,15 +424,15 @@ def _element_constructor_(self, datum):
             NotImplementedError: conversion from q-expansion not yet implemented
         """
         if isinstance(datum, list):
-            if len(datum) == 0:
+            if not datum:
                 raise ValueError("the given list should be non-empty")
             for idx, f in enumerate(datum):
                 if not isinstance(f, (GradedModularFormElement, ModularFormElement)):
                     raise ValueError("one list element is not a modular form")
-                datum[idx] = self.__modular_forms_subring(f) #to ensure that every forms is a GradedModularFormElement
+                datum[idx] = self.__modular_forms_subring(f)  # to ensure that every form is a GradedModularFormElement
             datum = self.__polynomial_subring(datum)
         elif isinstance(datum, (GradedModularFormElement, ModularFormElement)):
-            datum = self.__modular_forms_subring(datum) # GradedModularFormElement
+            datum = self.__modular_forms_subring(datum)  # GradedModularFormElement
             datum = self.__polynomial_subring(datum)
         elif isinstance(datum, Polynomial):
             datum = self.__polynomial_subring(datum.coefficients(sparse=False))
@@ -489,7 +492,7 @@ def gens(self):
             gen_list.append(self(f))
         return gen_list
 
-    generators = gens # alias
+    generators = gens  # alias
 
     def ngens(self):
         r"""
@@ -690,7 +693,7 @@ def polynomial_ring(self, names=None):
                     # the letters E and S are reserved for basis elements of the
                     # Eisenstein subspaces and cuspidal subspaces respectively.
                     iter_names = (product(letters, repeat=r)
-                                    for r in range(1, len(same_weights)//len(letters) + 2))
+                                  for r in range(1, len(same_weights)//len(letters) + 2))
                     iter_names = chain(*iter_names)
                     for k in same_weights:
                         form = next(gens)
@@ -714,7 +717,7 @@ def polynomial_ring(self, names=None):
                         else:
                             name = "".join(next(iter_names)) + str(k)
                         names.append(name)
-        weights.insert(0, 2) # add the weight 2 Eisenstein series
+        weights.insert(0, 2)  # add the weight 2 Eisenstein series
         return PolynomialRing(self.base_ring(), len(weights), names,
                               order=TermOrder('wdeglex', weights))
 
@@ -777,7 +780,7 @@ def from_polynomial(self, polynomial):
         nb_var = poly_parent.ngens()
         if nb_var > self.ngens():
             raise ValueError("the number of variables (%s) of the given polynomial cannot exceed the number of generators (%s) of the quasimodular forms ring" % (nb_var, self.ngens()))
-        gens_dict = {poly_parent.gen(i):self.gen(i) for i in range(0, nb_var)}
+        gens_dict = {poly_parent.gen(i): self.gen(i) for i in range(nb_var)}
         return self(polynomial.subs(gens_dict))
 
     def basis_of_weight(self, weight):
@@ -818,9 +821,9 @@ def basis_of_weight(self, weight):
         E2 = self.weight_2_eisenstein_series()
         M = self.__modular_forms_subring
         E2_pow = self.one()
-        for j in range(weight//2):
-            basis += [f*E2_pow for f
-                      in M.modular_forms_of_weight(weight - 2*j).basis()]
+        for j in range(weight // 2):
+            basis.extend(f * E2_pow
+                         for f in M.modular_forms_of_weight(weight - 2*j).basis())
             E2_pow *= E2
         if not weight % 2:
             basis.append(E2_pow)
diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py
index 0cb8a0be8fd..1654c8cf6ed 100644
--- a/src/sage/modules/free_module.py
+++ b/src/sage/modules/free_module.py
@@ -509,12 +509,18 @@ def FreeModule(base_ring, rank_or_basis_keys=None, sparse=False, inner_product_m
         sage: FreeModule(QQ, ['a', 2, 3, 4], with_basis=None)
         Traceback (most recent call last):
         ...
-        NotImplementedError: FiniteRankFreeModule only supports integer ranges as basis_keys, got ['a', 2, 3, 4]
+        NotImplementedError: FiniteRankFreeModule only supports integer ranges
+        as basis_keys, got ['a', 2, 3, 4]
         sage: FreeModule(QQ, [1, 3, 5], with_basis=None)
         Traceback (most recent call last):
         ...
-        NotImplementedError: FiniteRankFreeModule only supports integer ranges as basis_keys, got [1, 3, 5]
+        NotImplementedError: FiniteRankFreeModule only supports integer ranges
+        as basis_keys, got [1, 3, 5]
 
+        sage: FreeModule(ZZ, rank=3, basis_keys=['c','d'])
+        Traceback (most recent call last):
+        ...
+        ValueError: inconsistent rank: should be cardinality of ['c', 'd'] but got 3
         sage: FreeModule(QQ, ['a', 'b'], rank=2)
         Free module generated by {'a', 'b'} over Rational Field
         sage: FreeModule(QQ, 2, basis_keys=['a', 'b'])
@@ -559,12 +565,15 @@ def FreeModule(base_ring, rank_or_basis_keys=None, sparse=False, inner_product_m
             return FiniteRankFreeModule(base_ring, rank, start_index=start_index, **args)
         return FiniteRankFreeModule(base_ring, rank, **args)
     elif with_basis == 'standard':
-        if basis_keys is None:
+        if rank is not None and basis_keys is None:
             return FreeModuleFactory_with_standard_basis(base_ring, rank, sparse,
                                                          inner_product_matrix, **args)
         else:
             if inner_product_matrix is not None:
                 raise NotImplementedError
+            if rank is not None and rank != len(basis_keys):
+                raise ValueError(f'inconsistent basis_keys: should be of cardinality {rank}, '
+                                 f'got {basis_keys}')
             from sage.combinat.free_module import CombinatorialFreeModule
             return CombinatorialFreeModule(base_ring, basis_keys, **args)
     else:
@@ -1963,7 +1972,8 @@ class FreeModule_generic(Module_free_ambient):
          (finite enumerated fields and subquotients of monoids and quotients of semigroups)
         sage: FreeModule(ZZ,3).category()
         Category of finite dimensional modules with basis over
-         (Dedekind domains and euclidean domains and infinite enumerated sets
+         (Dedekind domains and euclidean domains
+          and noetherian rings and infinite enumerated sets
           and metric spaces)
         sage: (QQ^0).category()
         Category of finite enumerated finite dimensional vector spaces with basis
diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py
index 9513a56ea56..9222bd8105e 100644
--- a/src/sage/modules/free_module_homspace.py
+++ b/src/sage/modules/free_module_homspace.py
@@ -28,6 +28,7 @@
        to Ambient free module of rank 2 over the principal ideal domain Integer Ring
        in Category of finite dimensional modules with basis over
           (Dedekind domains and euclidean domains
+           and noetherian rings
            and infinite enumerated sets and metric spaces)
     sage: B = H.basis()
     sage: len(B)
diff --git a/src/sage/modules/matrix_morphism.py b/src/sage/modules/matrix_morphism.py
index 4b1f2af7e0d..ce2b88ede82 100644
--- a/src/sage/modules/matrix_morphism.py
+++ b/src/sage/modules/matrix_morphism.py
@@ -1607,7 +1607,7 @@ def matrix(self, side=None):
 
         INPUT:
 
-        - ``side`` -- (default: ``'None'``) the side of the matrix
+        - ``side`` -- (default: ``None``) the side of the matrix
           where a vector is placed to effect the morphism (function)
 
         OUTPUT:
diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py
index 590a20eaa1a..ee5e8df0257 100644
--- a/src/sage/monoids/free_abelian_monoid.py
+++ b/src/sage/monoids/free_abelian_monoid.py
@@ -169,6 +169,10 @@ def is_FreeAbelianMonoid(x):
 
         sage: from sage.monoids.free_abelian_monoid import is_FreeAbelianMonoid
         sage: is_FreeAbelianMonoid(5)
+        doctest:warning...
+        DeprecationWarning: the function is_FreeAbelianMonoid is deprecated;
+        use 'isinstance(..., FreeAbelianMonoid_class)' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_FreeAbelianMonoid(FreeAbelianMonoid(7,'a'))
         True
@@ -177,6 +181,8 @@ def is_FreeAbelianMonoid(x):
         sage: is_FreeAbelianMonoid(FreeMonoid(0,''))
         False
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_FreeAbelianMonoid is deprecated; use 'isinstance(..., FreeAbelianMonoid_class)' instead")
     return isinstance(x, FreeAbelianMonoid_class)
 
 
diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py
index 88277320a63..3e8ed837cf8 100644
--- a/src/sage/monoids/free_monoid.py
+++ b/src/sage/monoids/free_monoid.py
@@ -44,6 +44,10 @@ def is_FreeMonoid(x):
 
         sage: from sage.monoids.free_monoid import is_FreeMonoid
         sage: is_FreeMonoid(5)
+        doctest:warning...
+        DeprecationWarning: the function is_FreeMonoid is deprecated;
+        use 'isinstance(..., (FreeMonoid, IndexedFreeMonoid))' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_FreeMonoid(FreeMonoid(7,'a'))
         True
@@ -56,6 +60,8 @@ def is_FreeMonoid(x):
         sage: is_FreeMonoid(FreeAbelianMonoid(index_set=ZZ))
         False
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_FreeMonoid is deprecated; use 'isinstance(..., (FreeMonoid, IndexedFreeMonoid))' instead")
     if isinstance(x, FreeMonoid):
         return True
     from sage.monoids.indexed_free_monoid import IndexedFreeMonoid
diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py
index 97c0dfa6ad3..c0330ad0eb3 100644
--- a/src/sage/monoids/monoid.py
+++ b/src/sage/monoids/monoid.py
@@ -14,6 +14,10 @@ def is_Monoid(x) -> bool:
 
         sage: from sage.monoids.monoid import is_Monoid
         sage: is_Monoid(0)
+        doctest:warning...
+        DeprecationWarning: the function is_Monoid is deprecated;
+        use 'isinstance(..., Monoid_class)' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_Monoid(ZZ)   # The technical math meaning of monoid has
         ....:                 # no bearing whatsoever on the result: it's
@@ -26,6 +30,8 @@ def is_Monoid(x) -> bool:
         sage: is_Monoid(F)
         True
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_Monoid is deprecated; use 'isinstance(..., Monoid_class)' instead")
     return isinstance(x, Monoid_class)
 
 
diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx
index 368b9e4b0e3..c8aecbf6482 100644
--- a/src/sage/numerical/mip.pyx
+++ b/src/sage/numerical/mip.pyx
@@ -2852,7 +2852,7 @@ cdef class MixedIntegerLinearProgram(SageObject):
 
               The command ::
 
-                  sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX
+                  sage: p = MixedIntegerLinearProgram(solver="CPLEX")   # optional - CPLEX
                   sage: p.solver_parameter("CPX_PARAM_TILIM", 60)       # optional - CPLEX
 
               works as intended.
diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py
index 54262183b1b..1d8e9fafd57 100644
--- a/src/sage/numerical/optimize.py
+++ b/src/sage/numerical/optimize.py
@@ -346,7 +346,7 @@ def minimize(func, x0, gradient=None, hessian=None, algorithm="default",
 
         sage: vars = var('x y z')                                                       # needs sage.symbolic
         sage: f = 100*(y-x^2)^2 + (1-x)^2 + 100*(z-y^2)^2 + (1-y)^2                     # needs sage.symbolic
-        sage: minimize(f, [.1,.3,.4]) # abs tol 1e-6                                    # needs sage.symbolic
+        sage: minimize(f, [.1,.3,.4])  # abs tol 1e-6                                   # needs sage.symbolic
         (1.0, 1.0, 1.0)
 
     Try the newton-conjugate gradient method; the gradient and hessian are
diff --git a/src/sage/plot/plot3d/implicit_surface.pyx b/src/sage/plot/plot3d/implicit_surface.pyx
index df28f7c6e6b..581eddff2d4 100644
--- a/src/sage/plot/plot3d/implicit_surface.pyx
+++ b/src/sage/plot/plot3d/implicit_surface.pyx
@@ -522,7 +522,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    y_vertices[y,z] = v
+                    y_vertices[y,z] = v
                 else:
                     y_vertices[y,z] = None
 
@@ -556,7 +556,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    z_vertices[y,z] = v
+                    z_vertices[y,z] = v
                 else:
                     z_vertices[y,z] = None
 
@@ -631,7 +631,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    x_vertices[y,z] = v
+                    x_vertices[y,z] = v
                 else:
                     x_vertices[y,z] = None
 
diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py
index 2c36107a577..69a5f3be93b 100755
--- a/src/sage/quadratic_forms/binary_qf.py
+++ b/src/sage/quadratic_forms/binary_qf.py
@@ -1806,7 +1806,7 @@ def solve_integer(self, n, *, algorithm="general", _flag=2):
         sol = self.__pari__().qfbsolve(n, _flag)
         if _flag == 2:
             return tuple(map(ZZ, sol)) if sol else None
-        return list(map(lambda tup: tuple(map(ZZ, tup)), sol))
+        return [tuple(map(ZZ, tup)) for tup in sol]
 
     def form_class(self):
         r"""
diff --git a/src/sage/quadratic_forms/bqf_class_group.py b/src/sage/quadratic_forms/bqf_class_group.py
index 6d3aefed881..4dc8d588110 100644
--- a/src/sage/quadratic_forms/bqf_class_group.py
+++ b/src/sage/quadratic_forms/bqf_class_group.py
@@ -140,7 +140,7 @@ def __init__(self, D, *, check=True):
         """
         self._disc = ZZ(D)
         if check:
-            if not self._disc or self._disc % 4 not in (0,1):
+            if not self._disc or self._disc % 4 not in (0, 1):
                 raise ValueError('not a discriminant')
             if self._disc > 0:
                 raise NotImplementedError('positive discriminants are not yet supported')
@@ -217,7 +217,7 @@ def random_element(self):
         c = (b**2 - self._disc) // (4*a)
         if randrange(2):
             b = -b
-        return self(BinaryQF([a,b,c]))
+        return self(BinaryQF([a, b, c]))
 
     def __hash__(self):
         r"""
@@ -447,8 +447,8 @@ def _neg_(self):
             sage: cl + (-cl) == cl.parent().zero()  # indirect doctest
             True
         """
-        a,b,c = self._form
-        F = BinaryQF([a,-b,c])
+        a, b, c = self._form
+        F = BinaryQF([a, -b, c])
         return BQFClassGroup_element(F, parent=self.parent())
 
     def _add_(self, other):
@@ -677,14 +677,14 @@ def _project_bqf(bqf, q):
     """
     q2 = q**2
     disc = bqf.discriminant()
-    if not q2.divides(disc) or disc//q2 % 4 not in (0,1):
+    if not q2.divides(disc) or disc//q2 % 4 not in (0, 1):
         raise ValueError('discriminant not divisible by q^2')
 
-    a,b,c = bqf
+    a, b, c = bqf
 
     # lucky case: q^2|c (and q|b)
     if q2.divides(c):
-        a,b,c = c,-b,a
+        a, b, c = c, -b, a
 
     # general case: neither q^2|a nor q^2|c
     elif not q2.divides(a):
@@ -704,21 +704,22 @@ def _project_bqf(bqf, q):
             assert False
 
         # find equivalent form with q^2|a (and q|b)
-        u,v = map(ZZ, (u,v))
-        assert q2.divides(bqf(u,v))
+        u, v = map(ZZ, (u, v))
+        assert q2.divides(bqf(u, v))
         if not v:
             v += q
-        g,r,s = u.xgcd(v)
+        g, r, s = u.xgcd(v)
         assert g.is_one()
-        M = matrix(ZZ, [[u,-v],[s,r]])
+        M = matrix(ZZ, [[u, -v], [s, r]])
         assert M.det().is_one()
-        a,b,c = bqf * M
+        a, b, c = bqf * M
 
     # remaining case: q^2|a (and q|b)
     assert q2.divides(a)
     assert q.divides(b)
     return BinaryQF(a//q2, b//q, c)
 
+
 class BQFClassGroupQuotientMorphism(Morphism):
     r"""
     Let `D` be a discriminant and `f > 0` an integer.
@@ -801,7 +802,7 @@ def _call_(self, elt):
         ALGORITHM: Repeated application of :func:`_project_bqf` for the prime factors in `f`.
         """
         bqf = elt.form()
-        for q,m in self.f:
+        for q, m in self.f:
             for _ in range(m):
                 bqf = _project_bqf(bqf, q)
         return self.codomain()(bqf)
diff --git a/src/sage/quadratic_forms/constructions.py b/src/sage/quadratic_forms/constructions.py
index 2fe117d2e29..da51beb1c2e 100644
--- a/src/sage/quadratic_forms/constructions.py
+++ b/src/sage/quadratic_forms/constructions.py
@@ -50,7 +50,7 @@ def BezoutianQuadraticForm(f, g):
 
     # Initialize the quadratic form
     R = f.base_ring()
-    P = PolynomialRing(R, ['x','y'])
+    P = PolynomialRing(R, ['x', 'y'])
     a, b = P.gens()
     n = max(f.degree(), g.degree())
     Q = QuadraticForm(R, n)
@@ -60,9 +60,9 @@ def BezoutianQuadraticForm(f, g):
     for i in range(n):
         for j in range(i, n):
             if i == j:
-                Q[i,j] = bez_poly.coefficient({a:i,b:j})
+                Q[i, j] = bez_poly.coefficient({a: i, b: j})
             else:
-                Q[i,j] = bez_poly.coefficient({a:i,b:j}) * 2
+                Q[i, j] = bez_poly.coefficient({a: i, b: j}) * 2
 
     return Q
 
diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py
index 829b4783226..7d739ab9eb1 100644
--- a/src/sage/quadratic_forms/genera/normal_form.py
+++ b/src/sage/quadratic_forms/genera/normal_form.py
@@ -539,7 +539,7 @@ def _homogeneous_normal_form(G, w):
         e1 = D[-2, -2].unit_part()
         e2 = D[-1, -1].unit_part()
         e = {e1, e2}
-        E = [{3, 3}, {3, 5}, {5, 5}, {5, 7}]
+        E = [{3}, {3, 5}, {5}, {5, 7}]
         if e in E:
             B[-2:, :] = _relations(D[-2:, -2:], 1) * B[-2:, :]
             D = B * G * B.T
diff --git a/src/sage/quadratic_forms/quadratic_form__local_density_interfaces.py b/src/sage/quadratic_forms/quadratic_form__local_density_interfaces.py
index ec39e489494..2e5be4f59de 100644
--- a/src/sage/quadratic_forms/quadratic_form__local_density_interfaces.py
+++ b/src/sage/quadratic_forms/quadratic_form__local_density_interfaces.py
@@ -47,12 +47,13 @@ def local_density(self, p, m):
     #                                                     TO DO:  Write a separate p-scale and p-norm routines!
     Q_local = self.local_normal_form(p)
     if n == 1:
-        p_valuation = valuation(Q_local[0,0], p)
+        p_valuation = valuation(Q_local[0, 0], p)
     else:
-        p_valuation = min(valuation(Q_local[0,0], p), valuation(Q_local[0,1], p))
+        p_valuation = min(valuation(Q_local[0, 0], p),
+                          valuation(Q_local[0, 1], p))
 
     # If m is less p-divisible than the matrix, return zero
-    if ((m != 0) and (valuation(m,p) < p_valuation)):   # Note: The (m != 0) condition protects taking the valuation of zero.
+    if ((m != 0) and (valuation(m, p) < p_valuation)):   # Note: The (m != 0) condition protects taking the valuation of zero.
         return QQ(0)
 
     # If the form is imprimitive, rescale it and call the local density routine
@@ -119,16 +120,17 @@ def local_primitive_density(self, p, m):
     #                                                     TO DO:  Write a separate p-scale and p-norm routines!
     Q_local = self.local_normal_form(p)
     if n == 1:
-        p_valuation = valuation(Q_local[0,0], p)
+        p_valuation = valuation(Q_local[0, 0], p)
     else:
-        p_valuation = min(valuation(Q_local[0,0], p), valuation(Q_local[0,1], p))
+        p_valuation = min(valuation(Q_local[0, 0], p),
+                          valuation(Q_local[0, 1], p))
 
     # If m is less p-divisible than the matrix, return zero
-    if ((m != 0) and (valuation(m,p) < p_valuation)):   # Note: The (m != 0) condition protects taking the valuation of zero.
-        return QQ(0)
+    if m != 0 and valuation(m, p) < p_valuation:   # Note: The (m != 0) condition protects taking the valuation of zero.
+        return QQ.zero()
 
     # If the form is imprimitive, rescale it and call the local density routine
-    p_adjustment = QQ(1) / p**p_valuation
+    p_adjustment = QQ.one() / p**p_valuation
     m_prim = QQ(m) / p**p_valuation
     Q_prim = Q_local.scale_by_factor(p_adjustment)
 
diff --git a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py
index aff8dad2d62..4bfbfa3bb6a 100644
--- a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py
+++ b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py
@@ -237,12 +237,12 @@ def _rational_diagonal_form_and_transformation(self):
         # Diagonal matrix
         D = MS()
         for i in range(n):
-            D[i,i] = R[i,i]
+            D[i, i] = R[i, i]
         Q = Q.parent()(D)
         # Transformation matrix (inverted)
         T = MS(R.sage())
         for i in range(n):
-            T[i,i] = K.one()
+            T[i, i] = K.one()
         try:
             return Q, ~T
         except ZeroDivisionError:
@@ -256,13 +256,13 @@ def _rational_diagonal_form_and_transformation(self):
     for i in range(n):
 
         # Deal with rows where the diagonal entry is zero.
-        if Q[i,i] == 0:
+        if Q[i, i] == 0:
 
             # Look for a non-zero entry and use it to make the diagonal non-zero (if it exists)
-            for j in range(i+1, n):
-                if Q[i,j] != 0:
+            for j in range(i + 1, n):
+                if Q[i, j] != 0:
                     temp = MS(1)
-                    if Q[i,j] + Q[j,j] == 0:
+                    if Q[i, j] + Q[j, j] == 0:
                         temp[j, i] = -1
                     else:
                         temp[j, i] = 1
@@ -274,9 +274,9 @@ def _rational_diagonal_form_and_transformation(self):
 
         # Create a matrix which deals with off-diagonal entries (all at once for each row)
         temp = MS(1)
-        for j in range(i+1, n):
-            if Q[i,j] != 0:
-                temp[i,j] = -Q[i,j] / (Q[i,i] * 2)    # This should only occur when Q[i,i] != 0, which the above step guarantees.
+        for j in range(i + 1, n):
+            if Q[i, j] != 0:
+                temp[i, j] = -Q[i, j] / (Q[i, i] * 2)    # This should only occur when Q[i,i] != 0, which the above step guarantees.
 
         Q = Q(temp)
         T = T * temp
@@ -325,9 +325,9 @@ def signature_vector(self):
     n = 0
     z = 0
     for i in range(diag.dim()):
-        if diag[i,i] > 0:
+        if diag[i, i] > 0:
             p += 1
-        elif diag[i,i] < 0:
+        elif diag[i, i] < 0:
             n += 1
         else:
             z += 1
@@ -448,14 +448,16 @@ def hasse_invariant(self, p):
     n = Diag.dim()
 
     if R == QQ:
-        for j in range(n-1):
-            for k in range(j+1, n):
-                hasse_temp = hasse_temp * hilbert_symbol(Diag[j,j], Diag[k,k], p)
+        for j in range(n - 1):
+            for k in range(j + 1, n):
+                hasse_temp = hasse_temp * hilbert_symbol(Diag[j, j],
+                                                         Diag[k, k], p)
 
     else:
-        for j in range(n-1):
-            for k in range(j+1, n):
-                hasse_temp = hasse_temp * R.hilbert_symbol(Diag[j,j], Diag[k,k], p)
+        for j in range(n - 1):
+            for k in range(j + 1, n):
+                hasse_temp = hasse_temp * R.hilbert_symbol(Diag[j, j],
+                                                           Diag[k, k], p)
 
     return hasse_temp
 
@@ -532,17 +534,19 @@ def hasse_invariant__OMeara(self, p):
     if R == QQ:
         for j in range(n):
             for k in range(j, n):
-                hasse_temp = hasse_temp * hilbert_symbol(Diag[j,j], Diag[k,k], p)
+                hasse_temp = hasse_temp * hilbert_symbol(Diag[j, j],
+                                                         Diag[k, k], p)
 
     else:
         for j in range(n):
             for k in range(j, n):
-                hasse_temp = hasse_temp * R.hilbert_symbol(Diag[j,j], Diag[k,k], p)
+                hasse_temp = hasse_temp * R.hilbert_symbol(Diag[j, j],
+                                                           Diag[k, k], p)
 
     return hasse_temp
 
 
-def is_hyperbolic(self, p):
+def is_hyperbolic(self, p) -> bool:
     r"""
     Check if the quadratic form is a sum of hyperbolic planes over
     the `p`-adic numbers `\QQ_p` or over the real numbers `\RR`.
@@ -895,8 +899,8 @@ def compute_definiteness_string_by_determinants(self):
         return "degenerate"
 
     # Check the sign of the ratios of consecutive determinants of the upper triangular r x r submatrices
-    first_coeff = self[0,0]
-    for r in range(1,n+1):
+    first_coeff = self[0, 0]
+    for r in range(1, n + 1):
         I = list(range(r))
         new_det = M.matrix_from_rows_and_columns(I, I).det()
 
diff --git a/src/sage/quadratic_forms/quadratic_form__neighbors.py b/src/sage/quadratic_forms/quadratic_form__neighbors.py
index 0486e0a4acb..25566313812 100644
--- a/src/sage/quadratic_forms/quadratic_form__neighbors.py
+++ b/src/sage/quadratic_forms/quadratic_form__neighbors.py
@@ -249,7 +249,7 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
     return QF(Gnew)
 
 
-def neighbor_iteration(seeds, p, mass=None, max_classes=ZZ(10)**3,
+def neighbor_iteration(seeds, p, mass=None, max_classes=None,
                        algorithm=None, max_neighbors=1000, verbose=False):
     r"""
     Return all classes in the `p`-neighbor graph of ``self``.
@@ -308,9 +308,11 @@ def neighbor_iteration(seeds, p, mass=None, max_classes=ZZ(10)**3,
         Warning: not all classes in the genus were found
         []
     """
-    p = ZZ(p)
     from sage.quadratic_forms.quadratic_form import QuadraticForm
     from warnings import warn
+    p = ZZ(p)
+    if max_classes is None:
+        max_classes = 1000
     if not all(isinstance(s, QuadraticForm) for s in seeds):
         raise ValueError("seeds must be a list of quadratic forms")
     if algorithm is None:
diff --git a/src/sage/quadratic_forms/random_quadraticform.py b/src/sage/quadratic_forms/random_quadraticform.py
index e372c625f57..0d751e1f584 100644
--- a/src/sage/quadratic_forms/random_quadraticform.py
+++ b/src/sage/quadratic_forms/random_quadraticform.py
@@ -13,7 +13,7 @@
 # Routines to create a random quadratic form ##
 ################################################
 
-def random_quadraticform(R, n, rand_arg_list=[]):
+def random_quadraticform(R, n, rand_arg_list=None):
     r"""
     Create a random quadratic form in `n` variables defined over the ring `R`.
 
@@ -57,6 +57,8 @@ def random_quadraticform(R, n, rand_arg_list=[]):
         ...
         TypeError: the list of randomness arguments can have at most 3 elements
     """
+    if rand_arg_list is None:
+        rand_arg_list = []
     if len(rand_arg_list) > 3:
         raise TypeError("the list of randomness arguments can have "
                         "at most 3 elements")
@@ -72,7 +74,7 @@ def random_quadraticform(R, n, rand_arg_list=[]):
 
 
 def random_quadraticform_with_conditions(R, n, condition_list=[],
-                                         rand_arg_list=[]):
+                                         rand_arg_list=None):
     """
     Create a random quadratic form in `n` variables defined over the ring `R`
     satisfying a list of boolean (i.e. True/False) conditions.
@@ -95,6 +97,9 @@ def random_quadraticform_with_conditions(R, n, condition_list=[],
         [ * 2 2 ]
         [ * * 3 ]
     """
+    if rand_arg_list is None:
+        rand_arg_list = []
+
     Q = random_quadraticform(R, n, rand_arg_list)
     done_flag = True
 
@@ -119,7 +124,7 @@ def random_quadraticform_with_conditions(R, n, condition_list=[],
     return Q
 
 
-def random_ternaryqf(rand_arg_list=[]):
+def random_ternaryqf(rand_arg_list=None):
     """
     Create a random ternary quadratic form.
 
@@ -149,15 +154,16 @@ def random_ternaryqf(rand_arg_list=[]):
         [7 -8 2]
         [0 3 -6]
     """
-    R = ZZ
+    if rand_arg_list is None:
+        rand_arg_list = []
     if not rand_arg_list:
-        rand_list = [R.random_element() for _ in range(6)]
+        rand_list = [ZZ.random_element() for _ in range(6)]
     else:
-        rand_list = [R.random_element(*rand_arg_list) for _ in range(6)]
+        rand_list = [ZZ.random_element(*rand_arg_list) for _ in range(6)]
     return TernaryQF(rand_list)
 
 
-def random_ternaryqf_with_conditions(condition_list=[], rand_arg_list=[]):
+def random_ternaryqf_with_conditions(condition_list=[], rand_arg_list=None):
     """
     Create a random ternary quadratic form satisfying a list of boolean
     (i.e. True/False) conditions.
@@ -179,6 +185,8 @@ def random_ternaryqf_with_conditions(condition_list=[], rand_arg_list=[]):
         [3 4 2]
         [2 -2 -1]
     """
+    if rand_arg_list is None:
+        rand_arg_list = []
     Q = random_ternaryqf(rand_arg_list)
     done_flag = True
 
diff --git a/src/sage/quadratic_forms/ternary_qf.py b/src/sage/quadratic_forms/ternary_qf.py
index d5384632694..cb7eeb179c8 100644
--- a/src/sage/quadratic_forms/ternary_qf.py
+++ b/src/sage/quadratic_forms/ternary_qf.py
@@ -837,8 +837,8 @@ def pseudorandom_primitive_zero_mod_p(self, p):
         a, b, c, r, s, t = self.coefficients()
         while True:
 
-            r1 = randint(0,p-1)
-            r2 = randint(0,p-1)
+            r1 = randint(0, p-1)
+            r2 = randint(0, p-1)
             alpha = (b*r1**2+t*r1+a) % p
             if alpha != 0:
 
diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py
index 5398270160e..72938a799ff 100644
--- a/src/sage/repl/ipython_kernel/kernel.py
+++ b/src/sage/repl/ipython_kernel/kernel.py
@@ -100,62 +100,68 @@ def help_links(self):
             sage: sk = SageKernel.__new__(SageKernel)
             sage: sk.help_links
             [{'text': 'Sage Documentation',
-              'url': 'https://doc.sagemath.org/html/en/index.html'},
+              'url': '.../html/en/index.html'},
              ...]
         """
-        # DEPRECATED: The URLs in the form 'kernelspecs/...' were used for
-        # classical Jupyter notebooks. For instance,
-        #
-        #  'kernelspecs/sagemath/doc/html/en/index.html'
-        #
-        # is constructed by kernel_url('doc/html/en/index.html'), but these
-        # URLs of local files don't work for JupyterLab. Hence all URLs here
-        # have been replaced with URLs of online documents.
-
-        from sage.repl.ipython_kernel.install import SageKernelSpec
-        identifier = SageKernelSpec.identifier()
-
-        def kernel_url(x):
-            # URLs starting with 'kernelspecs' are prepended by the
-            # browser with the appropriate path
-            return 'kernelspecs/{0}/{1}'.format(identifier, x)
+        # A Sage doc server starts when Jupyter notebook launches if the Sage
+        # documentation is available locally.  See the corresponding code in
+        # src/bin/sage-notebook.
+
+        from sage.env import SAGE_DOC_SERVER_URL
+        from sage.features.sagemath import sagemath_doc_html
+
+        if SAGE_DOC_SERVER_URL:
+            def doc_url(path):
+                return f'{SAGE_DOC_SERVER_URL}/{path}'
+        elif sagemath_doc_html().is_present():
+            from sage.env import SAGE_DOC_LOCAL_PORT as port
+
+            def doc_url(path):
+                return f'http://127.0.0.1:{port}/{path}'
+        else:
+            def doc_url(path):
+                return f'https://doc.sagemath.org/{path}'
 
         return [
             {
                 'text': 'Sage Documentation',
-                'url': "https://doc.sagemath.org/html/en/index.html",
+                'url': doc_url('html/en/index.html'),
             },
             {
                 'text': 'A Tour of Sage',
-                'url': "https://doc.sagemath.org/html/en/a_tour_of_sage/index.html",
+                'url': doc_url('html/en/a_tour_of_sage/index.html'),
             },
             {
                 'text': 'Tutorial',
-                'url': "https://doc.sagemath.org/html/en/tutorial/index.html",
+                'url': doc_url('html/en/tutorial/index.html'),
             },
             {
                 'text': 'Thematic Tutorials',
-                'url': "https://doc.sagemath.org/html/en/thematic_tutorials/index.html",
+                'url': doc_url('html/en/thematic_tutorials/index.html'),
             },
             {
                 'text': 'PREP Tutorials',
-                'url': "https://doc.sagemath.org/html/en/prep/index.html",
+                'url': doc_url('html/en/prep/index.html'),
             },
             {
                 'text': 'Constructions',
-                'url': "https://doc.sagemath.org/html/en/constructions/index.html",
+                'url': doc_url('html/en/constructions/index.html'),
             },
             {
                 'text': 'FAQ',
-                'url': "https://doc.sagemath.org/html/en/faq/index.html",
+                'url': doc_url('html/en/faq/index.html'),
+            },
+            {
+                'text': 'Reference Manual',
+                'url': doc_url('html/en/reference/index.html'),
             },
             {
-                'text': 'Reference',
-                'url': "https://doc.sagemath.org/html/en/reference/index.html",
+                'text': "Installation Guide",
+                'url': doc_url('html/en/installation/index.html'),
             },
             {
-                'text': "Developer's Guide",
-                'url': "https://doc.sagemath.org/html/en/developer/index.html",
+                'text': "Developer Guide",
+                'url': doc_url('html/en/developer/index.html'),
             },
             {
                 'text': "Python",
diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx
index d272a8e81db..ea17b530ceb 100644
--- a/src/sage/rings/complex_interval.pyx
+++ b/src/sage/rings/complex_interval.pyx
@@ -791,16 +791,16 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: b = CIF(-1, (2, 3))
             sage: c = a/b
             sage: c.endpoints()
-            (0.500000000000000 - 1.60000000000000*I,
-            1.50000000000000 - 0.600000000000000*I,
-            0.500000000000000 - 0.600000000000000*I,
-            1.50000000000000 - 1.60000000000000*I)
+            (0.200000000000000 - 2.00000000000000*I,
+             2.30000000000000 - 0.500000000000000*I,
+             0.200000000000000 - 0.500000000000000*I,
+             2.30000000000000 - 2.00000000000000*I)
             sage: c = b/a
             sage: c.endpoints()
-            (0.246153846153846 + 0.317647058823529*I,
-            0.841176470588236 + 0.761538461538462*I,
-            0.246153846153846 + 0.761538461538462*I,
-            0.841176470588236 + 0.317647058823529*I)
+            (0.100000000000000 + 0.250000000000000*I,
+             1.15000000000000 + 1.00000000000000*I,
+             0.100000000000000 + 1.00000000000000*I,
+             1.15000000000000 + 0.250000000000000*I)
         """
         return self * right.__invert__()
 
@@ -1123,14 +1123,14 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: I = CIF.0
             sage: a = ~(5+I) # indirect doctest
             sage: a * (5+I)
-            1.000000000000000? + -1.?e-16*I
+            1.000000000000000? + 0.?e-16*I
             sage: a = CIF((1, 2), (3, 4))
             sage: c = a.__invert__()
             sage: c.endpoints()
-            (0.0588235294117647 - 0.300000000000000*I,
-            0.153846153846154 - 0.200000000000000*I,
-            0.0588235294117647 - 0.200000000000000*I,
-            0.153846153846154 - 0.300000000000000*I)
+            (0.0500000000000000 - 0.400000000000000*I,
+             0.200000000000000 - 0.150000000000000*I,
+             0.0500000000000000 - 0.150000000000000*I,
+             0.200000000000000 - 0.400000000000000*I)
 
         TESTS:
 
@@ -1146,266 +1146,36 @@ cdef class ComplexIntervalFieldElement(FieldElement):
 
         Test that the bug reported in :issue:`25414` has been fixed::
 
-            sage: 1 / CIF(RIF(-1,1),0)
-            [.. NaN ..] + [.. NaN ..]*I
+            sage: 1 / CIF(RIF(-1 ,1), 0)
+            [-infinity .. +infinity] + [0.0000000000000000 .. +infinity]*I
+
+        Test that the bug reported in :issue:`37927` is fixed::
+
+            sage: (961 * (1 / CIF(0, 31))**2 + 1).contains_zero()
+            True
 
         REFERENCES:
 
         - [RL1971]_
         """
-        # Constructor sets intervals for real and imaginary part to NaN
+        cdef ComplexIntervalFieldElement x
         x = self._new()
 
-        if mpfi_nan_p(self.__re) or mpfi_nan_p(self.__im):
-            # Early bail
-            return x
+        cdef mpfi_t t0, t1
+        mpfi_init2(t0, self._prec)
+        mpfi_init2(t1, self._prec)
 
-        # We checked for NaN, so we can assume we have
-        # valid intervals now.
-
-        # Allocate memory
-        cdef mpfr_t a, b, c, d
-        mpfr_init2(a, self._prec)
-        mpfr_init2(b, self._prec)
-        mpfr_init2(c, self._prec)
-        mpfr_init2(d, self._prec)
-
-        cdef mpfr_t rmin, rmax, imin, imax
-        mpfr_init2(rmin, self._prec)
-        mpfr_init2(rmax, self._prec)
-        mpfr_init2(imin, self._prec)
-        mpfr_init2(imax, self._prec)
-
-        cdef mpfr_t r
-        mpfr_init2(r, self._prec)
-
-        cdef mpfr_t a2, b2, d2, c2
-        mpfr_init2(a2, self._prec)
-        mpfr_init2(b2, self._prec)
-        mpfr_init2(c2, self._prec)
-        mpfr_init2(d2, self._prec)
-
-        cdef mpfr_t div1, div2, aux, aux2
-        mpfr_init2(div1, self._prec)
-        mpfr_init2(div2, self._prec)
-        mpfr_init2(aux, self._prec)
-        mpfr_init2(aux2, self._prec)
-
-        # Variables to remember what we do to make the complex
-        # interval lie in the first quadrant or cross the line between
-        # first and second quadrant.
-        cdef int flipped_real_imag = 0
-        cdef int negated_real = 0
-        cdef int negated_imag = 0
-
-        # Get endpoints of real and imaginary part
-        mpfi_get_left(a, self.__re)
-        mpfi_get_right(b, self.__re)
-        mpfi_get_left(c, self.__im)
-        mpfi_get_right(d, self.__im)
-
-        # In the next three steps, we try to make the interval lie in the
-        # first quadrant or cross the line between the first and second
-        # quadrant.
-
-        # First, we flip the complex plane about the diagonal if the
-        # input interval crosses the real line.
-        if mpfr_sgn(c) < 0 and mpfr_sgn(d) > 0:
-            # Switch real and imaginary part.
-            flipped_real_imag = 1
-            mpfr_swap(a, c)
-            mpfr_swap(b, d)
-
-        # Second, we flip the complex plane about the real line if
-        # part of the interval is (still) below the real line.
-        if mpfr_sgn(c) < 0:
-            # Negate imaginary part interval.
-            negated_imag = 1
-            mpfr_swap(c, d)
-            mpfr_neg(c, c, MPFR_RNDD)
-            mpfr_neg(d, d, MPFR_RNDU)
-
-        # Third, we flip the complex plane about the imaginary line if
-        # the interval is entirely to the left of the imaginary line
-        # (or touches it).
-        if mpfr_sgn(b) <= 0:
-            # Negate real part
-            negated_real = 1
-            mpfr_swap(a, b)
-            mpfr_neg(a, a, MPFR_RNDD)
-            mpfr_neg(b, b, MPFR_RNDU)
-
-        # The last step ensures that the interval for the real part
-        # always contains a non-negative number.
-
-        # The imaginary part could still contain a negative number, but
-        # only if the input interval contained zero to begin with in
-        # which case we will return NaN anyway. We check for this in
-        # the below branches with mpfr_sgn(c).
-
-        # We now distinguish between the cases where the interval
-        # is entirely contained in the first quadrant and where it is
-        # crossing the line between the first and second quadrant.
-        if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0:
-            # Input interval lies in first quadrant
-
-            # Computation follows Rokne-Lancaster.
-
-            # Left endpoint
-            mpfr_mul(a2, a, a, MPFR_RNDU)
-            mpfr_mul(b2, b, b, MPFR_RNDU)
-            mpfr_mul(d2, d, d, MPFR_RNDU)
-            mpfr_add(div1, a2, d2, MPFR_RNDU)
-            mpfr_add(div2, b2, d2, MPFR_RNDU)
-            mpfr_div(rmin, a, div1, MPFR_RNDD)
-            mpfr_div(aux, b, div2, MPFR_RNDD)
-            mpfr_min(rmin, rmin, aux, MPFR_RNDD)
-            # Higher endpoint
-            mpfr_mul(c2, c, c, MPFR_RNDU)
-            mpfr_add(div1, b2, c2, MPFR_RNDU)
-            mpfr_div(imax, c, div1, MPFR_RNDU)
-            mpfr_set_si(aux, 0, MPFR_RNDD)
-            mpfr_sub(imax, aux, imax, MPFR_RNDU)
-            mpfr_div(aux2, d, div2, MPFR_RNDU)
-            mpfr_sub(aux2, aux, aux2, MPFR_RNDU)
-            mpfr_max(imax, aux2, imax, MPFR_RNDU)
-            # Lower endpoint, it is the lowest point of the circle or one of
-            if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0:
-                mpfr_add(imin, a, a, MPFR_RNDD)
-                mpfr_set_si(aux, -1, MPFR_RNDD)
-                mpfr_div(imin, aux, imin, MPFR_RNDD)
-            elif mpfr_cmp(c, a) > 0:
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(imin, c, div1, MPFR_RNDU)
-                mpfr_set_si(aux, 0, MPFR_RNDD)
-                mpfr_sub(imin, aux, imin, MPFR_RNDD)
-            else:
-                mpfr_mul(d2, d, d, MPFR_RNDD)
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_add(div1, a2, d2, MPFR_RNDD)
-                mpfr_div(imin, d, div1, MPFR_RNDU)
-                mpfr_set_si(aux, 0, MPFR_RNDD)
-                mpfr_sub(imin, aux, imin, MPFR_RNDD)
-            # Right endpoint
-            if mpfr_cmp(c, a) >=0 and mpfr_cmp(b, c) >= 0:
-                mpfr_add(rmax, c, c, MPFR_RNDD)
-                mpfr_set_si(aux, 1, MPFR_RNDU)
-                mpfr_div(rmax, aux, rmax, MPFR_RNDU)
-            elif mpfr_cmp(a,c) > 0:
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(rmax, a, div1, MPFR_RNDU)
-            else:
-                mpfr_mul(b2, b, b, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, b2, c2, MPFR_RNDD)
-                mpfr_div(rmax, b, div1, MPFR_RNDU)
-        elif mpfr_sgn(c) > 0:
-            # Input interval crosses line between first and second quadrant.
-
-            # Computation follows Rokne-Lancaster.
-
-            # Left endpoint
-            mpfr_abs(aux, a, MPFR_RNDU)
-            if mpfr_cmp(aux, c) >= 0:
-                mpfr_set_str(aux, '-0.5', 10, MPFR_RNDD)
-                mpfr_div(rmin, aux, c, MPFR_RNDD)
-            else:
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(rmin, a, div1, MPFR_RNDU)
-            # Lower endpoint
-            mpfr_set_si(aux2, -1, MPFR_RNDD)
-            mpfr_div(imin, aux2, c, MPFR_RNDD)
-            # Right endpoint
-            if mpfr_cmp(b, c) >=0:
-                mpfr_set_str(aux2, '0.5', 10, MPFR_RNDU)
-                mpfr_div(rmax, aux2, c, MPFR_RNDU)
-            else:
-                mpfr_mul(b2, b, b, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, b2, c2, MPFR_RNDD)
-                mpfr_div(rmax, b, div1, MPFR_RNDU)
-            # Upper endpoint
-            mpfr_mul(a2, a, a, MPFR_RNDU)
-            mpfr_mul(b2, b, b, MPFR_RNDU)
-            mpfr_mul(c2, c, c, MPFR_RNDU)
-            mpfr_mul(d2, d, d, MPFR_RNDU)
-            mpfr_add(div1, a2, c2, MPFR_RNDU)
-            mpfr_div(imax, c, div1, MPFR_RNDD)
-            mpfr_add(div1, b2, c2, MPFR_RNDU)
-            mpfr_div(aux, c, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_add(div1, a2, d2, MPFR_RNDU)
-            mpfr_div(aux, d, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_add(div1, b2, d2, MPFR_RNDU)
-            mpfr_div(aux, d, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_set_zero(aux, -1)
-            mpfr_sub(imax, aux, imax, MPFR_RNDU)
-        else:
-            # The interval must have contained the origin.
+        mpfi_sqr(t0, self.__re)
+        mpfi_sqr(t1, self.__im)
 
-            # Return NaN intervals by doing nothing
-            # (rmin, rmax, imin, imax were initialized as NaN)
-            #
-            # Note that we cannot "return x" here since
-            # that would not call mpfr_clear and produce a memory leak
-            pass
+        mpfi_add(t0, t0, t1)         # now t0 is the norm
+        mpfi_div(x.__re, self.__re, t0)   #     x.__re = self.__re/norm
+
+        mpfi_neg(t1, self.__im)
+        mpfi_div(x.__im, t1, t0)  #     x.__im = -self.__im/norm
 
-        # Negate the real and imaginary part if we did so for the input
-        # interval.
-        # Note that
-        #      Re(1/(b+ai)) = -Im(1/(a+bi))
-        #      Im(1/(b+ai)) = -Re(1/(a+bi))
-        # so we also need to negate (again) both real and imaginary part
-        # again if we flipped them.
-
-        if flipped_real_imag ^ negated_real:
-            mpfr_swap(rmin, rmax)
-            mpfr_neg(rmin, rmin, MPFR_RNDD)
-            mpfr_neg(rmax, rmax, MPFR_RNDU)
-
-        if flipped_real_imag ^ negated_imag:
-            mpfr_swap(imin, imax)
-            mpfr_neg(imin, imin, MPFR_RNDD)
-            mpfr_neg(imax, imax, MPFR_RNDU)
-
-        # Flip real and imaginary part if we did so for the input interval.
-        if flipped_real_imag:
-            mpfr_swap(rmin, imin)
-            mpfr_swap(rmax, imax)
-
-        # Set the intervals.
-        mpfi_interv_fr(x.__re, rmin, rmax)
-        mpfi_interv_fr(x.__im, imin, imax)
-
-        # Free memory
-        mpfr_clear(a)
-        mpfr_clear(b)
-        mpfr_clear(c)
-        mpfr_clear(d)
-        mpfr_clear(imin)
-        mpfr_clear(imax)
-        mpfr_clear(rmin)
-        mpfr_clear(rmax)
-        mpfr_clear(r)
-        mpfr_clear(a2)
-        mpfr_clear(b2)
-        mpfr_clear(c2)
-        mpfr_clear(d2)
-        mpfr_clear(div1)
-        mpfr_clear(div2)
-        mpfr_clear(aux)
-        mpfr_clear(aux2)
+        mpfi_clear(t0)
+        mpfi_clear(t1)
 
         return x
 
@@ -2098,7 +1868,7 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: CIF(1,1).tan()
             0.27175258531952? + 1.08392332733870?*I
             sage: CIF(2).tan()
-            -2.185039863261519?
+            -2.18503986326152?
             sage: CIF(0,2).tan()
             0.964027580075817?*I
         """
@@ -2187,7 +1957,7 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: CIF(2).tanh()
             0.964027580075817?
             sage: CIF(0,2).tanh()
-            -2.185039863261519?*I
+            -2.18503986326152?*I
         """
         return self.sinh() / self.cosh()
 
diff --git a/src/sage/rings/function_field/jacobian_khuri_makdisi.py b/src/sage/rings/function_field/jacobian_khuri_makdisi.py
index eeb2ae12fd6..436ddb6ad32 100644
--- a/src/sage/rings/function_field/jacobian_khuri_makdisi.py
+++ b/src/sage/rings/function_field/jacobian_khuri_makdisi.py
@@ -496,20 +496,20 @@ class JacobianGroupEmbedding(Map):
 
     EXAMPLES::
 
-        sage: k = GF(7)
+        sage: k = GF(5)
         sage: P2. = ProjectiveSpace(k, 2)
-        sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
+        sage: C = Curve(x^3 + z^3 - y^2*z, P2)
         sage: h = C.function(y/x).divisor_of_poles()
         sage: J = C.jacobian(model='km_large', base_div=h)
         sage: G1 = J.group()
-        sage: K = k.extension(3)
-        sage: G3 = J.group(K)
-        sage: G3.coerce_map_from(G1)
+        sage: K = k.extension(2)
+        sage: G2 = J.group(K)
+        sage: G2.coerce_map_from(G1)
         Jacobian group embedding map:
           From: Group of rational points of Jacobian
-           over Finite Field of size 7 (Khuri-Makdisi large model)
+                over Finite Field of size 5 (Khuri-Makdisi large model)
           To:   Group of rational points of Jacobian
-           over Finite Field in z3 of size 7^3 (Khuri-Makdisi large model)
+                over Finite Field in z2 of size 5^2 (Khuri-Makdisi large model)
     """
     def __init__(self, base_group, extension_group):
         """
@@ -517,15 +517,15 @@ def __init__(self, base_group, extension_group):
 
         TESTS::
 
-            sage: k = GF(7)
+            sage: k = GF(5)
             sage: P2. = ProjectiveSpace(k, 2)
-            sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
+            sage: C = Curve(x^3 + z^3 - y^2*z, P2)
             sage: h = C.function(y/x).divisor_of_poles()
             sage: J = C.jacobian(model='km_large', base_div=h)
             sage: G1 = J.group()
-            sage: K = k.extension(3)
-            sage: G3 = J.group(K)
-            sage: map = G3.coerce_map_from(G1)
+            sage: K = k.extension(2)
+            sage: G2 = J.group(K)
+            sage: map = G2.coerce_map_from(G1)
             sage: TestSuite(map).run(skip=['_test_category', '_test_pickling'])
         """
         F_ext = extension_group._function_field
@@ -541,20 +541,20 @@ def _repr_type(self):
 
         TESTS::
 
-            sage: k = GF(7)
+            sage: k = GF(5)
             sage: P2. = ProjectiveSpace(k, 2)
-            sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
+            sage: C = Curve(x^3 + z^3 - y^2*z, P2)
             sage: h = C.function(y/x).divisor_of_poles()
             sage: J = C.jacobian(model='km_large', base_div=h)
             sage: G1 = J.group()
-            sage: K = k.extension(3)
-            sage: G3 = J.group(K)
-            sage: G3.coerce_map_from(G1)  # indirect doctest
+            sage: K = k.extension(2)
+            sage: G2 = J.group(K)
+            sage: G2.coerce_map_from(G1)  # indirect doctest
             Jacobian group embedding map:
               From: Group of rational points of Jacobian
-                    over Finite Field of size 7 (Khuri-Makdisi large model)
+                    over Finite Field of size 5 (Khuri-Makdisi large model)
               To:   Group of rational points of Jacobian
-                    over Finite Field in z3 of size 7^3 (Khuri-Makdisi large model)
+                    over Finite Field in z2 of size 5^2 (Khuri-Makdisi large model)
         """
         return 'Jacobian group embedding'
 
@@ -564,16 +564,16 @@ def _call_(self, x):
 
         TESTS::
 
-            sage: k = GF(7)
+            sage: k = GF(5)
             sage: P2. = ProjectiveSpace(k, 2)
-            sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
+            sage: C = Curve(x^3 + z^3 - y^2*z, P2)
             sage: h = C.function(y/x).divisor_of_poles()
             sage: J = C.jacobian(model='km_large', base_div=h)
             sage: G1 = J.group()
-            sage: K = k.extension(3)
-            sage: G3 = J.group(K)
-            sage: m = G3.coerce_map_from(G1)
-            sage: m(G1.zero()) == G3.zero()
+            sage: K = k.extension(2)
+            sage: G2 = J.group(K)
+            sage: m = G2.coerce_map_from(G1)
+            sage: m(G1.zero()) == G2.zero()
             True
         """
         w = (x._w).change_ring(self._K_ext)
@@ -895,7 +895,7 @@ def __iter__(self):
             sage: b = C([0,0,1]).place()
             sage: J = C.jacobian(model='km_large', base_div=3*b)
             sage: G = J.group()
-            sage: len([pt for pt in G])
+            sage: len([pt for pt in G])  # long time
             11
         """
         d0 = self._base_div.degree()
diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py
index 615cbab3690..5b550555f34 100644
--- a/src/sage/rings/function_field/order.py
+++ b/src/sage/rings/function_field/order.py
@@ -162,7 +162,7 @@ def is_field(self, proof=True):
 
     def is_noetherian(self):
         """
-        Return ``True`` since orders in function fields are noetherian.
+        Return ``True`` since orders in function fields are Noetherian.
 
         EXAMPLES::
 
diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py
index 4dba8ec9d42..f463a7f31d5 100644
--- a/src/sage/rings/ideal.py
+++ b/src/sage/rings/ideal.py
@@ -1077,7 +1077,7 @@ def _mul_(self, other):
         ``self.ngens() * other.ngens()``. So if used repeatedly this method
         will create an ideal with a uselessly large amount of generators.
         Therefore it is advisable to overwrite this method with a method that
-        takes advantage of the structure of the ring your working in.
+        takes advantage of the structure of the ring you are working in.
 
         Example::
 
diff --git a/src/sage/rings/integer_ring.pxd b/src/sage/rings/integer_ring.pxd
index d0af1bc068f..204ccbe141c 100644
--- a/src/sage/rings/integer_ring.pxd
+++ b/src/sage/rings/integer_ring.pxd
@@ -1,7 +1,7 @@
-from sage.rings.ring cimport PrincipalIdealDomain
+from sage.rings.ring cimport CommutativeRing
 from sage.rings.integer cimport Integer
 from sage.libs.gmp.types cimport mpz_t
 
-cdef class IntegerRing_class(PrincipalIdealDomain):
+cdef class IntegerRing_class(CommutativeRing):
     cdef int _randomize_mpz(self, mpz_t value, x, y, distribution) except -1
     cdef object _zero
diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx
index ab445056cc7..315b4c5f529 100644
--- a/src/sage/rings/integer_ring.pyx
+++ b/src/sage/rings/integer_ring.pyx
@@ -58,6 +58,7 @@ import sage.libs.pari.all
 import sage.rings.ideal
 from sage.categories.basic import EuclideanDomains, DedekindDomains
 from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
+from sage.categories.noetherian_rings import NoetherianRings
 from sage.rings.number_field.number_field_element_base import NumberFieldElement_base
 from sage.structure.coerce cimport is_numpy_type
 from sage.structure.element cimport parent
@@ -106,7 +107,7 @@ def is_IntegerRing(x):
     """
     return isinstance(x, IntegerRing_class)
 
-cdef class IntegerRing_class(PrincipalIdealDomain):
+cdef class IntegerRing_class(CommutativeRing):
     r"""
     The ring of integers.
 
@@ -124,6 +125,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
         sage: Z.category()
         Join of Category of Dedekind domains
             and Category of euclidean domains
+            and Category of noetherian rings
             and Category of infinite enumerated sets
             and Category of metric spaces
         sage: Z(2^(2^5) + 1)
@@ -312,8 +314,10 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
             sage: A in InfiniteEnumeratedSets()
             True
         """
+        cat = (EuclideanDomains(), DedekindDomains(),
+               InfiniteEnumeratedSets().Metric(), NoetherianRings())
         Parent.__init__(self, base=self, names=('x',), normalize=False,
-                        category=(EuclideanDomains(), DedekindDomains(), InfiniteEnumeratedSets().Metric()))
+                        category=cat)
         self._populate_coercion_lists_(init_no_parent=True,
                                        convert_method_name='_integer_')
 
@@ -422,7 +426,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
             K, _ = parent(x).subfield(x)
             return K.order(K.gen())
 
-        return PrincipalIdealDomain.__getitem__(self, x)
+        return CommutativeRing.__getitem__(self, x)
 
     def range(self, start, end=None, step=None):
         """
diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py
index 4e0c6f0d557..b8bd1cf062f 100644
--- a/src/sage/rings/laurent_series_ring.py
+++ b/src/sage/rings/laurent_series_ring.py
@@ -155,7 +155,7 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing):
         sage: LaurentSeriesRing(ZZ, 'x').category()
         Category of infinite commutative no zero divisors algebras
          over (Dedekind domains and euclidean domains
-         and infinite enumerated sets and metric spaces)
+         and noetherian rings and infinite enumerated sets and metric spaces)
         sage: LaurentSeriesRing(QQ, 'x').category()
         Join of Category of complete discrete valuation fields and Category of commutative algebras
          over (number fields and quotient fields and metric spaces) and Category of infinite sets
@@ -227,7 +227,8 @@ def __init__(self, power_series):
             sage: RZZ.category()
             Category of infinite commutative no zero divisors algebras
              over (Dedekind domains and euclidean domains
-             and infinite enumerated sets and metric spaces)
+             and noetherian rings and infinite enumerated sets
+             and metric spaces)
             sage: TestSuite(RZZ).run()
 
             sage: R1 = LaurentSeriesRing(Zmod(1), 't')
diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py
index 06fd7a5d595..6f96ffc839d 100644
--- a/src/sage/rings/lazy_series_ring.py
+++ b/src/sage/rings/lazy_series_ring.py
@@ -1475,6 +1475,7 @@ def __init__(self, base_ring, names, sparse=True, category=None):
             sage: L.category()
             Category of infinite commutative no zero divisors algebras over
              (Dedekind domains and euclidean domains
+              and noetherian rings
               and infinite enumerated sets and metric spaces)
 
             sage: L = LazyLaurentSeriesRing(QQ, 't')
@@ -1490,6 +1491,7 @@ def __init__(self, base_ring, names, sparse=True, category=None):
             Category of infinite commutative no zero divisors algebras over
              (unique factorization domains and commutative algebras over
               (Dedekind domains and euclidean domains
+              and noetherian rings
               and infinite enumerated sets and metric spaces)
               and infinite sets)
 
diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py
index 066459c65a2..349a8ec17dc 100644
--- a/src/sage/rings/padics/local_generic.py
+++ b/src/sage/rings/padics/local_generic.py
@@ -21,13 +21,14 @@
 # *****************************************************************************
 
 from copy import copy
-from sage.rings.ring import CommutativeRing
+
 from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields
 from sage.structure.category_object import check_default_category
-from sage.structure.parent import Parent
 from sage.rings.integer import Integer
 from sage.rings.integer_ring import ZZ
 from sage.rings.infinity import Infinity
+from sage.rings.ring import CommutativeRing
+
 
 class LocalGeneric(CommutativeRing):
     def __init__(self, base, prec, names, element_class, category=None):
@@ -73,7 +74,8 @@ def __init__(self, base, prec, names, element_class, category=None):
         category = category.Metric().Complete().Infinite()
         if default_category is not None:
             category = check_default_category(default_category, category)
-        Parent.__init__(self, base, names=(names,), normalize=False, category=category)
+        CommutativeRing.__init__(self, base, names=(names,),
+                                 normalize=False, category=category)
 
     def is_capped_relative(self):
         r"""
@@ -451,7 +453,7 @@ def get_unramified_modulus(q, res_name):
                 kwds['type'] = 'capped-rel'
             elif self._prec_type() == 'fixed-mod':
                 kwds['type'] = 'floating-point'
-                kwds['show_prec'] = False # This can be removed once printing of fixed mod elements is changed.
+                kwds['show_prec'] = False  # This can be removed once printing of fixed mod elements is changed.
 
         # There are two kinds of functors possible:
         # CompletionFunctor and AlgebraicExtensionFunctor
@@ -488,7 +490,7 @@ def get_unramified_modulus(q, res_name):
             # Labels for lattice precision
             if 'label' in kwds:
                 functor.extras['label'] = kwds.pop('label')
-            elif 'label' in functor.extras and functor.type not in ['lattice-cap','lattice-float']:
+            elif 'label' in functor.extras and functor.type not in ['lattice-cap', 'lattice-float']:
                 del functor.extras['label']
             for atr in ('ram_name', 'var_name'):
                 if atr in kwds:
@@ -659,9 +661,9 @@ def defining_polynomial(self, var='x', exact=False):
         from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
         if exact:
             from sage.rings.integer_ring import ZZ
-            return PolynomialRing(ZZ,var).gen()
+            return PolynomialRing(ZZ, var).gen()
         else:
-            return PolynomialRing(self,var).gen()
+            return PolynomialRing(self, var).gen()
 
     def ground_ring(self):
         r"""
@@ -1117,7 +1119,7 @@ def _test_add_bigoh(self, **options):
                 tester.assertLessEqual(y.precision_absolute(), -1)
 
             # make sure that we handle very large values correctly
-            if self._prec_type() not in [ 'lattice-float', 'relaxed' ]:   # no cap in these models
+            if self._prec_type() not in ['lattice-float', 'relaxed']:  # no cap in these models
                 absprec = Integer(2)**1000
                 tester.assertEqual(x.add_bigoh(absprec), x)
 
@@ -1194,15 +1196,15 @@ def _matrix_flatten_precision(self, M):
         cap = parent.precision_cap()
         n = M.nrows()
         m = M.ncols()
-        shift_rows = n * [ ZZ(0) ]
-        shift_cols = m * [ ZZ(0) ]
+        shift_rows = n * [ZZ.zero()]
+        shift_cols = m * [ZZ.zero()]
         for i in range(n):
-            prec = min(M[i,j].precision_absolute() for j in range(m))
+            prec = min(M[i, j].precision_absolute() for j in range(m))
             if prec is Infinity or prec == cap:
                 continue
             shift_rows[i] = s = cap - prec
             for j in range(m):
-                M[i,j] <<= s
+                M[i, j] <<= s
         for j in range(m):
             prec = min(M[i,j].precision_absolute() for i in range(n))
             if prec is Infinity or prec == cap:
diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py
index 6452d77db22..60eb71e9e7c 100644
--- a/src/sage/rings/padics/padic_generic.py
+++ b/src/sage/rings/padics/padic_generic.py
@@ -33,7 +33,6 @@
 from sage.categories.fields import Fields
 from sage.rings.infinity import infinity
 from .local_generic import LocalGeneric
-from sage.rings.ring import PrincipalIdealDomain
 from sage.rings.integer import Integer
 from sage.rings.infinity import Infinity
 from sage.rings.padics.precision_error import PrecisionError
@@ -41,7 +40,7 @@
 from sage.structure.richcmp import richcmp_not_equal
 
 
-class pAdicGeneric(PrincipalIdealDomain, LocalGeneric):
+class pAdicGeneric(LocalGeneric):
     def __init__(self, base, p, prec, print_mode, names, element_class, category=None):
         r"""
         Initialize ``self``.
@@ -68,7 +67,7 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non
             category = category.Metric().Complete()
         LocalGeneric.__init__(self, base, prec, names, element_class, category)
         self._printer = pAdicPrinter(self, print_mode)
-        self._qth_roots_of_unity = [ (1, Infinity) ]
+        self._qth_roots_of_unity = [(1, Infinity)]
 
     def some_elements(self):
         r"""
diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py
index bb6b4296d1a..2e8003c7ce8 100644
--- a/src/sage/rings/polynomial/binary_form_reduce.py
+++ b/src/sage/rings/polynomial/binary_form_reduce.py
@@ -78,8 +78,8 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001):
         sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\
         ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8
         sage: covariant_z0(F, prec=80)
-        (0.64189877107807122203366 + 1.1852516565091601348355*I,
-         3134.5148284344627168276)
+        (0.64189877107807122203369 + 1.1852516565091601348355*I,
+         3134.5148284344627168275)
 
     ::
 
diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py
index dbb71289f89..e962e1f1a2e 100644
--- a/src/sage/rings/polynomial/infinite_polynomial_ring.py
+++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py
@@ -1098,11 +1098,11 @@ def is_noetherian(self):
 
         Since Infinite Polynomial Rings must have at least one
         generator, they have infinitely many variables and are thus
-        not noetherian, as a ring.
+        not Noetherian, as a ring.
 
         .. NOTE::
 
-            Infinite Polynomial Rings over a field `F` are noetherian as
+            Infinite Polynomial Rings over a field `F` are Noetherian as
             `F(G)` modules, where `G` is the symmetric group of the
             natural numbers. But this is not what the method
             ``is_noetherian()`` is answering.
diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx
index 37247f36571..3d58de17015 100644
--- a/src/sage/rings/polynomial/multi_polynomial.pyx
+++ b/src/sage/rings/polynomial/multi_polynomial.pyx
@@ -2140,6 +2140,10 @@ cdef class MPolynomial(CommutativePolynomial):
             sage: Pol = QQ['x']['x','y']
             sage: Pol.one().gcd(1)
             1
+
+            sage: P = PolynomialRing(QQ, 'x', 0)
+            sage: P.gens()
+            ()
         """
         flatten = self._parent.flattening_morphism()
         tgt = flatten.codomain()
@@ -2154,7 +2158,13 @@ cdef class MPolynomial(CommutativePolynomial):
         except (TypeError, AttributeError):
             pass
 
-        x = self._parent.gens()[-1]
+        gens = self.parent().gens()
+        if not gens:
+            # no variables
+            base = self.parent().base_ring()
+            return base(self).gcd(base(other))
+
+        x = gens[-1]
         uniself = self.polynomial(x)
         unibase = uniself.base_ring()
         try:
diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py
index f80f7b141a7..eeb24026d9d 100644
--- a/src/sage/rings/polynomial/multi_polynomial_ideal.py
+++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py
@@ -1383,11 +1383,7 @@ def _groebner_basis_ginv(self, algorithm="TQ", criteria='CritPartially', divisio
         T = P.term_order()
         K = P.base_ring()
 
-        try:
-            import ginv
-        except ImportError:
-            from sage.misc.package import PackageNotFoundError
-            raise PackageNotFoundError("ginv")
+        import ginv
 
         st = ginv.SystemType("Polynomial")
 
diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx
index 40fbc8c69aa..d1f17f7f899 100644
--- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx
+++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx
@@ -1285,7 +1285,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base):
             polynomial ring, over a field, global ordering
             //   coefficients: ZZ/2[a]/(a^8+a^4+a^3+a^2+1)
             //   number of vars : 10
-            //        block   1 : ordering rp
+            //        block   1 : ordering ip
             //                  : names    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9
             //        block   2 : ordering C
 
@@ -1294,7 +1294,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base):
             polynomial ring, over a field, global ordering
             //   coefficients: ZZ/127
             //   number of vars : 2
-            //        block   1 : ordering rp
+            //        block   1 : ordering ip
             //                  : names    x0 x1
             //        block   2 : ordering C
 
@@ -1303,7 +1303,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base):
             polynomial ring, over a field, global ordering
             //   coefficients: QQ
             //   number of vars : 2
-            //        block   1 : ordering rp
+            //        block   1 : ordering ip
             //                  : names    x0 x1
             //        block   2 : ordering C
 
diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py
index e9476061b57..8797a84efd8 100644
--- a/src/sage/rings/polynomial/multi_polynomial_sequence.py
+++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py
@@ -1724,9 +1724,24 @@ def coefficients_monomials(self, order=None, sparse=True):
             (x*y, y, z, 1)
             sage: A*v
             (x*y + y + 1, z + 1)
+
+        TESTS:
+
+        Check that :issue:`37837` has been fixed::
+
+            sage: R. = PolynomialRing(GF(2), ['a', 'b', 'c'])
+            sage: A, v = Sequence([a+b+c]).coefficients_monomials()
+            sage: A
+            [1 1 1]
+            sage: v
+            (a, b, c)
+            sage: A*v
+            (a + b + c)
         """
         from sage.modules.free_module_element import vector
         from sage.matrix.constructor import Matrix
+        from sage.rings.polynomial.multi_polynomial_ring_base import \
+            BooleanPolynomialRing_base
 
         if order is None:
             v = sorted(self.monomials(), reverse=True)
@@ -1736,16 +1751,27 @@ def coefficients_monomials(self, order=None, sparse=True):
             else:
                 raise ValueError("order argument can only accept list or tuple")
 
-        R = self.ring().base_ring()
-        one = R.one()
+        R = self.ring()
+        K = R.base_ring()
         y = dict(zip(v, range(len(v))))  # construct dictionary for fast lookups
-        A = Matrix(R, len(self), len(v), sparse=sparse)
-        for x, poly in enumerate(self):
-            for m in poly:
-                try:
-                    A[x, y[m]] = one
-                except KeyError:
-                    raise ValueError("order argument does not contain all monomials")
+        A = Matrix(K, len(self), len(v), sparse=sparse)
+
+        if isinstance(R, BooleanPolynomialRing_base):
+            one = K.one()
+            for x, poly in enumerate(self):
+                for m in poly:
+                    try:
+                        A[x, y[m]] = one
+                    except KeyError:
+                        raise ValueError("order argument does not contain all monomials")
+        else:
+            for x, poly in enumerate(self):
+                for c, m in poly:
+                    try:
+                        A[x, y[m]] = c
+                    except KeyError:
+                        raise ValueError("order argument does not contain all monomials")
+
         return A, vector(v)
 
 
diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py
index ba6b9c7b493..8280fd7db81 100644
--- a/src/sage/rings/polynomial/pbori/blocks.py
+++ b/src/sage/rings/polynomial/pbori/blocks.py
@@ -5,7 +5,7 @@
 from .PyPolyBoRi import (Ring, Polynomial, VariableFactory, Variable)
 
 
-class Block():
+class Block:
     r"""
     The block class represents a block of variables
     (start_index,...,start_index+size-1), it is the preferred
@@ -37,12 +37,12 @@ def register(self, start, context):
         ring = ring_context['r']
 
         var_func = VariableBlock(self.size, self.start_index, start, self.
-            reverse, ring)
+                                 reverse, ring)
         var_func.__name__ = self.var_name
         context[self.var_name] = var_func
 
 
-class AlternatingBlock():
+class AlternatingBlock:
     r"""
     The Alternating Block class is used for doing tricky variable
     schemes,where base names vary, e.g.
@@ -74,7 +74,7 @@ def __getitem__(self, i):
     def register(self, start, context):
         def gen_var_func(var_pos):
 
-            class var_factory():
+            class var_factory:
                 def __init__(self, ring, index2pos, size):
                     self.ring = ring
                     self.index2pos = index2pos
@@ -82,7 +82,7 @@ def __init__(self, ring, index2pos, size):
 
                 def __call__(self, idx):
                     return self.ring.variable(self.index2pos[idx] * self.size +
-                                        var_pos + start)
+                                              var_pos + start)
             ring_context = context
             while isinstance(ring_context, PrefixedDictProxy):
                 ring_context = ring_context.wrapped
@@ -107,7 +107,8 @@ class AdderBlock(AlternatingBlock):
     def __init__(self, adder_bits, sums="s", carries="c", input1="a",
                  input2="b", start_index=0):
         AlternatingBlock.__init__(self, (sums, carries, input1, input2),
-            adder_bits, start_index=start_index, reverse=True)
+                                  adder_bits, start_index=start_index,
+                                  reverse=True)
         self.input1 = input1
         self.input2 = input2
         self.sums = sums
@@ -124,13 +125,14 @@ def register(self, start, context):
         a = shift(a, self.start_index)
         b = shift(b, self.start_index)
         carries = [Polynomial(a(0).ring().zero())]
+        last = carries[0]
         for i in range(self.adder_bits):
-            c = 1 + (1 + a(i) * b(i)) * (1 + carries[-1] * a(i)) * (1 +
-                carries[-1] * b(i))
+            c = 1 + (1 + a(i) * b(i)) * (1 + last * a(i)) * (1 + last * b(i))
             carries.append(c)
+            last = c
 
-        self.add_results = [a(i) + b(i) + carries[i] for i in range(self.
-            adder_bits)]
+        self.add_results = [a(i) + b(i) + carries[i]
+                            for i in range(self.adder_bits)]
         self.carries_polys = carries[1:]
 
     # def s(i):
@@ -146,7 +148,7 @@ def implement(self, equations):
             equations.append(self.c(i) + self.carries_polys[i])
 
 
-class HigherOrderBlock():
+class HigherOrderBlock:
     r"""
     HigherOrderBlocks are multidimensional blocks of variables.
 
@@ -192,14 +194,14 @@ def var_func(*indices):
         context[self.var_name] = var_func
 
 
-class InOutBlock():
+class InOutBlock:
     def __init__(self, out_size, in_size, output="out", input="in",
                  in_start_index=0, out_start_index=0,
                  out_reverse=False, in_reverse=False):
         self.output = Block(var_name=output, start_index=out_start_index,
-                        size=out_size, reverse=out_reverse)
+                            size=out_size, reverse=out_reverse)
         self.input = Block(var_name=input, start_index=in_start_index,
-                       size=in_size, reverse=in_reverse)
+                           size=in_size, reverse=in_reverse)
         self.out_start_index = out_start_index
 
         self.in_start_index = in_start_index
@@ -219,11 +221,11 @@ def register(self, start, context):
         self.output.register(start, context)
         self.input.register(start + len(self.output), context)
         self.out_vars = shift(context[self.output.var_name], self.
-            out_start_index)
+                              out_start_index)
         self.in_vars = shift(context[self.input.var_name], self.in_start_index)
 
 
-class MultiBlock():
+class MultiBlock:
     def __init__(self, sizes=None, var_names=["v"],
                  start_indices=[], reverses=None):
         if reverses is None:
@@ -236,8 +238,9 @@ def __init__(self, sizes=None, var_names=["v"],
         sizes += [1] * (len(var_names) - len(sizes))
 
         self.blocks = [Block(var_name=var_names[idx], size=sizes[idx],
-            start_index=self.start_indices[idx], reverse=reverses[idx]) for
-            idx in range(len(var_names))]
+                             start_index=self.start_indices[idx],
+                             reverse=reverses[idx])
+                       for idx in range(len(var_names))]
 
     def __iter__(self):
         return chain(*self.blocks)
@@ -247,7 +250,7 @@ def __getitem__(self, i):
         # sum([bl.names for bl in self.blocks])[i]
 
     def __len__(self):
-        return sum((len(bl) for bl in self.blocks))
+        return sum(len(bl) for bl in self.blocks)
 
     def register(self, start, context):
         offset = 0
@@ -255,11 +258,12 @@ def register(self, start, context):
             bl.register(start + offset, context)
             offset += len(bl)
 
-        self.vars = [shift(context[self.blocks[idx].var_name], self.
-            start_indices[idx]) for idx in range(len(self.blocks))]
+        self.vars = [shift(context[self.blocks[idx].var_name],
+                           self.start_indices[idx])
+                     for idx in range(len(self.blocks))]
 
 
-class PrefixedDictProxy():
+class PrefixedDictProxy:
     """docstring for PrefixedDictProxy"""
 
     def __init__(self, wrapped, prefix):
@@ -278,7 +282,7 @@ def __setitem__(self, k, v):
         self.wrapped[self.prefix + k] = v
 
 
-class MacroBlock():
+class MacroBlock:
     def __init__(self, prefix):
 
         self.prefix = prefix
@@ -299,7 +303,7 @@ def __getitem__(self, i):
         return self.prefix + "_" + next(islice(chain(*self.blocks), i, i + 1))
 
     def __len__(self):
-        return sum((len(bl) for bl in self.blocks))
+        return sum(len(bl) for bl in self.blocks)
 
     def resolve(self, localname):
         return self.prefix + "_" + localname
@@ -324,7 +328,7 @@ def implement(self, equations):
         equations += self.connections
 
 
-class IfThen():
+class IfThen:
     def __init__(self, ifpart, thenpart, supposed_to_be_valid=True):
         self.ifpart = [Polynomial(p) for p in ifpart]
         self.thenpart = [Polynomial(p) for p in thenpart]
@@ -369,10 +373,7 @@ def canonicalize(blocks):
     n = 0
 
     for b in blocks:
-        if isinstance(b, str):
-            n = n + 1
-        else:
-            n = n + len(b)
+        n = n + 1 if isinstance(b, str) else n + len(b)
 
     r = Ring(n, names=canonicalize(blocks))
 
diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py
index 266a6f0f8a8..61d85866013 100644
--- a/src/sage/rings/polynomial/pbori/cnf.py
+++ b/src/sage/rings/polynomial/pbori/cnf.py
@@ -4,7 +4,7 @@
 from .statistics import used_vars_set
 
 
-class CNFEncoder():
+class CNFEncoder:
     def __init__(self, r, random_seed=16):
         self.random_generator = Random(random_seed)
         self.one_set = r.one().set()
@@ -174,7 +174,7 @@ def dimacs_cnf(self, polynomial_system):
         res = ["c cnf generated by PolyBoRi"]
         r = polynomial_system[0].ring()
         n_variables = r.n_variables()
-        res.append("p cnf %s %s" % (n_variables, len(clauses_list)))
+        res.append(f"p cnf {n_variables} {len(clauses_list)}")
         res.extend(clauses_list)
         return "\n".join(res)
 
@@ -213,7 +213,7 @@ def dimacs_encode_polynomial(self, p):
             indices.append(0)
             res = ["x" + " ".join(str(v) for v in indices)]
         self.group_counter = self.group_counter + 1
-        group_comment = "\nc g %s %s" % (self.group_counter, str(p)[:30])
+        group_comment = f"\nc g {self.group_counter} {str(p)[:30]}"
         return [c + group_comment for c in res]
 
     def dimacs_cnf(self, polynomial_system):
@@ -234,6 +234,6 @@ def dimacs_cnf(self, polynomial_system):
         """
         uv = list(used_vars_set(polynomial_system).variables())
         res = super().dimacs_cnf(polynomial_system)
-        res += "\n" + "\n".join("c v %s %s" % (self.to_dimacs_index(v), v)
+        res += "\n" + "\n".join(f"c v {self.to_dimacs_index(v)} {v}"
                                 for v in uv)
         return res
diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py
index 3eb6a73ff25..b5bc0a56192 100644
--- a/src/sage/rings/polynomial/pbori/gbcore.py
+++ b/src/sage/rings/polynomial/pbori/gbcore.py
@@ -1,3 +1,4 @@
+import contextlib
 from copy import copy
 from itertools import chain
 from inspect import getfullargspec as getargspec
@@ -32,7 +33,7 @@ def filter_oldstyle_options(**options):
 def filter_newstyle_options(func, **options):
     allowed = get_options_from_function(func).keys()
     filtered = {}
-    for key in options.keys():
+    for key in options:
         for prefix in ['', 'use_', 'opt_', 'opt_allow_']:
             if prefix + key in allowed:
                 filtered[prefix + key] = options[key]
@@ -80,7 +81,7 @@ def change_order_heuristic(d):
     if not I:
         return d
     switch_table = {OrderCode.lp: OrderCode.dp_asc, OrderCode.dlex: OrderCode.
-        dp_asc}
+                    dp_asc}
     if "other_ordering_first" not in d:
         # TODO after ll situation might look much different, so heuristic is on
         # wrong place
@@ -152,14 +153,12 @@ def trivial_heuristic(d):
     return d
 
 
-class HeuristicalFunction():
+class HeuristicalFunction:
     def __call__(self, *args, **kwds):
         complete_dict = copy(kwds)
         heuristic = True
-        try:
+        with contextlib.suppress(KeyError):
             heuristic = complete_dict["heuristic"]
-        except KeyError:
-            pass
         for (k, v) in zip(self.argnames, args):
             complete_dict[k] = v
         if heuristic:
@@ -172,7 +171,7 @@ def __init__(self, f, heuristic_function):
             self.options = f.options
         else:
             self.options = dict(zip(self.argnames[-len(self.defaults):], self.
-                defaults))
+                                    defaults))
         self.heuristicFunction = heuristic_function
         self.f = f
         self.__doc__ = f.__doc__
@@ -188,42 +187,40 @@ def make_wrapper(f):
 
 def clean_polys_pre(I):
     wrap = (Polynomial(p) for p in I)
-    return (list(set(p for p in wrap if not p.is_zero())), None)
+    return (list({p for p in wrap if not p.is_zero()}), None)
 
 
 def gb_with_pre_post_option(option, pre=None,
-                            post=None, if_not_option=tuple(),
+                            post=None, if_not_option=(),
                             default=False):
     def make_wrapper(f):
         def wrapper(I, **kwds):
             prot = kwds.get("prot", False)
             for o in if_not_option:
                 if (o in kwds and kwds[o]) or (o not in kwds and
-                        groebner_basis.options[o]):
+                                               groebner_basis.options[o]):
                     option_set = False
             if "option_set" not in locals():
                 option_set = kwds.get(option, default)
-            kwds = dict(((o, kwds[o]) for o in kwds if o != option))
+            kwds = {o: kwds[o] for o in kwds if o != option}
             state = None
 
-            if option_set:
-                if pre:
-                    pre_args = getargspec(pre)[0]
-                    if prot:
-                        print("preprocessing for option:", option)
+            if option_set and pre:
+                pre_args = getargspec(pre)[0]
+                if prot:
+                    print("preprocessing for option:", option)
 
-                    local_symbols = copy(locals())
-                    (I, state) = pre(**{k: v for (k, v) in local_symbols.items()
-                                        if k in pre_args})
+                local_symbols = copy(locals())
+                (I, state) = pre(**{k: v for (k, v) in local_symbols.items()
+                                    if k in pre_args})
             I = f(I, **kwds)
-            if option_set:
-                if post:
-                    post_args = getargspec(post)[0]
-                    if prot:
-                        print("postprocessing for option:", option)
-                    local_symbols = copy(locals())
-                    I = post(**{k: v for (k, v) in local_symbols.items()
-                                if k in post_args})
+            if option_set and post:
+                post_args = getargspec(post)[0]
+                if prot:
+                    print("postprocessing for option:", option)
+                local_symbols = copy(locals())
+                I = post(**{k: v for (k, v) in local_symbols.items()
+                            if k in post_args})
 
             return I
         wrapper.__name__ = f.__name__
@@ -271,15 +268,15 @@ def llfirst_pre(I, prot):
 def ll_constants_pre(I):
     ll_res = []
 
-    while len([p for p in I if p.lex_lead_deg() == 1 and
-    (p + p.lex_lead()).constant()]) > 0:
+    while any(p.lex_lead_deg() == 1 and (p + p.lex_lead()).constant()
+              for p in I):
         I_new = []
         ll = []
         leads = set()
         for p in I:
             if p.lex_lead_deg() == 1:
                 l = p.lead()
-                if not (l in leads) and p.is_singleton_or_pair():
+                if l not in leads and p.is_singleton_or_pair():
                     tail = p + l
                     if tail.deg() <= 0:
                         ll.append(p)
@@ -289,9 +286,9 @@ def ll_constants_pre(I):
         encoded = ll_encode(ll)
         reduced = []
         for p in I_new:
-            p = ll_red_nf_redsb(p, encoded)
-            if not p.is_zero():
-                reduced.append(p)
+            rp = ll_red_nf_redsb(p, encoded)
+            if not rp.is_zero():
+                reduced.append(rp)
         I = reduced
         ll_res.extend(ll)
     return (I, ll_res)
@@ -418,12 +415,12 @@ def llfirst_post(I, state, prot, kwds):
         # redsb just for safety, as don't know how option is set
         kwds = copy(kwds)
         kwds.update(
-            dict(llfirst=False,
-            llfirstonthefly=False,
-            ll_constants=False,
-            deg_bound=False,
-            other_ordering_first=False,
-            eliminate_identical_variables=False, redsb=True))
+            {'llfirst': False,
+             'llfirstonthefly': False,
+             'll_constants': False,
+             'deg_bound': False,
+             'other_ordering_first': False,
+             'eliminate_identical_variables': False, 'redsb': True})
         I = groebner_basis(I, **kwds)
     return I
 
@@ -489,56 +486,63 @@ def eliminate_identical_variables_pre(I, prot):
         def my_sort_key(l):
             return l.navigation().value()
 
-        for (t, leads) in rules.items():
+        for t, leads in rules.items():
             if len(leads) > 1:
                 changed = True
-                leads = sorted(leads, key=my_sort_key, reverse=True)
-                chosen = leads[0]
-                ll_system.extend(chosen + v for v in leads[1:])
+                sleads = sorted(leads, key=my_sort_key, reverse=True)
+                chosen = sleads[0]
+                ll_system.extend(chosen + v for v in sleads[1:])
     if ll_system:
         ll_encoded = ll_encode(ll_system, reduce=True)
-        I = set(ll_red_nf_redsb(p, ll_encoded) for p in I)
+        I = {ll_red_nf_redsb(p, ll_encoded) for p in I}
     return (I, ll_system)
 
 
 @gb_with_pre_post_option("clean_arguments", pre=clean_polys_pre, default=True)
 @gb_with_pre_post_option("easy_linear_polynomials",
-    pre=easy_linear_polynomials_pre, default=True)
+                         pre=easy_linear_polynomials_pre, default=True)
 @gb_with_pre_post_option("result_to_list", post=result_to_list_post,
-    default=True)
+                         default=True)
 @with_heuristic(interpolation_gb_heuristic)
 @gb_with_pre_post_option("invert", pre=invert_all_pre,
-    post=invert_all_post, default=False)
+                         post=invert_all_post, default=False)
 @gb_with_pre_post_option("gauss_on_linear", pre=gauss_on_linear_pre,
-    default=True)
+                         default=True)
 @gb_with_pre_post_option("ll_constants", pre=ll_constants_pre,
-    post=ll_constants_post, default=True)
+                         post=ll_constants_post, default=True)
 @gb_with_pre_post_option("eliminate_identical_variables",
-    pre=eliminate_identical_variables_pre, post=llfirst_post, default=True)
+                         pre=eliminate_identical_variables_pre,
+                         post=llfirst_post, default=True)
 @with_heuristic(ll_heuristic)
 @gb_with_pre_post_option("llfirst", if_not_option=["llfirstonthefly"],
-    pre=llfirst_pre, post=llfirst_post, default=False)
+                         pre=llfirst_pre, post=llfirst_post, default=False)
 @gb_with_pre_post_option("llfirstonthefly", pre=llfirstonthefly_pre,
-    post=llfirst_post, default=False)
+                         post=llfirst_post, default=False)
 @gb_with_pre_post_option("incremental", pre=incremental_pre)
 @with_heuristic(change_order_heuristic)
 @gb_with_pre_post_option("other_ordering_first", if_not_option=[
     "interpolation_gb"], pre=other_ordering_pre, default=False)
 @with_heuristic(linear_algebra_heuristic)
 @gb_with_pre_post_option("fix_deg_bound", if_not_option=["interpolation_gb"],
-    post=fix_deg_bound_post, default=True)
-@gb_with_pre_post_option("minsb", post=minsb_post, if_not_option=["redsb",
-    "deg_bound", "interpolation_gb", "convert_with_fglm_from_ring"],
-    default=True)
-@gb_with_pre_post_option("redsb", post=redsb_post, if_not_option=["deg_bound",
-    "interpolation_gb", "convert_with_fglm_from_ring"], default=True)
+                         post=fix_deg_bound_post, default=True)
+@gb_with_pre_post_option("minsb", post=minsb_post,
+                         if_not_option=["redsb", "deg_bound",
+                                        "interpolation_gb",
+                                        "convert_with_fglm_from_ring"],
+                         default=True)
+@gb_with_pre_post_option("redsb", post=redsb_post,
+                         if_not_option=["deg_bound", "interpolation_gb",
+                                        "convert_with_fglm_from_ring"],
+                         default=True)
 def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
-        interpolation_gb=False, clean_and_restart_algorithm=False,
-        convert_with_fglm_from_ring=None, convert_with_fglm_to_ring=None,
-        fglm_bound=40000,
-        modified_linear_algebra=True, preprocessor=None, deg_bound=False,
-        implementation="Python", full_prot=False, prot=False,
-        draw_matrices=False, preprocess_only=False, **impl_options):
+                   interpolation_gb=False, clean_and_restart_algorithm=False,
+                   convert_with_fglm_from_ring=None,
+                   convert_with_fglm_to_ring=None,
+                   fglm_bound=40000,
+                   modified_linear_algebra=True, preprocessor=None,
+                   deg_bound=False,
+                   implementation="Python", full_prot=False, prot=False,
+                   draw_matrices=False, preprocess_only=False, **impl_options):
     """Computes a Groebner basis of a given ideal I, w.r.t options."""
 
     if not I:
@@ -567,10 +571,7 @@ def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
             prod = (p + 1) * prod
         I = [prod + 1]
 
-    if implementation == "Python":
-        implementation = symmGB_F2_python
-    else:
-        implementation = symmGB_F2_C
+    implementation = symmGB_F2_python if implementation == 'Python' else symmGB_F2_C
 
     # custom preprocessing
     if preprocessor:
@@ -584,11 +585,13 @@ def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
 
     def call_algorithm(I, max_generators=None):
         return implementation(I,
-            deg_bound=deg_bound,
-            full_prot=full_prot,
-            prot=prot,
-            max_generators=max_generators, draw_matrices=draw_matrices,
-            **filter_newstyle_options(implementation, **impl_options))
+                              deg_bound=deg_bound,
+                              full_prot=full_prot,
+                              prot=prot,
+                              max_generators=max_generators,
+                              draw_matrices=draw_matrices,
+                              **filter_newstyle_options(implementation,
+                                                        **impl_options))
 
     if clean_and_restart_algorithm:
         for max_generators in [1000, 10000, 50000, 100000, 200000, 300000,
@@ -606,31 +609,26 @@ def call_algorithm(I, max_generators=None):
 
 
 def build_groebner_basis_doc_string():
-    additional_options_from_buchberger = filter_oldstyle_options(**
-        get_options_from_function(symmGB_F2_python))
+    additional_options_from_buchberger = filter_oldstyle_options(
+        **get_options_from_function(symmGB_F2_python))
     for k in list(additional_options_from_buchberger):
         if k in groebner_basis.options:
             del additional_options_from_buchberger[k]
 
-    groebner_basis.__doc__ = (groebner_basis.__doc__ + "\nOptions are:\n" +
-        "\n".join((k + "  :  " + repr(groebner_basis.options[k]) for k in
-        groebner_basis.options)) + """
+    gdoc = groebner_basis.__doc__
+    gdoc += "\nOptions are:\n"
+    gdoc += "\n".join(k + "  :  " + repr(groebner_basis.options[k])
+                      for k in groebner_basis.options)
+    gdoc += """
 
 Turn off heuristic by setting heuristic=False
   Additional options come from the actual buchberger implementation.
   In case of our standard Python implementation these are the following:
 
-""" + "\n".join((k + "  :  " + repr(additional_options_from_buchberger[k])
-                 for k in additional_options_from_buchberger)))
+"""
+    gdoc += "\n".join(k + "  :  " + repr(additional_options_from_buchberger[k])
+                      for k in additional_options_from_buchberger)
+    groebner_basis.__doc__ = gdoc
 
 
 build_groebner_basis_doc_string()
-
-
-def _test():
-    import doctest
-    doctest.testmod()
-
-
-if __name__ == "__main__":
-    _test()
diff --git a/src/sage/rings/polynomial/pbori/gbrefs.py b/src/sage/rings/polynomial/pbori/gbrefs.py
index 70dc795cbab..0f5436c5b5f 100644
--- a/src/sage/rings/polynomial/pbori/gbrefs.py
+++ b/src/sage/rings/polynomial/pbori/gbrefs.py
@@ -74,7 +74,8 @@ def load_ref_gz_uu(s, o, b):
 
 
 def convert_refs(ref_file_orig):
-    content = open(ref_file_orig).read()
+    with open(ref_file_orig) as file:
+        content = file.read()
     buf_out = StringIO()
     zipped = gzip.GzipFile(filename=ref_file_orig, mode="w", fileobj=buf_out)
     zipped.write(content)
diff --git a/src/sage/rings/polynomial/pbori/interpolate.py b/src/sage/rings/polynomial/pbori/interpolate.py
index 68f25d57bb2..783e1209e2a 100644
--- a/src/sage/rings/polynomial/pbori/interpolate.py
+++ b/src/sage/rings/polynomial/pbori/interpolate.py
@@ -89,7 +89,7 @@ def lex_groebner_basis_points(points, variables):
 def lex_groebner_basis_for_polynomial_via_variety(p):
     variables = p.vars_as_monomial()
     return lex_groebner_basis_points(p.zeros_in(variables.divisors()),
-        variables)
+                                     variables)
 
 
 if __name__ == '__main__':
diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py
index fe59be30896..9ecb705057f 100644
--- a/src/sage/rings/polynomial/pbori/interred.py
+++ b/src/sage/rings/polynomial/pbori/interred.py
@@ -13,7 +13,7 @@ def interred(l, completely=False):
     If completely is set to ``True``, then also terms in the
     tail are not reducible by other polynomials.
     """
-    l = [Polynomial(p) for p in l if not p == 0]
+    l = [Polynomial(p) for p in l if p != 0]
     if not l:
         return []
     ring = l[0].ring()
@@ -26,8 +26,8 @@ def interred(l, completely=False):
         if completely:
             g.opt_red_tail = True
         for p in l:
-            p = g.nf(p)
-            if not p.is_zero():
-                g.add_generator(p)
+            gp = g.nf(p)
+            if not gp.is_zero():
+                g.add_generator(gp)
         l = tuple(e.p for e in g)
-    return list(l)
+    return l
diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py
index 71a01bd3b50..5292050d6a7 100644
--- a/src/sage/rings/polynomial/pbori/ll.py
+++ b/src/sage/rings/polynomial/pbori/ll.py
@@ -20,16 +20,13 @@ def combine(reductors, p, reduce=None):
 
 def llredsb_Cudd_style(polys):
 
-    if polys:
-        reductors = Polynomial(polys[0].ring().one()).set()
-    else:
-        reductors = None
+    reductors = Polynomial(polys[0].ring().one()).set() if polys else None
 
     linear_lead = sorted(polys, key=lead_index, reverse=True)
-    assert len(set(p.lex_lead() for p in linear_lead)) == len(polys)
+    assert len({p.lex_lead() for p in linear_lead}) == len(polys)
     assert not any(p.constant() for p in polys)
     assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys)
-    assert len(set(p.navigation().value() for p in polys)) == len(polys)
+    assert len({p.navigation().value() for p in polys}) == len(polys)
     for p in linear_lead:
         reductors = combine(reductors, p, reduce=ll_red_nf_redsb)
     return reductors
@@ -38,26 +35,20 @@ def llredsb_Cudd_style(polys):
 def ll_encode(polys, reduce=False, prot=False, reduce_by_linear=True):
     polys = [Polynomial(p) for p in polys]
     linear_lead = sorted(polys, key=lead_index, reverse=True)
-    assert len(set(p.lex_lead() for p in linear_lead)) == len(polys)
+    assert len({p.lex_lead() for p in linear_lead}) == len(polys)
     assert not any(p.constant() for p in polys)
     assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys)
-    assert len(set(p.navigation().value() for p in polys)) == len(polys)
+    assert len({p.navigation().value() for p in polys}) == len(polys)
     if (not reduce) and reduce_by_linear:
         linear_polys = [p for p in polys if p.deg() == 1]
         if linear_polys:
             linear_ll = ll_encode(linear_polys, reduce=True,
-                reduce_by_linear=False)
+                                  reduce_by_linear=False)
             polys = [p.lex_lead() + ll_red_nf_redsb(p + p.lex_lead(),
-                linear_ll) for p in polys]
-    if reduce:
-        reduce = ll_red_nf_redsb
-    else:
-        reduce = None
+                                                    linear_ll) for p in polys]
+    reduce = ll_red_nf_redsb if reduce else None
 
-    if polys:
-        reductors = Polynomial(polys[0].ring().one()).set()
-    else:
-        reductors = None
+    reductors = Polynomial(polys[0].ring().one()).set() if polys else None
 
     last = None
     counter = 0
@@ -119,11 +110,11 @@ def llnf(p):
         reduced_list = []
         reductors = ll_encode(linear_leads, reduce=(not on_the_fly), prot=prot)
         for p in rest:
-            p = reduction_function(p, reductors)
-            if p.is_one():
-                reduced_list = [p]
+            rp = reduction_function(p, reductors)
+            if rp.is_one():
+                reduced_list = [rp]
                 break
-            reduced_list.append(p)
+            reduced_list.append(rp)
 
     return (linear_leads, llnf, reduced_list)
 
@@ -145,7 +136,7 @@ def eliminate_ll_ranked(ll_system, to_reduce,
 
     ll_ranks = rank(ll_system)
     add_vars = set(used_vars_set(to_reduce).variables()).difference(ll_ranks.
-        keys())
+                                                                    keys())
     for v in add_vars:
         ll_ranks[v] = -1
 
@@ -182,10 +173,11 @@ def map_from(p):
 
     def map_back(p):
         return substitute_variables(from_ring, map_back_vec, p)
+
     try:
         ll_opt_encoded = ll_encode([map_from(p) for p in ll_system],
-            prot=False,
-            reduce=reduce_ll_system)
+                                   prot=False,
+                                   reduce=reduce_ll_system)
 
         def llnf(p):
             return map_back(reduction_function(map_from(p), ll_opt_encoded))
@@ -195,7 +187,7 @@ def llnf(p):
     return (llnf, opt_eliminated)
 
 
-class RingMap():
+class RingMap:
     r"""
     Define a mapping between two rings by common variable names.
 
diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py
index ac6e447c525..0148826e033 100644
--- a/src/sage/rings/polynomial/pbori/nf.py
+++ b/src/sage/rings/polynomial/pbori/nf.py
@@ -33,21 +33,21 @@ def build_and_print_matrices(v, strat):
     """
     treated = BooleSet()
     v = list(v)
-    rows = 0
-    polys_in_mat = []
     if not v:
         return
+    rows = 0
+    polys_in_mat = []
     while v:
         rows = rows + 1
         p = v[0]
         v = v[1:]
         for m in list(p.terms()):
-            m = Monomial(m)
-            if m not in BooleSet(treated):
-                i = strat.select(m)
+            mom = Monomial(m)
+            if mom not in BooleSet(treated):
+                i = strat.select(mom)
                 if i >= 0:
                     p2 = strat[i]
-                    p2 = p2 * (m // p2.lead())
+                    p2 = p2 * (mom // p2.lead())
                     v.append(p2)
         polys_in_mat.append(p)
         treated = treated.union(p.set())
@@ -63,12 +63,10 @@ def build_and_print_matrices(v, strat):
     rows = len(polys_in_mat)
     cols = len(m2i)
     im = Image.new("1", (cols, rows), "white")
-    for i in range(len(polys_in_mat)):
-        p = polys_in_mat[i]
-        for j in p:
+    for i, pi in enumerate(polys_in_mat):
+        for j in pi:
             assert i < rows
             assert j < cols
-
             im.putpixel((j, i), 0)
 
     file_name = strat.matrix_prefix + str(mat_counter) + ".png"
@@ -120,17 +118,17 @@ def build_and_print_matrices_deg_colored(v, strat):
         p = v[0]
         v = v[1:]
         for m in list(p.terms()):
-            m = Monomial(m)
+            mom = Monomial(m)
             if m not in BooleSet(treated):
-                i = strat.select(m)
+                i = strat.select(mom)
                 if i >= 0:
                     p2 = strat[i]
-                    p2 = p2 * (m // p2.lead())
+                    p2 = p2 * (mom // p2.lead())
                     v.append(p2)
         polys_in_mat.append(p)
         treated = treated.union(p.set())
     m2i = {v: k for k, v in enumerate(BooleSet(treated))}
-    max_deg = max([m.deg() for m in BooleSet(treated)])
+    max_deg = max(m.deg() for m in BooleSet(treated))
     if max_deg == 0:
         max_deg = 1
     i2deg = {m2i[m]: m.deg() for m in BooleSet(treated)}
@@ -149,8 +147,8 @@ def build_and_print_matrices_deg_colored(v, strat):
         for j in p:
             assert i < rows
             assert j < cols
-            im.putpixel((j, i), ImageColor.getrgb("hsl(" + str(270 - (270 *
-                i2deg[j]) / max_deg) + ",100%,50%)"))
+            hsl = str(270 - (270 * i2deg[j]) / max_deg)
+            im.putpixel((j, i), ImageColor.getrgb("hsl(" + hsl + ",100%,50%)"))
     file_name = strat.matrix_prefix + str(mat_counter) + ".png"
     if os.path.exists(file_name):
         os.remove(file_name)
@@ -208,14 +206,17 @@ def high_probability_polynomials_trick(p, strat):
 
 
 def symmGB_F2_python(G, deg_bound=1000000000000, over_deg_bound=0,
-        use_faugere=False, use_noro=False, opt_lazy=True, opt_red_tail=True,
-        max_growth=2.0, step_factor=1.0, implications=False, prot=False,
-        full_prot=False, selection_size=1000, opt_exchange=True,
-        opt_allow_recursion=False, ll=False,
-        opt_linear_algebra_in_last_block=True, max_generators=None,
-        red_tail_deg_growth=True, matrix_prefix='mat',
-        modified_linear_algebra=True, draw_matrices=False,
-        easy_linear_polynomials=True):
+                     use_faugere=False, use_noro=False,
+                     opt_lazy=True, opt_red_tail=True,
+                     max_growth=2.0, step_factor=1.0,
+                     implications=False, prot=False,
+                     full_prot=False, selection_size=1000, opt_exchange=True,
+                     opt_allow_recursion=False, ll=False,
+                     opt_linear_algebra_in_last_block=True,
+                     max_generators=None,
+                     red_tail_deg_growth=True, matrix_prefix='mat',
+                     modified_linear_algebra=True, draw_matrices=False,
+                     easy_linear_polynomials=True):
     if use_noro and use_faugere:
         raise ValueError('both use_noro and use_faugere specified')
 
@@ -227,8 +228,8 @@ def add_to_basis(strat, p):
             if prot:
                 if full_prot:
                     print(p)
-                print("Result: ", "deg:", p.deg(), "lm: ", p.lead(), "el: ", p
-                    .elength())
+                print("Result: ", "deg:", p.deg(), "lm: ",
+                      p.lead(), "el: ", p.elength())
             if easy_linear_polynomials and p.lead_deg() > 2:
                 lin = easy_linear_polynomials_func(p)
                 for q in lin:
@@ -291,7 +292,7 @@ def add_to_basis(strat, p):
                 ps = [strat.reduction_strategy.cheap_reductions(p) for p in ps]
                 ps = [p for p in ps if not p.is_zero()]
                 if ps:
-                    min_deg = min((p.deg() for p in ps))
+                    min_deg = min(p.deg() for p in ps)
                 new_ps = []
                 for p in ps:
                     if p.deg() <= min_deg:
@@ -305,9 +306,9 @@ def add_to_basis(strat, p):
             if prot:
                 print("start reducing")
                 print("Chain Crit. : ", strat.chain_criterions, "VC:", strat.
-                    variable_chain_criterions, "EASYP", strat.
-                    easy_product_criterions, "EXTP", strat.
-                    extended_product_criterions)
+                      variable_chain_criterions, "EASYP", strat.
+                      easy_product_criterions, "EXTP", strat.
+                      extended_product_criterions)
                 print(len(ps), "spolys added")
 
             if use_noro or use_faugere:
@@ -316,18 +317,16 @@ def add_to_basis(strat, p):
                 for p in ps:
                     if not p.is_zero():
                         v.append(p)
-                if use_noro:
-                    res = strat.noro_step(v)
-                else:
-                    res = strat.faugere_step_dense(v)
+                res = strat.noro_step(v) if use_noro else strat.faugere_step_dense(v)
 
             else:
                 v = BoolePolynomialVector()
                 for p in ps:
-                    p = Polynomial(mod_mon_set(BooleSet(p.set()),
-                                        strat.reduction_strategy.monomials))
-                    if not p.is_zero():
-                        v.append(p)
+                    rp = Polynomial(mod_mon_set(
+                        BooleSet(p.set()),
+                        strat.reduction_strategy.monomials))
+                    if not rp.is_zero():
+                        v.append(rp)
                 if len(v) > 100:
                     res = parallel_reduce(v, strat, int(step_factor * 10),
                                           max_growth)
@@ -382,7 +381,7 @@ def step(strat, trace, var, val):
         strat.add_generator_delayed(Polynomial(
             Monomial(Variable(var, strat.r)) + val))
         strat = symmGB_F2_python(strat, prot=True, deg_bound=2,
-            over_deg_bound=10)
+                                 over_deg_bound=10)
         if var <= vars_start:
             strat = symmGB_F2_python(strat, prot=True, opt_lazy=False,
                                      opt_red_tail=False)
@@ -428,7 +427,7 @@ def step(strat, trace, proof_path, pos, val):
         print("npairs", strat.npairs())
         print("pos:", pos)
         strat = symmGB_F2_python(strat, deg_bound=deg_bound, opt_lazy=False,
-            over_deg_bound=over_deg_bound, prot=True)
+                                 over_deg_bound=over_deg_bound, prot=True)
         print("npairs", strat.npairs())
         pos = pos + 1
         if pos >= len(proof_path):
@@ -472,7 +471,8 @@ def step(strat, trace, var, val):
         print("npairs", strat.npairs())
 
         strat = symmGB_F2_python(strat, deg_bound=deg_bound,
-            opt_lazy=opt_lazy, over_deg_bound=over_deg_bound, prot=True)
+                                 opt_lazy=opt_lazy,
+                                 over_deg_bound=over_deg_bound, prot=True)
 
         if not strat.containsOne():
             branch(strat, trace)
@@ -483,8 +483,8 @@ def branch(strat, trace):
 
         if index < 0:
             uv = set(used_vars_set(strat))
-            lv = set(next(iter(p.lead())).index()
-                     for p in strat if p.lead_deg() == 1)
+            lv = {next(iter(p.lead())).index()
+                  for p in strat if p.lead_deg() == 1}
             candidates = uv.difference(lv)
             if candidates:
                 index = next(iter(candidates)).index()
@@ -528,7 +528,9 @@ def sort_crit(p):
             strat.add_as_you_wish(g)
     if initial_bb:
         strat = symmGB_F2_python(strat, deg_bound=max(deg_bound,
-            first_deg_bound), opt_lazy=opt_lazy, over_deg_bound=0, prot=True)
+                                                      first_deg_bound),
+                                 opt_lazy=opt_lazy, over_deg_bound=0,
+                                 prot=True)
     strat.opt_lazy = opt_lazy
     print("INITIALIZED")
     branch(strat, [])
@@ -549,7 +551,7 @@ def step(strat, trace, proof_path, pos, choice):
         print("npairs", strat.npairs())
         print("pos:", pos)
         strat = symmGB_F2_python(strat, deg_bound=deg_bound,
-            over_deg_bound=over_deg_bound, prot=True)
+                                 over_deg_bound=over_deg_bound, prot=True)
         print("npairs", strat.npairs())
         pos = pos + 1
         if pos >= len(proof_path):
@@ -581,16 +583,16 @@ def branch(strat, trace, proof_path, pos):
 
 
 def symmGB_F2_C(G, opt_exchange=True,
-        deg_bound=1000000000000, opt_lazy=False,
-        over_deg_bound=0, opt_red_tail=True,
-        max_growth=2.0, step_factor=1.0,
-        implications=False, prot=False,
-        full_prot=False, selection_size=1000,
-        opt_allow_recursion=False, use_noro=False, use_faugere=False,
-        ll=False, opt_linear_algebra_in_last_block=True,
-        max_generators=None, red_tail_deg_growth=True,
-        modified_linear_algebra=True, matrix_prefix="",
-        draw_matrices=False):
+                deg_bound=1000000000000, opt_lazy=False,
+                over_deg_bound=0, opt_red_tail=True,
+                max_growth=2.0, step_factor=1.0,
+                implications=False, prot=False,
+                full_prot=False, selection_size=1000,
+                opt_allow_recursion=False, use_noro=False, use_faugere=False,
+                ll=False, opt_linear_algebra_in_last_block=True,
+                max_generators=None, red_tail_deg_growth=True,
+                modified_linear_algebra=True, matrix_prefix="",
+                draw_matrices=False):
     if use_noro:
         raise NotImplementedError("noro not implemented for symmgb")
     if isinstance(G, list):
diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py
index be10865dd50..32eb3894312 100644
--- a/src/sage/rings/polynomial/pbori/parallel.py
+++ b/src/sage/rings/polynomial/pbori/parallel.py
@@ -1,4 +1,3 @@
-# coding=utf-8
 r"""
 parallel.py
 PolyBoRi
diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py
index 1e0dcd56189..34cc53d5dd4 100644
--- a/src/sage/rings/polynomial/pbori/randompoly.py
+++ b/src/sage/rings/polynomial/pbori/randompoly.py
@@ -99,5 +99,5 @@ def sparse_random_system_data_file_content(number_of_variables, **kwds):
                      dummy_dict)
     polynomials = sparse_random_system(r, **kwds)
     polynomials = pformat(polynomials)
-    return "declare_ring(['x'+str(i) for in range(%s)])\nideal=\\\n%s\n\n" % (
+    return "declare_ring(['x'+str(i) for in range({})])\nideal=\\\n{}\n\n".format(
         number_of_variables, polynomials)
diff --git a/src/sage/rings/polynomial/pbori/rank.py b/src/sage/rings/polynomial/pbori/rank.py
index bf16e6220ce..98d0a1727c9 100644
--- a/src/sage/rings/polynomial/pbori/rank.py
+++ b/src/sage/rings/polynomial/pbori/rank.py
@@ -18,7 +18,7 @@ def rank(data):
     def do_rank(v):
         if v in res:
             return res[v]
-        my_res = res[v] = max([do_rank(p) + 1 for p in parents[v]] + [0])
+        my_res = res[v] = max((do_rank(p) + 1 for p in parents[v]), default=0)
         return my_res
     for v in parents:
         do_rank(v)
diff --git a/src/sage/rings/polynomial/pbori/specialsets.py b/src/sage/rings/polynomial/pbori/specialsets.py
index 74f56c2faf5..9c5b4bdefc8 100644
--- a/src/sage/rings/polynomial/pbori/specialsets.py
+++ b/src/sage/rings/polynomial/pbori/specialsets.py
@@ -89,7 +89,8 @@ def power_set(variables):
     print(list(power_set([Variable(i) for i in range(4)])))
     print(list(power_set([])))
     # every monomial in the first 8 var, which is at most linear in the first 5
-    print(list(mod_mon_set(power_set([Variable(i) for i in range(8)]),
+    print(list(mod_mon_set(
+        power_set([Variable(i) for i in range(8)]),
         all_monomials_of_degree_d(2, [Variable(i) for i in range(5)]))))
 
     # specialized normal form computation
@@ -97,7 +98,8 @@ def power_set(variables):
         mod_mon_set(
             (x(1) * x(2) + x(1) + 1).set(),
             all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)]))))
-    print(list(mod_mon_set(power_set([Variable(i) for i in range(50)]),
+    print(list(mod_mon_set(
+        power_set([Variable(i) for i in range(50)]),
         all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)]))))
 
 
diff --git a/src/sage/rings/polynomial/pbori/statistics.py b/src/sage/rings/polynomial/pbori/statistics.py
index 93cced307a7..efd49fcd005 100644
--- a/src/sage/rings/polynomial/pbori/statistics.py
+++ b/src/sage/rings/polynomial/pbori/statistics.py
@@ -21,7 +21,7 @@ def used_vars_set(l, bound=None):
         s.update(Polynomial(p).vars_as_monomial().variables())
         if bound and len(s) > bound:
             break
-    sorted_s = sorted(list(s), key=top_index, reverse=True)
+    sorted_s = sorted(s, key=top_index, reverse=True)
     m = Monomial(next(iter(l)).ring())
     for v in sorted_s:
         m = v * m
diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx
index 23dd2a11179..b03bd07d34a 100644
--- a/src/sage/rings/polynomial/plural.pyx
+++ b/src/sage/rings/polynomial/plural.pyx
@@ -126,7 +126,8 @@ from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_
 from sage.rings.polynomial.multi_polynomial_ideal import NCPolynomialIdeal
 
 from sage.rings.polynomial.polydict import ETuple
-from sage.rings.ring import check_default_category, CommutativeRing
+from sage.rings.ring import CommutativeRing
+from sage.structure.category_object cimport check_default_category
 from sage.structure.element cimport CommutativeRingElement, Element, RingElement
 from sage.structure.factory import UniqueFactory
 from sage.structure.richcmp cimport rich_to_bool
diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py
index d2dab0db512..f5cab5bbc88 100644
--- a/src/sage/rings/polynomial/polynomial_ring.py
+++ b/src/sage/rings/polynomial/polynomial_ring.py
@@ -144,9 +144,11 @@
 import sys
 
 from sage.structure.element import Element
+from sage.structure.category_object import check_default_category
 
 import sage.categories as categories
 from sage.categories.morphism import IdentityMorphism
+from sage.categories.principal_ideal_domains import PrincipalIdealDomains
 from sage.categories.rings import Rings
 
 from sage.rings.ring import (Ring, IntegralDomain, PrincipalIdealDomain)
@@ -246,7 +248,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
             Join of Category of unique factorization domains
              and Category of commutative algebras over
               (Dedekind domains and euclidean domains
-               and infinite enumerated sets and metric spaces)
+               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
@@ -264,9 +267,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         Check that category for zero ring::
 
             sage: PolynomialRing(Zmod(1), 'x').category()
-            Category of finite commutative algebras over
-            (finite commutative rings and subquotients of monoids and
-            quotients of semigroups and finite enumerated sets)
+            Category of finite commutative rings
 
         Check `is_finite` inherited from category (:issue:`24432`)::
 
@@ -284,11 +285,11 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         """
         # We trust that, if category is given, it is useful and does not need to be joined
         # with the default category
-        if category is None:
-            if base_ring.is_zero():
-                category = categories.rings.Rings().Finite()
-            else:
-                category = polynomial_default_category(base_ring.category(), 1)
+        if base_ring.is_zero():
+            category = categories.rings.Rings().Commutative().Finite()
+        else:
+            defaultcat = polynomial_default_category(base_ring.category(), 1)
+            category = check_default_category(defaultcat, category)
         self.__is_sparse = sparse
         if element_class:
             self._polynomial_class = element_class
@@ -788,6 +789,11 @@ def _coerce_map_from_(self, P):
             False
         """
         base_ring = self.base_ring()
+
+        # workaround, useful for the zero ring
+        if P == base_ring:
+            return self._coerce_map_from_base_ring()
+
         # handle constants that canonically coerce into self.base_ring()
         # first, if possible
         try:
@@ -1781,11 +1787,11 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         if base_ring not in _CommutativeRings:
             raise TypeError("Base ring %s must be a commutative ring." % repr(base_ring))
         # We trust that, if a category is given, that it is useful.
-        if category is None:
-            if base_ring.is_zero():
-                category = categories.algebras.Algebras(base_ring.category()).Commutative().Finite()
-            else:
-                category = polynomial_default_category(base_ring.category(), 1)
+        if base_ring.is_zero():
+            category = categories.algebras.Algebras(base_ring.category()).Commutative().Finite()
+        else:
+            defaultcat = polynomial_default_category(base_ring.category(), 1)
+            category = check_default_category(defaultcat, category)
         PolynomialRing_general.__init__(self, base_ring, name=name,
                                         sparse=sparse, implementation=implementation,
                                         element_class=element_class, category=category)
@@ -3203,7 +3209,7 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None
 
 class PolynomialRing_dense_mod_n(PolynomialRing_commutative):
     def __init__(self, base_ring, name=None, element_class=None,
-            implementation=None, category=None):
+                 implementation=None, category=None):
         """
         TESTS::
 
@@ -3446,6 +3452,9 @@ def __init__(self, base_ring, name="x", implementation=None, element_class=None,
                     self._implementation_repr = ' (using GF2X)'
                 break
 
+        category = check_default_category(PrincipalIdealDomains(),
+                                          category)
+
         PolynomialRing_dense_mod_n.__init__(self, base_ring, name=name, implementation=implementation,
                                             element_class=element_class, category=category)
 
@@ -3467,6 +3476,10 @@ def _implementation_names_impl(implementation, base_ring, sparse):
             Traceback (most recent call last):
             ...
             ValueError: GF2X only supports modulus 2
+            sage: A = PolynomialRing(Zmod(2), 'x'); A
+            Univariate Polynomial Ring in x over Ring of integers modulo 2 (using GF2X)
+            sage: A in PrincipalIdealDomains()
+            True
 
             sage: PolynomialRing(GF(2), 'x', implementation="FLINT")                    # needs sage.libs.flint
             Univariate Polynomial Ring in x over Finite Field of size 2
diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py
index 2a1fb99131f..e417e8a6779 100644
--- a/src/sage/rings/polynomial/polynomial_ring_constructor.py
+++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py
@@ -22,7 +22,6 @@
 # ****************************************************************************
 
 from sage.structure.category_object import normalize_names
-from sage.rings.ring import IntegralDomain
 
 try:
     import sage.rings.padics.padic_base_leaves as padic_base_leaves
@@ -862,7 +861,7 @@ def _multi_variate(base_ring, names, sparse=None, order="degrevlex", implementat
 
     if R is None and implementation == "generic":
         from . import multi_polynomial_ring
-        if isinstance(base_ring, IntegralDomain):
+        if base_ring in _Domains:
             constructor = multi_polynomial_ring.MPolynomialRing_polydict_domain
         else:
             constructor = multi_polynomial_ring.MPolynomialRing_polydict
diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py
index 4a3e78fe879..ce2faa2e41b 100644
--- a/src/sage/rings/polynomial/term_order.py
+++ b/src/sage/rings/polynomial/term_order.py
@@ -388,7 +388,7 @@
 
 singular_name_mapping = {
     'lex'           : 'lp',
-    'invlex'        : 'rp',
+    'invlex'        : 'ip',
     'degrevlex'     : 'dp',
     'deglex'        : 'Dp',
     'neglex'        : 'ls',
diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx
index 6a73e2748e4..f9d61a2a352 100755
--- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx
+++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx
@@ -324,7 +324,7 @@ class WeilPolynomials_iter():
         if node_limit is None:
             node_limit = -1
         force_squarefree = Integer(squarefree)
-        self.process = dfs_manager(d2, q, coefflist, modlist, coeffsign,
+        self.process = None if d2<0 else dfs_manager(d2, q, coefflist, modlist, coeffsign,
                                    num_cofactor, node_limit, parallel,
                                    force_squarefree)
         self.q = q
@@ -537,7 +537,12 @@ class WeilPolynomials():
         sage: list(WeilPolynomials(10, 2, lead=(1,-3,5,-5,5,-5)))
         [x^10 - 3*x^9 + 5*x^8 - 5*x^7 + 5*x^6 - 5*x^5 + 10*x^4 - 20*x^3 + 40*x^2 - 48*x + 32]
 
+    Test that :issue:`37860` is resolved::
 
+        sage: list(WeilPolynomials(-1, 1))
+        []
+        sage: list(WeilPolynomials(0, 1, sign=-1))
+        []
     """
     def __init__(self, d, q, sign=1, lead=1, node_limit=None, parallel=False, squarefree=False, polring=None):
         r"""
diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py
index 4e4454988ca..0c262529852 100644
--- a/src/sage/rings/qqbar.py
+++ b/src/sage/rings/qqbar.py
@@ -543,14 +543,36 @@
     ....: (83132288614462248899077910195645998*a + 254420831388756945210526696075302411552001)*y^2)
     sage: lc = lc.change_ring(QQbar)
     sage: lc.roots(CIF)
-    [(-1.000505492239?, 2),
-     (-1.000000000000?, 2),
-     (-0.999999999662605?, 1),
+    [(-1.0005054922387982573627768714?, 2),
+     (-1.0000000000000000000000000000?, 2),
+     (-0.9999999996626050731848036720993?, 1),
      (0, 2),
-     (1.000000000000?, 2),
-     (1.000505492239?, 2),
-     (0.999999587? + 0.?e-11*I, 1),
-     (0.999999999? + 0.?e-11*I, 1)]
+     (1.0000000000000000000000000000?, 2),
+     (1.0005054922387982573627768714?, 2),
+     (0.999999586737109168744488? + 0.?e-43*I, 1),
+     (0.999999999662605073184804? + 0.?e-43*I, 1)]
+
+Check that issue:`37927` is fixed::
+
+    sage: y = polygen(QQ, 'y')
+    sage: v1 = QQbar.polynomial_root(y**2 + 1, CIF(0, -1))
+    sage: v2 = -QQbar(2).sqrt()
+    sage: M = matrix(QQbar, [[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
+    ....:              [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
+    ....:              [-4, 2*v1, 1, 64, -32*v1, -16, 8*v1, 4, -2*v1, -1],
+    ....:              [4*v1, 1, 0, -192*v1, -80, 32*v1, 12, -4*v1, -1, 0],
+    ....:               [2, 0, 0, -480, 160*v1, 48, -12*v1, -2, 0, 0],
+    ....:               [-4, 2*I, 1, 64, -32*I, -16, 8*I, 4, -2*I, -1],
+    ....:               [4*I, 1, 0, -192*I, -80, 32*I, 12, -4*I, -1, 0],
+    ....:               [2, 0, 0, -480, 160*I, 48, -12*I, -2, 0, 0],
+    ....:               [0, 0, 0, 8, 4*v2, 4, 2*v2, 2, v2, 1],
+    ....:               [0, 0, 0, 24*v2, 20, 8*v2, 6, 2*v2, 1, 0],
+    ....:               [0, 0, 0, 8, 4*v2, 4, 2*v2, 2, -v2, 1],
+    ....:               [0, 0, 0, 24*v2, 20, 8*v2, 6, 2*v2, 1, 0],
+    ....:               [0, 0, 0, -4096, -1024*I, 256, 64*I, -16, -4*I, 1],
+    ....:               [0, 0, 0, -4096, 1024*I, 256, -64*I, -16, 4*I, 1]])
+    sage: M.right_kernel_matrix()
+    [   1.000000000000000? + 0.?e-16*I                                 0                                 0 -0.00925925925925926? + 0.?e-19*I               0.?e-35 + 0.?e-18*I -0.11111111111111111? + 0.?e-18*I               0.?e-34 + 0.?e-17*I   0.5555555555555555? + 0.?e-16*I                                 0  -0.5925925925925926? + 0.?e-17*I]
 
 AUTHOR:
 
@@ -4913,8 +4935,8 @@ def _richcmp_(self, other, op):
             [-0.0221204634374361? - 1.090991904211621?*I,
              -0.0221204634374361? + 1.090991904211621?*I,
              -0.8088604911480535?*I,
-             0.?e-215 - 0.7598602580415435?*I,
-             0.?e-229 + 0.7598602580415435?*I,
+             0.?e-182 - 0.7598602580415435?*I,
+             0.?e-249 + 0.7598602580415435?*I,
              0.8088604911480535?*I,
              0.0221204634374361? - 1.090991904211621?*I,
              0.0221204634374361? + 1.090991904211621?*I]
@@ -8789,7 +8811,7 @@ def an_binop_element(a, b, op):
     for t2 in (ANUnaryExpr, ANBinaryExpr, ANRoot):
         _binop_algo[t1, t2] = _binop_algo[t2, t1] = an_binop_expr
 
-qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly.gen() - 1, RIF(1)))
+qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly([1, -1]), RIF.one()))
 
 
 def _init_qqbar():
diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx
index 17842e7e80e..de1f843ce87 100644
--- a/src/sage/rings/real_double.pyx
+++ b/src/sage/rings/real_double.pyx
@@ -250,7 +250,7 @@ cdef class RealDoubleField_class(sage.rings.abc.RealDoubleField):
         return (CompletionFunctor(sage.rings.infinity.Infinity,
                                   53,
                                   {'type': 'RDF'}),
-               sage.rings.rational_field.QQ)
+                sage.rings.rational_field.QQ)
 
     def complex_field(self):
         """
@@ -431,10 +431,8 @@ cdef class RealDoubleField_class(sage.rings.abc.RealDoubleField):
         """
         if prec == 53:
             return self
-        else:
-            from sage.rings.real_mpfr import RealField
-            return RealField(prec)
-
+        from sage.rings.real_mpfr import RealField
+        return RealField(prec)
 
     def gen(self, n=0):
         """
@@ -901,7 +899,7 @@ cdef class RealDoubleElement(FieldElement):
             sage: complex(RDF(a))
             (2303+0j)
         """
-        return complex(self._value,0)
+        return complex(self._value, 0)
 
     def _integer_(self, ZZ=None):
         """
@@ -1154,7 +1152,7 @@ cdef class RealDoubleElement(FieldElement):
             sage: RDF(2.1)._im_gens_(R, [R(1)])                                         # needs sage.rings.real_mpfr
             2.1000
         """
-        return codomain(self) # since 1 |--> 1
+        return codomain(self)  # since 1 |--> 1
 
     def str(self):
         """
@@ -1931,7 +1929,8 @@ cdef class RealDoubleElement(FieldElement):
         while True:
             a1 = (a+b)/2
             b1 = libc.math.sqrt(a*b)
-            if abs((b1/a1)-1) < eps: return self._new_c(a1)
+            if abs((b1/a1)-1) < eps:
+                return self._new_c(a1)
             a, b = a1, b1
 
     def algebraic_dependency(self, n):
@@ -1956,7 +1955,7 @@ cdef class RealDoubleElement(FieldElement):
             sage: r.algebraic_dependency(5)                                             # needs sage.libs.pari
             x^2 - 2
         """
-        return sage.arith.misc.algdep(self,n)
+        return sage.arith.misc.algdep(self, n)
 
     algdep = algebraic_dependency
 
@@ -2033,6 +2032,7 @@ _RDF = RealDoubleField_class()
 
 RDF = _RDF   # external interface
 
+
 def RealDoubleField():
     """
     Return the unique instance of the
@@ -2046,6 +2046,7 @@ def RealDoubleField():
     global _RDF
     return _RDF
 
+
 def is_RealDoubleElement(x):
     """
     Check if ``x`` is an element of the real double field.
@@ -2061,9 +2062,9 @@ def is_RealDoubleElement(x):
     return isinstance(x, RealDoubleElement)
 
 
-################# FAST CREATION CODE ######################
-########### Based on fast integer creation code   #########
-######## There is nothing to see here, move along   #######
+# ################ FAST CREATION CODE ######################
+#            Based on fast integer creation code
+#         There is nothing to see here, move along
 
 # We use a global element to steal all the references
 # from.  DO NOT INITIALIZE IT AGAIN and DO NOT REFERENCE IT!
@@ -2125,12 +2126,12 @@ cdef PyObject* fast_tp_new(type t, args, kwds) noexcept:
         # objects (As indicated by the Py_TPFLAGS_HAVE_GC flag).
         # See below for a more detailed description.
 
-        new = PyObject_Malloc( sizeof(RealDoubleElement) )
+        new = PyObject_Malloc(sizeof(RealDoubleElement))
 
         # Now set every member as set in z, the global dummy RealDoubleElement
         # created before this tp_new started to operate.
 
-        memcpy(new, (global_dummy_element), sizeof(RealDoubleElement) )
+        memcpy(new, (global_dummy_element), sizeof(RealDoubleElement))
 
     # This line is only needed if Python is compiled in debugging mode
     # './configure --with-pydebug' or SAGE_DEBUG=yes. If that is the
diff --git a/src/sage/rings/real_double_element_gsl.pyx b/src/sage/rings/real_double_element_gsl.pyx
index 9d5f2d9a93b..cf2be375b78 100644
--- a/src/sage/rings/real_double_element_gsl.pyx
+++ b/src/sage/rings/real_double_element_gsl.pyx
@@ -15,14 +15,13 @@ gsl_set_error_handler_off()
 
 cdef class RealDoubleElement_gsl(RealDoubleElement):
 
-
     def nth_root(self, int n):
         """
         Return the `n^{th}` root of ``self``.
 
         INPUT:
 
-        -  ``n`` -- an integer
+        - ``n`` -- an integer
 
         OUTPUT:
 
@@ -53,9 +52,9 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
                 from sage.rings.complex_double import CDF
                 return self._complex_double_(CDF).nth_root(n)
             else:
-                return - ( (-self) ** (float(1)/n) )
-        else:
-            return self ** (float(1)/n)
+                return - ((-self) ** (float(1)/n))
+
+        return self ** (float(1)/n)
 
     cdef __pow_double(self, double exponent, double sign):
         """
@@ -325,7 +324,6 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
         sig_off()
         return a
 
-
     def log10(self):
         """
         Return log to the base 10 of ``self``.
@@ -530,7 +528,6 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
             sage: q.tan()
             0.5773502691896256
         """
-        cdef double denom
         cos = gsl_sf_cos(self._value)
         a = self._new_c(gsl_sf_sin(self._value) / cos)
         return a
@@ -604,7 +601,6 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
         """
         return self._new_c(libc.math.atan(self._value))
 
-
     def cosh(self):
         """
         Return the hyperbolic cosine of ``self``.
@@ -615,7 +611,7 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
             sage: q.cosh()
             1.0344656400955106
         """
-        return self._new_c(gsl_ldexp( gsl_sf_exp(self._value) + gsl_sf_exp(-self._value), -1)) # (e^x + e^-x)/2
+        return self._new_c(gsl_ldexp(gsl_sf_exp(self._value) + gsl_sf_exp(-self._value), -1))  # (e^x + e^-x)/2
 
     def sinh(self):
         """
@@ -627,7 +623,7 @@ cdef class RealDoubleElement_gsl(RealDoubleElement):
             sage: q.sinh()
             0.26480022760227073
         """
-        return self._new_c(gsl_ldexp( gsl_sf_expm1(self._value) - gsl_sf_expm1(-self._value), -1)) # (e^x - e^-x)/2
+        return self._new_c(gsl_ldexp(gsl_sf_expm1(self._value) - gsl_sf_expm1(-self._value), -1))  # (e^x - e^-x)/2
 
     def tanh(self):
         """
diff --git a/src/sage/rings/real_interval_absolute.pyx b/src/sage/rings/real_interval_absolute.pyx
index d190329872e..e1c60c049c7 100644
--- a/src/sage/rings/real_interval_absolute.pyx
+++ b/src/sage/rings/real_interval_absolute.pyx
@@ -14,7 +14,7 @@ from sage.rings.integer cimport Integer
 import sage.rings.abc
 
 from sage.structure.parent cimport Parent
-from sage.structure.element cimport parent
+from sage.structure.element cimport parent as parent_of
 
 from sage.rings.real_mpfr import RR_min_prec
 from sage.rings.real_mpfi import RealIntervalField, RealIntervalFieldElement
@@ -61,6 +61,7 @@ cpdef inline Integer shift_ceil(Integer x, long shift):
     mpz_cdiv_q_2exp(z.value, x.value, shift)
     return z
 
+
 class Factory(UniqueFactory):
     def create_key(self, prec):
         """
@@ -86,9 +87,11 @@ class Factory(UniqueFactory):
         """
         return RealIntervalAbsoluteField_class(prec)
 
+
 RealIntervalAbsoluteField = Factory('sage.rings.real_interval_absolute.RealIntervalAbsoluteField')
 RealIntervalAbsoluteField.__doc__ = RealIntervalAbsoluteField_class.__doc__
 
+
 cdef class RealIntervalAbsoluteField_class(Field):
     """
     This field is similar to the :class:`RealIntervalField` except instead of
@@ -239,7 +242,7 @@ cdef inline shift_left(value, shift):
 cdef class RealIntervalAbsoluteElement(FieldElement):
 
     # This could be optimized by letting these be raw mpz_t.
-    cdef Integer _mantissa # left endpoint
+    cdef Integer _mantissa  # left endpoint
     cdef Integer _diameter
 
     def __init__(self, RealIntervalAbsoluteField_class parent, value):
@@ -420,7 +423,7 @@ cdef class RealIntervalAbsoluteElement(FieldElement):
             sage: RIF(a)
             0.3333333333333334?
         """
-        return R(self._mantissa, self._mantissa+self._diameter) >> (self._parent)._absprec
+        return R(self._mantissa, self._mantissa + self._diameter) >> (self._parent)._absprec
 
     cpdef long mpfi_prec(self) noexcept:
         """
@@ -757,12 +760,12 @@ cdef class RealIntervalAbsoluteElement(FieldElement):
             return None if neg is None else -neg
         if type(x) in (int, Integer):
             return self._new_c(self._mantissa * x, self._diameter * x)
-        P = parent(x)
+        P = parent_of(x)
         if isinstance(P, Parent):
             if P.is_exact():
                 left = (self._mantissa * x).floor()
                 right = ((self._mantissa + self._diameter) * x).ceil()
-                return self._new_c(left, right-left)
+                return self._new_c(left, right - left)
             elif isinstance(x, RealIntervalFieldElement):
                 if x.contains_zero() or self.contains_zero():
                     return self * RealIntervalAbsoluteElement(self._parent, (x.lower(), x.upper()))
@@ -773,7 +776,7 @@ cdef class RealIntervalAbsoluteElement(FieldElement):
                 else:
                     left = (self._mantissa * x.upper()).floor()
                     right = ((self._mantissa + self._diameter) * x.lower()).ceil()
-                return self._new_c(left, right-left)
+                return self._new_c(left, right - left)
 
     def __invert__(self):
         """
@@ -821,7 +824,7 @@ cdef class RealIntervalAbsoluteElement(FieldElement):
         mpz_init_set_ui(scaling_factor, 1)
         try:
             mpz_set_ui(scaling_factor, 1)
-            mpz_mul_2exp(scaling_factor, scaling_factor, 2*absprec)
+            mpz_mul_2exp(scaling_factor, scaling_factor, 2 * absprec)
             # Use diameter as temp value for right endpoint...
             mpz_add(diameter.value, self._mantissa.value, self._diameter.value)
             mpz_fdiv_q(mantissa.value, scaling_factor, diameter.value)
@@ -963,8 +966,7 @@ cdef class RealIntervalAbsoluteElement(FieldElement):
         import math
         cdef double height
         base_height = max(abs(base.lower()), abs(base.upper()))
-        midpoint = base.upper()
-        height = (math.log(base_height)/math.log(2))
+        height = math.log(base_height) / math.log(2)
         height *= (exponent.midpoint() if isinstance(exponent, RealIntervalAbsoluteElement) else exponent)
         relprec = max(height + parent._absprec, 10)
         RIF = RealIntervalField(relprec)
diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx
index da282aceb3b..2ddd9135878 100644
--- a/src/sage/rings/real_mpfi.pyx
+++ b/src/sage/rings/real_mpfi.pyx
@@ -288,21 +288,21 @@ from sage.cpython.string cimport char_to_str, bytes_to_str
 from sage.misc.superseded import deprecation
 import sage.rings.infinity
 
-#*****************************************************************************
+# ****************************************************************************
 #
 #       Implementation
 #
-#*****************************************************************************
+# ****************************************************************************
 
 # Global settings
 printing_style = 'question'
 printing_error_digits = 0
 
-#*****************************************************************************
+# ****************************************************************************
 #
 #       Real Interval Field
 #
-#*****************************************************************************
+# ****************************************************************************
 
 cdef dict RealIntervalField_cache = {}
 cpdef RealIntervalField_class RealIntervalField(prec=53, sci_not=False):
@@ -602,12 +602,11 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField):
         """
         if rnd == "RNDD":
             return self.lower_field()
-        elif rnd == "RNDN":
+        if rnd == "RNDN":
             return self.middle_field()
-        elif rnd == "RNDU":
+        if rnd == "RNDU":
             return self.upper_field()
-        else:
-            return RealField(self._prec, self.sci_not, rnd)
+        return RealField(self._prec, self.sci_not, rnd)
 
     def _repr_(self):
         """
@@ -620,7 +619,7 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField):
             sage: RealIntervalField(200) # indirect doctest
             Real Interval Field with 200 bits of precision
         """
-        s = "Real Interval Field with %s bits of precision"%self._prec
+        s = "Real Interval Field with %s bits of precision" % self._prec
         return s
 
     def _latex_(self):
@@ -753,8 +752,9 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField):
         from sage.categories.pushout import CompletionFunctor
         return (CompletionFunctor(sage.rings.infinity.Infinity,
                                   self.prec(),
-                                  {'sci_not': self.scientific_notation(), 'type': 'Interval'}),
-               sage.rings.rational_field.QQ)
+                                  {'sci_not': self.scientific_notation(),
+                                   'type': 'Interval'}),
+                sage.rings.rational_field.QQ)
 
     cpdef _coerce_map_from_(self, S):
         """
@@ -1006,7 +1006,7 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField):
             sage: RealIntervalField(200).name()
             'IntervalRealIntervalField200'
         """
-        return "IntervalRealIntervalField%s"%(self._prec)
+        return "IntervalRealIntervalField%s" % (self._prec)
 
     def __hash__(self):
         """
@@ -1163,11 +1163,11 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField):
         raise ValueError("No %sth root of unity in self" % n)
 
 
-#*****************************************************************************
+# ****************************************************************************
 #
 #     RealIntervalFieldElement -- element of Real Interval Field
 #
-#*****************************************************************************
+# ****************************************************************************
 cdef class RealIntervalFieldElement(RingElement):
     """
     A real number interval.
@@ -1372,7 +1372,6 @@ cdef class RealIntervalFieldElement(RingElement):
         """
         raise TypeError
 
-
     def _sage_input_(self, sib, coerce):
         r"""
         Produce an expression which will reproduce this value when evaluated.
@@ -1408,7 +1407,7 @@ cdef class RealIntervalFieldElement(RingElement):
             # The following line would also be correct, but even though it
             # uses coerced=2, that doesn't help because RealNumber doesn't
             # print pretty for directed-rounding fields.
-            #return sib(self.parent())(sib(self.lower(), 2), sib(self.upper(), 2))
+            # return sib(self.parent())(sib(self.lower(), 2), sib(self.upper(), 2))
             return sib(self.parent())(sib(self.lower(rnd='RNDN')), sib(self.upper(rnd='RNDN')))
 
     def __hash__(self):
@@ -1437,7 +1436,7 @@ cdef class RealIntervalFieldElement(RingElement):
             sage: RIF(2.1)._im_gens_(R, [R(1)])
             2.10000?
         """
-        return codomain(self) # since 1 |--> 1
+        return codomain(self)  # since 1 |--> 1
 
     def real(self):
         """
@@ -1687,7 +1686,7 @@ cdef class RealIntervalFieldElement(RingElement):
             t1 = self.lower().str(base=base, no_sci=no_sci, e=e)
             t2 = self.upper().str(base=base, no_sci=no_sci, e=e)
 
-            return "[%s .. %s]"%(t1, t2)
+            return "[%s .. %s]" % (t1, t2)
 
         elif style == 'question':
             if no_sci is None:
@@ -1931,9 +1930,9 @@ cdef class RealIntervalFieldElement(RingElement):
 
         sig_on()
         lower_s = mpfr_get_str(0, &lower_expo, base, 0,
-                                &self.value.left, MPFR_RNDD)
+                               &self.value.left, MPFR_RNDD)
         upper_s = mpfr_get_str(0, &upper_expo, base, 0,
-                                &self.value.right, MPFR_RNDU)
+                               &self.value.right, MPFR_RNDU)
         sig_off()
 
         if lower_s ==  0:
@@ -2165,24 +2164,25 @@ cdef class RealIntervalFieldElement(RingElement):
             scientific = True
 
         if scientific:
-            return '%s%s.%s?%s%s%s'%(sign_string,
-                                     mant_string[0], mant_string[1:],
-                                     error_string, e, sci_expo)
+            return '%s%s.%s?%s%s%s' % (sign_string,
+                                       mant_string[0], mant_string[1:],
+                                       error_string, e, sci_expo)
 
         if expo + digits <= 0:
-            return '%s0.%s%s?%s'%(sign_string,
-                                  '0' * -(expo + digits), mant_string,
-                                  error_string)
+            return '%s0.%s%s?%s' % (sign_string,
+                                    '0' * -(expo + digits), mant_string,
+                                    error_string)
 
-        return '%s%s.%s?%s'%(sign_string,
-                             mant_string[:expo+digits],
-                             mant_string[expo+digits:],
-                             error_string)
+        return '%s%s.%s?%s' % (sign_string,
+                               mant_string[:expo+digits],
+                               mant_string[expo+digits:],
+                               error_string)
 
     def __copy__(self):
         """
-        Return copy of ``self`` - since ``self`` is immutable, we just return
-        ``self`` again.
+        Return copy of ``self``.
+
+        Since ``self`` is immutable, we just return ``self`` again.
 
         EXAMPLES::
 
@@ -2790,7 +2790,6 @@ cdef class RealIntervalFieldElement(RingElement):
         mpfi_mul(x.value, self.value, (right).value)
         return x
 
-
     cpdef _div_(self, right):
         """
         Divide ``self`` by ``right``, where both are real intervals with the
@@ -2957,7 +2956,7 @@ cdef class RealIntervalFieldElement(RingElement):
             0.062500000000000000?
         """
         if isinstance(x, RealIntervalFieldElement) and \
-               isinstance(y, (int, Integer)):
+           isinstance(y, (int, Integer)):
             return x._rshift_(y)
         return sage.structure.element.bin_op(x, y, operator.rshift)
 
@@ -3209,9 +3208,9 @@ cdef class RealIntervalFieldElement(RingElement):
         b = self.upper()
         P = self.parent()
         r = P(a.frac(), b.frac())
-        if b.floor() > max(a,0):
+        if b.floor() > max(a, 0):
             r = r.union(P(0, 1))
-        if a.ceil() < min(b,0):
+        if a.ceil() < min(b, 0):
             r = r.union(P(-1, 0))
         return r
 
@@ -3270,7 +3269,7 @@ cdef class RealIntervalFieldElement(RingElement):
             else:
                 mpfi_get_left(x.value, self.value)
         else:
-            raise AssertionError("%s has unknown rounding mode"%field)
+            raise AssertionError("%s has unknown rounding mode" % field)
         return x
 
     def __float__(self):
@@ -4052,7 +4051,7 @@ cdef class RealIntervalFieldElement(RingElement):
             True
         """
         return mpfr_greaterequal_p(&self.value.right, &other.value.left) \
-           and mpfr_greaterequal_p(&other.value.right, &self.value.left)
+            and mpfr_greaterequal_p(&other.value.right, &self.value.left)
 
     def intersection(self, other):
         """
@@ -4381,7 +4380,6 @@ cdef class RealIntervalFieldElement(RingElement):
             raise ValueError("self (=%s) is not >= 0" % self)
         return self.square_root()
 
-
     def square_root(self):
         """
         Return a square root of ``self``. An interval will always be returned
@@ -5027,12 +5025,11 @@ cdef class RealIntervalFieldElement(RingElement):
             sage: RIF(-1, 1).algdep(5)
             x
         """
-
         # If 0 is in the interval, then we have no known bits!  But
         # fortunately, there's a perfectly valid answer we can
         # return anyway.
         if 0 in self:
-            #import sage.rings.polynomial.polynomial_ring
+            # import sage.rings.polynomial.polynomial_ring
             return sage.rings.polynomial.polynomial_ring.polygen(
                 sage.rings.integer_ring.IntegerRing())
 
@@ -5176,6 +5173,7 @@ cdef class RealIntervalFieldElement(RingElement):
         return RealBallField(self.precision())(self).zeta(a).\
             _real_mpfi_(self._parent)
 
+
 def _simplest_rational_test_helper(low, high, low_open=False, high_open=False):
     """
     Call ``_simplest_rational_exact()``. Only used to allow doctests on
@@ -5189,6 +5187,7 @@ def _simplest_rational_test_helper(low, high, low_open=False, high_open=False):
     """
     return _simplest_rational_exact(low, high, low_open, high_open)
 
+
 cdef _simplest_rational_exact(Rational low, Rational high, int low_open, int high_open):
     """
     Return the simplest rational between ``low`` and ``high``. May return
@@ -5313,10 +5312,10 @@ def RealInterval(s, upper=None, int base=10, int pad=0, min_prec=53):
         s = str(s)
     if base == 10:
         # hard-code the common case
-        bits = int(LOG_TEN_TWO_PLUS_EPSILON*len(s))
+        bits = int(LOG_TEN_TWO_PLUS_EPSILON * len(s))
     else:
-        bits = int(math.log(base,2)*1.00001*len(s))
-    R = RealIntervalField(prec=max(bits+pad, min_prec))
+        bits = int(math.log(base, 2) * 1.00001 * len(s))
+    R = RealIntervalField(prec=max(bits + pad, min_prec))
     if upper is not None:
         s = (s, upper)
     return RealIntervalFieldElement(R, s, base)
@@ -5325,6 +5324,7 @@ def RealInterval(s, upper=None, int base=10, int pad=0, min_prec=53):
 # The default real interval field, with precision 53 bits
 RIF = RealIntervalField()
 
+
 def is_RealIntervalField(x):
     """
     Check if ``x`` is a :class:`RealIntervalField_class`.
@@ -5338,6 +5338,7 @@ def is_RealIntervalField(x):
     """
     return isinstance(x, RealIntervalField_class)
 
+
 def is_RealIntervalFieldElement(x):
     """
     Check if ``x`` is a :class:`RealIntervalFieldElement`.
@@ -5352,7 +5353,7 @@ def is_RealIntervalFieldElement(x):
     return isinstance(x, RealIntervalFieldElement)
 
 
-#### pickle functions
+# pickle functions
 def __create__RealIntervalField_version0(prec, sci_not):
     """
     For pickling.
diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx
index 1100a63e3fe..885003ed7f8 100644
--- a/src/sage/rings/real_mpfr.pyx
+++ b/src/sage/rings/real_mpfr.pyx
@@ -142,7 +142,6 @@ from sage.libs.gmp.pylong cimport mpz_set_pylong
 from sage.libs.mpfr cimport *
 from sage.libs.mpmath.utils cimport mpfr_to_mpfval
 from sage.misc.randstate cimport randstate, current_randstate
-from sage.misc.superseded import deprecation_cython as deprecation
 
 from sage.structure.element cimport Element
 from sage.structure.parent cimport Parent
@@ -183,11 +182,12 @@ cdef object numpy_object_interface = {'typestr': '|O'}
 cdef enum:
     SIG_PREC_THRESHOLD = 1000
 
-#*****************************************************************************
+
+# ****************************************************************************
 #
 #       External Python access to constants
 #
-#*****************************************************************************
+# ****************************************************************************
 
 def mpfr_prec_min():
     """
@@ -211,6 +211,7 @@ def mpfr_prec_min():
     """
     return MPFR_PREC_MIN
 
+
 # see Issue #11666 for the origin of this magical constant
 def mpfr_prec_max():
     """
@@ -252,6 +253,7 @@ def mpfr_get_exp_min():
     """
     return mpfr_get_emin()
 
+
 def mpfr_get_exp_max():
     """
     Return the current maximal exponent for MPFR numbers.
@@ -270,6 +272,7 @@ def mpfr_get_exp_max():
     """
     return mpfr_get_emax()
 
+
 def mpfr_set_exp_min(mp_exp_t e):
     """
     Set the minimal exponent for MPFR numbers.
@@ -290,6 +293,7 @@ def mpfr_set_exp_min(mp_exp_t e):
     if mpfr_set_emin(e) != 0:
         raise OverflowError("bad value for mpfr_set_exp_min()")
 
+
 def mpfr_set_exp_max(mp_exp_t e):
     """
     Set the maximal exponent for MPFR numbers.
@@ -310,6 +314,7 @@ def mpfr_set_exp_max(mp_exp_t e):
     if mpfr_set_emax(e) != 0:
         raise OverflowError("bad value for mpfr_set_exp_max()")
 
+
 def mpfr_get_exp_min_min():
     """
     Get the minimal value allowed for :func:`mpfr_set_exp_min`.
@@ -330,6 +335,7 @@ def mpfr_get_exp_min_min():
     """
     return mpfr_get_emin_min()
 
+
 def mpfr_get_exp_max_max():
     """
     Get the maximal value allowed for :func:`mpfr_set_exp_max`.
@@ -350,6 +356,7 @@ def mpfr_get_exp_max_max():
     """
     return mpfr_get_emax_max()
 
+
 # On Sage startup, set the exponent range to the maximum allowed
 mpfr_set_exp_min(mpfr_get_emin_min())
 mpfr_set_exp_max(mpfr_get_emax_max())
@@ -365,9 +372,11 @@ mpfr_set_exp_max(mpfr_get_emax_max())
 from sage.arith.long cimport (pyobject_to_long, integer_check_long_py,
                               ERR_OVERFLOW)
 cdef dict rounding_modes = dict(RNDN=MPFR_RNDN, RNDZ=MPFR_RNDZ,
-        RNDD=MPFR_RNDD, RNDU=MPFR_RNDU, RNDA=MPFR_RNDA, RNDF=MPFR_RNDF)
+                                RNDD=MPFR_RNDD, RNDU=MPFR_RNDU,
+                                RNDA=MPFR_RNDA, RNDF=MPFR_RNDF)
 
-cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2)
+cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363
+# a small overestimate of log(10,2)
 
 cdef object RealField_cache = sage.misc.weak_dict.WeakValueDictionary()
 
@@ -443,8 +452,8 @@ cpdef RealField(mpfr_prec_t prec=53, int sci_not=0, rnd=MPFR_RNDN):
         try:
             r = rounding_modes[rnd]
         except KeyError:
-            raise ValueError("rounding mode (={!r}) must be one of {}".format(rnd,
-                sorted(rounding_modes)))
+            msg = "rounding mode (={!r}) must be one of {}"
+            raise ValueError(msg.format(rnd, sorted(rounding_modes)))
 
     try:
         return RealField_cache[prec, sci_not, r]
@@ -570,9 +579,9 @@ cdef class RealField_class(sage.rings.abc.RealField):
             sage: RealField(17,rnd='RNDD') # indirect doctest
             Real Field with 17 bits of precision and rounding RNDD
         """
-        s = "Real Field with %s bits of precision"%self._prec
+        s = "Real Field with %s bits of precision" % self._prec
         if self.rnd != MPFR_RNDN:
-            s = s + " and rounding %s"%(self.rnd_str)
+            s = s + " and rounding %s" % (self.rnd_str)
         return s
 
     def _latex_(self):
@@ -829,8 +838,10 @@ cdef class RealField_class(sage.rings.abc.RealField):
         from sage.categories.pushout import CompletionFunctor
         return (CompletionFunctor(sage.rings.infinity.Infinity,
                                   self.prec(),
-                                  {'type': 'MPFR', 'sci_not': self.scientific_notation(), 'rnd': self.rnd}),
-               sage.rings.rational_field.QQ)
+                                  {'type': 'MPFR',
+                                   'sci_not': self.scientific_notation(),
+                                   'rnd': self.rnd}),
+                sage.rings.rational_field.QQ)
 
     def gen(self, i=0):
         """
@@ -976,7 +987,7 @@ cdef class RealField_class(sage.rings.abc.RealField):
             sage: RealField(100,rnd='RNDU').name()
             'RealField100_2'
         """
-        return "RealField%s_%s"%(self._prec,self.rnd)
+        return "RealField%s_%s" % (self._prec, self.rnd)
 
     def __hash__(self):
         """
@@ -1005,7 +1016,7 @@ cdef class RealField_class(sage.rings.abc.RealField):
         """
         return Integer(self._prec)
 
-    prec=precision # an alias
+    prec = precision  # an alias
 
     def _magma_init_(self, magma):
         r"""
@@ -1060,7 +1071,8 @@ cdef class RealField_class(sage.rings.abc.RealField):
             0.88622692545275801364908374167057259139877473
         """
         cdef RealNumber x = self._new()
-        if self._prec > SIG_PREC_THRESHOLD: sig_on()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         # The docs for mpfr_free_cache say "Free the cache used by
         # the functions computing constants if needed (currently
         # mpfr_const_log2, mpfr_const_pi and mpfr_const_euler)", so
@@ -1070,7 +1082,8 @@ cdef class RealField_class(sage.rings.abc.RealField):
         # functions, but this free is needed for them too!
         mpfr_free_cache()
         mpfr_const_pi(x.value, self.rnd)
-        if self._prec > SIG_PREC_THRESHOLD: sig_off()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def euler_constant(self):
@@ -1099,10 +1112,12 @@ cdef class RealField_class(sage.rings.abc.RealField):
             0.91596559417721901505460351493
         """
         cdef RealNumber x = self._new()
-        if self._prec > SIG_PREC_THRESHOLD: sig_on()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_free_cache()
         mpfr_const_catalan(x.value, self.rnd)
-        if self._prec > SIG_PREC_THRESHOLD: sig_off()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def log2(self):
@@ -1119,10 +1134,12 @@ cdef class RealField_class(sage.rings.abc.RealField):
             0.69314718055994530941723212146
         """
         cdef RealNumber x = self._new()
-        if self._prec > SIG_PREC_THRESHOLD: sig_on()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_free_cache()
         mpfr_const_log2(x.value, self.rnd)
-        if self._prec > SIG_PREC_THRESHOLD: sig_off()
+        if self._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def random_element(self, min=-1, max=1, distribution=None):
@@ -1189,9 +1206,11 @@ cdef class RealField_class(sage.rings.abc.RealField):
         if n < 0:
             raise ArithmeticError("n must be nonnegative")
         x = self._new()
-        if self._prec > SIG_PREC_THRESHOLD and n < SIG_PREC_THRESHOLD: sig_on()
+        if self._prec > SIG_PREC_THRESHOLD and n < SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_fac_ui(x.value, n, self.rnd)
-        if self._prec > SIG_PREC_THRESHOLD and n < SIG_PREC_THRESHOLD: sig_off()
+        if self._prec > SIG_PREC_THRESHOLD and n < SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def rounding_mode(self):
@@ -1314,13 +1333,14 @@ cdef class RealField_class(sage.rings.abc.RealField):
         F = list(f._pari_with_name().factor())
 
         from sage.structure.factorization import Factorization
-        return Factorization([(R(g).monic(),e) for g,e in zip(*F)], f.leading_coefficient())
+        return Factorization([(R(g).monic(), e) for g, e in zip(*F)],
+                             f.leading_coefficient())
 
-#*****************************************************************************
+# ****************************************************************************
 #
 #     RealNumber -- element of Real Field
 #
-#*****************************************************************************
+# ****************************************************************************
 
 cdef class RealLiteral(RealNumber)
 
@@ -1496,7 +1516,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
         elif type(x) is gmpy2.mpz:
             mpfr_set_z(self.value, (x).z, parent.rnd)
         else:
-            s = str(x).replace(' ','').replace('_', '')
+            s = str(x).replace(' ', '').replace('_', '')
             s_lower = s.lower()
             if s_lower == 'infinity':
                 raise ValueError('can only convert signed infinity to RR')
@@ -1742,8 +1762,8 @@ cdef class RealNumber(sage.structure.element.RingElement):
         # and "-1.3*x".)
         cdef bint can_use_float_literal = \
             rnd == MPFR_RNDN and (sib.preparse() or
-                                 ((will_convert or self.prec() <= 53) and
-                                  self._parent(float(self_str)) == self))
+                                  ((will_convert or self.prec() <= 53) and
+                                   self._parent(float(self_str)) == self))
 
         if can_use_int_literal or can_use_float_literal:
             if can_use_int_literal:
@@ -1809,7 +1829,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
             sage: RR(2.1)._im_gens_(R, [R(1)])
             2.1000
         """
-        return codomain(self) # since 1 |--> 1
+        return codomain(self)  # since 1 |--> 1
 
     def real(self):
         """
@@ -2537,7 +2557,6 @@ cdef class RealNumber(sage.structure.element.RingElement):
         mpfr_mul(x.value, self.value, (right).value, (self._parent).rnd)
         return x
 
-
     cpdef _div_(self, right):
         """
         Divide ``self`` by other, where both are real numbers with the same
@@ -2687,7 +2706,6 @@ cdef class RealNumber(sage.structure.element.RingElement):
         except TypeError:
             raise TypeError("unsupported operands for >>")
 
-
     def multiplicative_order(self):
         """
         Return the multiplicative order of ``self``.
@@ -2738,7 +2756,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
         """
         return Integer((self._parent)._prec)
 
-    prec = precision # alias
+    prec = precision  # alias
 
     def conjugate(self):
         """
@@ -2861,7 +2879,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
                 mpfr_nextabove(x.value)
             elif mpfr_inf_p(self.value):
                 mpfr_set_inf(x.value, 1)
-            else: # NaN
+            else:  # NaN
                 mpfr_set_nan(x.value)
         return x
 
@@ -2940,7 +2958,6 @@ cdef class RealNumber(sage.structure.element.RingElement):
         mpfr_abs(x.value, x.value, _parent.rnd)
         return x
 
-
     ###################
     # Rounding etc
     ###################
@@ -2965,8 +2982,8 @@ cdef class RealNumber(sage.structure.element.RingElement):
         cdef RealNumber x
         x = (left)._new()
         mpfr_remainder (x.value, (left).value,
-                (right).value,
-                ((left)._parent).rnd)
+                        (right).value,
+                        ((left)._parent).rnd)
         return x
 
     def round(self):
@@ -3049,7 +3066,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
         mpfr_ceil(x.value, self.value)
         return x.integer_part()
 
-    ceiling = ceil # alias
+    ceiling = ceil  # alias
 
     def trunc(self):
         """
@@ -3273,10 +3290,10 @@ cdef class RealNumber(sage.structure.element.RingElement):
         """
         prec = self.parent().prec()
 
-        #Set the precision in Axiom
-        old_prec = axiom('precision(%s)$Float'%prec)
-        res = axiom('%s :: Float'%self.exact_rational())
-        axiom.eval('precision(%s)$Float'%old_prec)
+        # Set the precision in Axiom
+        old_prec = axiom('precision(%s)$Float' % prec)
+        res = axiom('%s :: Float' % self.exact_rational())
+        axiom.eval('precision(%s)$Float' % old_prec)
 
         return res
 
@@ -3412,15 +3429,14 @@ cdef class RealNumber(sage.structure.element.RingElement):
         cdef Integer sign
         cdef mp_exp_t exponent
 
-
-        if mpfr_signbit(self.value)==0:
-            sign=Integer(1)
+        if mpfr_signbit(self.value) == 0:
+            sign = Integer(1)
         else:
-            sign=Integer(-1)
+            sign = Integer(-1)
 
         if mpfr_zero_p(self.value):
-            mantissa=Integer(0)
-            exponent=Integer(0)
+            mantissa = Integer(0)
+            exponent = Integer(0)
         else:
             mantissa = Integer()
             exponent = mpfr_get_z_exp(mantissa.value, self.value)
@@ -3752,7 +3768,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
             raise ValueError('cannot convert NaN or infinity to rational number')
 
         if ((max_error is None and max_denominator is None) or
-            (max_error is not None and max_denominator is not None)):
+                (max_error is not None and max_denominator is not None)):
             raise ValueError('Must specify exactly one of max_error or max_denominator in nearby_rational()')
 
         if max_error is not None:
@@ -4192,9 +4208,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
         cdef RealNumber x
         if mpfr_cmp_ui(self.value, 0) >= 0:
             x = self._new()
-            if (self._parent)._prec > 10*SIG_PREC_THRESHOLD: sig_on()
+            if (self._parent)._prec > 10*SIG_PREC_THRESHOLD:
+                sig_on()
             mpfr_sqrt(x.value, self.value, (self._parent).rnd)
-            if (self._parent)._prec > 10*SIG_PREC_THRESHOLD: sig_off()
+            if (self._parent)._prec > 10*SIG_PREC_THRESHOLD:
+                sig_off()
             if all:
                 if x.is_zero():
                     return [x]
@@ -4238,9 +4256,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             -1.42108547152020e-14
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > 10*SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > 10*SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_cbrt(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > 10*SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > 10*SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def _pow(self, RealNumber exponent):
@@ -4380,9 +4400,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
                 return self._complex_number_().log(base)
         if base is None or base == 'e':
             x = self._new()
-            if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+            if (self._parent)._prec > SIG_PREC_THRESHOLD:
+                sig_on()
             mpfr_log(x.value, self.value, (self._parent).rnd)
-            if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+            if (self._parent)._prec > SIG_PREC_THRESHOLD:
+                sig_off()
             return x
         elif base == 10:
             return self.log10()
@@ -4421,9 +4443,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
         if self < 0:
             return self._complex_number_().log(2)
         x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_log2(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def log10(self):
@@ -4458,9 +4482,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
         if self < 0:
             return self._complex_number_().log(10)
         x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_log10(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def log1p(self):
@@ -4507,9 +4533,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
         if self < -1:
             return (self+1.0)._complex_number_().log()
         x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_log1p(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def exp(self):
@@ -4565,9 +4593,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             1.89117248253021e-10
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_exp2(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def exp10(self):
@@ -4593,9 +4623,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             5.01187233627276e-33
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_exp10(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def expm1(self):
@@ -4617,9 +4649,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             1.00000000000000e-16
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_expm1(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def eint(self):
@@ -4639,9 +4673,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             -0.219383934395520
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_eint(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         return x
 
     def cos(self):
@@ -4706,13 +4742,13 @@ cdef class RealNumber(sage.structure.element.RingElement):
             sage: t.sincos()
             (0.500000000000000, 0.866025403784439)
         """
-        cdef RealNumber x,y
+        cdef RealNumber x, y
         x = self._new()
         y = self._new()
         sig_on()
         mpfr_sin_cos(x.value, y.value, self.value, (self._parent).rnd)
         sig_off()
-        return x,y
+        return x, y
 
     def arccos(self):
         """
@@ -5070,9 +5106,11 @@ cdef class RealNumber(sage.structure.element.RingElement):
             _other = self._parent(other)
 
         x = self._new()
-        if (self._parent)._prec > 10000: sig_on()
+        if (self._parent)._prec > 10000:
+            sig_on()
         mpfr_agm(x.value, self.value, _other.value, (self._parent).rnd)
-        if (self._parent)._prec > 10000: sig_off()
+        if (self._parent)._prec > 10000:
+            sig_off()
         return x
 
     def erf(self):
@@ -5225,16 +5263,19 @@ cdef class RealNumber(sage.structure.element.RingElement):
             0.886226925452758
         """
         cdef RealNumber x = self._new()
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_on()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_on()
         mpfr_gamma(x.value, self.value, (self._parent).rnd)
-        if (self._parent)._prec > SIG_PREC_THRESHOLD: sig_off()
+        if (self._parent)._prec > SIG_PREC_THRESHOLD:
+            sig_off()
         return x
 
     def log_gamma(self):
         """
-        Return the principal branch of the log gamma of ``self``. Note that
-        this is not in general equal to log(gamma(``self``)) for negative
-        input.
+        Return the principal branch of the log gamma of ``self``.
+
+        Note that this is not in general equal to log(gamma(``self``))
+        for negative input.
 
         EXAMPLES::
 
@@ -5334,7 +5375,7 @@ cdef class RealNumber(sage.structure.element.RingElement):
             sage: r.algebraic_dependency(5)
             x^2 - 2
         """
-        return sage.arith.misc.algdep(self,n)
+        return sage.arith.misc.algdep(self, n)
 
     algdep = algebraic_dependency
 
@@ -5827,9 +5868,9 @@ def create_RealNumber(s, int base=10, int pad=0, rnd="RNDN", int min_prec=53):
 
         if base == 10:
             # hard-code the common case
-            bits = int(LOG_TEN_TWO_PLUS_EPSILON*sigfigs)+1
+            bits = int(LOG_TEN_TWO_PLUS_EPSILON * sigfigs) + 1
         else:
-            bits = int(math.log(base,2)*1.00001*sigfigs)+1
+            bits = int(math.log(base, 2) * 1.00001 * sigfigs) + 1
 
         R = RealField(prec=max(bits + pad, min_prec), rnd=rnd)
 
@@ -5855,6 +5896,7 @@ def is_RealNumber(x):
     """
     return isinstance(x, RealNumber)
 
+
 def __create__RealField_version0(prec, sci_not, rnd):
     """
     Create a :class:`RealField_class` by calling :func:`RealField()`.
@@ -5866,6 +5908,7 @@ def __create__RealField_version0(prec, sci_not, rnd):
     """
     return RealField(prec, sci_not, rnd)
 
+
 def __create__RealNumber_version0(parent, x, base=10):
     """
     Create a :class:`RealNumber`.
diff --git a/src/sage/rings/ring.pxd b/src/sage/rings/ring.pxd
index e74638b91e2..cf5399b66d9 100644
--- a/src/sage/rings/ring.pxd
+++ b/src/sage/rings/ring.pxd
@@ -16,14 +16,13 @@ cdef class CommutativeRing(Ring):
 cdef class IntegralDomain(CommutativeRing):
     pass
 
-cdef class DedekindDomain(IntegralDomain):
+cdef class DedekindDomain(CommutativeRing):
     pass
 
-
-cdef class PrincipalIdealDomain(IntegralDomain):
+cdef class PrincipalIdealDomain(CommutativeRing):
     pass
 
-cdef class Field(PrincipalIdealDomain):
+cdef class Field(CommutativeRing):
     pass
 
 cdef class Algebra(Ring):
diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx
index fe346931ce5..36f8336786c 100644
--- a/src/sage/rings/ring.pyx
+++ b/src/sage/rings/ring.pyx
@@ -25,9 +25,9 @@ The class inheritance hierarchy is:
     - :class:`IntegralDomain` (deprecated)
 
       - :class:`DedekindDomain` (deprecated and essentially removed)
-      - :class:`PrincipalIdealDomain` (deprecated)
+      - :class:`PrincipalIdealDomain` (deprecated and essentially removed)
 
-Subclasses of :class:`PrincipalIdealDomain` are
+Subclasses of :class:`CommutativeRing` are
 
 - :class:`Field`
 
@@ -35,9 +35,7 @@ Subclasses of :class:`PrincipalIdealDomain` are
 
 Some aspects of this structure may seem strange, but this is an unfortunate
 consequence of the fact that Cython classes do not support multiple
-inheritance. Hence, for instance, :class:`Field` cannot be a subclass of both
-:class:`NoetherianRing` and :class:`PrincipalIdealDomain`, although all fields
-are Noetherian PIDs.
+inheritance.
 
 (A distinct but equally awkward issue is that sometimes we may not know *in
 advance* whether or not a ring belongs in one of these classes; e.g. some
@@ -58,7 +56,7 @@ TESTS:
 
 This is to test a deprecation::
 
-    sage: from sage.rings.ring import DedekindDomain, CommutativeAlgebra
+    sage: from sage.rings.ring import DedekindDomain
     sage: class No(DedekindDomain):
     ....:     pass
     sage: F = No(QQ)
@@ -68,6 +66,7 @@ This is to test a deprecation::
     sage: F.category()
     Category of Dedekind domains
 
+    sage: from sage.rings.ring import CommutativeAlgebra
     sage: class Nein(CommutativeAlgebra):
     ....:     pass
     sage: F = Nein(QQ, QQ)
@@ -76,6 +75,16 @@ This is to test a deprecation::
     See https://github.com/sagemath/sage/issues/37999 for details.
     sage: F.category()
     Category of commutative rings
+
+    sage: from sage.rings.ring import PrincipalIdealDomain
+    sage: class Non(PrincipalIdealDomain):
+    ....:     pass
+    sage: F = Non(QQ)
+    ...:
+    DeprecationWarning: use the category PrincipalIdealDomains
+    See https://github.com/sagemath/sage/issues/37719 for details.
+    sage: F.category()
+    Category of principal ideal domains
 """
 
 # ****************************************************************************
@@ -92,7 +101,7 @@ from sage.misc.superseded import deprecation
 
 from sage.structure.coerce cimport coercion_model
 from sage.structure.parent cimport Parent
-from sage.structure.category_object import check_default_category
+from sage.structure.category_object cimport check_default_category
 from sage.structure.sequence import Sequence
 from sage.misc.prandom import randint
 from sage.categories.rings import Rings
@@ -100,6 +109,7 @@ from sage.categories.commutative_rings import CommutativeRings
 from sage.categories.integral_domains import IntegralDomains
 from sage.categories.dedekind_domains import DedekindDomains
 from sage.categories.principal_ideal_domains import PrincipalIdealDomains
+from sage.categories.noetherian_rings import NoetherianRings
 
 _Rings = Rings()
 _CommutativeRings = CommutativeRings()
@@ -225,7 +235,7 @@ cdef class Ring(ParentWithGens):
         # This is a low-level class. For performance, we trust that the category
         # is fine, if it is provided. If it isn't, we use the category of rings.
         if category is None:
-            category=_Rings
+            category = check_default_category(_Rings, category)
         Parent.__init__(self, base=base, names=names, normalize=normalize,
                         category=category)
 
@@ -436,7 +446,7 @@ cdef class Ring(ParentWithGens):
             elif isinstance(first, (list, tuple)):
                 gens = first
             elif is_Parent(first) and self.has_coerce_map_from(first):
-                gens = first.gens() # we have a ring as argument
+                gens = first.gens()  # we have a ring as argument
             else:
                 break
 
@@ -445,14 +455,14 @@ cdef class Ring(ParentWithGens):
 
         if coerce:
             gens = [self(g) for g in gens]
-        if isinstance(self, PrincipalIdealDomain):
+        if self in PrincipalIdealDomains():
             # Use GCD algorithm to obtain a principal ideal
             g = gens[0]
             if len(gens) == 1:
                 try:
                     # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
                     g = g.gcd(g)
-                except (AttributeError, NotImplementedError):
+                except (AttributeError, NotImplementedError, IndexError):
                     pass
             else:
                 for h in gens[1:]:
@@ -531,57 +541,6 @@ cdef class Ring(ParentWithGens):
             else:
                 raise TypeError("Don't know how to transform %s into an ideal of %s" % (self, x))
 
-    def _ideal_class_(self, n=0):
-        r"""
-        Return a callable object that can be used to create ideals in this
-        ring. For generic rings, this returns the factory function
-        :func:`sage.rings.ideal.Ideal`, which does its best to be clever about
-        what is required.
-
-        This class can depend on `n`, the number of generators of the ideal.
-        The default input of `n=0` indicates an unspecified number of generators,
-        in which case a class that works for any number of generators is returned.
-
-        EXAMPLES::
-
-            sage: ZZ._ideal_class_()
-            
-            sage: RR._ideal_class_()
-            
-            sage: R. = GF(5)[]
-            sage: R._ideal_class_(1)
-            
-            sage: S = R.quo(x^3 - y^2)
-            sage: S._ideal_class_(1)
-            
-            sage: S._ideal_class_(2)
-            
-            sage: T. = S[]                                                           # needs sage.libs.singular
-            sage: T._ideal_class_(5)                                                    # needs sage.libs.singular
-            
-            sage: T._ideal_class_(1)                                                    # needs sage.libs.singular
-            
-
-        Since :issue:`7797`, non-commutative rings have ideals as well::
-
-            sage: A = SteenrodAlgebra(2)                                                # needs sage.combinat sage.modules
-            sage: A._ideal_class_()                                                     # needs sage.combinat sage.modules
-            
-
-        """
-        # One might need more than just n, but I can't think of an example.
-        from sage.rings.noncommutative_ideals import Ideal_nc
-        try:
-            if not self.is_commutative():
-                return Ideal_nc
-        except (NotImplementedError, AttributeError):
-            return Ideal_nc
-        from sage.rings.ideal import Ideal_generic, Ideal_principal
-        if n == 1:
-            return Ideal_principal
-        else:
-            return Ideal_generic
-
     def principal_ideal(self, gen, coerce=True):
         """
         Return the principal ideal generated by gen.
@@ -696,7 +655,7 @@ cdef class Ring(ParentWithGens):
             return x
         return self._one_element
 
-    def is_field(self, proof = True):
+    def is_field(self, proof=True):
         """
         Return ``True`` if this ring is a field.
 
@@ -837,99 +796,6 @@ cdef class Ring(ParentWithGens):
         """
         return False
 
-    def is_integral_domain(self, proof = True):
-        """
-        Return ``True`` if this ring is an integral domain.
-
-        INPUT:
-
-        - ``proof`` -- (default: ``True``) Determines what to do in unknown
-          cases
-
-        ALGORITHM:
-
-        If the parameter ``proof`` is set to ``True``, the returned value is
-        correct but the method might throw an error.  Otherwise, if it is set
-        to ``False``, the method returns ``True`` if it can establish that self
-        is an integral domain and ``False`` otherwise.
-
-        EXAMPLES::
-
-            sage: QQ.is_integral_domain()
-            True
-            sage: ZZ.is_integral_domain()
-            True
-            sage: ZZ['x,y,z'].is_integral_domain()
-            True
-            sage: Integers(8).is_integral_domain()
-            False
-            sage: Zp(7).is_integral_domain()                                            # needs sage.rings.padics
-            True
-            sage: Qp(7).is_integral_domain()                                            # needs sage.rings.padics
-            True
-            sage: R. = QQ[]
-            sage: S. = R.quo((b^3))                                                # needs sage.libs.singular
-            sage: S.is_integral_domain()                                                # needs sage.libs.singular
-            False
-
-        This illustrates the use of the ``proof`` parameter::
-
-            sage: R. = ZZ[]
-            sage: S. = R.quo((b^3))                                                # needs sage.libs.singular
-            sage: S.is_integral_domain(proof=True)                                      # needs sage.libs.singular
-            Traceback (most recent call last):
-            ...
-            NotImplementedError
-            sage: S.is_integral_domain(proof=False)                                     # needs sage.libs.singular
-            False
-
-        TESTS:
-
-        Make sure :issue:`10481` is fixed::
-
-            sage: x = polygen(ZZ, 'x')
-            sage: R. = ZZ['x'].quo(x^2)                                              # needs sage.libs.pari
-            sage: R.fraction_field()                                                    # needs sage.libs.pari
-            Traceback (most recent call last):
-            ...
-            TypeError: self must be an integral domain.
-            sage: R.is_integral_domain()                                                # needs sage.libs.pari
-            False
-
-        Forward the proof flag to ``is_field``, see :issue:`22910`::
-
-            sage: # needs sage.libs.singular
-            sage: R1. = GF(5)[]
-            sage: F1 = R1.quotient_ring(x^2 + x + 1)
-            sage: R2. = F1[]
-            sage: F2 = R2.quotient_ring(x^2 + x + 1)
-            sage: F2.is_integral_domain(False)
-            False
-        """
-        if self.is_field(proof):
-            return True
-
-        if self.is_zero():
-            return False
-
-        if proof:
-            raise NotImplementedError
-        else:
-            return False
-
-    def is_noetherian(self):
-        """
-        Return ``True`` if this ring is Noetherian.
-
-        EXAMPLES::
-
-            sage: QQ.is_noetherian()
-            True
-            sage: ZZ.is_noetherian()
-            True
-        """
-        raise NotImplementedError
-
     def order(self):
         """
         The number of elements of ``self``.
@@ -1158,6 +1024,8 @@ cdef class CommutativeRing(Ring):
     """
     Generic commutative ring.
     """
+    _default_category = _CommutativeRings
+
     def __init__(self, base_ring, names=None, normalize=True, category=None):
         """
         Initialize ``self``.
@@ -1175,8 +1043,7 @@ cdef class CommutativeRing(Ring):
         # This is a low-level class. For performance, we trust that
         # the category is fine, if it is provided. If it isn't, we use
         # the category of commutative rings.
-        if category is None:
-            category=_CommutativeRings
+        category = check_default_category(self._default_category, category)
         Ring.__init__(self, base_ring, names=names, normalize=normalize,
                       category=category)
 
@@ -1615,10 +1482,9 @@ cdef class IntegralDomain(CommutativeRing):
 
         This method is used by all the abstract subclasses of
         :class:`IntegralDomain`, like :class:`NoetherianRing`,
-        :class:`PrincipalIdealDomain`,
         :class:`Field`, ... in order to
         avoid cascade calls Field.__init__ ->
-        PrincipalIdealDomain.__init__ -> IntegralDomain.__init__ ->
+        IntegralDomain.__init__ ->
         ...
 
         EXAMPLES::
@@ -1627,56 +1493,22 @@ cdef class IntegralDomain(CommutativeRing):
             sage: F.category()
             Category of integral domains
 
-            sage: F = PrincipalIdealDomain(QQ)
-            sage: F.category()
-            Category of principal ideal domains
-
             sage: F = Field(QQ)
             sage: F.category()
             Category of fields
 
-        If a category is specified, then the category is set to the
-        join of that category with the default category::
-
-            sage: F = PrincipalIdealDomain(QQ, category=EnumeratedSets())
-
         The default value for the category is specified by the class
         attribute ``default_category``::
 
             sage: IntegralDomain._default_category
             Category of integral domains
 
-            sage: PrincipalIdealDomain._default_category
-            Category of principal ideal domains
-
             sage: Field._default_category
             Category of fields
-
         """
-        category = check_default_category(self._default_category, category)
         CommutativeRing.__init__(self, base_ring, names=names, normalize=normalize,
                                  category=category)
 
-    def is_integral_domain(self, proof = True):
-        """
-        Return ``True``, since this ring is an integral domain.
-
-        (This is a naive implementation for objects with type
-        ``IntegralDomain``)
-
-        EXAMPLES::
-
-            sage: ZZ.is_integral_domain()
-            True
-            sage: QQ.is_integral_domain()
-            True
-            sage: ZZ['x'].is_integral_domain()
-            True
-            sage: R = ZZ.quotient(ZZ.ideal(10)); R.is_integral_domain()
-            False
-        """
-        return True
-
     def is_integrally_closed(self):
         r"""
         Return ``True`` if this ring is integrally closed in its field of
@@ -1708,7 +1540,7 @@ cdef class IntegralDomain(CommutativeRing):
         """
         raise NotImplementedError
 
-    def is_field(self, proof = True):
+    def is_field(self, proof=True):
         r"""
         Return ``True`` if this ring is a field.
 
@@ -1733,35 +1565,14 @@ cdef class IntegralDomain(CommutativeRing):
             return False
 
 cdef class NoetherianRing(CommutativeRing):
-    """
-    Generic Noetherian ring class.
-
-    A Noetherian ring is a commutative ring in which every ideal is
-    finitely generated.
-
-    This class is deprecated, and not actually used anywhere in the
-    Sage code base.  If you think you need it, please create a
-    category :class:`NoetherianRings`, move the code of this class
-    there, and use it instead.
-    """
-    def is_noetherian(self):
-        """
-        Return ``True`` since this ring is Noetherian.
-
-        EXAMPLES::
+    _default_category = NoetherianRings()
 
-            sage: ZZ.is_noetherian()
-            True
-            sage: QQ.is_noetherian()
-            True
-            sage: R. = PolynomialRing(QQ)
-            sage: R.is_noetherian()
-            True
-        """
-        return True
+    def __init__(self, *args, **kwds):
+        deprecation(37234, "use the category DedekindDomains")
+        super().__init__(*args, **kwds)
 
 
-cdef class DedekindDomain(IntegralDomain):
+cdef class DedekindDomain(CommutativeRing):
     _default_category = DedekindDomains()
 
     def __init__(self, *args, **kwds):
@@ -1769,154 +1580,12 @@ cdef class DedekindDomain(IntegralDomain):
         super().__init__(*args, **kwds)
 
 
-cdef class PrincipalIdealDomain(IntegralDomain):
-    """
-    Generic principal ideal domain.
-
-    This class is deprecated. Please use the
-    :class:`~sage.categories.principal_ideal_domains.PrincipalIdealDomains`
-    category instead.
-    """
+cdef class PrincipalIdealDomain(CommutativeRing):
     _default_category = PrincipalIdealDomains()
 
-    def is_noetherian(self):
-        """
-        Every principal ideal domain is noetherian, so we return ``True``.
-
-        EXAMPLES::
-
-            sage: Zp(5).is_noetherian()                                                 # needs sage.rings.padics
-            True
-        """
-        return True
-
-    def class_group(self):
-        """
-        Return the trivial group, since the class group of a PID is trivial.
-
-        EXAMPLES::
-
-            sage: QQ.class_group()                                                      # needs sage.groups
-            Trivial Abelian group
-        """
-        from sage.groups.abelian_gps.abelian_group import AbelianGroup
-        return AbelianGroup([])
-
-    def gcd(self, x, y, coerce=True):
-        r"""
-        Return the greatest common divisor of ``x`` and ``y``, as elements
-        of ``self``.
-
-        EXAMPLES:
-
-        The integers are a principal ideal domain and hence a GCD domain::
-
-            sage: ZZ.gcd(42, 48)
-            6
-            sage: 42.factor(); 48.factor()
-            2 * 3 * 7
-            2^4 * 3
-            sage: ZZ.gcd(2^4*7^2*11, 2^3*11*13)
-            88
-            sage: 88.factor()
-            2^3 * 11
-
-        In a field, any nonzero element is a GCD of any nonempty set
-        of nonzero elements. In previous versions, Sage used to return
-        1 in the case of the rational field. However, since :issue:`10771`,
-        the rational field is considered as the
-        *fraction field* of the integer ring. For the fraction field
-        of an integral domain that provides both GCD and LCM, it is
-        possible to pick a GCD that is compatible with the GCD of the
-        base ring::
-
-            sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48)))
-            6
-            
-            sage: QQ.gcd(1/2, 1/3)
-            1/6
-
-        Polynomial rings over fields are GCD domains as well. Here is a simple
-        example over the ring of polynomials over the rationals as well as
-        over an extension ring. Note that ``gcd`` requires x and y to be
-        coercible::
-
-            sage: # needs sage.rings.number_field
-            sage: R. = PolynomialRing(QQ)
-            sage: S. = NumberField(x^2 - 2, 'a')
-            sage: f = (x - a)*(x + a); g = (x - a)*(x^2 - 2)
-            sage: print(f); print(g)
-            x^2 - 2
-            x^3 - a*x^2 - 2*x + 2*a
-            sage: f in R
-            True
-            sage: g in R
-            False
-            sage: R.gcd(f, g)
-            Traceback (most recent call last):
-            ...
-            TypeError: Unable to coerce 2*a to a rational
-            sage: R.base_extend(S).gcd(f,g)
-            x^2 - 2
-            sage: R.base_extend(S).gcd(f, (x - a)*(x^2 - 3))
-            x - a
-        """
-        if coerce:
-            x = self(x)
-            y = self(y)
-        return x.gcd(y)
-
-    def content(self, x, y, coerce=True):
-        r"""
-        Return the content of `x` and `y`, i.e. the unique element `c` of
-        ``self`` such that `x/c` and `y/c` are coprime and integral.
-
-        EXAMPLES::
-
-            sage: QQ.content(ZZ(42), ZZ(48)); type(QQ.content(ZZ(42), ZZ(48)))
-            6
-            
-            sage: QQ.content(1/2, 1/3)
-            1/6
-            sage: factor(1/2); factor(1/3); factor(1/6)
-            2^-1
-            3^-1
-            2^-1 * 3^-1
-            sage: a = (2*3)/(7*11); b = (13*17)/(19*23)
-            sage: factor(a); factor(b); factor(QQ.content(a,b))
-            2 * 3 * 7^-1 * 11^-1
-            13 * 17 * 19^-1 * 23^-1
-            7^-1 * 11^-1 * 19^-1 * 23^-1
-
-        Note the changes to the second entry::
-
-            sage: c = (2*3)/(7*11); d = (13*17)/(7*19*23)
-            sage: factor(c); factor(d); factor(QQ.content(c,d))
-            2 * 3 * 7^-1 * 11^-1
-            7^-1 * 13 * 17 * 19^-1 * 23^-1
-            7^-1 * 11^-1 * 19^-1 * 23^-1
-            sage: e = (2*3)/(7*11); f = (13*17)/(7^3*19*23)
-            sage: factor(e); factor(f); factor(QQ.content(e,f))
-            2 * 3 * 7^-1 * 11^-1
-            7^-3 * 13 * 17 * 19^-1 * 23^-1
-            7^-3 * 11^-1 * 19^-1 * 23^-1
-        """
-        if coerce:
-            x = self(x)
-            y = self(y)
-        return x.content(y)
-
-    def _ideal_class_(self, n=0):
-        """
-        Ideals in PIDs have their own special class.
-
-        EXAMPLES::
-
-            sage: ZZ._ideal_class_()
-            
-        """
-        from sage.rings.ideal import Ideal_pid
-        return Ideal_pid
+    def __init__(self, *args, **kwds):
+        deprecation(37719, "use the category PrincipalIdealDomains")
+        super().__init__(*args, **kwds)
 
 
 cpdef bint _is_Field(x) except -2:
@@ -1955,7 +1624,7 @@ from sage.categories.commutative_algebras import CommutativeAlgebras
 from sage.categories.fields import Fields
 _Fields = Fields()
 
-cdef class Field(PrincipalIdealDomain):
+cdef class Field(CommutativeRing):
     """
     Generic field
     """
@@ -2154,13 +1823,13 @@ cdef class Algebra(Ring):
         # This is a low-level class. For performance, we trust that the category
         # is fine, if it is provided. If it isn't, we use the category of Algebras(base_ring).
         if category is None:
-            category = Algebras(base_ring)
+            category = check_default_category(Algebras(base_ring), category)
         Ring.__init__(self,base_ring, names=names, normalize=normalize,
                       category=category)
 
 
 cdef class CommutativeAlgebra(CommutativeRing):
-    __default_category = CommutativeRings()
+    __default_category = _CommutativeRings
 
     def __init__(self, base_ring, *args, **kwds):
         deprecation(37999, "use the category CommutativeAlgebras")
diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py
index 992cd528803..efbc690b543 100644
--- a/src/sage/schemes/curves/affine_curve.py
+++ b/src/sage/schemes/curves/affine_curve.py
@@ -174,10 +174,11 @@ class AffineCurve(Curve_generic, AlgebraicScheme_subscheme_affine):
 
     EXAMPLES::
 
+        sage: # needs sage.rings.number_field
         sage: R. = QQ[]
-        sage: K. = NumberField(v^2 + 3)                                              # needs sage.rings.number_field
-        sage: A. = AffineSpace(K, 3)                                             # needs sage.rings.number_field
-        sage: C = Curve([z - u*x^2, y^2], A); C                                         # needs sage.rings.number_field
+        sage: K. = NumberField(v^2 + 3)
+        sage: A. = AffineSpace(K, 3)
+        sage: C = Curve([z - u*x^2, y^2], A); C
         Affine Curve over Number Field in u with defining polynomial v^2 + 3
         defined by (-u)*x^2 + z, y^2
 
@@ -194,10 +195,11 @@ def __init__(self, A, X):
 
         EXAMPLES::
 
+            sage: # needs sage.rings.number_field
             sage: R. = QQ[]
-            sage: K. = NumberField(v^2 + 3)                                          # needs sage.rings.number_field
-            sage: A. = AffineSpace(K, 3)                                         # needs sage.rings.number_field
-            sage: C = Curve([z - u*x^2, y^2], A); C                                     # needs sage.rings.number_field
+            sage: K. = NumberField(v^2 + 3)
+            sage: A. = AffineSpace(K, 3)
+            sage: C = Curve([z - u*x^2, y^2], A); C
             Affine Curve over Number Field in u with defining polynomial v^2 + 3
             defined by (-u)*x^2 + z, y^2
 
@@ -456,23 +458,25 @@ def plot(self, *args, **kwds):
 
             sage: R. = QQ[]
             sage: C = Curve(x^3 - y^2)
-            sage: C.plot()                                                              # needs sage.plot
+            sage: C.plot()                                      # needs sage.plot
             Graphics object consisting of 1 graphics primitive
 
         A 5-nodal curve of degree 11.  This example also illustrates
         some of the optional arguments::
 
+            sage: # needs sage.plot()
             sage: R. = ZZ[]
             sage: C = Curve(32*x^2 - 2097152*y^11 + 1441792*y^9
             ....:            - 360448*y^7 + 39424*y^5 - 1760*y^3 + 22*y - 1)
-            sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400)                       # needs sage.plot
+            sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400)
             Graphics object consisting of 1 graphics primitive
 
         A line over `\mathbf{RR}`::
 
+            sage: # needs sage.symbolic sage.plot
             sage: R. = RR[]
-            sage: C = Curve(R(y - sqrt(2)*x))                                           # needs sage.symbolic
-            sage: C.plot()                                                              # needs sage.plot
+            sage: C = Curve(R(y - sqrt(2)*x))
+            sage: C.plot()
             Graphics object consisting of 1 graphics primitive
         """
         Id = self.defining_ideal()
@@ -843,10 +847,11 @@ def __init__(self, A, X):
 
         EXAMPLES::
 
+            sage: # needs sage.rings.number_field
             sage: R. = QQ[]
-            sage: K. = NumberField(v^2 + 3)                                          # needs sage.rings.number_field
-            sage: A. = AffineSpace(K, 3)                                         # needs sage.rings.number_field
-            sage: C = Curve([z - u*x^2, y^2], A); C                                     # needs sage.rings.number_field
+            sage: K. = NumberField(v^2 + 3)
+            sage: A. = AffineSpace(K, 3)
+            sage: C = Curve([z - u*x^2, y^2], A); C
             Affine Curve over Number Field in u with defining polynomial v^2 + 3
             defined by (-u)*x^2 + z, y^2
 
@@ -1293,9 +1298,10 @@ def blowup(self, P=None):
 
         ::
 
-            sage: A. = AffineSpace(QuadraticField(-1), 2)                          # needs sage.rings.number_field
-            sage: C = A.curve([y^2 + x^2])                                              # needs sage.rings.number_field
-            sage: C.blowup()                                                            # needs sage.rings.number_field
+            sage: # needs sage.rings.number_field
+            sage: A. = AffineSpace(QuadraticField(-1), 2)
+            sage: C = A.curve([y^2 + x^2])
+            sage: C.blowup()
             Traceback (most recent call last):
             ...
             TypeError: this curve must be irreducible
@@ -1725,7 +1731,6 @@ def tangent_line(self, p):
              defined by: -2*y + z + 1, x + y + z
             sage: _ == C.tangent_line(p)
             True
-
         """
         A = self.ambient_space()
         R = A.coordinate_ring()
@@ -1752,8 +1757,48 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field):
     """
     _point = AffinePlaneCurvePoint_field
 
+    def has_vertical_asymptote(self):
+        """
+        Check if the curve is not a line and has vertical asymptotes.
+
+        EXAMPLES::
+
+            sage: A2. = AffineSpace(2, QQ)
+            sage: Curve(x).has_vertical_asymptote()
+            False
+            sage: Curve(y^2 * x + x + y).has_vertical_asymptote()
+            True
+        """
+        A = self.ambient_space()
+        R = A.coordinate_ring()
+        x, y = R.gens()
+        f = self.defining_polynomial().radical()
+        dy = f.degree(y)
+        dxy = f.coefficient({y: dy}).degree()
+        return dxy > 0 and f.degree() > 1
+
+    def is_vertical_line(self):
+        """
+        Check if the curve is a vertical line.
+
+        EXAMPLES::
+
+            sage: A2. = AffineSpace(2, QQ)
+            sage: Curve(x - 1).is_vertical_line()
+            True
+            sage: Curve(x - y).is_vertical_line()
+            False
+            sage: Curve(y^2 * x + x + y).is_vertical_line()
+            False
+        """
+        A = self.ambient_space()
+        R = A.coordinate_ring()
+        x, y = R.gens()
+        f = self.defining_polynomial().radical()
+        return f.degree(y) == 0 and f.degree() == 1
+
     @cached_method
-    def fundamental_group(self, simplified=True, puiseux=False):
+    def fundamental_group(self, simplified=True, puiseux=True):
         r"""
         Return a presentation of the fundamental group of the complement
         of ``self``.
@@ -1762,9 +1807,9 @@ def fundamental_group(self, simplified=True, puiseux=False):
 
         - ``simplified`` -- (default: ``True``) boolean to simplify the presentation.
 
-        - ``puiseux`` -- (default: ``False``) boolean to decide if the
+        - ``puiseux`` -- (default: ``True``) boolean to decide if the
           presentation is constructed in the classical way or using Puiseux
-          shortcut. If ``True``, ``simplified`` is set to ``False``.
+          shortcut.
 
         OUTPUT:
 
@@ -1781,17 +1826,18 @@ def fundamental_group(self, simplified=True, puiseux=False):
         .. NOTE::
 
             The curve must be defined over the rationals or a number field
-            with an embedding over `\QQbar`.
+            with an embedding over `\QQbar`. This functionality requires
+            the ``sirocco`` package to be installed.
 
         EXAMPLES::
 
-            sage: # optional - sirocco
+            sage: # needs sirocco
             sage: A. = AffineSpace(QQ, 2)
             sage: C = A.curve(y^2 - x^3 - x^2)
-            sage: C.fundamental_group()
+            sage: C.fundamental_group(puiseux=False)
             Finitely presented group < x0 |  >
             sage: bm = C.braid_monodromy()
-            sage: g = C.fundamental_group(puiseux=True)
+            sage: g = C.fundamental_group(simplified=False)
             sage: g.sorted_presentation()
             Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0, x1^-1*x0 >
             sage: g.simplified()
@@ -1808,15 +1854,28 @@ def fundamental_group(self, simplified=True, puiseux=False):
             Defining a
             sage: A. = AffineSpace(F, 2)
             sage: C = A.curve(y^2 - a*x^3 - x^2)
-            sage: C.fundamental_group()         # optional - sirocco
+            sage: C.fundamental_group()                     # needs sirocco
             Finitely presented group < x0 |  >
-
-        .. WARNING::
-
-            This functionality requires the sirocco package to be installed.
+            sage: C = A.curve(x * (x - 1))
+            sage: C.fundamental_group()                     # needs sirocco
+            Finitely presented group < x0, x1 |  >
         """
         from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon
-        return fundamental_group_from_braid_mon(self.braid_monodromy(), simplified=simplified, puiseux=puiseux)
+        bm = self.braid_monodromy()
+        if not bm:
+            f = self.defining_polynomial()
+            x, y = f.parent().gens()
+            d0 = f.degree(y)
+            f0 = f.coefficient({y: d0})
+            d = d0 + f0.degree(x)
+        else:
+            d = bm[0].parent().strands()
+        G = fundamental_group_from_braid_mon(bm, degree=d,
+                                             simplified=simplified,
+                                             puiseux=puiseux)
+        if simplified:
+            G = G.simplified()
+        return G
 
     @cached_method
     def braid_monodromy(self):
@@ -1829,20 +1888,31 @@ def braid_monodromy(self):
         each of this paths is the conjugated of a loop around one of the points
         in the discriminant of the projection of ``self``.
 
-        NOTE:
+        .. NOTE::
+
+            The projection over the `x` axis is used if there are no vertical asymptotes.
+            Otherwise, a linear change of variables is done to fall into the previous case.
 
-        The projection over the `x` axis is used if there are no vertical asymptotes.
-        Otherwise, a linear change of variables is done to fall into the previous case.
+        .. NOTE::
+
+            This functionality requires the ``sirocco`` package to be installed.
 
         EXAMPLES::
 
             sage: A. = AffineSpace(QQ, 2)
             sage: C = A.curve((x^2-y^3)*(x+3*y-5))
-            sage: C.braid_monodromy()               # optional - sirocco
+            sage: C.braid_monodromy()                                   # needs sirocco
             [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
              s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
              s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
              s1*s0*s2*s0^-1*s2*s1^-1]
+            sage: T. = QQ[]
+            sage: K. = NumberField(t^3 + 2, 'a')
+            sage: A. = AffineSpace(K, 2)
+            sage: Curve(y^2 + a * x).braid_monodromy()
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: the base field must have an embedding to the algebraic field
 
         """
         from sage.schemes.curves.zariski_vankampen import braid_monodromy
@@ -2047,9 +2117,10 @@ def function_field(self):
 
         ::
 
-            sage: A. = AffineSpace(GF(8), 2)                                       # needs sage.rings.finite_rings
-            sage: C = Curve(x^5 + y^5 + x*y + 1)                                        # needs sage.rings.finite_rings
-            sage: C.function_field()                                                    # needs sage.rings.finite_rings
+            sage: # needs sage.rings.finite_rings
+            sage: A. = AffineSpace(GF(8), 2)
+            sage: C = Curve(x^5 + y^5 + x*y + 1)
+            sage: C.function_field()
             Function field in y defined by y^5 + x*y + x^5 + 1
         """
         return self._function_field
diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py
index 147c2e1e6fe..b34592457c6 100644
--- a/src/sage/schemes/curves/all.py
+++ b/src/sage/schemes/curves/all.py
@@ -23,3 +23,11 @@
 from .constructor import Curve
 
 from .projective_curve import Hasse_bounds
+
+from sage.misc.lazy_import import lazy_import
+
+lazy_import('sage.schemes.curves.plane_curve_arrangement', 'PlaneCurveArrangements')
+
+lazy_import('sage.schemes.curves.plane_curve_arrangement', 'AffinePlaneCurveArrangements')
+
+lazy_import('sage.schemes.curves.plane_curve_arrangement', 'ProjectivePlaneCurveArrangements')
diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py
new file mode 100644
index 00000000000..c16c9cf6644
--- /dev/null
+++ b/src/sage/schemes/curves/plane_curve_arrangement.py
@@ -0,0 +1,1304 @@
+r"""
+Affine and Projective Plane Curve Arrangements
+
+We create classes :class:`AffinePlaneCurveArrangements`
+and :class:`ProjectivePlaneCurveArrangements`
+following the properties of :class:`HyperplaneArrangements`::
+
+    sage: H. = AffinePlaneCurveArrangements(QQ)
+    sage: C = H(3*x + 2*y - x^2 + y^3 - 7);  C
+    Arrangement (y^3 - x^2 + 3*x + 2*y - 7) in Affine Space of dimension 2 over Rational Field
+
+The individual curves will be in  :class:`AffinePlaneCurve` or in :class:`ProjectivePlaneCurve`::
+
+    sage: C[0].parent()
+    
+
+The default base field is `\QQ`, the rational numbers.
+Number fields are also possible (also with fixed embeddings in
+`\QQbar`)::
+
+    sage: # needs sage.rings.number_field
+    sage: x = polygen(QQ, 'x')
+    sage: NF. = NumberField(x^4 - 5 * x^2 + 5, embedding=1.90)
+    sage: H. = AffinePlaneCurveArrangements(NF)
+    sage: A = H(y^2 - a * z, y^2 + a * z); A
+    Arrangement (y^2 + (-a)*z, y^2 + a*z) in Affine Space of dimension 2
+    over Number Field in a with defining polynomial
+    x^4 - 5*x^2 + 5 with a = 1.902113032590308?
+    sage: A.base_ring()
+    Number Field in a with defining polynomial x^4 - 5*x^2 + 5
+    with a = 1.902113032590308?
+
+
+AUTHORS:
+
+- Enrique Artal (2023-10): initial version
+"""
+
+# *****************************************************************************
+#       Copyright (C) 2023 Enrique Artal 
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#                  http://www.gnu.org/licenses/
+# *****************************************************************************
+
+from itertools import combinations
+from sage.categories.sets_cat import Sets
+from sage.groups.free_group import FreeGroup
+from sage.misc.abstract_method import abstract_method
+from sage.misc.cachefunc import cached_method
+from sage.misc.misc_c import prod
+from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
+from sage.rings.qqbar import QQbar
+from sage.rings.ring import _Fields
+from sage.schemes.affine.affine_space import AffineSpace
+from sage.schemes.curves.affine_curve import AffinePlaneCurve
+from sage.schemes.curves.constructor import Curve
+from sage.schemes.curves.projective_curve import ProjectiveSpace, ProjectivePlaneCurve
+from sage.schemes.curves.zariski_vankampen import braid_monodromy, fundamental_group_arrangement
+from sage.structure.category_object import normalize_names
+from sage.structure.parent import Parent
+from sage.structure.element import Element
+from sage.structure.richcmp import richcmp
+from sage.structure.unique_representation import UniqueRepresentation
+
+
+class PlaneCurveArrangementElement(Element):
+    """
+    An ordered plane curve arrangement.
+    """
+    def __init__(self, parent, curves, check=True):
+        """
+        Construct a plane curve arrangement.
+
+        INPUT:
+
+        - ``parent`` -- the parent :class:`PlaneCurveArrangements`
+
+        - ``curves`` -- a tuple of curves
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: elt = H(x, y); elt
+            Arrangement (x, y) in Affine Space of dimension 2 over Rational Field
+            sage: TestSuite(elt).run()
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: elt = H(x, y); elt
+            Arrangement (x, y) in Projective Space of dimension 2 over Rational Field
+            sage: TestSuite(elt).run()
+        """
+        super().__init__(parent)
+        self._curves = tuple(curves)
+        if check:
+            affine = all(isinstance(h, AffinePlaneCurve) for h in curves)
+            projective = all(isinstance(h, ProjectivePlaneCurve) for h in curves)
+            if not (affine or projective):
+                raise ValueError("not all elements are curves")
+            if not all(h.ambient_space() is parent.ambient_space()
+                       for h in curves):
+                raise ValueError("not all curves are in the same ambient space")
+
+    def __getitem__(self, i):
+        """
+        Return the `i`-th curve.
+
+        INPUT:
+
+        - ``i`` -- integer
+
+        OUTPUT:
+
+        The `i`-th curve.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1)
+            Arrangement (y^2 - x, y^3 + 2*x^2, x^4 + y^4 + 1)
+            in Affine Space of dimension 2 over Rational Field
+        """
+        return self._curves[i]
+
+    def __hash__(self):
+        r"""
+        TESTS::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: H((x * y, x + y +1)).__hash__()   # random
+            -4938643871296220686
+        """
+        return hash(self.curves())
+
+    def ncurves(self):
+        r"""
+        Return the number of curves in the arrangement.
+
+        OUTPUT:
+
+        An integer.
+
+        EXAMPLES::
+
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: h = H((x * y, x + y + z))
+            sage: h.ncurves()
+            2
+            sage: len(h)    # equivalent
+            2
+        """
+        return len(self._curves)
+
+    __len__ = ncurves
+
+    def curves(self):
+        r"""
+        Return the curves in the arrangement as a tuple.
+
+        OUTPUT:
+
+        A tuple.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: h = H((x * y, x + y + 1))
+            sage: h.curves()
+            (Affine Plane Curve over Rational Field defined by x*y,
+             Affine Plane Curve over Rational Field defined by x + y + 1)
+
+        Note that the curves can be indexed as if they were a list::
+
+            sage: h[1]
+            Affine Plane Curve over Rational Field defined by x + y + 1
+        """
+        return self._curves
+
+    def _repr_(self):
+        r"""
+        String representation for a curve arrangement.
+
+        OUTPUT:
+
+        A string.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2])
+            sage: h
+            Arrangement of 5 curves in Affine Space of dimension 2 over Rational Field
+            sage: H(())
+            Arrangement () in Affine Space of dimension 2 over Rational Field
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2])
+            sage: h
+            Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field
+        """
+        if not self:
+            return 'Empty curve arrangement in {}'.format(self.parent().ambient_space())
+        elif len(self) < 5:
+            curves = ', '.join(h.defining_polynomial()._repr_()
+                               for h in self._curves)
+            return 'Arrangement ({}) in {}'.format(curves,
+                                                     self.parent().ambient_space())
+        return 'Arrangement of {} curves in {}'.format(len(self),
+                                                         self.parent().ambient_space())
+
+    def _richcmp_(self, other, op):
+        """
+        Compare two curve arrangements.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: H(x) == H(y)
+            False
+            sage: H(x) == H(2 * x)
+            True
+
+        TESTS::
+
+            sage: H(x) == 0
+            False
+        """
+        return richcmp(self._curves, other._curves, op)
+
+    def union(self, other):
+        r"""
+        The union of ``self`` with ``other``.
+
+        INPUT:
+
+        - ``other`` -- a curve arrangement or something that can
+          be converted into a curve arrangement
+
+        OUTPUT:
+
+        A new curve arrangement.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2])
+            sage: C = Curve(x^8 - y^8 -x^4 * y^4)
+            sage: h1 = h.union(C); h1
+            Arrangement of 6 curves in Affine Space of dimension 2 over Rational Field
+            sage: h1 == h1.union(C)
+            True
+        """
+        P = self.parent()
+        other_h = P(other)
+        curves0 = self._curves + other_h._curves
+        curves = []
+        for h in curves0:
+            if h not in curves:
+                curves.append(h)
+        result = P(*curves)
+        return result
+
+    add_curves = union
+
+    __or__ = union
+
+    def deletion(self, curves):
+        r"""
+        Return the curve arrangement obtained by removing ``curves``.
+
+        INPUT:
+
+        - ``curves`` -- a curve or curve arrangement
+
+        OUTPUT:
+
+        A new curve arrangement with the given curve(s)
+        ``h`` removed.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2])
+            sage: C = h[-1]
+            sage: h.deletion(C)
+            Arrangement (x*y, x + y + 1, -y^5 + x^3, x^5 + y^5 + x^2*y^2)
+            in Affine Space of dimension 2 over Rational Field
+            sage: h.deletion(x)
+            Traceback (most recent call last):
+            ...
+            ValueError: curve is not in the arrangement
+            """
+        parent = self.parent()
+        curves = parent(curves)
+        planes = list(self)
+        for curve in curves:
+            try:
+                planes.remove(curve)
+            except ValueError:
+                raise ValueError('curve is not in the arrangement')
+        return parent(planes)
+
+    def change_ring(self, base_ring):
+        """
+        Return curve arrangement over the new base ring.
+
+        INPUT:
+
+        - ``base_ring`` -- the new base ring; must be a field for
+          curve arrangements
+
+        OUTPUT:
+
+        The curve arrangement obtained by changing the base
+        field, as a new curve arrangement.
+
+        EXAMPLES::
+
+            sage: # needs sage.rings.number_field
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2)
+            sage: K. = CyclotomicField(3)
+            sage: A.change_ring(K)
+            Arrangement (-x^3 + y^2, x, y, x^2 + x*y + y^2) in Affine Space of
+            dimension 2 over Cyclotomic Field of order 3 and degree 2
+        """
+        parent = self.parent().change_ring(base_ring)
+        curves = tuple(c.change_ring(base_ring) for c in self)
+        return parent(curves)
+
+    @cached_method
+    def coordinate_ring(self):
+        """
+        Return the coordinate ring of ``self``.
+
+        OUTPUT:
+
+        The coordinate ring of the curve arrangement.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: C = L(x, y)
+            sage: C.coordinate_ring()
+            Multivariate Polynomial Ring in x, y over Rational Field
+            sage: P. = ProjectivePlaneCurveArrangements(QQ)
+            sage: C = P(x, y)
+            sage: C.coordinate_ring()
+            Multivariate Polynomial Ring in x, y, z over Rational Field
+        """
+        return self._curves[0].defining_polynomial().parent()
+
+    def defining_polynomials(self):
+        r"""
+        Return the defining polynomials of the elements of ``self``.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2)
+            sage: A.defining_polynomials()
+            (-x^3 + y^2, x, y, x^2 + x*y + y^2)
+        """
+        return tuple([h.defining_polynomial() for h in self._curves])
+
+    def defining_polynomial(self, simplified=True):
+        r"""
+        Return the defining polynomial of the union of the curves in ``self``.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y ** 2 + x ** 2, x, y)
+            sage: prod(A.defining_polynomials()) == A.defining_polynomial()
+            True
+        """
+        return prod(self.defining_polynomials())
+
+    def have_common_factors(self):
+        r"""
+        Check if the curves have common factors.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(x * y, x^2 + x* y^3)
+            sage: A.have_common_factors()
+            True
+            sage: H(x * y, x + y^3).have_common_factors()
+            False
+        """
+        L = [c.defining_polynomial() for c in self._curves]
+        C = combinations(L, 2)
+        return any(f1.gcd(f2).degree() > 0 for f1, f2 in C)
+
+    def reduce(self, clean=False, verbose=False):
+        r"""
+        Replace the curves by their reduction.
+
+        INPUT:
+
+        - ``clean`` -- boolean (default: ``False``); if ``False``
+          and there are common factors it returns ``None`` and
+          a warning message. If ``True``, the common factors are kept
+          only in the first occurance.
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2))
+            sage: A.reduce()
+            Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space
+            of dimension 2 over Rational Field
+            sage: C = H(x*y, x*(y + 1))
+            sage: C.reduce(verbose=True)
+            Some curves have common components
+            sage: C.reduce(clean=True)
+            Arrangement (x*y, y + 1) in Affine Space of dimension 2
+            over Rational Field
+            sage: C = H(x*y, x)
+            sage: C.reduce(clean=True)
+            Arrangement (x*y) in Affine Space of dimension 2 over Rational Field
+        """
+        P = self.parent()
+        L = [self._curves[0].defining_polynomial().radical()]
+        for c in self._curves[1:]:
+            g = c.defining_polynomial().radical()
+            for f in L:
+                d = g.gcd(f)
+                if d.degree() > 0 and not clean:
+                    if verbose:
+                        print("Some curves have common components")
+                    return None
+                g //= d
+            if g.degree() > 0:
+                L.append(g)
+        return P(*L)
+
+
+class AffinePlaneCurveArrangementElement(PlaneCurveArrangementElement):
+    """
+    An ordered affine plane curve arrangement.
+    """
+    def __init__(self, parent, curves, check=True):
+        """
+        Construct an ordered affine plane curve arrangement.
+
+        INPUT:
+
+        - ``parent`` -- the parent :class:`AffinePlaneCurveArrangements`
+
+        - ``curves`` -- a tuple of curves
+
+        EXAMPLES::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: elt = H(x, y); elt
+            Arrangement (x, y) in Affine Space of dimension 2 over Rational Field
+            sage: TestSuite(elt).run()
+        """
+        Element.__init__(self, parent)
+        self._curves = tuple(curves)
+        if check:
+            if not all(isinstance(h, AffinePlaneCurve) for h in curves):
+                raise ValueError("not all elements are curves")
+            if not all(h.ambient_space() is self.parent().ambient_space()
+                       for h in curves):
+                raise ValueError("not all curves are in the same ambient space")
+        self._braid_monodromy_non_vertical = None
+        self._braid_monodromy_vertical = None
+        self._strands_nonvertical = None
+        self._strands_vertical = None
+        self._fundamental_group_nonsimpl_nonvertical = None
+        self._fundamental_group_nonsimpl_vertical = None
+        self._fundamental_group_simpl_nonvertical = None
+        self._fundamental_group_simpl_vertical = None
+        self._meridians_nonsimpl_nonvertical = None
+        self._meridians_nonsimpl_vertical = None
+        self._meridians_simpl_nonvertical = None
+        self._meridians_simpl_vertical = None
+        self._vertical_lines_in_braid_mon = None
+
+    def fundamental_group(self, simplified=True, vertical=True,
+                          projective=False):
+        r"""
+        Return the fundamental group of the complement of the union
+        of affine plane curves in `\CC^2`.
+
+        INPUT:
+
+        - ``vertical`` -- boolean (default: ``True``); if ``True``, there
+          are no vertical asymptotes, and there are vertical lines, then a
+          simplified braid :func:`braid_monodromy` is used
+
+        - ``simplified`` -- boolean (default: ``True``); if it is ``True``, the
+          group is simplified
+
+        - ``projective`` -- boolean (default: ``False``); to be used in the
+          method for projective curves
+
+        OUTPUT:
+
+        A finitely presented group.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x, y + x - 1, x)
+            sage: A.fundamental_group()
+            Finitely presented group
+            < x0, x1, x2 | x2*x0*x2^-1*x0^-1, x1*x0*x1^-1*x0^-1, (x2*x1)^2*(x2^-1*x1^-1)^2 >
+            sage: A.meridians()
+            {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2],
+             3: [x1^-1*x2^-1*x1^-1*x0^-1]}
+            sage: G = A.fundamental_group(simplified=False)
+            sage: G.sorted_presentation()
+            Finitely presented group
+            < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x0*x1*x0^-1,
+                               x3^-1*x1^-1*x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1,
+                               x3^-1*x0^-1*x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1,
+                               x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 >
+            sage: A.meridians(simplified=False)
+            {0: [x1, x2], 1: [x0], 2: [x3], 3: [x3^-1*x2^-1*x1^-1*x0^-1]}
+            sage: A.fundamental_group(vertical=False)
+            Finitely presented group
+            < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 >
+            sage: A.meridians(vertical=False)
+            {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]}
+            sage: G = A.fundamental_group(simplified=False, vertical=False)
+            sage: G.sorted_presentation()
+            Finitely presented group
+            < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
+                               x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
+                               (x3^-1*x2^-1*x0^-1*x2)^2*(x3*x2^-1*x0*x2)^2,
+                               x3^-1*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0*x2*x3*x2,
+                               x1^-1*x0^-1*x1*x0 >
+            sage: A.meridians(simplified=False, vertical=False)
+            {0: [x2, x3], 1: [x1], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1]}
+            sage: A = H(x * y^2 + x + y, y + x -1, x, y)
+            sage: G = A.fundamental_group()
+            sage: G.sorted_presentation()
+            Finitely presented group
+            < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1,
+                               x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1,
+                               x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 >
+        """
+        if simplified and vertical:
+            computed = self._fundamental_group_simpl_vertical
+        elif simplified and not vertical:
+            computed = self._fundamental_group_simpl_nonvertical
+        elif not simplified and vertical:
+            computed = self._fundamental_group_nonsimpl_vertical
+        else:
+            computed = self._fundamental_group_nonsimpl_nonvertical
+        if computed:
+            return computed
+        K = self.base_ring()
+        R = self.coordinate_ring()
+        if not K.is_subring(QQbar):
+            raise TypeError('the base field is not in QQbar')
+        C = self.reduce()
+        L = C.defining_polynomials()
+        if vertical:
+            bm = self._braid_monodromy_vertical
+        else:
+            bm = self._braid_monodromy_non_vertical
+        if bm is not None:  # bm could be []
+            if not vertical:
+                st = self._strands_nonvertical
+                d1 = prod(L).degree()
+                bd = (bm, st, {}, d1)
+            else:
+                st = self._strands_vertical
+                d1 = prod(L).degree(R.gen(1))
+                bd = (bm, st, self._vertical_lines_in_braid_mon, d1)
+        else:
+            bd = None
+        G, dic = fundamental_group_arrangement(L, simplified=simplified,
+                                               puiseux=True,
+                                               projective=projective,
+                                               vertical=vertical,
+                                               braid_data=bd)
+        if simplified and vertical:
+            self._fundamental_group_simpl_vertical = G
+            self._meridians_simpl_vertical = dic
+        elif simplified and not vertical:
+            self._fundamental_group_simpl_nonvertical = G
+            self._meridians_simpl_nonvertical = dic
+        elif not simplified and vertical:
+            self._fundamental_group_nonsimpl_vertical = G
+            self._meridians_nonsimpl_vertical = dic
+        else:
+            self._fundamental_group_nonsimpl_nonvertical = G
+            self._meridians_nonsimpl_nonvertical = dic
+        return G
+
+    def meridians(self, simplified=True, vertical=True):
+        r"""
+        Return the meridians of each irreducible component.
+
+        OUTPUT:
+
+        A dictionary which associates the index of each curve with its meridians,
+        including the line at infinity if it can be omputed
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed
+           and :meth:`AffinePlaneCurveArrangements.fundamental_group` with the same options,
+           where some examples are shown.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(x-1, y, x, y - 1)
+            sage: A.fundamental_group()
+            Finitely presented group
+            < x0, x1, x2, x3 | x2*x0*x2^-1*x0^-1, x2*x1*x2^-1*x1^-1,
+                               x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1 >
+            sage: A.meridians()
+            {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}
+        """
+        if simplified and vertical:
+            computed = self._meridians_simpl_vertical
+        elif simplified and not vertical:
+            computed = self._meridians_simpl_nonvertical
+        elif not simplified and vertical:
+            computed = self._meridians_nonsimpl_vertical
+        else:
+            computed = self._meridians_nonsimpl_nonvertical
+        if computed:
+            return dict(computed)
+        self.fundamental_group(simplified=simplified, vertical=vertical)
+        if simplified and vertical:
+            return dict(self._meridians_simpl_vertical)
+        elif simplified and not vertical:
+            return dict(self._meridians_group_simpl_nonvertical)
+        elif not simplified and vertical:
+            return dict(self._meridians_nonsimpl_vertical)
+        else:
+            return dict(self._meridians_nonsimpl_nonvertical)
+
+    def braid_monodromy(self, vertical=True):
+        r"""
+        Return the braid monodromy of the complement of the union
+        of affine plane curves in `\CC^2`. If there are vertical
+        asymptotes a change of variable is done.
+
+        INPUT:
+
+        - ``vertical`` -- boolean (default: ``True``); if it is ``True``, there
+          are no vertical asymptotes, and there are vertical lines, then a
+          simplified :func:`braid_monodromy` is computed.
+
+        OUTPUT:
+
+        A braid monodromy with dictionnaries identifying strands with components
+        and braids with vertical lines.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x, y + x - 1, x)
+            sage: A.braid_monodromy(vertical=False)
+            [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1,
+             s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
+             s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
+             s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
+             s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1]
+            sage: A.braid_monodromy(vertical=True)
+            [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0]
+        """
+        if vertical:
+            computed = self._braid_monodromy_vertical
+        else:
+            computed = self._braid_monodromy_non_vertical
+        if computed is not None:
+            return computed
+        K = self.base_ring()
+        if not K.is_subring(QQbar):
+            raise TypeError('the base field is not in QQbar')
+        L = self.defining_polynomials()
+        bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L,
+                                          vertical=vertical)
+        if vertical:
+            self._braid_monodromy_vertical = bm
+            self._strands_vertical = dic
+            self._vertical_lines_in_braid_mon = dv
+        else:
+            self._braid_monodromy_non_vertical = bm
+            self._strands_nonvertical = dic
+        return bm
+
+    def strands(self):
+        r"""
+        Return the strands for each member of the arrangement.
+
+        OUTPUT:
+
+        A dictionary which associates to the index of each strand
+        its associated component if the braid monodromy has been
+        calculated with ``vertical=False``.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x, y + x - 1, x)
+            sage: bm = A.braid_monodromy()
+            sage: A.strands()
+            {0: 2, 1: 1, 2: 0, 3: 0}
+        """
+        if not self._strands_nonvertical:
+            self._braid_monodromy = self.braid_monodromy(vertical=False)
+        return self._strands_nonvertical
+
+    def vertical_strands(self):
+        r"""
+        Return the strands if the braid monodromy has been computed with
+        the vertical option.
+
+        OUTPUT:
+
+        A dictionary which associates to the index of each strand
+        its associated component if the braid monodromy has been
+        calculated with ``vertical=True``.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x, y + x - 1, x)
+            sage: A.vertical_strands()
+            {0: 1, 1: 0, 2: 0}
+            sage: A.braid_monodromy(vertical=True)
+            [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0]
+        """
+        if not self._strands_vertical:
+            self.braid_monodromy(vertical=True)
+        return self._strands_vertical
+
+    def vertical_lines_in_braid_monodromy(self):
+        r"""
+        Return the vertical lines in the arrangement.
+
+        OUTPUT:
+
+        A dictionary which associates the index of a braid
+        to the index of the vertical line associated to the braid.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x, y + x - 1, x)
+            sage: A.vertical_lines_in_braid_monodromy()
+            {1: 2}
+            sage: A.braid_monodromy(vertical=True)
+            [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0]
+        """
+        if not self._vertical_lines_in_braid_mon:
+            self.braid_monodromy(vertical=True)
+        return self._vertical_lines_in_braid_mon
+
+
+class ProjectivePlaneCurveArrangementElement(PlaneCurveArrangementElement):
+    """
+    An ordered projective plane curve arrangement.
+    """
+    def __init__(self, parent, curves, check=True):
+        """
+        Construct an ordered projective plane curve arrangement.
+
+        INPUT:
+
+        - ``parent`` -- the parent :class:`ProjectivePlaneCurveArrangements`
+
+        - ``curves`` -- a tuple of curves
+
+        EXAMPLES::
+
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: elt = H(x, y, z); elt
+            Arrangement (x, y, z) in Projective Space of dimension 2 over Rational Field
+            sage: TestSuite(elt).run()
+        """
+        Element.__init__(self, parent)
+        self._curves = tuple(curves)
+        if check:
+            if not all(isinstance(h, ProjectivePlaneCurve) for h in curves):
+                raise ValueError("not all elements are curves")
+            if not all(h.ambient_space() is self.parent().ambient_space()
+                       for h in curves):
+                raise ValueError("not all curves are in the same ambient space")
+        self._fundamental_group_nonsimpl = None
+        self._fundamental_group_simpl = None
+        self._meridians_nonsimpl = None
+        self._meridians_simpl = None
+
+    def fundamental_group(self, simplified=True):
+        r"""
+        Return the fundamental group of the complement of the union
+        of an arragnement of projective plane curves
+        in the projective plane.
+
+        INPUT:
+
+        - ``simplified`` -- boolean (default: True); set if the group
+          is simplified
+
+        OUTPUT:
+
+        A finitely presented group.
+
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: H(z).fundamental_group()
+            Finitely presented group <  |  >
+            sage: H(x*y).fundamental_group()
+            Finitely presented group < x |  >
+            sage: A = H(y^2 + x*z, y + x - z, x)
+            sage: A.fundamental_group().sorted_presentation()
+            Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >
+            sage: A.meridians()
+            {0: [x1], 1: [x0], 2: [x1^-1*x0^-1*x1^-1]}
+            sage: G = A.fundamental_group(simplified=False)
+            sage: G.sorted_presentation()
+            Finitely presented group
+            < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1, x3^-1*x2^-1*x3*x0*x1*x0^-1,
+                               x3^-1*x1^-1*x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1,
+                               x3^-1*x0^-1*x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1,
+                               x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 >
+            sage: A.meridians(simplified=False)
+            {0: [x1, x2], 1: [x0], 2: [x3]}
+            sage: A = H(y^2 + x*z, z, x)
+            sage: A.fundamental_group()
+            Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 >
+            sage: A = H(y^2 + x*z, z*x, y)
+            sage: A.fundamental_group()
+            Finitely presented group
+            < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1,
+                           x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 >
+        """
+        if simplified:
+            computed = self._fundamental_group_simpl
+        else:
+            computed = self._fundamental_group_nonsimpl
+        if computed:
+            return computed
+        H = self.parent()
+        K = self.base_ring()
+        R = self.coordinate_ring()
+        x, y, z = R.gens()
+        if not K.is_subring(QQbar):
+            raise TypeError('the base field is not in QQbar')
+        C = self.reduce()
+        n = len(C)
+        infinity = Curve(z)
+        infinity_in_C = infinity in C
+        if infinity_in_C and n == 1:
+            G = FreeGroup(0) / []
+            if simplified:
+                self._fundamental_group_simpl = G
+                self._meridians_simpl = {0: [G.one()]}
+            else:
+                self._fundamental_group_nonsimpl = G
+                self._meridians_nonsimpl = {0: [G.one()]}
+            return G
+        if infinity_in_C:
+            j = C.curves().index(infinity)
+            C = H(C.curves()[:j] + C.curves()[j + 1:])
+        infinity_divides = False
+        for j, c in enumerate(C):
+            g = c.defining_polynomial()
+            infinity_divides = z.divides(g)
+            if infinity_divides:
+                h = R(g / z)
+                C = H(C.curves()[:j] + (h, ) + C.curves()[j + 1:])
+                break
+        affine = AffinePlaneCurveArrangements(K, names=('u', 'v'))
+        u, v = affine.gens()
+        affines = [f.defining_polynomial().subs({x: u, y: v, z: 1}) for f in C]
+        changes = any(g.degree(v) < g.degree() > 1 for g in affines)
+        while changes:
+            affines = [f.subs({u: u + v}) for f in affines]
+            changes = any(g.degree(v) < g.degree() > 1 for g in affines)
+        C_affine = affine(affines)
+        proj = not (infinity_divides or infinity_in_C)
+        G = C_affine.fundamental_group(simplified=simplified, vertical=True,
+                                       projective=proj)
+        dic = C_affine.meridians(simplified=simplified, vertical=True)
+        if infinity_in_C:
+            dic1 = {}
+            for k in range(j):
+                dic1[k] = dic[k]
+            dic1[j] = dic[n - 1]
+            for k in range(j + 1, n):
+                dic1[k] = dic[k - 1]
+        elif infinity_divides:
+            dic1 = {k: dic[k] for k in range(n)}
+            dic1[j] += dic[n]
+        else:
+            dic1 = dic
+        if simplified:
+            self._fundamental_group_simpl = G
+            self._meridians_simpl = dic1
+        else:
+            self._fundamental_group_nonsimpl = G
+            self._meridians_nonsimpl = dic1
+        return G
+
+    def meridians(self, simplified=True):
+        r"""
+        Return the meridians of each irreducible component.
+
+        OUTPUT:
+
+        A dictionary which associates the index of each curve with
+        its meridians, including the line at infinity if it can be
+        computed
+
+        .. NOTE::
+
+           This function requires the ``sirocco`` package to be installed and
+           :func:`ProjectivePlaneCurveArrangements.fundamental_group`
+           with the same options, where some examples are shown.
+
+        EXAMPLES::
+
+            sage: # needs sirocco
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: A = H(y^2 + x*z, y + x - z, x)
+            sage: A.fundamental_group().sorted_presentation()
+            Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >
+            sage: A.meridians()
+            {0: [x1], 1: [x0], 2: [x1^-1*x0^-1*x1^-1]}
+            sage: A = H(y^2 + x*z, z, x)
+            sage: A.fundamental_group()
+            Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 >
+            sage: A.meridians()
+            {0: [x0, x1*x0*x1^-1], 1: [x0^-1*x1^-1*x0^-1], 2: [x1]}
+            sage: A = H(y^2 + x*z, z*x, y)
+            sage: A.fundamental_group()
+            Finitely presented group < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1,
+                                                    x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 >
+            sage: A.meridians()
+            {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]}
+        """
+        if simplified:
+            computed = self._meridians_simpl
+        else:
+            computed = self._meridians_nonsimpl
+        if computed:
+            return dict(computed)
+        self._fundamental_group(simplified=simplified)
+        if simplified:
+            return dict(self._meridians_simpl)
+        else:
+            return dict(self._meridians_nonsimpl)
+
+
+class PlaneCurveArrangements(UniqueRepresentation, Parent):
+    """
+    Plane curve arrangements.
+
+    INPUT:
+
+    - ``base_ring`` -- ring; the base ring
+
+    - ``names`` -- tuple of strings; the variable names
+
+    EXAMPLES::
+
+        sage: H. = AffinePlaneCurveArrangements(QQ)
+        sage: H(x, y^2, x-1, y-1)
+        Arrangement (x, y^2, x - 1, y - 1) in Affine Space
+        of dimension 2 over Rational Field
+    """
+    Element = PlaneCurveArrangementElement
+
+    @staticmethod
+    def __classcall__(cls, base, names=()):
+        """
+        Normalize the inputs to ensure a unique representation.
+
+        TESTS::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: K = AffinePlaneCurveArrangements(QQ, names=('x', 'y'))
+            sage: H is K
+            True
+        """
+        names = normalize_names(len(names), names)
+        return super().__classcall__(cls, base, names)
+
+    def __init__(self, base_ring, names=()):
+        """
+        Initialize ``self``.
+
+        TESTS::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: TestSuite(H).run()
+        """
+        if base_ring not in _Fields:
+            raise ValueError('base ring must be a field')
+        super().__init__(base_ring, names=names, category=Sets())
+        self._embedded = None
+        if len(names) == 2:
+            self._embedded = 'affine'
+        elif len(names) == 3:
+            self._embedded = 'projective'
+
+    def coordinate_ring(self):
+        """
+        Return the coordinate ring.
+
+        OUTPUT:
+
+        The coordinate ring of the curve arrangement.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.coordinate_ring()
+            Multivariate Polynomial Ring in x, y over Rational Field
+        """
+        return PolynomialRing(self.base_ring(), self.variable_names())
+
+    def change_ring(self, base_ring):
+        """
+        Return curve arrangements over a different base ring.
+
+        INPUT:
+
+        - ``base_ring`` -- a ring; the new base ring.
+
+        OUTPUT:
+
+        A new :class:`PlaneCurveArrangements` instance over the new
+        base ring
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.change_ring(RR).base_ring()
+            Real Field with 53 bits of precision
+
+        TESTS::
+
+            sage: L.change_ring(QQ) is L
+            True
+        """
+        return self.__reduce__()[1][0](base_ring, names=self.variable_names())
+
+    @abstract_method
+    def ambient_space(self):
+        """
+        Return the ambient space.
+
+        EXAMPLES::
+
+            sage: L. = PlaneCurveArrangements(QQ)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: 
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.ambient_space()
+            Affine Space of dimension 2 over Rational Field
+            sage: L. = ProjectivePlaneCurveArrangements(QQ)
+            sage: L.ambient_space()
+            Projective Space of dimension 2 over Rational Field
+        """
+
+    def _repr_(self):
+        """
+        Return a string representation.
+
+        OUTPUT:
+
+        A string.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ);  L
+            Curve arrangements in Affine Space of dimension 2 over Rational Field
+        """
+        return 'Curve arrangements in {}'.format(self.ambient_space())
+
+    def _element_constructor_(self, *args, **kwds):
+        """
+        Construct an element of ``self``.
+
+        INPUT:
+
+        - ``*args`` -- positional arguments, each defining a curve
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: A = L(x, y); A
+            Arrangement (x, y) in Affine Space of dimension 2 over Rational Field
+            sage: L([x, y]) == A
+            True
+            sage: L(Curve(x), Curve(y)) == A
+            True
+            sage: L(y, x) == A
+            False
+       """
+        if len(args) == 1:
+            if not isinstance(args[0], (tuple, list)):
+                arg = (args[0], )
+            else:
+                arg = tuple(args[0])
+        else:
+            arg = tuple(args)
+        ambient_space = self.ambient_space()
+        R = ambient_space.coordinate_ring()
+        curves = ()
+        for h in arg:
+            try:
+                ambient = h.ambient_space()
+                if ambient == ambient_space:
+                    curves += (h, )
+                else:
+                    raise TypeError('the curves do not have the same ambient space')
+            except AttributeError:
+                try:
+                    h = R(h)
+                    curves += (Curve(h), )
+                except TypeError:
+                    raise TypeError('elements are not curves')
+        return self.element_class(self, curves)
+
+    def _an_element_(self):
+        """
+        Return an element of ``self``.
+
+        TESTS::
+
+            sage: H. = AffinePlaneCurveArrangements(QQ)
+            sage: H._an_element_()
+            Arrangement (t) in Affine Space of dimension 2 over Rational Field
+            sage: H. = ProjectivePlaneCurveArrangements(QQ)
+            sage: H._an_element_()
+            Arrangement (t) in Projective Space of dimension 2 over Rational Field
+        """
+        return self(self.gen(0))
+
+    @cached_method
+    def ngens(self):
+        """
+        Return the number of variables, i.e. 2 or 3, kept for completness.
+
+        OUTPUT:
+
+        An integer, 2 or 3, depending if the arrangement is projective or affine.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.ngens()
+            2
+            sage: L. = ProjectivePlaneCurveArrangements(QQ)
+            sage: L.ngens()
+            3
+        """
+        return len(self.variable_names())
+
+    def gens(self):
+        """
+        Return the coordinates.
+
+        OUTPUT:
+
+        A tuple of linear expressions, one for each linear variable.
+
+        EXAMPLES::
+
+            sage: L = AffinePlaneCurveArrangements(QQ, ('x', 'y'))
+            sage: L.gens()
+            (x, y)
+            sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z'))
+            sage: L.gens()
+            (x, y, z)
+        """
+        return self.ambient_space().gens()
+
+    def gen(self, i):
+        """
+        Return the `i`-th coordinate.
+
+        INPUT:
+
+        - ``i`` -- integer
+
+        OUTPUT:
+
+        A variable.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.gen(1)
+            y
+            sage: L. = ProjectivePlaneCurveArrangements(QQ)
+            sage: L.gen(2)
+            z
+        """
+        return self.gens()[i]
+
+
+class AffinePlaneCurveArrangements(PlaneCurveArrangements):
+    """
+    Affine curve arrangements.
+
+    INPUT:
+
+    - ``base_ring`` -- ring; the base ring
+
+    - ``names`` -- tuple of strings; the variable names
+
+    EXAMPLES::
+
+        sage: H. = AffinePlaneCurveArrangements(QQ)
+        sage: H(x, y^2, x-1, y-1)
+        Arrangement (x, y^2, x - 1, y - 1) in Affine Space
+        of dimension 2 over Rational Field
+    """
+    Element = AffinePlaneCurveArrangementElement
+
+    def ambient_space(self):
+        """
+        Return the ambient space.
+
+        EXAMPLES::
+
+            sage: L. = AffinePlaneCurveArrangements(QQ)
+            sage: L.ambient_space()
+            Affine Space of dimension 2 over Rational Field
+        """
+        return AffineSpace(self.base_ring(), 2, self._names)
+
+
+class ProjectivePlaneCurveArrangements(PlaneCurveArrangements):
+    """
+    Projective curve arrangements.
+
+    INPUT:
+
+    - ``base_ring`` -- ring; the base ring
+
+    - ``names`` -- tuple of strings; the variable names
+
+    EXAMPLES::
+
+        sage: H. = ProjectivePlaneCurveArrangements(QQ)
+        sage: H(x, y^2, x-z, y-z)
+        Arrangement (x, y^2, x - z, y - z) in Projective Space
+        of dimension 2 over Rational Field
+    """
+    Element = ProjectivePlaneCurveArrangementElement
+
+    def ambient_space(self):
+        """
+        Return the ambient space.
+
+        EXAMPLES::
+
+            sage: L. = ProjectivePlaneCurveArrangements(QQ)
+            sage: L.ambient_space()
+            Projective Space of dimension 2 over Rational Field
+        """
+        return ProjectiveSpace(self.base_ring(), 2, self._names)
diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py
index e1f3403b834..1cc7fce0906 100644
--- a/src/sage/schemes/curves/projective_curve.py
+++ b/src/sage/schemes/curves/projective_curve.py
@@ -487,12 +487,12 @@ def projection(self, P=None, PS=None):
         # defining polynomials of this curve with the polynomials defining the inverse of the change of coordinates
         invcoords = [Q[i]*PP.gens()[j] + PP.gens()[i] for i in range(n + 1)]
         invcoords[j] = Q[j]*PP.gens()[j]
-        I = PP.coordinate_ring().ideal([f(invcoords) for f in self.defining_polynomials()])
-        J = I.elimination_ideal(PP.gens()[j])
+        id = PP.coordinate_ring().ideal([f(invcoords) for f in self.defining_polynomials()])
+        J = id.elimination_ideal(PP.gens()[j])
         K = Hom(PP.coordinate_ring(), PP2.coordinate_ring())
-        l = list(PP2.gens())
-        l.insert(j, 0)
-        phi = K(l)
+        ll = list(PP2.gens())
+        ll.insert(j, 0)
+        phi = K(ll)
         G = [phi(f) for f in J.gens()]
         C = PP2.curve(G)
         return (psi, C)
@@ -781,32 +781,35 @@ def plot(self, *args, **kwds):
 
         The other affine patches of the same curve::
 
-            sage: C.plot(patch=0)                                                       # needs sage.plot
+            sage: # needs sage.plot
+            sage: C.plot(patch=0)
             Graphics object consisting of 1 graphics primitive
-            sage: C.plot(patch=1)                                                       # needs sage.plot
+            sage: C.plot(patch=1)
             Graphics object consisting of 1 graphics primitive
 
         An elliptic curve::
 
+            sage: # needs sage.plot
             sage: E = EllipticCurve('101a')
             sage: C = Curve(E)
-            sage: C.plot()                                                              # needs sage.plot
+            sage: C.plot()
             Graphics object consisting of 1 graphics primitive
-            sage: C.plot(patch=0)                                                       # needs sage.plot
+            sage: C.plot(patch=0)
             Graphics object consisting of 1 graphics primitive
-            sage: C.plot(patch=1)                                                       # needs sage.plot
+            sage: C.plot(patch=1)
             Graphics object consisting of 1 graphics primitive
 
         A hyperelliptic curve::
 
+            sage: # needs sage.plot
             sage: P. = QQ[]
             sage: f = 4*x^5 - 30*x^3 + 45*x - 22
             sage: C = HyperellipticCurve(f)
-            sage: C.plot()                                                              # needs sage.plot
+            sage: C.plot()
             Graphics object consisting of 1 graphics primitive
-            sage: C.plot(patch=0)                                                       # needs sage.plot
+            sage: C.plot(patch=0)
             Graphics object consisting of 1 graphics primitive
-            sage: C.plot(patch=1)                                                       # needs sage.plot
+            sage: C.plot(patch=1)
             Graphics object consisting of 1 graphics primitive
         """
         # if user has not specified a favorite affine patch, take the
@@ -862,16 +865,17 @@ def is_singular(self, P=None):
 
         Over `\CC`::
 
+            sage: # needs sage.rings.function_field
             sage: F = CC
             sage: P2. = ProjectiveSpace(F, 2)
             sage: C = Curve(X)
-            sage: C.is_singular()                                                       # needs sage.rings.function_field
+            sage: C.is_singular()
             False
             sage: D = Curve(Y^2*Z - X^3)
-            sage: D.is_singular()                                                       # needs sage.rings.function_field
+            sage: D.is_singular()
             True
             sage: E = Curve(Y^2*Z - X^3 + Z^3)
-            sage: E.is_singular()                                                       # needs sage.rings.function_field
+            sage: E.is_singular()
             False
 
         Showing that :issue:`12187` is fixed::
@@ -883,10 +887,11 @@ def is_singular(self, P=None):
 
         ::
 
+            sage: # needs sage.fings.function_field
             sage: P. = ProjectiveSpace(CC, 2)
             sage: C = Curve([y^4 - x^3*z], P)
             sage: Q = P([0,0,1])
-            sage: C.is_singular()                                                       # needs sage.rings.function_field
+            sage: C.is_singular()
             True
         """
         if P is None:
@@ -1277,11 +1282,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.dict().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.dict().items()})
                     # check the degree again
                     if npoly.degree() != d - r:
                         need_continue = True
@@ -1660,9 +1665,9 @@ def is_complete_intersection(self):
             False
         """
         singular.lib("sing.lib")
-        I = singular.simplify(self.defining_ideal(), 10)
-        L = singular.is_ci(I).sage()
-        return len(self.ambient_space().gens()) - len(I.sage().gens()) == L[-1]
+        id = singular.simplify(self.defining_ideal(), 10)
+        L = singular.is_ci(id).sage()
+        return len(self.ambient_space().gens()) - len(id.sage().gens()) == L[-1]
 
     def tangent_line(self, p):
         """
@@ -1742,12 +1747,18 @@ def fundamental_group(self):
             The curve must be defined over the rationals or a number field
             with an embedding over `\QQbar`.
 
+        .. NOTE::
+
+           This functionality requires the ``sirocco`` package to be installed.
+
         EXAMPLES::
 
             sage: P. = ProjectiveSpace(QQ, 2)
             sage: C = P.curve(x^2*z - y^3)
-            sage: C.fundamental_group()                         # optional - sirocco
+            sage: C.fundamental_group()                                 # needs sirocco
             Finitely presented group < x0 | x0^3 >
+            sage: P.curve(z*(x^2*z - y^3)).fundamental_group()          # needs sirocco
+            Finitely presented group < x0, x1 | x1*x0*x1*x0^-1*x1^-1*x0^-1 >
 
         In the case of number fields, they need to have an embedding
         into the algebraic field::
@@ -1761,13 +1772,9 @@ def fundamental_group(self):
             sage: F.inject_variables()
             Defining a
             sage: C = P.curve(x^2 + a * y^2)
-            sage: C.fundamental_group()                         # optional - sirocco
+            sage: C.fundamental_group()                         # needs sirocco
             Finitely presented group < x0 |  >
 
-        .. WARNING::
-
-            This functionality requires the ``sirocco`` package to be installed.
-
         TESTS::
 
             sage: F. = FreeGroup()
@@ -1778,11 +1785,13 @@ def fundamental_group(self):
             sage: C = P.curve(z^2*y^3 - z*(33*x*z+2*x^2+8*z^2)*y^2
             ....:             + (21*z^2+21*x*z-x^2)*(z^2+11*x*z-x^2)*y
             ....:             + (x-18*z)*(z^2+11*x*z-x^2)^2)
-            sage: G0 = C.fundamental_group()                 # optional - sirocco
-            sage: G.is_isomorphic(G0)                        # optional - sirocco
+            sage: G0 = C.fundamental_group()                    # needs sirocco
+            sage: G.is_isomorphic(G0)                           # needs sirocco
             #I  Forcing finiteness test
             True
-
+            sage: C = P.curve(z)
+            sage: C.fundamental_group()                         # needs sirocco
+            Finitely presented group <  |  >
         """
         from sage.schemes.curves.zariski_vankampen import fundamental_group
         F = self.base_ring()
@@ -1790,7 +1799,11 @@ def fundamental_group(self):
         if QQbar.coerce_map_from(F) is None:
             raise NotImplementedError("the base field must have an embedding"
                                       " to the algebraic field")
-        f = self.affine_patch(2).defining_polynomial()
+        g = self.defining_polynomial()
+        ring = self.ambient_space().affine_patch(2).coordinate_ring()
+        if g.degree() == 1:
+            return fundamental_group(ring.one())
+        f = ring(self.affine_patch(2).defining_polynomial())
         if f.degree() == self.degree():
             return fundamental_group(f, projective=True)
         else:  # in this case, the line at infinity is part of the curve, so the complement lies in the affine patch
diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py
index 0fc01b81347..7dec97439db 100644
--- a/src/sage/schemes/curves/zariski_vankampen.py
+++ b/src/sage/schemes/curves/zariski_vankampen.py
@@ -23,12 +23,12 @@
 
 EXAMPLES::
 
-    sage: # optional - sirocco
+    sage: # needs sirocco
     sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy
     sage: R. = QQ[]
     sage: f = y^3 + x^3 - 1
     sage: braid_monodromy(f)
-    ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0})
+    ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}, {}, 3)
     sage: fundamental_group(f)
     Finitely presented group < x0 |  >
 """
@@ -44,7 +44,7 @@
 import itertools
 from copy import copy
 
-from sage.combinat.combination import Combinations
+from itertools import combinations
 from sage.combinat.permutation import Permutation
 from sage.functions.generalized import sign
 from sage.geometry.voronoi_diagram import VoronoiDiagram
@@ -67,7 +67,7 @@
 from sage.rings.qqbar import QQbar
 from sage.rings.rational_field import QQ
 from sage.rings.real_mpfr import RealField
-# from sage.sets.set import Set
+from .constructor import Curve
 
 roots_interval_cache = {}
 
@@ -88,7 +88,7 @@ def braid_from_piecewise(strands):
 
     EXAMPLES::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise
         sage: paths = [[(0, 0, 1), (0.2, -1, -0.5), (0.8, -1, 0), (1, 0, -1)],
         ....:          [(0, -1, 0), (0.5, 0, -1), (1, 1, 0)],
@@ -109,8 +109,8 @@ def braid_from_piecewise(strands):
                 yauxi = val[indices[j]][2]
                 aaux = val[indices[j] - 1][0]
                 baux = val[indices[j]][0]
-                interpolar = xauxr + (yauxr - xauxr) * (i - aaux) / (baux - aaux)
-                interpolai = xauxi + (yauxi - xauxi) * (i - aaux) / (baux - aaux)
+                interpolar = xauxr + (yauxr - xauxr)*(i - aaux) / (baux - aaux)
+                interpolai = xauxi + (yauxi - xauxi)*(i - aaux) / (baux - aaux)
                 totalpoints[j].append([interpolar, interpolai])
             else:
                 totalpoints[j].append([val[indices[j]][1],
@@ -129,6 +129,7 @@ def sgn(x, y):
         if x > y:
             return -1
         return 0
+
     for i in range(len(totalpoints[0]) - 1):
         l1 = [totalpoints[j][i] for j in range(len(L))]
         l2 = [totalpoints[j][i + 1] for j in range(len(L))]
@@ -177,7 +178,7 @@ def discrim(pols) -> tuple:
     INPUT:
 
     - ``pols`` -- a list or tuple of polynomials in two variables with
-      coefficients in a number field with a fixed embedding in `\QQbar`
+      coefficients in a number field with a fixed embedding in `\QQbar`.
 
     OUTPUT:
 
@@ -188,13 +189,13 @@ def discrim(pols) -> tuple:
         sage: from sage.schemes.curves.zariski_vankampen import discrim
         sage: R. = QQ[]
         sage: flist = (y^3 + x^3 - 1, 2 * x + y)
-        sage: discrim(flist)
-        (1,
-        -0.500000000000000? - 0.866025403784439?*I,
-        -0.500000000000000? + 0.866025403784439?*I,
-        -0.522757958574711?,
-        0.2613789792873551? - 0.4527216721561923?*I,
-        0.2613789792873551? + 0.4527216721561923?*I)
+        sage: sorted((discrim(flist)))
+        [-0.522757958574711?,
+         -0.500000000000000? - 0.866025403784439?*I,
+         -0.500000000000000? + 0.866025403784439?*I,
+         0.2613789792873551? - 0.4527216721561923?*I,
+         0.2613789792873551? + 0.4527216721561923?*I,
+         1]
     """
     x, y = pols[0].parent().gens()
     field = pols[0].base_ring()
@@ -206,7 +207,8 @@ def discrim_pairs(f, g):
             return pol_ring(f.discriminant(y))
         return pol_ring(f.resultant(g, y))
 
-    pairs = [(f, None) for f in pols] + [tuple(t) for t in Combinations(pols, 2)]
+    pairs = [(f, None) for f in pols] + [tuple(t) for t
+                                         in combinations(pols, 2)]
     fdiscrim = discrim_pairs(pairs)
     rts = ()
     poly = 1
@@ -226,7 +228,7 @@ def corrected_voronoi_diagram(points):
 
     INPUT:
 
-    - ``points`` -- a list of complex numbers
+    - ``points`` -- a tuple of complex numbers
 
     OUTPUT:
 
@@ -266,7 +268,8 @@ def corrected_voronoi_diagram(points):
         V = VoronoiDiagram(configuration)
         valid = True
         for r in V.regions().items():
-            if not r[1].rays() and not r[1].interior_contains(apprpoints[r[0].affine()]):
+            if (not r[1].rays() and
+               not r[1].interior_contains(apprpoints[r[0].affine()])):
                 prec += 53
                 valid = False
                 break
@@ -284,12 +287,12 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False):
     - ``circuit`` --  a circuit in the graph of a Voronoi Diagram, given
       by a list of edges
 
-    - ``convex`` -- boolean (default: `False`), if set to ``True`` a simpler
+    - ``convex`` -- boolean (default: ``False``); if set to ``True`` a simpler
       computation is made
 
     -  ``precision`` -- bits of precision (default: 53)
 
-    - ``verbose`` -- boolean (default: ``False``) for testing purposes
+    - ``verbose`` -- boolean (default: ``False``); for testing purposes
 
     OUTPUT:
 
@@ -353,7 +356,6 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False):
             # return circuit
             return circuit_vertex
         elif pr < 0:
-            # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit]))
             return tuple(reversed(circuit_vertex))
     prec = precision
     while True:
@@ -361,17 +363,15 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False):
         totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument()
                          for i in range(len(vectors)))
         if totalangle < 0:
-            # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit]))
             return tuple(reversed(circuit_vertex))
         if totalangle > 0:
-            # return circuit
             return circuit_vertex
         prec *= 2
         if verbose:
             print(prec)
 
 
-def voronoi_cells(V):
+def voronoi_cells(V, vertical_lines=frozenset()):
     r"""
     Compute the graph, the boundary graph, a base point, a positive orientation
     of the boundary graph, and the dual graph of a corrected Voronoi diagram.
@@ -380,6 +380,9 @@ def voronoi_cells(V):
 
     - ``V`` -- a corrected Voronoi diagram
 
+    - ``vertical_lines`` -- frozenset (default: ``frozenset()``); indices of the
+      vertical lines
+
     OUTPUT:
 
     - ``G`` -- the graph of the 1-skeleton of ``V``
@@ -389,13 +392,15 @@ def voronoi_cells(V):
       of ``E``) with identical first and last elements)
     - ``DG`` -- the dual graph of ``V``, where the vertices are labelled
       by the compact regions of ``V`` and the edges by their dual edges.
+    - ``vertical_regions`` -- dictionary for the regions associated
+      with vertical lines
 
     EXAMPLES::
 
         sage: from sage.schemes.curves.zariski_vankampen import corrected_voronoi_diagram, voronoi_cells
         sage: points = (2, I, 0.000001, 0, 0.000001*I)
         sage: V = corrected_voronoi_diagram(points)
-        sage: G, E, p, EC, DG = voronoi_cells(V)
+        sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((1,)))
         sage: Gv = G.vertices(sort=True)
         sage: Ge = G.edges(sort=True)
         sage: len(Gv), len(Ge)
@@ -443,25 +448,38 @@ def voronoi_cells(V):
          (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None))
         sage: edg[-1] in Ge
         True
+        sage: VR
+        {1: (A vertex at (-49000001/14000000, 1000001/2000000),
+             A vertex at (1000001/2000000, 1000001/2000000),
+             A vertex at (2000001/2000000, 500001/1000000),
+             A vertex at (11/4, 4),
+             A vertex at (-4, 4),
+             A vertex at (-49000001/14000000, 1000001/2000000))}
     """
-    compact_regions = [_ for _ in V.regions().values() if _.is_compact()]
-    non_compact_regions = [_ for _ in V.regions().values() if not _.is_compact()]
-    G = Graph([u.vertices() for v in compact_regions for u in v.faces(1)], format='list_of_edges')
-    E = Graph([u.vertices() for v in non_compact_regions for u in v.faces(1) if u.is_compact()], format='list_of_edges')
+    regions = V.regions()
+    points = [p for p in V.regions().keys() if V.regions()[p].is_compact()]
+    compact_regions = [regions[p] for p in points]
+    vertical_regions = {}
+    non_compact_regions = [reg for reg in V.regions().values()
+                           if not reg.is_compact()]
+    G = Graph([u.vertices() for v in compact_regions for u in v.faces(1)],
+              format='list_of_edges')
+    E = Graph([u.vertices() for v in non_compact_regions for u in v.faces(1)
+               if u.is_compact()], format='list_of_edges')
     p = next(E.vertex_iterator())
     EC = orient_circuit(E.eulerian_circuit())
-    # EC = [EC0[0][0]] + [e[1] for e in EC0]
     DG = Graph()
     for i, reg in enumerate(compact_regions):
         Greg0 = orient_circuit(reg.graph().eulerian_circuit(), convex=True)
-        # Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0)
+        if i in vertical_lines:
+            vertical_regions[i] = Greg0
         DG.add_vertex((i, Greg0))
     for e in G.edges(sort=True):
         a, b = e[:2]
         regs = [v for v in DG.vertices(sort=True) if a in v[1] and b in v[1]]
         if len(regs) == 2:
             DG.add_edge(regs[0], regs[1], e)
-    return (G, E, p, EC, DG)
+    return (G, E, p, EC, DG, vertical_regions)
 
 
 def followstrand(f, factors, x0, x1, y0a, prec=53) -> list:
@@ -492,7 +510,7 @@ def followstrand(f, factors, x0, x1, y0a, prec=53) -> list:
 
     EXAMPLES::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import followstrand
         sage: R. = QQ[]
         sage: f = x^2 + y^3
@@ -548,7 +566,7 @@ def followstrand(f, factors, x0, x1, y0a, prec=53) -> list:
                 ci = c.imag()
                 coefsfactors += list(cr.endpoints())
                 coefsfactors += list(ci.endpoints())
-    from sage.libs.sirocco import contpath, contpath_mp, contpath_comps, contpath_mp_comps
+    from sage.libs.sirocco import (contpath, contpath_mp, contpath_comps, contpath_mp_comps)
     try:
         if prec == 53:
             if factors:
@@ -590,7 +608,7 @@ def newton(f, x0, i0):
         sage: n
         0.0?
         sage: n.real().endpoints()
-        (-0.0147727272727274, 0.00982142857142862)
+        (-0.0460743801652894, 0.0291454081632654)
         sage: n.imag().endpoints()
         (0.000000000000000, -0.000000000000000)
     """
@@ -603,11 +621,11 @@ def fieldI(field):
 
     INPUT:
 
-    - ``field`` -- a number field with an embedding in ``QQbar``.
+    - ``field`` -- a number field with an embedding in `\QQbar`.
 
     OUTPUT:
 
-    The extension ``F`` of ``field`` containing  ``I`` with  an embedding in ``QQbar``.
+    The extension ``F`` of ``field`` containing  ``I`` with  an embedding in `\QQbar`.
 
     EXAMPLES::
 
@@ -616,9 +634,20 @@ def fieldI(field):
         sage: a0 = p.roots(QQbar, multiplicities=False)[0]
         sage: F0. = NumberField(p, embedding=a0)
         sage: fieldI(F0)
-        Number Field in b with defining polynomial
+        Number Field in prim with defining polynomial
         x^10 + 5*x^8 + 14*x^6 - 2*x^5 - 10*x^4 + 20*x^3 - 11*x^2 - 14*x + 10
-        with b = 0.4863890359345430? + 1.000000000000000?*I
+        with prim = 0.4863890359345430? + 1.000000000000000?*I
+        sage: F0 = CyclotomicField(5)
+        sage: fieldI(F0)
+        Number Field in prim with defining polynomial
+        x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1
+        with prim = -0.3090169943749474? + 0.04894348370484643?*I
+        sage: fieldI(QuadraticField(3))
+        Number Field in prim with defining polynomial x^4 - 4*x^2 + 16
+        with prim = -1.732050807568878? + 1.000000000000000?*I
+        sage: fieldI(QuadraticField(-3))
+        Number Field in prim with defining polynomial x^4 + 8*x^2 + 4
+        with prim = 0.?e-18 - 0.732050807568878?*I
 
     If ``I`` is already in the field, the result is the field itself::
 
@@ -629,6 +658,8 @@ def fieldI(field):
         sage: F1 = fieldI(F0)
         sage: F0 == F1
         True
+        sage: QuadraticField(-1) == fieldI(QuadraticField(-1))
+        True
     """
     I0 = QQbar.gen()
     if I0 in field:
@@ -641,8 +672,8 @@ def fieldI(field):
     for h1 in qembd:
         b1 = h1(b0)
         b2 = h1(field_b(field_a.gen(0)))
-        b3 = field.gen(0)
-        F1 = NumberField(q, 'b', embedding=b1)
+        b3 = QQbar(field.gen(0))
+        F1 = NumberField(q, 'prim', embedding=b1)
         if b3 in F1 and b2.imag() > 0:
             return F1
 
@@ -803,7 +834,7 @@ def braid_in_segment(glist, x0, x1, precision={}):
     - ``glist`` -- a tuple of polynomials in two variables
     - ``x0`` -- a Gauss rational
     - ``x1`` -- a Gauss rational
-    - ``precision`` -- a dictionary (default: `dict()`) which assigns a number
+    - ``precision`` -- a dictionary (default: `{}`) which assigns a number
       precision bits to each element of ``glist``
 
     OUTPUT:
@@ -812,7 +843,6 @@ def braid_in_segment(glist, x0, x1, precision={}):
 
     EXAMPLES::
 
-        sage: # optional - sirocco
         sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI
         sage: R. = QQ[]
         sage: K = fieldI(QQ)
@@ -820,7 +850,7 @@ def braid_in_segment(glist, x0, x1, precision={}):
         sage: f = f.change_ring(K)
         sage: x0 = 1
         sage: x1 = 1 + I / 2
-        sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1)
+        sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1)     # needs sirocco
         s1
 
     TESTS:
@@ -844,43 +874,44 @@ def braid_in_segment(glist, x0, x1, precision={}):
         sage: p2a = CC(p2)
         sage: p2b = QQ(p2a.real()) + I*QQ(p2a.imag())
         sage: glist = tuple([_[0] for _ in g.factor()])
-        sage: B = braid_in_segment(glist, p1b, p2b); B              # optional - sirocco
+        sage: B = braid_in_segment(glist, p1b, p2b); B              # needs sirocco
         s5*s3^-1
     """
     precision1 = precision.copy()
     g = prod(glist)
     F1 = g.base_ring()
     x, y = g.parent().gens()
-    X0 = F1(x0)
-    X1 = F1(x1)
     intervals = {}
-    if not precision1:  # new
-        precision1 = {f: 53 for f in glist}  # new
+    if not precision1:
+        precision1 = {f: 53 for f in glist}
     y0s = []
     for f in glist:
         if f.variables() == (y,):
             f0 = F1[y](f)
         else:
-            f0 = F1[y](f.subs({x: X0}))
+            f0 = F1[y](f.subs({x: F1(x0)}))
         y0sf = f0.roots(QQbar, multiplicities=False)
         y0s += list(y0sf)
         while True:
             CIFp = ComplexIntervalField(precision1[f])
             intervals[f] = [r.interval(CIFp) for r in y0sf]
-            if not any(a.overlaps(b) for a, b in itertools.combinations(intervals[f], 2)):
+            if not any(a.overlaps(b) for a, b in
+                       itertools.combinations(intervals[f], 2)):
                 break
             precision1[f] *= 2
     strands = []
     for f in glist:
         for i in intervals[f]:
-            aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision1[f])
+            aux = followstrand(f, [p for p in glist if p != f],
+                               x0, x1, i.center(), precision1[f])
             strands.append(aux)
-    complexstrands = [[(QQ(a[0]), QQ(a[1]), QQ(a[2])) for a in b] for b in strands]
+    complexstrands = [[(QQ(a[0]), QQ(a[1]), QQ(a[2])) for a in b]
+                      for b in strands]
     centralbraid = braid_from_piecewise(complexstrands)
     initialstrands = []
     finalstrands = []
-    initialintervals = roots_interval_cached(g, X0)
-    finalintervals = roots_interval_cached(g, X1)
+    initialintervals = roots_interval_cached(g, x0)
+    finalintervals = roots_interval_cached(g, x1)
     I1 = QQbar.gen()
     for cs in complexstrands:
         ip = cs[0][1] + I1 * cs[0][2]
@@ -888,27 +919,29 @@ def braid_in_segment(glist, x0, x1, precision={}):
         matched = 0
         for center, interval in initialintervals.items():
             if ip in interval:
-                initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])])
+                initialstrands.append([(0, center.real(), center.imag()),
+                                       (1, cs[0][1], cs[0][2])])
                 matched += 1
         if matched != 1:
-            precision1 = {f: precision1[f] * 2 for f in glist}  # new
-            return braid_in_segment(glist, x0, x1, precision=precision1)  # new
+            precision1 = {f: precision1[f] * 2 for f in glist}
+            return braid_in_segment(glist, x0, x1, precision=precision1)
 
         matched = 0
         for center, interval in finalintervals.items():
             if fp in interval:
-                finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())])
+                finalstrands.append([(0, cs[-1][1], cs[-1][2]),
+                                     (1, center.real(), center.imag())])
                 matched += 1
         if matched != 1:
-            precision1 = {f: precision1[f] * 2 for f in glist}  # new
-            return braid_in_segment(glist, x0, x1, precision=precision1)  # new
+            precision1 = {f: precision1[f] * 2 for f in glist}
+            return braid_in_segment(glist, x0, x1, precision=precision1)
     initialbraid = braid_from_piecewise(initialstrands)
     finalbraid = braid_from_piecewise(finalstrands)
 
     return initialbraid * centralbraid * finalbraid
 
 
-def geometric_basis(G, E, EC0, p, dual_graph) -> list:
+def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list:
     r"""
     Return a geometric basis, based on a vertex.
 
@@ -927,74 +960,76 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
       ``E`` is the boundary of the non-bounded component of the complement.
       The edges are labelled as the dual edges and the vertices are labelled
       by a tuple whose first element is the an integer for the position and the
-      second one is the cyclic ordered list of vertices in the region.
+      second one is the cyclic ordered list of vertices in the region
+
+    - ``vertical_regions`` -- dictionary (default: `{}`); its keys are
+      the vertices of ``dual_graph`` to fix regions associated with
+      vertical lines
 
-    OUTPUT: A geometric basis. It is formed by a list of sequences of paths.
-    Each path is a list of vertices, that form a closed path in ``G``, based at
-    ``p``, that goes to a region, surrounds it, and comes back by the same
-    path it came. The concatenation of all these paths is equivalent to ``E``.
+    OUTPUT: A geometric basis and a dictionary.
+
+    The geometric basis is formed by a list of sequences of paths. Each path is a
+    ist of vertices, that form a closed path in ``G``, based at ``p``, that goes
+    to a region, surrounds it, and comes back by the same path it came. The
+    concatenation of all these paths is equivalent to ``E``.
+
+    The dictionary associates to each vertical line the index of the generator
+    of the geometric basis associated to it.
 
     EXAMPLES::
 
-        sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, voronoi_cells
-        sage: points = [(-3,0),(3,0),(0,3),(0,-3)]+ [(0,0),(0,-1),(0,1),(1,0),(-1,0)]
-        sage: V = VoronoiDiagram(points)
-        sage: G, E, p, EC, DG = voronoi_cells(V)
-        sage: geometric_basis(G, E, EC, p, DG)
-        [[A vertex at (-2, -2),
-          A vertex at (2, -2),
-          A vertex at (2, 2),
-          A vertex at (1/2, 1/2),
-          A vertex at (1/2, -1/2),
-          A vertex at (2, -2),
-          A vertex at (-2, -2)],
-         [A vertex at (-2, -2),
-          A vertex at (2, -2),
-          A vertex at (1/2, -1/2),
-          A vertex at (1/2, 1/2),
-          A vertex at (-1/2, 1/2),
-          A vertex at (-1/2, -1/2),
-          A vertex at (1/2, -1/2),
-          A vertex at (2, -2),
-          A vertex at (-2, -2)],
-         [A vertex at (-2, -2),
-          A vertex at (2, -2),
-          A vertex at (1/2, -1/2),
-          A vertex at (-1/2, -1/2),
-          A vertex at (-2, -2)],
-         [A vertex at (-2, -2),
-          A vertex at (-1/2, -1/2),
-          A vertex at (-1/2, 1/2),
-          A vertex at (1/2, 1/2),
-          A vertex at (2, 2),
-          A vertex at (-2, 2),
-          A vertex at (-1/2, 1/2),
-          A vertex at (-1/2, -1/2),
-          A vertex at (-2, -2)],
-         [A vertex at (-2, -2),
-          A vertex at (-1/2, -1/2),
-          A vertex at (-1/2, 1/2),
-          A vertex at (-2, 2),
-          A vertex at (-2, -2)]]
+        sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, corrected_voronoi_diagram, voronoi_cells
+        sage: points = (0, -1, I, 1, -I)
+        sage: V = corrected_voronoi_diagram(points)
+        sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((0 .. 4)))
+        sage: gb, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR)
+        sage: gb
+        [[A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2),
+          A vertex at (-1/2, 1/2), A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2),
+          A vertex at (1/2, 1/2), A vertex at (-1/2, 1/2), A vertex at (-5/2, 5/2),
+          A vertex at (5/2, 5/2), A vertex at (5/2, -5/2)],
+         [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2),
+          A vertex at (-1/2, 1/2), A vertex at (1/2, 1/2), A vertex at (5/2, 5/2),
+          A vertex at (5/2, -5/2)],
+         [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (1/2, 1/2),
+          A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)], [A vertex at (5/2, -5/2),
+          A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2), A vertex at (-1/2, 1/2),
+          A vertex at (-5/2, 5/2), A vertex at (-5/2, -5/2), A vertex at (-1/2, -1/2),
+          A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)],
+         [A vertex at (5/2, -5/2), A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2),
+          A vertex at (-5/2, -5/2), A vertex at (5/2, -5/2)]]
+        sage: vd
+        {0: 0, 1: 3, 2: 1, 3: 2, 4: 4}
     """
     i = EC0.index(p)
-    EC = EC0[i:-1] + EC0[:i + 1]   # A counterclockwise eulerian circuit on the boundary, starting and ending at p
+    EC = EC0[i:-1] + EC0[:i + 1]
+    # A counterclockwise eulerian circuit on the boundary,
+    # starting and ending at p
     if G.size() == E.size():
         if E.is_cycle():
-            return [EC]
+            j = next(dual_graph.vertex_iterator())[0]
+            if j in vertical_regions:
+                vd = {j: 0}
+            else:
+                vd = {}
+            return [EC], vd
     edges_E = E.edges(sort=True)
     InternalEdges = [e for e in G.edges(sort=True) if e not in edges_E]
     InternalVertices = [v for e in InternalEdges for v in e[:2]]
     Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges)
     for i, ECi in enumerate(EC):  # q and r are the points we will cut through
         if ECi in Internal:
-            EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi, sort=True) and v != ECi]
+            EI = [v for v in E if v in
+                  Internal.connected_component_containing_vertex(ECi, sort=True)
+                  and v != ECi]
             if EI:
                 q = ECi
                 connecting_path = list(EC[:i])
                 break
         if EC[-i] in Internal:
-            EI = [v for v in E if v in Internal.connected_component_containing_vertex(EC[-i], sort=True) and v != EC[-i]]
+            EI = [v for v in E if v in
+                  Internal.connected_component_containing_vertex(EC[-i], sort=True)
+                  and v != EC[-i]]
             if EI:
                 q = EC[-i]
                 connecting_path = list(reversed(EC[-i:]))
@@ -1003,7 +1038,6 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
     E_dist_q = E.shortest_path_lengths(q)
     I_dist_q = Internal.shortest_path_lengths(q)
     distancequotients = [(E_dist_q[v]**2 / I_dist_q[v], v) for v in EI]
-    # distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI]
     r = max(distancequotients)[1]
     cutpath = Internal.shortest_path(q, r)
     for i, v in enumerate(cutpath):
@@ -1037,7 +1071,8 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
         E1.add_edge(cutpath[i], cutpath[i + 1], None)
         E2.add_edge(cutpath[i], cutpath[i + 1], None)
     Gd = copy(dual_graph)
-    to_delete = [e for e in Gd.edges(sort=True) if e[2][0] in cutpath and e[2][1] in cutpath]
+    to_delete = [e for e in Gd.edges(sort=True) if e[2][0] in cutpath and
+                 e[2][1] in cutpath]
     Gd.delete_edges(to_delete)
     Gd1, Gd2 = Gd.connected_components_subgraphs()
     edges_2 = []
@@ -1045,16 +1080,20 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
     for reg in Gd2.vertices(sort=True):
         vertices_2 += reg[1][:-1]
         reg_circuit = reg[1]
-        edges_2 += [(v1, reg_circuit[i + 1]) for i, v1 in enumerate(reg_circuit[:-1])]
-        edges_2 += [(v1, reg_circuit[i - 1]) for i, v1 in enumerate(reg_circuit[1:])]
+        edges_2 += [(v1, reg_circuit[i + 1])
+                    for i, v1 in enumerate(reg_circuit[:-1])]
+        edges_2 += [(v1, reg_circuit[i - 1])
+                    for i, v1 in enumerate(reg_circuit[1:])]
     G2 = G.subgraph(vertices=vertices_2, edges=edges_2)
     edges_1 = []
     vertices_1 = []
     for reg in Gd1.vertices(sort=True):
         vertices_1 += reg[1]
         reg_circuit = reg[1] + (reg[1][0],)
-        edges_1 += [(v1, reg_circuit[i + 1]) for i, v1 in enumerate(reg_circuit[:-1])]
-        edges_1 += [(v1, reg_circuit[i - 1]) for i, v1 in enumerate(reg_circuit[1:])]
+        edges_1 += [(v1, reg_circuit[i + 1])
+                    for i, v1 in enumerate(reg_circuit[:-1])]
+        edges_1 += [(v1, reg_circuit[i - 1])
+                    for i, v1 in enumerate(reg_circuit[1:])]
     G1 = G.subgraph(vertices=vertices_1, edges=edges_1)
     if EC[qi + 1] in G2:
         G1, G2 = G2, G1
@@ -1067,9 +1106,13 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
         EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath))
         EC2 = cutpath + list(EC[ri + 1:qi + 1])
 
-    gb1 = geometric_basis(G1, E1, EC1, q, Gd1)
-    gb2 = geometric_basis(G2, E2, EC2, q, Gd2)
+    gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions=vertical_regions)
+    gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions=vertical_regions)
 
+    vd = {j: vd1[j] for j in vd1}
+    m = len(gb1)
+    for j in vd2.keys():
+        vd[j] = vd2[j] + m
     reverse_connecting = list(reversed(connecting_path))
     resul = [connecting_path + path + reverse_connecting
              for path in gb1 + gb2]
@@ -1083,10 +1126,52 @@ def geometric_basis(G, E, EC0, p, dual_graph) -> list:
                     i -= 1
             else:
                 i += 1
-    return resul
+    return (resul, vd)
 
 
-def strand_components(f, flist, p1):
+def vertical_lines_in_braidmon(pols) -> list:
+    r"""
+    Return the vertical lines in ``pols``, unless
+    one of the other components has a vertical asymptote.
+
+    INPUT:
+
+    - ``pols`` -- a  list of polynomials with two variables whose
+      product equals ``f``
+
+    OUTPUT:
+
+    A list with the indices of the vertical lines in ``flist`` if there is
+    no other componnet with vertical asymptote; otherwise it returns an empty
+    list.
+
+    EXAMPLES::
+
+        sage: from sage.schemes.curves.zariski_vankampen import vertical_lines_in_braidmon
+        sage: R. = QQ[]
+        sage: flist = [x^2 - y^3, x, x + 3 * y - 5, 1 - x]
+        sage: vertical_lines_in_braidmon(flist)
+        [1, 3]
+        sage: flist += [x * y - 1]
+        sage: vertical_lines_in_braidmon(flist)
+        []
+        sage: vertical_lines_in_braidmon([])
+        []
+    """
+    if not pols:
+        return []
+    res = []
+    for j, f in enumerate(pols):
+        C = Curve(f)
+        vertical_asymptote = C.has_vertical_asymptote()
+        if vertical_asymptote:
+            return []
+        if C.is_vertical_line():
+            res.append(j)
+    return res
+
+
+def strand_components(f, pols, p1):
     r"""
     Compute only the assignment from strands to elements of ``flist``.
 
@@ -1095,7 +1180,7 @@ def strand_components(f, flist, p1):
     - ``f`` -- a  reduced polynomial with two variables, over a number field
       with an embedding in the complex numbers
 
-    - ``flist`` -- a  list of polynomials with two variables whose
+    - ``pols`` -- a  list of polynomials with two variables whose
       product equals ``f``
 
     - ``p1`` -- a Gauss rational
@@ -1103,13 +1188,13 @@ def strand_components(f, flist, p1):
     OUTPUT:
 
     - A list and a dictionary.  The first one is an ordered list of pairs
-      consisting of ``(z,i)`` where ``z`` is a root of ``f(p_1,y)`` and `i` is the position
-      of the polynomial in the list whose root is ``z``. The second one attaches
-      a number `i` (strand) to a number `j` (a polynomial in the list).
+      consisting of ``(z,i)`` where ``z`` is a root of ``f(p_1,y)``
+      and `i` is the position of the polynomial in the list whose root
+      is ``z``. The second one attaches a number `i` (strand) to a
+      number `j` (a polynomial in the list).
 
     EXAMPLES::
 
-        sage: # optional - sirocco
         sage: from sage.schemes.curves.zariski_vankampen import strand_components
         sage: R. = QQ[]
         sage: flist = [x^2 - y^3, x + 3 * y - 5]
@@ -1119,20 +1204,20 @@ def strand_components(f, flist, p1):
           (1, 0), (1.333333333333334?, 1)], {0: 0, 1: 0, 2: 0, 3: 1})
     """
     x, y = f.parent().gens()
-    F = flist[0].base_ring()
+    F = pols[0].base_ring()
     strands = {}
     roots_base = []
-    for i, h in enumerate(flist):
+    for i, h in enumerate(pols):
         h0 = h.subs({x: p1})
         h1 = F[y](h0)
         rt = h1.roots(QQbar, multiplicities=False)
         roots_base += [(r, i) for r in rt]
     roots_base.sort()
-    strands = {i: par[1] for i, par in enumerate(roots_base)}  # quitar +1 despues de revision
+    strands = {i: par[1] for i, par in enumerate(roots_base)}
     return (roots_base, strands)
 
 
-def braid_monodromy(f, arrangement=()):
+def braid_monodromy(f, arrangement=(), vertical=False):
     r"""
     Compute the braid monodromy of a projection of the curve defined by
     a polynomial.
@@ -1142,31 +1227,45 @@ def braid_monodromy(f, arrangement=()):
     - ``f`` -- a polynomial with two variables, over a number field
       with an embedding in the complex numbers
 
-    - ``arrangement`` -- an optional tuple of polynomials whose product
-      equals ``f``.
+    - ``arrangement`` -- tuple (default: ``()``); an optional tuple
+      of polynomials whose product equals ``f``
+
+    - ``vertical`` -- boolean (default: ``False`); if set to ``True``,
+      ``arrangements`` contains more than one polynomial, some of them
+      are of degree `1` in `x` and degree `0` in `y`, and none of
+      the other components have vertical asymptotes, then these
+      components are marked as *vertical* and not used for the computation
+      of the braid monodromy. The other ones are marked as *horizontal*. If
+      a vertical component does not pass through a singular points of the
+      projection of the horizontal components a trivial braid is added
+      to the list.
 
     OUTPUT:
 
-    A list of braids and a dictionary.
-    The braids correspond to paths based in the same point;
-    each of these paths is the conjugated of a loop around one of the points
-    in the discriminant of the projection of ``f``. The dictionary assigns each
-    strand to the index of the corresponding factor in ``arrangement``.
+    - A list of braids, images by the braid monodromy of a geometric
+      basis of the complement of the discriminant of `f` in `\CC`.
+
+    - A dictionary: ``i``, index of a strand is sent to the index of
+      the corresponding factor in ``arrangement``.
+
+    - Another dictionary ``dv``, only relevant if ``vertical`` is ``True``.
+      If  ``j`` is the index
+      of a braid corresponding to a vertical line with index ``i``
+      in ``arrangement``, then ``dv[j] = i``.
+
+    - A non-negative integer: the number of strands of the braids,
+      only necessary if the list of braids is empty.
 
     .. NOTE::
 
         The projection over the `x` axis is used if there are no vertical
         asymptotes. Otherwise, a linear change of variables is done to fall
-        into the previous case.
-
-    .. TODO::
-
-        Create a class ``arrangements_of_curves`` with a ``braid_monodromy``
-        method; it can be also a method for affine line arrangements.
+        into the previous case except if the only vertical asymptotes are lines
+        and ``vertical=True``.
 
     EXAMPLES::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy
         sage: R. = QQ[]
         sage: f = (x^2 - y^3) * (x + 3*y - 5)
@@ -1174,7 +1273,7 @@ def braid_monodromy(f, arrangement=()):
         ([s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
           s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
           s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
-          s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0})
+          s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0}, {}, 4)
         sage: flist = (x^2 - y^3, x + 3*y - 5)
         sage: bm1 = braid_monodromy(f, arrangement=flist)
         sage: bm1[0] == bm[0]
@@ -1182,42 +1281,86 @@ def braid_monodromy(f, arrangement=()):
         sage: bm1[1]
         {0: 0, 1: 1, 2: 0, 3: 0}
         sage: braid_monodromy(R(1))
-        ([], {})
+        ([], {}, {}, 0)
         sage: braid_monodromy(x*y^2 - 1)
-        ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0})
+        ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}, 3)
+        sage: L = [x, y, x - 1, x -y]
+        sage: braid_monodromy(prod(L), arrangement=L, vertical=True)
+        ([s^2, 1], {0: 1, 1: 3}, {0: 0, 1: 2}, 2)
     """
     global roots_interval_cache
     F = fieldI(f.base_ring())
     I1 = F(QQbar.gen())
     f = f.change_ring(F)
-    if arrangement == ():
+    if not arrangement:
         arrangement1 = (f,)
     else:
-        arrangement1 = tuple(_.change_ring(F) for _ in arrangement)
+        arrangement1 = tuple(g.change_ring(F) for g in arrangement)
     x, y = f.parent().gens()
-    glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor())
+    if vertical:
+        indices_v = vertical_lines_in_braidmon(arrangement1)
+    else:
+        indices_v = []
+    arrangement_h = tuple(f0 for j, f0 in enumerate(arrangement1)
+                          if j not in indices_v)
+    arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1)
+                          if j in indices_v)
+    glist = tuple(fc[0] for f0 in arrangement_h for fc in f0.factor())
     g = f.parent()(prod(glist))
     d = g.degree(y)
-    while not g.coefficient(y**d) in F:
-        g = g.subs({x: x + y})
-        d = g.degree(y)
-        arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1)
-        glist = tuple(f1.subs({x: x + y}) for f1 in glist)
+    if not arrangement_v:  # change of coordinates only if indices_v is empty
+        while not g.coefficient(y**d) 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)
+            arrangement1 = arrangement_h
+            glist = tuple(f1.subs({x: x + y}) for f1 in glist)
     if d > 0:
         disc = discrim(glist)
     else:
         disc = []
+    vertical_braid = {}
+    transversal = {}
+    vl = []
+    for f0 in arrangement_v:
+        pt = [j for j, t in enumerate(disc) if f0.subs({x: t}) == 0]
+        if pt:
+            vertical_braid[f0] = (pt[0], arrangement1.index(f0))
+            vl.append(pt[0])
+        else:
+            transversal[f0] = arrangement1.index(f0)
+    vl.sort()
+    vl = frozenset(vl)
     if not disc:
-        result = []
+        vertical_braids = {i: transversal[f0]
+                           for i, f0 in enumerate(transversal)}
+        if d > 1:
+            result = [BraidGroup(d).one() for p in transversal]
+        else:
+            G = FreeGroup(0) / []
+            result = [G.one() for p in transversal]
         p1 = F(0)
-        roots_base, strands = strand_components(g, arrangement1, p1)
-        return ([], strands)
+        if d > 0:
+            roots_base, strands = strand_components(g, arrangement_h, p1)
+            strands1 = {}
+            for j in range(d):
+                i = strands[j]
+                k = arrangement1.index(arrangement_h[i])
+                strands1[j] = k
+        else:
+            strands1 = {}
+        return (result, strands1, vertical_braids, d)
     V = corrected_voronoi_diagram(tuple(disc))
-    G, E, p, EC, DG = voronoi_cells(V)
+    G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=vl)
     p0 = (p[0], p[1])
     p1 = p0[0] + I1 * p0[1]
-    roots_base, strands = strand_components(g, arrangement1, p1)
-    geombasis = geometric_basis(G, E, EC, p, DG)
+    roots_base, strands = strand_components(g, arrangement_h, p1)
+    strands1 = {}
+    for j in range(d):
+        i = strands[j]
+        k = arrangement1.index(arrangement_h[i])
+        strands1[j] = k
+    geombasis, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR)
     segs = set()
     for p in geombasis:
         for s in zip(p[:-1], p[1:]):
@@ -1253,7 +1396,18 @@ def braid_monodromy(f, arrangement=()):
             x1 = tuple(path[i + 1].vector())
             braidpath = braidpath * segsbraids[(x0, x1)]
         result.append(braidpath)
-    return (result, strands)
+    vertical_braids = {}
+    r = len(result)
+    t = 0
+    for f0 in arrangement_v:
+        if f0 in vertical_braid.keys():
+            k, j = vertical_braid[f0]
+            vertical_braids[vd[k]] = j
+        else:
+            vertical_braids[r + t] = transversal[f0]
+            t += 1
+            result.append(B.one())
+    return (result, strands1, vertical_braids, d)
 
 
 def conjugate_positive_form(braid):
@@ -1263,7 +1417,7 @@ def conjugate_positive_form(braid):
 
     INPUT:
 
-    - ``braid`` -- a braid ``\sigma``.
+    - ``braid`` -- a braid `\sigma`
 
     OUTPUT:
 
@@ -1316,10 +1470,7 @@ def conjugate_positive_form(braid):
             A1 = rightnormalform(sg)
             par = A1[-1][0] % 2
             A1 = [B(a) for a in A1[:-1]]
-            if not A1:
-                b = B.one()
-            else:
-                b = prod(A1)
+            b = prod(A1, B.one())
             b1 = len(b.Tietze()) / (len(A1) + 1)
             if res is None or b1 < res[3]:
                 res = [tau, A1, par, b1]
@@ -1373,14 +1524,15 @@ def braid2rels(L):
     k = min(T1) - 1
     B0 = BraidGroup(m)
     F0 = FreeGroup(m)
-    br0 = B0([_-k for _ in T])
+    br0 = B0([j - k for j in T])
     br0_left = leftnormalform(br0)
     q, r = ZZ(br0_left[0][0]).quo_rem(2)
-    br1 = B0.delta()**r * B0(prod(B0(_) for _ in br0_left[1:]))
+    br1 = B0.delta()**r * prod(map(B0, br0_left[1:]), B0.one())
     cox = prod(F0.gens())
     U0 = [cox**q * (f0 * br1) / cox**q / f0 for f0 in F0.gens()[:-1]]
-    U = [tuple(sign(k1) * (abs(k1) + k) for k1 in _.Tietze()) for _ in U0]
-    pasos = [B.one()] + list(reversed(L1))
+    U = [tuple(sign(k1) * (abs(k1) + k) for k1 in br.Tietze()) for br in U0]
+    pasos = [B.one()]
+    pasos.extend(reversed(L1))
     for C in pasos:
         U = [(F(a) * C.inverse()).Tietze() for a in U]
         ga = F / U
@@ -1392,7 +1544,7 @@ def braid2rels(L):
         P.TzGoGo()
         P.TzGoGo()
         gb = wrap_FpGroup(P.FpGroupPresentation())
-        U = [_.Tietze() for _ in gb.relations()]
+        U = [rel.Tietze() for rel in gb.relations()]
     return U
 
 
@@ -1406,7 +1558,9 @@ def relation(x, b):
     return x * b / x
 
 
-def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, puiseux=False, vertical=[]):
+def fundamental_group_from_braid_mon(bm, degree=None,
+                                     simplified=True, projective=False,
+                                     puiseux=True, vertical=[]):
     r"""
     Return a presentation of the fundamental group computed from
     a braid monodromy.
@@ -1426,16 +1580,15 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv
       of the curve will be computed, otherwise, the fundamental group of
       the complement in the affine plane will be computed
 
-    - ``puiseux`` -- boolean (default: ``False``); if set to ``True``,
-      ``simplified`` is set to ``False``, and
+    - ``puiseux`` -- boolean (default: ``True``); if set to ``True``
       a presentation of the fundamental group with the homotopy type
       of the complement of the affine curve will be computed, adding
       one relation if ``projective`` is set to ``True``.
 
     - ``vertical`` -- list of integers (default: ``[]``); the indices in
-      ``[1..r]`` of the braids that surround a vertical line
+      ``[0 .. r - 1]`` of the braids that surround a vertical line
 
-    If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is
+    If ``projective``` is ``False`` and ``puiseux`` is
     ``True``, a Zariski-VanKampen presentation is returned.
 
     OUTPUT:
@@ -1450,37 +1603,45 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv
         sage: bm = [s1*s2*s0*s1*s0^-1*s1^-1*s0^-1,
         ....:       s0*s1^2*s0*s2*s1*(s0^-1*s1^-1)^2*s0^-1,
         ....:       (s0*s1)^2]
-        sage: g = fundamental_group_from_braid_mon(bm, projective=True); g
-        Finitely presented group < x0, x1 | x1*x0^2*x1, x0^-1*x1^-1*x0^-1*x1*x0^-1*x1^-1 >
-        sage: print (g.order(), g.abelian_invariants())
+        sage: g = fundamental_group_from_braid_mon(bm, projective=True); g      # needs sirocco
+        Finitely presented group
+        < x1, x3 | x3^2*x1^2, x1^-1*x3^-1*x1*x3^-1*x1^-1*x3^-1 >
+        sage: print(g.order(), g.abelian_invariants())                         # needs sirocco
         12 (4,)
         sage: B2 = BraidGroup(2)
         sage: bm = [B2(3 * [1])]
-        sage: g = fundamental_group_from_braid_mon(bm, vertical=[1]); g
-        Finitely presented group < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1,
-                                                x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 >
-        sage: fundamental_group_from_braid_mon([]) is None      # optional - sirocco
+        sage: g = fundamental_group_from_braid_mon(bm, vertical=[0]); g         # needs sirocco
+        Finitely presented group
+        < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1,
+                       x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 >
+        sage: fundamental_group_from_braid_mon([]) is None                      # needs sirocco
         True
-        sage: fundamental_group_from_braid_mon([], degree=2)    # optional - sirocco
+        sage: fundamental_group_from_braid_mon([], degree=2)                    # needs sirocco
         Finitely presented group < x0, x1 |  >
+        sage: fundamental_group_from_braid_mon([SymmetricGroup(1).one()])       # needs sirocco
+        Finitely presented group < x |  >
     """
     vertical0 = sorted(vertical)
     v = len(vertical0)
     if not bm:
         d = degree
+    elif bm[0].parent().order() == 1:
+        d = 1
     else:
         d = bm[0].parent().strands()
     if d is None:
         return None
     F = FreeGroup(d)
     Fv = FreeGroup(d + v)
-    bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0]
+    if d == 0:
+        return Fv / []
+    if d == 1:
+        return Fv / [(1, j, -1, -j) for j in range(2, d + v + 1)]
+    bmh = [br for j, br in enumerate(bm) if j not in vertical0]
     if not puiseux:
         relations_h = (relation([(x, b) for x in F.gens() for b in bmh]))
         rel_h = [r[1] for r in relations_h]
-        simplified0 = simplified
     else:
-        simplified0 = False
         conjugate_desc = conjugate_positive_form_p(bmh)
         trenzas_desc = [b1[-1] for b1 in conjugate_desc]
         trenzas_desc_1 = flatten(trenzas_desc, max_level=1)
@@ -1490,7 +1651,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv
     rel_v = []
     for j, k in enumerate(vertical0):
         l1 = d + j + 1
-        br = bm[k - 1]
+        br = bm[k]
         for gen in F.gens():
             j0 = gen.Tietze()[0]
             rl = (l1,) + (gen * br).Tietze() + (-l1, -j0)
@@ -1499,12 +1660,12 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv
     if projective:
         rel.append(prod(Fv.gens()).Tietze())
     G = Fv / rel
-    if simplified0:
+    if simplified:
         return G.simplified()
     return G
 
 
-def fundamental_group(f, simplified=True, projective=False, puiseux=False):
+def fundamental_group(f, simplified=True, projective=False, puiseux=True):
     r"""
     Return a presentation of the fundamental group of the complement of
     the algebraic set defined by the polynomial ``f``.
@@ -1522,12 +1683,14 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False):
       of the curve will be computed, otherwise, the fundamental group of
       the complement in the affine plane will be computed
 
-    - ``puiseux`` -- boolean (default: ``False``); if set to ``True``,
+    - ``puiseux`` -- boolean (default: ``True``); if set to ``True``,
       a presentation of the fundamental group with the homotopy type
-      of the complement of the affine curve is computed, ``simplified`` is
-      ignored. One relation is added if ``projective`` is set to ``True``.
+      of the complement of the affine curve is computed. If the Euler
+      characteristic does not match, the homotopy type is obtained
+      with a wedge of 2-spheres. One relation is added if ``projective``
+      is set to ``True``.
 
-    If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is
+    If ``projective``` is ``False`` and ``puiseux`` is
     ``True``, a Zariski-VanKampen presentation is returned.
 
     OUTPUT:
@@ -1537,13 +1700,13 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False):
 
     EXAMPLES::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy
         sage: R. = QQ[]
         sage: f = x^2 + y^3
         sage: fundamental_group(f)
-        Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 >
-        sage: fundamental_group(f, simplified=False).sorted_presentation()
+        Finitely presented group < x0, x1 | x0*x1^-1*x0^-1*x1^-1*x0*x1 >
+        sage: fundamental_group(f, simplified=False, puiseux=False).sorted_presentation()
         Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0*x1,
                                                 x2^-1*x0*x1*x0^-1,
                                                 x1^-1*x0^-1*x1^-1*x0*x1*x0 >
@@ -1554,17 +1717,17 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False):
 
     ::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
         sage: R. = QQ[]
         sage: f = y^3 + x^3
-        sage: fundamental_group(f)
-        Finitely presented group < x0, x1, x2 | x0*x1*x2*x0^-1*x2^-1*x1^-1, x2*x0*x1*x2^-1*x1^-1*x0^-1 >
+        sage: fundamental_group(f).sorted_presentation()
+        Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0^-1*x2*x0*x1,
+                                                x2^-1*x1^-1*x2*x0*x1*x0^-1 >
 
     It is also possible to have coefficients in a number field with a
     fixed embedding in `\QQbar`::
 
-        sage: # optional - sirocco
         sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
         sage: zeta = QQbar['x']('x^2 + x+ 1').roots(multiplicities=False)[0]
         sage: zeta
@@ -1574,22 +1737,19 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False):
         Defining zeta
         sage: R. = F[]
         sage: f = y^3 + x^3 + zeta * x + 1
-        sage: fundamental_group(f)
+        sage: fundamental_group(f)                                  # needs sirocco
         Finitely presented group < x0 |  >
 
-    We compute the fundamental group of the complement of a quartic using the ``puiseux`` option::
+    We compute the fundamental group of the complement of a
+    quartic using the ``puiseux`` option::
 
         sage: # optional - sirocco
         sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
         sage: R. = QQ[]
         sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y  * (x + y + 1)
-        sage: g = fundamental_group(f, puiseux=True); g.sorted_presentation()
-        Finitely presented group
-         < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2,
-                            x3^-1*x2^-1*x1*x2, x2^-1*x1^-1*x0^-1*x1*x2*x1, x2^-1*x0 >
-        sage: g.simplified().sorted_presentation()
+        sage: g = fundamental_group(f); g.sorted_presentation()
         Finitely presented group < x0, x1 | x1^-2*x0^2, (x1^-1*x0)^3 >
-        sage: g = fundamental_group(f, puiseux=True, projective=True)
+        sage: g = fundamental_group(f, projective=True)
         sage: g.order(), g.abelian_invariants()
         (12, (4,))
         sage: fundamental_group(y * (y - 1))
@@ -1610,10 +1770,15 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False):
         d = g.degree(y)
     else:
         d = bm[0].parent().strands()
-    return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux)
+    return fundamental_group_from_braid_mon(bm, degree=d,
+                                            simplified=simplified,
+                                            projective=projective,
+                                            puiseux=puiseux)
 
 
-def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False):
+def fundamental_group_arrangement(flist, simplified=True, projective=False,
+                                  puiseux=True, vertical=False,
+                                  braid_data=None):
     r"""
     Compute the fundamental group of the complement of a curve
     defined by a list of polynomials with the extra information
@@ -1633,27 +1798,34 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis
       of the curve will be computed, otherwise, the fundamental group of
       the complement in the affine plane will be computed
 
-    - ``puiseux`` -- boolean (default: ``False``); if set to ``True``,
-      ``simplified`` is set to ``False``, and
+    - ``puiseux`` -- boolean (default: ``True``); if set to ``True``
       a presentation of the fundamental group with the homotopy type
       of the complement of the affine curve will be computed, adding
       one relation if ``projective`` is set to ``True``.
 
+    - ``vertical`` -- boolean (default: ``False``); if set to ``True``,
+      whenever no curve has vertical asymptotes the computation of braid
+      monodromy is simpler if some lines are vertical
+
+    - ``braid_data`` -- tuple (default: ``None``); if it is not the default
+      it is the output of ``fundamental_group_from_braid_mon`` previously
+      computed
+
     OUTPUT:
 
     - A list of braids. The braids correspond to paths based in the same point;
       each of this paths is the conjugated of a loop around one of the points
       in the discriminant of the projection of ``f``.
 
-    - A dictionary attaching a tuple ``(i,)`` (generator) to a number ``j``
-      (a polynomial in the list). If ``simplified`` is set to ``True``,
-      a longer key may appear for either the meridian of the line at infinity,
-      if ``projective`` is ``True``, or a simplified generator,
-      if ``projective`` is ``False``
+    - A dictionary attaching to ``j`` a tuple a list of elements
+      of the group  which are meridians of the curve in position ``j``.
+      If ``projective`` is ``False`` and the `y`-degree of the horizontal
+      components coincide with the total degree, another key is added
+      to give a meridian of the line at infinity.
 
     EXAMPLES::
 
-        sage: # optional - sirocco
+        sage: # needs sirocco
         sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy
         sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement
         sage: R. = QQ[]
@@ -1661,36 +1833,48 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis
         sage: g, dic = fundamental_group_arrangement(flist)
         sage: g.sorted_presentation()
         Finitely presented group
-         < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0, x1^-1*x0^-1*x1*x0 >
+         < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0,
+                        x1^-1*x0^-1*x1*x0 >
         sage: dic
-        {0: [x0, x2, x0], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]}
-        sage: g, dic = fundamental_group_arrangement(flist, simplified=False)
+        {0: [x0, x2], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]}
+        sage: g, dic = fundamental_group_arrangement(flist, simplified=False, puiseux=False)
         sage: g.sorted_presentation(), dic
         (Finitely presented group
-         < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1, x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
+         < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1,
+                            x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
                             x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x3*x2,
                             x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2,
-                            x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, x3^-1*x1^-1*x0*x1,
-                            x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0,
-                            x1^-1*x0^-1*x1*x0 >,
+                            x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
+                            x3^-1*x1^-1*x0*x1,
+                            x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0,
+                            x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0 >,
          {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]})
         sage: fundamental_group_arrangement(flist, projective=True)
-        (Finitely presented group < x |  >, {0: [x0, x0, x0], 1: [x0^-3]})
+        (Finitely presented group < x |  >, {0: [x], 1: [x^-3]})
         sage: fundamental_group_arrangement([])
         (Finitely presented group <  |  >, {})
         sage: g, dic = fundamental_group_arrangement([x * y])
         sage: g.sorted_presentation(), dic
         (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >,
-        {0: [x0, x1], 1: [x1^-1*x0^-1]})
-        sage: fundamental_group_arrangement([y + x^2], projective=True)
-        (Finitely presented group < x | x^2 >, {0: [x0, x0]})
-
-    .. TODO::
-
-        Create a class ``arrangements_of_curves`` with a ``fundamental_group``
-        method it can be also a method for affine or projective line
-        arrangements, even for hyperplane arrangements defined over a number
-        subfield of ``QQbar`` after applying a generic line section.
+         {0: [x0, x1], 1: [x1^-1*x0^-1]})
+        sage: fundamental_group_arrangement([y + x^2])
+        (Finitely presented group < x |  >, {0: [x]})
+        sage: fundamental_group_arrangement([y^2 + x], projective=True)
+        (Finitely presented group < x | x^2 >, {0: [x]})
+        sage: L = [x, y, x - 1, x -y]
+        sage: G, dic =fundamental_group_arrangement(L)
+        sage: G.sorted_presentation()
+        Finitely presented group
+        < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x0^-1*x1*x3*x0,
+                           x3^-1*x1^-1*x3*x0*x1*x0^-1, x2^-1*x0^-1*x2*x0 >
+        sage: dic
+        {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}
+        sage: fundamental_group_arrangement(L, vertical=True)
+        (Finitely presented group
+         < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1,
+                            x1*x2*x0*x2^-1*x1^-1*x0^-1,
+                            x1*x2*x0*x1^-1*x0^-1*x2^-1 >,
+         {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]})
     """
     if flist:
         f = prod(flist)
@@ -1699,23 +1883,32 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis
         R = PolynomialRing(QQ, ('x', 'y'))
         f = R(1)
     x, y = R.gens()
-    F = R.base_ring()
-    flist1 = list(flist)
-    d = f.degree(y)
-    while not f.coefficient(y**d) in F:
-        flist1 = [g.subs({x: x + y}) for g in flist1]
-        f = prod(flist1)
-        d = f.degree(y)
-    if projective:
-        while f.degree(y) < f.degree():
-            flist1 = [g.subs({x: x + y}) for g in flist]
-            f = prod(flist1)
-    if not flist1:
+    flist1 = tuple(flist)
+    if vertical and vertical_lines_in_braidmon(flist1):
+        infinity = all(Curve(g).is_vertical_line() or
+                       g.degree(y) == g.degree() for g in flist1)
+    else:
+        infinity = any(Curve(g).has_vertical_asymptote() or
+                       Curve(g).is_vertical_line() for g in flist1)
+        if not infinity:
+            infinity = all(g.degree(y) == g.degree() for g in flist1)
+    if braid_data:
+        bm, dic, dv, d1 = braid_data
+    elif not flist:
         bm = []
         dic = {}
+        dv = {j: j for j, f in flist1}
+        d1 = 0
     else:
-        bm, dic = braid_monodromy(f, flist1)
-    g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux)
+        bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical)
+    vert_lines = list(dv)
+    vert_lines.sort()
+    for i, j in enumerate(vert_lines):
+        dic[d1 + i] = dv[j]
+    g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False,
+                                         projective=projective,
+                                         puiseux=puiseux,
+                                         vertical=vert_lines)
     if simplified:
         hom = g.simplification_isomorphism()
     else:
@@ -1725,12 +1918,13 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis
         return (g1, {})
     dic1 = {}
     for i in range(len(flist1)):
-        L = [j1 for j1 in dic.keys() if dic[j1] == i]
+        L = [j1 for j1 in dic if dic[j1] == i]
         dic1[i] = [hom(g.gen(j)) for j in L]
-    if not projective and f.degree(y) == f.degree():
-        t = prod(hom(x) for x in g.gens()).inverse()
+    if not projective and infinity:
+        t = prod(hom(a) for a in g.gens()).inverse()
         dic1[len(flist1)] = [t]
     n = g1.ngens()
-    rels = [_.Tietze() for _ in g1.relations()]
+    rels = [rel.Tietze() for rel in g1.relations()]
     g1 = FreeGroup(n) / rels
+    dic1 = {i: list({g1(el.Tietze()) for el in dic1[i]}) for i in dic1}
     return (g1, dic1)
diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py
index 9889808b35d..99e3e8c81fa 100644
--- a/src/sage/schemes/elliptic_curves/ell_point.py
+++ b/src/sage/schemes/elliptic_curves/ell_point.py
@@ -1818,6 +1818,17 @@ def weil_pairing(self, Q, n, algorithm=None):
             sage: z.multiplicative_order()
             360
 
+        Another larger example::
+
+            sage: F = GF(65537^2, modulus=[3,-1,1], name='a')
+            sage: F.inject_variables()
+            Defining a
+            sage: E = EllipticCurve(F, [0,1])
+            sage: P = E(22, 28891)
+            sage: Q = E(-93, 2728*a + 64173)
+            sage: P.weil_pairing(Q, 7282, algorithm='sage')
+            53278*a + 36700
+
         An example over a number field::
 
             sage: # needs sage.rings.number_field
@@ -1833,16 +1844,20 @@ def weil_pairing(self, Q, n, algorithm=None):
 
         TESTS:
 
-        Check that the original Sage implementation still works::
+        Check that the original Sage implementation still works and
+        that the result coincides with the PARI implementation::
 
             sage: # needs sage.rings.finite_rings
             sage: GF(65537^2).inject_variables()
             Defining z2
             sage: E = EllipticCurve(GF(65537^2), [0,1])
-            sage: P = E(22, 28891)
-            sage: Q = E(-93, 40438*z2 + 31573)
-            sage: P.weil_pairing(Q, 7282, algorithm='sage')
-            19937*z2 + 65384
+            sage: R, S = E.torsion_basis(7282)
+            sage: a, b = ZZ.random_element(), ZZ.random_element()
+            sage: P = a*R + b*S
+            sage: c, d = ZZ.random_element(), ZZ.random_element()
+            sage: Q = c*R + d*S
+            sage: P.weil_pairing(Q, 7282, algorithm='sage') == P.weil_pairing(Q, 7282, algorithm='pari')
+            True
 
         Passing an unknown ``algorithm=`` argument should fail::
 
@@ -2047,19 +2062,19 @@ def tate_pairing(self, Q, n, k, q=None):
             sage: Px.weil_pairing(Qx, 41)^e == num/den
             True
 
-        TESTS:
-
-        Check that the PARI output matches the original Sage implementation::
+        An example over a large base field::
 
-            sage: # needs sage.rings.finite_rings
-            sage: GF(65537^2).inject_variables()
+            sage: F = GF(65537^2, modulus=[3,46810,1], name='z2')
+            sage: F.inject_variables()
             Defining z2
-            sage: E = EllipticCurve(GF(65537^2), [0,1])
+            sage: E = EllipticCurve(F, [0,1])
             sage: P = E(22, 28891)
             sage: Q = E(-93, 40438*z2 + 31573)
             sage: P.tate_pairing(Q, 7282, 2)
             34585*z2 + 4063
 
+        TESTS:
+
         The point ``P (self)`` must have ``n`` torsion::
 
             sage: P.tate_pairing(Q, 163, 2)
diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py
index f49ccfbc568..ce77cdca614 100644
--- a/src/sage/schemes/elliptic_curves/ell_rational_field.py
+++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py
@@ -5823,8 +5823,8 @@ def height(self, precision=None):
         c4 = self.c4()
         c6 = self.c6()
         j = self.j_invariant()
-        log_g2 = R((c4/12)).abs().log()
-        log_g3 = R((c6/216)).abs().log()
+        log_g2 = R(c4/12).abs().log()
+        log_g3 = R(c6/216).abs().log()
 
         if j == 0:
             h_j = R(1)
@@ -6348,7 +6348,7 @@ def point_preprocessing(free,tor):
             #new bound according to low_bound and upper bound
             #[c_5 exp((-c_2*H_q^2)/2)] provided by Corollary 8.7.3
             if low_bound != 0:
-                H_q_new = R((log(low_bound/c5)/(-c2/2))).sqrt()
+                H_q_new = R(log(low_bound/c5)/(-c2/2)).sqrt()
                 H_q_new = H_q_new.ceil()
                 if H_q_new == 1:
                     break_cond = 1 # stops reduction
@@ -7025,9 +7025,8 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound):
             else:
                 bound_list.append(H_q)
 
-         ##reduction for finite places in S
-            for p in S:
-                bound_list.append(reduction_at(p))
+            # reduction for finite places in S
+            bound_list.extend(reduction_at(p) for p in S)
 
             if verbose:
                 print('bound_list', bound_list)
diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
index ea9d3bdc2ed..12f565d9c48 100644
--- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
+++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
@@ -739,9 +739,7 @@ def _exceptionals(E, L, patience=1000):
         if (not D) or (patience == 0):
             break
 
-    for l in D:
-        output.append(l)
-
+    output.extend(D)
     output.sort()
     return output
 
diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py
index df91b74b280..f0ad1420bcf 100644
--- a/src/sage/schemes/elliptic_curves/heegner.py
+++ b/src/sage/schemes/elliptic_curves/heegner.py
@@ -7055,7 +7055,7 @@ def _heegner_index_in_EK(self, D):
     basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]
     # Make a list of the 2-power order torsion points in E(K), including 0.
     T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or
-            ((z.order() % 2 == 0 and len(z.order().factor()) == 1))]
+            (z.order() % 2 == 0 and len(z.order().factor()) == 1)]
 
     r = len(basis)   # rank
     V = QQ**r
@@ -7066,9 +7066,8 @@ def _heegner_index_in_EK(self, D):
         if not v:
             continue
         P = sum([basis[i] for i in range(r) if v[i]])
-        for t in T:
-            if (P+t).is_divisible_by(2):
-                B.append(V(v)/2)
+        w = V(v) / 2
+        B.extend(w for t in T if (P + t).is_divisible_by(2))
 
     A = ZZ**r
     # Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r.  This is E(K)/tor.
diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
index 4623828ccc5..b712650bd9b 100644
--- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
+++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
@@ -564,7 +564,7 @@ def _sporadic_Q_data(j):
     w = w1 # real period
     if j in [-121, -24729001, -162677523113838677, QQ(-882216989)/131072]:
         w = 2*w2-w1 # imaginary period
-    kerpol = prod(([X-L.elliptic_exponential(n*w/ell)[0] for n in range(1,(ell+1)//2)]))
+    kerpol = prod([X-L.elliptic_exponential(n*w/ell)[0] for n in range(1,(ell+1)//2)])
     if j == -162677523113838677:
         kerpolcoeffs = [(37*c.real()).round()/37 for c in list(kerpol)]
     else:
@@ -2110,7 +2110,7 @@ def isogenies_prime_degree_genus_plus_0_j0(E, l, minimal_models=True):
         raise ValueError("%s must be one of %s." % (l,hyperelliptic_primes))
     F = E.base_field()
     if E.j_invariant() != 0:
-        raise ValueError(("j-invariant must be 0."))
+        raise ValueError("j-invariant must be 0.")
     if F.characteristic() in [2,3,l]:
         raise NotImplementedError("Not implemented in characteristic 2, 3 or l.")
 
diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py
index 113d7e16104..cd6d034ccdb 100644
--- a/src/sage/schemes/elliptic_curves/padic_lseries.py
+++ b/src/sage/schemes/elliptic_curves/padic_lseries.py
@@ -1554,7 +1554,7 @@ def __phi_bpr(self, prec=0):
         if prec > 10:
             print("Warning: Very large value for the precision.")
         if prec == 0:
-            prec = floor((log(10000)/log(p)))
+            prec = floor(log(10000)/log(p))
             verbose("prec set to %s" % prec)
         eh = E.formal()
         om = eh.differential(prec=p**prec+3)
diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
index 422b28b67fe..878b5a78d82 100644
--- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
+++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
@@ -32,7 +32,7 @@
 from sage.rings.integer import Integer
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
 
-class baseWI():
+class baseWI:
     r"""
     This class implements the basic arithmetic of isomorphisms between
     Weierstrass models of elliptic curves.
diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py
index 3fee46cc15a..2b4e60905df 100644
--- a/src/sage/schemes/product_projective/space.py
+++ b/src/sage/schemes/product_projective/space.py
@@ -256,7 +256,7 @@ def _repr_(self):
             Product of projective spaces P^1 x P^1 x P^1 over Integer Ring
         """
         return ''.join(['Product of projective spaces ',
-                        ' x '.join('P^{0}'.format(d) for d in self._dims),
+                        ' x '.join('P^{}'.format(d) for d in self._dims),
                         ' over ', str(self.base_ring())])
 
     def _repr_generic_point(self, v=None):
@@ -1197,8 +1197,8 @@ def points_of_bounded_height(self, **kwds):
         P = []
         for i in range(m):
             pt = next(iters[i])
-            for j in range(dim[i]):
-                P.append(pt[j]) # initial value of P
+            P.extend(pt[j] for j in range(dim[i]))
+            # initial value of P
         yield self(P)
 
         i = 0
@@ -1248,9 +1248,7 @@ def __iter__(self):
              (1 : 0 : 0 , 1 : 0)]
         """
         iters = [iter(T) for T in self._components]
-        L = []
-        for x in iters:
-            L.append(next(x))  # put at zero
+        L = [next(x) for x in iters]  # put at zero
         yield self(L)
         j = 0
         while j < self.num_components():
diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py
index 6d81b47abc2..a284ecbc585 100644
--- a/src/sage/schemes/projective/projective_morphism.py
+++ b/src/sage/schemes/projective/projective_morphism.py
@@ -1233,9 +1233,8 @@ def dehomogenize(self, n):
             G = phi(self._polys[ind[1]])
             # ind[1] is relative to codomain
             M = self.codomain().ambient_space().dimension_relative()
-            for i in range(0, M + 1):
-                if i != ind[1]:
-                    F.append(phi(self._polys[i]) / G)
+            F.extend(phi(self._polys[i]) / G
+                     for i in range(M + 1) if i != ind[1])
             H = Hom(Aff_domain, self.codomain().affine_patch(ind[1]))
             # since often you dehomogenize at the same coordinate in domain
             # and codomain it should be stored appropriately.
@@ -1720,17 +1719,13 @@ def rational_preimages(self, Q, k=1):
             L2 = []
             for P in L:
                 I = list(self.domain().defining_polynomials())
-                for i in range(N+1):
-                    for j in range(i+1, N+1):
-                        I.append(P[i]*self[j] - P[j]*self[i])
+                I.extend(P[i] * self[j] - P[j] * self[i]
+                         for i in range(N + 1) for j in range(i + 1, N + 1))
                 X = PS.subscheme(I)
                 if X.dimension() > 0:
                     return X
-                preimages = []
-                for T in X.rational_points():
-                    if not all(g(tuple(T)) == 0 for g in self):
-                        preimages.append(PS(T))
-                L2 = L2 + preimages
+                L2.extend(PS(T) for T in X.rational_points()
+                          if not all(g(tuple(T)) == 0 for g in self))
             L = L2
         return L
 
@@ -1770,10 +1765,10 @@ def _number_field_from_algebraics(self):
             Scheme morphism:
               From: Projective Space of dimension 1 over Number Field in a
                     with defining polynomial y^4 + 3*y^2 + 1
-                    with a = 0.?e-113 + 0.618033988749895?*I
+                    with a = 0.?e-151 + 0.618033988749895?*I
               To:   Projective Space of dimension 2 over Number Field in a
                     with defining polynomial y^4 + 3*y^2 + 1
-                    with a = 0.?e-113 + 0.618033988749895?*I
+                    with a = 0.?e-151 + 0.618033988749895?*I
               Defn: Defined on coordinates by sending (x : y) to
                     (x^2 + (a^3 + 2*a)*x*y + 3*y^2 : y^2 : (2*a^2 + 3)*x*y)
 
@@ -2224,8 +2219,8 @@ def reduce_base_field(self):
                         # find the right subfield and it's embedding
                         if M.degree() == da:
                             break
-                    c = M((str(c).replace(c.as_finite_field_element()[0].variable_name(),
-                                          M.variable_name())))
+                    c = M(str(c).replace(c.as_finite_field_element()[0].variable_name(),
+                                          M.variable_name()))
                     new_c.append(M_to_L(c))
                 # reconstruct as a poly in the new domain
                 new_f.append(sum([new_c[i] * prod(new_R.gen(j)**mon_deg[i][j]
@@ -2613,9 +2608,8 @@ def self_with_domain(C):
         polys = list(X.defining_polynomials())
 
         for r in self.representatives():
-            r_proj = r if emb is None else emb*r
-            for p in r_proj:
-                polys.append(p)
+            r_proj = r if emb is None else emb * r
+            polys.extend(r_proj)
 
         return Amb.subscheme(polys).reduce()
 
diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py
index fb6715f5f87..880a4c271f5 100644
--- a/src/sage/schemes/riemann_surfaces/riemann_surface.py
+++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py
@@ -481,7 +481,7 @@ def reparameterize_differential_minpoly(minpoly, z0):
     return mt
 
 
-class RiemannSurface():
+class RiemannSurface:
     r"""
     Construct a Riemann Surface. This is specified by the zeroes of a bivariate
     polynomial with rational coefficients `f(z,w) = 0`.
diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py
index 3a72638ac2d..a80d9a41b85 100644
--- a/src/sage/schemes/toric/divisor.py
+++ b/src/sage/schemes/toric/divisor.py
@@ -346,7 +346,7 @@ def ToricDivisor(toric_variety, arg=None, ring=None, check=True, reduce=True):
     except (AssertionError, TypeError):
         n_rays = toric_variety.fan().nrays()
         assert len(arg) == n_rays, \
-            'Argument list {0} is not of the required length {1}!' \
+            'Argument list {} is not of the required length {}!' \
             .format(arg, n_rays)
         arg = list(zip(arg, toric_variety.gens()))
         reduce = False
diff --git a/src/sage/schemes/toric/morphism.py b/src/sage/schemes/toric/morphism.py
index d5b61a651f1..c22f88e99fb 100644
--- a/src/sage/schemes/toric/morphism.py
+++ b/src/sage/schemes/toric/morphism.py
@@ -1537,12 +1537,11 @@ def fiber_dimension(self, codomain_cone):
         dim = []
         fm = self.fan_morphism()
         base_dim = codomain_cone.dim()
-        for c in fm.primitive_preimage_cones(codomain_cone):
-            dim.append(base_dim - c.dim())
+        dim.extend(base_dim - c.dim()
+                   for c in fm.primitive_preimage_cones(codomain_cone))
         if dim:
             return max(dim) + self.domain().dimension() - self.codomain().dimension()
-        else:
-            return ZZ(-1)
+        return ZZ(-1)
 
     def fiber_graph(self, codomain_cone):
         r"""
diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py
index 20507e5c0af..799606a4d1f 100644
--- a/src/sage/schemes/toric/points.py
+++ b/src/sage/schemes/toric/points.py
@@ -44,7 +44,7 @@
 from sage.parallel.decorate import Parallel
 
 
-class InfinitePointEnumerator():
+class InfinitePointEnumerator:
 
     def __init__(self, fan, ring):
         """
@@ -105,7 +105,7 @@ def __iter__(self):
                 yield tuple(p)
 
 
-class NaiveFinitePointEnumerator():
+class NaiveFinitePointEnumerator:
 
     def __init__(self, fan, ring):
         """
@@ -756,7 +756,7 @@ def cardinality(self):
         return n
 
 
-class NaiveSubschemePointEnumerator():
+class NaiveSubschemePointEnumerator:
 
     def __init__(self, polynomials, ambient):
         """
diff --git a/src/sage/schemes/toric/sheaf/constructor.py b/src/sage/schemes/toric/sheaf/constructor.py
index 72ed52a0115..0fb0c0a34e5 100644
--- a/src/sage/schemes/toric/sheaf/constructor.py
+++ b/src/sage/schemes/toric/sheaf/constructor.py
@@ -138,7 +138,7 @@ def LineBundle(X, D):
     return klyachko.Bundle(X, filtrations, check=True)
 
 
-class SheafLibrary():
+class SheafLibrary:
 
     def __init__(self, toric_variety):
         """
diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py
index aee10319916..6a6a8d3252a 100644
--- a/src/sage/schemes/toric/variety.py
+++ b/src/sage/schemes/toric/variety.py
@@ -2660,7 +2660,7 @@ def _orbit_closure_projection(self, cone, x):
         # TODO: make the following work nicely.
         # if x in cone.lattice():
         # return quot(x)
-        # assert is_Cone(x)
+        # assert x is ConvexRationalPolyhedralCone object
         # return Cone(x.rays(), lattice=quot)
 
     def orbit_closure(self, cone):
diff --git a/src/sage/sets/disjoint_set.pxd b/src/sage/sets/disjoint_set.pxd
index 1ec38f5966b..3c8351983e7 100644
--- a/src/sage/sets/disjoint_set.pxd
+++ b/src/sage/sets/disjoint_set.pxd
@@ -11,13 +11,25 @@
 from sage.groups.perm_gps.partn_ref.data_structures cimport OrbitPartition
 from sage.structure.sage_object cimport SageObject
 
+cpdef DisjointSet(arg)
+
 cdef class DisjointSet_class(SageObject):
     cdef OrbitPartition *_nodes
+    cpdef cardinality(self)
+    cpdef number_of_subsets(self)
 
 cdef class DisjointSet_of_integers(DisjointSet_class):
-    pass
+    cpdef int find(self, int i)
+    cpdef void union(self, int i, int j)
+    cpdef root_to_elements_dict(self)
+    cpdef element_to_root_dict(self)
+    cpdef to_digraph(self)
 
 cdef class DisjointSet_of_hashables(DisjointSet_class):
     cdef list _int_to_el
     cdef dict _el_to_int
-    cdef DisjointSet_of_integers _d
+    cpdef find(self, e)
+    cpdef void union(self, e, f)
+    cpdef root_to_elements_dict(self)
+    cpdef element_to_root_dict(self)
+    cpdef to_digraph(self)
diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx
index e8ae11fc3a1..ce44a37710a 100644
--- a/src/sage/sets/disjoint_set.pyx
+++ b/src/sage/sets/disjoint_set.pyx
@@ -6,7 +6,7 @@ The main entry point is :func:`DisjointSet` which chooses the appropriate
 type to return. For more on the data structure, see :func:`DisjointSet`.
 
 This module defines a class for mutable partitioning of a set, which
-cannot be used as a key of a dictionary, vertex of a graph etc. For
+cannot be used as a key of a dictionary, a vertex of a graph, etc. For
 immutable partitioning see :class:`SetPartition`.
 
 AUTHORS:
@@ -14,6 +14,7 @@ AUTHORS:
 - Sébastien Labbé (2008) - Initial version.
 - Sébastien Labbé (2009-11-24) - Pickling support
 - Sébastien Labbé (2010-01) - Inclusion into sage (:issue:`6775`).
+- Giorgos Mousa (2024-04-22): Optimize
 
 EXAMPLES:
 
@@ -39,9 +40,9 @@ Disjoint set of hashables objects::
     sage: d = DisjointSet('abcde')
     sage: d
     {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
-    sage: d.union('a','b')
-    sage: d.union('b','c')
-    sage: d.union('c','d')
+    sage: d.union('a', 'b')
+    sage: d.union('b', 'c')
+    sage: d.union('c', 'd')
     sage: d
     {{'a', 'b', 'c', 'd'}, {'e'}}
     sage: d.find('c')
@@ -58,15 +59,14 @@ Disjoint set of hashables objects::
 #                  https://www.gnu.org/licenses/
 # ****************************************************************************
 
-from sage.rings.integer import Integer
+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 *
 
-
-def DisjointSet(arg):
+cpdef DisjointSet(arg):
     r"""
-    Constructs a disjoint set where each element of ``arg`` is in its
+    Construct a disjoint set where each element of ``arg`` is in its
     own set. If ``arg`` is an integer, then the disjoint set returned is
     made of the integers from ``0`` to ``arg - 1``.
 
@@ -86,11 +86,11 @@ def DisjointSet(arg):
 
     INPUT:
 
-    - ``arg`` -- non negative integer or an iterable of hashable objects.
+    - ``arg`` -- nonnegative integer or an iterable of hashable objects
 
     EXAMPLES:
 
-    From a non-negative integer::
+    From a nonnegative integer::
 
         sage: DisjointSet(5)
         {{0}, {1}, {2}, {3}, {4}}
@@ -101,7 +101,7 @@ def DisjointSet(arg):
         {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
         sage: DisjointSet(range(6))
         {{0}, {1}, {2}, {3}, {4}, {5}}
-        sage: DisjointSet(['yi',45,'cheval'])
+        sage: DisjointSet(['yi', 45, 'cheval'])
         {{'cheval'}, {'yi'}, {45}}
 
     TESTS::
@@ -113,12 +113,12 @@ def DisjointSet(arg):
         sage: DisjointSet([])
         {}
 
-    The argument must be a non negative integer::
+    The argument must be a nonnegative integer::
 
         sage: DisjointSet(-1)
         Traceback (most recent call last):
         ...
-        ValueError: arg (=-1) must be a non negative integer
+        ValueError: arg must be a nonnegative integer (-1 given)
 
     or an iterable::
 
@@ -136,12 +136,11 @@ def DisjointSet(arg):
     """
     if isinstance(arg, (Integer, int)):
         if arg < 0:
-            raise ValueError('arg (=%s) must be a non negative integer' % arg)
+            raise ValueError('arg must be a nonnegative integer (%s given)' % arg)
         return DisjointSet_of_integers(arg)
     else:
         return DisjointSet_of_hashables(arg)
 
-
 cdef class DisjointSet_class(SageObject):
     r"""
     Common class and methods for :class:`DisjointSet_of_integers` and
@@ -149,24 +148,28 @@ cdef class DisjointSet_class(SageObject):
     """
     def _repr_(self):
         r"""
-        Return ``self`` as a unique str.
+        Return ``self`` as a unique ``str``.
 
         EXAMPLES::
 
             sage: e = DisjointSet(5)
-            sage: e.union(2,4); e._repr_()
+            sage: e.union(2, 4)
+            sage: e._repr_()
             '{{0}, {1}, {2, 4}, {3}}'
             sage: e = DisjointSet(5)
-            sage: e.union(4,2); e._repr_()
+            sage: e.union(4, 2)
+            sage: e._repr_()
             '{{0}, {1}, {2, 4}, {3}}'
 
         ::
 
             sage: e = DisjointSet(range(5))
-            sage: e.union(2,4); e._repr_()
+            sage: e.union(2, 4)
+            sage: e._repr_()
             '{{0}, {1}, {2, 4}, {3}}'
             sage: e = DisjointSet(range(5))
-            sage: e.union(4,2); e._repr_()
+            sage: e.union(4, 2)
+            sage: e._repr_()
             '{{0}, {1}, {2, 4}, {3}}'
         """
         res = []
@@ -183,10 +186,9 @@ cdef class DisjointSet_class(SageObject):
         EXAMPLES::
 
             sage: d = DisjointSet(4)
-            sage: d.union(2,0)
+            sage: d.union(2, 0)
             sage: sorted(d)
             [[0, 2], [1], [3]]
-
             sage: d = DisjointSet('abc')
             sage: sorted(d)
             [['a'], ['b'], ['c']]
@@ -211,10 +213,10 @@ cdef class DisjointSet_class(SageObject):
 
         ::
 
-            sage: d.union(0,3)
-            sage: d.union(3,4)
-            sage: e.union(4,0)
-            sage: e.union(3,0)
+            sage: d.union(0, 3)
+            sage: d.union(3, 4)
+            sage: e.union(4, 0)
+            sage: e.union(3, 0)
             sage: e == d
             True
 
@@ -229,12 +231,12 @@ cdef class DisjointSet_class(SageObject):
 
             sage: d = DisjointSet('abcde')
             sage: e = DisjointSet('abcde')
-            sage: d.union('a','b')
-            sage: d.union('b','c')
-            sage: e.union('c','a')
+            sage: d.union('a', 'b')
+            sage: d.union('b', 'c')
+            sage: e.union('c', 'a')
             sage: e == d
             False
-            sage: e.union('a','b')
+            sage: e.union('a', 'b')
             sage: e == d
             True
         """
@@ -246,7 +248,43 @@ cdef class DisjointSet_class(SageObject):
             return NotImplemented
         return PyObject_RichCompare(s, t, op)
 
-    def cardinality(self):
+    def __dealloc__(self):
+        r"""
+        Deallocate ``self`` (i.e. the ``self._nodes``).
+
+        EXAMPLES::
+
+            sage: d = DisjointSet(5)
+            sage: del d
+            sage: d = DisjointSet('abc')
+            sage: del d
+        """
+        OP_dealloc(self._nodes)
+
+    def __reduce__(self):
+        r"""
+        Return a tuple of three elements:
+
+        - The function :func:`DisjointSet`
+        - Arguments for the function :func:`DisjointSet`
+        - The actual state of ``self``.
+
+        EXAMPLES::
+
+            sage: d = DisjointSet(5)
+            sage: d.__reduce__()
+            (, (5,), [0, 1, 2, 3, 4])
+
+        ::
+
+            sage: d.union(2, 4)
+            sage: d.union(1, 3)
+            sage: d.__reduce__()
+            (, (5,), [0, 1, 2, 1, 2])
+        """
+        return DisjointSet, (self._nodes.degree,), self.__getstate__()
+
+    cpdef cardinality(self):
         r"""
         Return the number of elements in ``self``, *not* the number of subsets.
 
@@ -267,7 +305,7 @@ cdef class DisjointSet_class(SageObject):
         """
         return self._nodes.degree
 
-    def number_of_subsets(self):
+    cpdef number_of_subsets(self):
         r"""
         Return the number of subsets in ``self``.
 
@@ -297,8 +335,8 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         sage: d = DisjointSet(5)
         sage: d
         {{0}, {1}, {2}, {3}, {4}}
-        sage: d.union(2,4)
-        sage: d.union(0,2)
+        sage: d.union(2, 4)
+        sage: d.union(0, 2)
         sage: d
         {{0, 2, 4}, {1}, {3}}
         sage: d.find(2)
@@ -312,18 +350,18 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
 
     ::
 
-        sage: a.union(3,4)
+        sage: a.union(3, 4)
         sage: a == loads(dumps(a))
         True
     """
     def __init__(self, n):
         r"""
-        Construction of the DisjointSet where each element (integers from ``0``
+        Construct the ``DisjointSet`` where each element (integers from ``0``
         to ``n-1``) is in its own set.
 
         INPUT:
 
-        - ``n`` -- Non negative integer
+        - ``n`` -- nonnegative integer
 
         EXAMPLES::
 
@@ -336,40 +374,6 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         """
         self._nodes = OP_new(n)
 
-    def __dealloc__(self):
-        r"""
-        Deallocates self, i.e. the self._nodes
-
-        EXAMPLES::
-
-            sage: d = DisjointSet(5)
-            sage: del d
-        """
-        OP_dealloc(self._nodes)
-
-    def __reduce__(self):
-        r"""
-        Return a tuple of three elements:
-
-        - The function :func:`DisjointSet`
-        - Arguments for the function :func:`DisjointSet`
-        - The actual state of ``self``.
-
-        EXAMPLES::
-
-            sage: d = DisjointSet(5)
-            sage: d.__reduce__()
-            (, (5,), [0, 1, 2, 3, 4])
-
-        ::
-
-            sage: d.union(2,4)
-            sage: d.union(1,3)
-            sage: d.__reduce__()
-            (, (5,), [0, 1, 2, 1, 2])
-        """
-        return DisjointSet, (self._nodes.degree,), self.__getstate__()
-
     def __getstate__(self):
         r"""
         Return a list of the parent of each node from ``0`` to ``n-1``.
@@ -379,28 +383,29 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
             sage: d = DisjointSet(5)
             sage: d.__getstate__()
             [0, 1, 2, 3, 4]
-            sage: d.union(2,3)
+            sage: d.union(2, 3)
             sage: d.__getstate__()
             [0, 1, 2, 2, 4]
-            sage: d.union(3,0)
+            sage: d.union(3, 0)
             sage: d.__getstate__()
             [2, 1, 2, 2, 4]
 
-        Other parents are obtained when the operations are done is a
-        distinct order::
+        Other parents are obtained when the operations are done in a
+        different order::
 
             sage: d = DisjointSet(5)
-            sage: d.union(0,3)
+            sage: d.union(0, 3)
             sage: d.__getstate__()
             [0, 1, 2, 0, 4]
-            sage: d.union(2,0)
+            sage: d.union(2, 0)
             sage: d.__getstate__()
             [0, 1, 0, 0, 4]
         """
-        l = []
+        cdef Py_ssize_t card = self._nodes.degree
+        cdef list l = [None] * card
         cdef int i
-        for i in range(self.cardinality()):
-            l.append(self._nodes.parent[i])
+        for i in range(card):
+            l[i] = self._nodes.parent[i]
         return l
 
     def __setstate__(self, l):
@@ -415,35 +420,36 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         EXAMPLES::
 
             sage: d = DisjointSet(5)
-            sage: d.__setstate__([0,1,2,3,4])
+            sage: d.__setstate__([0, 1, 2, 3, 4])
             sage: d
             {{0}, {1}, {2}, {3}, {4}}
 
         ::
 
             sage: d = DisjointSet(5)
-            sage: d.__setstate__([1,2,3,4,0])
+            sage: d.__setstate__([1, 2, 3, 4, 0])
             sage: d
             {{0, 1, 2, 3, 4}}
 
         ::
 
             sage: d = DisjointSet(5)
-            sage: d.__setstate__([1,1,1])
+            sage: d.__setstate__([1, 1, 1])
             sage: d
             {{0, 1, 2}, {3}, {4}}
 
         ::
 
             sage: d = DisjointSet(5)
-            sage: d.__setstate__([3,3,3])
+            sage: d.__setstate__([3, 3, 3])
             sage: d
             {{0, 1, 2, 3}, {4}}
         """
+        cdef int i, parent
         for i, parent in enumerate(l):
             self.union(parent, i)
 
-    def find(self, int i):
+    cpdef int find(self, int i):
         r"""
         Return the representative of the set that ``i`` currently belongs to.
 
@@ -454,41 +460,46 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         EXAMPLES::
 
             sage: e = DisjointSet(5)
-            sage: e.union(4,2)
+            sage: e.union(4, 2)
             sage: e
             {{0}, {1}, {2, 4}, {3}}
             sage: e.find(2)
             4
             sage: e.find(4)
             4
-            sage: e.union(1,3)
+            sage: e.union(1, 3)
             sage: e
             {{0}, {1, 3}, {2, 4}}
             sage: e.find(1)
             1
             sage: e.find(3)
             1
-            sage: e.union(3,2)
+            sage: e.union(3, 2)
             sage: e
             {{0}, {1, 2, 3, 4}}
             sage: [e.find(i) for i in range(5)]
             [0, 1, 1, 1, 1]
-            sage: e.find(5)
-            Traceback (most recent call last):
+            sage: e.find(2**10)
+            ValueError: i must be between 0 and 4 (1024 given)
             ...
-            ValueError: i(=5) must be between 0 and 4
+
+        .. NOTE::
+
+            This method performs input checks. To avoid them you may directly
+            use :meth:`~sage.groups.perm_gps.partn_ref.data_structures.OP_find`.
         """
-        card = self.cardinality()
-        if i < 0 or i>= card:
-            raise ValueError('i(=%s) must be between 0 and %s' % (i, card - 1))
+        card = self._nodes.degree
+        if i < 0 or i >= card:
+            raise ValueError('i must be between 0 and %s (%s given)' % (card - 1, i))
         return OP_find(self._nodes, i)
 
-    def union(self, int i, int j):
+    cpdef void union(self, int i, int j):
         r"""
         Combine the set of ``i`` and the set of ``j`` into one.
 
         All elements in those two sets will share the same representative
-        that can be gotten using find.
+        that can be retrieved using
+        :meth:`~sage.sets.disjoint_set.DisjointSet_of_integers.find`.
 
         INPUT:
 
@@ -500,28 +511,32 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
             sage: d = DisjointSet(5)
             sage: d
             {{0}, {1}, {2}, {3}, {4}}
-            sage: d.union(0,1)
+            sage: d.union(0, 1)
             sage: d
             {{0, 1}, {2}, {3}, {4}}
-            sage: d.union(2,4)
+            sage: d.union(2, 4)
             sage: d
             {{0, 1}, {2, 4}, {3}}
-            sage: d.union(1,4)
+            sage: d.union(1, 4)
             sage: d
             {{0, 1, 2, 4}, {3}}
-            sage: d.union(1,5)
-            Traceback (most recent call last):
+            sage: d.union(1, 5)
+            ValueError: j must be between 0 and 4 (5 given)
             ...
-            ValueError: j(=5) must be between 0 and 4
+
+        .. NOTE::
+
+            This method performs input checks. To avoid them you may directly
+            use :meth:`~sage.groups.perm_gps.partn_ref.data_structures.OP_join`.
         """
         cdef int card = self._nodes.degree
         if i < 0 or i >= card:
-            raise ValueError('i(=%s) must be between 0 and %s' % (i, card - 1))
+            raise ValueError('i must be between 0 and %s (%s given)' % (card - 1, i))
         if j < 0 or j >= card:
-            raise ValueError('j(=%s) must be between 0 and %s' % (j, card - 1))
+            raise ValueError('j must be between 0 and %s (%s given)' % (card - 1, j))
         OP_join(self._nodes, i, j)
 
-    def root_to_elements_dict(self):
+    cpdef root_to_elements_dict(self):
         r"""
         Return the dictionary where the keys are the roots of ``self`` and the
         values are the elements in the same set as the root.
@@ -531,25 +546,25 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
             sage: d = DisjointSet(5)
             sage: sorted(d.root_to_elements_dict().items())
             [(0, [0]), (1, [1]), (2, [2]), (3, [3]), (4, [4])]
-            sage: d.union(2,3)
+            sage: d.union(2, 3)
             sage: sorted(d.root_to_elements_dict().items())
             [(0, [0]), (1, [1]), (2, [2, 3]), (4, [4])]
-            sage: d.union(3,0)
+            sage: d.union(3, 0)
             sage: sorted(d.root_to_elements_dict().items())
             [(1, [1]), (2, [0, 2, 3]), (4, [4])]
             sage: d
             {{0, 2, 3}, {1}, {4}}
         """
-        s = {}
-        cdef int i
-        for i in range(self.cardinality()):
-            o = self.find(i)
+        cdef dict s = {}
+        cdef int i, o
+        for i in range(self._nodes.degree):
+            o = OP_find(self._nodes, i)
             if o not in s:
                 s[o] = []
             s[o].append(i)
         return s
 
-    def element_to_root_dict(self):
+    cpdef element_to_root_dict(self):
         r"""
         Return the dictionary where the keys are the elements of ``self`` and
         the values are their representative inside a list.
@@ -557,30 +572,31 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         EXAMPLES::
 
             sage: d = DisjointSet(5)
-            sage: d.union(2,3)
-            sage: d.union(4,1)
-            sage: e = d.element_to_root_dict(); e
+            sage: d.union(2, 3)
+            sage: d.union(4, 1)
+            sage: e = d.element_to_root_dict()
+            sage: e
             {0: 0, 1: 4, 2: 2, 3: 2, 4: 4}
             sage: WordMorphism(e)                                                       # needs sage.combinat
             WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4
         """
-        d = {}
+        cdef dict d = {}
         cdef int i
-        for i in range(self.cardinality()):
-            d[i] = self.find(i)
+        for i in range(self._nodes.degree):
+            d[i] = OP_find(self._nodes, i)
         return d
 
-    def to_digraph(self):
+    cpdef to_digraph(self):
         r"""
-        Return the current digraph of ``self`` where `(a,b)` is an oriented
+        Return the current digraph of ``self`` where `(a, b)` is an oriented
         edge if `b` is the parent of `a`.
 
         EXAMPLES::
 
             sage: d = DisjointSet(5)
-            sage: d.union(2,3)
-            sage: d.union(4,1)
-            sage: d.union(3,4)
+            sage: d.union(2, 3)
+            sage: d.union(4, 1)
+            sage: d.union(3, 4)
             sage: d
             {{0}, {1, 2, 3, 4}}
             sage: g = d.to_digraph(); g                                                 # needs sage.graphs
@@ -591,15 +607,15 @@ cdef class DisjointSet_of_integers(DisjointSet_class):
         The result depends on the ordering of the union::
 
             sage: d = DisjointSet(5)
-            sage: d.union(1,2)
-            sage: d.union(1,3)
-            sage: d.union(1,4)
+            sage: d.union(1, 2)
+            sage: d.union(1, 3)
+            sage: d.union(1, 4)
             sage: d
             {{0}, {1, 2, 3, 4}}
             sage: d.to_digraph().edges(sort=True)                                       # needs sage.graphs
             [(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)]
         """
-        d = {i: [self._nodes.parent[i]] for i in range(self.cardinality())}
+        cdef dict d = {i: [self._nodes.parent[i]] for i in range(self._nodes.degree)}
         from sage.graphs.digraph import DiGraph
         return DiGraph(d)
 
@@ -626,18 +642,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
 
     ::
 
-        sage: a.union('a','c')
+        sage: a.union('a', 'c')
         sage: a == loads(dumps(a))
         True
     """
     def __init__(self, iterable):
         r"""
-        Construction of the trivial disjoint set where each element is in its
-        own set.
+        Construct the trivial disjoint set where each element is in its own set.
 
         INPUT:
 
-        - ``iterable`` -- An iterable of hashable objects.
+        - ``iterable`` -- iterable of hashable objects
 
         EXAMPLES::
 
@@ -645,18 +660,18 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
             sage: DisjointSet(range(6))
             {{0}, {1}, {2}, {3}, {4}, {5}}
-            sage: DisjointSet(['yi',45,'cheval'])
+            sage: DisjointSet(['yi', 45, 'cheval'])
             {{'cheval'}, {'yi'}, {45}}
             sage: DisjointSet(set([0, 1, 2, 3, 4]))
             {{0}, {1}, {2}, {3}, {4}}
         """
+        cdef int i
         self._int_to_el = []
         self._el_to_int = {}
         for i, e in enumerate(iterable):
             self._int_to_el.append(e)
             self._el_to_int[e] = i
-        self._d = DisjointSet_of_integers(len(self._int_to_el))
-        self._nodes = self._d._nodes
+        self._nodes = OP_new(len(self._int_to_el))
 
     def __reduce__(self):
         r"""
@@ -678,8 +693,8 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
 
          ::
 
-            sage: d.union(2,4)
-            sage: d.union(1,3)
+            sage: d.union(2, 4)
+            sage: d.union(1, 3)
             sage: d.__reduce__()
             (,
              ([0, 1, 2, 3, 4],),
@@ -696,32 +711,32 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             sage: d = DisjointSet('abcde')
             sage: d.__getstate__()
             [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e')]
-            sage: d.union('c','d')
+            sage: d.union('c', 'd')
             sage: d.__getstate__()
             [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'c'), ('e', 'e')]
-            sage: d.union('d','a')
+            sage: d.union('d', 'a')
             sage: d.__getstate__()
             [('a', 'c'), ('b', 'b'), ('c', 'c'), ('d', 'c'), ('e', 'e')]
 
-        Other parents are obtained when the operations are done is a
+        Other parents are obtained when the operations are done in a
         different order::
 
             sage: d = DisjointSet('abcde')
-            sage: d.union('d','c')
+            sage: d.union('d', 'c')
             sage: d.__getstate__()
             [('a', 'a'), ('b', 'b'), ('c', 'd'), ('d', 'd'), ('e', 'e')]
         """
-        gs = self._d.__getstate__()
-        l = []
+        cdef int card = self._nodes.degree
+        cdef list l = [None] * card
         cdef int i
-        for i in range(self.cardinality()):
-            l.append(self._int_to_el[gs[i]])
+        for i in range(card):
+            l[i] = self._int_to_el[self._nodes.parent[i]]
         return list(zip(self._int_to_el, l))
 
     def __setstate__(self, l):
         r"""
         Merge the nodes ``a`` and ``b`` for each pair of nodes
-        ``(a,b)`` in ``l``.
+        ``(a, b)`` in ``l``.
 
         INPUT:
 
@@ -730,21 +745,21 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
         EXAMPLES::
 
             sage: d = DisjointSet('abcde')
-            sage: d.__setstate__([('a','a'),('b','b'),('c','c')])
+            sage: d.__setstate__([('a', 'a'), ('b', 'b'), ('c', 'c')])
             sage: d
             {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
 
         ::
 
             sage: d = DisjointSet('abcde')
-            sage: d.__setstate__([('a','b'),('b','c'),('c','d'),('d','e')])
+            sage: d.__setstate__([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')])
             sage: d
             {{'a', 'b', 'c', 'd', 'e'}}
         """
         for a, b in l:
             self.union(a, b)
 
-    def find(self, e):
+    cpdef find(self, e):
         r"""
         Return the representative of the set that ``e`` currently belongs to.
 
@@ -755,7 +770,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
         EXAMPLES::
 
             sage: e = DisjointSet(range(5))
-            sage: e.union(4,2)
+            sage: e.union(4, 2)
             sage: e
             {{0}, {1}, {2, 4}, {3}}
             sage: e.find(2)
@@ -769,7 +784,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             1
             sage: e.find(3)
             1
-            sage: e.union(3,2)
+            sage: e.union(3, 2)
             sage: e
             {{0}, {1, 2, 3, 4}}
             sage: [e.find(i) for i in range(5)]
@@ -779,16 +794,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             ...
             KeyError: 5
         """
-        i = self._el_to_int[e]
-        r = self._d.find(i)
+        cdef int i =  self._el_to_int[e]
+        cdef int r =  OP_find(self._nodes, i)
         return self._int_to_el[r]
 
-    def union(self, e, f):
+    cpdef void union(self, e, f):
         r"""
         Combine the set of ``e`` and the set of ``f`` into one.
 
         All elements in those two sets will share the same representative
-        that can be gotten using find.
+        that can be retrieved using
+        :meth:`~sage.sets.disjoint_set.DisjointSet_of_hashables.find`.
 
         INPUT:
 
@@ -800,21 +816,24 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             sage: e = DisjointSet('abcde')
             sage: e
             {{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
-            sage: e.union('a','b')
+            sage: e.union('a', 'b')
             sage: e
             {{'a', 'b'}, {'c'}, {'d'}, {'e'}}
-            sage: e.union('c','e')
+            sage: e.union('c', 'e')
             sage: e
             {{'a', 'b'}, {'c', 'e'}, {'d'}}
-            sage: e.union('b','e')
+            sage: e.union('b', 'e')
             sage: e
             {{'a', 'b', 'c', 'e'}, {'d'}}
+            sage: e.union('a', 2**10)
+            KeyError: 1024
+            ...
         """
-        i = self._el_to_int[e]
-        j = self._el_to_int[f]
-        self._d.union(i, j)
+        cdef int i =  self._el_to_int[e]
+        cdef int j =  self._el_to_int[f]
+        OP_join(self._nodes, i, j)
 
-    def root_to_elements_dict(self):
+    cpdef root_to_elements_dict(self):
         r"""
         Return the dictionary where the keys are the roots of ``self`` and the
         values are the elements in the same set.
@@ -822,13 +841,13 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
         EXAMPLES::
 
             sage: d = DisjointSet(range(5))
-            sage: d.union(2,3)
-            sage: d.union(4,1)
+            sage: d.union(2, 3)
+            sage: d.union(4, 1)
             sage: e = d.root_to_elements_dict()
             sage: sorted(e.items())
             [(0, [0]), (2, [2, 3]), (4, [1, 4])]
         """
-        s = {}
+        cdef dict s = {}
         for e in self._int_to_el:
             r = self.find(e)
             if r not in s:
@@ -836,7 +855,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
             s[r].append(e)
         return s
 
-    def element_to_root_dict(self):
+    cpdef element_to_root_dict(self):
         r"""
         Return the dictionary where the keys are the elements of ``self`` and
         the values are their representative inside a list.
@@ -844,33 +863,34 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
         EXAMPLES::
 
             sage: d = DisjointSet(range(5))
-            sage: d.union(2,3)
-            sage: d.union(4,1)
+            sage: d.union(2, 3)
+            sage: d.union(4, 1)
             sage: e = d.element_to_root_dict()
             sage: sorted(e.items())
             [(0, 0), (1, 4), (2, 2), (3, 2), (4, 4)]
             sage: WordMorphism(e)                                                       # needs sage.combinat
             WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4
         """
-        d = {}
+        cdef dict d = {}
         for a in self._int_to_el:
             d[a] = self.find(a)
         return d
 
-    def to_digraph(self):
+    cpdef to_digraph(self):
         r"""
-        Return the current digraph of ``self`` where `(a,b)` is an oriented
+        Return the current digraph of ``self`` where `(a, b)` is an oriented
         edge if `b` is the parent of `a`.
 
         EXAMPLES::
 
             sage: d = DisjointSet(range(5))
-            sage: d.union(2,3)
-            sage: d.union(4,1)
-            sage: d.union(3,4)
+            sage: d.union(2, 3)
+            sage: d.union(4, 1)
+            sage: d.union(3, 4)
             sage: d
             {{0}, {1, 2, 3, 4}}
-            sage: g = d.to_digraph(); g                                                 # needs sage.graphs
+            sage: g = d.to_digraph()
+            sage: g                                                                     # needs sage.graphs
             Looped digraph on 5 vertices
             sage: g.edges(sort=True)                                                    # needs sage.graphs
             [(0, 0, None), (1, 2, None), (2, 2, None), (3, 2, None), (4, 2, None)]
@@ -878,16 +898,17 @@ cdef class DisjointSet_of_hashables(DisjointSet_class):
         The result depends on the ordering of the union::
 
             sage: d = DisjointSet(range(5))
-            sage: d.union(1,2)
-            sage: d.union(1,3)
-            sage: d.union(1,4)
+            sage: d.union(1, 2)
+            sage: d.union(1, 3)
+            sage: d.union(1, 4)
             sage: d
             {{0}, {1, 2, 3, 4}}
             sage: d.to_digraph().edges(sort=True)                                       # needs sage.graphs
             [(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)]
         """
-        d = {}
-        for i in range(self.cardinality()):
+        cdef dict d = {}
+        cdef int i
+        for i in range(self._nodes.degree):
             e = self._int_to_el[i]
             p = self._int_to_el[self._nodes.parent[i]]
             d[e] = [p]
diff --git a/src/sage/sets/family.pyx b/src/sage/sets/family.pyx
index 36a6b0d6b1f..9579c7caab6 100644
--- a/src/sage/sets/family.pyx
+++ b/src/sage/sets/family.pyx
@@ -55,8 +55,6 @@ from sage.rings.integer import Integer
 from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
 from sage.sets.non_negative_integers import NonNegativeIntegers
 
-CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass')
-
 
 def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None):
     r"""
@@ -971,7 +969,7 @@ class LazyFamily(AbstractFamily):
             category = FiniteEnumeratedSets()
         elif set in InfiniteEnumeratedSets():
             category = InfiniteEnumeratedSets()
-        elif isinstance(set, (list, tuple, range, CombinatorialClass)):
+        elif isinstance(set, (list, tuple, range)):
             category = FiniteEnumeratedSets()
         else:
             category = EnumeratedSets()
diff --git a/src/sage/sets/image_set.py b/src/sage/sets/image_set.py
index 0472d793c19..c98fc8c534b 100644
--- a/src/sage/sets/image_set.py
+++ b/src/sage/sets/image_set.py
@@ -7,7 +7,7 @@
 #                     2012      Christian Stump
 #                     2020-2021 Frédéric Chapoton
 #                     2021      Travis Scrimshaw
-#                     2021      Matthias Koeppe
+#                     2021-2024 Matthias Koeppe
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -131,6 +131,64 @@ def map(arg):
         self._domain_subset = domain_subset
         self._is_injective = is_injective
 
+    def __eq__(self, other):
+        r"""
+        EXAMPLES::
+
+            sage: from sage.sets.image_set import ImageSubobject
+            sage: D = ZZ
+            sage: def f(x):
+            ....:     return 2 * x
+            sage: I = ImageSubobject(f, ZZ)
+            sage: I == ImageSubobject(f, ZZ)
+            True
+
+        This method does not take into account whether an inverse is provided,
+        injectivity is declared, or the category::
+
+            sage: def f_inv(y):
+            ....:     return y // 2
+            sage: I == ImageSubobject(f, ZZ, inverse=f_inv)
+            True
+            sage: I == ImageSubobject(f, ZZ, is_injective=True)
+            True
+            sage: I.category()
+            Category of enumerated subobjects of sets
+            sage: I == ImageSubobject(f, ZZ, category=EnumeratedSets().Infinite())
+            True
+        """
+        if not isinstance(other, ImageSubobject):
+            return False
+        return (self._map == other._map
+                and self._domain_subset == other._domain_subset)
+
+    def __ne__(self, other):
+        r"""
+        EXAMPLES::
+
+            sage: from sage.sets.image_set import ImageSubobject
+            sage: D = ZZ
+            sage: def f(x):
+            ....:     return 2 * x
+            sage: I = ImageSubobject(f, ZZ)
+            sage: I != ImageSubobject(f, QQ)
+            True
+        """
+        return not (self == other)
+
+    def __hash__(self):
+        r"""
+        TESTS::
+
+            sage: from sage.sets.image_set import ImageSubobject
+            sage: def f(x):
+            ....:     return 2 * x
+            sage: I = ImageSubobject(f, ZZ)
+            sage: hash(I) == hash(ImageSubobject(f, ZZ))
+            True
+        """
+        return hash((self._map, self._domain_subset))
+
     def _element_constructor_(self, x):
         """
         EXAMPLES::
diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx
index 56bd8d16850..183d52b8c4a 100644
--- a/src/sage/sets/recursively_enumerated_set.pyx
+++ b/src/sage/sets/recursively_enumerated_set.pyx
@@ -209,7 +209,7 @@ or even::
 We can then create the :class:`RecursivelyEnumeratedSet` object with either::
 
     sage: S = RecursivelyEnumeratedSet([''],
-    ....:     lambda x: [x+letter for letter in ['a', 'b', 'c']]
+    ....:     lambda x: [x + letter for letter in ['a', 'b', 'c']]
     ....:               if len(x) < 2 else [],
     ....:     structure='forest', enumeration='depth',
     ....:     category=FiniteEnumeratedSets())
@@ -248,8 +248,8 @@ convention is that the generated elements are the ``s := f(n)``, except when
     ....:     st = set(st) # make a copy
     ....:     if st:
     ....:        el = st.pop()
-    ....:        for i in range(len(lst)+1):
-    ....:            yield (lst[0:i]+[el]+lst[i:], st)
+    ....:        for i in range(len(lst) + 1):
+    ....:            yield (lst[0:i] + [el] + lst[i:], st)
     sage: list(children(([1,2], {3,7,9})))
     [([9, 1, 2], {3, 7}), ([1, 9, 2], {3, 7}), ([1, 2, 9], {3, 7})]
     sage: def post_process(node):
@@ -347,8 +347,7 @@ def RecursivelyEnumeratedSet(seeds, successors, structure=None,
     A recursive set with a forest structure::
 
         sage: f = lambda a: [2*a,2*a+1]
-        sage: C = RecursivelyEnumeratedSet([1], f, structure='forest')
-        sage: C
+        sage: C = RecursivelyEnumeratedSet([1], f, structure='forest'); C
         An enumerated set with a forest structure
         sage: it = C.depth_first_search_iterator()
         sage: [next(it) for _ in range(7)]
@@ -676,7 +675,7 @@ cdef class RecursivelyEnumeratedSet_generic(Parent):
 
         EXAMPLES::
 
-            sage: R = RecursivelyEnumeratedSet([1], lambda x: [x+1, x-1])
+            sage: R = RecursivelyEnumeratedSet([1], lambda x: [x + 1, x - 1])
             sage: R.seeds()
             [1]
         """
@@ -1125,7 +1124,7 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic):
             sage: # needs sage.symbolic
             sage: def f(a):
             ....:     sleep(0.05r)
-            ....:     return [a-1,a+1]
+            ....:     return [a - 1, a + 1]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='symmetric')
             sage: it = C.graded_component_iterator()
             sage: next(it)
@@ -1185,7 +1184,7 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic):
 
             sage: def f(a):
             ....:    sleep(0.1r)
-            ....:    return [a-1,a+1]
+            ....:    return [a - 1, a + 1]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='symmetric')
             sage: from cysignals.alarm import alarm
             sage: alarm(0.45); C.graded_component(10)
@@ -1410,7 +1409,7 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic):
             sage: # needs sage.symbolic
             sage: def f(a):
             ....:    sleep(0.1r)
-            ....:    return [a+1, a+I]
+            ....:    return [a + 1, a + I]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='graded')
             sage: from cysignals.alarm import alarm
             sage: alarm(0.45); C.graded_component(10)
@@ -1481,7 +1480,7 @@ def _imap_and_filter_none(function, iterable):
         sage: p = _imap_and_filter_none(lambda x: x if is_prime(x) else None, range(15))
         sage: [next(p), next(p), next(p), next(p), next(p), next(p)]
         [2, 3, 5, 7, 11, 13]
-        sage: p = _imap_and_filter_none(lambda x: x+x, ['a','b','c','d','e'])
+        sage: p = _imap_and_filter_none(lambda x: x + x, ['a','b','c','d','e'])
         sage: [next(p), next(p), next(p), next(p), next(p)]
         ['aa', 'bb', 'cc', 'dd', 'ee']
     """
@@ -1509,7 +1508,7 @@ def search_forest_iterator(roots, children, algorithm='depth'):
     three, and enumerate its nodes::
 
         sage: from sage.sets.recursively_enumerated_set import search_forest_iterator
-        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
+        sage: list(search_forest_iterator([[]], lambda l: [l + [0], l + [1]]
         ....:                                   if len(l) < 3 else []))
         [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0],
          [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
@@ -1517,7 +1516,7 @@ def search_forest_iterator(roots, children, algorithm='depth'):
     By default, the nodes are iterated through by depth first search.
     We can instead use a breadth first search (increasing depth)::
 
-        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
+        sage: list(search_forest_iterator([[]], lambda l: [l + [0], l + [1]]
         ....:                                   if len(l) < 3 else [],
         ....:                             algorithm='breadth'))
         [[],
@@ -1528,7 +1527,8 @@ def search_forest_iterator(roots, children, algorithm='depth'):
 
     This allows for iterating through trees of infinite depth::
 
-        sage: it = search_forest_iterator([[]], lambda l: [l+[0], l+[1]], algorithm='breadth')
+        sage: it = search_forest_iterator([[]], lambda l: [l + [0], l + [1]],
+        ....:                             algorithm='breadth')
         sage: [ next(it) for i in range(16) ]
         [[],
          [0], [1], [0, 0], [0, 1], [1, 0], [1, 1],
@@ -1607,7 +1607,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
         sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
         sage: S = RecursivelyEnumeratedSet_forest( [[]],
-        ....:     lambda l: [l+[0], l+[1]] if len(l) < 3 else [],
+        ....:     lambda l: [l + [0], l + [1]] if len(l) < 3 else [],
         ....:     category=FiniteEnumeratedSets())
         sage: S.list()
         [[],
@@ -1691,9 +1691,10 @@ class RecursivelyEnumeratedSet_forest(Parent):
         sage: class A(UniqueRepresentation, RecursivelyEnumeratedSet_forest):
         ....:     def __init__(self):
         ....:         RecursivelyEnumeratedSet_forest.__init__(self, [()],
-        ....:             lambda x : [x+(0,), x+(1,)] if sum(x) < 3 else [],
-        ....:             lambda x : sum(x[i]*2^i for i in range(len(x))) if sum(x) != 0 and x[-1] != 0 else None,
-        ....:             algorithm = 'breadth',
+        ....:             lambda x: [x + (0,), x + (1,)] if sum(x) < 3 else [],
+        ....:             lambda x: sum(x[i]*2^i for i in range(len(x)))
+        ....:                           if sum(x) != 0 and x[-1] != 0 else None,
+        ....:             algorithm='breadth',
         ....:             category=InfiniteEnumeratedSets())
         sage: MyForest = A(); MyForest
         An enumerated set with a forest structure
@@ -1701,7 +1702,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         Category of infinite enumerated sets
         sage: p = iter(MyForest)
         sage: [next(p) for i in range(30)]
-        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
+        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24,
+         20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
 
     An alternative approach is to implement ``roots`` and ``children``
     as methods of the subclass (in fact they could also be attributes
@@ -1714,13 +1716,13 @@ class RecursivelyEnumeratedSet_forest(Parent):
         sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
         sage: class A(UniqueRepresentation, RecursivelyEnumeratedSet_forest):
         ....:     def __init__(self):
-        ....:         RecursivelyEnumeratedSet_forest.__init__(self, algorithm = 'breadth',
+        ....:         RecursivelyEnumeratedSet_forest.__init__(self, algorithm='breadth',
         ....:                               category=InfiniteEnumeratedSets())
         ....:     def roots(self):
         ....:         return [()]
         ....:     def children(self, x):
         ....:         if sum(x) < 3:
-        ....:             return [x+(0,), x+(1,)]
+        ....:             return [x + (0,), x + (1,)]
         ....:         else:
         ....:             return []
         ....:     def post_process(self, x):
@@ -1734,7 +1736,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         Category of infinite enumerated sets
         sage: p = iter(MyForest)
         sage: [next(p) for i in range(30)]
-        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
+        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24,
+         20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
 
     .. warning::
 
@@ -1743,8 +1746,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         anonymous or interactively defined functions::
 
             sage: def children(x):
-            ....:     return [x+1]
-            sage: S = RecursivelyEnumeratedSet_forest( [1], children, category=InfiniteEnumeratedSets())
+            ....:     return [x + 1]
+            sage: S = RecursivelyEnumeratedSet_forest([1], children, category=InfiniteEnumeratedSets())
             sage: dumps(S)
             Traceback (most recent call last):
             ...
@@ -1754,7 +1757,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: import __main__
             sage: __main__.children = children
-            sage: S = RecursivelyEnumeratedSet_forest( [1], children, category=InfiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([1], children, category=InfiniteEnumeratedSets())
             sage: loads(dumps(S))
             An enumerated set with a forest structure
     """
@@ -1764,7 +1767,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
         TESTS::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest(NN, lambda x : [], lambda x: x^2 if x.is_prime() else None)
+            sage: S = RecursivelyEnumeratedSet_forest(NN, lambda x: [], lambda x: x^2 if x.is_prime() else None)
             sage: S.category()
             Category of enumerated sets
         """
@@ -1837,7 +1840,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
             sage: def children(l):
-            ....:      return [l+[0], l+[1]]
+            ....:      return [l + [0], l + [1]]
             sage: C = RecursivelyEnumeratedSet_forest(([],), children)
             sage: f = C.__iter__()
             sage: next(f)
@@ -1862,9 +1865,10 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
             sage: f = RecursivelyEnumeratedSet_forest([[]],
-            ....:                  lambda l: [l+[0], l+[1]] if len(l) < 3 else [])
+            ....:                  lambda l: [l + [0], l + [1]] if len(l) < 3 else [])
             sage: list(f.depth_first_search_iterator())
-            [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
+            [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1],
+                 [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
         """
         return iter(self)
 
@@ -1963,7 +1967,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         EXAMPLES::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest( [[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], category=FiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([[]], lambda l: [l + [0], l + [1]] if len(l) < 3 else [],
+            ....:                                     category=FiniteEnumeratedSets())
             sage: [4] in S
             False
             sage: [1] in S
@@ -1972,12 +1977,12 @@ class RecursivelyEnumeratedSet_forest(Parent):
             False
             sage: all(S.__contains__(i) for i in iter(S))
             True
-            sage: S = RecursivelyEnumeratedSet_forest([1], lambda x: [x+1], category=InfiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([1], lambda x: [x + 1], category=InfiniteEnumeratedSets())
             sage: 1 in S
             True
             sage: 732 in S
             True
-            sage: -1 in S # not tested : Will never stop
+            sage: -1 in S  # not tested : Will never stop
 
         The algorithm uses a random enumeration of the nodes of the
         forest. This choice was motivated by examples in which both
@@ -1987,8 +1992,9 @@ class RecursivelyEnumeratedSet_forest(Parent):
         root has an infinite number of children::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x : (x, 0)),
-            ....: lambda x : Family(PositiveIntegers(), lambda y : (x[0], y)) if x[1] == 0 else [])
+            sage: S = RecursivelyEnumeratedSet_forest(
+            ....:         Family(NN, lambda x: (x, 0)),
+            ....:         lambda x: Family(PositiveIntegers(), lambda y: (x[0], y)) if x[1] == 0 else [])
             sage: p = S.depth_first_search_iterator()
             sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)]
             [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
@@ -2010,7 +2016,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         child. From each root starts an infinite branch of breadth
         `1`::
 
-            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x : (x, 0)) , lambda x : [(x[0], x[1]+1)])
+            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x: (x, 0)),
+            ....:                                     lambda x: [(x[0], x[1] + 1)])
             sage: p = S.depth_first_search_iterator()
             sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)]
             [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
@@ -2065,12 +2072,12 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
         EXAMPLES::
 
-            sage: seeds = [([i],i, i) for i in range(1,10)]
+            sage: seeds = [([i], i, i) for i in range(1, 10)]
             sage: def succ(t):
             ....:     list, sum, last = t
             ....:     return [(list + [i], sum + i, i) for i in range(1, last)]
             sage: F = RecursivelyEnumeratedSet(seeds, succ,
-            ....:                       structure='forest', enumeration='depth')
+            ....:         structure='forest', enumeration='depth')
 
             sage: # needs sage.symbolic
             sage: y = var('y')
diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py
index fe351298d0a..bdabfee5098 100644
--- a/src/sage/sets/set.py
+++ b/src/sage/sets/set.py
@@ -1720,7 +1720,9 @@ def __init__(self, X, Y, category=None):
             sage: S = Set(QQ)
             sage: T = Set(ZZ)
             sage: X = S.difference(T); X
-            Set-theoretic difference of Set of elements of Rational Field and Set of elements of Integer Ring
+            Set-theoretic difference of
+             Set of elements of Rational Field and
+             Set of elements of Integer Ring
             sage: X.category()
             Category of sets
             sage: latex(X)
diff --git a/src/sage/sets/totally_ordered_finite_set.py b/src/sage/sets/totally_ordered_finite_set.py
index 7efba359dc0..9191992c285 100644
--- a/src/sage/sets/totally_ordered_finite_set.py
+++ b/src/sage/sets/totally_ordered_finite_set.py
@@ -45,7 +45,7 @@ def __init__(self, parent, data):
         r"""
         TESTS::
 
-            sage: T = TotallyOrderedFiniteSet([3,2,1],facade=False)
+            sage: T = TotallyOrderedFiniteSet([3,2,1], facade=False)
             sage: TestSuite(T.an_element()).run()
         """
         Element.__init__(self, parent)
diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx
index 48cee10c2bc..f7e8c4f6d11 100644
--- a/src/sage/structure/category_object.pyx
+++ b/src/sage/structure/category_object.pyx
@@ -64,11 +64,13 @@ from sage.structure.dynamic_class import DynamicMetaclass
 
 
 cpdef inline check_default_category(default_category, category):
-    ## The resulting category is guaranteed to be
-    ## a sub-category of the default.
+    """
+    The resulting category is guaranteed to be
+    a sub-category of the default.
+    """
     if category is None:
         return default_category
-    return default_category.join([default_category,category])
+    return default_category.join([default_category, category])
 
 
 cdef class CategoryObject(SageObject):
@@ -212,6 +214,7 @@ cdef class CategoryObject(SageObject):
             sage: ZZ.categories()
             [Join of Category of Dedekind domains
                  and Category of euclidean domains
+                 and Category of noetherian rings
                  and Category of infinite enumerated sets
                  and Category of metric spaces,
              Category of Dedekind domains,
@@ -220,7 +223,7 @@ cdef class CategoryObject(SageObject):
              Category of unique factorization domains,
              Category of gcd domains,
              Category of integral domains,
-             Category of domains,
+             Category of domains, ...
              Category of commutative rings, ...
              Category of monoids, ...,
              Category of commutative additive groups, ...,
diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx
index 888643314f5..9f1819efdc0 100644
--- a/src/sage/structure/coerce.pyx
+++ b/src/sage/structure/coerce.pyx
@@ -491,7 +491,7 @@ cpdef bint is_mpmath_type(t) noexcept:
 
 cdef class CoercionModel:
     """
-    See also sage.categories.pushout
+    See also :mod:`sage.categories.pushout`
 
     EXAMPLES::
 
diff --git a/src/sage/structure/coerce_actions.pyx b/src/sage/structure/coerce_actions.pyx
index e1ff3b378e7..4d7975429e9 100644
--- a/src/sage/structure/coerce_actions.pyx
+++ b/src/sage/structure/coerce_actions.pyx
@@ -70,7 +70,7 @@ cdef class GenericAction(Action):
 
         This will break if we tried to use it::
 
-            sage: sage.structure.coerce_actions.GenericAction(QQ, Z6, True, check=False)
+            sage: GenericAction(QQ, Z6, True, check=False)
             Left action by Rational Field on Ring of integers modulo 6
 
         """
@@ -172,7 +172,8 @@ def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None):
         sage: ZZx = ZZ['x']
         sage: M = MatrixSpace(ZZ, 2)                                                    # needs sage.modules
         sage: detect_element_action(ZZx, ZZ, False)
-        Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
+        Left scalar multiplication by Integer Ring
+         on Univariate Polynomial Ring in x over Integer Ring
         sage: detect_element_action(ZZx, QQ, True)
         Right scalar multiplication by Rational Field
          on Univariate Polynomial Ring in x over Integer Ring
@@ -301,7 +302,8 @@ cdef class ModuleAction(Action):
             sage: LeftModuleAction(QQ, ZZx)
             Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
             sage: LeftModuleAction(QQ, ZZxy)
-            Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
+            Left scalar multiplication by Rational Field
+             on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
 
         The following tests against a problem that was relevant during work on
         :issue:`9944`::
@@ -313,6 +315,23 @@ cdef class ModuleAction(Action):
             sage: 1/S.0
             1/x
 
+        If there is a coercion from ``G`` to ``S``, we do not create
+        the module action of ``G`` on the pushout of ``G`` and ``S``::
+
+            sage: G = PolynomialRing(QQ, "x")
+            sage: S = PolynomialRing(MatrixSpace(QQ, 2), "x")
+            sage: G.gen() * S.gen()
+            [1 0]
+            [0 1]*x^2
+
+        Contrast the previous example with the following, where we
+        have no coercion from ``G`` to ``S``::
+
+            sage: S = PolynomialRing(MatrixSpace(QQ, 2), "y")
+            sage: G.gen() * S.gen()
+            [x 0]
+            [0 x]*y
+
         """
         Action.__init__(self, G, S, not isinstance(self, RightModuleAction), operator.mul)
         if not isinstance(G, Parent):
@@ -328,6 +347,8 @@ cdef class ModuleAction(Action):
             # first we try the easy case of coercing G to the base ring of S
             self.connecting = base._internal_coerce_map_from(G)
             if self.connecting is None:
+                if S._internal_coerce_map_from(G) is not None:
+                    raise CoercionException("Best viewed as standard coercion multiplication.")
                 # otherwise, we try and find a base extension
                 from sage.categories.pushout import pushout
                 # this may raise a type error, which we propagate
@@ -399,7 +420,8 @@ cdef class ModuleAction(Action):
             sage: from sage.structure.coerce_actions import LeftModuleAction, RightModuleAction
             sage: ZZx = ZZ['x']
             sage: A = LeftModuleAction(ZZ, ZZx); A
-            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
+            Left scalar multiplication by Integer Ring
+             on Univariate Polynomial Ring in x over Integer Ring
             sage: A._repr_name_()
             'scalar multiplication'
 
@@ -533,7 +555,8 @@ cdef class ModuleAction(Action):
             sage: cm = sage.structure.element.get_coercion_model()
             sage: cm.explain(x, 1, operator.truediv)
             Action discovered.
-                Right inverse action by Symbolic Constants Subring on Univariate Polynomial Ring in x over Symbolic Constants Subring
+                Right inverse action by Symbolic Constants Subring
+                 on Univariate Polynomial Ring in x over Symbolic Constants Subring
                 with precomposition on right by Conversion via _symbolic_ method map:
                   From: Integer Ring
                   To:   Symbolic Constants Subring
diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx
index 6ee4d7c8201..46037b048a5 100644
--- a/src/sage/structure/element.pyx
+++ b/src/sage/structure/element.pyx
@@ -2860,11 +2860,11 @@ cdef class RingElement(ModuleElement):
 
         EXAMPLES::
 
-            sage: RR(-1).abs()
+            sage: RR(-1).abs()                                                          # needs sage.rings.real_mpfr
             1.00000000000000
             sage: ZZ(-1).abs()
             1
-            sage: CC(I).abs()
+            sage: CC(I).abs()                                                           # needs sage.rings.real_mpfr sage.symbolic
             1.00000000000000
             sage: Mod(-15, 37).abs()
             Traceback (most recent call last):
diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py
index 00ad9658e59..d17480fc6aa 100644
--- a/src/sage/structure/factorization.py
+++ b/src/sage/structure/factorization.py
@@ -876,7 +876,7 @@ def _latex_(self):
             -1 \cdot 2^{2} \cdot 5^{2}
             sage: f._latex_()
             '-1 \\cdot 2^{2} \\cdot 5^{2}'
-            sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_() # trac 12178             # needs sage.rings.number_field
+            sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_()  # Issue #12178          # needs sage.rings.number_field
             '(x^{2} + x + 1.000000000000000?)'
         """
         if len(self) == 0:
diff --git a/src/sage/structure/global_options.py b/src/sage/structure/global_options.py
index 01bbfd79ad9..ce188a58f21 100644
--- a/src/sage/structure/global_options.py
+++ b/src/sage/structure/global_options.py
@@ -560,7 +560,7 @@ def __repr__(self):
 
         EXAMPLES::
 
-            sage: Partitions.options.display # indirect doctest                         # needs sage.combinat
+            sage: Partitions.options.display  # indirect doctest                        # needs sage.combinat
             list
         """
         # NOTE: we intentionally use str() instead of repr()
@@ -1389,7 +1389,7 @@ def __eq__(self, other):
 
         EXAMPLES::
 
-            sage: Partitions.options == PartitionsGreatestLE.options # indirect doctest             # needs sage.combinat
+            sage: Partitions.options == PartitionsGreatestLE.options  # indirect doctest            # needs sage.combinat
             True
             sage: Partitions.options == Tableaux.options                                # needs sage.combinat
             False
diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py
index f0d521bee87..42bcecf8938 100644
--- a/src/sage/structure/indexed_generators.py
+++ b/src/sage/structure/indexed_generators.py
@@ -248,7 +248,7 @@ def print_options(self, **kwds):
              ('sorting_key',  at ...>),
              ('sorting_reverse', False), ('string_quotes', True),
              ('tensor_symbol', None)]
-            sage: F.print_options(bracket='[') # reset                                  # needs sage.modules
+            sage: F.print_options(bracket='[')  # reset                                 # needs sage.modules
         """
         # don't just use kwds.get(...) because I want to distinguish
         # between an argument like "option=None" and the option not
@@ -399,7 +399,7 @@ def _repr_generator(self, m):
             sage: a              # indirect doctest                                     # needs sage.combinat sage.modules
             2*|1, 2, 3| + 4*|3, 2, 1|
 
-            sage: QS3.print_options(**original_print_options) # reset                   # needs sage.combinat sage.modules
+            sage: QS3.print_options(**original_print_options)  # reset                  # needs sage.combinat sage.modules
 
         TESTS::
 
diff --git a/src/sage/structure/proof/proof.py b/src/sage/structure/proof/proof.py
index 49d4ecebe5a..5f55e04fbb5 100644
--- a/src/sage/structure/proof/proof.py
+++ b/src/sage/structure/proof/proof.py
@@ -192,7 +192,7 @@ def get_flag(t=None, subsystem=None):
 
 class WithProof():
     """
-    Use WithProof to temporarily set the value of one of the proof
+    Use :class:`WithProof` to temporarily set the value of one of the proof
     systems for a block of code, with a guarantee that it will be set
     back to how it was before after the block is done, even if there is an error.
 
diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx
index e14674678a0..02b23d16cf4 100644
--- a/src/sage/symbolic/expression.pyx
+++ b/src/sage/symbolic/expression.pyx
@@ -7497,14 +7497,14 @@ cdef class Expression(Expression_abc):
 
         INPUT:
 
-        -  ``base_ring`` - (optional) the base ring for the polynomial
+        -  ``base_ring`` -- (optional) the base ring for the polynomial
 
-        -  ``ring`` - (optional) the parent for the polynomial
+        -  ``ring`` -- (optional) the parent for the polynomial
 
         .. warning::
 
            This is different from :meth:`poly` which is used to rewrite
-           self as a polynomial in terms of one of the variables.
+           ``self`` as a polynomial in terms of one of the variables.
 
         EXAMPLES::
 
@@ -9019,7 +9019,7 @@ cdef class Expression(Expression_abc):
 
         This also works using functional notation::
 
-            sage: arccos(1,hold=True)
+            sage: arccos(1, hold=True)
             arccos(1)
             sage: arccos(1)
             0
@@ -9070,7 +9070,7 @@ cdef class Expression(Expression_abc):
 
         This also works using functional notation::
 
-            sage: arctan(1,hold=True)
+            sage: arctan(1, hold=True)
             arctan(1)
             sage: arctan(1)
             1/4*pi
@@ -9342,7 +9342,7 @@ cdef class Expression(Expression_abc):
 
         This also works using functional notation::
 
-            sage: tanh(arcsinh(x),hold=True)
+            sage: tanh(arcsinh(x), hold=True)
             tanh(arcsinh(x))
             sage: tanh(arcsinh(x))
             x/sqrt(x^2 + 1)
@@ -9623,7 +9623,7 @@ cdef class Expression(Expression_abc):
 
         The ``hold`` parameter also works in functional notation::
 
-            sage: log(-1,hold=True)
+            sage: log(-1, hold=True)
             log(-1)
             sage: log(-1)
             I*pi
@@ -9668,7 +9668,7 @@ cdef class Expression(Expression_abc):
 
         This also works using functional notation::
 
-            sage: zeta(2,hold=True)
+            sage: zeta(2, hold=True)
             zeta(2)
             sage: zeta(2)
             1/6*pi^2
@@ -9845,7 +9845,7 @@ cdef class Expression(Expression_abc):
 
         This also works using functional notation::
 
-            sage: gamma(1/2,hold=True)
+            sage: gamma(1/2, hold=True)
             gamma(1/2)
             sage: gamma(1/2)
             sqrt(pi)
@@ -9876,8 +9876,8 @@ cdef class Expression(Expression_abc):
 
     def log_gamma(self, hold=False):
         """
-        Return the log gamma function evaluated at self.
-        This is the logarithm of gamma of self, where
+        Return the log gamma function evaluated at ``self``.
+        This is the logarithm of gamma of ``self``, where
         gamma is a complex function such that `gamma(n)`
         equals `factorial(n-1)`.
 
@@ -12142,12 +12142,12 @@ cdef class Expression(Expression_abc):
 
         .. warning::
 
-           This is *not* a numerical solver - use ``find_root`` to
-           solve for self == 0 numerically on an interval.
+           This is *not* a numerical solver - use :meth:`find_root` to
+           solve for ``self == 0`` numerically on an interval.
 
         INPUT:
 
-        - ``x`` - variable to view the function in terms of
+        - ``x`` -- variable to view the function in terms of
           (use default variable if not given)
 
         - ``explicit_solutions`` -- bool (default ``True``); require that
@@ -12157,7 +12157,7 @@ cdef class Expression(Expression_abc):
           multiplicities
 
         - ``ring`` -- a ring (default ``None``): if not ``None``, convert
-          ``self`` to a polynomial over ring and find roots over ring
+          ``self`` to a polynomial over ``ring`` and find roots over ``ring``
 
         OUTPUT:
 
@@ -12210,9 +12210,9 @@ cdef class Expression(Expression_abc):
         .. NOTE::
 
             It is possible to solve a greater variety of equations
-            using ``solve()`` and the keyword ``to_poly_solve``,
+            using :func:`solve` and the keyword ``to_poly_solve``,
             but only at the price of possibly encountering
-            approximate solutions.  See documentation for f.solve
+            approximate solutions.  See documentation for :meth:`solve`
             for more details.
 
         We derive the roots of a general quadratic polynomial::
@@ -12388,7 +12388,7 @@ cdef class Expression(Expression_abc):
         Solve a polynomial equation in the integers (a so called Diophantine).
 
         If the argument is just a polynomial expression, equate to zero.
-        If ``solution_dict=True`` return a list of dictionaries instead of
+        If ``solution_dict=True``, return a list of dictionaries instead of
         a list of tuples.
 
         EXAMPLES::
@@ -12505,26 +12505,26 @@ cdef class Expression(Expression_abc):
     def find_root(self, a, b, var=None, xtol=10e-13, rtol=2.0**-50,
                   maxiter=100, full_output=False, imaginary_tolerance=1e-8):
         """
-        Numerically find a root of self on the closed interval [a,b] (or
-        [b,a]) if possible, where self is a function in the one variable.
+        Numerically find a root of ``self`` on the closed interval [a,b] (or
+        [b,a]) if possible, where ``self`` is a function in one variable.
         Note: this function only works in fixed (machine) precision, it is not
         possible to get arbitrary precision approximations with it.
 
         INPUT:
 
-        -  ``a, b`` - endpoints of the interval
+        -  ``a``, ``b`` -- endpoints of the interval
 
-        -  ``var`` - optional variable
+        -  ``var`` -- optional variable
 
-        -  ``xtol, rtol`` - the routine converges when a root
-           is known to lie within xtol of the value return. Should be >= 0. The
+        -  ``xtol, rtol`` -- the routine converges when a root
+           is known to lie within ``xtol`` of the value return. Should be >= 0. The
            routine modifies this to take into account the relative precision
            of doubles.
 
-        -  ``maxiter`` - integer; if convergence is not
+        -  ``maxiter`` -- integer; if convergence is not
            achieved in maxiter iterations, an error is raised. Must be >= 0.
 
-        -  ``full_output`` - bool (default: False), if True,
+        -  ``full_output`` -- bool (default: ``False``), if ``True``,
            also return object that contains information about convergence.
 
         - ``imaginary_tolerance`` -- (default: ``1e-8``); if an imaginary
@@ -12698,19 +12698,19 @@ cdef class Expression(Expression_abc):
 
         INPUT:
 
-        -  ``a`` - real number; left endpoint of interval on which to
+        -  ``a`` -- real number; left endpoint of interval on which to
            minimize
 
-        -  ``b`` - real number; right endpoint of interval on which to
+        -  ``b`` -- real number; right endpoint of interval on which to
            minimize
 
-        -  ``var`` - variable (default: first variable in self); the
+        -  ``var`` -- variable (default: first variable in self); the
            variable in self to maximize over
 
-        -  ``tol`` - positive real (default: 1.48e-08); the convergence
+        -  ``tol`` -- positive real (default: 1.48e-08); the convergence
            tolerance
 
-        -  ``maxfun`` - natural number (default: 500); maximum function
+        -  ``maxfun`` -- natural number (default: 500); maximum function
            evaluations
 
         - ``imaginary_tolerance`` -- (default: ``1e-8``); if an imaginary
@@ -12842,7 +12842,7 @@ cdef class Expression(Expression_abc):
 
     def plot(self, *args, **kwds):
         """
-        Plot a symbolic expression. All arguments are passed onto the standard plot command.
+        Plot a symbolic expression. All arguments are passed onto the standard :func:`plot` command.
 
         EXAMPLES:
 
@@ -12890,7 +12890,8 @@ cdef class Expression(Expression_abc):
             sage: plot(2*sin, -4, 4)                                                    # needs sage.plot
             Traceback (most recent call last):
             ...
-            TypeError: unsupported operand parent(s) for *: 'Integer Ring' and ''
+            TypeError: unsupported operand parent(s) for *:
+            'Integer Ring' and ''
 
         You should evaluate the function first::
 
@@ -12990,7 +12991,7 @@ cdef class Expression(Expression_abc):
 
         -  ``b`` -- upper endpoint of the sum
 
-        - ``algorithm`` - (default: ``'maxima'``)  one of
+        - ``algorithm`` -- (default: ``'maxima'``)  one of
 
           - ``'maxima'`` -- use Maxima (the default)
           - ``'maple'`` -- (optional) use Maple
diff --git a/src/sage/symbolic/expression_conversion_algebraic.py b/src/sage/symbolic/expression_conversion_algebraic.py
index d5764cb2421..11314aaeb70 100644
--- a/src/sage/symbolic/expression_conversion_algebraic.py
+++ b/src/sage/symbolic/expression_conversion_algebraic.py
@@ -146,9 +146,9 @@ def composition(self, ex, operator):
             sage: a.composition(complex_root_of(x^5 - 1, 3), complex_root_of)
             0.3090169943749474? - 0.9510565162951536?*I
             sage: a.composition(complex_root_of(x^2 + 1, 0), complex_root_of)
-            1.?e-684 - 0.9999999999999999?*I
+            1.?e-884 - 0.9999999999999999?*I
             sage: a.composition(complex_root_of(x^2 + 1, 1), complex_root_of)
-            1.?e-684 + 0.9999999999999999?*I
+            1.?e-884 + 0.9999999999999999?*I
 
         TESTS::
 
diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py
index 3944390c746..486bb4324b1 100644
--- a/src/sage/symbolic/expression_conversions.py
+++ b/src/sage/symbolic/expression_conversions.py
@@ -695,32 +695,32 @@ def pyobject(self, ex, obj):
         EXAMPLES::
 
             sage: 2._fricas_().domainOf()                                       # optional - fricas
-            PositiveInteger()
+            PositiveInteger...
 
             sage: (-1/2)._fricas_().domainOf()                                  # optional - fricas
-            Fraction(Integer())
+            Fraction(Integer...)
 
             sage: SR(2)._fricas_().domainOf()                                   # optional - fricas
-            Expression(Integer())
+            Expression(Integer...)
 
             sage: (sqrt(2))._fricas_().domainOf()                               # optional - fricas
-            Expression(Integer())
+            Expression(Integer...)
 
             sage: pi._fricas_().domainOf()                                      # optional - fricas
-            Pi()
+            Pi...
 
             sage: asin(pi)._fricas_()                                           # optional - fricas
             asin(%pi)
 
             sage: I._fricas_().domainOf()                                   # optional - fricas
-            Complex(Integer())
+            Complex(Integer...)
 
             sage: SR(I)._fricas_().domainOf()                                   # optional - fricas
-            Expression(Complex(Integer()))
+            Expression(Complex(Integer...))
 
             sage: ex = (I+sqrt(2)+2)
             sage: ex._fricas_().domainOf()                                      # optional - fricas
-            Expression(Complex(Integer()))
+            Expression(Complex(Integer...))
 
             sage: ex._fricas_()^2                                               # optional - fricas
                        +-+
@@ -759,7 +759,7 @@ def symbol(self, ex):
             Variable(x)
 
             sage: (x^2)._fricas_().domainOf()                                   # optional - fricas
-            Expression(Integer())
+            Expression(Integer...)
 
             sage: (2*x)._fricas_().integrate(x)                                 # optional - fricas
              2
diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx
index f33b8eb47e6..574fa649597 100644
--- a/src/sage/symbolic/function.pyx
+++ b/src/sage/symbolic/function.pyx
@@ -290,7 +290,7 @@ cdef class Function(SageObject):
 
             sage: coth(5)  # indirect doctest                                           # needs sage.symbolic
             coth(5)
-            sage: coth(0.5)
+            sage: coth(0.5)                                                             # needs sage.rings.real_mpfr
             2.16395341373865
             sage: from sage.symbolic.function import BuiltinFunction
             sage: class Test(BuiltinFunction):
@@ -307,13 +307,13 @@ cdef class Function(SageObject):
             ....:         else:
             ....:             return
             sage: test = Test()
-            sage: test(1.3, 4)
+            sage: test(1.3, 4)                                                          # needs sage.rings.real_mpfr
             2.30000000000000
             sage: test(pi, 4)                                                           # needs sage.symbolic
             test(pi, 4)
             sage: test(2, x)                                                            # needs sage.symbolic
             3
-            sage: test(2., 4)
+            sage: test(2., 4)                                                           # needs sage.rings.real_mpfr
             3.00000000000000
             sage: test(1 + 1.0*I, 2)                                                    # needs sage.symbolic
             2.00000000000000 + 1.00000000000000*I
@@ -329,7 +329,7 @@ cdef class Function(SageObject):
             ....:         else:
             ....:             return 3
             sage: test2 = Test2()
-            sage: test2(1.3)
+            sage: test2(1.3)                                                            # needs sage.rings.real_mpfr
             0.500000000000000
             sage: test2(pi)                                                             # needs sage.symbolic
             3
@@ -456,7 +456,7 @@ cdef class Function(SageObject):
 
         Precision of the result depends on the precision of the input::
 
-            sage: arctan(RR(1))
+            sage: arctan(RR(1))                                                         # needs sage.rings.real_mpfr
             0.785398163397448
             sage: arctan(RealField(100)(1))                                             # needs sage.rings.real_mpfr
             0.78539816339744830961566084582
@@ -646,7 +646,7 @@ cdef class Function(SageObject):
 
             sage: airy_ai(iv)                                                           # needs sage.rings.real_interval_field
             airy_ai(1.0001?)
-            sage: airy_ai(CIF(iv))                                                      # needs sage.rings.complex_interval_field
+            sage: airy_ai(CIF(iv))                                                      # needs sage.rings.complex_interval_field sage.rings.real_interval_field
             airy_ai(1.0001?)
         """
         if isinstance(x, (float, complex)):
diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py
index 61e1513d87f..554af7063ce 100644
--- a/src/sage/symbolic/integration/integral.py
+++ b/src/sage/symbolic/integration/integral.py
@@ -452,28 +452,28 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False):
 
     INPUT:
 
-    - ``v`` - a variable or variable name.  This can also be a tuple of
+    - ``v`` -- a variable or variable name.  This can also be a tuple of
       the variable (optional) and endpoints (i.e., ``(x,0,1)`` or ``(0,1)``).
 
-    - ``a`` - (optional) lower endpoint of definite integral
+    - ``a`` -- (optional) lower endpoint of definite integral
 
-    - ``b`` - (optional) upper endpoint of definite integral
+    - ``b`` -- (optional) upper endpoint of definite integral
 
-    - ``algorithm`` - (default: 'maxima', 'libgiac' and 'sympy') one of
+    - ``algorithm`` -- (default: ``'maxima'``, ``'libgiac'`` and ``'sympy'``) one of
 
-       - 'maxima' - use maxima
+      - ``'maxima'`` -- use maxima
 
-       - 'sympy' - use sympy (also in Sage)
+      - ``'sympy'`` -- use sympy (also in Sage)
 
-       - 'mathematica_free' - use http://integrals.wolfram.com/
+      - ``'mathematica_free'`` -- use http://integrals.wolfram.com/
 
-       - 'fricas' - use FriCAS (the optional fricas spkg has to be installed)
+      - ``'fricas'`` -- use FriCAS (the optional fricas spkg has to be installed)
 
-       - 'giac' - use Giac
+      - ``'giac'`` - use Giac
 
-       - 'libgiac' - use libgiac
+      - ``'libgiac'`` - use libgiac
 
-    To prevent automatic evaluation use the ``hold`` argument.
+    To prevent automatic evaluation, use the ``hold`` argument.
 
     .. SEEALSO::
 
@@ -596,7 +596,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False):
 
         sage: _ = var('x, y, z')
         sage: f = sin(x^2) + y^z
-        sage: g = mathematica(f)                           # optional - mathematica
+        sage: g = mathematica(f)                            # optional - mathematica
         sage: print(g)                                      # optional - mathematica
                   z        2
                  y  + Sin[x ]
@@ -605,7 +605,10 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False):
                  x y  + Sqrt[--] FresnelS[Sqrt[--] x]
                              2                 Pi
         sage: print(f.integral(x))
-        x*y^z + 1/16*sqrt(pi)*((I + 1)*sqrt(2)*erf((1/2*I + 1/2)*sqrt(2)*x) + (I - 1)*sqrt(2)*erf((1/2*I - 1/2)*sqrt(2)*x) - (I - 1)*sqrt(2)*erf(sqrt(-I)*x) + (I + 1)*sqrt(2)*erf((-1)^(1/4)*x))
+        x*y^z + 1/16*sqrt(pi)*((I + 1)*sqrt(2)*erf((1/2*I + 1/2)*sqrt(2)*x)
+                                + (I - 1)*sqrt(2)*erf((1/2*I - 1/2)*sqrt(2)*x)
+                                - (I - 1)*sqrt(2)*erf(sqrt(-I)*x)
+                                + (I + 1)*sqrt(2)*erf((-1)^(1/4)*x))
 
     Alternatively, just use algorithm='mathematica_free' to integrate via Mathematica
     over the internet (does NOT require a Mathematica license!)::
@@ -659,12 +662,15 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False):
 
         sage: f(x) = sqrt(x+sqrt(1+x^2))/x
         sage: integrate(f(x), x, algorithm="fricas")      # optional - fricas
-        2*sqrt(x + sqrt(x^2 + 1)) - 2*arctan(sqrt(x + sqrt(x^2 + 1))) - log(sqrt(x + sqrt(x^2 + 1)) + 1) + log(sqrt(x + sqrt(x^2 + 1)) - 1)
+        2*sqrt(x + sqrt(x^2 + 1)) - 2*arctan(sqrt(x + sqrt(x^2 + 1)))
+         - log(sqrt(x + sqrt(x^2 + 1)) + 1) + log(sqrt(x + sqrt(x^2 + 1)) - 1)
 
     where the default integrator obtains another answer::
 
         sage: integrate(f(x), x)  # long time
-        1/8*sqrt(x)*gamma(1/4)*gamma(-1/4)^2*hypergeometric((-1/4, -1/4, 1/4), (1/2, 3/4), -1/x^2)/(pi*gamma(3/4))
+        1/8*sqrt(x)*gamma(1/4)*gamma(-1/4)^2*hypergeometric((-1/4, -1/4, 1/4),
+                                                            (1/2, 3/4),
+                                                            -1/x^2)/(pi*gamma(3/4))
 
     The following definite integral is not found by maxima::
 
@@ -713,7 +719,10 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False):
 
         sage: assume(a>0)
         sage: integrate(1/(x^3 *(a+b*x)^(1/3)), x)
-        2/9*sqrt(3)*b^2*arctan(1/3*sqrt(3)*(2*(b*x + a)^(1/3) + a^(1/3))/a^(1/3))/a^(7/3) - 1/9*b^2*log((b*x + a)^(2/3) + (b*x + a)^(1/3)*a^(1/3) + a^(2/3))/a^(7/3) + 2/9*b^2*log((b*x + a)^(1/3) - a^(1/3))/a^(7/3) + 1/6*(4*(b*x + a)^(5/3)*b^2 - 7*(b*x + a)^(2/3)*a*b^2)/((b*x + a)^2*a^2 - 2*(b*x + a)*a^3 + a^4)
+        2/9*sqrt(3)*b^2*arctan(1/3*sqrt(3)*(2*(b*x + a)^(1/3) + a^(1/3))/a^(1/3))/a^(7/3)
+         - 1/9*b^2*log((b*x + a)^(2/3) + (b*x + a)^(1/3)*a^(1/3) + a^(2/3))/a^(7/3)
+         + 2/9*b^2*log((b*x + a)^(1/3) - a^(1/3))/a^(7/3) + 1/6*(4*(b*x + a)^(5/3)*b^2
+         - 7*(b*x + a)^(2/3)*a*b^2)/((b*x + a)^2*a^2 - 2*(b*x + a)*a^3 + a^4)
 
     TESTS:
 
diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py
index 785b4ecc6bd..bdae1b58f22 100644
--- a/src/sage/tensor/modules/ext_pow_free_module.py
+++ b/src/sage/tensor/modules/ext_pow_free_module.py
@@ -173,7 +173,7 @@ class ExtPowerFreeModule(FiniteRankFreeModule_abstract):
         a = 3 e_0∧e_1 - e_0∧e_2 + 4 e_1∧e_2
 
     An alternative is to construct the alternating contravariant tensor from an
-     empty list of components and to set the nonzero components afterwards::
+    empty list of components and to set the nonzero components afterwards::
 
         sage: a = A([], name='a')
         sage: a.set_comp(e)[0,1] = 3
diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py
index cb0922eaa10..924dce1dc15 100644
--- a/src/sage/tensor/modules/finite_rank_free_module.py
+++ b/src/sage/tensor/modules/finite_rank_free_module.py
@@ -248,7 +248,7 @@ class :class:`~sage.modules.free_module.FreeModule_generic`
     Category of finite dimensional modules over Integer Ring
     sage: N.category()
     Category of finite dimensional modules with basis over
-     (Dedekind domains and euclidean domains
+     (Dedekind domains and euclidean domains and noetherian rings
      and infinite enumerated sets and metric spaces)
 
 In other words, the module created by ``FreeModule`` is actually `\ZZ^3`,
diff --git a/src/sage/tensor/modules/free_module_automorphism.py b/src/sage/tensor/modules/free_module_automorphism.py
index ebe30d6255b..b74a3798728 100644
--- a/src/sage/tensor/modules/free_module_automorphism.py
+++ b/src/sage/tensor/modules/free_module_automorphism.py
@@ -32,6 +32,7 @@
 #                  https://www.gnu.org/licenses/
 # *****************************************************************************
 
+from sage.misc.lazy_attribute import lazy_attribute
 from sage.structure.element import MultiplicativeGroupElement
 from sage.tensor.modules.free_module_tensor import FreeModuleTensor
 
@@ -1054,6 +1055,55 @@ def matrix(self, basis1=None, basis2=None):
                 raise NotImplementedError("basis1 != basis2 not implemented yet")
         return self._matrices[(basis1, basis2)]
 
+    def _some_matrix(self):
+        r"""
+        Return the matrix of ``self`` w.r.t. some basis.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(QQ, 2, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism([[1,1],[0,2]], name='a')
+            sage: a._some_matrix()
+            [1 1]
+            [0 2]
+        """
+        self.matrix()  # forces the update of the matrix in the module's default
+                       # basis, to make sure that the dictionary self._matrices
+                       # is not empty
+        return next(iter(self._matrices.values()))
+
+    @lazy_attribute
+    def characteristic_polynomial(self):
+        r"""
+        Return the characteristic polynomial of ``self``.
+
+        :meth:`characteristic_polynomial` and :meth:`charpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(QQ, 2, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism([[1,1],[0,2]], name='a')
+            sage: a.matrix(e)
+            [1 1]
+            [0 2]
+            sage: a.characteristic_polynomial()
+            x^2 - 3*x + 2
+            sage: a.charpoly()
+            x^2 - 3*x + 2
+            sage: a.charpoly('T')
+            T^2 - 3*T + 2
+        """
+        return self._some_matrix().characteristic_polynomial
+
+    charpoly = characteristic_polynomial
+
+    @lazy_attribute
     def det(self):
         r"""
         Return the determinant of ``self``.
@@ -1084,13 +1134,62 @@ def det(self):
             1
 
         """
-        self.matrix() # forces the update of the matrix in the module's default
-                      # basis, to make sure that the dictionary self._matrices
-                      # is not empty
-        return next(iter(self._matrices.values())).det() # pick a random value in the
-                                                # dictionary self._matrices
-                                                # and compute the determinant
+        return self._some_matrix().det
+
+    determinant = det
+
+    @lazy_attribute
+    def fcp(self):
+        r"""
+        Return the factorization of the characteristic polynomial of ``self``.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(QQ, 2, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism([[1,1],[0,2]], name='a')
+            sage: a.matrix(e)
+            [1 1]
+            [0 2]
+            sage: a.fcp()                                                               # needs sage.libs.pari
+            (x - 2) * (x - 1)
+            sage: a.fcp('T')                                                            # needs sage.libs.pari
+            (T - 2) * (T - 1)
+        """
+        return self._some_matrix().fcp
+
+    @lazy_attribute
+    def minimal_polynomial(self):
+        r"""
+        Return the minimal polynomial of ``self``.
+
+        :meth:`minimal_polynomial` and :meth:`minpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(GF(7), 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism([[0,1,2], [-1,0,3], [2,4,1]], name='a')
+            sage: a.minpoly()                                                           # needs sage.libs.pari
+            x^3 + 6*x^2 + 6*x + 1
+            sage: a.minimal_polynomial()                                                # needs sage.libs.pari
+            x^3 + 6*x^2 + 6*x + 1
+            sage: a.minimal_polynomial('T')                                             # needs sage.libs.pari
+            T^3 + 6*T^2 + 6*T + 1
+        """
+        return self._some_matrix().minimal_polynomial
+
+    minpoly = minimal_polynomial
 
+    @lazy_attribute
     def trace(self):
         r"""
         Return the trace of ``self``.
@@ -1117,9 +1216,4 @@ def trace(self):
             2
 
         """
-        self.matrix() # forces the update of the matrix in the module's default
-                      # basis, to make sure that the dictionary self._matrices
-                      # is not empty
-        return next(iter(self._matrices.values())).trace() # pick a random value in the
-                                                  # dictionary self._matrices
-                                                  # and compute the trace
+        return self._some_matrix().trace
diff --git a/src/sage/tensor/modules/free_module_basis.py b/src/sage/tensor/modules/free_module_basis.py
index 9d112ef48d7..44447401b1f 100644
--- a/src/sage/tensor/modules/free_module_basis.py
+++ b/src/sage/tensor/modules/free_module_basis.py
@@ -48,7 +48,7 @@ class Basis_abstract(UniqueRepresentation, AbstractFamily):
     :class:`Mapping` subclasses, not the :meth:`keys` but the
     :meth:`values` are considered the elements.
 
-    EXAMPLES:
+    EXAMPLES::
 
         sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1)
         sage: e = M.basis('e'); e
@@ -111,8 +111,8 @@ def values(self):
             sage: e = M.basis('e')
             sage: list(e.values())
             [Element e_0 of the Rank-3 free module M over the Integer Ring,
-            Element e_1 of the Rank-3 free module M over the Integer Ring,
-            Element e_2 of the Rank-3 free module M over the Integer Ring]
+             Element e_1 of the Rank-3 free module M over the Integer Ring,
+             Element e_2 of the Rank-3 free module M over the Integer Ring]
         """
         return self._vec
 
diff --git a/src/sage/tensor/modules/free_module_homset.py b/src/sage/tensor/modules/free_module_homset.py
index 509ae146d49..ff774f540ed 100644
--- a/src/sage/tensor/modules/free_module_homset.py
+++ b/src/sage/tensor/modules/free_module_homset.py
@@ -4,9 +4,13 @@
 The class :class:`FreeModuleHomset` implements sets of homomorphisms between
 two free modules of finite rank over the same commutative ring.
 
+The subclass :class:`FreeModuleEndset` implements the special case of
+sets of endomorphisms.
+
 AUTHORS:
 
 - Eric Gourgoulhon, Michal Bejger (2014-2015): initial version
+- Matthias Koeppe (2024): add :class:`FreeModuleEndset`
 
 REFERENCES:
 
@@ -25,11 +29,13 @@
 #******************************************************************************
 
 from sage.categories.homset import Homset
-from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleMorphism
+from sage.misc.classcall_metaclass import ClasscallMetaclass
+from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleEndomorphism, FiniteRankFreeModuleMorphism
 from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism
 from sage.tensor.modules.free_module_tensor import FreeModuleTensor
 
-class FreeModuleHomset(Homset):
+
+class FreeModuleHomset(Homset, metaclass=ClasscallMetaclass):
     r"""
     Set of homomorphisms between free modules of finite rank over a
     commutative ring.
@@ -43,6 +49,8 @@ class FreeModuleHomset(Homset):
     This is a Sage *parent* class, whose *element* class is
     :class:`~sage.tensor.modules.free_module_morphism.FiniteRankFreeModuleMorphism`.
 
+    The case `M=N` (endomorphisms) is delegated to the subclass :class:`FreeModuleEndset`.
+
     INPUT:
 
     - ``fmodule1`` -- free module `M` (domain of the homomorphisms), as an
@@ -117,99 +125,24 @@ class FreeModuleHomset(Homset):
     The test suite for H is passed::
 
         sage: TestSuite(H).run()
-
-    The set of homomorphisms `M\rightarrow M`, i.e. endomorphisms, is
-    obtained by the function ``End``::
-
-        sage: End(M)
-        Set of Morphisms from Rank-3 free module M over the Integer Ring
-         to Rank-3 free module M over the Integer Ring
-         in Category of finite dimensional modules over Integer Ring
-
-    ``End(M)`` is actually identical to ``Hom(M,M)``::
-
-        sage: End(M) is Hom(M,M)
-        True
-
-    The unit of the endomorphism ring is the identity map::
-
-        sage: End(M).one()
-        Identity endomorphism of Rank-3 free module M over the Integer Ring
-
-    whose matrix in any basis is of course the identity matrix::
-
-        sage: End(M).one().matrix(e)
-        [1 0 0]
-        [0 1 0]
-        [0 0 1]
-
-    There is a canonical identification between endomorphisms of `M` and
-    tensors of type `(1,1)` on `M`. Accordingly, coercion maps have been
-    implemented between `\mathrm{End}(M)` and `T^{(1,1)}(M)` (the module of
-    all type-`(1,1)` tensors on `M`, see
-    :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule`)::
-
-        sage: T11 = M.tensor_module(1,1) ; T11
-        Free module of type-(1,1) tensors on the Rank-3 free module M over
-         the Integer Ring
-        sage: End(M).has_coerce_map_from(T11)
-        True
-        sage: T11.has_coerce_map_from(End(M))
-        True
-
-    See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` for
-    examples of the above coercions.
-
-    There is a coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)`, since
-    every automorphism is an endomorphism::
-
-        sage: GL = M.general_linear_group() ; GL
-        General linear group of the Rank-3 free module M over the Integer Ring
-        sage: End(M).has_coerce_map_from(GL)
-        True
-
-    Of course, there is no coercion in the reverse direction, since only
-    bijective endomorphisms are automorphisms::
-
-        sage: GL.has_coerce_map_from(End(M))
-        False
-
-    The coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)` in action::
-
-        sage: a = GL.an_element() ; a
-        Automorphism of the Rank-3 free module M over the Integer Ring
-        sage: a.matrix(e)
-        [ 1  0  0]
-        [ 0 -1  0]
-        [ 0  0  1]
-        sage: ea = End(M)(a) ; ea
-        Generic endomorphism of Rank-3 free module M over the Integer Ring
-        sage: ea.matrix(e)
-        [ 1  0  0]
-        [ 0 -1  0]
-        [ 0  0  1]
-
     """
 
     Element = FiniteRankFreeModuleMorphism
 
-    def __init__(self, fmodule1, fmodule2, name=None, latex_name=None):
+    @staticmethod
+    def __classcall_private__(cls, fmodule1, fmodule2, name=None, latex_name=None):
         r"""
+        Delegate to the subclass :class:`FreeModuleEndset` if ``fmodule1 == fmodule2``.
+
         TESTS::
 
-            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleEndset, FreeModuleHomset
             sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
             sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
-            sage: FreeModuleHomset(M, N)
-            Set of Morphisms from Rank-3 free module M over the Integer Ring
-             to Rank-2 free module N over the Integer Ring
-             in Category of finite dimensional modules over Integer Ring
-
-            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
-            ....:                      latex_name=r'\mathcal{L}(M,N)')
-            sage: latex(H)
-            \mathcal{L}(M,N)
-
+            sage: isinstance(FreeModuleHomset(M, N), FreeModuleEndset)
+            False
+            sage: isinstance(FreeModuleHomset(M, M), FreeModuleEndset)
+            True
         """
         from .finite_rank_free_module import FiniteRankFreeModule
         if not isinstance(fmodule1, FiniteRankFreeModule):
@@ -221,19 +154,37 @@ def __init__(self, fmodule1, fmodule2, name=None, latex_name=None):
         if fmodule1.base_ring() != fmodule2.base_ring():
             raise TypeError("the domain and codomain are not defined over " +
                             "the same ring")
-        Homset.__init__(self, fmodule1, fmodule2)
         if name is None:
-            self._name = "Hom(" + fmodule1._name + "," + fmodule2._name + ")"
-        else:
-            self._name = name
+            name = "Hom(" + fmodule1._name + "," + fmodule2._name + ")"
         if latex_name is None:
-            self._latex_name = \
-                    r"\mathrm{Hom}\left(" + fmodule1._latex_name + "," + \
-                    fmodule2._latex_name + r"\right)"
-        else:
-            self._latex_name = latex_name
-        self._one = None # to be set by self.one() if self is an endomorphism
-                         # set (fmodule1 = fmodule2)
+            latex_name = \
+                r"\mathrm{Hom}\left(" + fmodule1._latex_name + "," + \
+                fmodule2._latex_name + r"\right)"
+        if fmodule1 == fmodule2:
+            return FreeModuleEndset(fmodule1, name, latex_name)
+        return type.__call__(cls, fmodule1, fmodule2, name, latex_name)
+
+    def __init__(self, fmodule1, fmodule2, name, latex_name):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: FreeModuleHomset(M, N)
+            Set of Morphisms from Rank-3 free module M over the Integer Ring
+             to Rank-2 free module N over the Integer Ring
+             in Category of finite dimensional modules over Integer Ring
+
+            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
+            ....:                      latex_name=r'\mathcal{L}(M,N)')
+            sage: latex(H)
+            \mathcal{L}(M,N)
+
+        """
+        Homset.__init__(self, fmodule1, fmodule2)
+        self._name = name
+        self._latex_name = latex_name
 
     def _latex_(self):
         r"""
@@ -370,35 +321,10 @@ def _element_constructor_(self, matrix_rep, bases=None, name=None,
             True
 
         """
-        if isinstance(matrix_rep, FreeModuleTensor):
-            # coercion of a type-(1,1) tensor to an endomorphism
-            # (this includes automorphisms, since the class
-            #  FreeModuleAutomorphism inherits from FreeModuleTensor)
-            tensor = matrix_rep # for readability
-            if tensor.tensor_type() == (1,1) and \
-                                         self.is_endomorphism_set() and \
-                                         tensor.base_module() is self.domain():
-                basis = tensor.pick_a_basis()
-                tcomp = tensor.comp(basis)
-                fmodule = tensor.base_module()
-                mat = [[ tcomp[[i,j]] for j in fmodule.irange()]
-                       for i in fmodule.irange()]
-                if isinstance(tensor, FreeModuleAutomorphism):
-                    is_identity = tensor._is_identity
-                else:
-                    is_identity = False
-                resu = self.element_class(self, mat, bases=(basis,basis),
-                              name=tensor._name, latex_name=tensor._latex_name,
-                              is_identity=is_identity)
-            else:
-                raise TypeError("cannot coerce the {}".format(tensor) +
-                                " to an element of {}".format(self))
-        else:
-            # Standard construction:
-            resu = self.element_class(self, matrix_rep, bases=bases, name=name,
-                                      latex_name=latex_name,
-                                      is_identity=is_identity)
-        return resu
+        # Standard construction:
+        return self.element_class(self, matrix_rep, bases=bases, name=name,
+                                  latex_name=latex_name,
+                                  is_identity=is_identity)
 
     def _an_element_(self):
         r"""
@@ -426,14 +352,136 @@ def _an_element_(self):
         matrix_rep = [[ring.an_element() for i in range(m)] for j in range(n)]
         return self.element_class(self, matrix_rep)
 
+    #### End of methods required for any Parent
+
+
+class FreeModuleEndset(FreeModuleHomset):
+    r"""
+    Ring of endomorphisms of a free module of finite rank over a commutative ring.
+
+    Given a free modules `M` of rank `n` over a commutative ring `R`, the
+    class :class:`FreeModuleEndset` implements the ring `\mathrm{Hom}(M,M)`
+    of endomorphisms `M\rightarrow M`.
+
+    This is a Sage *parent* class, whose *element* class is
+    :class:`~sage.tensor.modules.free_module_morphism.FiniteRankFreeModuleMorphism`.
+
+    INPUT:
+
+    - ``fmodule`` -- free module `M` (domain and codomain of the endomorphisms), as an
+      instance of
+      :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`
+    - ``name`` -- (default: ``None``) string; name given to the end-set; if
+      none is provided, Hom(M,M) will be used
+    - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
+      hom-set; if none is provided, `\mathrm{Hom}(M,M)` will be used
+
+    EXAMPLES:
+
+    The set of homomorphisms `M\rightarrow M`, i.e. endomorphisms, is
+    obtained by the function :func:`End`::
+
+        sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+        sage: e = M.basis('e')
+        sage: End(M)
+        Set of Morphisms from Rank-3 free module M over the Integer Ring
+         to Rank-3 free module M over the Integer Ring
+         in Category of finite dimensional modules over Integer Ring
+
+    ``End(M)`` is actually identical to ``Hom(M,M)``::
+
+        sage: End(M) is Hom(M,M)
+        True
+
+    The unit of the endomorphism ring is the identity map::
+
+        sage: End(M).one()
+        Identity endomorphism of Rank-3 free module M over the Integer Ring
+
+    whose matrix in any basis is of course the identity matrix::
+
+        sage: End(M).one().matrix(e)
+        [1 0 0]
+        [0 1 0]
+        [0 0 1]
+
+    There is a canonical identification between endomorphisms of `M` and
+    tensors of type `(1,1)` on `M`. Accordingly, coercion maps have been
+    implemented between `\mathrm{End}(M)` and `T^{(1,1)}(M)` (the module of
+    all type-`(1,1)` tensors on `M`, see
+    :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule`)::
+
+        sage: T11 = M.tensor_module(1,1) ; T11
+        Free module of type-(1,1) tensors on the Rank-3 free module M over
+         the Integer Ring
+        sage: End(M).has_coerce_map_from(T11)
+        True
+        sage: T11.has_coerce_map_from(End(M))
+        True
+
+    See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` for
+    examples of the above coercions.
+
+    There is a coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)`, since
+    every automorphism is an endomorphism::
+
+        sage: GL = M.general_linear_group() ; GL
+        General linear group of the Rank-3 free module M over the Integer Ring
+        sage: End(M).has_coerce_map_from(GL)
+        True
+
+    Of course, there is no coercion in the reverse direction, since only
+    bijective endomorphisms are automorphisms::
+
+        sage: GL.has_coerce_map_from(End(M))
+        False
+
+    The coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)` in action::
+
+        sage: a = GL.an_element() ; a
+        Automorphism of the Rank-3 free module M over the Integer Ring
+        sage: a.matrix(e)
+        [ 1  0  0]
+        [ 0 -1  0]
+        [ 0  0  1]
+        sage: ea = End(M)(a) ; ea
+        Generic endomorphism of Rank-3 free module M over the Integer Ring
+        sage: ea.matrix(e)
+        [ 1  0  0]
+        [ 0 -1  0]
+        [ 0  0  1]
+    """
+
+    Element = FiniteRankFreeModuleEndomorphism
+
+    def __init__(self, fmodule, name, latex_name):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: FreeModuleHomset(M, N)
+            Set of Morphisms from Rank-3 free module M over the Integer Ring
+             to Rank-2 free module N over the Integer Ring
+             in Category of finite dimensional modules over Integer Ring
+
+            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
+            ....:                      latex_name=r'\mathcal{L}(M,N)')
+            sage: latex(H)
+            \mathcal{L}(M,N)
+
+        """
+        FreeModuleHomset.__init__(self, fmodule, fmodule, name, latex_name)
+        self._one = None  # to be set by self.one()
+
     def _coerce_map_from_(self, other):
         r"""
-        Determine whether coercion to self exists from other parent.
+        Determine whether coercion to ``self`` exists from other parent.
 
         EXAMPLES:
 
-        The module of type-`(1,1)` tensors coerces to ``self``, if the latter
-        is some endomorphism set::
+        The module of type-`(1,1)` tensors coerces to ``self``::
 
             sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
             sage: e = M.basis('e')
@@ -464,17 +512,98 @@ def _coerce_map_from_(self, other):
                                            other.base_module() is self.domain()
         return False
 
-    #### End of methods required for any Parent
+    #### Methods required for any Parent
+
+    def _element_constructor_(self, matrix_rep, bases=None, name=None,
+                              latex_name=None, is_identity=False):
+        r"""
+        Construct an element of ``self``, i.e. a homomorphism M --> N, where
+        M is the domain of ``self`` and N its codomain.
+
+        INPUT:
+
+        - ``matrix_rep`` -- matrix representation of the homomorphism with
+          respect to the bases ``basis1`` and ``basis2``; this entry can
+          actually be any material from which a matrix of size rank(N)*rank(M)
+          can be constructed
+        - ``bases`` -- (default: ``None``) pair (basis_M, basis_N) defining the
+          matrix representation, basis_M being a basis of module `M` and
+          basis_N a basis of module `N` ; if none is provided the pair formed
+          by the default bases of each module is assumed.
+        - ``name`` -- (default: ``None``) string; name given to the
+          homomorphism
+        - ``latex_name`` -- (default: ``None``)string;  LaTeX symbol to denote
+          the homomorphism; if none is provided, ``name`` will be used.
+        - ``is_identity`` -- (default: ``False``) determines whether the
+          constructed object is the identity endomorphism; if set to ``True``,
+          then N must be M and the entry ``matrix_rep`` is not used.
+
+        EXAMPLES:
+
+        Construction of an endomorphism::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: EM = End(M)
+            sage: phi = EM._element_constructor_([[1,2,3],[4,5,6],[7,8,9]],
+            ....:                                name='phi', latex_name=r'\phi')
+            sage: phi
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi.matrix(e,e)
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+
+        Coercion of a type-`(1,1)` tensor to an endomorphism::
 
-    #### Monoid methods (case of an endomorphism set) ####
+            sage: a = M.tensor((1,1))
+            sage: a[:] = [[1,2,3],[4,5,6],[7,8,9]]
+            sage: EM = End(M)
+            sage: phi_a = EM._element_constructor_(a) ; phi_a
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi_a.matrix(e,e)
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+            sage: phi_a == phi
+            True
+            sage: phi_a1 = EM(a) ; phi_a1  # indirect doctest
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi_a1 == phi
+            True
+
+        """
+        if isinstance(matrix_rep, FreeModuleTensor):
+            # coercion of a type-(1,1) tensor to an endomorphism
+            # (this includes automorphisms, since the class
+            #  FreeModuleAutomorphism inherits from FreeModuleTensor)
+            tensor = matrix_rep  # for readability
+            if tensor.tensor_type() == (1,1) and tensor.base_module() is self.domain():
+                basis = tensor.pick_a_basis()
+                tcomp = tensor.comp(basis)
+                fmodule = tensor.base_module()
+                mat = [[tcomp[[i,j]] for j in fmodule.irange()]
+                       for i in fmodule.irange()]
+                if isinstance(tensor, FreeModuleAutomorphism):
+                    is_identity = tensor._is_identity
+                else:
+                    is_identity = False
+                return self.element_class(self, mat, bases=(basis,basis),
+                                          name=tensor._name,
+                                          latex_name=tensor._latex_name,
+                                          is_identity=is_identity)
+            else:
+                raise TypeError("cannot coerce the {}".format(tensor) +
+                                " to an element of {}".format(self))
+        return super()._element_constructor_(matrix_rep, bases=bases,
+                                             name=name, latex_name=latex_name,
+                                             is_identity=is_identity)
+
+    #### Monoid methods ####
 
     def one(self):
         r"""
-        Return the identity element of ``self`` considered as a monoid (case of
-        an endomorphism set).
-
-        This applies only when the codomain of ``self`` is equal to its domain,
-        i.e. when ``self`` is of the type `\mathrm{Hom}(M,M)`.
+        Return the identity element of ``self`` considered as a monoid.
 
         OUTPUT:
 
@@ -526,8 +655,6 @@ def one(self):
 
         """
         if self._one is None:
-            if self.codomain() != self.domain():
-                raise TypeError("the {} is not a monoid".format(self))
             self._one = self.element_class(self, [], is_identity=True)
         return self._one
 
diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py
index 073cb91e60c..ba905a989ea 100644
--- a/src/sage/tensor/modules/free_module_morphism.py
+++ b/src/sage/tensor/modules/free_module_morphism.py
@@ -4,9 +4,13 @@
 The class :class:`FiniteRankFreeModuleMorphism` implements homomorphisms
 between two free modules of finite rank over the same commutative ring.
 
+The subclass :class:`FiniteRankFreeModuleEndomorphism` implements the
+special case of endomorphisms.
+
 AUTHORS:
 
 - Eric Gourgoulhon, Michal Bejger (2014-2015): initial version
+- Matthias Koeppe (2024): add subclass :class:`FiniteRankFreeModuleEndomorphism`
 
 REFERENCES:
 
@@ -28,6 +32,7 @@
 from typing import TYPE_CHECKING
 
 from sage.categories.morphism import Morphism
+from sage.misc.lazy_attribute import lazy_attribute
 from sage.rings.integer import Integer
 
 if TYPE_CHECKING:
@@ -50,6 +55,9 @@ class FiniteRankFreeModuleMorphism(Morphism):
     This is a Sage *element* class, the corresponding *parent* class being
     :class:`~sage.tensor.modules.free_module_homset.FreeModuleHomset`.
 
+    For the special case of endomorphisms (`M=N`), use the subclass
+    :class:`FiniteRankFreeModuleEndomorphism`.
+
     INPUT:
 
     - ``parent`` -- hom-set Hom(M,N) to which the homomorphism belongs
@@ -64,10 +72,7 @@ class FiniteRankFreeModuleMorphism(Morphism):
       default bases of each module is assumed.
     - ``name`` -- (default: ``None``) string; name given to the homomorphism
     - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
-      homomorphism; if None, ``name`` will be used.
-    - ``is_identity`` -- (default: ``False``) determines whether the
-      constructed object is the identity endomorphism; if set to ``True``, then
-      N must be M and the entry ``matrix_rep`` is not used.
+      homomorphism; if ``None``, ``name`` will be used.
 
     EXAMPLES:
 
@@ -166,31 +171,6 @@ class FiniteRankFreeModuleMorphism(Morphism):
         34 f_0 - 51 f_1
         sage: phi(3*u + v) == 3*phi(u) + phi(v)
         True
-
-    The identity endomorphism::
-
-        sage: Id = End(M).one() ; Id
-        Identity endomorphism of Rank-3 free module M over the Integer Ring
-        sage: Id.parent()
-        Set of Morphisms from Rank-3 free module M over the Integer Ring
-         to Rank-3 free module M over the Integer Ring
-         in Category of finite dimensional modules over Integer Ring
-        sage: Id.parent() is End(M)
-        True
-
-    The matrix of Id with respect to the basis e is of course the identity
-    matrix::
-
-        sage: Id.matrix(e)
-        [1 0 0]
-        [0 1 0]
-        [0 0 1]
-
-    The identity acting on a module element::
-
-        sage: Id(v) is v
-        True
-
     """
     def __init__(self, parent, matrix_rep, bases=None, name=None,
                  latex_name=None, is_identity=False):
@@ -214,27 +194,9 @@ def __init__(self, parent, matrix_rep, bases=None, name=None,
             [ 2  1  4]
             sage: latex(phi)
             \phi
-
-        Generic endomorphism::
-
-            sage: phi = FiniteRankFreeModuleMorphism(End(M), [[1,0,-3], [2,1,4], [7,8,9]],
-            ....:                                    name='phi', latex_name=r'\phi')
-            sage: phi
-            Generic endomorphism of Rank-3 free module M over the Integer Ring
-
-        Identity endomorphism::
-
-            sage: phi = FiniteRankFreeModuleMorphism(End(M), 'whatever', is_identity=True)
-            sage: phi
-            Identity endomorphism of Rank-3 free module M over the Integer Ring
-            sage: phi.matrix(e)
-            [1 0 0]
-            [0 1 0]
-            [0 0 1]
-            sage: latex(phi)
-            \mathrm{Id}
-
         """
+        if is_identity:
+            raise TypeError('use the subclass FiniteRankFreeModuleEndomorphism for the identity morphis')
         from sage.matrix.constructor import matrix
         from sage.misc.constant_function import ConstantFunction
         Morphism.__init__(self, parent)
@@ -263,41 +225,12 @@ def __init__(self, parent, matrix_rep, bases=None, name=None,
         ring = parent.base_ring()
         n1 = fmodule1.rank()
         n2 = fmodule2.rank()
-        if is_identity:
-            # Construction of the identity endomorphism
-            if fmodule1 != fmodule2:
-                raise TypeError("the domain and codomain must coincide " +
-                                "for the identity endomorphism.")
-            if bases[0] != bases[1]:
-                raise TypeError("the two bases must coincide for " +
-                                "constructing the identity endomorphism.")
-            self._is_identity = True
-            zero = ring.zero()
-            one = ring.one()
-            matrix_rep = []
-            for i in range(n1):
-                row = [zero]*n1
-                row[i] = one
-                matrix_rep.append(row)
-            if name is None:
-                name = 'Id'
-            if latex_name is None and name == 'Id':
-                latex_name = r'\mathrm{Id}'
-            self._repr_type_str = 'Identity'
-        else:
-            # Construction of a generic morphism
-            self._is_identity = False
-            if isinstance(matrix_rep, ConstantFunction):
-                # the zero morphism
-                if matrix_rep().is_zero():
-                    matrix_rep = 0
-            if matrix_rep == 1:
-                if fmodule1 == fmodule2:
-                    # the identity endomorphism (again):
-                    self._is_identity = True
-                    self._repr_type_str = 'Identity'
-                    name = 'Id'
-                    latex_name = r'\mathrm{Id}'
+        # Construction of a generic morphism
+        self._is_identity = False
+        if isinstance(matrix_rep, ConstantFunction):
+            # the zero morphism
+            if matrix_rep().is_zero():
+                matrix_rep = 0
         self._matrices = {bases: matrix(ring, n2, n1, matrix_rep)}
         self._name = name
         if latex_name is None:
@@ -1021,6 +954,58 @@ def is_identity(self):
     # End of Morphism methods
     #
 
+    def _modules_and_bases(self, basis1=None, basis2=None):
+        r"""
+        Return domain, codomain, domain basis, and codomain basis.
+
+        This method implements default argument handling for methods
+        :meth:`matrix` and :meth:`display`.
+
+        INPUT:
+
+        - ``basis1`` -- (default: ``None``) basis of the domain of ``self``; if
+          none is provided, the domain's default basis is assumed
+        - ``basis2`` -- (default: ``None``) basis of the codomain of ``self``;
+          if none is provided, ``basis2`` is set to ``basis1`` if ``self`` is
+          an endomorphism, otherwise, ``basis2`` is set to the codomain's
+          default basis.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: e = M.basis('e'); f = N.basis('f')
+            sage: phi = M.hom(N, [[-1,2,0], [5,1,2]])
+            sage: phi._modules_and_bases()
+            (Rank-3 free module M over the Integer Ring,
+             Rank-2 free module N over the Integer Ring,
+             Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring,
+             Basis (f_0,f_1) on the Rank-2 free module N over the Integer Ring)
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi._modules_and_bases(ep)
+            (Rank-3 free module M over the Integer Ring,
+             Rank-2 free module N over the Integer Ring,
+             Basis (ep_0,ep_1,ep_2) on the Rank-3 free module M over the Integer Ring,
+             Basis (f_0,f_1) on the Rank-2 free module N over the Integer Ring)
+        """
+        fmodule1 = self.domain()
+        fmodule2 = self.codomain()
+        if basis1 is None:
+            basis1 = fmodule1.default_basis()
+        elif basis1 not in fmodule1.bases():
+            raise TypeError(str(basis1) + " is not a basis on the " +
+                            str(fmodule1) + ".")
+        if basis2 is None:
+            if self.is_endomorphism():
+                basis2 = basis1
+            else:
+                basis2 = fmodule2.default_basis()
+        elif basis2 not in fmodule2.bases():
+            raise TypeError(str(basis2) + " is not a basis on the " +
+                            str(fmodule2) + ".")
+        return fmodule1, fmodule2, basis1, basis2
+
     def matrix(self, basis1=None, basis2=None):
         r"""
         Return the matrix of ``self`` w.r.t to a pair of bases.
@@ -1108,21 +1093,7 @@ def matrix(self, basis1=None, basis2=None):
 
         """
         from sage.matrix.constructor import matrix
-        fmodule1 = self.domain()
-        fmodule2 = self.codomain()
-        if basis1 is None:
-            basis1 = fmodule1.default_basis()
-        elif basis1 not in fmodule1.bases():
-            raise TypeError(str(basis1) + " is not a basis on the " +
-                            str(fmodule1) + ".")
-        if basis2 is None:
-            if self.is_endomorphism():
-                basis2 = basis1
-            else:
-                basis2 = fmodule2.default_basis()
-        elif basis2 not in fmodule2.bases():
-            raise TypeError(str(basis2) + " is not a basis on the " +
-                            str(fmodule2) + ".")
+        fmodule1, fmodule2, basis1, basis2 = self._modules_and_bases(basis1, basis2)
         if (basis1, basis2) not in self._matrices:
             if self._is_identity:
                 # The identity endomorphism
@@ -1247,3 +1218,391 @@ def _common_bases(self, other):
                 except ValueError:
                     continue
         return resu
+
+    def display(self, basis1=None, basis2=None):
+        r"""
+        Display ``self`` as a matrix w.r.t to a pair of bases.
+
+        If the matrix is not known already, it is computed from the matrix in
+        another pair of bases by means of the change-of-basis formula.
+
+        INPUT:
+
+        - ``basis1`` -- (default: ``None``) basis of the domain of ``self``; if
+          none is provided, the domain's default basis is assumed
+        - ``basis2`` -- (default: ``None``) basis of the codomain of ``self``;
+          if none is provided, ``basis2`` is set to ``basis1`` if ``self`` is
+          an endomorphism, otherwise, ``basis2`` is set to the codomain's
+          default basis.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: e = M.basis('e'); f = N.basis('f')
+            sage: phi = M.hom(N, [[-1,2,0], [5,1,2]])
+            sage: phi.display()     # default bases
+                e_0 e_1 e_2
+            f_0⎛ -1   2   0⎞
+            f_1⎝  5   1   2⎠
+            sage: phi.display(e, f)  # given bases
+                e_0 e_1 e_2
+            f_0⎛ -1   2   0⎞
+            f_1⎝  5   1   2⎠
+
+        Matrix of an endomorphism::
+
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.display(ep)
+                 ep_0 ep_1 ep_2
+            ep_0⎛   1    2    3⎞
+            ep_1⎜   4    5    6⎟
+            ep_2⎝   7    8    9⎠
+            sage: phi.display(ep, ep)  # same as above
+                 ep_0 ep_1 ep_2
+            ep_0⎛   1    2    3⎞
+            ep_1⎜   4    5    6⎟
+            ep_2⎝   7    8    9⎠
+            sage: phi.display()  # matrix w.r.t to the module's default basis
+                e_0 e_1 e_2
+            e_0⎛  1  -3   1⎞
+            e_1⎜-18  39 -18⎟
+            e_2⎝-25  54 -25⎠
+        """
+        from sage.misc.latex import latex
+        from .format_utilities import is_atomic, FormattedExpansion
+        fmodule1, fmodule2, basis1, basis2 = self._modules_and_bases(basis1, basis2)
+        matrix = self.matrix(basis1, basis2)
+        if all(element._name for element in basis1):
+            basis1_names = [element._name for element in basis1]
+        else:
+            basis1_names = None
+        if all(element._name for element in basis2):
+            basis2_names = [element._name for element in basis2]
+        else:
+            basis2_names = None
+        resu_txt = matrix.str(unicode=True,
+                              top_border=basis1_names,
+                              left_border=basis2_names)
+        resu_latex = latex(matrix)
+        return FormattedExpansion(resu_txt, resu_latex)
+
+
+class FiniteRankFreeModuleEndomorphism(FiniteRankFreeModuleMorphism):
+    r"""
+    Endomorphism of a free module of finite rank over a commutative ring.
+
+    An instance of this class is an endomorphism
+
+    .. MATH::
+
+        \phi:\ M \longrightarrow M,
+
+    where `M` is a free module of finite rank over a commutative ring `R`.
+
+    This is a Sage *element* class, the corresponding *parent* class being
+    :class:`~sage.tensor.modules.free_module_homset.FreeModuleEndset`.
+
+    INPUT:
+
+    - ``parent`` -- hom-set Hom(M,M) to which the endomorphism belongs
+    - ``matrix_rep`` -- matrix representation of the endomorphism with
+      respect to the basis ``bases``; this entry can actually
+      be any material from which a matrix of size rank(N)*rank(M) of
+      elements of `R` can be constructed; the *columns* of the matrix give
+      the images of the basis of `M` (see the convention in the example below)
+    - ``bases`` -- (default: ``None``) pair ``(basis_domain, basis_codomain)``
+      defining the matrix representation, ``basis_domain`` and ``basis_codomain``
+      being two bases (typically the same) of the same module `M`; if ``None``,
+      the default basis of `M` is used for both.
+    - ``name`` -- (default: ``None``) string; name given to the endomorphism
+    - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
+      endomorphism; if ``None``, ``name`` will be used.
+    - ``is_identity`` -- (default: ``False``) determines whether the
+      constructed object is the identity endomorphism; if set to ``True``,
+      then the entry ``matrix_rep`` is not used.
+
+    EXAMPLES:
+
+    The identity endomorphism::
+
+        sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+        sage: e = M.basis('e')
+        sage: Id = End(M).one(); Id
+        Identity endomorphism of Rank-3 free module M over the Integer Ring
+        sage: Id.parent()
+        Set of Morphisms from Rank-3 free module M over the Integer Ring
+         to Rank-3 free module M over the Integer Ring
+         in Category of finite dimensional modules over Integer Ring
+        sage: Id.parent() is End(M)
+        True
+
+    The matrix of ``Id`` with respect to the basis ``e`` is of course the identity
+    matrix::
+
+        sage: Id.matrix(e)
+        [1 0 0]
+        [0 1 0]
+        [0 0 1]
+
+    The identity acting on a module element::
+
+        sage: v = M([-2,1,4], basis=e, name='v'); v.display()
+        v = -2 e_0 + e_1 + 4 e_2
+        sage: Id(v) is v
+        True
+    """
+    def __init__(self, parent, matrix_rep, bases=None, name=None,
+                 latex_name=None, is_identity=False):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleEndomorphism
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+
+        Generic endomorphism::
+
+            sage: phi = FiniteRankFreeModuleEndomorphism(End(M),
+            ....:                                        [[1,0,-3], [2,1,4], [7,8,9]],
+            ....:                                        name='phi', latex_name=r'\phi')
+            sage: phi
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+
+        Identity endomorphism::
+
+            sage: phi = FiniteRankFreeModuleEndomorphism(End(M), 'whatever',
+            ....:                                        is_identity=True)
+            sage: phi
+            Identity endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi.matrix(e)
+            [1 0 0]
+            [0 1 0]
+            [0 0 1]
+            sage: latex(phi)
+            \mathrm{Id}
+        """
+        from sage.matrix.special import identity_matrix
+        from sage.misc.constant_function import ConstantFunction
+
+        fmodule = parent.domain()
+        if bases is None:
+            def_basis = fmodule.default_basis()
+            if def_basis is None:
+                raise ValueError("the {} has no default ".format(fmodule) +
+                                 "basis")
+            bases = (def_basis, def_basis)
+        else:
+            bases = tuple(bases)  # insures bases is a tuple
+            if len(bases) != 2:
+                raise TypeError("the argument bases must contain 2 bases")
+            if bases[0] not in fmodule.bases():
+                raise TypeError("{} is not a basis on the {}".format(bases[0],
+                                                                     fmodule))
+            if bases[1] not in fmodule.bases():
+                raise TypeError("{} is not a basis on the {}".format(bases[1],
+                                                                     fmodule))
+        if not is_identity:
+            # Construction of a generic endomorphism
+            if isinstance(matrix_rep, ConstantFunction):
+                # the zero morphism
+                if matrix_rep().is_zero():
+                    matrix_rep = 0
+            if bases[0] == bases[1] and matrix_rep == 1:
+                is_identity = True
+        if is_identity:
+            # Construction of the identity endomorphism
+            if bases[0] != bases[1]:
+                raise TypeError("the two bases must coincide for " +
+                                "constructing the identity endomorphism.")
+            matrix_rep = 1
+            if name is None:
+                name = 'Id'
+            if latex_name is None and name == 'Id':
+                latex_name = r'\mathrm{Id}'
+        FiniteRankFreeModuleMorphism.__init__(self, parent, matrix_rep, bases,
+                                              name, latex_name)
+        if is_identity:
+            self._is_identity = True
+            self._repr_type_str = 'Identity'
+
+    def _some_matrix(self):
+        r"""
+        Return the matrix of the endomorphism ``self`` w.r.t. some basis.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi._some_matrix()
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+        """
+        for (basis1, basis2), matrix in self._matrices.items():
+            if basis1 == basis2:
+                return matrix
+        for basis in self.domain().bases():
+            try:
+                return self.matrix(basis)
+            except ValueError:
+                pass
+        return self.matrix()
+
+    @lazy_attribute
+    def characteristic_polynomial(self):
+        r"""
+        Return the characteristic polynomial of ``self``.
+
+        :meth:`characteristic_polynomial` and :meth:`charpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.characteristic_polynomial()
+            x^3 - 15*x^2 - 18*x
+            sage: phi.charpoly()
+            x^3 - 15*x^2 - 18*x
+            sage: phi.charpoly('T')
+            T^3 - 15*T^2 - 18*T
+        """
+        return self._some_matrix().characteristic_polynomial
+
+    charpoly = characteristic_polynomial
+
+    @lazy_attribute
+    def det(self):
+        r"""
+        Return the determinant of ``self``.
+
+        OUTPUT:
+
+        - element of the base ring of the modules on which ``self`` is defined,
+          equal to the determinant of ``self``.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.det()
+            0
+            sage: det(phi)
+            0
+        """
+        return self._some_matrix().det
+
+    determinant = det
+
+    @lazy_attribute
+    def fcp(self):
+        r"""
+        Return the factorization of the characteristic polynomial of ``self``.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.fcp()                                                             # needs sage.libs.pari
+            x * (x^2 - 15*x - 18)
+            sage: phi.fcp('T')                                                          # needs sage.libs.pari
+            T * (T^2 - 15*T - 18)
+        """
+        return self._some_matrix().fcp
+
+    @lazy_attribute
+    def minimal_polynomial(self):
+        r"""
+        Return the minimal polynomial of ``self``.
+
+        :meth:`minimal_polynomial` and :meth:`minpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.minpoly()                                                         # needs sage.libs.pari
+            x^3 - 15*x^2 - 18*x
+            sage: phi.minimal_polynomial()                                              # needs sage.libs.pari
+            x^3 - 15*x^2 - 18*x
+            sage: phi.minimal_polynomial('T')                                           # needs sage.libs.pari
+            T^3 - 15*T^2 - 18*T
+        """
+        return self._some_matrix().minimal_polynomial
+
+    minpoly = minimal_polynomial
+
+    @lazy_attribute
+    def trace(self):
+        r"""
+        Return the trace of ``self``.
+
+        OUTPUT:
+
+        - element of the base ring of the modules on which ``self`` is defined,
+          equal to the trace of ``self``.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.trace()
+            15
+            sage: id = M.identity_map()
+            sage: id.trace()
+            3
+
+        """
+        return self._some_matrix().trace
diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py
index 2dd54a64c1a..5cd35997b0a 100644
--- a/src/sage/tensor/modules/free_module_tensor.py
+++ b/src/sage/tensor/modules/free_module_tensor.py
@@ -825,11 +825,11 @@ def display_comp(self, basis=None, format_spec=None, symbol=None,
         The LaTeX output for the notebook::
 
             sage: latex(t.display_comp())
-            \begin{array}{lcl} T_{\phantom{\, 1}\phantom{\, 2}\,1}^{\,1\,2\phantom{\, 1}}
-             & = & \frac{2}{3} \\ T_{\phantom{\, 1}\phantom{\, 2}\,2}^{\,1\,2\phantom{\, 2}}
-             & = & -\frac{1}{4} \\ T_{\phantom{\, 2}\phantom{\, 1}\,1}^{\,2\,1\phantom{\, 1}}
-             & = & \frac{2}{3} \\ T_{\phantom{\, 2}\phantom{\, 1}\,2}^{\,2\,1\phantom{\, 2}}
-             & = & -\frac{1}{4} \\ T_{\phantom{\, 2}\phantom{\, 2}\,2}^{\,2\,2\phantom{\, 2}}
+            \begin{array}{lcl} {T}_{\phantom{\, 1}\phantom{\, 2}\,1}^{\,1\,2\phantom{\, 1}}
+             & = & \frac{2}{3} \\ {T}_{\phantom{\, 1}\phantom{\, 2}\,2}^{\,1\,2\phantom{\, 2}}
+             & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,1}^{\,2\,1\phantom{\, 1}}
+             & = & \frac{2}{3} \\ {T}_{\phantom{\, 2}\phantom{\, 1}\,2}^{\,2\,1\phantom{\, 2}}
+             & = & -\frac{1}{4} \\ {T}_{\phantom{\, 2}\phantom{\, 2}\,2}^{\,2\,2\phantom{\, 2}}
              & = & 3 \end{array}
 
         By default, only the non-vanishing components are displayed; to see
@@ -887,7 +887,7 @@ def display_comp(self, basis=None, format_spec=None, symbol=None,
                 symbol = 'X'
         if latex_symbol is None:
             if self._latex_name is not None:
-                latex_symbol = self._latex_name
+                latex_symbol = r'{' + self._latex_name + r'}'
             else:
                 latex_symbol = 'X'
         index_positions = self._tensor_type[0]*'u' + self._tensor_type[1]*'d'
diff --git a/src/sage/topology/cubical_complex.py b/src/sage/topology/cubical_complex.py
index 4e8e80a564b..ebabf526b15 100644
--- a/src/sage/topology/cubical_complex.py
+++ b/src/sage/topology/cubical_complex.py
@@ -1689,30 +1689,6 @@ def algebraic_topological_model(self, base_ring=None):
             base_ring = QQ
         return algebraic_topological_model(self, base_ring)
 
-    def _chomp_repr_(self):
-        r"""
-        String representation of self suitable for use by the CHomP
-        program.  This lists each maximal cube on its own line.
-
-        This function is deprecated.
-
-        EXAMPLES::
-
-            sage: C = cubical_complexes.Cube(0).product(cubical_complexes.Cube(2))
-            sage: C.maximal_cells()
-            {[0,0] x [0,1] x [0,1]}
-            sage: C._chomp_repr_()
-            doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
-            See https://github.com/sagemath/sage/issues/33777 for details.
-            '[0,0] x [0,1] x [0,1]\n'
-        """
-        deprecation(33777, "the CHomP interface is deprecated; hence so is this function")
-        s = ""
-        for c in self.maximal_cells():
-            s += str(c)
-            s += "\n"
-        return s
-
     def _simplicial_(self):
         r"""
         Simplicial complex constructed from self.
diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py
index f7870435782..9c1e8e8f7f1 100644
--- a/src/sage/topology/simplicial_complex.py
+++ b/src/sage/topology/simplicial_complex.py
@@ -4496,42 +4496,6 @@ def _translation_from_numeric(self):
         d = self._vertex_to_index
         return {idx: v for v, idx in d.items()}
 
-    def _chomp_repr_(self):
-        r"""
-        String representation of ``self`` suitable for use by the CHomP
-        program.  This lists each facet on its own line, and makes
-        sure vertices are listed as numbers.
-
-        This function is deprecated.
-
-        EXAMPLES::
-
-            sage: S = SimplicialComplex([(0,1,2), (2,3,5)])
-            sage: print(S._chomp_repr_())
-            doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
-            See https://github.com/sagemath/sage/issues/33777 for details.
-            (2, 3, 5)
-            (0, 1, 2)
-
-        A simplicial complex whose vertices are tuples, not integers::
-
-            sage: S = SimplicialComplex([[(0,1), (1,2), (3,4)]])
-            sage: S._chomp_repr_()
-            '(0, 1, 2)\n'
-        """
-        deprecation(33777, "the CHomP interface is deprecated; hence so is this function")
-        s = ""
-        numeric = self._is_numeric()
-        if not numeric:
-            d = self._translation_to_numeric()
-        for f in self.facets():
-            if numeric:
-                s += str(f)
-            else:
-                s += '(' + ', '.join(str(d[a]) for a in f) + ')'
-            s += '\n'
-        return s
-
     # this function overrides the standard one for GenericCellComplex,
     # because it lists the maximal faces, not the total number of faces.
     def _repr_(self):
diff --git a/src/sage/version.py b/src/sage/version.py
index 191a5474629..35a9364e251 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.4.beta4'
-date = '2024-04-27'
-banner = 'SageMath version 10.4.beta4, Release Date: 2024-04-27'
+version = '10.4.beta6'
+date = '2024-05-12'
+banner = 'SageMath version 10.4.beta6, Release Date: 2024-05-12'
diff --git a/src/sage_setup/clean.py b/src/sage_setup/clean.py
index e9c81c9ed1a..1b00f211d75 100644
--- a/src/sage_setup/clean.py
+++ b/src/sage_setup/clean.py
@@ -80,6 +80,7 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module
     course. We check that when the doctest is being run, that is,
     after installation, there are no stale files::
 
+        sage: # needs SAGE_SRC
         sage: from sage.env import SAGE_SRC, SAGE_LIB, SAGE_ROOT
         sage: from sage_setup.find import _cythonized_dir
         sage: cythonized_dir = _cythonized_dir(SAGE_SRC)
@@ -98,6 +99,7 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module
 
     TODO: Also check extension modules::
 
+        sage: # needs SAGE_SRC
         sage: stale_iter = _find_stale_files(SAGE_LIB, python_packages, python_modules, [], extra_files)
         sage: from importlib.machinery import EXTENSION_SUFFIXES
         sage: skip_extensions = tuple(EXTENSION_SUFFIXES)
diff --git a/src/sage_setup/find.py b/src/sage_setup/find.py
index 61d91abc2eb..69bad9a3ebf 100644
--- a/src/sage_setup/find.py
+++ b/src/sage_setup/find.py
@@ -1,3 +1,4 @@
+# sage.doctest: needs SAGE_SRC
 """
 Recursive Directory Contents
 """
diff --git a/src/tox.ini b/src/tox.ini
index aab671441a9..ca69ed6c8bf 100644
--- a/src/tox.ini
+++ b/src/tox.ini
@@ -26,8 +26,10 @@ envlist = doctest, coverage, startuptime, pycodestyle-minimal, relint, codespell
 skipsdist = true
 
 requires =
-    # For the renamed "allowlist_externals" keyword
+    # For the renamed "allowlist_externals" keyword, need >= 3.18
+    # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
     tox>=3.18
+    tox<4.14.1
 
 [sagedirect]
 # Base for tox environments that bypass the virtual environment set up by tox,
@@ -86,6 +88,24 @@ commands_post =
     {env:SAGE} --python -m coverage report
     {env:SAGE} --python -m coverage html -d "{envdir}"
 
+[testenv:coverage.py-xml]
+# https://coverage.readthedocs.io/en/latest/index.html
+description =
+    run the Sage doctester with Coverage.py, generate XML report
+## This toxenv bypasses the virtual environment set up by tox.
+passenv = {[sagedirect]passenv}
+setenv  = {[sagedirect]setenv}
+envdir  = {[sagedirect]envdir}
+allowlist_externals = {[sagedirect]allowlist_externals}
+commands_pre =
+    {env:SAGE} -pip install -U coverage
+commands =
+    {env:SAGE} --python -m coverage run "{toxinidir}/../venv/bin/sage-runtests" -p 0 {posargs:--all}
+commands_post =
+    {env:SAGE} --python -m coverage combine
+    {env:SAGE} --python -m coverage report
+    {env:SAGE} --python -m coverage xml -o "{envdir}/coverage.xml"
+
 [testenv:coverage]
 description =
     give information about doctest coverage of files
diff --git a/tox.ini b/tox.ini
index 837764e6441..b06f4529afc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -129,8 +129,10 @@ envlist =
     # pycodestyle
 
 requires =
-    # For repaired numerical factors in tox 4:
+    # For repaired numerical factors in tox 4, need >= 4.2.7.
+    # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
     tox>=4.2.7
+    tox<4.14.1
 
 skipsdist = true
 
@@ -185,6 +187,7 @@ setenv =
     # What system packages should be installed. Default: All standard packages with spkg-configure.
                            SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard:
     recommended:             EXTRA_SAGE_PACKAGES_3=_recommended $(head -n 1 build/pkgs/_recommended/dependencies)
+    incremental:             EXTRA_SAGE_PACKAGES_4=git
     develop:                 EXTRA_SAGE_PACKAGES_4=_develop $(head -n 1 build/pkgs/_develop/dependencies)
     minimal:               SAGE_PACKAGE_LIST_ARGS=_prereq
     maximal:               SAGE_PACKAGE_LIST_ARGS=:standard: :optional:
@@ -499,8 +502,9 @@ setenv =
     #
     docker:             FULL_BASE_IMAGE_AND_TAG={env:ARCH_IMAGE_PREFIX:}{env:BASE_IMAGE}{env:ARCH_IMAGE_SUFFIX:}:{env:ARCH_TAG_PREFIX:}{env:BASE_TAG}{env:ARCH_TAG_SUFFIX:}
     docker-incremental: FULL_BASE_IMAGE_AND_TAG={env:FROM_DOCKER_REPOSITORY:ghcr.io/sagemath/sage/}sage-$(echo {envname} | sed -E "s/(docker-|-incremental|-sitepackages)//g")-{env:FROM_DOCKER_TARGET:with-targets}:{env:FROM_DOCKER_TAG:dev}
-    docker-incremental:              SKIP_SYSTEM_PKG_INSTALL=yes
-    docker-incremental-sitepackages: SKIP_SYSTEM_PKG_INSTALL=no
+    # Can SKIP_SYSTEM_PKG_INSTALL if the base image already has git
+    docker-incremental-{develop,recommended,maximal}:   SKIP_SYSTEM_PKG_INSTALL=yes
+    docker-incremental-sitepackages:                    SKIP_SYSTEM_PKG_INSTALL=no
     #
     docker-nobootstrap: BOOTSTRAP=./bootstrap -D
     ###
@@ -754,7 +758,7 @@ commands =
     local:         bash -c 'if [ ! -d prefix -o -L prefix ]; then rm -f prefix; ln -sf {env:PREFIX:{envdir}/local} prefix; fi'
 
 ##commands =
-    docker:        bash -c 'build/bin/write-dockerfile.sh {env:SYSTEM} "{env:SAGE_PACKAGE_LIST_ARGS:}" {env:WITH_SYSTEM_SPKG} {env:IGNORE_MISSING_SYSTEM_PACKAGES} "{env:ALL_EXTRA_SAGE_PACKAGES}" > {envdir}/Dockerfile'
+    docker:        bash -c '.ci/write-dockerfile.sh {env:SYSTEM} "{env:SAGE_PACKAGE_LIST_ARGS:}" {env:WITH_SYSTEM_SPKG} {env:IGNORE_MISSING_SYSTEM_PACKAGES} "{env:ALL_EXTRA_SAGE_PACKAGES}" > {envdir}/Dockerfile'
     # From https://hub.docker.com/r/multiarch/ubuntu-core/
     # configure binfmt-support on the Docker host (works locally or remotely, i.e: using boot2docker)
     docker-{arm64,armhf}:  docker run --rm --privileged multiarch/qemu-user-static:register --reset
@@ -768,10 +772,6 @@ commands =
     docker:            docker build . -f {envdir}/Dockerfile \
     docker:              --target $docker_target \
     docker:              $TAG_ARGS \
-    docker:              --build-arg EXTRA_CONFIGURE_ARGS="{env:CONFIGURE_ARGS}" \
-    docker:              --build-arg BASE_IMAGE={env:FULL_BASE_IMAGE_AND_TAG} \
-    docker-conda:        --build-arg USE_CONDARC="{env:USE_CONDARC}" \
-    docker:              --build-arg BOOTSTRAP="{env:BOOTSTRAP}" \
     docker:              --build-arg TARGETS_PRE="$(if test -n "$TARGETS_PRE"; then echo $TARGETS_PRE; else echo {posargs:all-sage-local}; fi)" \
     docker:              --build-arg TARGETS="{posargs:build}" \
     docker:              --build-arg TARGETS_OPTIONAL="{env:TARGETS_OPTIONAL:ptest}" \
@@ -997,7 +997,22 @@ allowlist_externals = {[sage_src]allowlist_externals}
 [testenv:coverage.py]
 description =
     run the Sage doctester with Coverage.py
-    (https://coverage.readthedocs.io/en/latest/index.html)
+passenv  = {[sage_src]passenv}
+envdir   = {[sage_src]envdir}
+commands = {[sage_src]commands}
+allowlist_externals = {[sage_src]allowlist_externals}
+
+[testenv:coverage.py-html]
+description =
+    run the Sage doctester with Coverage.py, generate HTML report
+passenv  = {[sage_src]passenv}
+envdir   = {[sage_src]envdir}
+commands = {[sage_src]commands}
+allowlist_externals = {[sage_src]allowlist_externals}
+
+[testenv:coverage.py-xml]
+description =
+    run the Sage doctester with Coverage.py, generate XML report
 passenv  = {[sage_src]passenv}
 envdir   = {[sage_src]envdir}
 commands = {[sage_src]commands}