diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index 2cca91d81c8..0e80d0d2814 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -1,11 +1,12 @@ #!/bin/sh if [ $# != 2 ]; then - echo >&2 "usage: $0 BASE_DOC_COMMIT DOC_REPO" - echo >&2 "creates CHANGES.html in the current directory" - echo >&2 "for the diffs of DOC_REPO against BASE_DOC_COMMIT" + echo >&2 "Usage: $0 DIFF_TEXT DOC_REPO" + echo >&2 "This script generates a CHANGES.html file in the current directory" + echo >&2 "and adds anchor targets in the documents within DOC_REPO" + echo >&2 "based on the diff hunks in the DIFF_TEXT file." exit 1 fi -BASE_DOC_COMMIT="$1" +DIFF_TEXT="$1" DOC_REPOSITORY="$2" # Create CHANGES.html @@ -52,11 +53,10 @@ diffParagraphs.forEach(paragraph => { EOF echo '' >> CHANGES.html echo '' >> CHANGES.html -(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- "*.html") > diff.txt python3 - << EOF import os, re, html from itertools import chain -with open('diff.txt', 'r') as f: +with open('$DIFF_TEXT', 'r') as f: diff_text = f.read() diff_blocks = re.split(r'^(?=diff --git)', diff_text, flags=re.MULTILINE) out_blocks = [] @@ -83,12 +83,21 @@ for block in diff_blocks: hunk_lines = [] search_result = re.search(r'@@ -(\d+),(\d+) \+(\d+),(\d+)', line) if search_result: - line_number = int(search_result.group(3)) + line_number = int(search_result.group(3)) - 1 span = int(search_result.group(4)) for i in chain(range(line_number, line_number + span), range(line_number - 1, -1, -1)): - if content[i].startswith('<') and not content[i].startswith('' + content[i] + content[i] = ln[:idx] + f'' + ln[idx:] hunks.append(f'

hunk #{count}

') break hunk_lines.append(line) @@ -107,4 +116,4 @@ EOF cat diff.html >> CHANGES.html echo '' >> CHANGES.html echo '' >> CHANGES.html -rm diff.txt diff.html +rm diff.html diff --git a/.ci/retrofit-worktree.sh b/.ci/retrofit-worktree.sh index 4c283f7ff4a..782d3ba44b7 100755 --- a/.ci/retrofit-worktree.sh +++ b/.ci/retrofit-worktree.sh @@ -43,4 +43,4 @@ git worktree add --detach $WORKTREE_NAME rm -rf $WORKTREE_DIRECTORY/.git && mv $WORKTREE_NAME/.git $WORKTREE_DIRECTORY/ rm -rf $WORKTREE_NAME && ln -s $WORKTREE_DIRECTORY $WORKTREE_NAME if [ ! -f $WORKTREE_NAME/.gitignore ]; then cp .gitignore $WORKTREE_NAME/; fi -(cd $WORKTREE_NAME && git add -A && git commit --quiet --allow-empty -m "old" -a && git tag -f old && git checkout new && git status) +(cd $WORKTREE_NAME && git add -A && git commit --quiet --allow-empty -m "old" -a && git tag -f old && git checkout -f new && git clean -fd && git status) diff --git a/.ci/write-dockerfile.sh b/.ci/write-dockerfile.sh index 75f89b23868..cfd4fb20810 100755 --- a/.ci/write-dockerfile.sh +++ b/.ci/write-dockerfile.sh @@ -35,7 +35,7 @@ STRIP_COMMENTS="sed s/#.*//;" SAGE_ROOT=. export PATH="$SAGE_ROOT"/build/bin:$PATH SYSTEM_PACKAGES=$EXTRA_SYSTEM_PACKAGES -SYSTEM_CONFIGURE_ARGS="--enable-option-checking " +SYSTEM_CONFIGURE_ARGS=" --enable-option-checking" for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do SYSTEM_PACKAGE=$(sage-get-system-packages $SYSTEM $SPKG) if [ -n "${SYSTEM_PACKAGE}" ]; then @@ -45,13 +45,20 @@ for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ # shell-quote package if necessary SYSTEM_PACKAGES+=$(printf " %q" "$a") done - SYSTEM_CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} " + # Check if SPKG is not a dummy package + if [[ $SPKG != _* ]]; then + SYSTEM_CONFIGURE_ARGS+=" --with-system-${SPKG}=${WITH_SYSTEM_SPKG}" + fi fi done echo "# Automatically generated by SAGE_ROOT/.ci/write-dockerfile.sh" echo "# the :comments: separate the generated file into sections" echo "# to simplify writing scripts that customize this file" -ADD="ADD $__CHOWN" +if [ -z "$__CHOWN" ]; then + ADD="ADD" +else + ADD="ADD $__CHOWN" +fi RUN=RUN cat < /dev/null; then \ - (yes | unminimize) || echo "(ignored)"; \ - rm -f "\$(command -v unminimize)"; \ - fi +RUN if command -v unminimize > /dev/null; then (yes | unminimize) || echo "(ignored)"; rm -f "\$(command -v unminimize)"; fi EOF if [ -n "$DIST_UPGRADE" ]; then cat <> /sage/.gitignore && \ - printf '/src\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /new/.gitignore && \ - if ! (cd /new && /.ci/retrofit-worktree.sh worktree-image /sage); then \ - echo "retrofit-worktree.sh failed, falling back to replacing /sage"; \ - for a in local logs; do \ - if [ -d /sage/\$a ]; then mv /sage/\$a /new/; fi; \ - done; \ - rm -rf /sage; \ - mv /new /sage; \ - fi; \ - else \ - mv /new /sage; \ +RUN if [ -d /sage ]; then \\ + echo "### Incremental build from \$(cat /sage/VERSION.txt)" && \\ + printf '/src/*\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /sage/.gitignore && \\ + printf '/src/*\n!/src/doc/bootstrap\n!/src/bin\n!/src/*.m4\n!/src/*.toml\n!/src/VERSION.txt\n' >> /new/.gitignore && \\ + if ! (cd /new && /.ci/retrofit-worktree.sh worktree-image /sage); then \\ + echo "retrofit-worktree.sh failed, falling back to replacing /sage"; \\ + for a in local logs; do \\ + if [ -d /sage/\$a ]; then mv /sage/\$a /new/; fi; \\ + done; \\ + rm -rf /sage; \\ + mv /new /sage; \\ + fi; \\ + else \\ + mv /new /sage; \\ fi WORKDIR /sage - ARG BOOTSTRAP="${BOOTSTRAP-./bootstrap}" -$RUN sh -x -c "\${BOOTSTRAP}" $ENDRUN $THEN_SAVE_STATUS +$RUN sh -x -c "\${BOOTSTRAP}"$ENDRUN$THEN_SAVE_STATUS FROM bootstrapped AS configured #:configuring: -RUN $CHECK_STATUS_THEN mkdir -p logs/pkgs; rm -f config.log; ln -s logs/pkgs/config.log config.log +RUN$CHECK_STATUS_THEN mkdir -p logs/pkgs; rm -f config.log; ln -s logs/pkgs/config.log config.log ARG CONFIGURE_ARGS="${CONFIGURE_ARGS:---enable-build-as-root}" EOF if [ ${WITH_SYSTEM_SPKG} = "force" ]; then cat <> $GITHUB_OUTPUT + echo "build_targets=$uninstall_targets reconfigure $build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT else - echo "build_targets=$build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT + echo "build_targets=$build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT fi cat $GITHUB_OUTPUT + - uses: actions/checkout@v4 with: ref: ${{ github.base_ref }} path: worktree-base if: github.base_ref && steps.changed-files.outputs.pkgs_all_changed_files + - name: Compute metrics run: | export PATH=build/bin:$PATH @@ -140,6 +163,7 @@ jobs: else sage-package metrics :all: fi + - name: Install test prerequisites # From docker.yml run: | @@ -147,6 +171,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -176,7 +201,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: # push and load may not be set together at the moment # @@ -254,9 +279,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -264,6 +291,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -291,7 +319,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: push: true load: false @@ -352,9 +380,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -362,6 +392,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -389,7 +420,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: push: true load: false @@ -442,8 +473,11 @@ jobs: if: (success() || failure()) && steps.container.outcome == 'success' uses: actions/upload-artifact@v4 with: + # The path .coverage is a directory that contains the combined coverage + # data file .coverage, which is a hidden file because of the leading dot name: coverage-${{ steps.copy-coverage.outputs.tests_id }} path: .coverage + include-hidden-files: true coverage-report: runs-on: ubuntu-latest @@ -466,9 +500,11 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites # From docker.yml run: | @@ -476,6 +512,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install tox sudo apt-get clean df -h + - name: Merge CI fixes from sagemath/sage # From docker.yml # This step needs to happen after the commit sha is put in DOCKER_TAG @@ -503,7 +540,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: push: true load: false @@ -530,11 +567,11 @@ jobs: # Combining - name: Download coverage artifacts + if: (success() || failure()) && steps.container.outcome == 'success' uses: actions/download-artifact@v4 with: path: .coverage pattern: coverage-* - if: (success() || failure()) && steps.container.outcome == 'success' - name: Coverage report if: (success() || failure()) && steps.container.outcome == 'success' diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index a7900534b32..8606e7a4ca5 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@v4 - name: Get all packages that have changed id: changed-files - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v45 with: files_yaml: | configures: @@ -60,9 +60,9 @@ jobs: uninstall_targets=$(echo $(for a in '' ${{ steps.changed-files.outputs.configures_all_changed_files }}; do echo $a | sed -E 's,build/pkgs/([a-z0-9][_.a-z0-9]*)/spkg-configure[.]m4 *,\1-uninstall,'; done | sort -u)) build_targets=$(echo $(for a in '' ${{ steps.changed-files.outputs.pkgs_all_changed_files }}; do SPKG=$(echo $a | sed -E 's,-,_,g;s,(build/)?pkgs/([a-z0-9][-_.a-z0-9]*)/[^ ]* *,\2,;'); if [ -f "build/pkgs/$SPKG/checksums.ini" -o -f "build/pkgs/$SPKG/requirements.txt" -o -f "build/pkgs/$SPKG/spkg-install" ]; then echo "$SPKG-ensure"; fi; done | sort -u)) if [ -n "$uninstall_targets" ]; then - echo "build_targets=$uninstall_targets reconfigure $build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT + echo "build_targets=$uninstall_targets reconfigure $build_targets" >> $GITHUB_OUTPUT else - echo "build_targets=$build_targets ci-build-with-fallback" >> $GITHUB_OUTPUT + echo "build_targets=$build_targets" >> $GITHUB_OUTPUT fi cat $GITHUB_OUTPUT - uses: actions/checkout@v4 @@ -92,7 +92,7 @@ jobs: from_docker_target: "with-targets" from_docker_tag: "dev" docker_targets: "with-targets" - targets: "${{needs.changed_files.outputs.build_targets}} doc-html ptest-nodoc" + targets: "${{needs.changed_files.outputs.build_targets}} ci-build-with-fallback doc-html ptest-nodoc" tox_system_factors: >- ["ubuntu-focal", "ubuntu-noble", @@ -108,6 +108,29 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ max_parallel: 8 + constraints_pkgs-norequirements: + needs: [changed_files] + uses: ./.github/workflows/docker.yml + with: + # Build incrementally from published Docker image + incremental: true + free_disk_space: true + from_docker_repository: ghcr.io/sagemath/sage/ + from_docker_target: "with-targets-pre" + from_docker_tag: "dev" + docker_targets: "with-targets-pre" + targets_pre: "${{needs.changed_files.outputs.build_targets}} all-sage-local python3-ensure tox-ensure sagelib-tox-sagepython-constraints_pkgs-norequirements" + tox_system_factors: >- + ["ubuntu-focal", + "ubuntu-noble", + "debian-bookworm", + "fedora-40", + "debian-bullseye-i386"] + tox_packages_factors: >- + ["standard"] + docker_push_repository: ghcr.io/${{ github.repository }}/ + max_parallel: 16 + site: needs: [changed_files] uses: ./.github/workflows/docker.yml diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index cae29a27819..ca4607e3cc3 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -34,7 +34,7 @@ permissions: jobs: - # standard-pre and standard (without ptest) for the default platform (used by build.yml etc.) + # standard (without ptest) for the default platform (used by build.yml etc.) default: uses: ./.github/workflows/docker.yml with: @@ -54,24 +54,27 @@ jobs: # All platforms. This duplicates the default platform, but why not, # it makes it more robust regarding random timeouts. - standard-pre: + standard: if: ${{ success() || failure() }} uses: ./.github/workflows/docker.yml with: # Build from scratch - docker_targets: "with-system-packages configured with-targets-pre" + free_disk_space: true + docker_targets: "with-system-packages configured with-targets-pre with-targets with-targets-optional" # FIXME: duplicated from env.TARGETS targets_pre: all-sage-local + targets: build doc-html + targets_optional: ptest tox_packages_factors: >- ["standard"] docker_push_repository: ghcr.io/${{ github.repository }}/ - # Make sure that all "standard-pre" jobs can start simultaneously, + # Make sure that all "standard" jobs can start simultaneously, # so that runners are available by the time that "default" starts. max_parallel: 50 - standard: + standard-constraints_pkgs-norequirements: if: ${{ success() || failure() }} - needs: [standard-pre] + needs: [standard] uses: ./.github/workflows/docker.yml with: # Build incrementally from previous stage (pre) @@ -79,20 +82,15 @@ jobs: free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" - docker_targets: "with-targets with-targets-optional" - # FIXME: duplicated from env.TARGETS - targets: build doc-html - targets_optional: ptest + docker_targets: "with-targets-pre" + targets_pre: all-sage-local python3-ensure tox-ensure sagelib-tox-sagepython-constraints_pkgs-norequirements tox_packages_factors: >- ["standard"] - docker_push_repository: ghcr.io/${{ github.repository }}/ - # Reduce from 30 to 20 because it runs in parallel with 'standard-sitepackages' - # and 'minimal-pre' below - max_parallel: 20 + max_parallel: 15 standard-sitepackages: if: ${{ success() || failure() }} - needs: [standard-pre] + needs: [standard] uses: ./.github/workflows/docker.yml with: # Build incrementally from previous stage (pre) @@ -128,38 +126,22 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ max_parallel: 8 - minimal-pre: - if: ${{ success() || failure() }} - uses: ./.github/workflows/docker.yml - with: - # Build from scratch - docker_targets: "with-system-packages configured with-targets-pre" - # FIXME: duplicated from env.TARGETS - targets_pre: all-sage-local - tox_packages_factors: >- - ["minimal"] - docker_push_repository: ghcr.io/${{ github.repository }}/ - # Reduced from 30 because it may run in parallel with 'standard' and 'standard-sitepackages' above. - # Calibrated for clogging the job pipeline until the "default" job has finished. - max_parallel: 24 - minimal: if: ${{ success() || failure() }} - needs: [minimal-pre] uses: ./.github/workflows/docker.yml with: - # Build incrementally from previous stage (pre) - incremental: true + # Build from scratch free_disk_space: true - from_docker_repository: ghcr.io/${{ github.repository }}/ - from_docker_target: "with-targets-pre" - docker_targets: "with-targets with-targets-optional" + docker_targets: "with-system-packages configured with-targets-pre with-targets with-targets-optional" # FIXME: duplicated from env.TARGETS + targets_pre: all-sage-local targets: build doc-html targets_optional: ptest tox_packages_factors: >- ["minimal"] docker_push_repository: ghcr.io/${{ github.repository }}/ + # Reduced from 30 because it may run in parallel with 'standard' and 'standard-sitepackages' above. + # Calibrated for clogging the job pipeline until the "default" job has finished. max_parallel: 24 maximal-pre: @@ -176,7 +158,7 @@ jobs: ["maximal"] docker_push_repository: ghcr.io/${{ github.repository }}/ - optional-0-o: + optional: if: ${{ success() || failure() }} needs: [maximal-pre] uses: ./.github/workflows/docker.yml @@ -188,42 +170,12 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - # [0-9a-o] excludes _, in particular package _develop - targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[0-9a-o]))' - - - optional-p-z: - if: ${{ success() || failure() }} - needs: [optional-0-o] - uses: ./.github/workflows/docker.yml - with: - incremental: true - free_disk_space: true - from_docker_repository: ghcr.io/${{ github.repository }}/ - from_docker_target: "with-targets-pre" - tox_packages_factors: >- - ["maximal"] - docker_targets: "with-targets-optional" - # [0-9a-o] excludes _, in particular package _develop - targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[p-z]))' - - experimental-0-o: - if: ${{ success() || failure() }} - needs: [optional-p-z] - uses: ./.github/workflows/docker.yml - with: - incremental: true - free_disk_space: true - from_docker_repository: ghcr.io/${{ github.repository }}/ - from_docker_target: "with-targets-pre" - tox_packages_factors: >- - ["maximal"] - docker_targets: "with-targets-optional" - targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[0-9a-o]))' + # We remove packages starting with _, in particular package _develop + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep -v ^_))' - experimental-p-z: + experimental: if: ${{ success() || failure() }} - needs: [experimental-0-o] + needs: [optional] uses: ./.github/workflows/docker.yml with: incremental: true @@ -233,4 +185,4 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[p-z]))' + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc))' diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml new file mode 100644 index 00000000000..3ecccb4c16c --- /dev/null +++ b/.github/workflows/ci-meson.yml @@ -0,0 +1,79 @@ +name: Build & Test using Meson + +on: + push: + branches: + - master + - develop + pull_request: + 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: + test: + name: Conda (${{ matrix.os }}, Python ${{ matrix.python }}) + runs-on: ${{ matrix.os }}-latest + + strategy: + fail-fast: false + matrix: + os: [ubuntu] + python: ['3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v4 + + - name: Merge CI fixes from sagemath/sage + run: | + .ci/merge-fixes.sh + env: + GH_TOKEN: ${{ github.token }} + + - name: Cache conda packages + uses: actions/cache@v4 + with: + path: ~/conda_pkgs_dir + key: + ${{ runner.os }}-conda-${{ hashFiles('src/environment-3.11-linux.yml') }} + + - name: Compiler cache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ runner.os }}-meson-${{ matrix.python }} + + - name: Setup Conda environment + uses: conda-incubator/setup-miniconda@v2 + with: + python-version: ${{ matrix.python }} + miniforge-version: latest + use-mamba: true + channels: conda-forge + channel-priority: true + activate-environment: sage + environment-file: src/environment-${{ matrix.python }}-${{ startsWith(matrix.os, 'macos') && (startsWith(runner.arch, 'ARM') && 'macos' || 'macos-x86_64') || 'linux' }}.yml + + - name: Print Conda environment + shell: bash -l {0} + run: | + conda info + conda list + + - name: Build + shell: bash -l {0} + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + export CC="ccache $CC" + export CXX="ccache $CXX" + pip install --no-build-isolation --config-settings=builddir=builddir . -v + + - name: Test + shell: bash -l {0} + run: | + # We don't install sage_setup, so don't try to test it + rm -R ./src/sage_setup/ + ./sage -t --all -p4 diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 8cc8249e3c6..538b44d1431 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -76,24 +76,6 @@ jobs: upstream name: release_dist - release: - - needs: release_dist - 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@v4 - with: - name: release_dist - - uses: softprops/action-gh-release@v2 - with: - generate_release_notes: true - files: | - dist/* - upstream/* - permissions: - contents: write - sdists_for_pypi: runs-on: ubuntu-latest @@ -124,6 +106,29 @@ jobs: verbose: true if: env.CAN_DEPLOY == 'true' + release: + + needs: [release_dist, sdists_for_pypi] + runs-on: ubuntu-latest + if: (success() || failure()) && github.repository == 'sagemath/sage' && startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/download-artifact@v4 + with: + name: release_dist + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist + - uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + files: | + dist/* + upstream/* + permissions: + contents: write + noarch_wheels_for_pypi: runs-on: ubuntu-latest @@ -258,6 +263,13 @@ jobs: run: | "${{ steps.python.outputs.python-path }}" -m cibuildwheel unpacked/sagemath*mcqd* + - name: sagemath-tdlib + run: | + case "${{ matrix.arch }}" in + i686) ;; # broken - boost-related + *) "${{ steps.python.outputs.python-path }}" -m cibuildwheel unpacked/sagemath*tdlib* + esac + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.os }}-${{ matrix.build }}-${{ matrix.arch }}-wheels diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml index 0c1085138b1..dce25a132c9 100644 --- a/.github/workflows/doc-build-pdf.yml +++ b/.github/workflows/doc-build-pdf.yml @@ -84,7 +84,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: # push and load may not be set together at the moment push: true diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index c5ff832b591..9deab60f8be 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -95,7 +95,7 @@ jobs: - name: Build Docker image id: image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: # push and load may not be set together at the moment push: true @@ -204,14 +204,18 @@ jobs: -e 's;#L[0-9]*";";' \ && git commit -a -m 'wipe-out') # Since HEAD is at commit 'wipe-out', HEAD~1 is commit 'new' (new doc), HEAD~2 is commit 'old' (old doc) - .ci/create-changes-html.sh $(cd doc && git rev-parse HEAD~2) doc - # Restore the new doc with changes made in create-changes-html.sh but dropping changes by "wipe out" - (cd doc && git stash -q && git checkout -q -f HEAD~1 && git stash pop -q) + (cd doc && git diff $(git rev-parse HEAD~2) -- "*.html") > diff.txt + # Restore the new doc dropping changes by "wipe out" + (cd doc && git checkout -q -f HEAD~1) + .ci/create-changes-html.sh diff.txt doc # Sometimes rm -rf .git errors out because of some diehard hidden files # So we simply move it out of the doc directory (cd doc && mv .git ../git && mv .gitattributes ../gitattributes) mv CHANGES.html doc fi + # Create the robots.txt file to discourage web crawlers from indexing doc preview webpages + echo "User-agent: *" > doc/robots.txt + echo "Disallow: /" >> doc/robots.txt # Zip everything for increased performance zip -r doc.zip doc diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2b6d83f6e78..4f3d1441544 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,7 +47,9 @@ on: "fedora-38", "fedora-39", "fedora-40", - "centos-stream-9-python3.9", + "fedora-41", + "centos-stream-9", + "centos-stream-9-python3.12", "almalinux-8-python3.9", "almalinux-9-python3.11", "gentoo-python3.10", @@ -242,9 +244,16 @@ jobs: # The arcane "find" command is a replacement for "pkill make", # which we use because pkill is not installed in the "minimal" package # configuration on many platforms. + # + # The "sed" command strips away timestamps from "docker build" (buildkit) + # such as "#25 1211.0" at the beginning of each line. The timestamps are + # redundant because GH Actions provides timestamps for each line already. + # Stripping the timestamps from the beginnings of lines also allows + # GitHub Actions to recognize workflow commands such as ::error etc. + # run: | (sleep ${{ inputs.timeout }}; for id in $(docker ps -q); do docker exec $id find /proc -maxdepth 2 -name cmdline -exec bash -c "grep -l [m][a][k][e] {} | cut -d/ -f3 | xargs --no-run-if-empty kill" \;; done) & - set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg NUMPROC=9 --build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=5\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;" + set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg NUMPROC=9 --build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=5\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed -E --unbuffered "s/^#[0-9]+ [0-9]+[.][0-9]+ //;/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;" - name: Copy logs from the Docker image or build container run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME" diff --git a/.github/workflows/docker_hub.yml b/.github/workflows/docker_hub.yml index bb5b5232284..ed07bc2f03e 100644 --- a/.github/workflows/docker_hub.yml +++ b/.github/workflows/docker_hub.yml @@ -82,7 +82,7 @@ jobs: if: env.JOB_DONE == 'false' && env.CAN_LOGIN == 'true' - name: Build and push make-build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: docker/Dockerfile diff --git a/.gitignore b/.gitignore index 70a6739c33b..7d8c2f0adc4 100644 --- a/.gitignore +++ b/.gitignore @@ -146,10 +146,12 @@ __pycache__/ # Generated by sage_setup.autogen /src/sage/ext/interpreters +!/src/sage/ext/interpreters/meson.build # Generated Cython files *.so **/*.so +**/*.so.old /src/cython_debug # Most C and C++ files are generated by Cython and should not # be included in the sdist. @@ -299,3 +301,171 @@ src/.coverage/ # git worktree worktree* **/worktree* + +# Meson build directory +builddir +builddir-* +build-install +build/cp* + +# Meson temporary files +src/sage/interfaces/__init__.py +src/sage/crypto/block_cipher/__init__.py +src/sage/crypto/public_key/__init__.py +src/sage/logic/__init__.py +src/sage/parallel/__init__.py +src/sage/dynamics/cellular_automata/__init__.py +src/sage/dynamics/arithmetic_dynamics/__init__.py +src/sage/dynamics/__init__.py +src/sage/dynamics/complex_dynamics/__init__.py +src/sage/knots/__init__.py +src/sage/topology/__init__.py +src/sage/functions/__init__.py +src/sage/manifolds/subsets/__init__.py +src/sage/manifolds/__init__.py +src/sage/manifolds/differentiable/examples/__init__.py +src/sage/manifolds/differentiable/__init__.py +src/sage/coding/source_coding/__init__.py +src/sage/coding/guruswami_sudan/__init__.py +src/sage/coding/__init__.py +src/sage/coding/codecan/__init__.py +src/sage/games/__init__.py +src/sage/quivers/__init__.py +src/sage/schemes/cyclic_covers/__init__.py +src/sage/schemes/plane_conics/__init__.py +src/sage/schemes/curves/__init__.py +src/sage/schemes/plane_quartics/__init__.py +src/sage/schemes/jacobians/__init__.py +src/sage/schemes/toric/sheaf/__init__.py +src/sage/schemes/toric/__init__.py +src/sage/schemes/product_projective/__init__.py +src/sage/schemes/elliptic_curves/__init__.py +src/sage/schemes/riemann_surfaces/__init__.py +src/sage/schemes/hyperelliptic_curves/__init__.py +src/sage/schemes/berkovich/__init__.py +src/sage/schemes/generic/__init__.py +src/sage/schemes/projective/__init__.py +src/sage/schemes/__init__.py +src/sage/schemes/affine/__init__.py +src/sage/modular/hecke/__init__.py +src/sage/modular/pollack_stevens/__init__.py +src/sage/modular/overconvergent/__init__.py +src/sage/modular/modform/__init__.py +src/sage/modular/quasimodform/__init__.py +src/sage/modular/modsym/__init__.py +src/sage/modular/local_comp/__init__.py +src/sage/modular/quatalg/__init__.py +src/sage/modular/ssmod/__init__.py +src/sage/modular/abvar/__init__.py +src/sage/modular/__init__.py +src/sage/modular/btquotients/__init__.py +src/sage/modular/arithgroup/__init__.py +src/sage/modular/modform_hecketriangle/__init__.py +src/sage/combinat/cluster_algebra_quiver/__init__.py +src/sage/combinat/root_system/__init__.py +src/sage/combinat/species/__init__.py +src/sage/combinat/designs/__init__.py +src/sage/combinat/posets/__init__.py +src/sage/combinat/matrices/__init__.py +src/sage/combinat/rigged_configurations/__init__.py +src/sage/combinat/ncsf_qsym/__init__.py +src/sage/combinat/path_tableaux/__init__.py +src/sage/combinat/sf/__init__.py +src/sage/combinat/__init__.py +src/sage/combinat/chas/__init__.py +src/sage/combinat/ncsym/__init__.py +src/sage/combinat/words/__init__.py +src/sage/combinat/crystals/__init__.py +src/sage/tensor/modules/__init__.py +src/sage/tensor/__init__.py +src/sage/groups/matrix_gps/__init__.py +src/sage/groups/semimonomial_transformations/__init__.py +src/sage/groups/perm_gps/partn_ref2/__init__.py +src/sage/groups/perm_gps/partn_ref/__init__.py +src/sage/groups/perm_gps/__init__.py +src/sage/groups/__init__.py +src/sage/groups/affine_gps/__init__.py +src/sage/groups/abelian_gps/__init__.py +src/sage/groups/additive_abelian/__init__.py +src/sage/groups/lie_gps/__init__.py +src/sage/groups/misc_gps/__init__.py +src/sage/symbolic/__init__.py +src/sage/symbolic/integration/__init__.py +src/sage/lfunctions/__init__.py +src/sage/arith/__init__.py +src/sage/ext/__init__.py +src/sage/categories/examples/__init__.py +src/sage/categories/__init__.py +src/sage/modules/fg_pid/__init__.py +src/sage/modules/__init__.py +src/sage/modules/with_basis/__init__.py +src/sage/modules/fp_graded/steenrod/__init__.py +src/sage/modules/fp_graded/__init__.py +src/sage/misc/__init__.py +src/sage/rings/convert/__init__.py +src/sage/rings/invariants/__init__.py +src/sage/rings/finite_rings/__init__.py +src/sage/rings/function_field/__init__.py +src/sage/rings/function_field/drinfeld_modules/__init__.py +src/sage/rings/semirings/__init__.py +src/sage/rings/number_field/__init__.py +src/sage/rings/__init__.py +src/sage/rings/padics/__init__.py +src/sage/rings/valuation/__init__.py +src/sage/rings/asymptotic/__init__.py +src/sage/rings/polynomial/weil/__init__.py +src/sage/rings/polynomial/__init__.py +src/sage/rings/polynomial/padics/__init__.py +src/sage/monoids/__init__.py +src/sage/matrix/__init__.py +src/sage/matroids/__init__.py +src/sage/interacts/__init__.py +src/sage/__init__.py +src/sage/plot/__init__.py +src/sage/plot/plot3d/__init__.py +src/sage/typeset/__init__.py +src/sage/algebras/lie_conformal_algebras/__init__.py +src/sage/algebras/fusion_rings/__init__.py +src/sage/algebras/letterplace/__init__.py +src/sage/algebras/quatalg/__init__.py +src/sage/algebras/steenrod/__init__.py +src/sage/algebras/finite_dimensional_algebras/__init__.py +src/sage/algebras/__init__.py +src/sage/algebras/hecke_algebras/__init__.py +src/sage/algebras/lie_algebras/__init__.py +src/sage/algebras/quantum_groups/__init__.py +src/sage/quadratic_forms/genera/__init__.py +src/sage/quadratic_forms/__init__.py +src/sage/game_theory/__init__.py +src/sage/sandpiles/__init__.py +src/sage/sat/__init__.py +src/sage/homology/__init__.py +src/sage/geometry/riemannian_manifolds/__init__.py +src/sage/geometry/hyperplane_arrangement/__init__.py +src/sage/geometry/triangulation/__init__.py +src/sage/geometry/polyhedron/modules/__init__.py +src/sage/geometry/polyhedron/__init__.py +src/sage/geometry/polyhedron/combinatorial_polyhedron/__init__.py +src/sage/geometry/__init__.py +src/sage/geometry/hyperbolic_space/__init__.py +src/sage/sets/__init__.py +src/sage/probability/__init__.py +src/sage/numerical/backends/__init__.py +src/sage/numerical/__init__.py +src/sage/data_structures/__init__.py +src/sage/graphs/graph_decompositions/__init__.py +src/sage/graphs/generators/__init__.py +src/sage/graphs/__init__.py +src/sage/graphs/base/__init__.py +src/sage/databases/__init__.py +src/sage/stats/hmm/__init__.py +src/sage/stats/__init__.py +src/sage/stats/distributions/__init__.py +src/sage/libs/gap/__init__.py +src/sage/libs/mpfi/__init__.py +src/sage/libs/__init__.py +src/sage/libs/polybori/__init__.py +src/sage/libs/mpfr/__init__.py +src/sage/libs/mpc/__init__.py +src/sage/calculus/transforms/__init__.py +src/sage/calculus/__init__.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 86eac03ffe9..052b55fe189 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,7 +15,8 @@ "pkgs/sage-conf_pypi/sage_root": true, "pkgs/sage-docbuild/sage_docbuild": true, "pkgs/sage-setup/sage_setup": true, - "pkgs/sagemath-*/sage": true + "pkgs/sagemath-*/sage": true, + "pkgs/sagemath-*/sage_setup": true }, "python.testing.pytestEnabled": true, "python.testing.pytestArgs": [ diff --git a/CITATION.cff b/CITATION.cff index bc17013072e..27b5af6ac84 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.5.beta2 +version: 10.5.beta9 doi: 10.5281/zenodo.8042260 -date-released: 2024-08-10 +date-released: 2024-11-03 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/README.md b/README.md index 23d10cca6d5..af91374fe19 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ virtualization). Detailed information on supported platforms for a specific version of Sage can be found in the section _Availability and installation help_ of the -[release tour](https://wiki.sagemath.org/ReleaseTours) for this version. +[release tour for this version](https://github.com/sagemath/sage/releases). We highly appreciate contributions to Sage that fix portability bugs and help port Sage to new platforms; let us know at the [sage-devel @@ -92,7 +92,7 @@ below](#sagemath-docker-images)) or other virtualization solutions. [macOS] Preparing the Platform ------------------------------ -- If your Mac uses the Apple Silicon (M1, M2, M3; arm64) architecture and +- If your Mac uses the Apple Silicon (M1, M2, M3, M4; arm64) architecture and you set up your Mac by transferring files from an older Mac, make sure that the directory ``/usr/local`` does not contain an old copy of Homebrew (or other software) for the x86_64 architecture that you may have copied @@ -489,8 +489,8 @@ Troubleshooting --------------- If you have problems building Sage, check the Sage Installation Guide, -as well as the version-specific Sage Installation FAQ in the [Sage Release -Tour](https://wiki.sagemath.org/ReleaseTours) corresponding to the +as well as the version-specific installation help in the [release +tour](https://github.com/sagemath/sage/releases) corresponding to the version that you are installing. Please do not hesitate to ask for help in the [SageMath forum diff --git a/VERSION.txt b/VERSION.txt index edf3563b259..01ace814c72 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.5.beta2, Release Date: 2024-08-10 +SageMath version 10.5.beta9, Release Date: 2024-11-03 diff --git a/bootstrap-conda b/bootstrap-conda index c64e2a72b6c..27e657281f9 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -99,6 +99,9 @@ echo >&2 $0:$LINENO: generate conda environment files ) > environment-template.yml ( sed 's/name: sage-build/name: sage/' environment-template.yml + echo " - meson" + echo " - meson-python" + echo " - pytest" echo " # Additional packages providing all dependencies for the Sage library" for pkg in $SAGELIB_SYSTEM_PACKAGES; do echo " - $pkg" diff --git a/build/make/Makefile.in b/build/make/Makefile.in index a59c81db529..420dadb9364 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -779,12 +779,14 @@ $(1)-sdist: FORCE python_build sage_setup cython . '$$(SAGE_ROOT)/src/bin/sage-env' && \ '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-src' -# Recursive tox invocation (note - we do not set the environment here). +# Recursive tox invocation # Setting SAGE_SPKG_WHEELS is for the benefit of sagelib's tox.ini $(1)-tox-%: FORCE $(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)/src' && \ - export PATH="$$(SAGE_ORIG_PATH)" && \ - SAGE_SPKG_WHEELS=$$(SAGE_LOCAL)/var/lib/sage/wheels \ + . '$$(SAGE_ROOT)/src/bin/sage-src-env-config' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env' && \ + SAGE_SPKG_WHEELS=$$(SAGE_VENV)/var/lib/sage/wheels \ tox -v -v -v -e $$* .PHONY: $(1) $(1)-uninstall $(1)-clean $(1)-build-deps $(1)-no-deps $(1)-clean diff --git a/build/pkgs/_python3.10/distros/fedora.txt b/build/pkgs/_python3.10/distros/fedora.txt index 02cc9cf73d1..8d607cac749 100644 --- a/build/pkgs/_python3.10/distros/fedora.txt +++ b/build/pkgs/_python3.10/distros/fedora.txt @@ -1,2 +1,3 @@ python3.10 python3.10-devel +python3.10-setuptools diff --git a/build/pkgs/_python3.11/distros/fedora.txt b/build/pkgs/_python3.11/distros/fedora.txt index 7fe5c300aed..439583212d6 100644 --- a/build/pkgs/_python3.11/distros/fedora.txt +++ b/build/pkgs/_python3.11/distros/fedora.txt @@ -1,2 +1,3 @@ python3.11 python3.11-devel +python3.11-setuptools diff --git a/build/pkgs/_python3.12/distros/fedora.txt b/build/pkgs/_python3.12/distros/fedora.txt index c3e29a02d27..bfc5e84864c 100644 --- a/build/pkgs/_python3.12/distros/fedora.txt +++ b/build/pkgs/_python3.12/distros/fedora.txt @@ -1,2 +1,3 @@ python3.12 python3.12-devel +python3.12-setuptools diff --git a/build/pkgs/_python3.9/distros/fedora.txt b/build/pkgs/_python3.9/distros/fedora.txt index 0e9352ce64c..466ad79cab3 100644 --- a/build/pkgs/_python3.9/distros/fedora.txt +++ b/build/pkgs/_python3.9/distros/fedora.txt @@ -1,3 +1,4 @@ python3.9 +python3.9-devel # Except on centos-stream-8 and almalinux-8, where it is called python39 and python39-devel; we special-case this in tox.ini -# python3.9 does not exist any more +python3.9-setuptools diff --git a/build/pkgs/alabaster/checksums.ini b/build/pkgs/alabaster/checksums.ini index a58c93e8f4a..137d299783d 100644 --- a/build/pkgs/alabaster/checksums.ini +++ b/build/pkgs/alabaster/checksums.ini @@ -1,4 +1,4 @@ tarball=alabaster-VERSION-py3-none-any.whl sha1=6c86446396c69236a1542e09771e8d7b8487dcfa sha256=b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92 -upstream_url=https://pypi.io/packages/py3/a/alabaster/alabaster-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/a/alabaster/alabaster-VERSION-py3-none-any.whl diff --git a/build/pkgs/anyio/checksums.ini b/build/pkgs/anyio/checksums.ini index 6559f060ac2..54c6718bec4 100644 --- a/build/pkgs/anyio/checksums.ini +++ b/build/pkgs/anyio/checksums.ini @@ -1,4 +1,4 @@ tarball=anyio-VERSION-py3-none-any.whl sha1=f5bd548b3a14c9c93622bf04f7c6464e3f44966a sha256=c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7 -upstream_url=https://pypi.io/packages/py3/a/anyio/anyio-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/a/anyio/anyio-VERSION-py3-none-any.whl diff --git a/build/pkgs/appdirs/checksums.ini b/build/pkgs/appdirs/checksums.ini index 0d44544631d..8e218bedd50 100644 --- a/build/pkgs/appdirs/checksums.ini +++ b/build/pkgs/appdirs/checksums.ini @@ -1,4 +1,4 @@ tarball=appdirs-VERSION-py2.py3-none-any.whl sha1=fc74022712122436427f8282a47bfa430ec2db56 sha256=a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128 -upstream_url=https://pypi.io/packages/py2.py3/a/appdirs/appdirs-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/a/appdirs/appdirs-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/appnope/checksums.ini b/build/pkgs/appnope/checksums.ini index 4e49f102dd5..8a91ec591b3 100644 --- a/build/pkgs/appnope/checksums.ini +++ b/build/pkgs/appnope/checksums.ini @@ -1,4 +1,4 @@ tarball=appnope-VERSION.tar.gz sha1=4dcd80020b345a184d6da6063a69e25b1d353323 sha256=1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee -upstream_url=https://pypi.io/packages/source/a/appnope/appnope-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/a/appnope/appnope-VERSION.tar.gz diff --git a/build/pkgs/argon2_cffi/checksums.ini b/build/pkgs/argon2_cffi/checksums.ini index acfeee93d3a..503f84c348e 100644 --- a/build/pkgs/argon2_cffi/checksums.ini +++ b/build/pkgs/argon2_cffi/checksums.ini @@ -1,4 +1,4 @@ tarball=argon2-cffi-VERSION.tar.gz sha1=c16c1506de0211bdfa23d4d51e780fb4aaff5222 sha256=d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b -upstream_url=https://pypi.io/packages/source/a/argon2_cffi/argon2-cffi-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/a/argon2_cffi/argon2-cffi-VERSION.tar.gz diff --git a/build/pkgs/argon2_cffi_bindings/checksums.ini b/build/pkgs/argon2_cffi_bindings/checksums.ini index b0e7ade0b5a..1d190691c09 100644 --- a/build/pkgs/argon2_cffi_bindings/checksums.ini +++ b/build/pkgs/argon2_cffi_bindings/checksums.ini @@ -1,4 +1,4 @@ tarball=argon2-cffi-bindings-VERSION.tar.gz sha1=5a9b8906d9ca73c53c2bf0a2f0a8127fda69e965 sha256=bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3 -upstream_url=https://pypi.io/packages/source/a/argon2_cffi_bindings/argon2-cffi-bindings-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/a/argon2_cffi_bindings/argon2-cffi-bindings-VERSION.tar.gz diff --git a/build/pkgs/arrow/checksums.ini b/build/pkgs/arrow/checksums.ini index 5ac9740854e..bc3fd448d7f 100644 --- a/build/pkgs/arrow/checksums.ini +++ b/build/pkgs/arrow/checksums.ini @@ -1,4 +1,4 @@ tarball=arrow-VERSION-py3-none-any.whl sha1=fd9376ef4788dc2b1c981e6b5beb9048e046c556 sha256=c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80 -upstream_url=https://pypi.io/packages/py3/a/arrow/arrow-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/a/arrow/arrow-VERSION-py3-none-any.whl diff --git a/build/pkgs/asttokens/checksums.ini b/build/pkgs/asttokens/checksums.ini index d9de4a44ea3..aabceb9def5 100644 --- a/build/pkgs/asttokens/checksums.ini +++ b/build/pkgs/asttokens/checksums.ini @@ -1,4 +1,4 @@ tarball=asttokens-VERSION-py2.py3-none-any.whl sha1=69a9448cd7fad3007a66f464f9daa35dd28183a6 sha256=051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24 -upstream_url=https://pypi.io/packages/py2.py3/a/asttokens/asttokens-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/a/asttokens/asttokens-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/async_lru/checksums.ini b/build/pkgs/async_lru/checksums.ini index f1b60406ec1..a8022baf28d 100644 --- a/build/pkgs/async_lru/checksums.ini +++ b/build/pkgs/async_lru/checksums.ini @@ -1,4 +1,4 @@ tarball=async_lru-VERSION-py3-none-any.whl sha1=99b2ea5d551cbad28e08e45f0d0b00827f9ff73d sha256=ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224 -upstream_url=https://pypi.io/packages/py3/a/async_lru/async_lru-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/a/async_lru/async_lru-VERSION-py3-none-any.whl diff --git a/build/pkgs/attrs/checksums.ini b/build/pkgs/attrs/checksums.ini index 7d271509d6d..74bf62402c0 100644 --- a/build/pkgs/attrs/checksums.ini +++ b/build/pkgs/attrs/checksums.ini @@ -1,4 +1,4 @@ tarball=attrs-VERSION-py3-none-any.whl sha1=563b272af703f8960b76f6637d009fa5fc5864d3 sha256=99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 -upstream_url=https://pypi.io/packages/py3/a/attrs/attrs-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/a/attrs/attrs-VERSION-py3-none-any.whl diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini index 84e9171f829..c7604abf9dd 100644 --- a/build/pkgs/babel/checksums.ini +++ b/build/pkgs/babel/checksums.ini @@ -1,4 +1,4 @@ tarball=Babel-VERSION-py3-none-any.whl sha1=7f8671a725d0bbf28618841c441af8bd7709d527 sha256=efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287 -upstream_url=https://pypi.io/packages/py3/b/babel/Babel-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/b/babel/Babel-VERSION-py3-none-any.whl diff --git a/build/pkgs/beautifulsoup4/checksums.ini b/build/pkgs/beautifulsoup4/checksums.ini index 01abdd62940..9f2536bd011 100644 --- a/build/pkgs/beautifulsoup4/checksums.ini +++ b/build/pkgs/beautifulsoup4/checksums.ini @@ -1,4 +1,4 @@ tarball=beautifulsoup4-VERSION.tar.gz sha1=d9cd72f81e7710692b8ff0a42e69bf93375b5fd3 sha256=492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da -upstream_url=https://pypi.io/packages/source/b/beautifulsoup4/beautifulsoup4-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/b/beautifulsoup4/beautifulsoup4-VERSION.tar.gz diff --git a/build/pkgs/beniget/checksums.ini b/build/pkgs/beniget/checksums.ini index b7b3d25085b..a199e02756e 100644 --- a/build/pkgs/beniget/checksums.ini +++ b/build/pkgs/beniget/checksums.ini @@ -1,4 +1,4 @@ tarball=beniget-VERSION.tar.gz sha1=0167f16d17fbd61b91e620bca07e4ec7054ce51d sha256=75554b3b8ad0553ce2f607627dad3d95c60c441189875b98e097528f8e23ac0c -upstream_url=https://pypi.io/packages/source/b/beniget/beniget-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/b/beniget/beniget-VERSION.tar.gz diff --git a/build/pkgs/bleach/checksums.ini b/build/pkgs/bleach/checksums.ini index 575b1452888..3386c286912 100644 --- a/build/pkgs/bleach/checksums.ini +++ b/build/pkgs/bleach/checksums.ini @@ -1,4 +1,4 @@ tarball=bleach-VERSION-py3-none-any.whl sha1=7ba81a446171fb840d3083afadd0c87f0b599305 sha256=3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6 -upstream_url=https://pypi.io/packages/py3/b/bleach/bleach-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/b/bleach/bleach-VERSION-py3-none-any.whl diff --git a/build/pkgs/boost_cropped/patches/integral_wrapper.patch b/build/pkgs/boost_cropped/patches/integral_wrapper.patch new file mode 100644 index 00000000000..387b87c05c3 --- /dev/null +++ b/build/pkgs/boost_cropped/patches/integral_wrapper.patch @@ -0,0 +1,13 @@ +diff --git a/boost/mpl/aux_/integral_wrapper.hpp b/boost/mpl/aux_/integral_wrapper.hpp +index 6bc05f7..4f3f696 100644 +--- a/boost/mpl/aux_/integral_wrapper.hpp ++++ b/boost/mpl/aux_/integral_wrapper.hpp +@@ -56,7 +56,7 @@ struct AUX_WRAPPER_NAME + // have to #ifdef here: some compilers don't like the 'N + 1' form (MSVC), + // while some other don't like 'value + 1' (Borland), and some don't like + // either +-#if BOOST_WORKAROUND(__EDG_VERSION__, <= 243) ++#if BOOST_WORKAROUND(__EDG_VERSION__, <= 243) || __clang_major__ >= 16 + private: + BOOST_STATIC_CONSTANT(AUX_WRAPPER_VALUE_TYPE, next_value = BOOST_MPL_AUX_STATIC_CAST(AUX_WRAPPER_VALUE_TYPE, (N + 1))); + BOOST_STATIC_CONSTANT(AUX_WRAPPER_VALUE_TYPE, prior_value = BOOST_MPL_AUX_STATIC_CAST(AUX_WRAPPER_VALUE_TYPE, (N - 1))); diff --git a/build/pkgs/cachetools/checksums.ini b/build/pkgs/cachetools/checksums.ini index 29f982e9277..65e1aa547ea 100644 --- a/build/pkgs/cachetools/checksums.ini +++ b/build/pkgs/cachetools/checksums.ini @@ -1,4 +1,4 @@ tarball=cachetools-VERSION-py3-none-any.whl sha1=4210b349f838f75d64da22fbef7e5dc8e494c5f6 sha256=0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 -upstream_url=https://pypi.io/packages/py3/c/cachetools/cachetools-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/cachetools/cachetools-VERSION-py3-none-any.whl diff --git a/build/pkgs/calver/checksums.ini b/build/pkgs/calver/checksums.ini index 8c23449b03d..d3a4c14c1d6 100644 --- a/build/pkgs/calver/checksums.ini +++ b/build/pkgs/calver/checksums.ini @@ -1,4 +1,4 @@ tarball=calver-VERSION-py3-none-any.whl sha1=4553e3fbfc58908f3be2dd529e5991986f6a46b5 sha256=a1d7fcdd67797afc52ee36ffb8c8adf6643173864306547bfd1380cbce6310a0 -upstream_url=https://pypi.io/packages/py3/c/calver/calver-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/calver/calver-VERSION-py3-none-any.whl diff --git a/build/pkgs/certifi/checksums.ini b/build/pkgs/certifi/checksums.ini index ecb9291cd62..d11756ecfa1 100644 --- a/build/pkgs/certifi/checksums.ini +++ b/build/pkgs/certifi/checksums.ini @@ -1,4 +1,4 @@ tarball=certifi-VERSION-py3-none-any.whl sha1=4acb42f49aed94aeb687b33d3dcb6b564ff36fac sha256=dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1 -upstream_url=https://pypi.io/packages/py3/c/certifi/certifi-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/certifi/certifi-VERSION-py3-none-any.whl diff --git a/build/pkgs/cffi/checksums.ini b/build/pkgs/cffi/checksums.ini index 6b22c299649..8ae4939c66b 100644 --- a/build/pkgs/cffi/checksums.ini +++ b/build/pkgs/cffi/checksums.ini @@ -1,4 +1,4 @@ tarball=cffi-VERSION.tar.gz sha1=c42a46cd11f6153f299cf10e9c236e8b2a143c21 sha256=d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 -upstream_url=https://pypi.io/packages/source/c/cffi/cffi-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/cffi/cffi-VERSION.tar.gz diff --git a/build/pkgs/chardet/checksums.ini b/build/pkgs/chardet/checksums.ini index 8bce7750395..e5efde10ffe 100644 --- a/build/pkgs/chardet/checksums.ini +++ b/build/pkgs/chardet/checksums.ini @@ -1,4 +1,4 @@ tarball=chardet-VERSION-py3-none-any.whl sha1=2facc0387556aa8a2956ef682d49fc3eae56d30a sha256=e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970 -upstream_url=https://pypi.io/packages/py3/c/chardet/chardet-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/chardet/chardet-VERSION-py3-none-any.whl diff --git a/build/pkgs/charset_normalizer/checksums.ini b/build/pkgs/charset_normalizer/checksums.ini index 0530b566631..627fa73f7cc 100644 --- a/build/pkgs/charset_normalizer/checksums.ini +++ b/build/pkgs/charset_normalizer/checksums.ini @@ -1,4 +1,4 @@ tarball=charset_normalizer-VERSION-py3-none-any.whl sha1=1aa12424059bec1d95d9dda38b4ff6d062dededf sha256=3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc -upstream_url=https://pypi.io/packages/py3/c/charset_normalizer/charset_normalizer-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/charset_normalizer/charset_normalizer-VERSION-py3-none-any.whl diff --git a/build/pkgs/cmake/spkg-configure.m4 b/build/pkgs/cmake/spkg-configure.m4 index 6149f49a7c5..62e04ffe135 100644 --- a/build/pkgs/cmake/spkg-configure.m4 +++ b/build/pkgs/cmake/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([cmake], [dnl - AC_CACHE_CHECK([for cmake >= 3.18], [ac_cv_path_CMAKE], [dnl + AC_CACHE_CHECK([for cmake >= 3.22], [ac_cv_path_CMAKE], [dnl dnl Do not accept cmake installed via https://pypi.org/project/cmake/ dnl in the default user scheme; it will not work in our venv because dnl we set PYTHONUSERBASE in sage-env. @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([cmake], [dnl cmake_version=`$ac_path_CMAKE --version 2>&1 \ | $SED -n -e 's/cmake version *\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\)/\1/p'` AS_IF([test -n "$cmake_version"], [dnl - AX_COMPARE_VERSION([$cmake_version], [ge], [3.18], [dnl + AX_COMPARE_VERSION([$cmake_version], [ge], [3.22], [dnl ac_cv_path_CMAKE="$ac_path_CMAKE" ac_path_CMAKE_found=: ]) diff --git a/build/pkgs/colorama/checksums.ini b/build/pkgs/colorama/checksums.ini index 3332565f156..99a9ca97c72 100644 --- a/build/pkgs/colorama/checksums.ini +++ b/build/pkgs/colorama/checksums.ini @@ -1,4 +1,4 @@ tarball=colorama-VERSION-py2.py3-none-any.whl sha1=d6ab1608850fecfc0e1cf50bf93d743695c04027 sha256=4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 -upstream_url=https://pypi.io/packages/py2.py3/c/colorama/colorama-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/c/colorama/colorama-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/comm/checksums.ini b/build/pkgs/comm/checksums.ini index 34f21ec0087..66845adf128 100644 --- a/build/pkgs/comm/checksums.ini +++ b/build/pkgs/comm/checksums.ini @@ -1,4 +1,4 @@ tarball=comm-VERSION-py3-none-any.whl sha1=e7e20f9c1524a9fe059c0b6df90a68e1cd2115a9 sha256=6d52794cba11b36ed9860999cd10fd02d6b2eac177068fdd585e1e2f8a96e67a -upstream_url=https://pypi.io/packages/py3/c/comm/comm-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/comm/comm-VERSION-py3-none-any.whl diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 69415aafab8..4d5e7295f5b 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=e162e0f52da5eca0c01ddf4831edcea1acd72033 -sha256=2033521228294e134368d69457d6e582706e84ff10bf3d6d0a57a2a4afad0a8b +sha1=490f6d988c1c4c0786a0c8b36e708db56e17bfa6 +sha256=2e2b6821c9abd7d2b910edb1237f3622e985a2321cf1eedb2161b714494c6643 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 2a5e3433ccb..95d7fe86650 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -b178c10a7401b381b8ed2daab5aaf026436452c9 +93661cbe00c3e83691d6ed8c64586163cfdae62f diff --git a/build/pkgs/contourpy/checksums.ini b/build/pkgs/contourpy/checksums.ini index 2823ef85c08..236ca51affa 100644 --- a/build/pkgs/contourpy/checksums.ini +++ b/build/pkgs/contourpy/checksums.ini @@ -1,4 +1,4 @@ tarball=contourpy-VERSION.tar.gz sha1=eb8520cb7172aa8b957d8ba2d09e8f6d9a068d2a sha256=96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab -upstream_url=https://pypi.io/packages/source/c/contourpy/contourpy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/contourpy/contourpy-VERSION.tar.gz diff --git a/build/pkgs/conway_polynomials/checksums.ini b/build/pkgs/conway_polynomials/checksums.ini index ee7479822ee..a3c4ebf5935 100644 --- a/build/pkgs/conway_polynomials/checksums.ini +++ b/build/pkgs/conway_polynomials/checksums.ini @@ -1,4 +1,4 @@ tarball=conway_polynomials-VERSION-py3-none-any.whl sha1=8d61cb30eb96dfce883a5761d08d45fcb035608a sha256=a354b4ac0a9985da75e2ac6ec6d7de2902396eff48913eeed86a962486171c28 -upstream_url=https://pypi.io/packages/py3/c/conway_polynomials/conway_polynomials-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/conway_polynomials/conway_polynomials-VERSION-py3-none-any.whl diff --git a/build/pkgs/cppy/checksums.ini b/build/pkgs/cppy/checksums.ini index d042532c770..93861c8add7 100644 --- a/build/pkgs/cppy/checksums.ini +++ b/build/pkgs/cppy/checksums.ini @@ -1,4 +1,4 @@ tarball=cppy-VERSION-py3-none-any.whl sha1=57304a8ceaaf7cb34e4315aa9b8084b17fc0332c sha256=c5b5eac3d3f42593a07d35275b0bc27f447b76b9ad8f27c62e3cfa286dc1988a -upstream_url=https://pypi.io/packages/py3/c/cppy/cppy-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/c/cppy/cppy-VERSION-py3-none-any.whl diff --git a/build/pkgs/cvxopt/checksums.ini b/build/pkgs/cvxopt/checksums.ini index ca2f4711f5d..bdfc701c912 100644 --- a/build/pkgs/cvxopt/checksums.ini +++ b/build/pkgs/cvxopt/checksums.ini @@ -1,4 +1,4 @@ tarball=cvxopt-VERSION.tar.gz sha1=f9c3c3fb61e87d27f05b3b66bc10734d5e6284e6 sha256=3461fa42c1b2240ba4da1d985ca73503914157fc4c77417327ed6d7d85acdbe6 -upstream_url=https://pypi.io/packages/source/c/cvxopt/cvxopt-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/cvxopt/cvxopt-VERSION.tar.gz diff --git a/build/pkgs/cvxpy/checksums.ini b/build/pkgs/cvxpy/checksums.ini index 025278a9b01..49a7ebfbab0 100644 --- a/build/pkgs/cvxpy/checksums.ini +++ b/build/pkgs/cvxpy/checksums.ini @@ -1,4 +1,4 @@ tarball=cvxpy-VERSION.tar.gz sha1=1ca24d9e2ee5add13b33724ab9a11e747fe4ed99 sha256=7a9ef34e3c57ff8c844d86f0a3834fb5575af19233947639de0ba577c6122e3e -upstream_url=https://pypi.io/packages/source/c/cvxpy/cvxpy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/cvxpy/cvxpy-VERSION.tar.gz diff --git a/build/pkgs/cylp/checksums.ini b/build/pkgs/cylp/checksums.ini index d4f3ea9867b..6a053d1deed 100644 --- a/build/pkgs/cylp/checksums.ini +++ b/build/pkgs/cylp/checksums.ini @@ -1,4 +1,4 @@ tarball=cylp-VERSION.tar.gz sha1=22398052ca88123b77e691a0045806a030c9b259 sha256=a7ee226caa274e190338da3d24314647df7e06599ab38cdd26c005d8b8258b16 -upstream_url=https://pypi.io/packages/source/c/cylp/cylp-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/cylp/cylp-VERSION.tar.gz diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index 8bedd4cf52d..1ead9490d1a 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,4 +1,4 @@ tarball=cypari2-VERSION.tar.gz -sha1=4cb5fc43899852b7fc0c0175e610318c38f0caac -sha256=1a25865c34f20b1dc95830798e34ab6436e278b8e0c80dc7bf0ab34c5db03ab8 -upstream_url=https://pypi.io/packages/source/c/cypari2/cypari2-VERSION.tar.gz +sha1=4e9f14d218bc1cea29e03a2ceec9bf3dfbfd5eb3 +sha256=817606bf661b71d33e1d012421907a4f8fb09dd81b7d3e3ae179b3978020bbf1 +upstream_url=https://files.pythonhosted.org/packages/source/c/cypari2/cypari2-VERSION.tar.gz diff --git a/build/pkgs/cypari/dependencies b/build/pkgs/cypari/dependencies index 69dfe7e7d8a..cbf047c8027 100644 --- a/build/pkgs/cypari/dependencies +++ b/build/pkgs/cypari/dependencies @@ -1,4 +1,4 @@ - cython pari cysignals | $(PYTHON_TOOLCHAIN) $(PYTHON) +pari cysignals | $(PYTHON_TOOLCHAIN) cython $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index cd57a8b95d6..ccbccc3dc62 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -2.1.5 +2.2.0 diff --git a/build/pkgs/cypari/patches/trashcan.patch b/build/pkgs/cypari/patches/trashcan.patch deleted file mode 100644 index f4918d4cd39..00000000000 --- a/build/pkgs/cypari/patches/trashcan.patch +++ /dev/null @@ -1,47 +0,0 @@ -commit 78e6dcf937c960c51132132e14f86bddbbe5b7d9 -Author: Jeroen Demeyer -Date: Tue Feb 19 11:50:49 2019 +0100 - - Use the trashcan for Gen - -diff --git a/cypari2/gen.pxd b/cypari2/gen.pxd -index 2ac0669..664d57d 100644 ---- a/cypari2/gen.pxd -+++ b/cypari2/gen.pxd -@@ -1,3 +1,4 @@ -+cimport cython - from cpython.object cimport PyObject - from .types cimport GEN, pari_sp - -diff --git a/cypari2/gen.pyx b/cypari2/gen.pyx -index d268cf1..687f5b2 100644 ---- a/cypari2/gen.pyx -+++ b/cypari2/gen.pyx -@@ -135,6 +135,7 @@ cdef extern from *: - GEN new_nfeltup(GEN nf, GEN x, GEN zknf) - - -+@cython.trashcan(True) - cdef class Gen(Gen_base): - """ - Wrapper for a PARI ``GEN`` with memory management. -diff --git a/cypari2/pari_instance.pyx b/cypari2/pari_instance.pyx -index ba82438..d13bc45 100644 ---- a/cypari2/pari_instance.pyx -+++ b/cypari2/pari_instance.pyx -@@ -243,6 +243,15 @@ Reset default precision for the following tests: - - >>> pari.set_real_precision_bits(53) - -+Test the trashcan mechanism (without the trashcan, this would cause -+a stack overflow): -+ -+>>> pari.allocatemem(2**27, silent=True) -+>>> L = [pari(i) for i in range(2**20)] -+>>> x = pari.Pi() -+>>> del L -+>>> del x -+ - Test that interrupts work properly: - - >>> pari.allocatemem(8000000, 2**29) diff --git a/build/pkgs/cysignals/checksums.ini b/build/pkgs/cysignals/checksums.ini index 6bf5ec53eaa..8ba1030ffdf 100644 --- a/build/pkgs/cysignals/checksums.ini +++ b/build/pkgs/cysignals/checksums.ini @@ -1,4 +1,4 @@ tarball=cysignals-VERSION.tar.gz sha1=76db7aa59d55e867c83b329c017382555253af43 sha256=0f1e321e55a07f901c86a36a1e4497f6ff9dfe700681d0130a38c36e4eb238c3 -upstream_url=https://pypi.io/packages/source/c/cysignals/cysignals-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/c/cysignals/cysignals-VERSION.tar.gz diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index 9393fe60b7f..3eb199915d7 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,4 @@ -tarball=Cython-VERSION.tar.gz -sha1=83d6428e3bb7869f44f92ed75d7dff867c2a38ce -sha256=dcc96739331fb854dcf503f94607576cfe8488066c61ca50dfd55836f132de99 -upstream_url=https://pypi.io/packages/source/C/Cython/Cython-VERSION.tar.gz +tarball=cython-VERSION.tar.gz +sha1=f692b0c6f209b75b6bbd69bdbd57fac23785c23e +sha256=7146dd2af8682b4ca61331851e6aebce9fe5158e75300343f80c07ca80b1faff +upstream_url=https://files.pythonhosted.org/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 a909317fe5a..778bf95c00f 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -3.0.10 +3.0.11 diff --git a/build/pkgs/database_cubic_hecke/checksums.ini b/build/pkgs/database_cubic_hecke/checksums.ini index 8cc4e5407c4..830cae1c1eb 100644 --- a/build/pkgs/database_cubic_hecke/checksums.ini +++ b/build/pkgs/database_cubic_hecke/checksums.ini @@ -1,4 +1,4 @@ tarball=database_cubic_hecke-VERSION.tar.gz sha1=a99713c94e4108f917aa354f53edc26a60fd3808 sha256=553654a4ce987a277fe956a9a450d738bd1f58b96c45499075e28f2bca927ae9 -upstream_url=https://pypi.io/packages/source/d/database_cubic_hecke/database_cubic_hecke-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/d/database_cubic_hecke/database_cubic_hecke-VERSION.tar.gz diff --git a/build/pkgs/database_knotinfo/checksums.ini b/build/pkgs/database_knotinfo/checksums.ini index d6116d6d0d3..cbccff452f7 100644 --- a/build/pkgs/database_knotinfo/checksums.ini +++ b/build/pkgs/database_knotinfo/checksums.ini @@ -1,4 +1,4 @@ tarball=database_knotinfo-VERSION.tar.gz sha1=8bc72e561c009cc6a7c15eceedee522281fbf763 sha256=805868c8f3c30888e5866d833aec4f9ab07ad92b7b7caeb09bfec4634b28b0fd -upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz diff --git a/build/pkgs/dateutil/checksums.ini b/build/pkgs/dateutil/checksums.ini index e4cd473a834..a20d118ea27 100644 --- a/build/pkgs/dateutil/checksums.ini +++ b/build/pkgs/dateutil/checksums.ini @@ -1,4 +1,4 @@ tarball=python_dateutil-VERSION-py2.py3-none-any.whl sha1=323a8e8de7e00a254fadae9c77b1264d56525178 sha256=a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 -upstream_url=https://pypi.io/packages/py2.py3/p/python_dateutil/python_dateutil-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/p/python_dateutil/python_dateutil-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/debugpy/checksums.ini b/build/pkgs/debugpy/checksums.ini index 1879bd6e197..8d3c15e3ae0 100644 --- a/build/pkgs/debugpy/checksums.ini +++ b/build/pkgs/debugpy/checksums.ini @@ -1,4 +1,4 @@ tarball=debugpy-VERSION-py2.py3-none-any.whl sha1=cc85be805ef4f25e85bae65cf5b04badca032bb6 sha256=28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242 -upstream_url=https://pypi.io/packages/py2.py3/d/debugpy/debugpy-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/d/debugpy/debugpy-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/decorator/checksums.ini b/build/pkgs/decorator/checksums.ini index daaa14c6732..bec87842238 100644 --- a/build/pkgs/decorator/checksums.ini +++ b/build/pkgs/decorator/checksums.ini @@ -1,4 +1,4 @@ tarball=decorator-VERSION.tar.gz sha1=929f42916ac8a4aa973599d558768b8f1728db46 sha256=637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330 -upstream_url=https://pypi.io/packages/source/d/decorator/decorator-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/d/decorator/decorator-VERSION.tar.gz diff --git a/build/pkgs/defusedxml/checksums.ini b/build/pkgs/defusedxml/checksums.ini index a6da3a2e278..1faa620fcc7 100644 --- a/build/pkgs/defusedxml/checksums.ini +++ b/build/pkgs/defusedxml/checksums.ini @@ -1,4 +1,4 @@ tarball=defusedxml-VERSION.tar.gz sha1=37667af1dc1357eb96b005c4f408ad5292d77b9f sha256=1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 -upstream_url=https://pypi.io/packages/source/d/defusedxml/defusedxml-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/d/defusedxml/defusedxml-VERSION.tar.gz diff --git a/build/pkgs/distlib/checksums.ini b/build/pkgs/distlib/checksums.ini index bd456ec2028..f5fc1bc145d 100644 --- a/build/pkgs/distlib/checksums.ini +++ b/build/pkgs/distlib/checksums.ini @@ -1,4 +1,4 @@ tarball=distlib-VERSION-py2.py3-none-any.whl sha1=97ea3bb71040f0348eaea272ec17fefea5806e87 sha256=034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 -upstream_url=https://pypi.io/packages/py2.py3/d/distlib/distlib-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/d/distlib/distlib-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/docutils/checksums.ini b/build/pkgs/docutils/checksums.ini index 472701e55d7..20bc4e5c415 100644 --- a/build/pkgs/docutils/checksums.ini +++ b/build/pkgs/docutils/checksums.ini @@ -1,4 +1,4 @@ tarball=docutils-VERSION-py3-none-any.whl sha1=a2120453cdb1498128183696711261dd5f328068 sha256=dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 -upstream_url=https://pypi.io/packages/py3/d/docutils/docutils-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/d/docutils/docutils-VERSION-py3-none-any.whl diff --git a/build/pkgs/ecos_python/checksums.ini b/build/pkgs/ecos_python/checksums.ini index 1b5791948e7..60d0d8b9728 100644 --- a/build/pkgs/ecos_python/checksums.ini +++ b/build/pkgs/ecos_python/checksums.ini @@ -1,4 +1,4 @@ tarball=ecos-VERSION.tar.gz sha1=7afce63aec44522052e05fa2e1c82e12fe20fd45 sha256=f48816d73b87ae325556ea537b7c8743187311403c80e3832035224156337c4e -upstream_url=https://pypi.io/packages/source/e/ecos/ecos-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/e/ecos/ecos-VERSION.tar.gz diff --git a/build/pkgs/editables/checksums.ini b/build/pkgs/editables/checksums.ini index 6470e5fd5d2..3c0fda846a1 100644 --- a/build/pkgs/editables/checksums.ini +++ b/build/pkgs/editables/checksums.ini @@ -1,4 +1,4 @@ tarball=editables-VERSION-py3-none-any.whl sha1=7aa90de86b05d6dc1a04c219b01ca7eab09de113 sha256=61e5ffa82629e0d8bfe09bc44a07db3c1ab8ed1ce78a6980732870f19b5e7d4c -upstream_url=https://pypi.io/packages/py3/e/editables/editables-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/e/editables/editables-VERSION-py3-none-any.whl diff --git a/build/pkgs/entrypoints/checksums.ini b/build/pkgs/entrypoints/checksums.ini index 60bb1387477..529229debcc 100644 --- a/build/pkgs/entrypoints/checksums.ini +++ b/build/pkgs/entrypoints/checksums.ini @@ -1,4 +1,4 @@ tarball=entrypoints-VERSION.tar.gz sha1=ca5c5976781db7ec6e8faece06af31ff32960529 sha256=b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4 -upstream_url=https://pypi.io/packages/source/e/entrypoints/entrypoints-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/e/entrypoints/entrypoints-VERSION.tar.gz diff --git a/build/pkgs/exceptiongroup/checksums.ini b/build/pkgs/exceptiongroup/checksums.ini index d42fab6a958..70e89792f64 100644 --- a/build/pkgs/exceptiongroup/checksums.ini +++ b/build/pkgs/exceptiongroup/checksums.ini @@ -1,4 +1,4 @@ tarball=exceptiongroup-VERSION-py3-none-any.whl sha1=fd04443cb88d35ee59387ba20f62761ea5546a7d sha256=3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b -upstream_url=https://pypi.io/packages/py3/e/exceptiongroup/exceptiongroup-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/e/exceptiongroup/exceptiongroup-VERSION-py3-none-any.whl diff --git a/build/pkgs/execnet/checksums.ini b/build/pkgs/execnet/checksums.ini index ee938e17c8b..5e6df579af0 100644 --- a/build/pkgs/execnet/checksums.ini +++ b/build/pkgs/execnet/checksums.ini @@ -1,4 +1,4 @@ tarball=execnet-VERSION-py3-none-any.whl sha1=3a3b88b478a03a9c9933a7bdaea3224a118cc121 sha256=26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc -upstream_url=https://pypi.io/packages/py3/e/execnet/execnet-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/e/execnet/execnet-VERSION-py3-none-any.whl diff --git a/build/pkgs/executing/checksums.ini b/build/pkgs/executing/checksums.ini index c316d78954d..9f519f28788 100644 --- a/build/pkgs/executing/checksums.ini +++ b/build/pkgs/executing/checksums.ini @@ -1,4 +1,4 @@ tarball=executing-VERSION-py2.py3-none-any.whl sha1=c32699ff6868bf3613d56795016880fdadde4fc6 sha256=eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc -upstream_url=https://pypi.io/packages/py2.py3/e/executing/executing-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/e/executing/executing-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/fastjsonschema/checksums.ini b/build/pkgs/fastjsonschema/checksums.ini index ae58c4c8577..7d5c2bc7709 100644 --- a/build/pkgs/fastjsonschema/checksums.ini +++ b/build/pkgs/fastjsonschema/checksums.ini @@ -1,4 +1,4 @@ tarball=fastjsonschema-VERSION.tar.gz sha1=eab76262783dd81303e2b1da0914a1d5a7f388aa sha256=e820349dd16f806e4bd1467a138dced9def4bc7d6213a34295272a6cac95b5bd -upstream_url=https://pypi.io/packages/source/f/fastjsonschema/fastjsonschema-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/f/fastjsonschema/fastjsonschema-VERSION.tar.gz diff --git a/build/pkgs/filelock/checksums.ini b/build/pkgs/filelock/checksums.ini index e6fc5ecc6f5..57883a7087d 100644 --- a/build/pkgs/filelock/checksums.ini +++ b/build/pkgs/filelock/checksums.ini @@ -1,4 +1,4 @@ tarball=filelock-VERSION-py3-none-any.whl sha1=475b56870663924527abcf2f91b7d8dd7a7ab0ee sha256=43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f -upstream_url=https://pypi.io/packages/py3/f/filelock/filelock-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/f/filelock/filelock-VERSION-py3-none-any.whl diff --git a/build/pkgs/flit_core/checksums.ini b/build/pkgs/flit_core/checksums.ini index 0a40b9891b2..ca7a757151c 100644 --- a/build/pkgs/flit_core/checksums.ini +++ b/build/pkgs/flit_core/checksums.ini @@ -1,4 +1,4 @@ tarball=flit_core-VERSION-py3-none-any.whl sha1=cf044db53e986d0735ad708cce9eba0b71684168 sha256=7aada352fb0c7f5538c4fafeddf314d3a6a92ee8e2b1de70482329e42de70301 -upstream_url=https://pypi.io/packages/py3/f/flit_core/flit_core-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/f/flit_core/flit_core-VERSION-py3-none-any.whl diff --git a/build/pkgs/fonttools/checksums.ini b/build/pkgs/fonttools/checksums.ini index 7cb3b54ce86..f1da656f164 100644 --- a/build/pkgs/fonttools/checksums.ini +++ b/build/pkgs/fonttools/checksums.ini @@ -1,4 +1,4 @@ tarball=fonttools-VERSION.tar.gz sha1=5432f0273040b044e8d6465947e3a4c00097bdbf sha256=c391cd5af88aacaf41dd7cfb96eeedfad297b5899a39e12f4c2c3706d0a3329d -upstream_url=https://pypi.io/packages/source/f/fonttools/fonttools-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/f/fonttools/fonttools-VERSION.tar.gz diff --git a/build/pkgs/fpylll/checksums.ini b/build/pkgs/fpylll/checksums.ini index 5f258e7518e..188e768b787 100644 --- a/build/pkgs/fpylll/checksums.ini +++ b/build/pkgs/fpylll/checksums.ini @@ -1,4 +1,4 @@ tarball=fpylll-VERSION.tar.gz sha1=c0bcf8c5583ebf614da9b26710a2c835d498bf34 sha256=dfd9529a26c50993a2a716177debd7994312219070574cad31b35b4f0c040a19 -upstream_url=https://pypi.io/packages/source/f/fpylll/fpylll-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/f/fpylll/fpylll-VERSION.tar.gz diff --git a/build/pkgs/fqdn/checksums.ini b/build/pkgs/fqdn/checksums.ini index 2e3cdea93c6..e12be04eb2c 100644 --- a/build/pkgs/fqdn/checksums.ini +++ b/build/pkgs/fqdn/checksums.ini @@ -1,4 +1,4 @@ tarball=fqdn-VERSION-py3-none-any.whl sha1=85a7ac7d7f45d2e0b64c4b7653ab277ceec91ecf sha256=3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014 -upstream_url=https://pypi.io/packages/py3/f/fqdn/fqdn-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/f/fqdn/fqdn-VERSION-py3-none-any.whl diff --git a/build/pkgs/fricas/checksums.ini b/build/pkgs/fricas/checksums.ini index 1790c649eb3..183c35bb94d 100644 --- a/build/pkgs/fricas/checksums.ini +++ b/build/pkgs/fricas/checksums.ini @@ -1,4 +1,4 @@ tarball=fricas-VERSION-full.tar.bz2 -sha1=2f1e1bbbad7e04a7114ffbd93eeedadc5db32272 -sha256=fc2112ad45ba7b45ac423165f32bd5b244622107a1e4e1d56b9136f96746f2a0 +sha1=c5104c92808ff01a02ecdd3f412d44da3b2c21fb +sha256=32b87461ef079659e97247ad0a771ec8cf0d5a0f934788d67d37fb7acf63f9c4 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 0c00f610817..17e63e7affd 100644 --- a/build/pkgs/fricas/package-version.txt +++ b/build/pkgs/fricas/package-version.txt @@ -1 +1 @@ -1.3.10 +1.3.11 diff --git a/build/pkgs/furo/checksums.ini b/build/pkgs/furo/checksums.ini index eaeef993c08..492fede82ba 100644 --- a/build/pkgs/furo/checksums.ini +++ b/build/pkgs/furo/checksums.ini @@ -1,4 +1,4 @@ tarball=furo-VERSION-py3-none-any.whl sha1=de4aa7aff48696580d62abed717bf1c309af10f5 sha256=b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f -upstream_url=https://pypi.io/packages/py3/f/furo/furo-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/f/furo/furo-VERSION-py3-none-any.whl diff --git a/build/pkgs/gap/SPKG.rst b/build/pkgs/gap/SPKG.rst index 2f59f140a01..a5a1dbc1782 100644 --- a/build/pkgs/gap/SPKG.rst +++ b/build/pkgs/gap/SPKG.rst @@ -25,26 +25,3 @@ Upstream Contact https://www.gap-system.org Mailing list at https://mail.gap-system.org/mailman/listinfo/gap - -Special Update/Build Instructions ---------------------------------- - -This is a stripped-down version of GAP. The downloading of the sources -and removal of unneeded parts is done by the script spkg-src. When you -update GAP, please also update and use the spkg-src script. - -- Do we really want to copy everything from the build directory??? - - You need the full GAP tree to compile/install many GAP packages. - -- There's apparently a command missing (in ``spkg-install``) building - the - (HTML?) documentation. Earlier changelog entries as well as the - description - above state the documentation was removed from the upstream - sources... - Since the (pre-)built HTML documentation is currently included, I've - commented out some lines in that part of ``spkg-install``. -leif - -Patches -~~~~~~~ diff --git a/build/pkgs/gap/checksums.ini b/build/pkgs/gap/checksums.ini index 2b1170e52d9..3704072d609 100644 --- a/build/pkgs/gap/checksums.ini +++ b/build/pkgs/gap/checksums.ini @@ -1,4 +1,4 @@ tarball=gap-VERSION.tar.gz -sha1=a6e36f3f874a2c46f51561402634497eab705cca -sha256=672308745eb78a222494ee8dd6786edd5bc331456fcc6456ac064bdb28d587a8 +sha1=cf91834954849dbaeae17079a4c4565bc28d03a8 +sha256=9794dbdba6fb998e0a2d0aa8ce21fc8848ad3d3f9cc9993b0b8e20be7e1dbeba upstream_url=https://github.com/gap-system/gap/releases/download/vVERSION/gap-VERSION.tar.gz diff --git a/build/pkgs/gap/distros/conda.txt b/build/pkgs/gap/distros/conda.txt index 7f5f5034610..401c67cba31 100644 --- a/build/pkgs/gap/distros/conda.txt +++ b/build/pkgs/gap/distros/conda.txt @@ -1 +1 @@ -gap-defaults>=4.12.2,<4.13.0 +gap-defaults diff --git a/build/pkgs/gap/distros/gentoo.txt b/build/pkgs/gap/distros/gentoo.txt new file mode 100644 index 00000000000..ea64ec957e5 --- /dev/null +++ b/build/pkgs/gap/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/gap diff --git a/build/pkgs/gap/package-version.txt b/build/pkgs/gap/package-version.txt index f1cd7de1de5..56d2fb548a2 100644 --- a/build/pkgs/gap/package-version.txt +++ b/build/pkgs/gap/package-version.txt @@ -1 +1 @@ -4.12.2 +4.13.1 diff --git a/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch b/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch new file mode 100644 index 00000000000..46f5e36f109 --- /dev/null +++ b/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch @@ -0,0 +1,29 @@ +From 89b95994807970d90671e1e02cc03ddca4cf0a10 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Sat, 8 Jun 2024 14:01:16 -0700 +Subject: [PATCH] Makefile.rules [Darwin]: Remove use of '-single_module' + (obsolete), activate '-install_name' + +--- + Makefile.rules | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/Makefile.rules b/Makefile.rules +index 8bfd3dba0..adfed731f 100644 +--- a/Makefile.rules ++++ b/Makefile.rules +@@ -446,10 +446,7 @@ else ifneq (,$(findstring darwin,$(host_os))) + LINK_SHLIB_FLAGS = -dynamiclib + LINK_SHLIB_FLAGS += -compatibility_version $(LIBGAP_COMPAT_VER) + LINK_SHLIB_FLAGS += -current_version $(LIBGAP_CURRENT_VER) +- LINK_SHLIB_FLAGS += -Wl,-single_module +- +- # TODO: set install_name, at least for installed version of the lib? +- #LINK_SHLIB_FLAGS += -install_name $(libdir)/$(LIBGAP_FULL) ++ LINK_SHLIB_FLAGS += -install_name $(libdir)/$(LIBGAP_FULL) + + GAP_CPPFLAGS += -DPIC + GAP_CFLAGS += -fno-common +-- +2.42.0 + diff --git a/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch b/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch new file mode 100644 index 00000000000..c99b39ab91c --- /dev/null +++ b/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch @@ -0,0 +1,178 @@ +diff --git a/lib/dicthf.gi b/lib/dicthf.gi +index 5ee7341..e4349ac 100644 +--- a/lib/dicthf.gi ++++ b/lib/dicthf.gi +@@ -152,16 +152,37 @@ end); + ## + InstallMethod(SparseIntKey,"for bounded tuples",true, + [ IsList,IsList and IsCyclotomicCollection ], 0, +-function(m,v) +-local c; +- if Length(m)<>3 or m[1]<>"BoundedTuples" then ++function(m, v) ++ if Length(m)<> 3 or m[1]<>"BoundedTuples" then + TryNextMethod(); + fi; +- c:=[1,Maximum(m[2])+1]; +- return function(a) +- return a*c; ++ # Due to the way BoundedTuples are presently implemented we expect the input ++ # to the hash function to always be a list of positive immediate integers. This means ++ # that using HashKeyWholeBag should be safe. ++ return function(x) ++ Assert(1, IsPositionsList(x)); ++ if not IsPlistRep(x) then ++ x := AsPlist(x); ++ fi; ++ return HashKeyWholeBag(x, 1); + end; + ++ # alternative code w/o HashKeyBag ++ ## build a weight vector to distinguish lists. Make entries large while staying clearly within ++ ## immediate int (2^55 replacing 2^60, since we take subsequent primes). ++ #step:=NextPrimeInt(QuoInt(2^55,Maximum(m[2])*m[3])); ++ #weights:=[1]; ++ #len:=Length(v); ++ ## up to 56 full, then increasingly reduce ++ #len:=Minimum(len,8*RootInt(len)); ++ #while Length(weights)0 and ForAll(pnt,IsPosInt) and ++ ForAll(acts,IsPerm) and + (act=OnSets or act=OnPoints or act=OnRight or act=\^)) then + TryNextMethod(); + fi; +diff --git a/lib/vecmat.gi b/lib/vecmat.gi +index 017c3c6..93ba828 100644 +--- a/lib/vecmat.gi ++++ b/lib/vecmat.gi +@@ -2142,7 +2142,8 @@ InstallMethod(DomainForAction,"matrix/matrix",IsElmsCollsX, + function(pnt,acts,act) + local l,f; + if (not ForAll(acts,IsMatrix)) or +- (act<>OnPoints and act<>OnSubspacesByCanonicalBasis and act<>OnRight) then ++ (act<>OnPoints and act<>OnSubspacesByCanonicalBasis and act<>OnRight and act<>OnSets and ++ act<>OnTuples) then + TryNextMethod(); # strange operation, might extend the domain + fi; + l:=NaturalActedSpace(acts,pnt); +diff --git a/tst/testbugfix/2024-09-14-actdomain.tst b/tst/testbugfix/2024-09-14-actdomain.tst +new file mode 100644 +index 0000000..84f8b50 +--- /dev/null ++++ b/tst/testbugfix/2024-09-14-actdomain.tst +@@ -0,0 +1,85 @@ ++# Fix #5786 and error reported by Len Soicher in support list ++gap> gg:=SpecialUnitaryGroup(4,2);; ++gap> hl:=Z(2)*[ ++> [0,0,1,0], ++> [1,1,0,0], ++> [0,1,0,1], ++> [0,1,1,0], ++> [1,1,0,1]];; ++gap> o:=Orbit(gg,Set(hl),OnSets);; ++gap> Length(o); ++216 ++gap> set:=[ 1,10,15,24,29,33,38,40,44,59, 60, 63, 69, 74, 77,79,85, 86, 90, ++> 95, 99, 103, 105, 110, 122, 125, 143, 148, 149, 153, 162, 165, 174, 182, ++> 185, 191, 197, 198, 202, 218, 223, 227, 228, 235, 236, 240, 243, 248, ++> 254, ++> 256, 259, 270, 275, 288, 291, 295, 298, 302, 305, 310, 312, 315, 325, ++> 329, ++> 333, 340, 341, 350, 356, 366, 369, 381, 385, 390, 397, 402, 412, 414, ++> 419, ++> 421, 425, 428, 433, 436, 445, 447, 451, 452, 453, 454, 461, 466, 474, ++> 479, ++> 481, 489, 490, 493, 497, 507, 509, 512, 513, 519, 521 ];; ++gap> gp:=Group( ++> ( 1,340,124,306,216,492,100, 25,108,270,220,332)( 2,138, 54,161,132,159,198, ++> 336,439,269, 89,419)( 3,467,177,404,505,437,379,312,481,271,223,135) ++> ( 4,510, 79,504,259,234,378,251,272,268,360,303)( 5,278,176,191,231,275,263, ++> 190,230,146,265,192)( 6,486,126,523,490,448,375,237,288,400,243,329) ++> ( 7,131,123,516, 48,392,350,333,418, 16,139,175)( 8,289,125,386,241, 29,376, ++> 334,242,417,442,331)( 9,430, 32, 59,446,367,377,335,411,416,515,330) ++> ( 10,391, 56,407,475,414,200,328,165,473, 86,119)( 11,368, 35,390,522,408,199, ++> 415,440,326, 87,503)( 12,412, 55,457,399,245,201, 33,438,431, 88,317) ++> ( 13,471, 40,348,452,292, 43,346,373, 77, 41,347)( 14,137,174,162, 60, 69,321, ++> 487, 61,158,322,370)( 15,101,114,109,130,160,488,489,352,351,420, 17) ++> ( 18,339,167,290,202,385, 99, 22, 90,323,217,129)( 19, 26, 93,304, 96,342) ++> ( 20,338,166,305,215,141, 97, 24, 51,150,219,507)( 21,337, 63,186,214,424, 98, ++> 23,107,382,218,349)( 27, 91,445,451,525, 67,519,239,144,203,155,353) ++> ( 28,324,444,128, 70,428,496,238,286,300,283, 64)( 30,236,287,441,387,354) ++> ( 31,345,366,517, 45,344,413,521, 46,248,244,121)( 34,314,394,402,222,447, 81, ++> 282,262,173,246,435)( 36,482,178,364,148,495,179,363,140,102,113,111) ++> ( 37,253,273,168,294,302,226,183, 72,480,154,233)( 38,483,520,393,403,465,362, ++> 298,143,356,153,369)( 39,157,320,472)( 42,228,277,264)( 44,343,147,501) ++> ( 47, 73,308,380,184,389,310,327,163,295,151,425)( 49,221,456, 80,474,260,405, ++> 325,164,524,152,449)( 50,479,365,477,461,459,497,169,296,247,134,117) ++> ( 52,361,299,285,355,188,423,464,434,453,133,118)( 53,257,509, 68,511,458,293, ++> 204,384,374, 75, 82)( 57,116,112,149,514,396,470,485,493,249,421,120) ++> ( 58,500,266,250,429,122)( 62,156,319,311)( 65,187,225,357,127, 71,388,235, ++> 460,252,274,371)( 66,106,462,291,205,383,372, 76, 92,410,280,498) ++> ( 74,401,381,476,409,281,171,104,297,307,426,182)( 78, 84,261,256,180,436,512, ++> 313,181,491,224,499)( 83,466,255,508,506,395,469,422,142,103,115,110) ++> ( 85,468,258,502,267,136)( 94,341)( 95,211)(105,478,195,432,518,316,197,484, ++> 494,455,196,170)(145,513,359,232,227,254)(172,209,398,207,279,206) ++> (185,194,309,443)(189,406,463,318,450,427,433,454,315,301,284,358) ++> (193,229,276,240)(208,397)(210,213,212) ++> ,( 1,379,148, 48,128,430,416)( 2, 34,338, 35,235,131,521)( 3,512,352, 47, ++> 318,289,237)( 4,272,506, 49,434,486,282)( 5,524,485, 10,483,340, 55) ++> ( 6,458, 36,487, 60,121, 16)( 7,313,140,336,127,435,270)( 8, 85,147,489, ++> 98,201,417)( 9,469, 94,488,129,329,400)( 11,291, 26, 54,234,473,169) ++> ( 12,207,339, 56,233,503,515)( 13,426,337, 40,232,295,500)( 14, 32,414, 27, ++> 167,130,472)( 15, 33,188, 38,382,109,501)( 17, 31,459, 37,496,132,517) ++> ( 18,263,294,446,451,134,497)( 19,198,525,241,441,244,470)( 20,100,199,490, ++> 242,429,413)( 21,378,403,216,523,421, 58)( 22,124,159, 77, 63,123,292) ++> ( 23,176,275,431,168, 86,293)( 24,177,492,326,104,151,290)( 25, 79,437,269, ++> 163,152,144)( 28,462,150,162, 62,120,415)( 29,239, 83,311, 61,117,260) ++> ( 30,452,149,370, 39,122,389)( 41,468, 73,254,277,432,371)( 42,210,319,502, ++> 373,205,283)( 43,387,194,212,320,508, 99)( 44,396,349,331,399,250,420) ++> ( 45,411,461,375,475,377,418)( 46,344,409,519,522,477,419)( 50,253,278,433, ++> 231, 88,422)( 51,482,138,358,229,463,381)( 52, 91,467,221,230,518,484) ++> ( 53,390,510,494,228,454, 92)( 57,310,460,118,259,367,363)( 59,281,227,274, ++> 505,402,215)( 64,125,245, 76, 93,160,471)( 65,126,408, 75,166, 69,264) ++> ( 66,302,186,116,257,424,327)( 67,187,297,479,217,425,171)( 68,303,296,345, ++> 280,226,273)( 70,240,359,366,364, 97,200)( 71,265,360,394,393,133,423) ++> ( 72,251,146,164,268, 87,312)( 74,252,276,406,513, 89,181)( 78,395,158,185, ++> 315,287,333)( 80,136,351,156,316,286,334)( 81,511,350,184,317,288,335) ++> ( 82,261,392,183,105,309,238)( 84,224,439,182,284,193,236)( 90,466,391,357, ++> 443,170,465)( 95,321,509,353,203,243,197)( 96,516,498,354,300,246,401) ++> (101,405,453,464,154,112,341)(102,139,450,208,388,365,111)(103,440,520,285, ++> 192,266,343)(106,308,218,119,262,448,298)(107,219,368,356,442,301,209) ++> (108,514,412,355,376,474,222)(110,495,137,478,361,444,380)(113,255,342,175, ++> 173,247,305)(114,256,141,328,153,307,304)(115,258,385,325,172,214,306) ++> (135,493,157,455,362,445,407)(142,346,189,398,323,161,499)(143,190,145,436, ++> 271,165,314)(155,195,211,174,404,374,204)(178,507,332,330,248,249,179) ++> (180,372,202,386,196,213,322)(191,267,491,384,438,279,225)(206,428,299,369, ++> 480,220,449)(223,481,383,427,397,324,504)(347,457,447,410,348,456,476));; ++gap> Length(Orbit(gp,set,OnSets)); ++241920 +diff --git a/tst/teststandard/hash2.tst b/tst/teststandard/hash2.tst +index 6bfa4d3..d47452e 100644 +--- a/tst/teststandard/hash2.tst ++++ b/tst/teststandard/hash2.tst +@@ -32,4 +32,8 @@ gap> Length(Orbit(h,h.1[1],OnRight)); + 2079 + gap> Length(Orbit(h,h.2^5,OnPoints)); + 693 ++gap> Length(Orbit(SymmetricGroup(14), [1 .. 7], OnSets)); ++3432 ++gap> Length(Orbit(SymmetricGroup(16), [1 .. 8], OnSets)); ++12870 + gap> STOP_TEST( "hash2.tst", 1); diff --git a/build/pkgs/gap/spkg-check.in b/build/pkgs/gap/spkg-check.in index d2fccda6e04..6dc12ca9266 100644 --- a/build/pkgs/gap/spkg-check.in +++ b/build/pkgs/gap/spkg-check.in @@ -10,7 +10,8 @@ cd pkg/io make cd ../.. -make testinstall +# This is the same as 'dev/ci.sh testinstall' (but dev/ci.sh is not part of the GAP tarball) +./gap tst/testinstall.g if [[ $? -ne 0 ]]; then exit 1 fi diff --git a/build/pkgs/gap/spkg-configure.m4 b/build/pkgs/gap/spkg-configure.m4 index d4c3680b430..d52d2c20b8c 100644 --- a/build/pkgs/gap/spkg-configure.m4 +++ b/build/pkgs/gap/spkg-configure.m4 @@ -2,8 +2,8 @@ SAGE_SPKG_CONFIGURE([gap], [ # Default to installing the SPKG, if the check is run at all. sage_spkg_install_gap=yes - m4_pushdef([GAP_MINVER],["4.12.2"]) - m4_pushdef([GAP_LTVER],["4.13.0"]) + m4_pushdef([GAP_MINVER],["4.13.0"]) + m4_pushdef([GAP_LTVER],["5.0.0"]) SAGE_SPKG_DEPCHECK([ncurses readline zlib], [ AC_PATH_PROG(GAP, gap) diff --git a/build/pkgs/gast/checksums.ini b/build/pkgs/gast/checksums.ini index 6035bc7c22d..83cdc0ef554 100644 --- a/build/pkgs/gast/checksums.ini +++ b/build/pkgs/gast/checksums.ini @@ -1,4 +1,4 @@ tarball=gast-VERSION.tar.gz sha1=6c113cf8d33cc654d33210335103485ab41d3dbb sha256=9c270fe5f4b130969b54174de7db4e764b09b4f7f67ccfc32480e29f78348d97 -upstream_url=https://pypi.io/packages/source/g/gast/gast-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/g/gast/gast-VERSION.tar.gz diff --git a/build/pkgs/gc/checksums.ini b/build/pkgs/gc/checksums.ini index ede35b376a2..109f63bce95 100644 --- a/build/pkgs/gc/checksums.ini +++ b/build/pkgs/gc/checksums.ini @@ -1,4 +1,4 @@ tarball=gc-VERSION.tar.gz -sha1=3f543532c47e592a8f5ea6f7a529c8ed7465a5c7 -sha256=b9183fe49d4c44c7327992f626f8eaa1d8b14de140f243edb1c9dcff7719a7fc +sha1=1c8d7bde86aa98df957563982f38c583c2c6707f +sha256=7649020621cb26325e1fb5c8742590d92fb48ce5c259b502faf7d9fb5dabb160 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 d00d9ba64a3..fc10bd4825e 100644 --- a/build/pkgs/gc/package-version.txt +++ b/build/pkgs/gc/package-version.txt @@ -1 +1 @@ -8.2.6 +8.2.8 diff --git a/build/pkgs/gcc/SPKG.rst b/build/pkgs/gcc/SPKG.rst index 1f90eb0bf67..7e01df5bea8 100644 --- a/build/pkgs/gcc/SPKG.rst +++ b/build/pkgs/gcc/SPKG.rst @@ -61,9 +61,9 @@ you need a recent version of Xcode. (Installing the ``gfortran`` SPKG becomes a no-op in this case.) -Building Sage from source on Apple Silicon (M1/M2) requires the use of -Apple's Command Line Tools, and those tools include a suitable -compiler. Sage's ``gcc`` SPKG is not suitable for M1/M2; building it +Building Sage from source on Apple Silicon (M1, M2, M3, M4; arm64) requires +the use of Apple's Command Line Tools, and those tools include a suitable +compiler. Sage's ``gcc`` SPKG is not suitable for Apple Silicon; building it will likely fail. License diff --git a/build/pkgs/gcc/patches/gcc-13.3.0-arm.patch b/build/pkgs/gcc/patches/gcc-13_3_0.patch similarity index 98% rename from build/pkgs/gcc/patches/gcc-13.3.0-arm.patch rename to build/pkgs/gcc/patches/gcc-13_3_0.patch index 56919f9e959..f121fdb6461 100644 --- a/build/pkgs/gcc/patches/gcc-13.3.0-arm.patch +++ b/build/pkgs/gcc/patches/gcc-13_3_0.patch @@ -1,5 +1,5 @@ diff --git a/Makefile.def b/Makefile.def -index 35e994eb77e..9b4a8a2bf7a 100644 +index 35e994e..9b4a8a2 100644 --- a/Makefile.def +++ b/Makefile.def @@ -47,7 +47,8 @@ host_modules= { module= fixincludes; bootstrap=true; @@ -13,7 +13,7 @@ index 35e994eb77e..9b4a8a2bf7a 100644 // Work around in-tree gmp configure bug with missing flex. extra_configure_flags='--disable-shared LEX="touch lex.yy.c" @host_libs_picflag@'; diff --git a/Makefile.in b/Makefile.in -index 205d3c30bde..fdfd3d75593 100644 +index 205d3c3..fdfd3d7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -12016,7 +12016,7 @@ configure-gcc: @@ -116,7 +116,7 @@ index 205d3c30bde..fdfd3d75593 100644 diff --git a/c++tools/Makefile.in b/c++tools/Makefile.in -index 77bda3d56dc..dcb1029e064 100644 +index 77bda3d..dcb1029 100644 --- a/c++tools/Makefile.in +++ b/c++tools/Makefile.in @@ -29,8 +29,9 @@ AUTOCONF := @AUTOCONF@ @@ -149,7 +149,7 @@ index 77bda3d56dc..dcb1029e064 100644 # copy to gcc dir so tests there can run all::../gcc/g++-mapper-server$(exeext) diff --git a/c++tools/configure b/c++tools/configure -index 742816e4253..88087009383 100755 +index 742816e..8808700 100755 --- a/c++tools/configure +++ b/c++tools/configure @@ -627,7 +627,8 @@ get_gcc_base_ver @@ -202,7 +202,7 @@ index 742816e4253..88087009383 100755 # Check if O_CLOEXEC is defined by fcntl diff --git a/c++tools/configure.ac b/c++tools/configure.ac -index 23e98c8e721..44dfaccbbfa 100644 +index 23e98c8..44dfacc 100644 --- a/c++tools/configure.ac +++ b/c++tools/configure.ac @@ -102,8 +102,15 @@ fi @@ -224,7 +224,7 @@ index 23e98c8e721..44dfaccbbfa 100644 # Check if O_CLOEXEC is defined by fcntl AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [ diff --git a/configure b/configure -index 117a7ef23f2..c721ee4b5b2 100755 +index 117a7ef..c721ee4 100755 --- a/configure +++ b/configure @@ -687,7 +687,10 @@ extra_host_zlib_configure_flags @@ -386,7 +386,7 @@ index 117a7ef23f2..c721ee4b5b2 100755 fi diff --git a/configure.ac b/configure.ac -index b3e9bbd2aa5..a75c9e8850c 100644 +index b3e9bbd..a75c9e8 100644 --- a/configure.ac +++ b/configure.ac @@ -710,6 +710,8 @@ esac @@ -513,7 +513,7 @@ index b3e9bbd2aa5..a75c9e8850c 100644 fi AC_SUBST(host_libs_picflag) diff --git a/fixincludes/Makefile.in b/fixincludes/Makefile.in -index 1937dcaa32d..e6ce41dba39 100644 +index 1937dca..e6ce41d 100644 --- a/fixincludes/Makefile.in +++ b/fixincludes/Makefile.in @@ -73,7 +73,7 @@ default : all @@ -557,7 +557,7 @@ index 1937dcaa32d..e6ce41dba39 100644 $(ALLOBJ) : $(HDR) fixincl.o : fixincl.c $(srcdir)/fixincl.x diff --git a/fixincludes/configure b/fixincludes/configure -index bdcc41f6ddc..b2759ee3b98 100755 +index bdcc41f..b2759ee 100755 --- a/fixincludes/configure +++ b/fixincludes/configure @@ -623,6 +623,8 @@ ac_subst_vars='LTLIBOBJS @@ -610,7 +610,7 @@ index bdcc41f6ddc..b2759ee3b98 100755 vax-dec-bsd* ) diff --git a/fixincludes/configure.ac b/fixincludes/configure.ac -index ef2227e3c93..4e78511d20f 100644 +index ef2227e..4e78511 100644 --- a/fixincludes/configure.ac +++ b/fixincludes/configure.ac @@ -68,6 +68,14 @@ if test $TARGET = twoprocess; then @@ -629,7 +629,7 @@ index ef2227e3c93..4e78511d20f 100644 vax-dec-bsd* ) AC_DEFINE(exit, xexit, [Define to xexit if the host system does not support atexit]) diff --git a/gcc/Makefile.in b/gcc/Makefile.in -index 775aaa1b3c4..740199cb36f 100644 +index 775aaa1..740199c 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -158,6 +158,9 @@ LDFLAGS = @LDFLAGS@ @@ -744,7 +744,7 @@ index 775aaa1b3c4..740199cb36f 100644 @cat ./site.tmp > site.exp @cat site.bak | sed \ diff --git a/gcc/aclocal.m4 b/gcc/aclocal.m4 -index 6be36df5190..126e09bbcd1 100644 +index 6be36df..126e09b 100644 --- a/gcc/aclocal.m4 +++ b/gcc/aclocal.m4 @@ -12,6 +12,56 @@ @@ -805,7 +805,7 @@ index 6be36df5190..126e09bbcd1 100644 m4_include([../ltoptions.m4]) m4_include([../ltsugar.m4]) diff --git a/gcc/ada/gcc-interface/Make-lang.in b/gcc/ada/gcc-interface/Make-lang.in -index 9507f2f0920..2b9b0de8273 100644 +index 9507f2f..2b9b0de 100644 --- a/gcc/ada/gcc-interface/Make-lang.in +++ b/gcc/ada/gcc-interface/Make-lang.in @@ -72,7 +72,8 @@ else @@ -837,7 +837,7 @@ index 9507f2f0920..2b9b0de8273 100644 include $(srcdir)/ada/Make-generated.in diff --git a/gcc/ada/gcc-interface/Makefile.in b/gcc/ada/gcc-interface/Makefile.in -index da6a56fcec8..51a4bf17038 100644 +index da6a56f..51a4bf1 100644 --- a/gcc/ada/gcc-interface/Makefile.in +++ b/gcc/ada/gcc-interface/Makefile.in @@ -91,6 +91,7 @@ LS = ls @@ -888,7 +888,7 @@ index da6a56fcec8..51a4bf17038 100644 cd $(RTSDIR); $(LN_S) libgnat$(hyphen)$(LIBRARY_VERSION)$(soext) \ libgnat$(soext) diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc -index 4389ff917b8..0fe5d2a2e67 100644 +index 4389ff9..0fe5d2a 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -1081,7 +1081,7 @@ register_known_functions (known_function_manager &kfm) @@ -901,7 +901,7 @@ index 4389ff917b8..0fe5d2a2e67 100644 #define errno (*__error()) and similarly __errno for newlib. diff --git a/gcc/builtins.cc b/gcc/builtins.cc -index 1bfdc598eec..1122527cbd7 100644 +index 1bfdc59..1122527 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -5501,6 +5501,13 @@ expand_builtin_trap (void) @@ -930,7 +930,7 @@ index 1bfdc598eec..1122527cbd7 100644 case BUILT_IN_EXECL: case BUILT_IN_EXECV: diff --git a/gcc/builtins.def b/gcc/builtins.def -index 4ad95a12f83..448cf837ec8 100644 +index 4ad95a1..448cf83 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -1067,6 +1067,8 @@ DEF_BUILTIN_STUB (BUILT_IN_ADJUST_TRAMPOLINE, "__builtin_adjust_trampoline") @@ -942,8 +942,218 @@ index 4ad95a12f83..448cf837ec8 100644 /* Implementing __builtin_setjmp. */ DEF_BUILTIN_STUB (BUILT_IN_SETJMP_SETUP, "__builtin_setjmp_setup") +diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc +index b4e0c8c..11e7aaa 100644 +--- a/gcc/c/c-lang.cc ++++ b/gcc/c/c-lang.cc +@@ -61,6 +61,15 @@ c_get_sarif_source_language (const char *) + return "c"; + } + ++/* Implement c-family hook to register language-specific features for ++ __has_{feature,extension}. */ ++ ++void ++c_family_register_lang_features () ++{ ++ c_register_features (); ++} ++ + #if CHECKING_P + + namespace selftest { +diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc +index e4aed61..fad4662 100644 +--- a/gcc/c/c-objc-common.cc ++++ b/gcc/c/c-objc-common.cc +@@ -34,6 +34,38 @@ along with GCC; see the file COPYING3. If not see + static bool c_tree_printer (pretty_printer *, text_info *, const char *, + int, bool, bool, bool, bool *, const char **); + ++/* Info for C language features which can be queried through ++ __has_{feature,extension}. */ ++ ++struct c_feature_info ++{ ++ const char *ident; ++ const int *enable_flag; ++}; ++ ++static const c_feature_info c_feature_table[] = ++{ ++ { "c_alignas", &flag_isoc11 }, ++ { "c_alignof", &flag_isoc11 }, ++ { "c_atomic", &flag_isoc11 }, ++ { "c_generic_selections", &flag_isoc11 }, ++ { "c_static_assert", &flag_isoc11 }, ++ { "c_thread_local", &flag_isoc11 } ++}; ++ ++/* Register features specific to the C language. */ ++ ++void ++c_register_features () ++{ ++ for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++) ++ { ++ const c_feature_info *info = c_feature_table + i; ++ const bool feat_p = !info->enable_flag || *info->enable_flag; ++ c_common_register_feature (info->ident, feat_p); ++ } ++} ++ + bool + c_missing_noreturn_ok_p (tree decl) + { +diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h +index d31dacb..34dc23a 100644 +--- a/gcc/c/c-objc-common.h ++++ b/gcc/c/c-objc-common.h +@@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see + #ifndef GCC_C_OBJC_COMMON + #define GCC_C_OBJC_COMMON + ++/* Implemented in c-objc-common.cc. */ ++extern void c_register_features (); ++ + /* Lang hooks that are shared between C and ObjC are defined here. Hooks + specific to C or ObjC go in c-lang.cc and objc/objc-lang.cc, respectively. */ + +diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc +index 3627a3f..5abc6e8 100644 +--- a/gcc/c/c-parser.cc ++++ b/gcc/c/c-parser.cc +@@ -217,6 +217,9 @@ struct GTY(()) c_parser { + should translate them to the execution character set (false + inside attributes). */ + BOOL_BITFIELD translate_strings_p : 1; ++ /* True if we want to lex arbitrary number-like sequences as their ++ string representation. */ ++ BOOL_BITFIELD lex_number_as_string : 1; + + /* Objective-C specific parser/lexer information. */ + +@@ -291,10 +294,10 @@ c_lex_one_token (c_parser *parser, c_token *token, bool raw = false) + + if (raw || vec_safe_length (parser->raw_tokens) == 0) + { ++ int lex_flags = parser->lex_joined_string ? 0 : C_LEX_STRING_NO_JOIN; ++ lex_flags |= parser->lex_number_as_string ? C_LEX_NUMBER_AS_STRING : 0; + token->type = c_lex_with_flags (&token->value, &token->location, +- &token->flags, +- (parser->lex_joined_string +- ? 0 : C_LEX_STRING_NO_JOIN)); ++ &token->flags, lex_flags); + token->id_kind = C_ID_NONE; + token->keyword = RID_MAX; + token->pragma_kind = PRAGMA_NONE; +@@ -4993,6 +4996,88 @@ c_parser_gnu_attribute_any_word (c_parser *parser) + return attr_name; + } + ++/* Handle parsing clang-form attribute arguments, where we need to adjust ++ the parsing rules to relate to a specific attribute. */ ++ ++static tree ++c_parser_clang_attribute_arguments (c_parser *parser, tree /*attr_id*/) ++{ ++ /* We can, if required, alter the parsing on the basis of the attribute. ++ At present, we handle the availability attr, where ach entry can be : ++ identifier ++ identifier=N.MM.Z ++ identifier="string" ++ followed by ',' or ) for the last entry*/ ++ ++ tree attr_args = NULL_TREE; ++ do ++ { ++ tree name = NULL_TREE; ++ tree value = NULL_TREE; ++ ++ if (c_parser_next_token_is (parser, CPP_NAME) ++ && c_parser_peek_token (parser)->id_kind == C_ID_ID) ++ { ++ name = c_parser_peek_token (parser)->value; ++ c_parser_consume_token (parser); ++ } ++ else if (c_parser_next_token_is (parser, CPP_COMMA)) ++ name = error_mark_node; /* Comma handled below. */ ++ else ++ { ++ bool saved_join_state = parser->lex_joined_string; ++ parser->lex_number_as_string = 1; ++ parser->lex_joined_string = 1; ++ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, ++ "expected an attribute keyword"); ++ parser->lex_number_as_string = 0; ++ parser->lex_joined_string = saved_join_state; ++ return error_mark_node; ++ } ++ if (c_parser_next_token_is (parser, CPP_EQ)) ++ { ++ c_parser_consume_token (parser); /* eat the '=' */ ++ /* We need to bludgeon the lexer into not trying to interpret the ++ xx.yy.zz form, since that just looks like a malformed float. ++ Also, as a result of macro processing, we can have strig literals ++ that are in multiple pieces so, for this specific part of the ++ parse, we need to join strings. */ ++ bool saved_join_state = parser->lex_joined_string; ++ parser->lex_number_as_string = 1; ++ parser->lex_joined_string = 1; ++ /* So look at the next token, expecting a string, or something that ++ looks initially like a number, but might be a version number. */ ++ c_parser_peek_token (parser); ++ /* Done with the funky number parsing. */ ++ parser->lex_number_as_string = 0; ++ parser->lex_joined_string = saved_join_state; ++ if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) ++ && c_parser_next_token_is_not (parser, CPP_COMMA)) ++ { ++ value = c_parser_peek_token (parser)->value; ++ /* ???: check for error mark and early-return? */ ++ c_parser_consume_token (parser); ++ } ++ /* else value is absent. */ ++ } ++ else if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) ++ && c_parser_next_token_is_not (parser, CPP_COMMA)) ++ { ++ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, ++ "expected %<,%> or %<=%>"); ++ return error_mark_node; ++ } ++ if (c_parser_next_token_is (parser, CPP_COMMA)) ++ c_parser_consume_token (parser); /* Just skip the comma. */ ++ tree t = tree_cons (value, name, NULL); ++ if (!attr_args) ++ attr_args = t; ++ else ++ chainon (attr_args, t); ++ } while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)); ++ return attr_args; ++} ++ + /* Parse attribute arguments. This is a common form of syntax + covering all currently valid GNU and standard attributes. + +@@ -5158,9 +5243,13 @@ c_parser_gnu_attribute (c_parser *parser, tree attrs, + attrs = chainon (attrs, attr); + return attrs; + } +- c_parser_consume_token (parser); ++ c_parser_consume_token (parser); /* The '('. */ + +- tree attr_args ++ tree attr_args; ++ if (attribute_clang_form_p (attr_name)) ++ attr_args = c_parser_clang_attribute_arguments (parser, attr_name); ++ else ++ attr_args + = c_parser_attribute_arguments (parser, + attribute_takes_identifier_p (attr_name), + false, diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc -index 67709912a11..a0adac162e9 100644 +index 6770991..a0adac1 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -607,6 +607,18 @@ attribute_takes_identifier_p (const_tree attr_id) @@ -966,7 +1176,7 @@ index 67709912a11..a0adac162e9 100644 applied to function FN (which is either a function declaration or function type) refers to a function parameter at position POS and the expected type diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc -index 303d7f1ef5d..e3c3fae8dea 100644 +index 303d7f1..e3c3fae 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -311,6 +311,44 @@ const struct fname_var_t fname_vars[] = @@ -1079,7 +1289,7 @@ index 303d7f1ef5d..e3c3fae8dea 100644 + #include "gt-c-family-c-common.h" diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h -index f96350b64af..41d69d45ea6 100644 +index f96350b..41d69d4 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1121,6 +1121,14 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level, @@ -1106,7 +1316,7 @@ index f96350b64af..41d69d45ea6 100644 extern tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *); extern int parse_tm_stmt_attr (tree, int); diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc -index 0acfdaa95c9..2a504a98edf 100644 +index 0acfdaa..2a504a9 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -82,6 +82,7 @@ init_c_lex (void) @@ -1209,7 +1419,7 @@ index 0acfdaa95c9..2a504a98edf 100644 unsigned int flags = cpp_classify_number (parse_in, tok, &suffix, *loc); diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc -index c68a2a27469..a600d40c87e 100644 +index c68a2a2..a600d40 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1070,7 +1070,7 @@ c_common_post_options (const char **pfilename) @@ -1222,7 +1432,7 @@ index c68a2a27469..a600d40c87e 100644 /* Lazy TLS initialization for a variable in another TU requires alias and weak reference support. */ diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc -index 4aa2bef2c0f..a1488c6f086 100644 +index 4aa2bef..a1488c6 100644 --- a/gcc/c-family/c-ppoutput.cc +++ b/gcc/c-family/c-ppoutput.cc @@ -162,6 +162,7 @@ init_pp_output (FILE *out_stream) @@ -1234,7 +1444,7 @@ index 4aa2bef2c0f..a1488c6f086 100644 cb->remap_filename = remap_macro_filename; diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h -index 9cc95ab3ee3..3e86a16d4ae 100644 +index 9cc95ab..3e86a16 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -272,6 +272,9 @@ extern enum cpp_ttype pragma_lex (tree *, location_t *loc = NULL); @@ -1248,7 +1458,7 @@ index 9cc95ab3ee3..3e86a16d4ae 100644 /* This is not actually available to pragma parsers. It's merely a convenient location to declare this function for c-lex, after diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt -index a75038930ae..c7e662018d5 100644 +index a750389..c7e6620 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1484,6 +1484,10 @@ Wsubobject-linkage @@ -1271,231 +1481,21 @@ index a75038930ae..c7e662018d5 100644 fnil-receivers ObjC ObjC++ Var(flag_nil_receivers) Init(1) -diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc -index b4e0c8cfb8a..11e7aaac2e3 100644 ---- a/gcc/c/c-lang.cc -+++ b/gcc/c/c-lang.cc -@@ -61,6 +61,15 @@ c_get_sarif_source_language (const char *) - return "c"; - } - -+/* Implement c-family hook to register language-specific features for -+ __has_{feature,extension}. */ -+ -+void -+c_family_register_lang_features () -+{ -+ c_register_features (); -+} -+ - #if CHECKING_P +diff --git a/gcc/calls.cc b/gcc/calls.cc +index 53b0f58..b58990f 100644 +--- a/gcc/calls.cc ++++ b/gcc/calls.cc +@@ -1367,7 +1367,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, + with those made by function.cc. */ - namespace selftest { -diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc -index e4aed61ed00..fad46626570 100644 ---- a/gcc/c/c-objc-common.cc -+++ b/gcc/c/c-objc-common.cc -@@ -34,6 +34,38 @@ along with GCC; see the file COPYING3. If not see - static bool c_tree_printer (pretty_printer *, text_info *, const char *, - int, bool, bool, bool, bool *, const char **); - -+/* Info for C language features which can be queried through -+ __has_{feature,extension}. */ -+ -+struct c_feature_info -+{ -+ const char *ident; -+ const int *enable_flag; -+}; -+ -+static const c_feature_info c_feature_table[] = -+{ -+ { "c_alignas", &flag_isoc11 }, -+ { "c_alignof", &flag_isoc11 }, -+ { "c_atomic", &flag_isoc11 }, -+ { "c_generic_selections", &flag_isoc11 }, -+ { "c_static_assert", &flag_isoc11 }, -+ { "c_thread_local", &flag_isoc11 } -+}; -+ -+/* Register features specific to the C language. */ -+ -+void -+c_register_features () -+{ -+ for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++) -+ { -+ const c_feature_info *info = c_feature_table + i; -+ const bool feat_p = !info->enable_flag || *info->enable_flag; -+ c_common_register_feature (info->ident, feat_p); -+ } -+} -+ - bool - c_missing_noreturn_ok_p (tree decl) - { -diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h -index d31dacb9dd4..34dc23a1bd0 100644 ---- a/gcc/c/c-objc-common.h -+++ b/gcc/c/c-objc-common.h -@@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see - #ifndef GCC_C_OBJC_COMMON - #define GCC_C_OBJC_COMMON - -+/* Implemented in c-objc-common.cc. */ -+extern void c_register_features (); -+ - /* Lang hooks that are shared between C and ObjC are defined here. Hooks - specific to C or ObjC go in c-lang.cc and objc/objc-lang.cc, respectively. */ - -diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc -index 3627a3fbdc7..5abc6e84697 100644 ---- a/gcc/c/c-parser.cc -+++ b/gcc/c/c-parser.cc -@@ -217,6 +217,9 @@ struct GTY(()) c_parser { - should translate them to the execution character set (false - inside attributes). */ - BOOL_BITFIELD translate_strings_p : 1; -+ /* True if we want to lex arbitrary number-like sequences as their -+ string representation. */ -+ BOOL_BITFIELD lex_number_as_string : 1; - - /* Objective-C specific parser/lexer information. */ - -@@ -291,10 +294,10 @@ c_lex_one_token (c_parser *parser, c_token *token, bool raw = false) - - if (raw || vec_safe_length (parser->raw_tokens) == 0) - { -+ int lex_flags = parser->lex_joined_string ? 0 : C_LEX_STRING_NO_JOIN; -+ lex_flags |= parser->lex_number_as_string ? C_LEX_NUMBER_AS_STRING : 0; - token->type = c_lex_with_flags (&token->value, &token->location, -- &token->flags, -- (parser->lex_joined_string -- ? 0 : C_LEX_STRING_NO_JOIN)); -+ &token->flags, lex_flags); - token->id_kind = C_ID_NONE; - token->keyword = RID_MAX; - token->pragma_kind = PRAGMA_NONE; -@@ -4993,6 +4996,88 @@ c_parser_gnu_attribute_any_word (c_parser *parser) - return attr_name; - } - -+/* Handle parsing clang-form attribute arguments, where we need to adjust -+ the parsing rules to relate to a specific attribute. */ -+ -+static tree -+c_parser_clang_attribute_arguments (c_parser *parser, tree /*attr_id*/) -+{ -+ /* We can, if required, alter the parsing on the basis of the attribute. -+ At present, we handle the availability attr, where ach entry can be : -+ identifier -+ identifier=N.MM.Z -+ identifier="string" -+ followed by ',' or ) for the last entry*/ -+ -+ tree attr_args = NULL_TREE; -+ do -+ { -+ tree name = NULL_TREE; -+ tree value = NULL_TREE; -+ -+ if (c_parser_next_token_is (parser, CPP_NAME) -+ && c_parser_peek_token (parser)->id_kind == C_ID_ID) -+ { -+ name = c_parser_peek_token (parser)->value; -+ c_parser_consume_token (parser); -+ } -+ else if (c_parser_next_token_is (parser, CPP_COMMA)) -+ name = error_mark_node; /* Comma handled below. */ -+ else -+ { -+ bool saved_join_state = parser->lex_joined_string; -+ parser->lex_number_as_string = 1; -+ parser->lex_joined_string = 1; -+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, -+ "expected an attribute keyword"); -+ parser->lex_number_as_string = 0; -+ parser->lex_joined_string = saved_join_state; -+ return error_mark_node; -+ } -+ if (c_parser_next_token_is (parser, CPP_EQ)) -+ { -+ c_parser_consume_token (parser); /* eat the '=' */ -+ /* We need to bludgeon the lexer into not trying to interpret the -+ xx.yy.zz form, since that just looks like a malformed float. -+ Also, as a result of macro processing, we can have strig literals -+ that are in multiple pieces so, for this specific part of the -+ parse, we need to join strings. */ -+ bool saved_join_state = parser->lex_joined_string; -+ parser->lex_number_as_string = 1; -+ parser->lex_joined_string = 1; -+ /* So look at the next token, expecting a string, or something that -+ looks initially like a number, but might be a version number. */ -+ c_parser_peek_token (parser); -+ /* Done with the funky number parsing. */ -+ parser->lex_number_as_string = 0; -+ parser->lex_joined_string = saved_join_state; -+ if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) -+ && c_parser_next_token_is_not (parser, CPP_COMMA)) -+ { -+ value = c_parser_peek_token (parser)->value; -+ /* ???: check for error mark and early-return? */ -+ c_parser_consume_token (parser); -+ } -+ /* else value is absent. */ -+ } -+ else if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) -+ && c_parser_next_token_is_not (parser, CPP_COMMA)) -+ { -+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, -+ "expected %<,%> or %<=%>"); -+ return error_mark_node; -+ } -+ if (c_parser_next_token_is (parser, CPP_COMMA)) -+ c_parser_consume_token (parser); /* Just skip the comma. */ -+ tree t = tree_cons (value, name, NULL); -+ if (!attr_args) -+ attr_args = t; -+ else -+ chainon (attr_args, t); -+ } while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)); -+ return attr_args; -+} -+ - /* Parse attribute arguments. This is a common form of syntax - covering all currently valid GNU and standard attributes. - -@@ -5158,9 +5243,13 @@ c_parser_gnu_attribute (c_parser *parser, tree attrs, - attrs = chainon (attrs, attr); - return attrs; - } -- c_parser_consume_token (parser); -+ c_parser_consume_token (parser); /* The '('. */ - -- tree attr_args -+ tree attr_args; -+ if (attribute_clang_form_p (attr_name)) -+ attr_args = c_parser_clang_attribute_arguments (parser, attr_name); -+ else -+ attr_args - = c_parser_attribute_arguments (parser, - attribute_takes_identifier_p (attr_name), - false, -diff --git a/gcc/calls.cc b/gcc/calls.cc -index 53b0f58b709..b58990f1b90 100644 ---- a/gcc/calls.cc -+++ b/gcc/calls.cc -@@ -1367,7 +1367,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, - with those made by function.cc. */ - - /* See if this argument should be passed by invisible reference. */ -- function_arg_info arg (type, argpos < n_named_args); -+ function_arg_info arg (type, argpos < n_named_args, -+ argpos == n_named_args - 1); - if (pass_by_reference (args_so_far_pnt, arg)) - { - const bool callee_copies -@@ -1487,10 +1488,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, + /* See if this argument should be passed by invisible reference. */ +- function_arg_info arg (type, argpos < n_named_args); ++ function_arg_info arg (type, argpos < n_named_args, ++ argpos == n_named_args - 1); + if (pass_by_reference (args_so_far_pnt, arg)) + { + const bool callee_copies +@@ -1487,10 +1488,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, unsignedp = TYPE_UNSIGNED (type); arg.type = type; @@ -1565,7 +1565,7 @@ index 53b0f58b709..b58990f1b90 100644 args_size.constant += argvec[count].locate.size.constant; gcc_assert (!argvec[count].locate.size.var); diff --git a/gcc/calls.h b/gcc/calls.h -index c7f8c5e4b39..42a1774fe84 100644 +index c7f8c5e..42a1774 100644 --- a/gcc/calls.h +++ b/gcc/calls.h @@ -35,24 +35,43 @@ class function_arg_info @@ -1627,7 +1627,7 @@ index c7f8c5e4b39..42a1774fe84 100644 the function_arg_info describes a pointer to the original argument. */ unsigned int pass_by_reference : 1; diff --git a/gcc/collect2.cc b/gcc/collect2.cc -index 63b9a0c233a..1d7d9a442ac 100644 +index 63b9a0c..1d7d9a4 100644 --- a/gcc/collect2.cc +++ b/gcc/collect2.cc @@ -73,7 +73,7 @@ along with GCC; see the file COPYING3. If not see @@ -1734,61 +1734,8 @@ index 63b9a0c233a..1d7d9a442ac 100644 /* Check to make sure the file is an LTO object file. */ -diff --git a/gcc/common.opt b/gcc/common.opt -index b055c7bd9ac..cf32af4bbaf 100644 ---- a/gcc/common.opt -+++ b/gcc/common.opt -@@ -2794,6 +2794,10 @@ fstack-usage - Common RejectNegative Var(flag_stack_usage) - Output stack usage information on a per-function basis. - -+fstack-use-cumulative-args -+Common RejectNegative Var(flag_stack_use_cumulative_args) Init(STACK_USE_CUMULATIVE_ARGS_INIT) -+Use cumulative args-based stack layout hooks. -+ - fstrength-reduce - Common Ignore - Does nothing. Preserved for backward compatibility. -@@ -2862,10 +2866,25 @@ Common Var(flag_tracer) Optimization - Perform superblock formation via tail duplication. - - ftrampolines --Common Var(flag_trampolines) Init(0) -+Common Var(flag_trampolines) Init(HEAP_TRAMPOLINES_INIT) - For targets that normally need trampolines for nested functions, always - generate them instead of using descriptors. - -+ftrampoline-impl= -+Common Joined RejectNegative Enum(trampoline_impl) Var(flag_trampoline_impl) Init(HEAP_TRAMPOLINES_INIT ? TRAMPOLINE_IMPL_HEAP : TRAMPOLINE_IMPL_STACK) -+Whether trampolines are generated in executable memory rather than -+executable stack. -+ -+Enum -+Name(trampoline_impl) Type(enum trampoline_impl) UnknownError(unknown trampoline implementation %qs) -+ -+EnumValue -+Enum(trampoline_impl) String(stack) Value(TRAMPOLINE_IMPL_STACK) -+ -+EnumValue -+Enum(trampoline_impl) String(heap) Value(TRAMPOLINE_IMPL_HEAP) -+ -+ - ; Zero means that floating-point math operations cannot generate a - ; (user-visible) trap. This is the case, for example, in nonstop - ; IEEE 754 arithmetic. -@@ -3123,6 +3142,10 @@ fuse-ld=mold - Common Driver Negative(fuse-ld=mold) - Use the Modern linker (MOLD) linker instead of the default linker. - -+fuse-ld=classic -+Common Driver Negative(fuse-ld=classic) -+Use the ld-classic linker instead of the default linker. -+ - fuse-linker-plugin - Common Undocumented Var(flag_use_linker_plugin) - diff --git a/gcc/common/config/aarch64/aarch64-common.cc b/gcc/common/config/aarch64/aarch64-common.cc -index 20bc4e1291b..5058d2feaf4 100644 +index 20bc4e1..5058d2f 100644 --- a/gcc/common/config/aarch64/aarch64-common.cc +++ b/gcc/common/config/aarch64/aarch64-common.cc @@ -301,8 +301,12 @@ aarch64_get_extension_string_for_isa_flags @@ -1797,167 +1744,94 @@ index 20bc4e1291b..5058d2feaf4 100644 issue, so we don't need this fix when targeting Armv8-R. */ - auto explicit_flags = (!(current_flags & AARCH64_FL_V8R) - ? AARCH64_FL_CRC : 0); -+ aarch64_feature_flags explicit_flags = -+#ifndef DISABLE_AARCH64_AS_CRC_BUGFIX -+ (!(current_flags & AARCH64_ISA_V8R) ? AARCH64_FL_CRC : 0); -+#else -+ 0; -+#endif - - /* Add the features in isa_flags & ~current_flags using the smallest - possible number of extensions. We can do this by iterating over the -@@ -331,7 +335,10 @@ aarch64_get_extension_string_for_isa_flags - if (added & opt.flag_canonical) - { - outstr += "+"; -- outstr += opt.name; -+ if (startswith (opt.name, "rdm")) -+ outstr += "rdm"; -+ else -+ outstr += opt.name; - } - - /* Remove the features in current_flags & ~isa_flags. If the feature does -@@ -344,7 +351,10 @@ aarch64_get_extension_string_for_isa_flags - { - current_flags &= ~opt.flags_off; - outstr += "+no"; -- outstr += opt.name; -+ if (startswith (opt.name, "rdm")) -+ outstr += "rdm"; -+ else -+ outstr += opt.name; - } - - return outstr; -diff --git a/gcc/config.gcc b/gcc/config.gcc -index c3b73d05eb7..1b807618a1e 100644 ---- a/gcc/config.gcc -+++ b/gcc/config.gcc -@@ -1125,6 +1125,26 @@ case ${target} in - ;; - esac - -+# Figure out if we need to enable heap trampolines -+# and variadic functions handling. -+case ${target} in -+aarch64*-*-darwin2*) -+ # This applies to arm64 Darwin variadic funtions. -+ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=1" -+ # Executable stack is forbidden. -+ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=1" -+ ;; -+*-*-darwin2*) -+ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=0" -+ # Currently, we do this for macOS 11 and above. -+ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=1" -+ ;; -+*) -+ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=0" -+ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=0" -+ ;; -+esac -+ - case ${target} in - aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*) - tm_file="${tm_file} elfos.h newlib-stdint.h" -@@ -1164,6 +1184,14 @@ aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*) - done - TM_MULTILIB_CONFIG=`echo $TM_MULTILIB_CONFIG | sed 's/^,//'` - ;; -+aarch64-*-darwin* ) -+ tm_file="${tm_file} aarch64/aarch64-errata.h" -+ tmake_file="${tmake_file} aarch64/t-aarch64 aarch64/t-aarch64-darwin" -+ tm_defines="${tm_defines} TARGET_DEFAULT_ASYNC_UNWIND_TABLES=1" -+ tm_defines="${tm_defines} DISABLE_AARCH64_AS_CRC_BUGFIX=1" -+ # Choose a default CPU version that will work for all current releases. -+ with_cpu=${with_cpu:-apple-m1} -+ ;; - aarch64*-*-freebsd*) - tm_file="${tm_file} elfos.h ${fbsd_tm_file}" - tm_file="${tm_file} aarch64/aarch64-elf.h aarch64/aarch64-errata.h aarch64/aarch64-freebsd.h" -@@ -4125,8 +4153,8 @@ case "${target}" in - fi - for which in cpu arch tune; do - eval "val=\$with_$which" -- base_val=`echo $val | sed -e 's/\+.*//'` -- ext_val=`echo $val | sed -e 's/[a-z0-9.-]\+//'` -+ base_val=`echo $val | sed -E -e 's/\+.*//'` -+ ext_val=`echo $val | sed -E -e 's/[a-z0-9.-]+//'` ++ aarch64_feature_flags explicit_flags = ++#ifndef DISABLE_AARCH64_AS_CRC_BUGFIX ++ (!(current_flags & AARCH64_ISA_V8R) ? AARCH64_FL_CRC : 0); ++#else ++ 0; ++#endif - if [ $which = arch ]; then - def=aarch64-arches.def -@@ -4158,9 +4186,9 @@ case "${target}" in + /* Add the features in isa_flags & ~current_flags using the smallest + possible number of extensions. We can do this by iterating over the +@@ -331,7 +335,10 @@ aarch64_get_extension_string_for_isa_flags + if (added & opt.flag_canonical) + { + outstr += "+"; +- outstr += opt.name; ++ if (startswith (opt.name, "rdm")) ++ outstr += "rdm"; ++ else ++ outstr += opt.name; + } - while [ x"$ext_val" != x ] - do -- ext_val=`echo $ext_val | sed -e 's/\+//'` -- ext=`echo $ext_val | sed -e 's/\+.*//'` -- base_ext=`echo $ext | sed -e 's/^no//'` -+ ext_val=`echo $ext_val | sed -E -e 's/\+//'` -+ ext=`echo $ext_val | sed -E -e 's/\+.*//'` -+ base_ext=`echo $ext | sed -E -e 's/^no//'` - opt_line=`echo -e "$options_parsed" | \ - grep "^\"$base_ext\""` + /* Remove the features in current_flags & ~isa_flags. If the feature does +@@ -344,7 +351,10 @@ aarch64_get_extension_string_for_isa_flags + { + current_flags &= ~opt.flags_off; + outstr += "+no"; +- outstr += opt.name; ++ if (startswith (opt.name, "rdm")) ++ outstr += "rdm"; ++ else ++ outstr += opt.name; + } -@@ -4171,7 +4199,7 @@ case "${target}" in - echo "Unknown extension used in --with-$which=$val" 1>&2 - exit 1 - fi -- ext_val=`echo $ext_val | sed -e 's/[a-z0-9]\+//'` -+ ext_val=`echo $ext_val | sed -E -e 's/[a-z0-9]+//'` - done + return outstr; +diff --git a/gcc/common.opt b/gcc/common.opt +index b055c7b..cf32af4 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -2794,6 +2794,10 @@ fstack-usage + Common RejectNegative Var(flag_stack_usage) + Output stack usage information on a per-function basis. - true -diff --git a/gcc/config.in b/gcc/config.in -index 5281a12a707..b70b0bebda9 100644 ---- a/gcc/config.in -+++ b/gcc/config.in -@@ -49,6 +49,19 @@ - #endif ++fstack-use-cumulative-args ++Common RejectNegative Var(flag_stack_use_cumulative_args) Init(STACK_USE_CUMULATIVE_ARGS_INIT) ++Use cumulative args-based stack layout hooks. ++ + fstrength-reduce + Common Ignore + Does nothing. Preserved for backward compatibility. +@@ -2862,10 +2866,25 @@ Common Var(flag_tracer) Optimization + Perform superblock formation via tail duplication. + ftrampolines +-Common Var(flag_trampolines) Init(0) ++Common Var(flag_trampolines) Init(HEAP_TRAMPOLINES_INIT) + For targets that normally need trampolines for nested functions, always + generate them instead of using descriptors. -+/* Specify a runpath directory, additional to those provided by the compiler -+ */ -+#ifndef USED_FOR_TARGET -+#undef DARWIN_ADD_RPATH -+#endif ++ftrampoline-impl= ++Common Joined RejectNegative Enum(trampoline_impl) Var(flag_trampoline_impl) Init(HEAP_TRAMPOLINES_INIT ? TRAMPOLINE_IMPL_HEAP : TRAMPOLINE_IMPL_STACK) ++Whether trampolines are generated in executable memory rather than ++executable stack. + ++Enum ++Name(trampoline_impl) Type(enum trampoline_impl) UnknownError(unknown trampoline implementation %qs) + -+/* Should add an extra runpath directory */ -+#ifndef USED_FOR_TARGET -+#undef DARWIN_DO_EXTRA_RPATH -+#endif ++EnumValue ++Enum(trampoline_impl) String(stack) Value(TRAMPOLINE_IMPL_STACK) + ++EnumValue ++Enum(trampoline_impl) String(heap) Value(TRAMPOLINE_IMPL_HEAP) + - /* Define to enable the use of a default assembler. */ - #ifndef USED_FOR_TARGET - #undef DEFAULT_ASSEMBLER -@@ -634,8 +647,7 @@ - #endif - - --/* Define if your Mac OS X assembler supports -mllvm -x86-pad-for-align=false. -- */ -+/* Define if your macOS assembler supports -mllvm -x86-pad-for-align=false. */ - #ifndef USED_FOR_TARGET - #undef HAVE_AS_MLLVM_X86_PAD_FOR_ALIGN - #endif -@@ -2201,6 +2213,12 @@ - #endif - - -+/* Define to 1 if ld64 supports '-demangle'. */ -+#ifndef USED_FOR_TARGET -+#undef LD64_HAS_DEMANGLE -+#endif + + ; Zero means that floating-point math operations cannot generate a + ; (user-visible) trap. This is the case, for example, in nonstop + ; IEEE 754 arithmetic. +@@ -3123,6 +3142,10 @@ fuse-ld=mold + Common Driver Negative(fuse-ld=mold) + Use the Modern linker (MOLD) linker instead of the default linker. + ++fuse-ld=classic ++Common Driver Negative(fuse-ld=classic) ++Use the ld-classic linker instead of the default linker. + - /* Define to 1 if ld64 supports '-export_dynamic'. */ - #ifndef USED_FOR_TARGET - #undef LD64_HAS_EXPORT_DYNAMIC + fuse-linker-plugin + Common Undocumented Var(flag_use_linker_plugin) + diff --git a/gcc/config/aarch64/aarch64-builtins.cc b/gcc/config/aarch64/aarch64-builtins.cc -index 8ad82841a4d..567fb10b7fb 100644 +index 8ad8284..567fb10 100644 --- a/gcc/config/aarch64/aarch64-builtins.cc +++ b/gcc/config/aarch64/aarch64-builtins.cc @@ -785,6 +785,8 @@ enum aarch64_builtins @@ -2036,7 +1910,7 @@ index 8ad82841a4d..567fb10b7fb 100644 tree aarch64_general_builtin_decl (unsigned code, bool) diff --git a/gcc/config/aarch64/aarch64-c.cc b/gcc/config/aarch64/aarch64-c.cc -index 578ec6f45b0..56c83ac05a7 100644 +index 578ec6f..56c83ac 100644 --- a/gcc/config/aarch64/aarch64-c.cc +++ b/gcc/config/aarch64/aarch64-c.cc @@ -224,6 +224,16 @@ aarch64_cpu_cpp_builtins (cpp_reader *pfile) @@ -2066,7 +1940,7 @@ index 578ec6f45b0..56c83ac05a7 100644 +#endif } diff --git a/gcc/config/aarch64/aarch64-cores.def b/gcc/config/aarch64/aarch64-cores.def -index fdda0697b88..2dc4ae6ce52 100644 +index fdda069..2dc4ae6 100644 --- a/gcc/config/aarch64/aarch64-cores.def +++ b/gcc/config/aarch64/aarch64-cores.def @@ -165,6 +165,18 @@ AARCH64_CORE("cortex-a76.cortex-a55", cortexa76cortexa55, cortexa53, V8_2A, (F @@ -2089,7 +1963,7 @@ index fdda0697b88..2dc4ae6ce52 100644 /* Arm ('A') cores. */ diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h -index 32716f6cb15..a107f9ef069 100644 +index 32716f6..a107f9e 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -109,6 +109,14 @@ enum aarch64_symbol_type @@ -2136,7 +2010,7 @@ index 32716f6cb15..a107f9ef069 100644 gimple *aarch64_general_gimple_fold_builtin (unsigned int, gcall *, gimple_stmt_iterator *); diff --git a/gcc/config/aarch64/aarch64-tune.md b/gcc/config/aarch64/aarch64-tune.md -index 9d46d38a292..3c3aa72d3de 100644 +index 9d46d38..3c3aa72 100644 --- a/gcc/config/aarch64/aarch64-tune.md +++ b/gcc/config/aarch64/aarch64-tune.md @@ -1,5 +1,5 @@ @@ -2147,7 +2021,7 @@ index 9d46d38a292..3c3aa72d3de 100644 + "cortexa34,cortexa35,cortexa53,cortexa57,cortexa72,cortexa73,thunderx,thunderxt88p1,thunderxt88,octeontx,octeontxt81,octeontxt83,thunderxt81,thunderxt83,ampere1,ampere1a,emag,xgene1,falkor,qdf24xx,exynosm1,phecda,thunderx2t99p1,vulcan,thunderx2t99,cortexa55,cortexa75,cortexa76,cortexa76ae,cortexa77,cortexa78,cortexa78ae,cortexa78c,cortexa65,cortexa65ae,cortexx1,cortexx1c,neoversen1,ares,neoversee1,octeontx2,octeontx2t98,octeontx2t96,octeontx2t93,octeontx2f95,octeontx2f95n,octeontx2f95mm,a64fx,tsv110,thunderx3t110,neoversev1,zeus,neoverse512tvb,saphira,cortexa57cortexa53,cortexa72cortexa53,cortexa73cortexa35,cortexa73cortexa53,cortexa75cortexa55,cortexa76cortexa55,cortexr82,applea12,applem1,applem2,applem3,cortexa510,cortexa710,cortexa715,cortexx2,cortexx3,neoversen2,cobalt100,neoversev2,demeter" (const (symbol_ref "((enum attr_tune) aarch64_tune)"))) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc -index b8a4ab1b980..d2f503447ae 100644 +index b8a4ab1..d2f5034 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -295,8 +295,10 @@ static bool aarch64_vfp_is_call_or_return_candidate (machine_mode, @@ -3565,7 +3439,7 @@ index b8a4ab1b980..d2f503447ae 100644 #include "gt-aarch64.h" diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h -index cfeaf4657ab..81d5658f1cb 100644 +index cfeaf46..81d5658 100644 --- a/gcc/config/aarch64/aarch64.h +++ b/gcc/config/aarch64/aarch64.h @@ -65,6 +65,10 @@ @@ -3644,7 +3518,7 @@ index cfeaf4657ab..81d5658f1cb 100644 So in order to unwind a function using a frame pointer, the very first function that is unwound must save the frame pointer. That way the frame diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md -index 922cc987595..5c7c81548e1 100644 +index 922cc98..5c7c815 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -304,6 +304,7 @@ @@ -3764,7 +3638,7 @@ index 922cc987595..5c7c81548e1 100644 ) diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt -index 1d7967db9c0..cc97d7263ba 100644 +index 1d7967d..cc97d72 100644 --- a/gcc/config/aarch64/aarch64.opt +++ b/gcc/config/aarch64/aarch64.opt @@ -158,6 +158,13 @@ Enum(aarch64_abi) String(ilp32) Value(AARCH64_ABI_ILP32) @@ -3782,7 +3656,7 @@ index 1d7967db9c0..cc97d7263ba 100644 Target Save Var(pcrelative_literal_loads) Init(2) Save PC relative literal loads. diff --git a/gcc/config/aarch64/constraints.md b/gcc/config/aarch64/constraints.md -index 5b20abc27e5..9c6a631c6fb 100644 +index 5b20abc..9c6a631 100644 --- a/gcc/config/aarch64/constraints.md +++ b/gcc/config/aarch64/constraints.md @@ -168,7 +168,9 @@ @@ -3808,9 +3682,9 @@ index 5b20abc27e5..9c6a631c6fb 100644 (define_constraint "vgb" "@internal A constraint that matches an immediate offset valid for SVE LD1B -diff --git a/gcc/config/aarch64/darwin.h b/gcc/config/aarch64/darwin.h +diff --git b/gcc/config/aarch64/darwin.h b/gcc/config/aarch64/darwin.h new file mode 100644 -index 00000000000..08febf1401b +index 0000000..08febf1 --- /dev/null +++ b/gcc/config/aarch64/darwin.h @@ -0,0 +1,289 @@ @@ -4104,7 +3978,7 @@ index 00000000000..08febf1401b + extern void sys_icache_invalidate(void *start, size_t len); \ + sys_icache_invalidate ((beg), (size_t)((end)-(beg))) diff --git a/gcc/config/aarch64/driver-aarch64.cc b/gcc/config/aarch64/driver-aarch64.cc -index 8e318892b10..23f0fa84200 100644 +index 8e31889..23f0fa8 100644 --- a/gcc/config/aarch64/driver-aarch64.cc +++ b/gcc/config/aarch64/driver-aarch64.cc @@ -28,6 +28,74 @@ @@ -4188,7 +4062,7 @@ index 8e318892b10..23f0fa84200 100644 +#endif diff --git a/gcc/config/aarch64/falkor-tag-collision-avoidance.cc b/gcc/config/aarch64/falkor-tag-collision-avoidance.cc -index 39e3f5c2d1b..9b3357f5952 100644 +index 39e3f5c..9b3357f 100644 --- a/gcc/config/aarch64/falkor-tag-collision-avoidance.cc +++ b/gcc/config/aarch64/falkor-tag-collision-avoidance.cc @@ -740,7 +740,7 @@ dump_insn_list (const rtx &t, const insn_info_list_t &insn_info, @@ -4201,7 +4075,7 @@ index 39e3f5c2d1b..9b3357f5952 100644 for (unsigned i = 0; i < insn_info.length (); i++) dump_insn_slim (dump_file, insn_info[i]->insn); diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md -index 86a196d3536..0708a924745 100644 +index 86a196d..0708a92 100644 --- a/gcc/config/aarch64/iterators.md +++ b/gcc/config/aarch64/iterators.md @@ -316,6 +316,9 @@ @@ -4215,7 +4089,7 @@ index 86a196d3536..0708a924745 100644 ;; Advanced SIMD opaque structure modes. diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md -index 3f5f4df8c46..4c3498dfe2c 100644 +index 3f5f4df..4c3498d 100644 --- a/gcc/config/aarch64/predicates.md +++ b/gcc/config/aarch64/predicates.md @@ -277,9 +277,24 @@ @@ -4243,9 +4117,9 @@ index 3f5f4df8c46..4c3498dfe2c 100644 return (aarch64_classify_symbolic_expression (op) != SYMBOL_FORCE_TO_MEM); }) -diff --git a/gcc/config/aarch64/t-aarch64-darwin b/gcc/config/aarch64/t-aarch64-darwin +diff --git b/gcc/config/aarch64/t-aarch64-darwin b/gcc/config/aarch64/t-aarch64-darwin new file mode 100644 -index 00000000000..e2b8ad9237f +index 0000000..e2b8ad9 --- /dev/null +++ b/gcc/config/aarch64/t-aarch64-darwin @@ -0,0 +1,25 @@ @@ -4275,7 +4149,7 @@ index 00000000000..e2b8ad9237f +# a) arm64e +# b) arm64_32 diff --git a/gcc/config/darwin-c.cc b/gcc/config/darwin-c.cc -index 579b9fa9317..10d5fb12d9b 100644 +index 579b9fa..10d5fb1 100644 --- a/gcc/config/darwin-c.cc +++ b/gcc/config/darwin-c.cc @@ -555,7 +555,7 @@ find_subframework_header (cpp_reader *pfile, const char *header, cpp_dir **dirp) @@ -4315,7 +4189,7 @@ index 579b9fa9317..10d5fb12d9b 100644 "10.9" produces "1090", and "10.10.1" produces "101001".) If darwin_macosx_version_min is invalid and cannot be coerced into a valid diff --git a/gcc/config/darwin-driver.cc b/gcc/config/darwin-driver.cc -index 9c1dcc3d794..b2f39af9f68 100644 +index 9c1dcc3..b2f39af 100644 --- a/gcc/config/darwin-driver.cc +++ b/gcc/config/darwin-driver.cc @@ -268,10 +268,13 @@ darwin_driver_init (unsigned int *decoded_options_count, @@ -4421,7 +4295,7 @@ index 9c1dcc3d794..b2f39af9f68 100644 be used. */ if (!seen_version_min) diff --git a/gcc/config/darwin-protos.h b/gcc/config/darwin-protos.h -index 9df358ee7d3..babc8883b6e 100644 +index 9df358e..babc888 100644 --- a/gcc/config/darwin-protos.h +++ b/gcc/config/darwin-protos.h @@ -86,9 +86,12 @@ extern void darwin_asm_lto_end (void); @@ -4449,7 +4323,7 @@ index 9df358ee7d3..babc8883b6e 100644 extern void darwin_patch_builtins (void); extern void darwin_rename_builtins (void); diff --git a/gcc/config/darwin.cc b/gcc/config/darwin.cc -index 1471dbb6046..e95520f2a15 100644 +index 1471dbb..e95520f 100644 --- a/gcc/config/darwin.cc +++ b/gcc/config/darwin.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see @@ -4908,7 +4782,7 @@ index 1471dbb6046..e95520f2a15 100644 /* Some codegen needs to account for the capabilities of the target linker. */ diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h -index 5c6c38ddc63..fcf9eff27d3 100644 +index 5c6c38d..fcf9eff 100644 --- a/gcc/config/darwin.h +++ b/gcc/config/darwin.h @@ -1,4 +1,4 @@ @@ -5175,7 +5049,7 @@ index 5c6c38ddc63..fcf9eff27d3 100644 extern void darwin_driver_init (unsigned int *,struct cl_decoded_option **); #define GCC_DRIVER_HOST_INITIALIZATION \ diff --git a/gcc/config/darwin.opt b/gcc/config/darwin.opt -index d655aaef2fb..97b1a747918 100644 +index d655aae..97b1a74 100644 --- a/gcc/config/darwin.opt +++ b/gcc/config/darwin.opt @@ -91,6 +91,10 @@ mtarget-linker @@ -5201,7 +5075,7 @@ index d655aaef2fb..97b1a747918 100644 Driver RejectNegative (Obsolete after 10.3.9) Set MH_NOPREFIXBINDING, in an executable. diff --git a/gcc/config/i386/darwin.h b/gcc/config/i386/darwin.h -index 588bd669bdd..657ea470683 100644 +index 588bd66..657ea47 100644 --- a/gcc/config/i386/darwin.h +++ b/gcc/config/i386/darwin.h @@ -121,6 +121,9 @@ along with GCC; see the file COPYING3. If not see @@ -5225,7 +5099,7 @@ index 588bd669bdd..657ea470683 100644 +#define X86_CUSTOM_FUNCTION_TEST \ + (flag_trampolines && flag_trampoline_impl == TRAMPOLINE_IMPL_HEAP) ? 0 : 1 diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc -index 499184166ff..be6d408031b 100644 +index 4991841..be6d408 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -25245,7 +25245,7 @@ ix86_libgcc_floating_mode_supported_p @@ -5238,7 +5112,7 @@ index 499184166ff..be6d408031b 100644 #undef TARGET_ADDR_SPACE_ZERO_ADDRESS_VALID #define TARGET_ADDR_SPACE_ZERO_ADDRESS_VALID ix86_addr_space_zero_address_valid diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h -index 539083f2fbf..77644b0ed1d 100644 +index 539083f..77644b0 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -754,6 +754,12 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); @@ -5255,7 +5129,7 @@ index 539083f2fbf..77644b0ed1d 100644 #define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_pfn diff --git a/gcc/config/rs6000/darwin.h b/gcc/config/rs6000/darwin.h -index 4d5d6f6d5a8..3a2e480ace6 100644 +index 4d5d6f6..3a2e480 100644 --- a/gcc/config/rs6000/darwin.h +++ b/gcc/config/rs6000/darwin.h @@ -98,7 +98,7 @@ @@ -5285,8 +5159,134 @@ index 4d5d6f6d5a8..3a2e480ace6 100644 /* We want -fPIC by default, unless we're using -static to compile for the kernel or some such. The "-faltivec" option should have been called "-maltivec" all along. */ +diff --git a/gcc/config.gcc b/gcc/config.gcc +index c3b73d0..1b80761 100644 +--- a/gcc/config.gcc ++++ b/gcc/config.gcc +@@ -1125,6 +1125,26 @@ case ${target} in + ;; + esac + ++# Figure out if we need to enable heap trampolines ++# and variadic functions handling. ++case ${target} in ++aarch64*-*-darwin2*) ++ # This applies to arm64 Darwin variadic funtions. ++ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=1" ++ # Executable stack is forbidden. ++ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=1" ++ ;; ++*-*-darwin2*) ++ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=0" ++ # Currently, we do this for macOS 11 and above. ++ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=1" ++ ;; ++*) ++ tm_defines="$tm_defines STACK_USE_CUMULATIVE_ARGS_INIT=0" ++ tm_defines="$tm_defines HEAP_TRAMPOLINES_INIT=0" ++ ;; ++esac ++ + case ${target} in + aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*) + tm_file="${tm_file} elfos.h newlib-stdint.h" +@@ -1164,6 +1184,14 @@ aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*) + done + TM_MULTILIB_CONFIG=`echo $TM_MULTILIB_CONFIG | sed 's/^,//'` + ;; ++aarch64-*-darwin* ) ++ tm_file="${tm_file} aarch64/aarch64-errata.h" ++ tmake_file="${tmake_file} aarch64/t-aarch64 aarch64/t-aarch64-darwin" ++ tm_defines="${tm_defines} TARGET_DEFAULT_ASYNC_UNWIND_TABLES=1" ++ tm_defines="${tm_defines} DISABLE_AARCH64_AS_CRC_BUGFIX=1" ++ # Choose a default CPU version that will work for all current releases. ++ with_cpu=${with_cpu:-apple-m1} ++ ;; + aarch64*-*-freebsd*) + tm_file="${tm_file} elfos.h ${fbsd_tm_file}" + tm_file="${tm_file} aarch64/aarch64-elf.h aarch64/aarch64-errata.h aarch64/aarch64-freebsd.h" +@@ -4125,8 +4153,8 @@ case "${target}" in + fi + for which in cpu arch tune; do + eval "val=\$with_$which" +- base_val=`echo $val | sed -e 's/\+.*//'` +- ext_val=`echo $val | sed -e 's/[a-z0-9.-]\+//'` ++ base_val=`echo $val | sed -E -e 's/\+.*//'` ++ ext_val=`echo $val | sed -E -e 's/[a-z0-9.-]+//'` + + if [ $which = arch ]; then + def=aarch64-arches.def +@@ -4158,9 +4186,9 @@ case "${target}" in + + while [ x"$ext_val" != x ] + do +- ext_val=`echo $ext_val | sed -e 's/\+//'` +- ext=`echo $ext_val | sed -e 's/\+.*//'` +- base_ext=`echo $ext | sed -e 's/^no//'` ++ ext_val=`echo $ext_val | sed -E -e 's/\+//'` ++ ext=`echo $ext_val | sed -E -e 's/\+.*//'` ++ base_ext=`echo $ext | sed -E -e 's/^no//'` + opt_line=`echo -e "$options_parsed" | \ + grep "^\"$base_ext\""` + +@@ -4171,7 +4199,7 @@ case "${target}" in + echo "Unknown extension used in --with-$which=$val" 1>&2 + exit 1 + fi +- ext_val=`echo $ext_val | sed -e 's/[a-z0-9]\+//'` ++ ext_val=`echo $ext_val | sed -E -e 's/[a-z0-9]+//'` + done + + true +diff --git a/gcc/config.in b/gcc/config.in +index 5281a12..b70b0be 100644 +--- a/gcc/config.in ++++ b/gcc/config.in +@@ -49,6 +49,19 @@ + #endif + + ++/* Specify a runpath directory, additional to those provided by the compiler ++ */ ++#ifndef USED_FOR_TARGET ++#undef DARWIN_ADD_RPATH ++#endif ++ ++ ++/* Should add an extra runpath directory */ ++#ifndef USED_FOR_TARGET ++#undef DARWIN_DO_EXTRA_RPATH ++#endif ++ ++ + /* Define to enable the use of a default assembler. */ + #ifndef USED_FOR_TARGET + #undef DEFAULT_ASSEMBLER +@@ -634,8 +647,7 @@ + #endif + + +-/* Define if your Mac OS X assembler supports -mllvm -x86-pad-for-align=false. +- */ ++/* Define if your macOS assembler supports -mllvm -x86-pad-for-align=false. */ + #ifndef USED_FOR_TARGET + #undef HAVE_AS_MLLVM_X86_PAD_FOR_ALIGN + #endif +@@ -2201,6 +2213,12 @@ + #endif + + ++/* Define to 1 if ld64 supports '-demangle'. */ ++#ifndef USED_FOR_TARGET ++#undef LD64_HAS_DEMANGLE ++#endif ++ ++ + /* Define to 1 if ld64 supports '-export_dynamic'. */ + #ifndef USED_FOR_TARGET + #undef LD64_HAS_EXPORT_DYNAMIC diff --git a/gcc/configure b/gcc/configure -index ade0af23e8c..4af01a09637 100755 +index ade0af2..4af01a0 100755 --- a/gcc/configure +++ b/gcc/configure @@ -632,10 +632,10 @@ ac_includes_default="\ @@ -5755,7 +5755,7 @@ index ade0af23e8c..4af01a09637 100755 : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/gcc/configure.ac b/gcc/configure.ac -index bf8ff4d6390..8c2ff477635 100644 +index bf8ff4d..8c2ff47 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -235,18 +235,17 @@ gcc_gxx_libcxx_include_dir= @@ -5957,7 +5957,7 @@ index bf8ff4d6390..8c2ff477635 100644 # Enable Intel CET on Intel CET enabled host if jit is enabled. GCC_CET_HOST_FLAGS(CET_HOST_FLAGS) diff --git a/gcc/coretypes.h b/gcc/coretypes.h -index ca8837cef67..7e022a427c4 100644 +index ca8837c..7e022a4 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -199,6 +199,12 @@ enum tls_model { @@ -5974,7 +5974,7 @@ index ca8837cef67..7e022a427c4 100644 enum offload_abi { OFFLOAD_ABI_UNSET, diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc -index 2f541460c4a..84200a9a04a 100644 +index 2f54146..84200a9 100644 --- a/gcc/cp/cp-lang.cc +++ b/gcc/cp/cp-lang.cc @@ -121,6 +121,15 @@ objcp_tsubst_copy_and_build (tree /*t*/, @@ -5994,7 +5994,7 @@ index 2f541460c4a..84200a9a04a 100644 cxx_dwarf_name (tree t, int verbosity) { diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc -index 93b027b80ce..10b9b35a1c0 100644 +index 93b027b..10b9b35 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -23,10 +23,153 @@ along with GCC; see the file COPYING3. If not see @@ -6152,7 +6152,7 @@ index 93b027b80ce..10b9b35a1c0 100644 alias_set_type diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h -index 80893aa1752..9d4d873deb7 100644 +index 80893aa..9d4d873 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -34,6 +34,7 @@ extern tree cp_classtype_as_base (const_tree); @@ -6164,7 +6164,7 @@ index 80893aa1752..9d4d873deb7 100644 location_t, const struct cl_option_handlers *); extern tree cxx_make_type_hook (tree_code); diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc -index 36e1f2ce300..213e52c70e4 100644 +index 36e1f2c..213e52c 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -3712,9 +3712,8 @@ get_tls_init_fn (tree var) @@ -6285,7 +6285,7 @@ index 36e1f2ce300..213e52c70e4 100644 if (!DECL_SAVED_TREE (decl)) continue; diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc -index 4e67da6ff3a..1f1b76234b4 100644 +index 4e67da6..1f1b762 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -694,6 +694,91 @@ cp_lexer_handle_early_pragma (cp_lexer *lexer) @@ -6568,9 +6568,9 @@ index 4e67da6ff3a..1f1b76234b4 100644 /* Add this attribute to the list. */ TREE_CHAIN (attribute) = attribute_list; attribute_list = attribute; -diff --git a/gcc/cumulative-args.h b/gcc/cumulative-args.h +diff --git b/gcc/cumulative-args.h b/gcc/cumulative-args.h new file mode 100644 -index 00000000000..b60928e37f9 +index 0000000..b60928e --- /dev/null +++ b/gcc/cumulative-args.h @@ -0,0 +1,20 @@ @@ -6595,7 +6595,7 @@ index 00000000000..b60928e37f9 + +#endif /* GCC_CUMULATIVE_ARGS_H */ diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in -index 1679fb81097..4fbf2096416 100644 +index 1679fb8..4fbf209 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -64,7 +64,7 @@ ALL_DFLAGS = $(DFLAGS-$@) $(GDCFLAGS) -fversion=IN_GCC $(CHECKING_DFLAGS) \ @@ -6608,7 +6608,7 @@ index 1679fb81097..4fbf2096416 100644 DPOSTCOMPILE = @mv $(@D)/$(DEPDIR)/$(*F).TPo $(@D)/$(DEPDIR)/$(*F).Po DLINKER = $(GDC) $(NO_PIE_FLAG) -lstdc++ diff --git a/gcc/doc/contrib.texi b/gcc/doc/contrib.texi -index 758805dc5db..f8f002cdc8e 100644 +index 758805d..f8f002c 100644 --- a/gcc/doc/contrib.texi +++ b/gcc/doc/contrib.texi @@ -1515,7 +1515,7 @@ Gael Thomas for @code{VMClassLoader} boot packages support suggestions. @@ -6621,7 +6621,7 @@ index 758805dc5db..f8f002cdc8e 100644 @item diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi -index b0a2ce3ac6b..f57278dd2e8 100644 +index b0a2ce3..f57278d 100644 --- a/gcc/doc/cpp.texi +++ b/gcc/doc/cpp.texi @@ -3198,6 +3198,8 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}. @@ -6680,7 +6680,7 @@ index b0a2ce3ac6b..f57278dd2e8 100644 @subsection @code{__has_include} @cindex @code{__has_include} diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi -index d6fcd611339..a16375085d5 100644 +index d6fcd61..a163750 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -23797,7 +23797,7 @@ attribute, do change the value of preprocessor macros like @@ -6702,7 +6702,7 @@ index d6fcd611339..a16375085d5 100644 You have the following options for dealing with template instantiations: diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi -index b30d3691fe6..de05d1c42da 100644 +index b30d369..de05d1c 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1075,14 +1075,26 @@ code. @@ -6748,7 +6748,7 @@ index b30d3691fe6..de05d1c42da 100644 Traditional AIX shared library versioning (versioned @code{Shared Object} files as members of unversioned @code{Archive Library} files named diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi -index 792ce283bb9..cedffc51987 100644 +index 792ce28..cedffc5 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -254,7 +254,8 @@ in the following sections. @@ -6958,7 +6958,7 @@ index 792ce283bb9..cedffc51987 100644 resulting code is suitable for applications, but not shared libraries. diff --git a/gcc/doc/plugins.texi b/gcc/doc/plugins.texi -index 26df8b490df..f9a23180ed8 100644 +index 26df8b4..f9a2318 100644 --- a/gcc/doc/plugins.texi +++ b/gcc/doc/plugins.texi @@ -44,7 +44,7 @@ Plugins are loaded with @@ -6971,7 +6971,7 @@ index 26df8b490df..f9a23180ed8 100644 plugins as key-value pairs. Multiple plugins can be invoked by specifying multiple @option{-fplugin} arguments. diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi -index a660e33739b..1080f85dfa1 100644 +index a660e33..1080f85 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -1042,6 +1042,10 @@ also define the hook to @code{default_promote_function_mode_always_promote} @@ -7041,7 +7041,7 @@ index a660e33739b..1080f85dfa1 100644 True if the backend architecture naturally supports ignoring some region of pointers. This feature means that @option{-fsanitize=hwaddress} can diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in -index f7ab5d48a63..7f82c02bf33 100644 +index f7ab5d4..7f82c02 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -938,6 +938,8 @@ applied. @@ -7085,7 +7085,7 @@ index f7ab5d48a63..7f82c02bf33 100644 @hook TARGET_MEMTAG_TAG_SIZE diff --git a/gcc/exec-tool.in b/gcc/exec-tool.in -index bddf46ab70a..a9120f36e19 100644 +index bddf46a..a9120f3 100644 --- a/gcc/exec-tool.in +++ b/gcc/exec-tool.in @@ -23,6 +23,8 @@ ORIGINAL_AS_FOR_TARGET="@ORIGINAL_AS_FOR_TARGET@" @@ -7158,7 +7158,7 @@ index bddf46ab70a..a9120f36e19 100644 if test "$original" = ../gold/ld-new$exeext; then dir=gold diff --git a/gcc/explow.cc b/gcc/explow.cc -index 6424c0802f0..7c2973a9602 100644 +index 6424c08..7c2973a 100644 --- a/gcc/explow.cc +++ b/gcc/explow.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see @@ -7187,7 +7187,7 @@ index 6424c0802f0..7c2973a9602 100644 PUNSIGNEDP points to the signedness of the type and may be adjusted to show what signedness to use on extension operations. */ diff --git a/gcc/explow.h b/gcc/explow.h -index 2db4f5c0de0..c7d22862187 100644 +index 2db4f5c..c7d2286 100644 --- a/gcc/explow.h +++ b/gcc/explow.h @@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see @@ -7216,7 +7216,7 @@ index 2db4f5c0de0..c7d22862187 100644 /* Return mode and signedness to use when an object in the given mode is promoted. */ diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi -index 87baf5ac9a1..908f2b7a344 100644 +index 87baf5a..908f2b7 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -978,7 +978,7 @@ low level file descriptor corresponding to an open Fortran unit. Then, @@ -7229,7 +7229,7 @@ index 87baf5ac9a1..908f2b7a344 100644 @smallexample diff --git a/gcc/function.cc b/gcc/function.cc -index 8d6c4478866..4308e24b5cb 100644 +index 8d6c447..4308e24 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -58,8 +58,8 @@ along with GCC; see the file COPYING3. If not see @@ -7335,7 +7335,7 @@ index 8d6c4478866..4308e24b5cb 100644 /* Alignment can't exceed MAX_SUPPORTED_STACK_ALIGNMENT. */ diff --git a/gcc/function.h b/gcc/function.h -index d4ce8a7c6c6..09ab17e66c1 100644 +index d4ce8a7..09ab17e 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see @@ -7355,7 +7355,7 @@ index d4ce8a7c6c6..09ab17e66c1 100644 struct locate_and_pad_arg_data *); extern void generate_setjmp_warnings (void); diff --git a/gcc/gcc.cc b/gcc/gcc.cc -index 16bb07f2cdc..d0349741d2f 100644 +index 16bb07f..d034974 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -575,6 +575,7 @@ or with constant text in a single argument. @@ -7434,7 +7434,7 @@ index 16bb07f2cdc..d0349741d2f 100644 for_each_path (&include_prefixes, false, info.append_len, spec_path, &info); diff --git a/gcc/ginclude/stddef.h b/gcc/ginclude/stddef.h -index 12ceef39180..af071079940 100644 +index 12ceef3..af07107 100644 --- a/gcc/ginclude/stddef.h +++ b/gcc/ginclude/stddef.h @@ -428,9 +428,8 @@ typedef struct { @@ -7450,7 +7450,7 @@ index 12ceef39180..af071079940 100644 #endif } max_align_t; diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in -index 55079209264..5bdba6c6a4c 100644 +index 5507920..5bdba6c 100644 --- a/gcc/jit/Make-lang.in +++ b/gcc/jit/Make-lang.in @@ -69,7 +69,7 @@ LIBGCCJIT_COMPAT = 0 @@ -7463,7 +7463,7 @@ index 55079209264..5bdba6c6a4c 100644 LIBGCCJIT_LINKER_NAME = $(LIBGCCJIT_BASENAME).dylib diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc -index e06f161aad9..2a04573b47a 100644 +index e06f161..2a04573 100644 --- a/gcc/jit/jit-playback.cc +++ b/gcc/jit/jit-playback.cc @@ -3024,7 +3024,7 @@ invoke_driver (const char *ctxt_progname, @@ -7476,7 +7476,7 @@ index e06f161aad9..2a04573b47a 100644 undefined until the .so is dynamically-linked into the process. Ensure that the driver passes in "-undefined dynamic_lookup" to the diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h -index 057d3e58e73..04545e4c6f6 100644 +index 057d3e5..04545e4 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see @@ -7490,7 +7490,7 @@ index 057d3e58e73..04545e4c6f6 100644 #ifdef __cplusplus extern "C" { diff --git a/gcc/m2/Make-lang.in b/gcc/m2/Make-lang.in -index 0ae3e183977..320f9300e5f 100644 +index 0ae3e18..320f930 100644 --- a/gcc/m2/Make-lang.in +++ b/gcc/m2/Make-lang.in @@ -501,6 +501,11 @@ GM2_MIN_FLAGS=$(GM2_G) $(GM2_OS) \ @@ -7515,7 +7515,7 @@ index 0ae3e183977..320f9300e5f 100644 m2/mc-boot/main.o: $(M2LINK) $(srcdir)/m2/init/mcinit -test -d $(@D)/$(DEPDIR) || $(mkinstalldirs) $(@D)/$(DEPDIR) diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc -index fe2d2b595b4..85580592803 100644 +index fe2d2b5..8558059 100644 --- a/gcc/objc/objc-act.cc +++ b/gcc/objc/objc-act.cc @@ -3317,7 +3317,7 @@ objc_build_string_object (tree string) @@ -7580,7 +7580,7 @@ index fe2d2b595b4..85580592803 100644 #include "gt-objc-objc-act.h" diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h -index e21ab52d8ca..bcf0249515a 100644 +index e21ab52..bcf0249 100644 --- a/gcc/objc/objc-act.h +++ b/gcc/objc/objc-act.h @@ -29,6 +29,9 @@ int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *); @@ -7594,7 +7594,7 @@ index e21ab52d8ca..bcf0249515a 100644 benefit of stub-objc.cc and objc-act.cc. */ diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc -index 89b3be48b9e..7568248ba13 100644 +index 89b3be4..7568248 100644 --- a/gcc/objc/objc-lang.cc +++ b/gcc/objc/objc-lang.cc @@ -58,6 +58,16 @@ objc_get_sarif_source_language (const char *) @@ -7615,7 +7615,7 @@ index 89b3be48b9e..7568248ba13 100644 there should be very few (if any) routines below. */ diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc -index 9887209b9c8..ede59a69d13 100644 +index 9887209..ede59a6 100644 --- a/gcc/objcp/objcp-lang.cc +++ b/gcc/objcp/objcp-lang.cc @@ -80,6 +80,16 @@ objcp_tsubst_copy_and_build (tree t, tree args, tsubst_flags_t complain, @@ -7636,7 +7636,7 @@ index 9887209b9c8..ede59a69d13 100644 objcxx_init_ts (void) { diff --git a/gcc/opts.cc b/gcc/opts.cc -index e0ba89ffe51..71371e23cf1 100644 +index e0ba89f..71371e2 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -3215,6 +3215,7 @@ common_handle_option (struct gcc_options *opts, @@ -7648,7 +7648,7 @@ index e0ba89ffe51..71371e23cf1 100644 case OPT_fuse_ld_lld: case OPT_fuse_ld_mold: diff --git a/gcc/plugin.cc b/gcc/plugin.cc -index 142f3fa4131..c3e40b2cf75 100644 +index 142f3fa..c3e40b2 100644 --- a/gcc/plugin.cc +++ b/gcc/plugin.cc @@ -190,10 +190,10 @@ add_new_plugin (const char* plugin_name) @@ -7665,7 +7665,7 @@ index 142f3fa4131..c3e40b2cf75 100644 recommendation rather than the rule. This raises the questions of how well they are supported by tools (e.g., libtool). So to avoid diff --git a/gcc/target.def b/gcc/target.def -index 171bbd1caf1..75b51d26074 100644 +index 171bbd1..75b51d2 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -4574,6 +4574,13 @@ if you would like to apply the same rules given by @code{PROMOTE_MODE}.", @@ -7738,7 +7738,7 @@ index 171bbd1caf1..75b51d26074 100644 HOOK_VECTOR_END (C90_EMPTY_HACK) diff --git a/gcc/target.h b/gcc/target.h -index cd448e4b7ab..064523f2a2e 100644 +index cd448e4..064523f 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -51,22 +51,8 @@ @@ -7767,7 +7767,7 @@ index cd448e4b7ab..064523f2a2e 100644 /* Types of memory operation understood by the "by_pieces" infrastructure. Used by the TARGET_USE_BY_PIECES_INFRASTRUCTURE_P target hook and diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc -index 51bf3fb7a82..13a7c206cc5 100644 +index 51bf3fb..13a7c20 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -159,6 +159,15 @@ default_promote_function_mode_always_promote (const_tree type, @@ -7817,7 +7817,7 @@ index 51bf3fb7a82..13a7c206cc5 100644 hook_void_bitmap (bitmap regs ATTRIBUTE_UNUSED) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h -index cf3d3107a0d..cd4e830b2f3 100644 +index cf3d310..cd4e830 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -34,6 +34,9 @@ extern machine_mode default_promote_function_mode (const_tree, machine_mode, @@ -7844,7 +7844,7 @@ index cf3d3107a0d..cd4e830b2f3 100644 extern rtx default_function_value (const_tree, const_tree, bool); extern HARD_REG_SET default_zero_call_used_regs (HARD_REG_SET); diff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc -index 0f44b3dc735..8355425f0d1 100644 +index 0f44b3d..8355425 100644 --- a/gcc/tree-nested.cc +++ b/gcc/tree-nested.cc @@ -611,6 +611,14 @@ get_trampoline_type (struct nesting_info *info) @@ -8006,7 +8006,7 @@ index 0f44b3dc735..8355425f0d1 100644 /* If a chain_decl was created, then it needs to be registered with diff --git a/gcc/tree.cc b/gcc/tree.cc -index 12dea81a5f3..88370418be4 100644 +index 12dea81..8837041 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -9853,6 +9853,28 @@ build_common_builtin_nodes (void) @@ -8039,7 +8039,7 @@ index 12dea81a5f3..88370418be4 100644 ptr_type_node, ptr_type_node, NULL_TREE); local_define_builtin ("__builtin_setjmp_setup", ftype, diff --git a/intl/Makefile.in b/intl/Makefile.in -index 409d693c48e..5beebdc152c 100644 +index 409d693..5beebdc 100644 --- a/intl/Makefile.in +++ b/intl/Makefile.in @@ -54,7 +54,7 @@ CTAGS = @CTAGS@ @@ -8052,7 +8052,7 @@ index 409d693c48e..5beebdc152c 100644 HEADERS = \ gmo.h \ diff --git a/intl/configure b/intl/configure -index 03f40487a92..79bb5831a47 100755 +index 03f4048..79bb583 100755 --- a/intl/configure +++ b/intl/configure @@ -623,6 +623,8 @@ ac_header_list= @@ -8115,7 +8115,7 @@ index 03f40487a92..79bb5831a47 100755 cat >confcache <<\_ACEOF diff --git a/intl/configure.ac b/intl/configure.ac -index 16a740aa230..81aa831f59f 100644 +index 16a740a..81aa831 100644 --- a/intl/configure.ac +++ b/intl/configure.ac @@ -83,10 +83,25 @@ fi @@ -8147,7 +8147,7 @@ index 16a740aa230..81aa831f59f 100644 AC_CONFIG_FILES(Makefile config.intl) diff --git a/libatomic/Makefile.am b/libatomic/Makefile.am -index c6c8d81c56a..3bb32f32ebf 100644 +index c6c8d81..3bb32f3 100644 --- a/libatomic/Makefile.am +++ b/libatomic/Makefile.am @@ -65,8 +65,13 @@ libatomic_version_script = @@ -8166,7 +8166,7 @@ index c6c8d81c56a..3bb32f32ebf 100644 fenv.c fence.c flag.c diff --git a/libatomic/Makefile.in b/libatomic/Makefile.in -index a0fa3dfc8cc..ef7ef451751 100644 +index a0fa3df..ef7ef45 100644 --- a/libatomic/Makefile.in +++ b/libatomic/Makefile.in @@ -417,7 +417,12 @@ noinst_LTLIBRARIES = libatomic_convenience.la @@ -8184,7 +8184,7 @@ index a0fa3dfc8cc..ef7ef451751 100644 init.c fenv.c fence.c flag.c $(am__append_2) SIZEOBJS = load store cas exch fadd fsub fand fior fxor fnand tas diff --git a/libatomic/configure b/libatomic/configure -index e47d2d7fb35..7c1d46b6762 100755 +index e47d2d7..7c1d46b 100755 --- a/libatomic/configure +++ b/libatomic/configure @@ -658,6 +658,8 @@ OPT_LDFLAGS @@ -8335,7 +8335,7 @@ index e47d2d7fb35..7c1d46b6762 100755 if test -z "${LIBAT_BUILD_VERSIONED_SHLIB_TRUE}" && test -z "${LIBAT_BUILD_VERSIONED_SHLIB_FALSE}"; then as_fn_error $? "conditional \"LIBAT_BUILD_VERSIONED_SHLIB\" was never defined. diff --git a/libatomic/configure.ac b/libatomic/configure.ac -index 31304685dbd..20981f16f70 100644 +index 3130468..20981f1 100644 --- a/libatomic/configure.ac +++ b/libatomic/configure.ac @@ -156,6 +156,8 @@ AC_SUBST(enable_shared) @@ -8348,7 +8348,7 @@ index 31304685dbd..20981f16f70 100644 libtool_VERSION=3:0:2 AC_SUBST(libtool_VERSION) diff --git a/libbacktrace/configure b/libbacktrace/configure -index 6af2c04c81a..4a25e38a2dc 100755 +index 6af2c04..4a25e38 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -681,6 +681,8 @@ PIC_FLAG @@ -8499,7 +8499,7 @@ index 6af2c04c81a..4a25e38a2dc 100755 as_fn_error $? "conditional \"HAVE_ELF\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac -index 39e6bf41e35..98b96fcb86f 100644 +index 39e6bf4..98b96fc 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -84,6 +84,8 @@ AM_CONDITIONAL(HAVE_DWZ, test "$DWZ" != "") @@ -8512,7 +8512,7 @@ index 39e6bf41e35..98b96fcb86f 100644 backtrace_supported=yes diff --git a/libcc1/configure b/libcc1/configure -index bae3b8712b6..cd9acc3bf13 100755 +index bae3b87..cd9acc3 100755 --- a/libcc1/configure +++ b/libcc1/configure @@ -787,6 +787,7 @@ with_pic @@ -8701,7 +8701,7 @@ index bae3b8712b6..cd9acc3bf13 100755 fi diff --git a/libcody/Makefile.in b/libcody/Makefile.in -index bb87468cb9a..cb01b0092d8 100644 +index bb87468..cb01b00 100644 --- a/libcody/Makefile.in +++ b/libcody/Makefile.in @@ -31,7 +31,7 @@ endif @@ -8714,7 +8714,7 @@ index bb87468cb9a..cb01b0092d8 100644 # Per-source & per-directory compile flags (warning: recursive) diff --git a/libcody/configure b/libcody/configure -index da52a5cfca5..0e536c0ccb0 100755 +index da52a5c..0e536c0 100755 --- a/libcody/configure +++ b/libcody/configure @@ -591,7 +591,10 @@ configure_args @@ -8781,7 +8781,7 @@ index da52a5cfca5..0e536c0ccb0 100755 # Check whether --enable-exceptions was given. diff --git a/libcody/configure.ac b/libcody/configure.ac -index 960191ecb72..14e8dd4a226 100644 +index 960191e..14e8dd4 100644 --- a/libcody/configure.ac +++ b/libcody/configure.ac @@ -63,9 +63,31 @@ fi @@ -8819,7 +8819,7 @@ index 960191ecb72..14e8dd4a226 100644 NMS_ENABLE_EXCEPTIONS diff --git a/libcpp/configure b/libcpp/configure -index e9937cde330..1389ddab544 100755 +index e9937cd..1389dda 100755 --- a/libcpp/configure +++ b/libcpp/configure @@ -625,6 +625,8 @@ ac_includes_default="\ @@ -8873,7 +8873,7 @@ index e9937cde330..1389ddab544 100755 PICFLAG= fi diff --git a/libcpp/configure.ac b/libcpp/configure.ac -index 89ac99b04bd..b29b4d6acf1 100644 +index 89ac99b..b29b4d6 100644 --- a/libcpp/configure.ac +++ b/libcpp/configure.ac @@ -211,8 +211,23 @@ esac @@ -8903,7 +8903,7 @@ index 89ac99b04bd..b29b4d6acf1 100644 # Enable Intel CET on Intel CET enabled host if jit is enabled. diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h -index b8e50ae15bb..26474a470ac 100644 +index b8e50ae..26474a4 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -756,6 +756,9 @@ struct cpp_callbacks @@ -8928,7 +8928,7 @@ index b8e50ae15bb..26474a470ac 100644 #define CPP_HASHNODE(HNODE) ((cpp_hashnode *) (HNODE)) diff --git a/libcpp/init.cc b/libcpp/init.cc -index c508f06112a..465dafefe9d 100644 +index c508f06..465dafe 100644 --- a/libcpp/init.cc +++ b/libcpp/init.cc @@ -433,6 +433,8 @@ static const struct builtin_macro builtin_array[] = @@ -8941,7 +8941,7 @@ index c508f06112a..465dafefe9d 100644 update init_builtins() if any more are added. */ B("_Pragma", BT_PRAGMA, true), diff --git a/libcpp/macro.cc b/libcpp/macro.cc -index d4238d4f621..d2e8f9bd411 100644 +index d4238d4..d2e8f9b 100644 --- a/libcpp/macro.cc +++ b/libcpp/macro.cc @@ -677,6 +677,12 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, @@ -8958,7 +8958,7 @@ index d4238d4f621..d2e8f9bd411 100644 if (result == NULL) diff --git a/libdecnumber/configure b/libdecnumber/configure -index fb6db05565a..84bc4ffc767 100755 +index fb6db05..84bc4ff 100755 --- a/libdecnumber/configure +++ b/libdecnumber/configure @@ -626,6 +626,8 @@ ac_subst_vars='LTLIBOBJS @@ -9012,7 +9012,7 @@ index fb6db05565a..84bc4ffc767 100755 PICFLAG= fi diff --git a/libdecnumber/configure.ac b/libdecnumber/configure.ac -index aafd06f8a64..30a51ca410b 100644 +index aafd06f..30a51ca 100644 --- a/libdecnumber/configure.ac +++ b/libdecnumber/configure.ac @@ -100,8 +100,23 @@ AC_C_BIGENDIAN @@ -9042,7 +9042,7 @@ index aafd06f8a64..30a51ca410b 100644 # Enable Intel CET on Intel CET enabled host if jit is enabled. diff --git a/libffi/Makefile.am b/libffi/Makefile.am -index c6d6f849c53..d2ae0c04c7b 100644 +index c6d6f84..d2ae0c0 100644 --- a/libffi/Makefile.am +++ b/libffi/Makefile.am @@ -214,7 +214,12 @@ libffi.map: $(top_srcdir)/libffi.map.in @@ -9060,7 +9060,7 @@ index c6d6f849c53..d2ae0c04c7b 100644 AM_CPPFLAGS = -I. -I$(top_srcdir)/include -Iinclude -I$(top_srcdir)/src diff --git a/libffi/Makefile.in b/libffi/Makefile.in -index 5524a6a571e..34e77a45d1a 100644 +index 5524a6a..34e77a4 100644 --- a/libffi/Makefile.in +++ b/libffi/Makefile.in @@ -597,7 +597,11 @@ AM_CFLAGS = -Wall -g -fexceptions $(CET_FLAGS) $(am__append_2) @@ -9077,7 +9077,7 @@ index 5524a6a571e..34e77a45d1a 100644 AM_CPPFLAGS = -I. -I$(top_srcdir)/include -Iinclude -I$(top_srcdir)/src AM_CCASFLAGS = $(AM_CPPFLAGS) $(CET_FLAGS) diff --git a/libffi/configure b/libffi/configure -index 2bb9f8d83d6..0fae8b5c96d 100755 +index 2bb9f8d..0fae8b5 100755 --- a/libffi/configure +++ b/libffi/configure @@ -667,6 +667,8 @@ MAINT @@ -9301,7 +9301,7 @@ index 2bb9f8d83d6..0fae8b5c96d 100755 as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libffi/configure.ac b/libffi/configure.ac -index 014d89d0423..716f20ae313 100644 +index 014d89d..716f20a 100644 --- a/libffi/configure.ac +++ b/libffi/configure.ac @@ -55,6 +55,7 @@ AC_SUBST(CET_FLAGS) @@ -9313,7 +9313,7 @@ index 014d89d0423..716f20ae313 100644 AC_CHECK_TOOL(READELF, readelf) diff --git a/libffi/doc/version.texi b/libffi/doc/version.texi -index f2b741e87e4..6261b21fec9 100644 +index f2b741e..6261b21 100644 --- a/libffi/doc/version.texi +++ b/libffi/doc/version.texi @@ -1,4 +1,4 @@ @@ -9323,143 +9323,9 @@ index f2b741e87e4..6261b21fec9 100644 +@set UPDATED-MONTH August 2022 @set EDITION 3.4.2 @set VERSION 3.4.2 -diff --git a/libgcc/config.host b/libgcc/config.host -index c94d69d84b7..c0a4cc6fa9b 100644 ---- a/libgcc/config.host -+++ b/libgcc/config.host -@@ -82,7 +82,7 @@ m32c*-*-*) - cpu_type=m32c - tmake_file=t-fdpbit - ;; --aarch64*-*-*) -+aarch64*-*-* | arm64*-*-*) - cpu_type=aarch64 - ;; - alpha*-*-*) -@@ -233,9 +233,11 @@ case ${host} in - ;; - esac - tmake_file="$tmake_file t-slibgcc-darwin" -- # newer toolsets produce warnings when building for unsupported versions. - case ${host} in -- *-*-darwin1[89]* | *-*-darwin2* ) -+ *-*-darwin2*) -+ tmake_file="t-darwin-min-11 $tmake_file" -+ ;; -+ *-*-darwin1[89]*) - tmake_file="t-darwin-min-8 $tmake_file" - ;; - *-*-darwin9* | *-*-darwin1[0-7]*) -@@ -251,7 +253,29 @@ case ${host} in - echo "Warning: libgcc configured to support macOS 10.5" 1>&2 - ;; - esac -- extra_parts="crt3.o libd10-uwfef.a crttms.o crttme.o libemutls_w.a" -+ # We are not using libtool to build the libs here, so we need to replicate -+ # a little of the logic around setting Darwin rpaths. Setting an explicit -+ # yes or no is honoured, otherwise we choose a suitable default. -+ # Sadly, this has to be kept in line with the rules in libtool.m4. -+ # This make fragment will override the setting in t-slibgcc-darwin so it -+ # must appear after it. -+ if test "x$enable_darwin_at_rpath" = "x"; then -+ echo "enable_darwin_at_rpath is unset" 1>&2 -+ case ${host} in -+ *-darwin[45678]*) ;; -+ *-darwin9* | *-darwin1[01234]*) ;; # We might default these on later. -+ *-darwin*) -+ echo "but is needed after macOS 10.11 (setting it on)" 1>&2 -+ enable_darwin_at_rpath=yes -+ ;; -+ esac -+ else -+ echo "enable_darwin_at_rpath is '$enable_darwin_at_rpath'" 1>&2 -+ fi -+ if test "x$enable_darwin_at_rpath" = "xyes"; then -+ tmake_file="$tmake_file t-darwin-rpath " -+ fi -+ extra_parts="crt3.o crttms.o crttme.o libemutls_w.a " - ;; - *-*-dragonfly*) - tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip" -@@ -395,6 +419,15 @@ aarch64*-*-elf | aarch64*-*-rtems*) - tmake_file="${tmake_file} t-dfprules" - md_unwind_header=aarch64/aarch64-unwind.h - ;; -+aarch64*-*-darwin*) -+ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" -+ tmake_file="${tmake_file} ${cpu_type}/t-aarch64" -+ tmake_file="${tmake_file} ${cpu_type}/t-lse " -+ tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp " -+ tmake_file="${tmake_file} t-crtfm t-dfprules" -+ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" -+ md_unwind_header=aarch64/aarch64-unwind.h -+ ;; - aarch64*-*-freebsd*) - extra_parts="$extra_parts crtfastmath.o" - tmake_file="${tmake_file} ${cpu_type}/t-aarch64" -@@ -423,6 +456,7 @@ aarch64*-*-linux*) - tmake_file="${tmake_file} ${cpu_type}/t-lse t-slibgcc-libgcc" - tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp t-crtfm" - tmake_file="${tmake_file} t-dfprules" -+ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" - ;; - aarch64*-*-vxworks7*) - extra_parts="$extra_parts crtfastmath.o" -@@ -691,12 +725,18 @@ hppa*-*-netbsd*) - i[34567]86-*-darwin*) - tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-msabi" - tm_file="$tm_file i386/darwin-lib.h" -- extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" -+ extra_parts="$extra_parts libd10-uwfef.a " -+ extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o" -+ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" -+ tmake_file="${tmake_file} i386/t-heap-trampoline" - ;; - x86_64-*-darwin*) - tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-msabi" - tm_file="$tm_file i386/darwin-lib.h" -- extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" -+ extra_parts="$extra_parts libd10-uwfef.a " -+ extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o" -+ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" -+ tmake_file="${tmake_file} i386/t-heap-trampoline" - ;; - i[34567]86-*-elfiamcu) - tmake_file="$tmake_file i386/t-crtstuff t-softfp-sfdftf i386/32/t-softfp i386/32/t-iamcu i386/t-softfp t-softfp t-dfprules" -@@ -746,6 +786,7 @@ i[34567]86-*-linux*) - tmake_file="${tmake_file} i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" - tm_file="${tm_file} i386/elf-lib.h" - md_unwind_header=i386/linux-unwind.h -+ tmake_file="${tmake_file} i386/t-heap-trampoline" - ;; - i[34567]86-*-kfreebsd*-gnu | i[34567]86-*-kopensolaris*-gnu) - extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" -@@ -763,6 +804,7 @@ x86_64-*-linux*) - tmake_file="${tmake_file} i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" - tm_file="${tm_file} i386/elf-lib.h" - md_unwind_header=i386/linux-unwind.h -+ tmake_file="${tmake_file} i386/t-heap-trampoline" - ;; - x86_64-*-kfreebsd*-gnu) - extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" -@@ -1171,12 +1213,14 @@ powerpc-*-darwin*) - # We build the darwin10 EH shim for Rosetta (running on x86 machines). - tm_file="$tm_file i386/darwin-lib.h" - tmake_file="$tmake_file rs6000/t-ppc64-fp rs6000/t-ibm-ldouble" -+ extra_parts="$extra_parts libd10-uwfef.a " - extra_parts="$extra_parts crt2.o crt3_2.o libef_ppc.a dw_ppc.o" - ;; - powerpc64-*-darwin*) - # We build the darwin10 EH shim for Rosetta (running on x86 machines). - tm_file="$tm_file i386/darwin-lib.h" - tmake_file="$tmake_file rs6000/t-darwin64 rs6000/t-ibm-ldouble" -+ extra_parts="$extra_parts libd10-uwfef.a " - extra_parts="$extra_parts crt2.o crt3_2.o libef_ppc.a dw_ppc.o" - ;; - powerpc*-*-freebsd*) -diff --git a/libgcc/config/aarch64/heap-trampoline.c b/libgcc/config/aarch64/heap-trampoline.c +diff --git b/libgcc/config/aarch64/heap-trampoline.c b/libgcc/config/aarch64/heap-trampoline.c new file mode 100644 -index 00000000000..b2c69aa5892 +index 0000000..b2c69aa --- /dev/null +++ b/libgcc/config/aarch64/heap-trampoline.c @@ -0,0 +1,185 @@ @@ -9649,7 +9515,7 @@ index 00000000000..b2c69aa5892 + +#endif /* !inhibit_libc */ diff --git a/libgcc/config/aarch64/lse.S b/libgcc/config/aarch64/lse.S -index dde3a28e07b..87ee33bc52a 100644 +index dde3a28..87ee33b 100644 --- a/libgcc/config/aarch64/lse.S +++ b/libgcc/config/aarch64/lse.S @@ -58,7 +58,11 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see @@ -9681,8 +9547,8 @@ index dde3a28e07b..87ee33bc52a 100644 + .text + .balign 16 + .private_extern _\name -+ .cfi_startproc +_\name: ++ .cfi_startproc + BTI_C +.endm + @@ -9703,7 +9569,7 @@ index dde3a28e07b..87ee33bc52a 100644 STARTFN NAME(cas) diff --git a/libgcc/config/aarch64/sfp-machine.h b/libgcc/config/aarch64/sfp-machine.h -index 97c38a34c86..b35e2c5e29a 100644 +index 97c38a3..b35e2c5 100644 --- a/libgcc/config/aarch64/sfp-machine.h +++ b/libgcc/config/aarch64/sfp-machine.h @@ -124,6 +124,27 @@ void __sfp_handle_exceptions (int); @@ -9734,9 +9600,9 @@ index 97c38a34c86..b35e2c5e29a 100644 # define _strong_alias(name, aliasname) \ extern __typeof (name) aliasname __attribute__ ((alias (#name))); +#endif -diff --git a/libgcc/config/aarch64/t-darwin b/libgcc/config/aarch64/t-darwin +diff --git b/libgcc/config/aarch64/t-darwin b/libgcc/config/aarch64/t-darwin new file mode 100644 -index 00000000000..f6ecda7b608 +index 0000000..f6ecda7 --- /dev/null +++ b/libgcc/config/aarch64/t-darwin @@ -0,0 +1,7 @@ @@ -9747,9 +9613,9 @@ index 00000000000..f6ecda7b608 +LIB2_SIDITI_CONV_FUNCS = yes + +BUILD_LIBGCCS1 = -diff --git a/libgcc/config/aarch64/t-heap-trampoline b/libgcc/config/aarch64/t-heap-trampoline +diff --git b/libgcc/config/aarch64/t-heap-trampoline b/libgcc/config/aarch64/t-heap-trampoline new file mode 100644 -index 00000000000..6468fb8704f +index 0000000..6468fb8 --- /dev/null +++ b/libgcc/config/aarch64/t-heap-trampoline @@ -0,0 +1,20 @@ @@ -9773,9 +9639,9 @@ index 00000000000..6468fb8704f + +LIB2ADDEH += $(srcdir)/config/aarch64/heap-trampoline.c +LIB2ADDEHSHARED += $(srcdir)/config/aarch64/heap-trampoline.c -diff --git a/libgcc/config/i386/heap-trampoline.c b/libgcc/config/i386/heap-trampoline.c +diff --git b/libgcc/config/i386/heap-trampoline.c b/libgcc/config/i386/heap-trampoline.c new file mode 100644 -index 00000000000..2e8df1cfbd2 +index 0000000..2e8df1c --- /dev/null +++ b/libgcc/config/i386/heap-trampoline.c @@ -0,0 +1,255 @@ @@ -10034,9 +9900,19 @@ index 00000000000..2e8df1cfbd2 +} + +#endif /* !inhibit_libc */ -diff --git a/libgcc/config/i386/t-heap-trampoline b/libgcc/config/i386/t-heap-trampoline +diff --git a/libgcc/config/i386/t-darwin b/libgcc/config/i386/t-darwin +index 4c18da1..84efe35 100644 +--- a/libgcc/config/i386/t-darwin ++++ b/libgcc/config/i386/t-darwin +@@ -5,5 +5,3 @@ LIB2FUNCS_EXCLUDE = _fixtfdi _fixunstfdi _floatditf _floatunditf + # Extra symbols for this port. + SHLIB_MAPFILES += $(srcdir)/config/i386/libgcc-darwin.ver + +-# Build a legacy libgcc_s.1 +-BUILD_LIBGCCS1 = YES +diff --git b/libgcc/config/i386/t-heap-trampoline b/libgcc/config/i386/t-heap-trampoline new file mode 100644 -index 00000000000..5cd11f594ba +index 0000000..5cd11f5 --- /dev/null +++ b/libgcc/config/i386/t-heap-trampoline @@ -0,0 +1,20 @@ @@ -10060,8 +9936,29 @@ index 00000000000..5cd11f594ba + +LIB2ADDEH += $(srcdir)/config/i386/heap-trampoline.c +LIB2ADDEHSHARED += $(srcdir)/config/i386/heap-trampoline.c +diff --git a/libgcc/config/rs6000/t-darwin b/libgcc/config/rs6000/t-darwin +index 183d0df..8b513bd 100644 +--- a/libgcc/config/rs6000/t-darwin ++++ b/libgcc/config/rs6000/t-darwin +@@ -56,6 +56,3 @@ unwind-dw2_s.o: HOST_LIBGCC2_CFLAGS += -maltivec + unwind-dw2.o: HOST_LIBGCC2_CFLAGS += -maltivec + + LIB2ADDEH += $(srcdir)/config/rs6000/darwin-fallback.c +- +-# Build a legacy libgcc_s.1 +-BUILD_LIBGCCS1 = YES +diff --git b/libgcc/config/rs6000/t-darwin-libgccs1 b/libgcc/config/rs6000/t-darwin-libgccs1 +new file mode 100644 +index 0000000..7dc252e +--- /dev/null ++++ b/libgcc/config/rs6000/t-darwin-libgccs1 +@@ -0,0 +1,3 @@ ++ ++# Build a legacy libgcc_s.1 ++BUILD_LIBGCCS1 = YES +\ No newline at end of file diff --git a/libgcc/config/t-darwin b/libgcc/config/t-darwin -index a3bb70c6a0a..0f65b54a230 100644 +index a3bb70c..0f65b54 100644 --- a/libgcc/config/t-darwin +++ b/libgcc/config/t-darwin @@ -51,5 +51,18 @@ LIB2ADDEH = $(srcdir)/unwind-dw2.c \ @@ -10083,25 +9980,25 @@ index a3bb70c6a0a..0f65b54a230 100644 + # Symbols for all the sub-ports. SHLIB_MAPFILES = libgcc-std.ver $(srcdir)/config/libgcc-libsystem.ver -diff --git a/libgcc/config/t-darwin-min-11 b/libgcc/config/t-darwin-min-11 +diff --git b/libgcc/config/t-darwin-min-11 b/libgcc/config/t-darwin-min-11 new file mode 100644 -index 00000000000..4009d41addb +index 0000000..4009d41 --- /dev/null +++ b/libgcc/config/t-darwin-min-11 @@ -0,0 +1,3 @@ +# Support building with -mmacosx-version-min back to macOS 11. +DARWIN_MIN_LIB_VERSION = -mmacosx-version-min=11 +DARWIN_MIN_CRT_VERSION = -mmacosx-version-min=11 -diff --git a/libgcc/config/t-darwin-rpath b/libgcc/config/t-darwin-rpath +diff --git b/libgcc/config/t-darwin-rpath b/libgcc/config/t-darwin-rpath new file mode 100644 -index 00000000000..e73d7f378b0 +index 0000000..e73d7f3 --- /dev/null +++ b/libgcc/config/t-darwin-rpath @@ -0,0 +1,2 @@ +# Use @rpath and add a search path to exes and dylibs that depend on this. +SHLIB_RPATH = @rpath diff --git a/libgcc/config/t-slibgcc-darwin b/libgcc/config/t-slibgcc-darwin -index cb0cbbdb1c5..da4886848e8 100644 +index cb0cbbd..da48868 100644 --- a/libgcc/config/t-slibgcc-darwin +++ b/libgcc/config/t-slibgcc-darwin @@ -1,4 +1,4 @@ @@ -10182,8 +10079,155 @@ index cb0cbbdb1c5..da4886848e8 100644 done $(LIPO) -output libgcc_s.1$(SHLIB_EXT) -create libgcc_s.1$(SHLIB_EXT)_T* rm libgcc_s.$(SHLIB_SOVERSION)$(SHLIB_EXT)_T* +diff --git a/libgcc/config.host b/libgcc/config.host +index c94d69d..e96b913 100644 +--- a/libgcc/config.host ++++ b/libgcc/config.host +@@ -82,7 +82,7 @@ m32c*-*-*) + cpu_type=m32c + tmake_file=t-fdpbit + ;; +-aarch64*-*-*) ++aarch64*-*-* | arm64*-*-*) + cpu_type=aarch64 + ;; + alpha*-*-*) +@@ -233,16 +233,21 @@ case ${host} in + ;; + esac + tmake_file="$tmake_file t-slibgcc-darwin" +- # newer toolsets produce warnings when building for unsupported versions. + case ${host} in +- *-*-darwin1[89]* | *-*-darwin2* ) +- tmake_file="t-darwin-min-8 $tmake_file" ++ x86_64-*-darwin2[0-3]*) ++ tmake_file="t-darwin-min-11 t-darwin-libgccs1 $tmake_file" ++ ;; ++ *-*-darwin2*) ++ tmake_file="t-darwin-min-11 t-darwin-libgccs1 $tmake_file" ++ ;; ++ *-*-darwin1[89]*) ++ tmake_file="t-darwin-min-8 t-darwin-libgccs1 $tmake_file" + ;; + *-*-darwin9* | *-*-darwin1[0-7]*) +- tmake_file="t-darwin-min-5 $tmake_file" ++ tmake_file="t-darwin-min-5 t-darwin-libgccs1 $tmake_file" + ;; + *-*-darwin[4-8]*) +- tmake_file="t-darwin-min-1 $tmake_file" ++ tmake_file="t-darwin-min-1 t-darwin-libgccs1 $tmake_file" + ;; + *) + # Fall back to configuring for the oldest system known to work with +@@ -251,7 +256,29 @@ case ${host} in + echo "Warning: libgcc configured to support macOS 10.5" 1>&2 + ;; + esac +- extra_parts="crt3.o libd10-uwfef.a crttms.o crttme.o libemutls_w.a" ++ # We are not using libtool to build the libs here, so we need to replicate ++ # a little of the logic around setting Darwin rpaths. Setting an explicit ++ # yes or no is honoured, otherwise we choose a suitable default. ++ # Sadly, this has to be kept in line with the rules in libtool.m4. ++ # This make fragment will override the setting in t-slibgcc-darwin so it ++ # must appear after it. ++ if test "x$enable_darwin_at_rpath" = "x"; then ++ echo "enable_darwin_at_rpath is unset" 1>&2 ++ case ${host} in ++ *-darwin[45678]*) ;; ++ *-darwin9* | *-darwin1[01234]*) ;; # We might default these on later. ++ *-darwin*) ++ echo "but is needed after macOS 10.11 (setting it on)" 1>&2 ++ enable_darwin_at_rpath=yes ++ ;; ++ esac ++ else ++ echo "enable_darwin_at_rpath is '$enable_darwin_at_rpath'" 1>&2 ++ fi ++ if test "x$enable_darwin_at_rpath" = "xyes"; then ++ tmake_file="$tmake_file t-darwin-rpath " ++ fi ++ extra_parts="crt3.o crttms.o crttme.o libemutls_w.a " + ;; + *-*-dragonfly*) + tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip" +@@ -395,6 +422,15 @@ aarch64*-*-elf | aarch64*-*-rtems*) + tmake_file="${tmake_file} t-dfprules" + md_unwind_header=aarch64/aarch64-unwind.h + ;; ++aarch64*-*-darwin*) ++ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" ++ tmake_file="${tmake_file} ${cpu_type}/t-aarch64" ++ tmake_file="${tmake_file} ${cpu_type}/t-lse " ++ tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp " ++ tmake_file="${tmake_file} t-crtfm t-dfprules" ++ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" ++ md_unwind_header=aarch64/aarch64-unwind.h ++ ;; + aarch64*-*-freebsd*) + extra_parts="$extra_parts crtfastmath.o" + tmake_file="${tmake_file} ${cpu_type}/t-aarch64" +@@ -423,6 +459,7 @@ aarch64*-*-linux*) + tmake_file="${tmake_file} ${cpu_type}/t-lse t-slibgcc-libgcc" + tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp t-crtfm" + tmake_file="${tmake_file} t-dfprules" ++ tmake_file="${tmake_file} ${cpu_type}/t-heap-trampoline" + ;; + aarch64*-*-vxworks7*) + extra_parts="$extra_parts crtfastmath.o" +@@ -691,12 +728,18 @@ hppa*-*-netbsd*) + i[34567]86-*-darwin*) + tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-msabi" + tm_file="$tm_file i386/darwin-lib.h" +- extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" ++ extra_parts="$extra_parts libd10-uwfef.a " ++ extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o" ++ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" ++ tmake_file="${tmake_file} i386/t-heap-trampoline" + ;; + x86_64-*-darwin*) + tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-msabi" + tm_file="$tm_file i386/darwin-lib.h" +- extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" ++ extra_parts="$extra_parts libd10-uwfef.a " ++ extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o" ++ extra_parts="$extra_parts crtfastmath.o libheapt_w.a" ++ tmake_file="${tmake_file} i386/t-heap-trampoline" + ;; + i[34567]86-*-elfiamcu) + tmake_file="$tmake_file i386/t-crtstuff t-softfp-sfdftf i386/32/t-softfp i386/32/t-iamcu i386/t-softfp t-softfp t-dfprules" +@@ -746,6 +789,7 @@ i[34567]86-*-linux*) + tmake_file="${tmake_file} i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" + tm_file="${tm_file} i386/elf-lib.h" + md_unwind_header=i386/linux-unwind.h ++ tmake_file="${tmake_file} i386/t-heap-trampoline" + ;; + i[34567]86-*-kfreebsd*-gnu | i[34567]86-*-kopensolaris*-gnu) + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" +@@ -763,6 +807,7 @@ x86_64-*-linux*) + tmake_file="${tmake_file} i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" + tm_file="${tm_file} i386/elf-lib.h" + md_unwind_header=i386/linux-unwind.h ++ tmake_file="${tmake_file} i386/t-heap-trampoline" + ;; + x86_64-*-kfreebsd*-gnu) + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" +@@ -1171,12 +1216,14 @@ powerpc-*-darwin*) + # We build the darwin10 EH shim for Rosetta (running on x86 machines). + tm_file="$tm_file i386/darwin-lib.h" + tmake_file="$tmake_file rs6000/t-ppc64-fp rs6000/t-ibm-ldouble" ++ extra_parts="$extra_parts libd10-uwfef.a " + extra_parts="$extra_parts crt2.o crt3_2.o libef_ppc.a dw_ppc.o" + ;; + powerpc64-*-darwin*) + # We build the darwin10 EH shim for Rosetta (running on x86 machines). + tm_file="$tm_file i386/darwin-lib.h" + tmake_file="$tmake_file rs6000/t-darwin64 rs6000/t-ibm-ldouble" ++ extra_parts="$extra_parts libd10-uwfef.a " + extra_parts="$extra_parts crt2.o crt3_2.o libef_ppc.a dw_ppc.o" + ;; + powerpc*-*-freebsd*) diff --git a/libgcc/libgcc-std.ver.in b/libgcc/libgcc-std.ver.in -index c4f87a50e70..ad854bf0ded 100644 +index c4f87a5..ad854bf 100644 --- a/libgcc/libgcc-std.ver.in +++ b/libgcc/libgcc-std.ver.in @@ -1944,3 +1944,9 @@ GCC_7.0.0 { @@ -10197,7 +10241,7 @@ index c4f87a50e70..ad854bf0ded 100644 + __gcc_nested_func_ptr_deleted +} diff --git a/libgcc/libgcc2.h b/libgcc/libgcc2.h -index 3ec9bbd8164..a7a5dff0184 100644 +index 3ec9bbd..a7a5dff 100644 --- a/libgcc/libgcc2.h +++ b/libgcc/libgcc2.h @@ -29,6 +29,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see @@ -10211,7 +10255,7 @@ index 3ec9bbd8164..a7a5dff0184 100644 extern void __clear_cache (void *, void *); extern void __eprintf (const char *, const char *, unsigned int, const char *) diff --git a/libgfortran/Makefile.am b/libgfortran/Makefile.am -index 454ad12e701..3d21373ae82 100644 +index 454ad12..3d21373 100644 --- a/libgfortran/Makefile.am +++ b/libgfortran/Makefile.am @@ -37,6 +37,11 @@ else @@ -10236,7 +10280,7 @@ index 454ad12e701..3d21373ae82 100644 libgfortran_la_DEPENDENCIES = $(version_dep) libgfortran.spec $(LIBQUADLIB_DEP) diff --git a/libgfortran/Makefile.in b/libgfortran/Makefile.in -index 23df0761096..ed0d05f502a 100644 +index 23df076..ed0d05f 100644 --- a/libgfortran/Makefile.in +++ b/libgfortran/Makefile.in @@ -91,8 +91,10 @@ POST_UNINSTALL = : @@ -10349,7 +10393,7 @@ index 23df0761096..ed0d05f502a 100644 $(gfor_helper_src) $(gfor_ieee_src) $(gfor_io_headers) $(gfor_specific_src) diff --git a/libgfortran/configure b/libgfortran/configure -index d7c3a5e27a0..feb75f6e55e 100755 +index d7c3a5e..feb75f6 100755 --- a/libgfortran/configure +++ b/libgfortran/configure @@ -654,6 +654,8 @@ extra_ldflags_libgfortran @@ -10578,7 +10622,7 @@ index d7c3a5e27a0..feb75f6e55e 100755 as_fn_error $? "conditional \"IEEE_SUPPORT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac -index 07b9a48a19f..4ee63cf723e 100644 +index 07b9a48..4ee63cf 100644 --- a/libgfortran/configure.ac +++ b/libgfortran/configure.ac @@ -282,6 +282,7 @@ LT_LIB_M @@ -10603,7 +10647,7 @@ index 07b9a48a19f..4ee63cf723e 100644 ;; esac diff --git a/libgm2/Makefile.am b/libgm2/Makefile.am -index 95df3ed7a30..aa35e747c9a 100644 +index 95df3ed..aa35e74 100644 --- a/libgm2/Makefile.am +++ b/libgm2/Makefile.am @@ -46,6 +46,12 @@ SUBDIRS = libm2min libm2log libm2cor libm2iso libm2pim @@ -10630,7 +10674,7 @@ index 95df3ed7a30..aa35e747c9a 100644 # Subdir rules rely on $(FLAGS_TO_PASS) FLAGS_TO_PASS = $(AM_MAKEFLAGS) diff --git a/libgm2/Makefile.in b/libgm2/Makefile.in -index 2b9592b3490..f97f6d0812d 100644 +index 2b9592b..f97f6d0 100644 --- a/libgm2/Makefile.in +++ b/libgm2/Makefile.in @@ -90,15 +90,15 @@ host_triplet = @host@ @@ -10674,7 +10718,7 @@ index 2b9592b3490..f97f6d0812d 100644 # Subdir rules rely on $(FLAGS_TO_PASS) diff --git a/libgm2/aclocal.m4 b/libgm2/aclocal.m4 -index c352303012d..832065fbb9b 100644 +index c352303..832065f 100644 --- a/libgm2/aclocal.m4 +++ b/libgm2/aclocal.m4 @@ -1187,14 +1187,14 @@ AC_SUBST([am__tar]) @@ -10698,7 +10742,7 @@ index c352303012d..832065fbb9b 100644 +m4_include([../ltversion.m4]) +m4_include([../lt~obsolete.m4]) diff --git a/libgm2/configure b/libgm2/configure -index bf35b403a20..64f4f8034ce 100755 +index bf35b40..64f4f80 100755 --- a/libgm2/configure +++ b/libgm2/configure @@ -649,6 +649,8 @@ GM2_FOR_TARGET @@ -10949,7 +10993,7 @@ index bf35b403a20..64f4f8034ce 100755 as_fn_error $? "conditional \"BUILD_PIMLIB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgm2/configure.ac b/libgm2/configure.ac -index 9386bbfe5ec..305d2dc4319 100644 +index 9386bbf..305d2dc 100644 --- a/libgm2/configure.ac +++ b/libgm2/configure.ac @@ -213,8 +213,12 @@ AC_PATH_PROG(PERL, perl, perl-not-found-in-path-error) @@ -10967,7 +11011,7 @@ index 9386bbfe5ec..305d2dc4319 100644 AC_SUBST(enable_static) diff --git a/libgm2/libm2cor/Makefile.am b/libgm2/libm2cor/Makefile.am -index ae96b4bfe78..a08e6a949e0 100644 +index ae96b4b..a08e6a9 100644 --- a/libgm2/libm2cor/Makefile.am +++ b/libgm2/libm2cor/Makefile.am @@ -123,6 +123,10 @@ libm2cor_la_link_flags = -Wl,-undefined,dynamic_lookup @@ -10982,7 +11026,7 @@ index ae96b4bfe78..a08e6a949e0 100644 BUILT_SOURCES = SYSTEM.def CLEANFILES = SYSTEM.def diff --git a/libgm2/libm2cor/Makefile.in b/libgm2/libm2cor/Makefile.in -index 8daf0eaa054..9e14c90ae7f 100644 +index 8daf0ea..9e14c90 100644 --- a/libgm2/libm2cor/Makefile.in +++ b/libgm2/libm2cor/Makefile.in @@ -105,17 +105,18 @@ POST_UNINSTALL = : @@ -11023,7 +11067,7 @@ index 8daf0eaa054..9e14c90ae7f 100644 @BUILD_CORLIB_TRUE@BUILT_SOURCES = SYSTEM.def @BUILD_CORLIB_TRUE@CLEANFILES = SYSTEM.def diff --git a/libgm2/libm2iso/Makefile.am b/libgm2/libm2iso/Makefile.am -index 90d344f0fa8..e88c4b68e4f 100644 +index 90d344f..e88c4b6 100644 --- a/libgm2/libm2iso/Makefile.am +++ b/libgm2/libm2iso/Makefile.am @@ -197,6 +197,10 @@ libm2iso_la_link_flags = -Wl,-undefined,dynamic_lookup @@ -11038,7 +11082,7 @@ index 90d344f0fa8..e88c4b68e4f 100644 CLEANFILES = SYSTEM.def BUILT_SOURCES = SYSTEM.def diff --git a/libgm2/libm2iso/Makefile.in b/libgm2/libm2iso/Makefile.in -index 8d6443d3946..7be5ad1d601 100644 +index 8d6443d..7be5ad1 100644 --- a/libgm2/libm2iso/Makefile.in +++ b/libgm2/libm2iso/Makefile.in @@ -105,17 +105,18 @@ POST_UNINSTALL = : @@ -11079,7 +11123,7 @@ index 8d6443d3946..7be5ad1d601 100644 @BUILD_ISOLIB_TRUE@CLEANFILES = SYSTEM.def @BUILD_ISOLIB_TRUE@BUILT_SOURCES = SYSTEM.def diff --git a/libgm2/libm2log/Makefile.am b/libgm2/libm2log/Makefile.am -index 27f38406b07..25f5f9b0916 100644 +index 27f3840..25f5f9b 100644 --- a/libgm2/libm2log/Makefile.am +++ b/libgm2/libm2log/Makefile.am @@ -142,6 +142,9 @@ libm2log_la_link_flags = -Wl,-undefined,dynamic_lookup @@ -11093,7 +11137,7 @@ index 27f38406b07..25f5f9b0916 100644 BUILT_SOURCES = ../libm2pim/SYSTEM.def diff --git a/libgm2/libm2log/Makefile.in b/libgm2/libm2log/Makefile.in -index 2188f9ec0c5..f82ddb61842 100644 +index 2188f9e..f82ddb6 100644 --- a/libgm2/libm2log/Makefile.in +++ b/libgm2/libm2log/Makefile.in @@ -105,17 +105,18 @@ POST_UNINSTALL = : @@ -11134,7 +11178,7 @@ index 2188f9ec0c5..f82ddb61842 100644 @BUILD_LOGLIB_TRUE@BUILT_SOURCES = ../libm2pim/SYSTEM.def @BUILD_LOGLIB_TRUE@M2LIBDIR = /m2/m2log/ diff --git a/libgm2/libm2min/Makefile.am b/libgm2/libm2min/Makefile.am -index 1ff160028f6..21411769505 100644 +index 1ff1600..2141176 100644 --- a/libgm2/libm2min/Makefile.am +++ b/libgm2/libm2min/Makefile.am @@ -113,6 +113,9 @@ libm2min_la_link_flags = -Wl,-undefined,dynamic_lookup @@ -11148,7 +11192,7 @@ index 1ff160028f6..21411769505 100644 BUILT_SOURCES = SYSTEM.def CLEANFILES = SYSTEM.def diff --git a/libgm2/libm2min/Makefile.in b/libgm2/libm2min/Makefile.in -index 42cba0e37b9..ed3312deb0f 100644 +index 42cba0e..ed3312d 100644 --- a/libgm2/libm2min/Makefile.in +++ b/libgm2/libm2min/Makefile.in @@ -105,17 +105,18 @@ POST_UNINSTALL = : @@ -11189,7 +11233,7 @@ index 42cba0e37b9..ed3312deb0f 100644 BUILT_SOURCES = SYSTEM.def CLEANFILES = SYSTEM.def diff --git a/libgm2/libm2pim/Makefile.am b/libgm2/libm2pim/Makefile.am -index ac172b93337..61d6c814cc4 100644 +index ac172b9..61d6c81 100644 --- a/libgm2/libm2pim/Makefile.am +++ b/libgm2/libm2pim/Makefile.am @@ -175,6 +175,9 @@ libm2pim_la_link_flags = -Wl,-undefined,dynamic_lookup @@ -11203,7 +11247,7 @@ index ac172b93337..61d6c814cc4 100644 BUILT_SOURCES = SYSTEM.def CLEANFILES = SYSTEM.def diff --git a/libgm2/libm2pim/Makefile.in b/libgm2/libm2pim/Makefile.in -index 4c2d574392b..0f3a6fe940e 100644 +index 4c2d574..0f3a6fe 100644 --- a/libgm2/libm2pim/Makefile.in +++ b/libgm2/libm2pim/Makefile.in @@ -105,17 +105,18 @@ POST_UNINSTALL = : @@ -11244,7 +11288,7 @@ index 4c2d574392b..0f3a6fe940e 100644 @BUILD_PIMLIB_TRUE@BUILT_SOURCES = SYSTEM.def @BUILD_PIMLIB_TRUE@CLEANFILES = SYSTEM.def diff --git a/libgo/configure b/libgo/configure -index a607dbff68e..72d46c3eec3 100755 +index a607dbf..72d46c3 100755 --- a/libgo/configure +++ b/libgo/configure @@ -708,6 +708,8 @@ glibgo_toolexecdir @@ -11301,7 +11345,7 @@ index a607dbff68e..72d46c3eec3 100755 as_fn_error $? "conditional \"USE_LIBFFI\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgo/configure.ac b/libgo/configure.ac -index a59aa091d1d..6f1ac32660b 100644 +index a59aa09..6f1ac32 100644 --- a/libgo/configure.ac +++ b/libgo/configure.ac @@ -53,6 +53,7 @@ AC_LIBTOOL_DLOPEN @@ -11313,7 +11357,7 @@ index a59aa091d1d..6f1ac32660b 100644 CC_FOR_BUILD=${CC_FOR_BUILD:-gcc} AC_SUBST(CC_FOR_BUILD) diff --git a/libgomp/Makefile.am b/libgomp/Makefile.am -index 428f7a9dab5..ceb8c910abd 100644 +index 428f7a9..ceb8c91 100644 --- a/libgomp/Makefile.am +++ b/libgomp/Makefile.am @@ -53,9 +53,14 @@ else @@ -11333,7 +11377,7 @@ index 428f7a9dab5..ceb8c910abd 100644 libgomp_la_DEPENDENCIES = $(libgomp_version_dep) libgomp_la_LINK = $(LINK) $(libgomp_la_LDFLAGS) diff --git a/libgomp/Makefile.in b/libgomp/Makefile.in -index f1afb5ef57f..ef97186e68d 100644 +index f1afb5e..ef97186 100644 --- a/libgomp/Makefile.in +++ b/libgomp/Makefile.in @@ -535,8 +535,11 @@ nodist_toolexeclib_HEADERS = libgomp.spec @@ -11350,7 +11394,7 @@ index f1afb5ef57f..ef97186e68d 100644 libgomp_la_LIBADD = $(DL_LIBS) libgomp_la_DEPENDENCIES = $(libgomp_version_dep) diff --git a/libgomp/configure b/libgomp/configure -index 389500df738..1c219c29f5e 100755 +index 389500d..1c219c2 100755 --- a/libgomp/configure +++ b/libgomp/configure @@ -682,6 +682,8 @@ FC @@ -11566,7 +11610,7 @@ index 389500df738..1c219c29f5e 100755 as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgomp/configure.ac b/libgomp/configure.ac -index dd88f20103a..5deba114027 100644 +index dd88f20..5deba11 100644 --- a/libgomp/configure.ac +++ b/libgomp/configure.ac @@ -149,6 +149,7 @@ AM_PROG_LIBTOOL @@ -11578,7 +11622,7 @@ index dd88f20103a..5deba114027 100644 AM_MAINTAINER_MODE diff --git a/libiberty/configure b/libiberty/configure -index 860f981fa18..b8a19c42110 100755 +index 860f981..b8a19c4 100755 --- a/libiberty/configure +++ b/libiberty/configure @@ -5258,8 +5258,8 @@ case "${enable_shared}" in @@ -11593,7 +11637,7 @@ index 860f981fa18..b8a19c42110 100755 fi diff --git a/libiberty/configure.ac b/libiberty/configure.ac -index 28d996f9cf7..6747a7b5cff 100644 +index 28d996f..6747a7b 100644 --- a/libiberty/configure.ac +++ b/libiberty/configure.ac @@ -233,8 +233,8 @@ case "${enable_shared}" in @@ -11608,7 +11652,7 @@ index 28d996f9cf7..6747a7b5cff 100644 fi diff --git a/libitm/Makefile.am b/libitm/Makefile.am -index 3f31ad30556..a25317b07fe 100644 +index 3f31ad3..a25317b 100644 --- a/libitm/Makefile.am +++ b/libitm/Makefile.am @@ -54,7 +54,12 @@ libitm_version_info = -version-info $(libtool_VERSION) @@ -11626,7 +11670,7 @@ index 3f31ad30556..a25317b07fe 100644 libitm_la_SOURCES = \ aatree.cc alloc.cc alloc_c.cc alloc_cpp.cc barrier.cc beginend.cc \ diff --git a/libitm/Makefile.in b/libitm/Makefile.in -index 7f53ea9b9db..ed28db45057 100644 +index 7f53ea9..ed28db4 100644 --- a/libitm/Makefile.in +++ b/libitm/Makefile.in @@ -481,7 +481,12 @@ libitm_version_info = -version-info $(libtool_VERSION) @@ -11644,7 +11688,7 @@ index 7f53ea9b9db..ed28db45057 100644 barrier.cc beginend.cc clone.cc eh_cpp.cc local.cc query.cc \ retry.cc rwlock.cc useraction.cc util.cc sjlj.S tls.cc \ diff --git a/libitm/config/aarch64/sjlj.S b/libitm/config/aarch64/sjlj.S -index 0342516cdc8..2c27f46dc43 100644 +index 0342516..2c27f46 100644 --- a/libitm/config/aarch64/sjlj.S +++ b/libitm/config/aarch64/sjlj.S @@ -57,10 +57,19 @@ @@ -11717,7 +11761,7 @@ index 0342516cdc8..2c27f46dc43 100644 /* GNU_PROPERTY_AARCH64_* macros from elf.h for use in asm code. */ #define FEATURE_1_AND 0xc0000000 diff --git a/libitm/configure b/libitm/configure -index 6230c04dd24..b941ecf83f9 100755 +index 6230c04..b941ecf 100755 --- a/libitm/configure +++ b/libitm/configure @@ -660,6 +660,8 @@ libtool_VERSION @@ -11941,7 +11985,7 @@ index 6230c04dd24..b941ecf83f9 100755 as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libitm/configure.ac b/libitm/configure.ac -index 050d6b23e18..d0d108e1737 100644 +index 050d6b2..d0d108e 100644 --- a/libitm/configure.ac +++ b/libitm/configure.ac @@ -157,6 +157,7 @@ AM_CONDITIONAL(BUILD_INFO, test $gcc_cv_prog_makeinfo_modern = "yes") @@ -11953,7 +11997,7 @@ index 050d6b23e18..d0d108e1737 100644 AM_MAINTAINER_MODE diff --git a/libitm/configure.tgt b/libitm/configure.tgt -index 0362e61570a..2818a587ebf 100644 +index 0362e61..2818a58 100644 --- a/libitm/configure.tgt +++ b/libitm/configure.tgt @@ -50,7 +50,7 @@ fi @@ -11966,7 +12010,7 @@ index 0362e61570a..2818a587ebf 100644 rs6000 | powerpc*) XCFLAGS="${XCFLAGS} -mhtm" diff --git a/libobjc/configure b/libobjc/configure -index 6da20b8e4ff..ce18c249b66 100755 +index 6da20b8..ce18c24 100755 --- a/libobjc/configure +++ b/libobjc/configure @@ -636,6 +636,9 @@ OBJC_BOEHM_GC_LIBS @@ -12167,7 +12211,7 @@ index 6da20b8e4ff..ce18c249b66 100755 : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/libobjc/configure.ac b/libobjc/configure.ac -index 9bd7d59d597..cb21ebbfcc7 100644 +index 9bd7d59..cb21ebb 100644 --- a/libobjc/configure.ac +++ b/libobjc/configure.ac @@ -148,17 +148,6 @@ m4_rename_force([real_PRECIOUS],[_AC_ARG_VAR_PRECIOUS]) @@ -12221,7 +12265,7 @@ index 9bd7d59d597..cb21ebbfcc7 100644 # Headers # ------- diff --git a/libphobos/configure b/libphobos/configure -index 925c53c5f5e..2e8c06d4d48 100755 +index 925c53c..2e8c06d 100755 --- a/libphobos/configure +++ b/libphobos/configure @@ -707,6 +707,8 @@ get_gcc_base_ver @@ -12437,7 +12481,7 @@ index 925c53c5f5e..2e8c06d4d48 100755 as_fn_error $? "conditional \"DRUNTIME_CPU_AARCH64\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libphobos/configure.ac b/libphobos/configure.ac -index 464f4105430..ba8b5ecd65b 100644 +index 464f410..ba8b5ec 100644 --- a/libphobos/configure.ac +++ b/libphobos/configure.ac @@ -93,6 +93,7 @@ AM_PROG_LIBTOOL @@ -12449,7 +12493,7 @@ index 464f4105430..ba8b5ecd65b 100644 # libtool variables for Phobos shared and position-independent compiles. # diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am -index 8225ba4a028..186948806d5 100644 +index 8225ba4..1869488 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -128,8 +128,11 @@ ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \ @@ -12466,7 +12510,7 @@ index 8225ba4a028..186948806d5 100644 libgdruntime_la_DEPENDENCIES = $(DRTSTUFF) # Also override library link commands: This is not strictly diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in -index 797d6435a7c..cd13090010f 100644 +index 797d643..cd13090 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -810,8 +810,9 @@ ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \ @@ -12481,7 +12525,7 @@ index 797d6435a7c..cd13090010f 100644 libgdruntime_la_LIBADD = $(LIBATOMIC) $(LIBBACKTRACE) libgdruntime_la_DEPENDENCIES = $(DRTSTUFF) diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am -index 6474fca5eb5..f6521ed5860 100644 +index 6474fca..f6521ed 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -44,8 +44,11 @@ toolexeclib_DATA = libgphobos.spec @@ -12498,7 +12542,7 @@ index 6474fca5eb5..f6521ed5860 100644 libgphobos_la_LIBADD = ../libdruntime/libgdruntime_convenience.la else diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in -index a6229587e7b..cc3358b437e 100644 +index a622958..cc3358b 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -529,8 +529,9 @@ toolexeclib_DATA = libgphobos.spec @@ -12513,7 +12557,7 @@ index a6229587e7b..cc3358b437e 100644 @ENABLE_LIBDRUNTIME_ONLY_FALSE@libgphobos_la_LIBADD = \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ ../libdruntime/libgdruntime_convenience.la $(LIBZ) diff --git a/libquadmath/Makefile.am b/libquadmath/Makefile.am -index 35dffb46f6e..f199adf4602 100644 +index 35dffb4..f199adf 100644 --- a/libquadmath/Makefile.am +++ b/libquadmath/Makefile.am @@ -36,8 +36,13 @@ endif @@ -12532,7 +12576,7 @@ index 35dffb46f6e..f199adf4602 100644 nodist_libsubinclude_HEADERS = quadmath.h quadmath_weak.h diff --git a/libquadmath/Makefile.in b/libquadmath/Makefile.in -index 8c011212258..70025758cd5 100644 +index 8c01121..7002575 100644 --- a/libquadmath/Makefile.in +++ b/libquadmath/Makefile.in @@ -355,6 +355,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ @@ -12556,7 +12600,7 @@ index 8c011212258..70025758cd5 100644 @BUILD_LIBQUADMATH_TRUE@libquadmath_la_DEPENDENCIES = $(version_dep) $(libquadmath_la_LIBADD) @BUILD_LIBQUADMATH_TRUE@nodist_libsubinclude_HEADERS = quadmath.h quadmath_weak.h diff --git a/libquadmath/configure b/libquadmath/configure -index 958fb876c5b..c51d4f3776c 100755 +index 958fb87..c51d4f3 100755 --- a/libquadmath/configure +++ b/libquadmath/configure @@ -644,11 +644,14 @@ LIBQUAD_USE_SYMVER_GNU_FALSE @@ -12733,7 +12777,7 @@ index 958fb876c5b..c51d4f3776c 100755 as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libquadmath/configure.ac b/libquadmath/configure.ac -index eec4084a45f..349be2607c6 100644 +index eec4084..349be26 100644 --- a/libquadmath/configure.ac +++ b/libquadmath/configure.ac @@ -59,6 +59,7 @@ AM_PROG_LIBTOOL @@ -12766,7 +12810,7 @@ index eec4084a45f..349be2607c6 100644 LIBQUAD_CHECK_MATH_H_SIGNGAM diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am -index 4f802f723d6..223d3e07816 100644 +index 4f802f7..223d3e0 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -60,7 +60,12 @@ libasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la @@ -12784,7 +12828,7 @@ index 4f802f723d6..223d3e07816 100644 libasan_preinit.o: asan_preinit.o cp $< $@ diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in -index 7833a9a4c3f..e88e5e0b0a7 100644 +index 7833a9a..e88e5e0 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -465,7 +465,12 @@ libasan_la_LIBADD = \ @@ -12802,7 +12846,7 @@ index 7833a9a4c3f..e88e5e0b0a7 100644 # Work around what appears to be a GNU make bug handling MAKEFLAGS # values defined in terms of make variables, as is the case for CC and diff --git a/libsanitizer/configure b/libsanitizer/configure -index e7984f96615..dac83083e30 100755 +index e7984f9..dac8308 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -666,6 +666,8 @@ LSAN_SUPPORTED_FALSE @@ -13027,7 +13071,7 @@ index e7984f96615..dac83083e30 100755 as_fn_error $? "conditional \"TSAN_SUPPORTED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac -index 04cd8910ed6..5906c8d4887 100644 +index 04cd891..5906c8d 100644 --- a/libsanitizer/configure.ac +++ b/libsanitizer/configure.ac @@ -85,6 +85,8 @@ esac @@ -13040,7 +13084,7 @@ index 04cd8910ed6..5906c8d4887 100644 if test "${multilib}" = "yes"; then diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am -index 5a89189f6d8..11b1a9c5c57 100644 +index 5a89189..11b1a9c 100644 --- a/libsanitizer/hwasan/Makefile.am +++ b/libsanitizer/hwasan/Makefile.am @@ -47,7 +47,11 @@ libhwasan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la @@ -13057,7 +13101,7 @@ index 5a89189f6d8..11b1a9c5c57 100644 libhwasan_preinit.o: hwasan_preinit.o cp $< $@ diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in -index 4240aa90147..f9ec8f9c177 100644 +index 4240aa9..f9ec8f9 100644 --- a/libsanitizer/hwasan/Makefile.in +++ b/libsanitizer/hwasan/Makefile.in @@ -445,7 +445,10 @@ libhwasan_la_SOURCES = $(hwasan_files) @@ -13073,7 +13117,7 @@ index 4240aa90147..f9ec8f9c177 100644 # Work around what appears to be a GNU make bug handling MAKEFLAGS # values defined in terms of make variables, as is the case for CC and diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am -index 6ff28ff5eea..7701b0e18cf 100644 +index 6ff28ff..7701b0e 100644 --- a/libsanitizer/lsan/Makefile.am +++ b/libsanitizer/lsan/Makefile.am @@ -41,8 +41,12 @@ if LIBBACKTRACE_SUPPORTED @@ -13092,7 +13136,7 @@ index 6ff28ff5eea..7701b0e18cf 100644 cp $< $@ diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in -index d8fd4ee9557..078edf01fda 100644 +index d8fd4ee..078edf0 100644 --- a/libsanitizer/lsan/Makefile.in +++ b/libsanitizer/lsan/Makefile.in @@ -413,7 +413,12 @@ liblsan_la_LIBADD = \ @@ -13118,7 +13162,7 @@ index d8fd4ee9557..078edf01fda 100644 cp $< $@ diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am -index da80743da9d..01290b0313d 100644 +index da80743..01290b0 100644 --- a/libsanitizer/tsan/Makefile.am +++ b/libsanitizer/tsan/Makefile.am @@ -57,7 +57,11 @@ libtsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la @@ -13135,7 +13179,7 @@ index da80743da9d..01290b0313d 100644 libtsan_preinit.o: tsan_preinit.o cp $< $@ diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in -index 36498832bb8..95011584bcb 100644 +index 3649883..9501158 100644 --- a/libsanitizer/tsan/Makefile.in +++ b/libsanitizer/tsan/Makefile.in @@ -464,7 +464,10 @@ libtsan_la_DEPENDENCIES = \ @@ -13151,7 +13195,7 @@ index 36498832bb8..95011584bcb 100644 # Work around what appears to be a GNU make bug handling MAKEFLAGS # values defined in terms of make variables, as is the case for CC and diff --git a/libsanitizer/ubsan/Makefile.am b/libsanitizer/ubsan/Makefile.am -index d480f26adc0..7769b3437e4 100644 +index d480f26..7769b34 100644 --- a/libsanitizer/ubsan/Makefile.am +++ b/libsanitizer/ubsan/Makefile.am @@ -36,7 +36,12 @@ if LIBBACKTRACE_SUPPORTED @@ -13169,7 +13213,7 @@ index d480f26adc0..7769b3437e4 100644 # Use special rules for files that require RTTI support. ubsan_handlers_cxx.% ubsan_type_hash.% ubsan_type_hash_itanium.% : AM_CXXFLAGS += -frtti diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in -index 92a8e387fd7..7e51480e970 100644 +index 92a8e38..7e51480 100644 --- a/libsanitizer/ubsan/Makefile.in +++ b/libsanitizer/ubsan/Makefile.in @@ -400,7 +400,12 @@ libubsan_la_SOURCES = $(ubsan_files) @@ -13187,7 +13231,7 @@ index 92a8e387fd7..7e51480e970 100644 # Work around what appears to be a GNU make bug handling MAKEFLAGS # values defined in terms of make variables, as is the case for CC and diff --git a/libssp/Makefile.am b/libssp/Makefile.am -index 1636e43b369..f7ed2aa6043 100644 +index 1636e43..f7ed2aa 100644 --- a/libssp/Makefile.am +++ b/libssp/Makefile.am @@ -49,8 +49,12 @@ libssp_la_SOURCES = \ @@ -13205,7 +13249,7 @@ index 1636e43b369..f7ed2aa6043 100644 libssp_nonshared_la_SOURCES = \ ssp-local.c diff --git a/libssp/Makefile.in b/libssp/Makefile.in -index bc8a0dc2b28..1cf86361b96 100644 +index bc8a0dc..1cf8636 100644 --- a/libssp/Makefile.in +++ b/libssp/Makefile.in @@ -376,8 +376,11 @@ libssp_la_SOURCES = \ @@ -13222,7 +13266,7 @@ index bc8a0dc2b28..1cf86361b96 100644 libssp_nonshared_la_SOURCES = \ ssp-local.c diff --git a/libssp/configure b/libssp/configure -index 492915d2ce0..72102be1742 100755 +index 492915d..72102be 100755 --- a/libssp/configure +++ b/libssp/configure @@ -636,6 +636,8 @@ LIBOBJS @@ -13373,7 +13417,7 @@ index 492915d2ce0..72102be1742 100755 : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/libssp/configure.ac b/libssp/configure.ac -index f30f81c54f6..90778e2355d 100644 +index f30f81c..90778e2 100644 --- a/libssp/configure.ac +++ b/libssp/configure.ac @@ -165,6 +165,8 @@ AC_SUBST(enable_static) @@ -13386,7 +13430,7 @@ index f30f81c54f6..90778e2355d 100644 # Also toolexecdir, though it's only used in toolexeclibdir case ${version_specific_libs} in diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure -index d35baaf7c6e..4cac54b99c9 100755 +index d35baaf..4cac54b 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -791,6 +791,8 @@ glibcxx_compiler_pic_flag @@ -13664,7 +13708,7 @@ index d35baaf7c6e..4cac54b99c9 100755 as_fn_error $? "conditional \"OS_IS_DARWIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac -index 0c3c7a2c11c..6dde72ca566 100644 +index 0c3c7a2..6dde72c 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -108,6 +108,7 @@ AM_PROG_LIBTOOL @@ -13676,7 +13720,7 @@ index 0c3c7a2c11c..6dde72ca566 100644 os_is_darwin=no case ${host_os} in diff --git a/libstdc++-v3/src/Makefile.am b/libstdc++-v3/src/Makefile.am -index 5b9af41cdb9..925137c2ccc 100644 +index 5b9af41..925137c 100644 --- a/libstdc++-v3/src/Makefile.am +++ b/libstdc++-v3/src/Makefile.am @@ -152,8 +152,13 @@ libstdc___la_DEPENDENCIES = \ @@ -13695,7 +13739,7 @@ index 5b9af41cdb9..925137c2ccc 100644 libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) $(lt_host_flags) diff --git a/libstdc++-v3/src/Makefile.in b/libstdc++-v3/src/Makefile.in -index f42d957af36..0ce75f30708 100644 +index f42d957..0ce75f3 100644 --- a/libstdc++-v3/src/Makefile.in +++ b/libstdc++-v3/src/Makefile.in @@ -560,8 +560,11 @@ libstdc___la_DEPENDENCIES = \ @@ -13712,7 +13756,7 @@ index f42d957af36..0ce75f30708 100644 libstdc___la_LINK = $(CXXLINK) $(libstdc___la_LDFLAGS) $(lt_host_flags) @GLIBCXX_LDBL_ALT128_COMPAT_FALSE@@GLIBCXX_LDBL_COMPAT_TRUE@LTCXXCOMPILE64 = $(LTCXXCOMPILE) diff --git a/libtool.m4 b/libtool.m4 -index b92e284d9f9..5361f2619cc 100644 +index b92e284..5361f26 100644 --- a/libtool.m4 +++ b/libtool.m4 @@ -1005,7 +1005,7 @@ _LT_EOF @@ -13804,7 +13848,7 @@ index b92e284d9f9..5361f2619cc 100644 # --------------------------------- # Figure out "hidden" library dependencies from verbose diff --git a/libvtv/configure b/libvtv/configure -index e7e490d8b3e..da4fe61d0cf 100755 +index e7e490d..da4fe61 100755 --- a/libvtv/configure +++ b/libvtv/configure @@ -640,6 +640,8 @@ VTV_CYGMIN_FALSE @@ -14028,7 +14072,7 @@ index e7e490d8b3e..da4fe61d0cf 100755 as_fn_error $? "conditional \"VTV_CYGMIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libvtv/configure.ac b/libvtv/configure.ac -index f3b937e4b10..50aaadbb3a3 100644 +index f3b937e..50aaadb 100644 --- a/libvtv/configure.ac +++ b/libvtv/configure.ac @@ -153,6 +153,7 @@ AM_PROG_LIBTOOL @@ -14040,7 +14084,7 @@ index f3b937e4b10..50aaadbb3a3 100644 # For libtool versioning info, format is CURRENT:REVISION:AGE libtool_VERSION=1:0:0 diff --git a/lto-plugin/configure b/lto-plugin/configure -index d522bd24c95..c3b1b5fe0b5 100755 +index d522bd2..c3b1b5f 100755 --- a/lto-plugin/configure +++ b/lto-plugin/configure @@ -634,6 +634,8 @@ LTLIBOBJS @@ -14190,7 +14234,7 @@ index d522bd24c95..c3b1b5fe0b5 100755 : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/lto-plugin/configure.ac b/lto-plugin/configure.ac -index 0a7202782ae..5812bbbfc08 100644 +index 0a72027..5812bbb 100644 --- a/lto-plugin/configure.ac +++ b/lto-plugin/configure.ac @@ -110,6 +110,7 @@ fi @@ -14202,7 +14246,7 @@ index 0a7202782ae..5812bbbfc08 100644 AC_SUBST(target_noncanonical) AC_TYPE_INT64_T diff --git a/zlib/Makefile.in b/zlib/Makefile.in -index 3f5102d1b87..80fe3b69116 100644 +index 3f5102d..80fe3b6 100644 --- a/zlib/Makefile.in +++ b/zlib/Makefile.in @@ -353,6 +353,8 @@ datadir = @datadir@ @@ -14215,7 +14259,7 @@ index 3f5102d1b87..80fe3b69116 100644 host = @host@ host_alias = @host_alias@ diff --git a/zlib/configure b/zlib/configure -index e35ac6e7e17..a7673a840ab 100755 +index e35ac6e..a7673a8 100755 --- a/zlib/configure +++ b/zlib/configure @@ -635,10 +635,14 @@ am__EXEEXT_TRUE @@ -14410,7 +14454,7 @@ index e35ac6e7e17..a7673a840ab 100755 as_fn_error $? "conditional \"TARGET_LIBRARY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/zlib/configure.ac b/zlib/configure.ac -index be1cfe29651..9501cdfea85 100644 +index be1cfe2..9501cdf 100644 --- a/zlib/configure.ac +++ b/zlib/configure.ac @@ -64,6 +64,7 @@ GCC_CET_FLAGS(CET_FLAGS) diff --git a/build/pkgs/gdb/SPKG.rst b/build/pkgs/gdb/SPKG.rst index 0716ac53fad..bd63d67763f 100644 --- a/build/pkgs/gdb/SPKG.rst +++ b/build/pkgs/gdb/SPKG.rst @@ -18,8 +18,3 @@ Upstream Contact ---------------- http://www.gnu.org/software/gdb/ - -Special Update/Build Instructions ---------------------------------- - -Current version needs makeinfo installed to build successfully. diff --git a/build/pkgs/gdb/checksums.ini b/build/pkgs/gdb/checksums.ini deleted file mode 100644 index ce68044ec98..00000000000 --- a/build/pkgs/gdb/checksums.ini +++ /dev/null @@ -1,3 +0,0 @@ -tarball=gdb-VERSION.tar.xz -sha1=ee66294d87a109f88a459d0da5d0bb2da5135f45 -sha256=c3a441a29c7c89720b734e5a9c6289c0a06be7e0c76ef538f7bbcef389347c39 diff --git a/build/pkgs/gdb/dependencies b/build/pkgs/gdb/dependencies deleted file mode 100644 index d6030116f99..00000000000 --- a/build/pkgs/gdb/dependencies +++ /dev/null @@ -1,4 +0,0 @@ -mpfr zlib ncurses | $(PYTHON) xz - ----------- -All lines of this file are ignored except the first. diff --git a/build/pkgs/gdb/package-version.txt b/build/pkgs/gdb/package-version.txt deleted file mode 100644 index 2983cad0495..00000000000 --- a/build/pkgs/gdb/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -8.2 diff --git a/build/pkgs/gdb/spkg-install.in b/build/pkgs/gdb/spkg-install.in deleted file mode 100644 index 1c33ff83595..00000000000 --- a/build/pkgs/gdb/spkg-install.in +++ /dev/null @@ -1,11 +0,0 @@ -cd src - -# We add -ltinfo flag to let gdb compile on some 32bit VM, see #22487. -export LDFLAGS="${LDFLAGS} -L${SAGE_LOCAL}/lib -ltinfo" - -sdh_configure \ - $SAGE_CONFIGURE_MPC \ - $SAGE_CONFIGURE_MPFR \ - $SAGE_CONFIGURE_GMP -sdh_make -sdh_make_install diff --git a/build/pkgs/gdb/type b/build/pkgs/gdb/type index 9839eb20815..134d9bc32d5 100644 --- a/build/pkgs/gdb/type +++ b/build/pkgs/gdb/type @@ -1 +1 @@ -experimental +optional diff --git a/build/pkgs/gmpy2/checksums.ini b/build/pkgs/gmpy2/checksums.ini index 34824279321..17b85732d44 100644 --- a/build/pkgs/gmpy2/checksums.ini +++ b/build/pkgs/gmpy2/checksums.ini @@ -1,4 +1,4 @@ tarball=gmpy2-VERSION.tar.gz sha1=700ef438964acd286d52e973a833cd57ae9a7ad7 sha256=3b8acc939a40411a8ad5541ed178ff866dd1759e667ee26fe34c9291b6b350c3 -upstream_url=https://pypi.io/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz diff --git a/build/pkgs/gnumake_tokenpool/checksums.ini b/build/pkgs/gnumake_tokenpool/checksums.ini index 889f64341f2..92f70df3c3f 100644 --- a/build/pkgs/gnumake_tokenpool/checksums.ini +++ b/build/pkgs/gnumake_tokenpool/checksums.ini @@ -1,4 +1,4 @@ tarball=gnumake_tokenpool-VERSION-py3-none-any.whl sha1=882c694dc3c0a935275a8d2acd9e766399719754 sha256=0c49578df1a76b6ff7724b99053d96f1583bd3e52fe9547587cfb6ffdb0d1fcd -upstream_url=https://pypi.io/packages/py3/g/gnumake_tokenpool/gnumake_tokenpool-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/g/gnumake_tokenpool/gnumake_tokenpool-VERSION-py3-none-any.whl diff --git a/build/pkgs/h11/checksums.ini b/build/pkgs/h11/checksums.ini index 8e1952d648a..9cf02f6021d 100644 --- a/build/pkgs/h11/checksums.ini +++ b/build/pkgs/h11/checksums.ini @@ -1,4 +1,4 @@ tarball=h11-VERSION-py3-none-any.whl sha1=c502d56dc3288212142a398704a5109749331dd8 sha256=e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 -upstream_url=https://pypi.io/packages/py3/h/h11/h11-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/h/h11/h11-VERSION-py3-none-any.whl diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index ce647e83c52..4c75e2006b2 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,4 +1,4 @@ tarball=hatchling-VERSION-py3-none-any.whl -sha1=2212af13a26dbaea72c7a4ecbdb950c05f6e7c00 -sha256=30ec7ee09f6e17b73257eedfd7f5bb5a9b028a6cf6d144d9faad1d826fa203b8 -upstream_url=https://pypi.io/packages/py3/h/hatchling/hatchling-VERSION-py3-none-any.whl +sha1=cae79d374a238c7674687fb4fff0b15cf1af9be4 +sha256=b47948e45d4d973034584dd4cb39c14b6a70227cf287ab7ec0ad7983408a882c +upstream_url=https://files.pythonhosted.org/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 da9594fd66f..ad2191947f7 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.22.5 +1.25.0 diff --git a/build/pkgs/httpcore/checksums.ini b/build/pkgs/httpcore/checksums.ini index 21cb43c4bf6..e489278fdac 100644 --- a/build/pkgs/httpcore/checksums.ini +++ b/build/pkgs/httpcore/checksums.ini @@ -1,4 +1,4 @@ tarball=httpcore-VERSION-py3-none-any.whl sha1=52c8337dcf474d63f5b053e0f65cf79714ae69fe sha256=421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5 -upstream_url=https://pypi.io/packages/py3/h/httpcore/httpcore-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/h/httpcore/httpcore-VERSION-py3-none-any.whl diff --git a/build/pkgs/httpx/checksums.ini b/build/pkgs/httpx/checksums.ini index adc2d9ae791..e1f0fe5fd9a 100644 --- a/build/pkgs/httpx/checksums.ini +++ b/build/pkgs/httpx/checksums.ini @@ -1,4 +1,4 @@ tarball=httpx-VERSION-py3-none-any.whl sha1=01f2a657e43842cb7c8dda30d38860fa741acb7e sha256=71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 -upstream_url=https://pypi.io/packages/py3/h/httpx/httpx-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/h/httpx/httpx-VERSION-py3-none-any.whl diff --git a/build/pkgs/idna/checksums.ini b/build/pkgs/idna/checksums.ini index b3a22afe657..ad7a4b77355 100644 --- a/build/pkgs/idna/checksums.ini +++ b/build/pkgs/idna/checksums.ini @@ -1,4 +1,4 @@ tarball=idna-VERSION-py3-none-any.whl sha1=ccb2491074ec1b6ffda6e6c11c1c668f885ed20a sha256=82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 -upstream_url=https://pypi.io/packages/py3/i/idna/idna-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/i/idna/idna-VERSION-py3-none-any.whl diff --git a/build/pkgs/imagesize/checksums.ini b/build/pkgs/imagesize/checksums.ini index 75413be5c69..8eb70146ee6 100644 --- a/build/pkgs/imagesize/checksums.ini +++ b/build/pkgs/imagesize/checksums.ini @@ -1,4 +1,4 @@ tarball=imagesize-VERSION-py2.py3-none-any.whl sha1=6054e528ed40a9979df9952437a20c3e5773d972 sha256=0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b -upstream_url=https://pypi.io/packages/py2.py3/i/imagesize/imagesize-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/i/imagesize/imagesize-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini index d21661c6dbc..28ed6e985bc 100644 --- a/build/pkgs/importlib_metadata/checksums.ini +++ b/build/pkgs/importlib_metadata/checksums.ini @@ -1,4 +1,4 @@ tarball=importlib_metadata-VERSION-py3-none-any.whl sha1=82c9e2e6cfbb2d5a14558085efa65e75a95bd12f sha256=4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e -upstream_url=https://pypi.io/packages/py3/i/importlib_metadata/importlib_metadata-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/i/importlib_metadata/importlib_metadata-VERSION-py3-none-any.whl diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini index c4025635c2f..da8792a31ec 100644 --- a/build/pkgs/importlib_resources/checksums.ini +++ b/build/pkgs/importlib_resources/checksums.ini @@ -1,4 +1,4 @@ tarball=importlib_resources-VERSION-py3-none-any.whl sha1=5caa4e8a9ee93123a5c3badb6edbc009b5d8494a sha256=e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6 -upstream_url=https://pypi.io/packages/py3/i/importlib_resources/importlib_resources-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/i/importlib_resources/importlib_resources-VERSION-py3-none-any.whl diff --git a/build/pkgs/iniconfig/checksums.ini b/build/pkgs/iniconfig/checksums.ini index 4067da0dcc5..d85d4d0accb 100644 --- a/build/pkgs/iniconfig/checksums.ini +++ b/build/pkgs/iniconfig/checksums.ini @@ -1,4 +1,4 @@ tarball=iniconfig-VERSION-py3-none-any.whl sha1=ec7addd8337eee38e86554d2f0f114f8a1c74b20 sha256=b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 -upstream_url=https://pypi.io/packages/py3/i/iniconfig/iniconfig-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/i/iniconfig/iniconfig-VERSION-py3-none-any.whl diff --git a/build/pkgs/ipykernel/checksums.ini b/build/pkgs/ipykernel/checksums.ini index ccb54046f0a..31a3bf15856 100644 --- a/build/pkgs/ipykernel/checksums.ini +++ b/build/pkgs/ipykernel/checksums.ini @@ -1,4 +1,4 @@ tarball=ipykernel-VERSION.tar.gz sha1=3465b4aa523705e930f295b5c549924e376a02e2 sha256=7d5d594b6690654b4d299edba5e872dc17bb7396a8d0609c97cb7b8a1c605de6 -upstream_url=https://pypi.io/packages/source/i/ipykernel/ipykernel-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/i/ipykernel/ipykernel-VERSION.tar.gz diff --git a/build/pkgs/ipympl/checksums.ini b/build/pkgs/ipympl/checksums.ini index ec23f194f50..45891ffb95d 100644 --- a/build/pkgs/ipympl/checksums.ini +++ b/build/pkgs/ipympl/checksums.ini @@ -1,4 +1,4 @@ tarball=ipympl-VERSION-py2.py3-none-any.whl sha1=9848409026669d9edd83074730d7e2456ae8a187 sha256=d113cd55891bafe9b27ef99b6dd111a87beb6bb2ae550c404292272103be8013 -upstream_url=https://pypi.io/packages/py2.py3/i/ipympl/ipympl-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/i/ipympl/ipympl-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index 0f7cc9c7d51..b22fa04d1e1 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,4 +1,4 @@ tarball=ipython-VERSION.tar.gz sha1=4b5ab06a1b5e1a3285ac91d7dac9a22d18898a31 sha256=ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27 -upstream_url=https://pypi.io/packages/source/i/ipython/ipython-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/i/ipython/ipython-VERSION.tar.gz diff --git a/build/pkgs/ipywidgets/checksums.ini b/build/pkgs/ipywidgets/checksums.ini index ad3dca0b6d8..33f8b3193dc 100644 --- a/build/pkgs/ipywidgets/checksums.ini +++ b/build/pkgs/ipywidgets/checksums.ini @@ -1,4 +1,4 @@ tarball=ipywidgets-VERSION.tar.gz sha1=95f7ec13e8ce75e2da40c1789b4af291946a6d99 sha256=40211efb556adec6fa450ccc2a77d59ca44a060f4f9f136833df59c9f538e6e8 -upstream_url=https://pypi.io/packages/source/i/ipywidgets/ipywidgets-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/i/ipywidgets/ipywidgets-VERSION.tar.gz diff --git a/build/pkgs/isoduration/checksums.ini b/build/pkgs/isoduration/checksums.ini index d3e080d1982..5b4c48333b5 100644 --- a/build/pkgs/isoduration/checksums.ini +++ b/build/pkgs/isoduration/checksums.ini @@ -1,4 +1,4 @@ tarball=isoduration-VERSION-py3-none-any.whl sha1=a113878d368fee6881efcfd12421b12f8e6ae11c sha256=b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042 -upstream_url=https://pypi.io/packages/py3/i/isoduration/isoduration-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/i/isoduration/isoduration-VERSION-py3-none-any.whl diff --git a/build/pkgs/jedi/checksums.ini b/build/pkgs/jedi/checksums.ini index bbfe9535f75..f7e115e80ec 100644 --- a/build/pkgs/jedi/checksums.ini +++ b/build/pkgs/jedi/checksums.ini @@ -1,4 +1,4 @@ tarball=jedi-VERSION.tar.gz sha1=07d1e04c24cecf1b7f38f8905ce81c006f76cc20 sha256=cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd -upstream_url=https://pypi.io/packages/source/j/jedi/jedi-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/j/jedi/jedi-VERSION.tar.gz diff --git a/build/pkgs/jinja2/checksums.ini b/build/pkgs/jinja2/checksums.ini index 8f1cd2ceb6a..b68ec8b0fe0 100644 --- a/build/pkgs/jinja2/checksums.ini +++ b/build/pkgs/jinja2/checksums.ini @@ -1,4 +1,4 @@ tarball=jinja2-VERSION-py3-none-any.whl sha1=57760ed83d9ad15fcdf63ad1ffa14941333ef655 sha256=bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d -upstream_url=https://pypi.io/packages/py3/j/jinja2/jinja2-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jinja2/jinja2-VERSION-py3-none-any.whl diff --git a/build/pkgs/jmol/type b/build/pkgs/jmol/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/jmol/type +++ b/build/pkgs/jmol/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/json5/checksums.ini b/build/pkgs/json5/checksums.ini index d95e497ddc7..af94454a5b0 100644 --- a/build/pkgs/json5/checksums.ini +++ b/build/pkgs/json5/checksums.ini @@ -1,4 +1,4 @@ tarball=json5-VERSION-py2.py3-none-any.whl sha1=54bf91b9c2812e82ccd212cefca5bc5607a538b4 sha256=740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f -upstream_url=https://pypi.io/packages/py2.py3/j/json5/json5-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/j/json5/json5-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/jsonpointer/checksums.ini b/build/pkgs/jsonpointer/checksums.ini index df24503fc54..a0718fcd7d0 100644 --- a/build/pkgs/jsonpointer/checksums.ini +++ b/build/pkgs/jsonpointer/checksums.ini @@ -1,4 +1,4 @@ tarball=jsonpointer-VERSION-py2.py3-none-any.whl sha1=de1b07c2d014f5b8e672cf0fb1225b2232d0b414 sha256=15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a -upstream_url=https://pypi.io/packages/py2.py3/j/jsonpointer/jsonpointer-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/j/jsonpointer/jsonpointer-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/jsonschema/checksums.ini b/build/pkgs/jsonschema/checksums.ini index e709e36e5d3..61c920e4de7 100644 --- a/build/pkgs/jsonschema/checksums.ini +++ b/build/pkgs/jsonschema/checksums.ini @@ -1,4 +1,4 @@ tarball=jsonschema-VERSION-py3-none-any.whl sha1=189537b18c91e60be991a3dba704577d19f8e48d sha256=a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6 -upstream_url=https://pypi.io/packages/py3/j/jsonschema/jsonschema-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jsonschema/jsonschema-VERSION-py3-none-any.whl diff --git a/build/pkgs/jsonschema_specifications/checksums.ini b/build/pkgs/jsonschema_specifications/checksums.ini index df5d6944abd..05fe820f3de 100644 --- a/build/pkgs/jsonschema_specifications/checksums.ini +++ b/build/pkgs/jsonschema_specifications/checksums.ini @@ -1,4 +1,4 @@ tarball=jsonschema_specifications-VERSION-py3-none-any.whl sha1=4132bed31478bc96960099e58ae4c083c514c551 sha256=764a2b9325c225208121948b15f2b2d16fddbe223fdfc096b45c70c1f7f7b8c1 -upstream_url=https://pypi.io/packages/py3/j/jsonschema_specifications/jsonschema_specifications-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jsonschema_specifications/jsonschema_specifications-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index af7a000f71d..39c8aff188b 100644 --- a/build/pkgs/jupyter_client/checksums.ini +++ b/build/pkgs/jupyter_client/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_client-VERSION-py3-none-any.whl sha1=341f822626b55b53f03a21a44d78dc203472406b sha256=5eb9f55eb0650e81de6b7e34308d8b92d04fe4ec41cd8193a913979e33d8e1a5 -upstream_url=https://pypi.io/packages/py3/j/jupyter_client/jupyter_client-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyter_client/jupyter_client-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_core/checksums.ini b/build/pkgs/jupyter_core/checksums.ini index 672c7f93bcc..919c1e88af7 100644 --- a/build/pkgs/jupyter_core/checksums.ini +++ b/build/pkgs/jupyter_core/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_core-VERSION.tar.gz sha1=0fe33e3247e595cdb83e2220f02c566ea9397e6a sha256=0c28db6cbe2c37b5b398e1a1a5b22f84fd64cd10afc1f6c05b02fb09481ba45f -upstream_url=https://pypi.io/packages/source/j/jupyter_core/jupyter_core-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/j/jupyter_core/jupyter_core-VERSION.tar.gz diff --git a/build/pkgs/jupyter_events/checksums.ini b/build/pkgs/jupyter_events/checksums.ini index d7fc20e35ba..aaf19ecdbf5 100644 --- a/build/pkgs/jupyter_events/checksums.ini +++ b/build/pkgs/jupyter_events/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_events-VERSION-py3-none-any.whl sha1=1b3fd8c003ea9e51b0f2d38daa89fded161767f7 sha256=57a2749f87ba387cd1bfd9b22a0875b889237dbf2edc2121ebb22bde47036c17 -upstream_url=https://pypi.io/packages/py3/j/jupyter_events/jupyter_events-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyter_events/jupyter_events-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_jsmol/checksums.ini b/build/pkgs/jupyter_jsmol/checksums.ini index 533f2b689d3..11e1c873b56 100644 --- a/build/pkgs/jupyter_jsmol/checksums.ini +++ b/build/pkgs/jupyter_jsmol/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_jsmol-VERSION-py2.py3-none-any.whl sha1=b00f1ca76aaa906c7c0a43e36baf608183f3d552 sha256=dca3a232f98aa92739de8b7905765d22f325a2ba5d7a3a2f5b2374e88cc80471 -upstream_url=https://pypi.io/packages/py2.py3/j/jupyter_jsmol/jupyter_jsmol-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/j/jupyter_jsmol/jupyter_jsmol-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/jupyter_jsmol/type b/build/pkgs/jupyter_jsmol/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/jupyter_jsmol/type +++ b/build/pkgs/jupyter_jsmol/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/jupyter_lsp/checksums.ini b/build/pkgs/jupyter_lsp/checksums.ini index b925e4da051..7f9c60b089c 100644 --- a/build/pkgs/jupyter_lsp/checksums.ini +++ b/build/pkgs/jupyter_lsp/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_lsp-VERSION-py3-none-any.whl sha1=0f7a63d99c5cf624315583099f00eafc4b996b59 sha256=9e06b8b4f7dd50300b70dd1a78c0c3b0c3d8fa68e0f2d8a5d1fbab62072aca3f -upstream_url=https://pypi.io/packages/py3/j/jupyter_lsp/jupyter_lsp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyter_lsp/jupyter_lsp-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_server/checksums.ini b/build/pkgs/jupyter_server/checksums.ini index 9519eaa2ae6..2f9a79e28d7 100644 --- a/build/pkgs/jupyter_server/checksums.ini +++ b/build/pkgs/jupyter_server/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_server-VERSION-py3-none-any.whl sha1=a54aa7f6f1657a55cae9ecc4a6654b6e3ca5fb73 sha256=8e4b90380b59d7a1e31086c4692231f2a2ea4cb269f5516e60aba72ce8317fc9 -upstream_url=https://pypi.io/packages/py3/j/jupyter_server/jupyter_server-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyter_server/jupyter_server-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_server_terminals/checksums.ini b/build/pkgs/jupyter_server_terminals/checksums.ini index be5ba3efe9b..bb48dce3bae 100644 --- a/build/pkgs/jupyter_server_terminals/checksums.ini +++ b/build/pkgs/jupyter_server_terminals/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_server_terminals-VERSION-py3-none-any.whl sha1=fd1201e9f0064b2a5a05ed7346dfe52546f13b0b sha256=75779164661cec02a8758a5311e18bb8eb70c4e86c6b699403100f1585a12a36 -upstream_url=https://pypi.io/packages/py3/j/jupyter_server_terminals/jupyter_server_terminals-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyter_server_terminals/jupyter_server_terminals-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyter_sphinx/checksums.ini b/build/pkgs/jupyter_sphinx/checksums.ini index fc843987136..a11301f5fe3 100644 --- a/build/pkgs/jupyter_sphinx/checksums.ini +++ b/build/pkgs/jupyter_sphinx/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_sphinx-VERSION.tar.gz sha1=85e6e1665488fac3131da2e3ab9648037c0d1da9 sha256=2e23699a3a1cf5db31b10981da5aa32606ee730f6b73a844d1e76d800756af56 -upstream_url=https://pypi.io/packages/source/j/jupyter_sphinx/jupyter_sphinx-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/j/jupyter_sphinx/jupyter_sphinx-VERSION.tar.gz diff --git a/build/pkgs/jupyterlab/checksums.ini b/build/pkgs/jupyterlab/checksums.ini index eea8e9d4191..0d3e3a75bf0 100644 --- a/build/pkgs/jupyterlab/checksums.ini +++ b/build/pkgs/jupyterlab/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyterlab-VERSION-py3-none-any.whl sha1=4efdd879660e719fd49be6ec169272f32a16593e sha256=67dbec7057c6ad46f08a3667a80bdb890df9453822c93b5ddfd5e8313a718ef9 -upstream_url=https://pypi.io/packages/py3/j/jupyterlab/jupyterlab-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyterlab/jupyterlab-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyterlab_mathjax2/checksums.ini b/build/pkgs/jupyterlab_mathjax2/checksums.ini index 3ff851f18bb..942770adea0 100644 --- a/build/pkgs/jupyterlab_mathjax2/checksums.ini +++ b/build/pkgs/jupyterlab_mathjax2/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyterlab_mathjax2-VERSION-py3-none-any.whl sha1=4e2bb182594a6c4f5d4edfb4f6e33597f09de402 sha256=c65c401ee5638e7cbf1223ba95aceed8b26a2a3e48fd1d585a10dd95b9327a8f -upstream_url=https://pypi.io/packages/py3/j/jupyterlab_mathjax2/jupyterlab_mathjax2-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyterlab_mathjax2/jupyterlab_mathjax2-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyterlab_pygments/checksums.ini b/build/pkgs/jupyterlab_pygments/checksums.ini index 7cbeda65aab..78b7c052f08 100644 --- a/build/pkgs/jupyterlab_pygments/checksums.ini +++ b/build/pkgs/jupyterlab_pygments/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyterlab_pygments-VERSION-py2.py3-none-any.whl sha1=601f547767fa867494ff0764891807904b8ebbd2 sha256=2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f -upstream_url=https://pypi.io/packages/py2.py3/j/jupyterlab_pygments/jupyterlab_pygments-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/j/jupyterlab_pygments/jupyterlab_pygments-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/jupyterlab_server/checksums.ini b/build/pkgs/jupyterlab_server/checksums.ini index 84eeb7a8b8d..18765921193 100644 --- a/build/pkgs/jupyterlab_server/checksums.ini +++ b/build/pkgs/jupyterlab_server/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyterlab_server-VERSION-py3-none-any.whl sha1=1fff8c8bc4c81b006cb83d4524dc8a6f3364e57c sha256=5f077e142bb8dc9b843d960f940c513581bceca3793a0d80f9c67d9522c4e876 -upstream_url=https://pypi.io/packages/py3/j/jupyterlab_server/jupyterlab_server-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyterlab_server/jupyterlab_server-VERSION-py3-none-any.whl diff --git a/build/pkgs/jupyterlab_widgets/checksums.ini b/build/pkgs/jupyterlab_widgets/checksums.ini index f7a3c9bbeb8..175c8761871 100644 --- a/build/pkgs/jupyterlab_widgets/checksums.ini +++ b/build/pkgs/jupyterlab_widgets/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyterlab_widgets-VERSION-py3-none-any.whl sha1=b10775bb3966af627bb44fbda4efb553b24a5b93 sha256=3cf5bdf5b897bf3bccf1c11873aa4afd776d7430200f765e0686bd352487b58d -upstream_url=https://pypi.io/packages/py3/j/jupyterlab_widgets/jupyterlab_widgets-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/j/jupyterlab_widgets/jupyterlab_widgets-VERSION-py3-none-any.whl diff --git a/build/pkgs/libpng/checksums.ini b/build/pkgs/libpng/checksums.ini index 3ff528ccdc6..2a47aa72465 100644 --- a/build/pkgs/libpng/checksums.ini +++ b/build/pkgs/libpng/checksums.ini @@ -1,3 +1,4 @@ tarball=libpng-VERSION.tar.gz -sha1=012c842e6454dc38c6390623ed31ec4005c00584 -sha256=e30bf36cd5882e017c23a5c6a79a9aa1a744dd5841bb45ff7035ec6e3b3096b8 +sha1=eb7ed4d6d4646542fb4b4a0a3f53f7b419cf4965 +sha256=e804e465d4b109b5ad285a8fb71f0dd3f74f0068f91ce3cdfde618180c174925 +upstream_url=https://download.sourceforge.net/libpng/libpng-VERSION.tar.gz diff --git a/build/pkgs/libpng/package-version.txt b/build/pkgs/libpng/package-version.txt index 23b5c1f9732..75c0e87c8c4 100644 --- a/build/pkgs/libpng/package-version.txt +++ b/build/pkgs/libpng/package-version.txt @@ -1 +1 @@ -1.6.29.p1 +1.6.43 diff --git a/build/pkgs/libpng/spkg-install.in b/build/pkgs/libpng/spkg-install.in index 95326748188..628134cb715 100644 --- a/build/pkgs/libpng/spkg-install.in +++ b/build/pkgs/libpng/spkg-install.in @@ -1,13 +1,8 @@ # libpng needs to have no default options set for grep unset GREP_OPTIONS -export CFLAGS="$CFLAGS -fPIC -g" - -# Pick up Sage's zlib: -export CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" - cd src -sdh_configure --enable-shared=yes +sdh_configure --enable-shared --disable-static sdh_make sdh_make_install diff --git a/build/pkgs/lrcalc_python/checksums.ini b/build/pkgs/lrcalc_python/checksums.ini index 99e136d82bb..e86d6c9f9bb 100644 --- a/build/pkgs/lrcalc_python/checksums.ini +++ b/build/pkgs/lrcalc_python/checksums.ini @@ -1,4 +1,4 @@ tarball=lrcalc-VERSION.tar.gz sha1=3e9366d9e8b8beccec70b07d174b8f6683c01574 sha256=e3a0509aeda487b412b391a52e817ca36b5c063a8305e09fd54d53259dd6aaa9 -upstream_url=https://pypi.io/packages/source/l/lrcalc/lrcalc-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/l/lrcalc/lrcalc-VERSION.tar.gz diff --git a/build/pkgs/markupsafe/checksums.ini b/build/pkgs/markupsafe/checksums.ini index 8b2cb02af26..e5cf079ef2d 100644 --- a/build/pkgs/markupsafe/checksums.ini +++ b/build/pkgs/markupsafe/checksums.ini @@ -1,4 +1,4 @@ tarball=MarkupSafe-VERSION.tar.gz sha1=08593f9490b9be070aa2337e7311a392d33944dd sha256=d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b -upstream_url=https://pypi.io/packages/source/m/markupsafe/MarkupSafe-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/markupsafe/MarkupSafe-VERSION.tar.gz diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index 317be590bcf..bf2fc1fad14 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,4 +1,4 @@ tarball=matplotlib-VERSION.tar.gz sha1=b3391b48ab0bf91778064ce5b2226ff2a2658d7c sha256=df8505e1c19d5c2c26aff3497a7cbd3ccfc2e97043d1e4db3e76afa399164b69 -upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz diff --git a/build/pkgs/matplotlib_inline/checksums.ini b/build/pkgs/matplotlib_inline/checksums.ini index 294cf1a777a..455e0e93785 100644 --- a/build/pkgs/matplotlib_inline/checksums.ini +++ b/build/pkgs/matplotlib_inline/checksums.ini @@ -1,4 +1,4 @@ tarball=matplotlib-inline-VERSION.tar.gz sha1=a09347e3f2eaa6f9453c773132bf4bd9d38e2163 sha256=f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304 -upstream_url=https://pypi.io/packages/source/m/matplotlib_inline/matplotlib-inline-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/matplotlib_inline/matplotlib-inline-VERSION.tar.gz diff --git a/build/pkgs/matroid_database/checksums.ini b/build/pkgs/matroid_database/checksums.ini index c5415da0919..5a87bc3e746 100644 --- a/build/pkgs/matroid_database/checksums.ini +++ b/build/pkgs/matroid_database/checksums.ini @@ -1,4 +1,4 @@ tarball=matroid_database-VERSION-py3-none-any.whl sha1=f6539d37c9a03e14a8b100555525d3b54feb602b sha256=85d0304575784ceb4797014fb5a761443d2a0bbf01aafc38ef293d2c64a1b5ce -upstream_url=https://pypi.io/packages/py3/m/matroid_database/matroid_database-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/m/matroid_database/matroid_database-VERSION-py3-none-any.whl diff --git a/src/sage_setup/autogen/interpreters/specs/__init__.py b/build/pkgs/matroid_database/math similarity index 100% rename from src/sage_setup/autogen/interpreters/specs/__init__.py rename to build/pkgs/matroid_database/math diff --git a/build/pkgs/maxima/checksums.ini b/build/pkgs/maxima/checksums.ini index 164be5ccf3f..2b0e2828df0 100644 --- a/build/pkgs/maxima/checksums.ini +++ b/build/pkgs/maxima/checksums.ini @@ -1,4 +1,4 @@ tarball=maxima-VERSION.tar.gz -sha1=1010594e6d6082bbd8efaac1b7756ec1721a4ed5 -sha256=7390f06b48da65c9033e8b2f629b978b90056454a54022db7de70e2225aa8b07 +sha1=fff13f4c010e6daab65fbc8a6895c2bd79925177 +sha256=9104021b24fd53e8c03a983509cb42e937a925e8c0c85c335d7709a14fd40f7a upstream_url=https://sourceforge.net/projects/maxima/files/Maxima-source/VERSION-source/maxima-VERSION.tar.gz/download diff --git a/build/pkgs/maxima/package-version.txt b/build/pkgs/maxima/package-version.txt index 5681375f3be..5ddc1d087c6 100644 --- a/build/pkgs/maxima/package-version.txt +++ b/build/pkgs/maxima/package-version.txt @@ -1 +1 @@ -5.46.0 +5.47.0 diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini index 9aaa169c6dd..fb1ba9b6c90 100644 --- a/build/pkgs/memory_allocator/checksums.ini +++ b/build/pkgs/memory_allocator/checksums.ini @@ -1,4 +1,4 @@ tarball=memory_allocator-VERSION.tar.gz sha1=21661580dd3f41aac0f2090033d8804e6ff495d9 sha256=d609216b03031967e2b45a804b12ff9029578f4ec019fde42cf6aed6ca09efe4 -upstream_url=https://pypi.io/packages/source/m/memory_allocator/memory_allocator-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/memory_allocator/memory_allocator-VERSION.tar.gz diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini index 00cf028db1e..23230d96040 100644 --- a/build/pkgs/meson/checksums.ini +++ b/build/pkgs/meson/checksums.ini @@ -1,4 +1,4 @@ tarball=meson-VERSION-py3-none-any.whl sha1=baf5b9bc9ca97f18c7dc87cfaf0e1dc4d617a4cf sha256=d5223ecca9564d735d36daaba2571abc6c032c8c3a7ffa0674e803ef0c7e0219 -upstream_url=https://pypi.io/packages/py3/m/meson/meson-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/m/meson/meson-VERSION-py3-none-any.whl diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index 7aa199d18fb..d754ae46233 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,4 +1,4 @@ tarball=meson_python-VERSION.tar.gz sha1=71bf382c2f2e76aada2f511a84bd59a99a6b1238 sha256=fddb73eecd49e89c1c41c87937cd89c2d0b65a1c63ba28238681d4bd9484d26f -upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/mistune/checksums.ini b/build/pkgs/mistune/checksums.ini index 93397f7ab48..8c77e16a54e 100644 --- a/build/pkgs/mistune/checksums.ini +++ b/build/pkgs/mistune/checksums.ini @@ -1,4 +1,4 @@ tarball=mistune-VERSION.tar.gz sha1=c15d02c98d04a3e615c3c1932d1b9a3b1759067a sha256=9ee0a66053e2267aba772c71e06891fa8f1af6d4b01d5e84e267b4570d4d9808 -upstream_url=https://pypi.io/packages/source/m/mistune/mistune-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/m/mistune/mistune-VERSION.tar.gz diff --git a/build/pkgs/mpmath/version_requirements.txt b/build/pkgs/mpmath/version_requirements.txt index fca72908c4f..8c3f379fbd4 100644 --- a/build/pkgs/mpmath/version_requirements.txt +++ b/build/pkgs/mpmath/version_requirements.txt @@ -1 +1 @@ -mpmath >=1.1.0 +mpmath >=1.1.0, <1.4 diff --git a/build/pkgs/nauty/checksums.ini b/build/pkgs/nauty/checksums.ini index 9663569c7c4..41ff91d3186 100644 --- a/build/pkgs/nauty/checksums.ini +++ b/build/pkgs/nauty/checksums.ini @@ -1,4 +1,4 @@ tarball=nauty${VERSION}.tar.gz -sha1=672e9fc9dfd07201af37ee65807a9b493331ed92 -sha256=159d2156810a6bb240410cd61eb641add85088d9f15c888cdaa37b8681f929ce +sha1=23504eeae95a1a8a9abfd47029b4ff9da886471f +sha256=c97ab42bf48796a86a598bce3e9269047ca2b32c14fc23e07208a244fe52c4ee upstream_url=https://pallini.di.uniroma1.it/nauty${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}.tar.gz diff --git a/build/pkgs/nauty/package-version.txt b/build/pkgs/nauty/package-version.txt index b8635c72de3..d578041c4b8 100644 --- a/build/pkgs/nauty/package-version.txt +++ b/build/pkgs/nauty/package-version.txt @@ -1 +1 @@ -2.8.8.p0 +2.8.9 diff --git a/build/pkgs/nauty/spkg-install.in b/build/pkgs/nauty/spkg-install.in index a2557c3cd8e..8f83cf3b841 100644 --- a/build/pkgs/nauty/spkg-install.in +++ b/build/pkgs/nauty/spkg-install.in @@ -8,7 +8,7 @@ fi # Nauty doesn't have an install target; passing a prefix to configure is # useless (but harmless) -sdh_configure CC="$CC -fPIC" $NAUTY_CONFIGURE +sdh_configure CC="$CC -fPIC" $NAUTY_CONFIGURE --enable-static --disable-shared sdh_make # No install target so we resort to manual copy @@ -18,7 +18,7 @@ countg countneg cubhamg deledgeg delptg dimacs2g directg dreadnaut dretodot dretog edgetransg genbg genbgL geng gengL genposetg genquarticg genrang genspecialg gentourng gentreeg genktreeg hamheuristic labelg linegraphg listg multig nbrhoodg newedgeg pickg planarg productg ranlabg ransubg shortg showg -subdivideg twohamg underlyingg vcolg watercluster2 NRswitchg" +subdivideg twohamg underlyingg uniqg vcolg watercluster2 NRswitchg" sdh_install $PROGRAMS "$SAGE_LOCAL/bin" sdh_install nauty.h "$SAGE_LOCAL/include/nauty" diff --git a/build/pkgs/nbclient/checksums.ini b/build/pkgs/nbclient/checksums.ini index 6290bb30c93..79d4e17cc53 100644 --- a/build/pkgs/nbclient/checksums.ini +++ b/build/pkgs/nbclient/checksums.ini @@ -1,4 +1,4 @@ tarball=nbclient-VERSION-py3-none-any.whl sha1=fcb4ad9b3ea1bea4d305076c0a7640a483bd11f3 sha256=25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548 -upstream_url=https://pypi.io/packages/py3/n/nbclient/nbclient-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/nbclient/nbclient-VERSION-py3-none-any.whl diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index 56a861567e4..dc985a9eabc 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,4 +1,4 @@ tarball=nbconvert-VERSION-py3-none-any.whl sha1=5317fa68bbd7f66fc3fcc5b0e6b0d6e2df967ba0 sha256=39fe4b8bdd1b0104fdd86fc8a43a9077ba64c720bda4c6132690d917a0a154ee -upstream_url=https://pypi.io/packages/py3/n/nbconvert/nbconvert-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/nbconvert/nbconvert-VERSION-py3-none-any.whl diff --git a/build/pkgs/nbformat/checksums.ini b/build/pkgs/nbformat/checksums.ini index e7a225cadfe..253277f0059 100644 --- a/build/pkgs/nbformat/checksums.ini +++ b/build/pkgs/nbformat/checksums.ini @@ -1,4 +1,4 @@ tarball=nbformat-VERSION-py3-none-any.whl sha1=e38af74817e9d81101583363d9ffe349f0038eb9 sha256=1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9 -upstream_url=https://pypi.io/packages/py3/n/nbformat/nbformat-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/nbformat/nbformat-VERSION-py3-none-any.whl diff --git a/build/pkgs/nest_asyncio/checksums.ini b/build/pkgs/nest_asyncio/checksums.ini index 48626a85ae1..8c842535040 100644 --- a/build/pkgs/nest_asyncio/checksums.ini +++ b/build/pkgs/nest_asyncio/checksums.ini @@ -1,4 +1,4 @@ tarball=nest_asyncio-VERSION-py3-none-any.whl sha1=675b145491553e6a725884081244860a635552cd sha256=87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c -upstream_url=https://pypi.io/packages/py3/n/nest_asyncio/nest_asyncio-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/nest_asyncio/nest_asyncio-VERSION-py3-none-any.whl diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index 7e02c083c6d..e550b84168b 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,4 +1,4 @@ tarball=networkx-VERSION.tar.gz sha1=b12cf95ed8bc3fe568e3c8e023473a3767c43f8d sha256=9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6 -upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/n/networkx/networkx-VERSION.tar.gz diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index 2c1fd65675e..597fdff1881 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION-py3-none-any.whl sha1=90ec65091058ac541a55cc2417de83c1bcb24985 sha256=197d8e0595acabf4005851c8716e952a81b405f7aefb648067a761fbde267ce7 -upstream_url=https://pypi.io/packages/py3/n/notebook/notebook-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/notebook/notebook-VERSION-py3-none-any.whl diff --git a/build/pkgs/notebook_shim/checksums.ini b/build/pkgs/notebook_shim/checksums.ini index 001afb43e00..d01ed7810ee 100644 --- a/build/pkgs/notebook_shim/checksums.ini +++ b/build/pkgs/notebook_shim/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook_shim-VERSION-py3-none-any.whl sha1=9bb3dce360ce69aec99f873d8e80c1e9fdf92fde sha256=a83496a43341c1674b093bfcebf0fe8e74cbe7eda5fd2bbc56f8e39e1486c0c7 -upstream_url=https://pypi.io/packages/py3/n/notebook_shim/notebook_shim-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/n/notebook_shim/notebook_shim-VERSION-py3-none-any.whl diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index fde72f78b6d..6814f0f5c10 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,4 @@ tarball=numpy-VERSION.tar.gz sha1=915414f1efabd7c183583154cf1a709bd2745828 sha256=697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4 -upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/n/numpy/numpy-VERSION.tar.gz diff --git a/build/pkgs/openblas/checksums.ini b/build/pkgs/openblas/checksums.ini index e05d9117e1f..7d8c96ad5a0 100644 --- a/build/pkgs/openblas/checksums.ini +++ b/build/pkgs/openblas/checksums.ini @@ -1,4 +1,4 @@ tarball=openblas-VERSION.tar.gz -sha1=af6dce0e486e684be842053fcbd98eaf97e39749 -sha256=4e6e4f5cb14c209262e33e6816d70221a2fe49eb69eaf0a06f065598ac602c68 +sha1=f75172274a7ce69901605b8c759e2e0891c9b7f4 +sha256=f1003466ad074e9b0c8d421a204121100b0751c96fc6fcf3d1456bd12f8a00a1 upstream_url=https://github.com/xianyi/OpenBLAS/archive/vVERSION.tar.gz diff --git a/build/pkgs/openblas/package-version.txt b/build/pkgs/openblas/package-version.txt index e23fb32dfcb..9578e9400c1 100644 --- a/build/pkgs/openblas/package-version.txt +++ b/build/pkgs/openblas/package-version.txt @@ -1 +1 @@ -0.3.26 +0.3.28 diff --git a/build/pkgs/osqp_python/checksums.ini b/build/pkgs/osqp_python/checksums.ini index d8e1ae4b81b..9fd80df4d5a 100644 --- a/build/pkgs/osqp_python/checksums.ini +++ b/build/pkgs/osqp_python/checksums.ini @@ -1,4 +1,4 @@ tarball=osqp-VERSION.tar.gz sha1=3358e48aa6d81496665a8b0ee157465ce6cd329a sha256=03e460e683ec2ce0f839353ddfa3c4c8ffa509ab8cf6a2b2afbb586fa453e180 -upstream_url=https://pypi.io/packages/source/o/osqp/osqp-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/o/osqp/osqp-VERSION.tar.gz diff --git a/build/pkgs/overrides/checksums.ini b/build/pkgs/overrides/checksums.ini index 2812a196d90..fde4eba9a05 100644 --- a/build/pkgs/overrides/checksums.ini +++ b/build/pkgs/overrides/checksums.ini @@ -1,4 +1,4 @@ tarball=overrides-VERSION-py3-none-any.whl sha1=740e9e607a9e4f78dea7a1b82bcb27f285bc5f48 sha256=3ad24583f86d6d7a49049695efe9933e67ba62f0c7625d53c59fa832ce4b8b7d -upstream_url=https://pypi.io/packages/py3/o/overrides/overrides-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/o/overrides/overrides-VERSION-py3-none-any.whl diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index 4b1b2974bde..eae534e3487 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -1,4 +1,4 @@ tarball=packaging-VERSION-py3-none-any.whl -sha1=21573cef174a05ac2794b34f3841d6f9ea9fa507 -sha256=2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 -upstream_url=https://pypi.io/packages/py3/p/packaging/packaging-VERSION-py3-none-any.whl +sha1=a050029d1e0c1b95b3ddcd566be4ad352cd42666 +sha256=5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 +upstream_url=https://files.pythonhosted.org/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 d9133a54b63..0dad123924d 100644 --- a/build/pkgs/packaging/package-version.txt +++ b/build/pkgs/packaging/package-version.txt @@ -1 +1 @@ -24.0 +24.1 diff --git a/build/pkgs/pandocfilters/checksums.ini b/build/pkgs/pandocfilters/checksums.ini index d85750a17f7..5e6e659ef46 100644 --- a/build/pkgs/pandocfilters/checksums.ini +++ b/build/pkgs/pandocfilters/checksums.ini @@ -1,4 +1,4 @@ tarball=pandocfilters-VERSION.tar.gz sha1=bdee4f81063c02168b421640f3e18917011153df sha256=0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38 -upstream_url=https://pypi.io/packages/source/p/pandocfilters/pandocfilters-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pandocfilters/pandocfilters-VERSION.tar.gz diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index 12c05141bf1..d56c437a2ee 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,4 +1,4 @@ tarball=pari-VERSION.tar.gz -sha1=ae962671b5bf86849d2021113dfb5b2f59331a10 -sha256=c3545bfee0c6dfb40b77fb4bbabaf999d82e60069b9f6d28bcb6cf004c8c5c0f -upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz +sha1=377593dfe72df13578ea0a517fcb0f81cc9758d4 +sha256=0efdda7515d9d954f63324c34b34c560e60f73a81c3924a71260a2cc91d5f981 +upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/OLD/${VERSION_MAJOR}.${VERSION_MINOR}/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index 86fbeafcc20..7a561821071 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.15.4 +2.15.5 diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 5ece8b4891e..207487119a6 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,7 +1,8 @@ SAGE_SPKG_CONFIGURE([pari], [ dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV m4_pushdef([SAGE_PARI_MINVER],["134916"])dnl this version and higher allowed - m4_pushdef([SAGE_PARI_MAXVER],["999999"])dnl this version and higher not allowed + dnl Do not allow Pari 2.17 or later, see #38769: + m4_pushdef([SAGE_PARI_MAXVER],["135424"])dnl this version and higher not allowed SAGE_SPKG_DEPCHECK([gmp readline], [ AC_PATH_PROG([GP], [gp]) if test x$GP = x; then dnl GP test diff --git a/build/pkgs/pari_jupyter/checksums.ini b/build/pkgs/pari_jupyter/checksums.ini index bdd7d5278be..c1fe2ec687f 100644 --- a/build/pkgs/pari_jupyter/checksums.ini +++ b/build/pkgs/pari_jupyter/checksums.ini @@ -1,4 +1,4 @@ tarball=pari-jupyter-VERSION.tar.gz sha1=b410ee0352cd58f5f140246540b71b5ff83ddf73 sha256=7cd9291d05b92b8303c6ae8cf25622e5ecbab1ac2bcf13911f900ea987471b9d -upstream_url=https://pypi.io/packages/source/p/pari_jupyter/pari-jupyter-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pari_jupyter/pari-jupyter-VERSION.tar.gz diff --git a/build/pkgs/parso/checksums.ini b/build/pkgs/parso/checksums.ini index d15402d82de..a45225c3a6d 100644 --- a/build/pkgs/parso/checksums.ini +++ b/build/pkgs/parso/checksums.ini @@ -1,4 +1,4 @@ tarball=parso-VERSION-py2.py3-none-any.whl sha1=091d37e09a601d854e18f4d42aeb478392bb7e63 sha256=a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18 -upstream_url=https://pypi.io/packages/py2.py3/p/parso/parso-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/p/parso/parso-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/pathspec/checksums.ini b/build/pkgs/pathspec/checksums.ini index 62d6d08775e..a3af9942abc 100644 --- a/build/pkgs/pathspec/checksums.ini +++ b/build/pkgs/pathspec/checksums.ini @@ -1,4 +1,4 @@ tarball=pathspec-VERSION-py3-none-any.whl sha1=e31b7b2b1a59ab192eb2e92ac283211a11039769 sha256=a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 -upstream_url=https://pypi.io/packages/py3/p/pathspec/pathspec-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pathspec/pathspec-VERSION-py3-none-any.whl diff --git a/build/pkgs/pexpect/checksums.ini b/build/pkgs/pexpect/checksums.ini index 413053e79a1..dadcb1e66e1 100644 --- a/build/pkgs/pexpect/checksums.ini +++ b/build/pkgs/pexpect/checksums.ini @@ -1,4 +1,4 @@ tarball=pexpect-VERSION.tar.gz sha1=5bff9230c419eecbf701059f104e74a3f3a1b208 sha256=ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f -upstream_url=https://pypi.io/packages/source/p/pexpect/pexpect-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pexpect/pexpect-VERSION.tar.gz diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index 7e64940e75b..5467334d4b7 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,4 @@ tarball=Pillow-VERSION.tar.gz sha1=be2dc6aeee145894f3ccbc2358a37f7849e710aa sha256=e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38 -upstream_url=https://pypi.io/packages/source/p/pillow/Pillow-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pillow/Pillow-VERSION.tar.gz diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 62ecf000278..43918d193ee 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,4 +1,4 @@ tarball=pip-VERSION-py3-none-any.whl -sha1=e44313ae1e6af3c2bd3b60ab2fa8c34308d00555 -sha256=ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc -upstream_url=https://pypi.io/packages/py3/p/pip/pip-VERSION-py3-none-any.whl +sha1=044a04440eef697c8ec9e03544117345c57aa683 +sha256=2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 +upstream_url=https://files.pythonhosted.org/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 d9133a54b63..9dc0ade5023 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -24.0 +24.2 diff --git a/build/pkgs/pkgconfig/checksums.ini b/build/pkgs/pkgconfig/checksums.ini index 4ecbe8c0096..dbf6ebdd342 100644 --- a/build/pkgs/pkgconfig/checksums.ini +++ b/build/pkgs/pkgconfig/checksums.ini @@ -1,4 +1,4 @@ tarball=pkgconfig-VERSION-py3-none-any.whl sha1=bca14b2806a8e8afb0bd04f8e3675550b286dda8 sha256=d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209 -upstream_url=https://pypi.io/packages/py3/p/pkgconfig/pkgconfig-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pkgconfig/pkgconfig-VERSION-py3-none-any.whl diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini index 201a01de014..8263d7baf15 100644 --- a/build/pkgs/platformdirs/checksums.ini +++ b/build/pkgs/platformdirs/checksums.ini @@ -1,4 +1,4 @@ tarball=platformdirs-VERSION-py3-none-any.whl sha1=81890edbdf2f709f83001cb5bcfd71c3978225ce sha256=2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee -upstream_url=https://pypi.io/packages/py3/p/platformdirs/platformdirs-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/platformdirs/platformdirs-VERSION-py3-none-any.whl diff --git a/build/pkgs/pluggy/checksums.ini b/build/pkgs/pluggy/checksums.ini index 3b6e7041f14..27359819ce2 100644 --- a/build/pkgs/pluggy/checksums.ini +++ b/build/pkgs/pluggy/checksums.ini @@ -1,4 +1,4 @@ tarball=pluggy-VERSION-py3-none-any.whl sha1=ccb7a74c114522f26e2c3b1884468343f54098d3 sha256=44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 -upstream_url=https://pypi.io/packages/py3/p/pluggy/pluggy-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pluggy/pluggy-VERSION-py3-none-any.whl diff --git a/build/pkgs/ply/checksums.ini b/build/pkgs/ply/checksums.ini index f1003c4c4a0..ffce85083d4 100644 --- a/build/pkgs/ply/checksums.ini +++ b/build/pkgs/ply/checksums.ini @@ -1,4 +1,4 @@ tarball=ply-VERSION.tar.gz sha1=10a555a32095991fbc7f7ed10c677a14e21fad1d sha256=00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3 -upstream_url=https://pypi.io/packages/source/p/ply/ply-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/ply/ply-VERSION.tar.gz diff --git a/build/pkgs/polymake/SPKG.rst b/build/pkgs/polymake/SPKG.rst index 844b603a0e1..f1e6522d65c 100644 --- a/build/pkgs/polymake/SPKG.rst +++ b/build/pkgs/polymake/SPKG.rst @@ -50,14 +50,19 @@ you will need the ``local::lib`` Perl module installed:: cpan -i XML::Writer XML::LibXML XML::LibXSLT File::Slurp Term::ReadLine::Gnu JSON SVG MongoDB -Several Sage packages should be installed before installing the ``polymake`` -package to give a more featureful Polymake installation:: +Before installing the ``polymake`` package, refer to the SPKG pages for the following packages to ensure a more featureful Polymake installation: - sage -i 4ti2 latte_int topcom qhull +- [4ti2](https://doc.sagemath.org/html/en/reference/spkg/4ti2.html) +- [latte_int](https://doc.sagemath.org/html/en/reference/spkg/latte_int.html) +- [topcom](https://doc.sagemath.org/html/en/reference/spkg/topcom.html) +- [qhull](https://doc.sagemath.org/html/en/reference/spkg/qhull.html) -Software that would need to be installed manually (no Sage package -available) for a more featureful Polymake installation: ``azove``, ``porta``, -``vinci``, ``SplitsTree4``. +For additional software that may enhance your Polymake installation (but for which no Sage package is available), you can manually install the following: + +- ``azove`` +- ``porta`` +- ``vinci`` +- ``SplitsTree4`` Information on missing Polymake prerequisites after installing polymake:: @@ -65,10 +70,8 @@ Information on missing Polymake prerequisites after installing polymake:: (sage-sh) $ polymake polytope> show_unconfigured; -In order to use Polymake from Sage, you will also need the ``jupymake`` -package:: +In order to use Polymake from Sage, please refer to the [Jupymake SPKG page](https://doc.sagemath.org/html/en/reference/spkg/jupymake.html) for installation instructions. - sage -i jupymake Debugging polymake install problems diff --git a/build/pkgs/pplpy/checksums.ini b/build/pkgs/pplpy/checksums.ini index 76268f169e2..b864f9fcd26 100644 --- a/build/pkgs/pplpy/checksums.ini +++ b/build/pkgs/pplpy/checksums.ini @@ -1,4 +1,4 @@ tarball=pplpy-VERSION.tar.gz sha1=dc9e8a7a867ee1c066bdbecf22b6a59dc2052711 sha256=db7a3b571d6ef053f75137975e947c3a1c1e45a30bab90eaf215b4e5cc15797e -upstream_url=https://pypi.io/packages/source/p/pplpy/pplpy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pplpy/pplpy-VERSION.tar.gz diff --git a/build/pkgs/primecount/checksums.ini b/build/pkgs/primecount/checksums.ini index c3f0b9ce77d..4e9965b9965 100644 --- a/build/pkgs/primecount/checksums.ini +++ b/build/pkgs/primecount/checksums.ini @@ -1,4 +1,4 @@ tarball=primecount-VERSION.tar.gz -sha1=3854ef6c7f454086f31aa80d68f628c5b685d702 -sha256=e9a1fa2c41b9a7b84f2bead21b53cc9f7e2a5a0a34ddd818431a4e789aa44230 +sha1=dac5db8fb6aadd8b96fcb190073becd420a2cc31 +sha256=d867ac18cc52c0f7014682169988a76f39e4cd56f8ce78fb56e064499b1d66bb upstream_url=https://github.com/kimwalisch/primecount/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primecount/package-version.txt b/build/pkgs/primecount/package-version.txt index 38abeb202c0..9ad4d4f295e 100644 --- a/build/pkgs/primecount/package-version.txt +++ b/build/pkgs/primecount/package-version.txt @@ -1 +1 @@ -7.6 +7.14 diff --git a/build/pkgs/primecountpy/checksums.ini b/build/pkgs/primecountpy/checksums.ini index 57cae477764..dd8445635a3 100644 --- a/build/pkgs/primecountpy/checksums.ini +++ b/build/pkgs/primecountpy/checksums.ini @@ -1,4 +1,4 @@ tarball=primecountpy-VERSION.tar.gz sha1=3526784adad04d67a15f05fb1367d12ec50a59dc sha256=78fe7cc32115f0669a45d7c90faaf39f7ce3939e39e2e7e5f14c17fe4bff0676 -upstream_url=https://pypi.io/packages/source/p/primecountpy/primecountpy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/primecountpy/primecountpy-VERSION.tar.gz diff --git a/build/pkgs/primesieve/checksums.ini b/build/pkgs/primesieve/checksums.ini index faae4fa7982..5bfc159aae0 100644 --- a/build/pkgs/primesieve/checksums.ini +++ b/build/pkgs/primesieve/checksums.ini @@ -1,4 +1,4 @@ tarball=primesieve-VERSION.tar.gz -sha1=cb0a7c49b37b51980fc610d3041b9591c67a460c -sha256=b29a7ec855764ce7474d00be03e1d83209bd097faa3778382dfb73a06866097e +sha1=19941abdd52bc44a8714ead8da4af0a7ddd92ec5 +sha256=eb7081adebe8030e93b3675c74ac603438d10a36792246b274c79f11d8a987ce upstream_url=https://github.com/kimwalisch/primesieve/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primesieve/package-version.txt b/build/pkgs/primesieve/package-version.txt index 2dbc24b32d3..800fd35ee86 100644 --- a/build/pkgs/primesieve/package-version.txt +++ b/build/pkgs/primesieve/package-version.txt @@ -1 +1 @@ -11.0 +12.4 diff --git a/build/pkgs/prometheus_client/checksums.ini b/build/pkgs/prometheus_client/checksums.ini index 9f4fa1845da..7e0ea2a4f0c 100644 --- a/build/pkgs/prometheus_client/checksums.ini +++ b/build/pkgs/prometheus_client/checksums.ini @@ -1,4 +1,4 @@ tarball=prometheus_client-VERSION.tar.gz sha1=dabd66e652ea8275b4980e337cefcea68cc0b560 sha256=5459c427624961076277fdc6dc50540e2bacb98eebde99886e59ec55ed92093a -upstream_url=https://pypi.io/packages/source/p/prometheus-client/prometheus_client-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/prometheus-client/prometheus_client-VERSION.tar.gz diff --git a/build/pkgs/prompt_toolkit/checksums.ini b/build/pkgs/prompt_toolkit/checksums.ini index 3f4bd25570c..e63fb8d4a10 100644 --- a/build/pkgs/prompt_toolkit/checksums.ini +++ b/build/pkgs/prompt_toolkit/checksums.ini @@ -1,4 +1,4 @@ tarball=prompt_toolkit-VERSION.tar.gz sha1=b5ada8cb45c11f9184c990bd33a98d3d690e6edc sha256=3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d -upstream_url=https://pypi.io/packages/source/p/prompt_toolkit/prompt_toolkit-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/prompt_toolkit/prompt_toolkit-VERSION.tar.gz diff --git a/build/pkgs/psutil/checksums.ini b/build/pkgs/psutil/checksums.ini index 417c2210384..53c177f96f5 100644 --- a/build/pkgs/psutil/checksums.ini +++ b/build/pkgs/psutil/checksums.ini @@ -1,4 +1,4 @@ tarball=psutil-VERSION.tar.gz sha1=24c493ef33d4df44e76a1801e480b4185bd911c5 sha256=e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a -upstream_url=https://pypi.io/packages/source/p/psutil/psutil-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/psutil/psutil-VERSION.tar.gz diff --git a/build/pkgs/ptyprocess/checksums.ini b/build/pkgs/ptyprocess/checksums.ini index 36a12e0f438..3382e090c02 100644 --- a/build/pkgs/ptyprocess/checksums.ini +++ b/build/pkgs/ptyprocess/checksums.ini @@ -1,4 +1,4 @@ tarball=ptyprocess-VERSION.tar.gz sha1=2d8830d1025c8e33149c7723c2f283122f9488c1 sha256=5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220 -upstream_url=https://pypi.io/packages/source/p/ptyprocess/ptyprocess-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/ptyprocess/ptyprocess-VERSION.tar.gz diff --git a/build/pkgs/pure_eval/checksums.ini b/build/pkgs/pure_eval/checksums.ini index 1cdf6f8d2a4..b753dbf3804 100644 --- a/build/pkgs/pure_eval/checksums.ini +++ b/build/pkgs/pure_eval/checksums.ini @@ -1,4 +1,4 @@ tarball=pure_eval-VERSION-py3-none-any.whl sha1=dbd5eaa9eb5a4910cff5ccd42b570f866f581da4 sha256=01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350 -upstream_url=https://pypi.io/packages/py3/p/pure_eval/pure_eval-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pure_eval/pure_eval-VERSION-py3-none-any.whl diff --git a/build/pkgs/py/checksums.ini b/build/pkgs/py/checksums.ini index a10022af08f..48e330dae0a 100644 --- a/build/pkgs/py/checksums.ini +++ b/build/pkgs/py/checksums.ini @@ -1,4 +1,4 @@ tarball=py-VERSION-py2.py3-none-any.whl sha1=44002baec8d2184d218bd2fa6049967cd9b4dbb5 sha256=607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 -upstream_url=https://pypi.io/packages/py2.py3/p/py/py-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/p/py/py-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/pybind11/checksums.ini b/build/pkgs/pybind11/checksums.ini index c3112e54327..66a54f106b5 100644 --- a/build/pkgs/pybind11/checksums.ini +++ b/build/pkgs/pybind11/checksums.ini @@ -1,4 +1,4 @@ tarball=pybind11-VERSION.tar.gz sha1=3c75333a9056f0be18eb612803cd46a2bb0c87fc sha256=00cd59116a6e8155aecd9174f37ba299d1d397ed4a6b86ac1dfe01b3e40f2cc4 -upstream_url=https://pypi.io/packages/source/p/pybind11/pybind11-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pybind11/pybind11-VERSION.tar.gz diff --git a/build/pkgs/pycparser/checksums.ini b/build/pkgs/pycparser/checksums.ini index 4d250e61a00..c5928f41542 100644 --- a/build/pkgs/pycparser/checksums.ini +++ b/build/pkgs/pycparser/checksums.ini @@ -1,4 +1,4 @@ tarball=pycparser-VERSION-py3-none-any.whl sha1=34702512290f3bfd4850bcc95dfaf1ae972a8929 sha256=c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc -upstream_url=https://pypi.io/packages/py3/p/pycparser/pycparser-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pycparser/pycparser-VERSION-py3-none-any.whl diff --git a/build/pkgs/pygments/checksums.ini b/build/pkgs/pygments/checksums.ini index 5a54fdd0b9a..9a712f15b35 100644 --- a/build/pkgs/pygments/checksums.ini +++ b/build/pkgs/pygments/checksums.ini @@ -1,4 +1,4 @@ tarball=pygments-VERSION-py3-none-any.whl sha1=edb9fdfbc4cf53d356a02beee8822dd8e9529f35 sha256=b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a -upstream_url=https://pypi.io/packages/py3/p/pygments/pygments-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pygments/pygments-VERSION-py3-none-any.whl diff --git a/build/pkgs/pynormaliz/checksums.ini b/build/pkgs/pynormaliz/checksums.ini index caac1894126..70f40fc73c8 100644 --- a/build/pkgs/pynormaliz/checksums.ini +++ b/build/pkgs/pynormaliz/checksums.ini @@ -1,4 +1,4 @@ tarball=pynormaliz-VERSION.tar.gz sha1=4ce4fef4db61a0408bc84747294922fc49afc090 sha256=95d29fff9380ea2948166fd2b4c730985e0881043fa41f3e44ff9f402b97f0b4 -upstream_url=https://pypi.io/packages/source/p/pynormaliz/pynormaliz-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pynormaliz/pynormaliz-VERSION.tar.gz diff --git a/build/pkgs/pyparsing/checksums.ini b/build/pkgs/pyparsing/checksums.ini index 931d057f6b8..997792b6c82 100644 --- a/build/pkgs/pyparsing/checksums.ini +++ b/build/pkgs/pyparsing/checksums.ini @@ -1,4 +1,4 @@ tarball=pyparsing-VERSION-py3-none-any.whl sha1=bf1ab91fb997ccee2793e1e7f22a56a25f5e3a93 sha256=f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742 -upstream_url=https://pypi.io/packages/py3/p/pyparsing/pyparsing-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pyparsing/pyparsing-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_api/checksums.ini b/build/pkgs/pyproject_api/checksums.ini index cb551b5f7dd..fb4bd700824 100644 --- a/build/pkgs/pyproject_api/checksums.ini +++ b/build/pkgs/pyproject_api/checksums.ini @@ -1,4 +1,4 @@ tarball=pyproject_api-VERSION-py3-none-any.whl -sha1=5ea24c784a68fd0ef0228c332dc078ce64387eb8 -sha256=4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675 -upstream_url=https://pypi.io/packages/py3/p/pyproject_api/pyproject_api-VERSION-py3-none-any.whl +sha1=3723eb52bd6844f30f30f6f5b3723ce0f57a3cbf +sha256=2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb +upstream_url=https://files.pythonhosted.org/packages/py3/p/pyproject_api/pyproject_api-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_api/package-version.txt b/build/pkgs/pyproject_api/package-version.txt index 9c6d6293b1a..943f9cbc4ec 100644 --- a/build/pkgs/pyproject_api/package-version.txt +++ b/build/pkgs/pyproject_api/package-version.txt @@ -1 +1 @@ -1.6.1 +1.7.1 diff --git a/build/pkgs/pyproject_hooks/checksums.ini b/build/pkgs/pyproject_hooks/checksums.ini index 4688d10283b..590aec528d6 100644 --- a/build/pkgs/pyproject_hooks/checksums.ini +++ b/build/pkgs/pyproject_hooks/checksums.ini @@ -1,4 +1,4 @@ tarball=pyproject_hooks-VERSION-py3-none-any.whl -sha1=6c99163c52786fb97eac8b4e38cc13fa3af141a9 -sha256=283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 -upstream_url=https://pypi.io/packages/py3/p/pyproject_hooks/pyproject_hooks-VERSION-py3-none-any.whl +sha1=f8c16752e1deea6a3e9a261c6725c1af408d04e7 +sha256=7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2 +upstream_url=https://files.pythonhosted.org/packages/py3/p/pyproject_hooks/pyproject_hooks-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_hooks/package-version.txt b/build/pkgs/pyproject_hooks/package-version.txt index 3eefcb9dd5b..9084fa2f716 100644 --- a/build/pkgs/pyproject_hooks/package-version.txt +++ b/build/pkgs/pyproject_hooks/package-version.txt @@ -1 +1 @@ -1.0.0 +1.1.0 diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini index 446d55ef265..6dc2957b57f 100644 --- a/build/pkgs/pyproject_metadata/checksums.ini +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -1,4 +1,4 @@ -tarball=pyproject-metadata-VERSION.tar.gz -sha1=41fba5c33917d77b9364fadb76e590e86789634d -sha256=0a94f18b108b9b21f3a26a3d541f056c34edcb17dc872a144a15618fed7aef67 -upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz +tarball=pyproject_metadata-VERSION-py3-none-any.whl +sha1=59998bbcd31cc63f3e95f3ad8120ff71326595a0 +sha256=ad858d448e1d3a1fb408ac5bac9ea7743e7a8bbb472f2693aaa334d2db42f526 +upstream_url=https://files.pythonhosted.org/packages/py3/p/pyproject_metadata/pyproject_metadata-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies index 3df264eee42..a0e3d35a70e 100644 --- a/build/pkgs/pyproject_metadata/dependencies +++ b/build/pkgs/pyproject_metadata/dependencies @@ -1,4 +1,4 @@ - packaging pyparsing | $(PYTHON_TOOLCHAIN) $(PYTHON) +packaging | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt index 39e898a4f95..a3df0a6959e 100644 --- a/build/pkgs/pyproject_metadata/package-version.txt +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -1 +1 @@ -0.7.1 +0.8.0 diff --git a/build/pkgs/pyproject_metadata/spkg-install.in b/build/pkgs/pyproject_metadata/spkg-install.in deleted file mode 100644 index 37ac1a53437..00000000000 --- a/build/pkgs/pyproject_metadata/spkg-install.in +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_pip_install . diff --git a/build/pkgs/pyrsistent/checksums.ini b/build/pkgs/pyrsistent/checksums.ini index b7a30f5a171..24a4d3c62ff 100644 --- a/build/pkgs/pyrsistent/checksums.ini +++ b/build/pkgs/pyrsistent/checksums.ini @@ -1,4 +1,4 @@ tarball=pyrsistent-VERSION.tar.gz sha1=79980873658f7634ae25758b9710088b62e0612a sha256=1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440 -upstream_url=https://pypi.io/packages/source/p/pyrsistent/pyrsistent-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pyrsistent/pyrsistent-VERSION.tar.gz diff --git a/build/pkgs/pyscipopt/checksums.ini b/build/pkgs/pyscipopt/checksums.ini index a88c5cf04d8..b41ec57e3f7 100644 --- a/build/pkgs/pyscipopt/checksums.ini +++ b/build/pkgs/pyscipopt/checksums.ini @@ -1,4 +1,4 @@ tarball=PySCIPOpt-VERSION.tar.gz sha1=713e32cc0ff112500c4f43487614094ece4a8bbf sha256=f9c36c941e1373406b00c030f2511578c3fb02a95a2cf5559772deb846a0af47 -upstream_url=https://pypi.io/packages/source/p/pyscipopt/PySCIPOpt-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pyscipopt/PySCIPOpt-VERSION.tar.gz diff --git a/build/pkgs/pysingular/checksums.ini b/build/pkgs/pysingular/checksums.ini index 9f26a37fc9b..0a5e4f3f176 100644 --- a/build/pkgs/pysingular/checksums.ini +++ b/build/pkgs/pysingular/checksums.ini @@ -1,4 +1,4 @@ tarball=PySingular-VERSION.tar.gz sha1=c8d4bbe4552490aac37afe6d87a2cd3a7b445a7e sha256=ca03d1d7538fc61f4350acff42708c6c443e0232712a2dc42ce72140831ef60c -upstream_url=https://pypi.io/packages/source/p/pysingular/PySingular-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pysingular/PySingular-VERSION.tar.gz diff --git a/build/pkgs/pytest/checksums.ini b/build/pkgs/pytest/checksums.ini index 3b273f6dc6a..78106ab01b5 100644 --- a/build/pkgs/pytest/checksums.ini +++ b/build/pkgs/pytest/checksums.ini @@ -1,4 +1,4 @@ tarball=pytest-VERSION-py3-none-any.whl sha1=985b136db51e6729983433c6632564dc557f5fb6 sha256=4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 -upstream_url=https://pypi.io/packages/py3/p/pytest/pytest-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pytest/pytest-VERSION-py3-none-any.whl diff --git a/build/pkgs/pytest_mock/checksums.ini b/build/pkgs/pytest_mock/checksums.ini index 181cfb34ea0..46f252da1c6 100644 --- a/build/pkgs/pytest_mock/checksums.ini +++ b/build/pkgs/pytest_mock/checksums.ini @@ -1,4 +1,4 @@ tarball=pytest_mock-VERSION-py3-none-any.whl sha1=7fcd316f0d08a7c961bec3ca0d756429ccb6b840 sha256=0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f -upstream_url=https://pypi.io/packages/py3/p/pytest_mock/pytest_mock-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pytest_mock/pytest_mock-VERSION-py3-none-any.whl diff --git a/build/pkgs/pytest_xdist/checksums.ini b/build/pkgs/pytest_xdist/checksums.ini index 97486be4a3a..80547c2d005 100644 --- a/build/pkgs/pytest_xdist/checksums.ini +++ b/build/pkgs/pytest_xdist/checksums.ini @@ -1,4 +1,4 @@ tarball=pytest_xdist-VERSION-py3-none-any.whl sha1=de5b68f355cb51478ba9d5104da67a6359dee9e0 sha256=9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 -upstream_url=https://pypi.io/packages/py3/p/pytest_xdist/pytest_xdist-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/pytest_xdist/pytest_xdist-VERSION-py3-none-any.whl diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 73dddf24722..3bac47d26a6 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,4 +1,4 @@ tarball=Python-VERSION.tar.xz -sha1=c221421f3ba734daaf013dbdc7b48aa725cea18e -sha256=f6d419a6d8743ab26700801b4908d26d97e8b986e14f95de31b32de2b0e79554 +sha1=d9b83c17a717e1cbd3ab6bd14cfe3e508e6d87b2 +sha256=fa8a2e12c5e620b09f53e65bcd87550d2e5a1e2e04bf8ba991dcc55113876397 upstream_url=https://www.python.org/ftp/python/VERSION/Python-VERSION.tar.xz diff --git a/build/pkgs/python3/package-version.txt b/build/pkgs/python3/package-version.txt index 455808f8e19..d9506ceba51 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.12.4 +3.12.5 diff --git a/build/pkgs/python_build/checksums.ini b/build/pkgs/python_build/checksums.ini index c2bafe9d49d..31a63bf6f56 100644 --- a/build/pkgs/python_build/checksums.ini +++ b/build/pkgs/python_build/checksums.ini @@ -1,4 +1,4 @@ tarball=build-VERSION-py3-none-any.whl sha1=950bf228726af5041adbe2bb04a7ca74e27bce60 sha256=75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4 -upstream_url=https://pypi.io/packages/py3/b/build/build-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/b/build/build-VERSION-py3-none-any.whl diff --git a/build/pkgs/python_build/distros/conda.txt b/build/pkgs/python_build/distros/conda.txt index 378eac25d31..162b1f739e8 100644 --- a/build/pkgs/python_build/distros/conda.txt +++ b/build/pkgs/python_build/distros/conda.txt @@ -1 +1 @@ -build +python-build diff --git a/build/pkgs/python_flint/checksums.ini b/build/pkgs/python_flint/checksums.ini index 19d40c1c7b1..2ed12a73b03 100644 --- a/build/pkgs/python_flint/checksums.ini +++ b/build/pkgs/python_flint/checksums.ini @@ -1,4 +1,4 @@ tarball=python-flint-VERSION.tar.gz sha1=c7d5b3b8db47c903eea9e752bd7732e34d6c5945 sha256=f829e00774534891b38de41bc511cf6c7d6d216544a6a84b92d9e1f159de0878 -upstream_url=https://pypi.io/packages/source/p/python_flint/python-flint-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/python_flint/python-flint-VERSION.tar.gz diff --git a/build/pkgs/python_igraph/checksums.ini b/build/pkgs/python_igraph/checksums.ini index d7247b21a30..f7e3d8bff22 100644 --- a/build/pkgs/python_igraph/checksums.ini +++ b/build/pkgs/python_igraph/checksums.ini @@ -1,4 +1,4 @@ tarball=python-igraph-VERSION.tar.gz sha1=da963213ab22c60938d4e77ffab811875ee43a8a sha256=2d71d645a4c3344c5910543fabbae10d3163f46a3e824ba7753c14b9036b8233 -upstream_url=https://pypi.io/packages/source/i/igraph/igraph-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/i/igraph/igraph-VERSION.tar.gz diff --git a/build/pkgs/python_json_logger/checksums.ini b/build/pkgs/python_json_logger/checksums.ini index 2856b66a14f..7b737ad2ad7 100644 --- a/build/pkgs/python_json_logger/checksums.ini +++ b/build/pkgs/python_json_logger/checksums.ini @@ -1,4 +1,4 @@ tarball=python_json_logger-VERSION-py3-none-any.whl sha1=c1176f521d95b5452b6169943b2b9b259e024b39 sha256=f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd -upstream_url=https://pypi.io/packages/py3/p/python_json_logger/python_json_logger-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/p/python_json_logger/python_json_logger-VERSION-py3-none-any.whl diff --git a/build/pkgs/pythran/checksums.ini b/build/pkgs/pythran/checksums.ini index 03d21286397..b9626c89359 100644 --- a/build/pkgs/pythran/checksums.ini +++ b/build/pkgs/pythran/checksums.ini @@ -1,4 +1,4 @@ tarball=pythran-VERSION.tar.gz sha1=dc8a6035c0c46d36630085003160a3aba4444add sha256=f9bc61bcb96df2cd4b578abc5a62dfb3fbb0b0ef02c264513dfb615c5f87871c -upstream_url=https://pypi.io/packages/source/p/pythran/pythran-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pythran/pythran-VERSION.tar.gz diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini index 8ff6396f13e..223d3179179 100644 --- a/build/pkgs/pytz/checksums.ini +++ b/build/pkgs/pytz/checksums.ini @@ -1,4 +1,4 @@ tarball=pytz-VERSION.tar.gz sha1=be3f14bc0d6b89b8c579d8ae4e0fcb4478ff92e6 sha256=7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b -upstream_url=https://pypi.io/packages/source/p/pytz/pytz-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pytz/pytz-VERSION.tar.gz diff --git a/build/pkgs/pytz_deprecation_shim/checksums.ini b/build/pkgs/pytz_deprecation_shim/checksums.ini index 199685f084c..fd07bf986b3 100644 --- a/build/pkgs/pytz_deprecation_shim/checksums.ini +++ b/build/pkgs/pytz_deprecation_shim/checksums.ini @@ -1,4 +1,4 @@ tarball=pytz_deprecation_shim-VERSION.tar.gz sha1=d7900c309c26d48f6499fbda955eb80bd0b437dd sha256=af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d -upstream_url=https://pypi.io/packages/source/p/pytz_deprecation_shim/pytz_deprecation_shim-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pytz_deprecation_shim/pytz_deprecation_shim-VERSION.tar.gz diff --git a/build/pkgs/pyyaml/checksums.ini b/build/pkgs/pyyaml/checksums.ini index 1e9b9f74722..0823c82b2f8 100644 --- a/build/pkgs/pyyaml/checksums.ini +++ b/build/pkgs/pyyaml/checksums.ini @@ -1,4 +1,4 @@ tarball=PyYAML-VERSION.tar.gz sha1=a80d802ad8f693bed34c8fb5ee168a1872663c9a sha256=bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 -upstream_url=https://pypi.io/packages/source/p/pyyaml/PyYAML-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pyyaml/PyYAML-VERSION.tar.gz diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index b4b99817bdc..e04ccdd1c15 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,4 +1,4 @@ tarball=pyzmq-VERSION.tar.gz sha1=f750e59a3d5fcca64d0a1a6723c1bc72173e976f sha256=259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23 -upstream_url=https://pypi.io/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz diff --git a/build/pkgs/qdldl_python/checksums.ini b/build/pkgs/qdldl_python/checksums.ini index a444f898ae6..0b59f50a58b 100644 --- a/build/pkgs/qdldl_python/checksums.ini +++ b/build/pkgs/qdldl_python/checksums.ini @@ -1,4 +1,4 @@ tarball=qdldl-VERSION.tar.gz sha1=af76c57ca1787f5e44e42f6c9f916b84ae599f1f sha256=69c092f6e1fc23fb779a80a62e6fcdfe2eba05c925860248c4d6754f4736938f -upstream_url=https://pypi.io/packages/source/q/qdldl/qdldl-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/q/qdldl/qdldl-VERSION.tar.gz diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 deleted file mode 100644 index 0552d0c56ce..00000000000 --- a/build/pkgs/r/spkg-configure.m4 +++ /dev/null @@ -1,14 +0,0 @@ -SAGE_SPKG_CONFIGURE([r], [dnl - dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements - m4_pushdef([SAGE_R_MINVER], ["3.5"]) - PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [dnl - AC_PATH_PROG([R], [R]) - AS_IF([test "x$R" = x], [dnl - AC_MSG_NOTICE([R is not found]) - sage_spkg_install_r=yes - ], [dnl TODO: check that versions of R and libR match - sage_spkg_install_r=no - ]) - ], [sage_spkg_install_r=yes]) - m4_popdef([SAGE_R_MINVER]) -]) diff --git a/build/pkgs/r_jupyter/dependencies b/build/pkgs/r_jupyter/dependencies index 44078fe297e..c9895da2b6d 100644 --- a/build/pkgs/r_jupyter/dependencies +++ b/build/pkgs/r_jupyter/dependencies @@ -1 +1,5 @@ -notebook r +notebook rpy2 + +---------- +R is the real dependency. But SPKG r is a dummy package, and does not install R. +Since SPKG rpy2 checks for the system R, we put rpy2 as a dependency instead. diff --git a/build/pkgs/referencing/checksums.ini b/build/pkgs/referencing/checksums.ini index 293629ca16d..ace04d9b28c 100644 --- a/build/pkgs/referencing/checksums.ini +++ b/build/pkgs/referencing/checksums.ini @@ -1,4 +1,4 @@ tarball=referencing-VERSION-py3-none-any.whl sha1=9d710ba3a604d24ffded218a3813b5fd1fe2e495 sha256=160f24a7d2411dc82b1efd96dfb083ee9e5cc9bc8e492d323e0dd853989d37b3 -upstream_url=https://pypi.io/packages/py3/r/referencing/referencing-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/r/referencing/referencing-VERSION-py3-none-any.whl diff --git a/build/pkgs/requests/checksums.ini b/build/pkgs/requests/checksums.ini index d7a9d3b8c66..025f971aa2b 100644 --- a/build/pkgs/requests/checksums.ini +++ b/build/pkgs/requests/checksums.ini @@ -1,4 +1,4 @@ tarball=requests-VERSION-py3-none-any.whl sha1=e82ece8f17ba4237cc5a4fd641349e45e1d3ddfd sha256=fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c -upstream_url=https://pypi.io/packages/py3/r/requests/requests-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/r/requests/requests-VERSION-py3-none-any.whl diff --git a/build/pkgs/rfc3339_validator/checksums.ini b/build/pkgs/rfc3339_validator/checksums.ini index a0766fbec23..babf4ab8107 100644 --- a/build/pkgs/rfc3339_validator/checksums.ini +++ b/build/pkgs/rfc3339_validator/checksums.ini @@ -1,4 +1,4 @@ tarball=rfc3339_validator-VERSION-py2.py3-none-any.whl sha1=daa86cb641dfd6ebfef4ece6dea1be8fd63dec00 sha256=24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa -upstream_url=https://pypi.io/packages/py2.py3/r/rfc3339_validator/rfc3339_validator-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/r/rfc3339_validator/rfc3339_validator-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/rfc3986_validator/checksums.ini b/build/pkgs/rfc3986_validator/checksums.ini index 5d08b72ca0e..e1da79014c9 100644 --- a/build/pkgs/rfc3986_validator/checksums.ini +++ b/build/pkgs/rfc3986_validator/checksums.ini @@ -1,4 +1,4 @@ tarball=rfc3986_validator-VERSION-py2.py3-none-any.whl sha1=c0fabd5c0568cc516f9258f3e5846a04a059dc31 sha256=2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9 -upstream_url=https://pypi.io/packages/py2.py3/r/rfc3986_validator/rfc3986_validator-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/r/rfc3986_validator/rfc3986_validator-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/rpy2/checksums.ini b/build/pkgs/rpy2/checksums.ini index 62224bc62f8..9236ef7b950 100644 --- a/build/pkgs/rpy2/checksums.ini +++ b/build/pkgs/rpy2/checksums.ini @@ -1,4 +1,4 @@ tarball=rpy2-VERSION.tar.gz sha1=7d236c0c6982333b20b6a126f0c17a5481fea64b sha256=5d31a5ea43f5a59f6dec30faca87edb01fc9b8affa0beae96a99be923bd7dab3 -upstream_url=https://pypi.io/packages/source/r/rpy2/rpy2-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/r/rpy2/rpy2-VERSION.tar.gz diff --git a/build/pkgs/rpy2/dependencies b/build/pkgs/rpy2/dependencies index b88615716d4..271ec08a7f9 100644 --- a/build/pkgs/rpy2/dependencies +++ b/build/pkgs/rpy2/dependencies @@ -1,4 +1,4 @@ - r cffi tzlocal pytz jinja2 | $(PYTHON_TOOLCHAIN) pycparser $(PYTHON) +cffi tzlocal pytz jinja2 | $(PYTHON_TOOLCHAIN) pycparser $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/rpy2/spkg-configure.m4 b/build/pkgs/rpy2/spkg-configure.m4 index 0cb3784ea9c..e19713c5775 100644 --- a/build/pkgs/rpy2/spkg-configure.m4 +++ b/build/pkgs/rpy2/spkg-configure.m4 @@ -1,14 +1,36 @@ SAGE_SPKG_CONFIGURE([rpy2], [ - SAGE_PYTHON_PACKAGE_CHECK([rpy2]) + SAGE_PYTHON_PACKAGE_CHECK([rpy2]) ], [dnl REQUIRED-CHECK - AC_REQUIRE([SAGE_SPKG_CONFIGURE_R]) - dnl rpy2 is only needed when there is a usable system R - AS_VAR_IF([sage_spkg_install_r], [yes], [dnl - AS_VAR_IF([sage_use_system_r], [installed], [dnl - dnl Legacy SPKG installation of r - AS_VAR_SET([SPKG_REQUIRE], [yes]) - ], [dnl No system package, no legacy SPKG installation - AS_VAR_SET([SPKG_REQUIRE], [no]) - ]) + dnl rpy2 is only needed when there is a usable system R + dnl Check for the R installation and version + dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements + m4_pushdef([SAGE_R_MINVER], ["3.5"]) + PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [dnl + AC_PATH_PROG([R_EXECUTABLE], [R]) + AS_IF([test "x$R_EXECUTABLE" = x], [dnl + AC_MSG_NOTICE([R is not found]) + dnl No R found, so do not require rpy2 package + AS_VAR_SET([SPKG_REQUIRE], [no]) + ], [dnl Extract R version + AC_MSG_CHECKING([for version of R executable]) + R_VERSION=$($R_EXECUTABLE --version | sed -n 's/^R version \([[0-9.]]*\).*/\1/p') + AC_MSG_RESULT([$R_VERSION]) + dnl Extract libR version + AC_MSG_CHECKING([for version of libR]) + LIBR_VERSION=$(pkg-config --modversion libR) + AC_MSG_RESULT([$LIBR_VERSION]) + dnl Compare R and libR versions + AS_IF([test "x$R_VERSION" = "x$LIBR_VERSION"], [dnl + AC_MSG_NOTICE([R and libR versions match ($R_VERSION)]) + dnl Good system R is found, require rpy2 package + AS_VAR_SET([SPKG_REQUIRE], [yes]) + ], [dnl R and libR versions do not match + AC_MSG_NOTICE([R version ($R_VERSION) does not match libR version ($LIBR_VERSION)]) + AS_VAR_SET([SPKG_REQUIRE], [no]) + ]) ]) + ], [dnl libR not found or outdated + AS_VAR_SET([SPKG_REQUIRE], [no]) + ]) + m4_popdef([SAGE_R_MINVER]) ]) diff --git a/build/pkgs/rst2ipynb/checksums.ini b/build/pkgs/rst2ipynb/checksums.ini index 2248cc0a54a..9c968023eee 100644 --- a/build/pkgs/rst2ipynb/checksums.ini +++ b/build/pkgs/rst2ipynb/checksums.ini @@ -1,4 +1,4 @@ tarball=rst2ipynb-VERSION.tar.gz sha1=98926df9a8336c8974f446a2a858458495b5aec4 sha256=30d70b0e96f1c37baad9c8dbe904fc2567354eec02c52b94e7c7287b6268eaa3 -upstream_url=https://pypi.io/packages/source/r/rst2ipynb/rst2ipynb-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/r/rst2ipynb/rst2ipynb-VERSION.tar.gz diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index a0d122e1756..7b7d288febd 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.5b2 +sage-conf ~= 10.5b9 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index b3dd5668f62..20be798e7f9 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.5b2 +sage-docbuild ~= 10.5b9 diff --git a/build/pkgs/sage_numerical_backends_coin/checksums.ini b/build/pkgs/sage_numerical_backends_coin/checksums.ini index 427dcedeb0a..84784d5d282 100644 --- a/build/pkgs/sage_numerical_backends_coin/checksums.ini +++ b/build/pkgs/sage_numerical_backends_coin/checksums.ini @@ -1,4 +1,4 @@ tarball=sage_numerical_backends_coin-VERSION.tar.gz sha1=2033e1ba209315366a6dbfe249d5de5f7a1bc1b0 sha256=6e34d48632d070e97dc37b724098c0f050026b166b328af78929b1ea079fa9e7 -upstream_url=https://pypi.io/packages/source/s/sage_numerical_backends_coin/sage_numerical_backends_coin-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/sage_numerical_backends_coin/sage_numerical_backends_coin-VERSION.tar.gz diff --git a/build/pkgs/sage_numerical_backends_cplex/checksums.ini b/build/pkgs/sage_numerical_backends_cplex/checksums.ini index 24a6756620a..8f4439b6691 100644 --- a/build/pkgs/sage_numerical_backends_cplex/checksums.ini +++ b/build/pkgs/sage_numerical_backends_cplex/checksums.ini @@ -1,4 +1,4 @@ tarball=sage_numerical_backends_cplex-VERSION.tar.gz sha1=b7085bfdeecb55a43c799493672a228687c30eaf sha256=367480d7a291e0ac4e3df529fbc2a17f78f3770ce7dc2cf78d765f72b7bd938e -upstream_url=https://pypi.io/packages/source/s/sage_numerical_backends_cplex/sage_numerical_backends_cplex-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/sage_numerical_backends_cplex/sage_numerical_backends_cplex-VERSION.tar.gz diff --git a/build/pkgs/sage_numerical_backends_gurobi/checksums.ini b/build/pkgs/sage_numerical_backends_gurobi/checksums.ini index 02b722b9bd2..d9dd145cb9c 100644 --- a/build/pkgs/sage_numerical_backends_gurobi/checksums.ini +++ b/build/pkgs/sage_numerical_backends_gurobi/checksums.ini @@ -1,4 +1,4 @@ tarball=sage_numerical_backends_gurobi-VERSION.tar.gz sha1=6891c154bd035932759152dba6a8bd77e8811f22 sha256=3c3b51d6577f651d10cb7f6fc37ca4bb27c6fe2716d6515d1d23eeed1f34e32a -upstream_url=https://pypi.io/packages/source/s/sage_numerical_backends_gurobi/sage_numerical_backends_gurobi-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/sage_numerical_backends_gurobi/sage_numerical_backends_gurobi-VERSION.tar.gz diff --git a/build/pkgs/sage_setup/dependencies b/build/pkgs/sage_setup/dependencies index cae3faf9049..497a8118c28 100644 --- a/build/pkgs/sage_setup/dependencies +++ b/build/pkgs/sage_setup/dependencies @@ -1,4 +1,4 @@ - cython pkgconfig jinja2 $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/*.py $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/autogen/interpreters/specs/*.py $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/command/*.py | $(PYTHON_TOOLCHAIN) $(PYTHON) + cython pkgconfig jinja2 $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/*.py $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/autogen/interpreters/internal/specs/*.py $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/command/*.py | $(PYTHON_TOOLCHAIN) $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index bab577d27f2..83c89067e18 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.5b2 +sage-setup ~= 10.5b9 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index e641cf4adc8..ddd53c64619 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.5b2 +sage-sws2rst ~= 10.5b9 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 086538d8f55..d831508e352 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.5b2 +sagemath-standard ~= 10.5b9 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index 3c1ecf4bcf2..045266674e4 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.5b2 +sagemath-bliss ~= 10.5b9 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 4e081e476c4..811a813dea6 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.5b2 +sagemath-categories ~= 10.5b9 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index f94c170af62..79c22b51001 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.5b2 +sagemath-coxeter3 ~= 10.5b9 diff --git a/build/pkgs/sagemath_doc_html/dependencies b/build/pkgs/sagemath_doc_html/dependencies index 1d286fe50bc..40717629a77 100644 --- a/build/pkgs/sagemath_doc_html/dependencies +++ b/build/pkgs/sagemath_doc_html/dependencies @@ -1,4 +1,4 @@ -sagelib sphinx sphinx_copybutton sphinx_inline_tabs pplpy_doc | $(SAGERUNTIME) maxima networkx scipy sympy matplotlib pillow mathjax mpmath ipykernel jupyter_client conway_polynomials tachyon jmol ipywidgets jupyter_sphinx sage_docbuild elliptic_curves furo fpylll graphs +sagelib sphinx sphinx_copybutton sphinx_inline_tabs pplpy_doc | $(SAGERUNTIME) maxima networkx scipy sympy matplotlib pillow mathjax mpmath ipykernel jupyter_client conway_polynomials tachyon ipywidgets sage_docbuild elliptic_curves furo fpylll graphs # Building the documentation has many dependencies, because all # documented modules are imported and because we use matplotlib to diff --git a/build/pkgs/sagemath_doc_html/dependencies_optional b/build/pkgs/sagemath_doc_html/dependencies_optional new file mode 100644 index 00000000000..e9b59e89755 --- /dev/null +++ b/build/pkgs/sagemath_doc_html/dependencies_optional @@ -0,0 +1 @@ +jupyter_sphinx diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 2915d669284..825be89f4b7 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.5b2 +sagemath-environment ~= 10.5b9 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index f066b99b596..e2f53c39eda 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.5b2 +sagemath-mcqd ~= 10.5b9 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index ad91adb1087..511fc349e39 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.5b2 +sagemath-meataxe ~= 10.5b9 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index e8b79dc7bb5..e76dace4ebf 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.5b2 +sagemath-objects ~= 10.5b9 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index 784aeb5cf8d..ddb705e31f7 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.5b2 +sagemath-repl ~= 10.5b9 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index dcfccd36238..05b07a6349c 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.5b2 +sagemath-sirocco ~= 10.5b9 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 3f3d3da3740..fa143114867 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.5b2 +sagemath-tdlib ~= 10.5b9 diff --git a/build/pkgs/sagetex/checksums.ini b/build/pkgs/sagetex/checksums.ini index 7cfac7e72f3..99442f969cc 100644 --- a/build/pkgs/sagetex/checksums.ini +++ b/build/pkgs/sagetex/checksums.ini @@ -1,4 +1,4 @@ tarball=sagetex-VERSION.tar.gz sha1=821c8a6ab11ee651d0dcc599c5582fefb6706775 sha256=03162ec62cb86da13a747f982241af3e4f4cdd4d29fcba8fbb6c6982a9e906d9 -upstream_url=https://pypi.io/packages/source/s/sagetex/sagetex-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/sagetex/sagetex-VERSION.tar.gz diff --git a/build/pkgs/sagetex/dependencies b/build/pkgs/sagetex/dependencies index af9b5f370fb..13610c4b390 100644 --- a/build/pkgs/sagetex/dependencies +++ b/build/pkgs/sagetex/dependencies @@ -1,4 +1,4 @@ - maxima scipy matplotlib pillow tachyon pyparsing | $(PYTHON) + maxima scipy matplotlib pillow tachyon pyparsing | $(PYTHON_TOOLCHAIN) $(PYTHON) To build SageTeX, you just need Python and pyparsing, but to test (SAGE_CHECK=yes) SageTeX, you actually need to run Sage, produce plots,... diff --git a/build/pkgs/sagetex/dependencies_check b/build/pkgs/sagetex/dependencies_check index d24e23242d4..ff812202db7 100644 --- a/build/pkgs/sagetex/dependencies_check +++ b/build/pkgs/sagetex/dependencies_check @@ -1,4 +1,4 @@ -$(SAGERUNTIME) sympy elliptic_curves jmol +$(SAGERUNTIME) sympy elliptic_curves To build SageTeX, you just need Python, but to test (SAGE_CHECK=yes) SageTeX, you actually need to run Sage, produce plots,... diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 8871f77250b..d834a3ad1b3 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,4 +1,4 @@ tarball=scipy-VERSION.tar.gz sha1=0fd6e14972d8dd9b4a656686a40aed00ad0f1396 sha256=4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3 -upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scs/checksums.ini b/build/pkgs/scs/checksums.ini index ceaf994ac5f..d78d19fbac9 100644 --- a/build/pkgs/scs/checksums.ini +++ b/build/pkgs/scs/checksums.ini @@ -1,4 +1,4 @@ tarball=scs-VERSION.tar.gz sha1=92e4ff21b450c9659f610064eb79e804de9167b4 sha256=e3bd779e7e977e3ae5a2f2035aa4c2a309e29082d59a722d5d6592edc4bdb4b3 -upstream_url=https://pypi.io/packages/source/s/scs/scs-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/scs/scs-VERSION.tar.gz diff --git a/build/pkgs/send2trash/checksums.ini b/build/pkgs/send2trash/checksums.ini index 8617b6b9b6b..bf0c8b27d6b 100644 --- a/build/pkgs/send2trash/checksums.ini +++ b/build/pkgs/send2trash/checksums.ini @@ -1,4 +1,4 @@ tarball=Send2Trash-VERSION-py3-none-any.whl sha1=e24d3494febe78a4a1c8ecbfde78ed52e78cf299 sha256=0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9 -upstream_url=https://pypi.io/packages/py3/s/send2trash/Send2Trash-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/send2trash/Send2Trash-VERSION-py3-none-any.whl diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 2436821630d..13bcfce0429 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools-VERSION-py3-none-any.whl -sha1=49841be6743b2d129d01d02d5fd339dd693c99dc -sha256=c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32 -upstream_url=https://pypi.io/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl +sha1=3756539d45341ca5cec9e2dfe11539faa066f5cd +sha256=b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e +upstream_url=https://files.pythonhosted.org/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 2a93f495d25..153e4cd6210 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -69.5.1 +73.0.1 diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index abae8c0a4f4..553d3df6614 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION-py3-none-any.whl sha1=be606b6acb67714b96e9e1e9a9944feaca504e44 sha256=897a3226a6fd4a6eb2f068745e49733261a21f70b1bb28fce0339feb978d9af3 -upstream_url=https://pypi.io/packages/py3/s/setuptools_scm/setuptools_scm-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/setuptools_scm/setuptools_scm-VERSION-py3-none-any.whl diff --git a/build/pkgs/six/checksums.ini b/build/pkgs/six/checksums.ini index 24f678da7eb..4944f7d54fe 100644 --- a/build/pkgs/six/checksums.ini +++ b/build/pkgs/six/checksums.ini @@ -1,4 +1,4 @@ tarball=six-VERSION-py2.py3-none-any.whl sha1=79e6f2e4f9e24898f1896df379871b9c9922f147 sha256=8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 -upstream_url=https://pypi.io/packages/py2.py3/s/six/six-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/s/six/six-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/sniffio/checksums.ini b/build/pkgs/sniffio/checksums.ini index c3a342052ec..554eef47d86 100644 --- a/build/pkgs/sniffio/checksums.ini +++ b/build/pkgs/sniffio/checksums.ini @@ -1,4 +1,4 @@ tarball=sniffio-VERSION-py3-none-any.whl sha1=bd8d1ec2b285eed542c53ca22232e6e9e468c389 sha256=2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 -upstream_url=https://pypi.io/packages/py3/s/sniffio/sniffio-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sniffio/sniffio-VERSION-py3-none-any.whl diff --git a/build/pkgs/snowballstemmer/checksums.ini b/build/pkgs/snowballstemmer/checksums.ini index cab5be15660..a013fcb27e2 100644 --- a/build/pkgs/snowballstemmer/checksums.ini +++ b/build/pkgs/snowballstemmer/checksums.ini @@ -1,4 +1,4 @@ tarball=snowballstemmer-VERSION.tar.gz sha1=aaf1b0e3b58d25e2e297ea3dbef59d8534ef8d92 sha256=09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 -upstream_url=https://pypi.io/packages/source/s/snowballstemmer/snowballstemmer-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/snowballstemmer/snowballstemmer-VERSION.tar.gz diff --git a/build/pkgs/soupsieve/checksums.ini b/build/pkgs/soupsieve/checksums.ini index 3e9ef0ba9fc..8d78dcb4d86 100644 --- a/build/pkgs/soupsieve/checksums.ini +++ b/build/pkgs/soupsieve/checksums.ini @@ -1,4 +1,4 @@ tarball=soupsieve-VERSION-py3-none-any.whl sha1=a155a6208211aa90bbc3bcfc9cab194a05000e59 sha256=eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7 -upstream_url=https://pypi.io/packages/py3/s/soupsieve/soupsieve-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/soupsieve/soupsieve-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index f46ca655b6d..1721e0e8521 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinx-VERSION-py3-none-any.whl -sha1=8128e17c3c013a91df4c08fc905db5a7e5f63dbf -sha256=413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3 -upstream_url=https://pypi.io/packages/py3/s/sphinx/sphinx-VERSION-py3-none-any.whl +sha1=f9af5608fbb188f12e38d3c09cdefeef40255365 +sha256=c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239 +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinx/sphinx-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 704726ec86c..25627bcf165 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -7.3.7 +7.4.7 diff --git a/build/pkgs/sphinx/version_requirements.txt b/build/pkgs/sphinx/version_requirements.txt index 9003fdec2f8..d311e5262f2 100644 --- a/build/pkgs/sphinx/version_requirements.txt +++ b/build/pkgs/sphinx/version_requirements.txt @@ -1 +1 @@ -sphinx >=5.2, <8 +sphinx >=7.4.7, <9 diff --git a/build/pkgs/sphinx_basic_ng/checksums.ini b/build/pkgs/sphinx_basic_ng/checksums.ini index 4933de2fa24..6ad0e9c0b5a 100644 --- a/build/pkgs/sphinx_basic_ng/checksums.ini +++ b/build/pkgs/sphinx_basic_ng/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinx_basic_ng-VERSION-py3-none-any.whl sha1=abcd9bda6ae61bb20c52bf46c17fb1bbdfdab4ea sha256=eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b -upstream_url=https://pypi.io/packages/py3/s/sphinx_basic_ng/sphinx_basic_ng-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinx_basic_ng/sphinx_basic_ng-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinx_copybutton/checksums.ini b/build/pkgs/sphinx_copybutton/checksums.ini index 1c83ae582e0..17531981252 100644 --- a/build/pkgs/sphinx_copybutton/checksums.ini +++ b/build/pkgs/sphinx_copybutton/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinx_copybutton-VERSION-py3-none-any.whl sha1=a15e038b665225b13f7bd3eae6a2a64c8bd4b582 sha256=fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e -upstream_url=https://pypi.io/packages/py3/s/sphinx_copybutton/sphinx_copybutton-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinx_copybutton/sphinx_copybutton-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinx_inline_tabs/checksums.ini b/build/pkgs/sphinx_inline_tabs/checksums.ini index 93c03352650..423f9c2772a 100644 --- a/build/pkgs/sphinx_inline_tabs/checksums.ini +++ b/build/pkgs/sphinx_inline_tabs/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinx_inline_tabs-VERSION-py3-none-any.whl sha1=1404e320d0533280355e7e1e71cffd9937015027 sha256=06809ac613f7c48ddd6e2fa588413e3fe92cff2397b56e2ccf0b0218f9ef6a78 -upstream_url=https://pypi.io/packages/py3/s/sphinx_inline_tabs/sphinx_inline_tabs-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinx_inline_tabs/sphinx_inline_tabs-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_applehelp/checksums.ini b/build/pkgs/sphinxcontrib_applehelp/checksums.ini index 25ebbacf69a..55b9e980fb3 100644 --- a/build/pkgs/sphinxcontrib_applehelp/checksums.ini +++ b/build/pkgs/sphinxcontrib_applehelp/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_applehelp-VERSION-py3-none-any.whl sha1=e426527562da2c5c520b27c58210cd1d44a1185b sha256=cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4 -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_applehelp/sphinxcontrib_applehelp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_applehelp/sphinxcontrib_applehelp-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_devhelp/checksums.ini b/build/pkgs/sphinxcontrib_devhelp/checksums.ini index 03e4c534a7a..2352006b2c9 100644 --- a/build/pkgs/sphinxcontrib_devhelp/checksums.ini +++ b/build/pkgs/sphinxcontrib_devhelp/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_devhelp-VERSION-py3-none-any.whl sha1=c1c774393d267d97eaf07f0e5c740f82af24d628 sha256=6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_devhelp/sphinxcontrib_devhelp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_devhelp/sphinxcontrib_devhelp-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini b/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini index 02f3fefa8df..2a7d6ba97bb 100644 --- a/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini +++ b/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_htmlhelp-VERSION-py3-none-any.whl sha1=6b60c617a0fe98a663ca146edc03867581da5e07 sha256=393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04 -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_htmlhelp/sphinxcontrib_htmlhelp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_htmlhelp/sphinxcontrib_htmlhelp-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_jsmath/checksums.ini b/build/pkgs/sphinxcontrib_jsmath/checksums.ini index 5ad932e6f6c..1f2ec07e8ab 100644 --- a/build/pkgs/sphinxcontrib_jsmath/checksums.ini +++ b/build/pkgs/sphinxcontrib_jsmath/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_jsmath-VERSION-py2.py3-none-any.whl sha1=beff4fc35d13a5f2883bc129f28ac031046195c5 sha256=2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 -upstream_url=https://pypi.io/packages/py2.py3/s/sphinxcontrib_jsmath/sphinxcontrib_jsmath-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/s/sphinxcontrib_jsmath/sphinxcontrib_jsmath-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_qthelp/checksums.ini b/build/pkgs/sphinxcontrib_qthelp/checksums.ini index bb35693af5e..d2c313eb1fb 100644 --- a/build/pkgs/sphinxcontrib_qthelp/checksums.ini +++ b/build/pkgs/sphinxcontrib_qthelp/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_qthelp-VERSION-py3-none-any.whl sha1=8f593bd6ca46487ed25ee0fca50f0d88b18e5f9e sha256=e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182 -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_qthelp/sphinxcontrib_qthelp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_qthelp/sphinxcontrib_qthelp-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini b/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini index 292f6b2b195..81e105df791 100644 --- a/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini +++ b/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_serializinghtml-VERSION-py3-none-any.whl sha1=a5198a72d1668e97fdda39a559586bcf57cb7278 sha256=326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7 -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_serializinghtml/sphinxcontrib_serializinghtml-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_serializinghtml/sphinxcontrib_serializinghtml-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinxcontrib_websupport/checksums.ini b/build/pkgs/sphinxcontrib_websupport/checksums.ini index 9964fd80b13..c51d7ed6b52 100644 --- a/build/pkgs/sphinxcontrib_websupport/checksums.ini +++ b/build/pkgs/sphinxcontrib_websupport/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinxcontrib_websupport-VERSION-py3-none-any.whl sha1=649d1447a4773b665588060efda66344cb9b99a5 sha256=2dc179d7f821ebd54f31f93c894ca52435ebc5364e4e4dfb0da834ac119d51fd -upstream_url=https://pypi.io/packages/py3/s/sphinxcontrib_websupport/sphinxcontrib_websupport-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinxcontrib_websupport/sphinxcontrib_websupport-VERSION-py3-none-any.whl diff --git a/build/pkgs/stack_data/checksums.ini b/build/pkgs/stack_data/checksums.ini index 4f749a287f0..ddcda4ebb4a 100644 --- a/build/pkgs/stack_data/checksums.ini +++ b/build/pkgs/stack_data/checksums.ini @@ -1,4 +1,4 @@ tarball=stack_data-VERSION-py3-none-any.whl sha1=96814b10bdc464e8ef00f4a07c60dd17a3dc9668 sha256=d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695 -upstream_url=https://pypi.io/packages/py3/s/stack_data/stack_data-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/s/stack_data/stack_data-VERSION-py3-none-any.whl diff --git a/build/pkgs/suitesparse/SPKG.rst b/build/pkgs/suitesparse/SPKG.rst index 7a9bd5d599c..ab673b390e7 100644 --- a/build/pkgs/suitesparse/SPKG.rst +++ b/build/pkgs/suitesparse/SPKG.rst @@ -2,26 +2,19 @@ suitesparse: A suite of sparse matrix software ============================================== SuiteSparse is a collection of software to deal with sparse matrix. It is -hosted at http://faculty.cse.tamu.edu/davis/suitesparse.html +hosted at https://people.engr.tamu.edu/davis/suitesparse.html with source code +now on github at https://github.com/DrTimothyAldenDavis/SuiteSparse -This spkg does a minimal install of suitesparse disabling the following +This spkg does a minimal install of suitesparse, only intalling the following -- metis -- GraphBLAS (need cmake) -- Mongoose (need cmake) +- AMD +- CAMD +- COLAMD +- CCOLAMD +- CHOLMOD +- UMFPACK -An external metis package can be used but we just disable its use. - -Patches: - -- The first patch disable the building of package using cmake. -- The second patch make sure we use sage's blas/lapack on OS X. By - default - suitesparse discard any configurations to use the accelerate framework. - -The building of metis is diabled by passing MY_METIS_LIB=none to make -(any value would have done) We also configure cholmod so it doesn't -require metis by passing CHOLMOD_CONFIG=-DNPARTITION to make. +Those are all the packages needed for cvxopt. Other configurations are self explanatory. diff --git a/build/pkgs/suitesparse/checksums.ini b/build/pkgs/suitesparse/checksums.ini index e05ee205baf..d3b1ad9641a 100644 --- a/build/pkgs/suitesparse/checksums.ini +++ b/build/pkgs/suitesparse/checksums.ini @@ -1,4 +1,4 @@ tarball=SuiteSparse-VERSION.tar.gz -sha1=83dd96b32701e12b7577acb7d9aea80138d7e46e -sha256=acb4d1045f48a237e70294b950153e48dce5b5f9ca8190e86c2b8c54ce00a7ee +sha1=aa2745d8e38d2c010e0a7256acd36eba1de19055 +sha256=8ea989f36be3646d0b0eecb06218698766ca06256c0de15a64350e993bf5c5f9 upstream_url=https://github.com/DrTimothyAldenDavis/SuiteSparse/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/suitesparse/dependencies b/build/pkgs/suitesparse/dependencies index c9663df28f5..8ea6f6f523a 100644 --- a/build/pkgs/suitesparse/dependencies +++ b/build/pkgs/suitesparse/dependencies @@ -1 +1 @@ -$(BLAS) gfortran mpfr $(MP_LIBRARY) +$(BLAS) gfortran mpfr $(MP_LIBRARY) | cmake diff --git a/build/pkgs/suitesparse/package-version.txt b/build/pkgs/suitesparse/package-version.txt index 4e32c7b1caf..09a6d30847d 100644 --- a/build/pkgs/suitesparse/package-version.txt +++ b/build/pkgs/suitesparse/package-version.txt @@ -1 +1 @@ -5.10.1 +7.8.0 diff --git a/build/pkgs/suitesparse/patches/01-no_cmake_project.patch b/build/pkgs/suitesparse/patches/01-no_cmake_project.patch deleted file mode 100644 index 8b2b1a2666c..00000000000 --- a/build/pkgs/suitesparse/patches/01-no_cmake_project.patch +++ /dev/null @@ -1,90 +0,0 @@ -diff --git a/Makefile b/Makefile -index abd00d0f..10c31390 100644 ---- a/Makefile -+++ b/Makefile -@@ -16,7 +16,6 @@ include SuiteSparse_config/SuiteSparse_config.mk - # installs all libraries SuiteSparse/lib. - go: metis - ( cd SuiteSparse_config && $(MAKE) ) -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) - ( cd AMD && $(MAKE) ) - ( cd BTF && $(MAKE) ) - ( cd CAMD && $(MAKE) ) -@@ -34,7 +33,6 @@ ifneq ($(GPU_CONFIG),) - ( cd GPUQREngine && $(MAKE) ) - endif - ( cd SPQR && $(MAKE) ) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) - ( cd SLIP_LU && $(MAKE) ) - # ( cd PIRO_BAND && $(MAKE) ) - # ( cd SKYLINE_SVD && $(MAKE) ) -@@ -46,7 +44,6 @@ endif - # (note that CSparse is not installed; CXSparse is installed instead) - install: metisinstall - ( cd SuiteSparse_config && $(MAKE) install ) -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) - ( cd AMD && $(MAKE) install ) - ( cd BTF && $(MAKE) install ) - ( cd CAMD && $(MAKE) install ) -@@ -63,7 +60,6 @@ ifneq (,$(GPU_CONFIG)) - ( cd GPUQREngine && $(MAKE) install ) - endif - ( cd SPQR && $(MAKE) install ) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) - # ( cd PIRO_BAND && $(MAKE) install ) - # ( cd SKYLINE_SVD && $(MAKE) install ) - ( cd SLIP_LU && $(MAKE) install ) -@@ -126,7 +122,6 @@ endif - # the static library - library: metis - ( cd SuiteSparse_config && $(MAKE) ) -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' library ) - ( cd AMD && $(MAKE) library ) - ( cd BTF && $(MAKE) library ) - ( cd CAMD && $(MAKE) library ) -@@ -144,7 +139,6 @@ ifneq (,$(GPU_CONFIG)) - ( cd GPUQREngine && $(MAKE) library ) - endif - ( cd SPQR && $(MAKE) library ) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' library ) - ( cd SLIP_LU && $(MAKE) library ) - # ( cd PIRO_BAND && $(MAKE) library ) - # ( cd SKYLINE_SVD && $(MAKE) library ) -@@ -154,7 +148,6 @@ endif - # both the dynamic and static libraries. - static: metis - ( cd SuiteSparse_config && $(MAKE) static ) -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' static ) - ( cd AMD && $(MAKE) static ) - ( cd BTF && $(MAKE) static ) - ( cd CAMD && $(MAKE) static ) -@@ -172,7 +165,6 @@ ifneq (,$(GPU_CONFIG)) - ( cd GPUQREngine && $(MAKE) static ) - endif - ( cd SPQR && $(MAKE) static ) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' static ) - ( cd SLIP_LU && $(MAKE) static ) - # ( cd PIRO_BAND && $(MAKE) static ) - # ( cd SKYLINE_SVD && $(MAKE) static ) -@@ -291,21 +283,3 @@ else - @echo 'Using pre-installed METIS 5.1.0 library at ' '[$(MY_METIS_LIB)]' - endif - --# just compile GraphBLAS --gb: -- echo $(CMAKE_OPTIONS) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) -- --# just install GraphBLAS --gbinstall: -- echo $(CMAKE_OPTIONS) -- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) -- --# just compile Mongoose --mon: -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) -- --# just install Mongoose --moninstall: -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) -- diff --git a/build/pkgs/suitesparse/patches/02-darwin_blas.patch b/build/pkgs/suitesparse/patches/02-darwin_blas.patch deleted file mode 100644 index 546bfc80642..00000000000 --- a/build/pkgs/suitesparse/patches/02-darwin_blas.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 2fe1ab09..352ff593 100644 ---- a/SuiteSparse_config/SuiteSparse_config.mk -+++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -370,8 +370,8 @@ SUITESPARSE_VERSION = 5.10.1 - # command line in the Terminal, before doing 'make': - # xcode-select --install - CF += -fno-common -- BLAS ?= -framework Accelerate -- LAPACK ?= -framework Accelerate -+ #BLAS ?= -framework Accelerate -+ #LAPACK ?= -framework Accelerate - # OpenMP is not yet supported by default in clang - CFOPENMP = - LDLIBS += -rpath $(INSTALL_LIB) diff --git a/build/pkgs/suitesparse/patches/03-buildflags.patch b/build/pkgs/suitesparse/patches/03-buildflags.patch deleted file mode 100644 index 8a2c312be39..00000000000 --- a/build/pkgs/suitesparse/patches/03-buildflags.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 352ff593..61f0076e 100644 ---- a/SuiteSparse_config/SuiteSparse_config.mk -+++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -72,9 +72,9 @@ SUITESPARSE_VERSION = 5.10.1 - # which puts the libraries in /yada/mylibs, include files in /yoda/myinc, - # and documentation in /solo/mydox. - INSTALL ?= $(SUITESPARSE) -- INSTALL_LIB ?= $(INSTALL)/lib -- INSTALL_INCLUDE ?= $(INSTALL)/include -- INSTALL_DOC ?= $(INSTALL)/share/doc/suitesparse-$(SUITESPARSE_VERSION) -+ INSTALL_LIB ?= $(DESTDIR)$(INSTALL)/lib -+ INSTALL_INCLUDE ?= $(DESTDIR)$(INSTALL)/include -+ INSTALL_DOC ?= $(DESTDIR)$(INSTALL)/share/doc/suitesparse-$(SUITESPARSE_VERSION) - - #--------------------------------------------------------------------------- - # parallel make -@@ -358,7 +358,7 @@ SUITESPARSE_VERSION = 5.10.1 - - ifeq ($(UNAME),Linux) - # add the realtime library, librt, and SuiteSparse/lib -- LDLIBS += -lrt -Wl,-rpath=$(INSTALL_LIB) -+ LDLIBS += -lrt - endif - - #--------------------------------------------------------------------------- -@@ -464,7 +464,7 @@ else - SO_TARGET = $(LIBRARY).$(VERSION).dylib - SO_OPTS += -dynamiclib -compatibility_version $(SO_VERSION) \ - -current_version $(VERSION) \ -- -Wl,-install_name -Wl,@rpath/$(SO_MAIN) \ -+ -Wl,-install_name -Wl,$(INSTALL)/lib/$(SO_MAIN) \ - -shared -undefined dynamic_lookup - # When a Mac *.dylib file is moved, this command is required - # to change its internal name to match its location in the filesystem: diff --git a/build/pkgs/suitesparse/patches/04-cygwin.patch b/build/pkgs/suitesparse/patches/04-cygwin.patch deleted file mode 100644 index f4118f86670..00000000000 --- a/build/pkgs/suitesparse/patches/04-cygwin.patch +++ /dev/null @@ -1,802 +0,0 @@ -diff --git a/AMD/Lib/Makefile b/AMD/Lib/Makefile -index 3b92f6a0..582bef2a 100644 ---- a/AMD/Lib/Makefile -+++ b/AMD/Lib/Makefile -@@ -2,7 +2,7 @@ - # AMD Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libamd -+LIBRARY = amd - VERSION = 2.4.6 - SO_VERSION = 2 - -@@ -81,19 +81,22 @@ libamdf77.a: $(AMDF77) - #------------------------------------------------------------------------------- - - # install AMD --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/amd.h $(INSTALL_INCLUDE) - $(CP) ../Doc/AMD_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/AMD_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/amd.h - chmod 644 $(INSTALL_DOC)/AMD_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/AMD_README.txt -diff --git a/BTF/Lib/Makefile b/BTF/Lib/Makefile -index 85b7a264..6e4e2ec4 100644 ---- a/BTF/Lib/Makefile -+++ b/BTF/Lib/Makefile -@@ -2,7 +2,7 @@ - # BTF Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libbtf -+LIBRARY = btf - VERSION = 1.2.6 - SO_VERSION = 1 - -@@ -66,18 +66,21 @@ btf_l_strongcomp.o: ../Source/btf_strongcomp.c - #------------------------------------------------------------------------------- - - # install BTF --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/btf.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/BTF_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/btf.h - chmod 644 $(INSTALL_DOC)/BTF_README.txt - -diff --git a/CAMD/Lib/Makefile b/CAMD/Lib/Makefile -index b3ae159e..27cb072a 100644 ---- a/CAMD/Lib/Makefile -+++ b/CAMD/Lib/Makefile -@@ -2,7 +2,7 @@ - # CAMD Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libcamd -+LIBRARY = camd - VERSION = 2.4.6 - SO_VERSION = 2 - -@@ -62,19 +62,22 @@ $(AR_TARGET): $(OBJ) - #------------------------------------------------------------------------------- - - # install CAMD --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/camd.h $(INSTALL_INCLUDE) - $(CP) ../Doc/CAMD_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/CAMD_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/camd.h - chmod 644 $(INSTALL_DOC)/CAMD_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/CAMD_README.txt -diff --git a/CCOLAMD/Lib/Makefile b/CCOLAMD/Lib/Makefile -index 52c52eb9..18190be0 100644 ---- a/CCOLAMD/Lib/Makefile -+++ b/CCOLAMD/Lib/Makefile -@@ -2,7 +2,7 @@ - # CCOLAMD Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libccolamd -+LIBRARY = ccolamd - VERSION = 2.9.6 - SO_VERSION = 2 - -@@ -49,18 +49,21 @@ distclean: clean - - $(RM) -r $(PURGE) - - # install CCOLAMD --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/ccolamd.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/CCOLAMD_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/ccolamd.h - chmod 644 $(INSTALL_DOC)/CCOLAMD_README.txt - -diff --git a/CHOLMOD/Lib/Makefile b/CHOLMOD/Lib/Makefile -index bc3eab69..d240b4cd 100644 ---- a/CHOLMOD/Lib/Makefile -+++ b/CHOLMOD/Lib/Makefile -@@ -2,7 +2,7 @@ - # CHOLMOD/Lib/Makefile: for compiling the CHOLMOD library - #=============================================================================== - --LIBRARY = libcholmod -+LIBRARY = cholmod - VERSION = 3.0.14 - SO_VERSION = 3 - -@@ -535,20 +535,23 @@ cholmod_l_gpu.o: ../GPU/cholmod_gpu.c - #------------------------------------------------------------------------------- - - # install CHOLMOD --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/cholmod*.h $(INSTALL_INCLUDE) - $(RM) $(INSTALL_INCLUDE)/cholmod_internal.h - $(CP) ../Doc/CHOLMOD_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/CHOLMOD_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/cholmod*.h - chmod 644 $(INSTALL_DOC)/CHOLMOD_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/CHOLMOD_README.txt -diff --git a/COLAMD/Lib/Makefile b/COLAMD/Lib/Makefile -index 2d0c5aa6..26720b62 100644 ---- a/COLAMD/Lib/Makefile -+++ b/COLAMD/Lib/Makefile -@@ -2,7 +2,7 @@ - # COLAMD Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libcolamd -+LIBRARY = colamd - VERSION = 2.9.6 - SO_VERSION = 2 - -@@ -49,18 +49,21 @@ distclean: clean - - $(RM) -r $(PURGE) - - # install COLAMD --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/colamd.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/COLAMD_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/colamd.h - chmod 644 $(INSTALL_DOC)/COLAMD_README.txt - -diff --git a/CSparse/Lib/Makefile b/CSparse/Lib/Makefile -index e3f32cec..468f7a47 100644 ---- a/CSparse/Lib/Makefile -+++ b/CSparse/Lib/Makefile -@@ -14,7 +14,7 @@ - # install' in this Makefile only installs a static compiled library in - # CSparse/Lib. It does not install it for system-wide usage. - --LIBRARY = libcsparse -+LIBRARY = csparse - CF = $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -O - - I = -I../Include -diff --git a/CXSparse/Lib/Makefile b/CXSparse/Lib/Makefile -index a21e3607..270b8027 100644 ---- a/CXSparse/Lib/Makefile -+++ b/CXSparse/Lib/Makefile -@@ -2,7 +2,7 @@ - # CXSparse Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libcxsparse -+LIBRARY = cxsparse - VERSION = 3.2.0 - SO_VERSION = 3 - -@@ -113,18 +113,21 @@ distclean: clean - - $(RM) -r $(PURGE) - - # install CXSparse --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(CS) -+$(INSTALL_SO): $(CS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/cs.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/CXSPARSE_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/cs.h - chmod 644 $(INSTALL_DOC)/CXSPARSE_README.txt - -diff --git a/CXSparse_newfiles/Lib/Makefile b/CXSparse_newfiles/Lib/Makefile -index a21e3607..270b8027 100644 ---- a/CXSparse_newfiles/Lib/Makefile -+++ b/CXSparse_newfiles/Lib/Makefile -@@ -2,7 +2,7 @@ - # CXSparse Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libcxsparse -+LIBRARY = cxsparse - VERSION = 3.2.0 - SO_VERSION = 3 - -@@ -113,18 +113,21 @@ distclean: clean - - $(RM) -r $(PURGE) - - # install CXSparse --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(CS) -+$(INSTALL_SO): $(CS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/cs.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/CXSPARSE_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/cs.h - chmod 644 $(INSTALL_DOC)/CXSPARSE_README.txt - -diff --git a/GPUQREngine/Lib/Makefile b/GPUQREngine/Lib/Makefile -index adf46039..7bb34807 100644 ---- a/GPUQREngine/Lib/Makefile -+++ b/GPUQREngine/Lib/Makefile -@@ -2,7 +2,7 @@ - # GPUQREngine/Lib/Makefile: for compiling the GPUQREngine library - #------------------------------------------------------------------------------- - --LIBRARY = libGPUQREngine -+LIBRARY = GPUQREngine - VERSION = 1.0.5 - SO_VERSION = 1 - -@@ -129,17 +129,20 @@ $(AR_TARGET): $(OBJS) - #------------------------------------------------------------------------------- - - # install GPUQREngine. Note that the include files are not installed. --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJS) -+$(INSTALL_SO): $(OBJS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -- $(CP) ../README.txt $(INSTALL_DOC)/GPUQRENGINE_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif -+ $(CP) ../README.txt $(INSTALL_DOC)/GPUQRENGINE_README.txt - chmod 644 $(INSTALL_DOC)/GPUQRENGINE_README.txt - - # uninstall GPUQREngine -diff --git a/KLU/Lib/Makefile b/KLU/Lib/Makefile -index 2dc62cb4..35e2ed3a 100644 ---- a/KLU/Lib/Makefile -+++ b/KLU/Lib/Makefile -@@ -2,7 +2,7 @@ - # KLU Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libklu -+LIBRARY = klu - VERSION = 1.3.8 - SO_VERSION = 1 - -@@ -263,19 +263,22 @@ klu_l_memory.o: ../Source/klu_memory.c - #------------------------------------------------------------------------------- - - # install KLU --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/klu.h $(INSTALL_INCLUDE) - $(CP) ../Doc/KLU_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/KLU_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/klu.h - chmod 644 $(INSTALL_DOC)/KLU_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/KLU_README.txt -diff --git a/LDL/Lib/Makefile b/LDL/Lib/Makefile -index 604ffa27..da5ecd53 100644 ---- a/LDL/Lib/Makefile -+++ b/LDL/Lib/Makefile -@@ -2,7 +2,7 @@ - # LDL Lib/Makefile - #------------------------------------------------------------------------------- - --LIBRARY = libldl -+LIBRARY = ldl - VERSION = 2.2.6 - SO_VERSION = 2 - -@@ -46,19 +46,22 @@ clean: - - $(RM) -r $(CLEAN) - - # install LDL --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/ldl.h $(INSTALL_INCLUDE) - $(CP) ../Doc/ldl_userguide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/LDL_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/ldl.h - chmod 644 $(INSTALL_DOC)/ldl_userguide.pdf - chmod 644 $(INSTALL_DOC)/LDL_README.txt -diff --git a/RBio/Lib/Makefile b/RBio/Lib/Makefile -index 056715de..4ba42157 100644 ---- a/RBio/Lib/Makefile -+++ b/RBio/Lib/Makefile -@@ -2,7 +2,7 @@ - # RBio/Lib/Makefile: for compiling the RBio library - #=============================================================================== - --LIBRARY = librbio -+LIBRARY = rbio - VERSION = 2.2.6 - SO_VERSION = 2 - -@@ -60,18 +60,21 @@ RBio_i.o: ../Source/RBio.c - #------------------------------------------------------------------------------- - - # install RBio --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/RBio.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/RBIO_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/RBio.h - chmod 644 $(INSTALL_DOC)/RBIO_README.txt - -diff --git a/SLIP_LU/Lib/Makefile b/SLIP_LU/Lib/Makefile -index 8b2221f1..b2153824 100644 ---- a/SLIP_LU/Lib/Makefile -+++ b/SLIP_LU/Lib/Makefile -@@ -8,7 +8,7 @@ - # To run a demo using the library - # cd ../Demo ; make - --LIBRARY = libsliplu -+LIBRARY = sliplu - VERSION = 1.0.2 - SO_VERSION = 1 - -@@ -63,19 +63,22 @@ $(AR_TARGET): $(OBJ) - #------------------------------------------------------------------------------- - - # install SLIP_LU --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/SLIP_LU.h $(INSTALL_INCLUDE) - $(CP) ../Doc/SLIP_LU_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.md $(INSTALL_DOC)/SLIP_LU_README.md -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/SLIP_LU.h - chmod 644 $(INSTALL_DOC)/SLIP_LU_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/SLIP_LU_README.md -diff --git a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile -index 06c142ae..10e32007 100644 ---- a/SPQR/Lib/Makefile -+++ b/SPQR/Lib/Makefile -@@ -2,7 +2,7 @@ - # SuiteSparseQR/Lib/Makefile - #=============================================================================== - --LIBRARY = libspqr -+LIBRARY = spqr - VERSION = 2.0.9 - SO_VERSION = 2 - -@@ -242,22 +242,25 @@ spqrgpu_computeFrontStaging.o: ../SPQRGPU/spqrgpu_computeFrontStaging.cpp - #------------------------------------------------------------------------------- - - # install SPQR --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/SuiteSparseQR.hpp $(INSTALL_INCLUDE) - $(CP) ../Include/SuiteSparseQR_C.h $(INSTALL_INCLUDE) - $(CP) ../Include/SuiteSparseQR_definitions.h $(INSTALL_INCLUDE) - $(CP) ../Include/spqr.hpp $(INSTALL_INCLUDE) - $(CP) ../Doc/spqr_user_guide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/SPQR_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR.hpp - chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR_C.h - chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR_definitions.h -diff --git a/SuiteSparse_GPURuntime/Lib/Makefile b/SuiteSparse_GPURuntime/Lib/Makefile -index 31f536a5..2f6c54d0 100644 ---- a/SuiteSparse_GPURuntime/Lib/Makefile -+++ b/SuiteSparse_GPURuntime/Lib/Makefile -@@ -2,7 +2,7 @@ - # SuiteSparse_GPURuntime/Lib/Makfile - #------------------------------------------------------------------------------- - --LIBRARY = libSuiteSparse_GPURuntime -+LIBRARY = SuiteSparse_GPURuntime - VERSION = 1.0.5 - SO_VERSION = 1 - -@@ -70,17 +70,20 @@ SuiteSparseGPU_Workspace_transfer.o: ../Source/SuiteSparseGPU_Workspace_transfer - #------------------------------------------------------------------------------- - - # install SuiteSparse_GPURuntime (just the library, not the include files) --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJS) -+$(INSTALL_SO): $(OBJS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -- $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif -+ $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt - chmod 644 $(INSTALL_DOC)/GPURUNTIME_README.txt - - # uninstall SuiteSparse_GPURuntime -diff --git a/SuiteSparse_config/Makefile b/SuiteSparse_config/Makefile -index 6a5814d4..e2acc53e 100644 ---- a/SuiteSparse_config/Makefile -+++ b/SuiteSparse_config/Makefile -@@ -6,7 +6,7 @@ SUITESPARSE ?= $(realpath $(CURDIR)/..) - export SUITESPARSE - - # version of SuiteSparse_config is also version of SuiteSparse meta-package --LIBRARY = libsuitesparseconfig -+LIBRARY = suitesparseconfig - VERSION = 5.10.1 - SO_VERSION = 5 - -@@ -44,19 +44,23 @@ clean: - - $(RM) -r $(CLEAN) - - # install SuiteSparse_config --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+# Likely redundant with the above but not on Cygwin -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -- $(CP) SuiteSparse_config.h $(INSTALL_INCLUDE) -- $(CP) README.txt $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 755 $(INSTALL_LIB)/$(SO_PLAIN) -+endif -+ $(CP) SuiteSparse_config.h $(INSTALL_INCLUDE) -+ $(CP) README.txt $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt - chmod 644 $(INSTALL_INCLUDE)/SuiteSparse_config.h - chmod 644 $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt - -diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 61f0076e..5caa89dd 100644 ---- a/SuiteSparse_config/SuiteSparse_config.mk -+++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -16,14 +16,14 @@ SUITESPARSE_VERSION = 5.10.1 - # To disable these auto configurations, use 'make UNAME=custom' - - ifndef UNAME -- ifeq ($(OS),Windows_NT) -- # Cygwin Make on Windows has an $(OS) variable, but not uname. -- # Note that this option is untested. -- UNAME = Windows -- else -- # Linux and Darwin (Mac OSX) have been tested. -- UNAME := $(shell uname) -- endif -+ # Linux and Darwin (Mac OSX) have been tested. -+ UNAME := $(shell uname) -+ endif -+ -+ # On Cygwin we'll typically have UNAME=CYGWIN but just normalize -+ # to "CYGWIN" -+ ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) -+ UNAME := Cygwin - endif - - #=============================================================================== -@@ -170,6 +170,11 @@ SUITESPARSE_VERSION = 5.10.1 - # It places its shared *.so libraries in SuiteSparse/lib. - # Linux also requires the -lrt library (see below) - LDLIBS ?= -lm -+ # Note: Because suitesparse doesn't really do install staging properly -+ # (it just outputs final build artifacts directly to their install paths) -+ # we must add that path to our linker flags in order to link properly -+ # against built libraries; it might be better to fix the whole build -+ # system though). - LDFLAGS += -L$(INSTALL_LIB) - - # NOTE: Use of the Intel MKL BLAS is strongly recommended. The OpenBLAS can -@@ -447,21 +452,23 @@ SUITESPARSE_VERSION = 5.10.1 - - SO_OPTS = $(LDFLAGS) - --ifeq ($(UNAME),Windows) -- # Cygwin Make on Windows (untested) -- AR_TARGET = $(LIBRARY).lib -- SO_PLAIN = $(LIBRARY).dll -- SO_MAIN = $(LIBRARY).$(SO_VERSION).dll -- SO_TARGET = $(LIBRARY).$(VERSION).dll -- SO_INSTALL_NAME = echo -+ifeq ($(UNAME),Cygwin) -+ # Cygwin Make on Windows -+ AR_TARGET = lib$(LIBRARY).a -+ SO_TARGET = cyg$(LIBRARY)-$(SO_VERSION).dll -+ IMPLIB = lib$(LIBRARY).dll.a -+ SO_OPTS += -shared -Wl,--no-undefined -Wl,--out-implib -Wl,$(INSTALL_LIB)/$(IMPLIB) -+ INSTALL_DLL := $(DESTDIR)$(INSTALL)/bin -+ INSTALL_SO = $(INSTALL_DLL)/$(SO_TARGET) - else - # Mac or Linux/Unix -- AR_TARGET = $(LIBRARY).a -+ AR_TARGET = lib$(LIBRARY).a -+ INSTALL_SO = $(INSTALL_LIB)/$(SO_TARGET) - ifeq ($(UNAME),Darwin) - # Mac -- SO_PLAIN = $(LIBRARY).dylib -- SO_MAIN = $(LIBRARY).$(SO_VERSION).dylib -- SO_TARGET = $(LIBRARY).$(VERSION).dylib -+ SO_PLAIN = lib$(LIBRARY).dylib -+ SO_MAIN = lib$(LIBRARY).$(SO_VERSION).dylib -+ SO_TARGET = lib$(LIBRARY).$(VERSION).dylib - SO_OPTS += -dynamiclib -compatibility_version $(SO_VERSION) \ - -current_version $(VERSION) \ - -Wl,-install_name -Wl,$(INSTALL)/lib/$(SO_MAIN) \ -@@ -471,9 +478,9 @@ else - SO_INSTALL_NAME = install_name_tool -id - else - # Linux and other variants of Unix -- SO_PLAIN = $(LIBRARY).so -- SO_MAIN = $(LIBRARY).so.$(SO_VERSION) -- SO_TARGET = $(LIBRARY).so.$(VERSION) -+ SO_PLAIN = lib$(LIBRARY).so -+ SO_MAIN = lib$(LIBRARY).so.$(SO_VERSION) -+ SO_TARGET = lib$(LIBRARY).so.$(VERSION) - SO_OPTS += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined - # Linux/Unix *.so files can be moved without modification: - SO_INSTALL_NAME = echo -diff --git a/SuiteSparse_config/xerbla/Makefile b/SuiteSparse_config/xerbla/Makefile -index db68a2ea..c59820c2 100644 ---- a/SuiteSparse_config/xerbla/Makefile -+++ b/SuiteSparse_config/xerbla/Makefile -@@ -16,9 +16,9 @@ library: - all: library - - ifeq ($(USE_FORTRAN),0) -- LIBRARY = libcerbla -+ LIBRARY = cerbla - else -- LIBRARY = libxerbla -+ LIBRARY = xerbla - endif - - include ../SuiteSparse_config.mk -@@ -44,19 +44,22 @@ $(AR_TARGET): $(DEPENDS) - - $(RM) xerbla.o - - # install libcerbla / libxerbla --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(DEPENDS) -+$(INSTALL_SO): $(DEPENDS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(COMPILE) - $(CC) $(SO_OPTS) xerbla.o -o $@ - - $(RM) xerbla.o -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -- $(CP) xerbla.h $(INSTALL_INCLUDE) - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif -+ $(CP) xerbla.h $(INSTALL_INCLUDE) - chmod 644 $(INSTALL_INCLUDE)/xerbla.h - - # uninstall libcerbla / libxerbla -diff --git a/UMFPACK/Lib/Makefile b/UMFPACK/Lib/Makefile -index 78436de1..0f291068 100644 ---- a/UMFPACK/Lib/Makefile -+++ b/UMFPACK/Lib/Makefile -@@ -2,7 +2,7 @@ - # UMFPACK Makefile for compiling on Unix systems - #------------------------------------------------------------------------------- - --LIBRARY = libumfpack -+LIBRARY = umfpack - VERSION = 5.7.9 - SO_VERSION = 5 - -@@ -288,20 +288,23 @@ clean: - - #------------------------------------------------------------------------------- - # install UMFPACK --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) -+$(INSTALL_SO): $(OBJ) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -+ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif - $(CP) ../Include/umfpack*.h $(INSTALL_INCLUDE) - $(CP) ../Doc/UMFPACK_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../Doc/UMFPACK_QuickStart.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/UMFPACK_README.txt -- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/umfpack*.h - chmod 644 $(INSTALL_DOC)/UMFPACK_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/UMFPACK_QuickStart.pdf diff --git a/build/pkgs/suitesparse/spkg-install.in b/build/pkgs/suitesparse/spkg-install.in index b6944a409ba..f7ef16bb48b 100644 --- a/build/pkgs/suitesparse/spkg-install.in +++ b/build/pkgs/suitesparse/spkg-install.in @@ -1,14 +1,21 @@ -# -*- shell-script -*- cd src -export CC="${CC-gcc} -std=c99" - -echo "print configuration" -$MAKE MY_METIS_LIB=none CHOLMOD_CONFIG=-DNPARTITION \ - BLAS="$(pkg-config --libs blas)" LAPACK="$(pkg-config --libs lapack)" \ - INSTALL="$SAGE_LOCAL" DESTDIR="$SAGE_DESTDIR" config -# build and install -$MAKE MY_METIS_LIB=none CHOLMOD_CONFIG=-DNPARTITION \ - BLAS="$(pkg-config --libs blas)" LAPACK="$(pkg-config --libs lapack)" \ - INSTALL="$SAGE_LOCAL" DESTDIR="$SAGE_DESTDIR" install +echo "Configuring suitesparse" +# * NSTATIC suitesparse defines some negative options like this one "NSTATIC=ON" means no static libraries. +# Hopefully these sill be normalised in the future. +# * SUITESPARSE_INCLUDEDIR_POSTFIX sets the subfolder in which to install headers. +# It default to "suitesparse" if not defined, which currently breaks dependencies. +# * SUITESPARSE_USE_FORTRAN make sure the fortran interface is off. There is trouble when +# gcc and gfortran version are not matching. +# * SUITESPARSE_ENABLE_PROJECTS semi column separated list of the desired packages. Default is +# all the packages in the suitesparse tarball. +# On macOS ARM cvxopt does not start if suitesparse uses @rpath, set explicit install name dir instead +sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_INSTALL_NAME_DIR="$SAGE_LOCAL/lib" \ + -DCMAKE_MACOSX_RPATH=OFF \ + -DNSTATIC=ON \ + -DSUITESPARSE_USE_FORTRAN=OFF \ + -DSUITESPARSE_INCLUDEDIR_POSTFIX="" \ + -DSUITESPARSE_ENABLE_PROJECTS="suitesparse_config;amd;camd;ccolamd;colamd;cholmod;umfpack" +sdh_make_install diff --git a/build/pkgs/symengine_py/checksums.ini b/build/pkgs/symengine_py/checksums.ini index b8984f6553c..243b69d4a3e 100644 --- a/build/pkgs/symengine_py/checksums.ini +++ b/build/pkgs/symengine_py/checksums.ini @@ -1,4 +1,4 @@ tarball=symengine.py-VERSION.tar.gz sha1=4a8da0d0a057c8709c5b28543dbb3d26a060f013 sha256=0dd30d29b804ebb7251bddec29c38c3b1fc15ea6953a2c57ee758d5f6fcba458 -upstream_url=https://pypi.io/packages/source/s/symengine/symengine-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/s/symengine/symengine-VERSION.tar.gz diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index 30e67c20de6..a03797698b1 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,4 +1,4 @@ tarball=sympy-VERSION-py3-none-any.whl -sha1=80fa00ab605295d61992ca3faa76771a62944527 -sha256=9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515 -upstream_url=https://pypi.io/packages/py3/s/sympy/sympy-VERSION-py3-none-any.whl +sha1=e34c28a2aa2b677efe2f1b7cefe275e20d2e652c +sha256=c51d75517712f1aed280d4ce58506a4a88d635d6b5dd48b39102a7ae1f3fcfe9 +upstream_url=https://files.pythonhosted.org/packages/py3/s/sympy/sympy-VERSION-py3-none-any.whl diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index f8f4f03b3dc..61ce01b3011 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -1.12.1 +1.13.2 diff --git a/build/pkgs/tachyon/package-version.txt b/build/pkgs/tachyon/package-version.txt index 2d666706df4..fcdf7816d66 100644 --- a/build/pkgs/tachyon/package-version.txt +++ b/build/pkgs/tachyon/package-version.txt @@ -1 +1 @@ -0.99.5 +0.99.5.p0 diff --git a/build/pkgs/tachyon/patches/Make-config.patch b/build/pkgs/tachyon/patches/Make-config.patch deleted file mode 100644 index 49462387134..00000000000 --- a/build/pkgs/tachyon/patches/Make-config.patch +++ /dev/null @@ -1,56 +0,0 @@ ---- a/unix/Make-config 2011-03-13 11:01:07.000000000 +0000 -+++ b/unix/Make-config 2016-07-04 15:29:57.982923000 +0000 -@@ -122,29 +122,25 @@ - # PNG support configuration: - # PNGINC is the directory where your libpng and libz include files - # are made available. PNGLIB is the directory where your libpng --# and libz libraries are made available. -+# and libz libraries are made available. - # - # LibPNG can be downlaoded from: - # http://www.libpng.org/ - ########################################################################## - # Uncomment the following lines to disable PNG support --USEPNG= --PNGINC= --PNGLIB= -+USEPNG= -DUSEPNG -+PNGINC= -I$(SAGE_LOCAL)/include -+PNGLIB= -L$(SAGE_LOCAL)/lib -lpng -lz - --# Uncomment the following lines to enable PNG support --#USEPNG= -DUSEPNG --#PNGINC= -I/usr/local/include --#PNGLIB= -L/usr/local/lib -lpng -lz - - - ########################################################################## --# OMF (Open Media Framework) configuration -+# OMF (Open Media Framework) configuration - # Requires OMF Toolkit version 2.x - ########################################################################## - #OMFDIR = /disk5/users/johns/graphics/OMFKT202/Toolkit - #OMFINC = -I$(OMFDIR)/include -I$(OMFDIR)/kitomfi -I$(OMFDIR)/bento -I$(OMFDIR)/jpeg -I$(OMFDIR)/portinc -I$(OMFDIR)/avidjpg --#OMFLIB = -L$(OMFDIR)/DO_sun5_opt/usr/lib -lAJPG -lOMFI -lbento -ljpeg -+#OMFLIB = -L$(OMFDIR)/DO_sun5_opt/usr/lib -lAJPG -lOMFI -lbento -ljpeg - #OMFDEF = -DUSEOMF - - -@@ -159,7 +155,7 @@ - - ########################################################################## - # Spaceball I/O library configuration: --# A spaceball can used for fly-throughs of scenes when running on -+# A spaceball can used for fly-throughs of scenes when running on - # a fast multiprocessor, parallel machine, or PC cluster. - # - # Libsball can be downloaded from: -@@ -172,7 +168,7 @@ - - ########################################################################## - # MGF Materials and Geometry Format scene parser library --# If enabled, this allows Tachyon to read MGF scene files using -+# If enabled, this allows Tachyon to read MGF scene files using - # compiled-in MGF scene parser code. - ########################################################################## - #MGFDIR=../../libmgf diff --git a/build/pkgs/tachyon/spkg-install.in b/build/pkgs/tachyon/spkg-install.in index dc1ebb49740..6a68d95688c 100644 --- a/build/pkgs/tachyon/spkg-install.in +++ b/build/pkgs/tachyon/spkg-install.in @@ -52,7 +52,8 @@ if [ -z "$TARGET" ]; then sdh_die "Error: Sorry, your platform isn't supported by Tachyon and/or Sage. Exiting..." fi -sdh_make "$TARGET" +# The Makefile ignores LDFLAGS; we include it here so that rpath is set on Linux +sdh_make "$TARGET" USEPNG=-DUSEPNG PNGLIB="$LDFLAGS -lpng -lz" echo "Installing the Tachyon binary..." cd "$CUR" diff --git a/build/pkgs/terminado/checksums.ini b/build/pkgs/terminado/checksums.ini index ce9c0407bad..e54d9b51727 100644 --- a/build/pkgs/terminado/checksums.ini +++ b/build/pkgs/terminado/checksums.ini @@ -1,4 +1,4 @@ tarball=terminado-VERSION.tar.gz sha1=608fcc44b845e1fb783e361d59e79fba83126e14 sha256=6ccbbcd3a4f8a25a5ec04991f39a0b8db52dfcd487ea0e578d977e6752380333 -upstream_url=https://pypi.io/packages/source/t/terminado/terminado-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/t/terminado/terminado-VERSION.tar.gz diff --git a/build/pkgs/texttable/checksums.ini b/build/pkgs/texttable/checksums.ini index 3446dba447f..c83680e54a9 100644 --- a/build/pkgs/texttable/checksums.ini +++ b/build/pkgs/texttable/checksums.ini @@ -1,4 +1,4 @@ tarball=texttable-VERSION.tar.gz sha1=0fa175fa6e0fefea31434746641bedc8cbb60248 sha256=2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638 -upstream_url=https://pypi.io/packages/source/t/texttable/texttable-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/t/texttable/texttable-VERSION.tar.gz diff --git a/build/pkgs/tinycss2/checksums.ini b/build/pkgs/tinycss2/checksums.ini index a6abd957086..b649e4f8b3e 100644 --- a/build/pkgs/tinycss2/checksums.ini +++ b/build/pkgs/tinycss2/checksums.ini @@ -1,4 +1,4 @@ tarball=tinycss2-VERSION.tar.gz sha1=3871ffec30bde346d1a17f80a423dce488bad4f7 sha256=8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627 -upstream_url=https://pypi.io/packages/source/t/tinycss2/tinycss2-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/t/tinycss2/tinycss2-VERSION.tar.gz diff --git a/build/pkgs/tomli/checksums.ini b/build/pkgs/tomli/checksums.ini index 00d70337b2b..0837611ad26 100644 --- a/build/pkgs/tomli/checksums.ini +++ b/build/pkgs/tomli/checksums.ini @@ -1,4 +1,4 @@ tarball=tomli-VERSION-py3-none-any.whl sha1=5bfc83c14bc54e6193a0d50a50c16a88eda0c4fa sha256=939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc -upstream_url=https://pypi.io/packages/py3/t/tomli/tomli-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/t/tomli/tomli-VERSION-py3-none-any.whl diff --git a/build/pkgs/tornado/checksums.ini b/build/pkgs/tornado/checksums.ini index 9cc0e020c01..c11b8fec974 100644 --- a/build/pkgs/tornado/checksums.ini +++ b/build/pkgs/tornado/checksums.ini @@ -1,4 +1,4 @@ tarball=tornado-VERSION.tar.gz sha1=5b4036313660a74034186ac63b10d244ca9444b8 sha256=72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee -upstream_url=https://pypi.io/packages/source/t/tornado/tornado-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/t/tornado/tornado-VERSION.tar.gz diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini index fa5a87fb673..713b1eb18fe 100644 --- a/build/pkgs/tox/checksums.ini +++ b/build/pkgs/tox/checksums.ini @@ -1,4 +1,4 @@ tarball=tox-VERSION-py3-none-any.whl sha1=d3312285c4988d3307d3b000a8a18cfcb16aea29 sha256=da761b4a57ee2b92b5ce39f48ff723fc42d185bf2af508effb683214efa662ea -upstream_url=https://pypi.io/packages/py3/t/tox/tox-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/t/tox/tox-VERSION-py3-none-any.whl diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index ad86191465c..a56a3cea9e1 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,4 +1,4 @@ tarball=traitlets-VERSION-py3-none-any.whl sha1=a6c667ce2b3adf2ab3562f144067f02326383c25 sha256=b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f -upstream_url=https://pypi.io/packages/py3/t/traitlets/traitlets-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/t/traitlets/traitlets-VERSION-py3-none-any.whl diff --git a/build/pkgs/trove_classifiers/checksums.ini b/build/pkgs/trove_classifiers/checksums.ini index 8d25c121cc2..ab2559ee8c7 100644 --- a/build/pkgs/trove_classifiers/checksums.ini +++ b/build/pkgs/trove_classifiers/checksums.ini @@ -1,4 +1,4 @@ tarball=trove_classifiers-VERSION-py3-none-any.whl -sha1=36240d053d16400380aee01f0879785693008a96 -sha256=678bd6fcc5218d72e3304e27a608acc9b91e17bd00c3f3d8c968497c843ad98b -upstream_url=https://pypi.io/packages/py3/t/trove_classifiers/trove_classifiers-VERSION-py3-none-any.whl +sha1=8219f839a8223a9dd0912cde22d579cfa75a516e +sha256=ccc57a33717644df4daca018e7ec3ef57a835c48e96a1e71fc07eb7edac67af6 +upstream_url=https://files.pythonhosted.org/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 c296ac66b65..981877628c9 100644 --- a/build/pkgs/trove_classifiers/package-version.txt +++ b/build/pkgs/trove_classifiers/package-version.txt @@ -1 +1 @@ -2024.4.10 +2024.7.2 diff --git a/build/pkgs/types_python_dateutil/checksums.ini b/build/pkgs/types_python_dateutil/checksums.ini index 08fb0805291..71ea4d06e2c 100644 --- a/build/pkgs/types_python_dateutil/checksums.ini +++ b/build/pkgs/types_python_dateutil/checksums.ini @@ -1,4 +1,4 @@ tarball=types_python_dateutil-VERSION-py3-none-any.whl sha1=fc0a6cbd54667dd8dadb95c448014efe66c9ae0a sha256=6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b -upstream_url=https://pypi.io/packages/py3/t/types_python_dateutil/types_python_dateutil-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/t/types_python_dateutil/types_python_dateutil-VERSION-py3-none-any.whl diff --git a/build/pkgs/typing_extensions/checksums.ini b/build/pkgs/typing_extensions/checksums.ini index c22c17569af..52a4f6256ea 100644 --- a/build/pkgs/typing_extensions/checksums.ini +++ b/build/pkgs/typing_extensions/checksums.ini @@ -1,4 +1,4 @@ tarball=typing_extensions-VERSION-py3-none-any.whl -sha1=049c6031f754e1c33932ce1c2ad78b857a70a244 -sha256=b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594 -upstream_url=https://pypi.io/packages/py3/t/typing_extensions/typing_extensions-VERSION-py3-none-any.whl +sha1=0fb5b2732cc421561b1348cac1334eb6a4e0bb7f +sha256=04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d +upstream_url=https://files.pythonhosted.org/packages/py3/t/typing_extensions/typing_extensions-VERSION-py3-none-any.whl diff --git a/build/pkgs/typing_extensions/package-version.txt b/build/pkgs/typing_extensions/package-version.txt index 815588ef140..f1cd7de1de5 100644 --- a/build/pkgs/typing_extensions/package-version.txt +++ b/build/pkgs/typing_extensions/package-version.txt @@ -1 +1 @@ -4.12.0 +4.12.2 diff --git a/build/pkgs/tzdata/checksums.ini b/build/pkgs/tzdata/checksums.ini index 449a195f648..334ff50b979 100644 --- a/build/pkgs/tzdata/checksums.ini +++ b/build/pkgs/tzdata/checksums.ini @@ -1,4 +1,4 @@ tarball=tzdata-VERSION-py2.py3-none-any.whl sha1=4686c7c91a01d5af9075903937c343afa05c141b sha256=7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda -upstream_url=https://pypi.io/packages/py2.py3/t/tzdata/tzdata-VERSION-py2.py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py2.py3/t/tzdata/tzdata-VERSION-py2.py3-none-any.whl diff --git a/build/pkgs/tzlocal/checksums.ini b/build/pkgs/tzlocal/checksums.ini index 5c246c69ced..7618e395db1 100644 --- a/build/pkgs/tzlocal/checksums.ini +++ b/build/pkgs/tzlocal/checksums.ini @@ -1,4 +1,4 @@ tarball=tzlocal-VERSION.tar.gz sha1=1d61e52edddf882c9af4f5f3f1be0db3788dd7b5 sha256=46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803 -upstream_url=https://pypi.io/packages/source/t/tzlocal/tzlocal-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/t/tzlocal/tzlocal-VERSION.tar.gz diff --git a/build/pkgs/uri_template/checksums.ini b/build/pkgs/uri_template/checksums.ini index 601b51db908..1ffd9d33334 100644 --- a/build/pkgs/uri_template/checksums.ini +++ b/build/pkgs/uri_template/checksums.ini @@ -1,4 +1,4 @@ tarball=uri_template-VERSION-py3-none-any.whl sha1=bbc8808bdb7e687f0c099c8120cd901dc90bce69 sha256=a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363 -upstream_url=https://pypi.io/packages/py3/u/uri_template/uri_template-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/u/uri_template/uri_template-VERSION-py3-none-any.whl diff --git a/build/pkgs/urllib3/checksums.ini b/build/pkgs/urllib3/checksums.ini index 1fbf6f0991c..3689a94a382 100644 --- a/build/pkgs/urllib3/checksums.ini +++ b/build/pkgs/urllib3/checksums.ini @@ -1,4 +1,4 @@ tarball=urllib3-VERSION-py3-none-any.whl sha1=1e197082cd0d0f98bc97f2fbfd7d2a597e3ff3e4 sha256=55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 -upstream_url=https://pypi.io/packages/py3/u/urllib3/urllib3-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/u/urllib3/urllib3-VERSION-py3-none-any.whl diff --git a/build/pkgs/valgrind/SPKG.rst b/build/pkgs/valgrind/SPKG.rst index 99a7ab96ed2..251e871a1a8 100644 --- a/build/pkgs/valgrind/SPKG.rst +++ b/build/pkgs/valgrind/SPKG.rst @@ -4,9 +4,6 @@ valgrind: Memory error detector, call graph generator, runtime profiler Description ----------- -This is an optional spkg. It supports Linux on x86, x86-64, ppc, ppc64 -and ARM as well as Darwin (Mac OS X 10.5 and 10.6) on x86 and x86-64. - Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in @@ -18,10 +15,7 @@ branch-prediction profiler, a call-graph generating cache and branch-prediction profiler, and a heap profiler. It also includes three experimental tools: a heap/stack/global array overrun detector, a second heap profiler that examines how heap blocks are used, and a SimPoint -basic block vector generator. It runs on the following platforms: -X86/Linux, AMD64/Linux, ARM/Linux, PPC32/Linux, PPC64/Linux, -S390X/Linux, ARM/Android (2.3.x), X86/Darwin and AMD64/Darwin (Mac OS X -10.6 and 10.7). +basic block vector generator. License ------- @@ -35,20 +29,3 @@ Upstream Contact - http://www.valgrind.org/ - valgrind-user, valgrind-devel mailing lists - -Dependencies ------------- - -- None - - -Special Build Instructions --------------------------- - -- To build on OS X, you need to use Apple's compiler. FSF GCC is - unsupported. - -Patches -~~~~~~~ - -- None. diff --git a/build/pkgs/valgrind/checksums.ini b/build/pkgs/valgrind/checksums.ini deleted file mode 100644 index 3ffe6ab866a..00000000000 --- a/build/pkgs/valgrind/checksums.ini +++ /dev/null @@ -1,3 +0,0 @@ -tarball=valgrind-VERSION.tar.bz2 -sha1=182afd405b92ddb6f52c6729e848eacf4b1daf46 -sha256=037c11bfefd477cc6e9ebe8f193bb237fe397f7ce791b4a4ce3fa1c6a520baa5 diff --git a/build/pkgs/valgrind/dependencies b/build/pkgs/valgrind/dependencies deleted file mode 100644 index 4f00de20375..00000000000 --- a/build/pkgs/valgrind/dependencies +++ /dev/null @@ -1,4 +0,0 @@ -# no dependencies - ----------- -All lines of this file are ignored except the first. diff --git a/build/pkgs/valgrind/package-version.txt b/build/pkgs/valgrind/package-version.txt deleted file mode 100644 index f982feb41bd..00000000000 --- a/build/pkgs/valgrind/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.14.0 diff --git a/build/pkgs/valgrind/spkg-install.in b/build/pkgs/valgrind/spkg-install.in deleted file mode 100644 index 2aa2762a7ed..00000000000 --- a/build/pkgs/valgrind/spkg-install.in +++ /dev/null @@ -1,20 +0,0 @@ -cd src - -./configure --prefix=$SAGE_LOCAL -if [ $? -ne 0 ]; then - echo >&2 "Error configuring Valgrind" - exit 1 -fi - -$MAKE -if [ $? -ne 0 ]; then - echo >&2 "Error building Valgrind" - exit 1 -fi - -$MAKE install -if [ $? -ne 0 ]; then - echo >&2 "Error installing Valgrind" - exit 1 -fi - diff --git a/build/pkgs/valgrind/type b/build/pkgs/valgrind/type index 9839eb20815..134d9bc32d5 100644 --- a/build/pkgs/valgrind/type +++ b/build/pkgs/valgrind/type @@ -1 +1 @@ -experimental +optional diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini index c83cfa5112d..ebe17fab050 100644 --- a/build/pkgs/virtualenv/checksums.ini +++ b/build/pkgs/virtualenv/checksums.ini @@ -1,4 +1,4 @@ tarball=virtualenv-VERSION-py3-none-any.whl sha1=f18ea5b827ba66a1724769371c6912601ec4d647 sha256=a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b -upstream_url=https://pypi.io/packages/py3/v/virtualenv/virtualenv-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/v/virtualenv/virtualenv-VERSION-py3-none-any.whl diff --git a/build/pkgs/wcwidth/checksums.ini b/build/pkgs/wcwidth/checksums.ini index 18801e12105..169913604d5 100644 --- a/build/pkgs/wcwidth/checksums.ini +++ b/build/pkgs/wcwidth/checksums.ini @@ -1,4 +1,4 @@ tarball=wcwidth-VERSION.tar.gz sha1=49bdbcac346f31be8201c663082331b693264382 sha256=f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02 -upstream_url=https://pypi.io/packages/source/w/wcwidth/wcwidth-VERSION.tar.gz +upstream_url=https://files.pythonhosted.org/packages/source/w/wcwidth/wcwidth-VERSION.tar.gz diff --git a/build/pkgs/webcolors/checksums.ini b/build/pkgs/webcolors/checksums.ini index 2db93e1e869..2b483c8d29b 100644 --- a/build/pkgs/webcolors/checksums.ini +++ b/build/pkgs/webcolors/checksums.ini @@ -1,4 +1,4 @@ tarball=webcolors-VERSION-py3-none-any.whl sha1=e13a9143964b824fc4972b60eddd8115f6839a26 sha256=29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf -upstream_url=https://pypi.io/packages/py3/w/webcolors/webcolors-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/w/webcolors/webcolors-VERSION-py3-none-any.whl diff --git a/build/pkgs/websocket_client/checksums.ini b/build/pkgs/websocket_client/checksums.ini index 6cc10d23812..0ccb613fdd6 100644 --- a/build/pkgs/websocket_client/checksums.ini +++ b/build/pkgs/websocket_client/checksums.ini @@ -1,4 +1,4 @@ tarball=websocket_client-VERSION-py3-none-any.whl sha1=eb78bd39f1ae4d531cc965bd21d121ba3d156f84 sha256=084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24 -upstream_url=https://pypi.io/packages/py3/w/websocket_client/websocket_client-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/w/websocket_client/websocket_client-VERSION-py3-none-any.whl diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini index ea4d40c1f27..67624baa8b2 100644 --- a/build/pkgs/wheel/checksums.ini +++ b/build/pkgs/wheel/checksums.ini @@ -1,4 +1,4 @@ tarball=wheel-VERSION-py3-none-any.whl -sha1=71a83a2237cb57ab45bdafed364564e36ca5dc95 -sha256=55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81 -upstream_url=https://pypi.io/packages/py3/w/wheel/wheel-VERSION-py3-none-any.whl +sha1=65ec55742da04152c8b06d6586fb36d779d7883e +sha256=2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f +upstream_url=https://files.pythonhosted.org/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 8298bb08b2d..a8ab6c9666a 100644 --- a/build/pkgs/wheel/package-version.txt +++ b/build/pkgs/wheel/package-version.txt @@ -1 +1 @@ -0.43.0 +0.44.0 diff --git a/build/pkgs/widgetsnbextension/checksums.ini b/build/pkgs/widgetsnbextension/checksums.ini index 099c0acb09e..3fc58cecc14 100644 --- a/build/pkgs/widgetsnbextension/checksums.ini +++ b/build/pkgs/widgetsnbextension/checksums.ini @@ -1,4 +1,4 @@ tarball=widgetsnbextension-VERSION-py3-none-any.whl sha1=067535b5d1738a4de0abb5f1219581a4a66d243c sha256=91452ca8445beb805792f206e560c1769284267a30ceb1cec9f5bcc887d15175 -upstream_url=https://pypi.io/packages/py3/w/widgetsnbextension/widgetsnbextension-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/w/widgetsnbextension/widgetsnbextension-VERSION-py3-none-any.whl diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini index 1af443d7f19..5fc5129cab4 100644 --- a/build/pkgs/zipp/checksums.ini +++ b/build/pkgs/zipp/checksums.ini @@ -1,4 +1,4 @@ tarball=zipp-VERSION-py3-none-any.whl sha1=22cf149f293964ac7538ad41af8caa9aacf8fe1a sha256=96dc6ad62f1441bcaccef23b274ec471518daf4fbbc580341204936a5a3dddec -upstream_url=https://pypi.io/packages/py3/z/zipp/zipp-VERSION-py3-none-any.whl +upstream_url=https://files.pythonhosted.org/packages/py3/z/zipp/zipp-VERSION-py3-none-any.whl diff --git a/build/pkgs/zlib/spkg-configure.m4 b/build/pkgs/zlib/spkg-configure.m4 index c2e3b7059c0..adb36006813 100644 --- a/build/pkgs/zlib/spkg-configure.m4 +++ b/build/pkgs/zlib/spkg-configure.m4 @@ -1,7 +1,8 @@ SAGE_SPKG_CONFIGURE([zlib], [ + PKG_CHECK_MODULES([zlib], [zlib >= 1.2.11], [zlib_cv_pc=yes], [zlib_cv_pc=no]) AC_CHECK_LIB([z], [inflateEnd], [zlib_cv_libz=yes], [zlib_cv_libz=no]) AC_CHECK_HEADER([zlib.h], [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no]) - if test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes"; then + if test "$zlib_cv_pc" = "yes" && test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes"; then PKG_CHECK_MODULES([LIBPNG], [libpng >= 1.2], [], [ dnl inflateValidate is needed for Sage's libpng, newer than 1.2; this ensures dnl we have the minimum required for building zlib version diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 2b8b44959b1..c5dac654cc5 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -430,9 +430,9 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre tarball = pypi_version.tarball.replace(pypi_version.version, 'VERSION') if not version: version = pypi_version.version - # Use a URL from pypi.io instead of the specific URL received from the PyPI query + # Use a URL from files.pythonhosted.org instead of the specific URL received from the PyPI query # because it follows a simple pattern. - upstream_url = 'https://pypi.io/packages/source/{0:1.1}/{0}/{1}'.format(package_name, tarball) + upstream_url = 'https://files.pythonhosted.org/packages/source/{0:1.1}/{0}/{1}'.format(package_name, tarball) elif source == 'wheel': if not tarball: tarball = pypi_version.tarball.replace(pypi_version.version, 'VERSION') @@ -459,7 +459,7 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre 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) + upstream_url = 'https://files.pythonhosted.org/packages/{2}/{0:1.1}/{0}/{1}'.format(package_name, tarball, pypi_version.python_version) if not description: description = pypi_version.summary if not license: diff --git a/build/sage_bootstrap/compat/__init__.py b/build/sage_bootstrap/compat/__init__.py index 7fb303e4edc..153820ad1b6 100644 --- a/build/sage_bootstrap/compat/__init__.py +++ b/build/sage_bootstrap/compat/__init__.py @@ -28,3 +28,11 @@ from StringIO import StringIO except ImportError: from io import StringIO + + +try: + # Use this for Python 3. This function is available for Python >= 3.3 + from shlex import quote +except ImportError: + # Use this for Python 2. This function is available for Python < 3.13 + from pipes import quote diff --git a/build/sage_bootstrap/flock.py b/build/sage_bootstrap/flock.py index 483482a6edf..bc95a0ecf8f 100644 --- a/build/sage_bootstrap/flock.py +++ b/build/sage_bootstrap/flock.py @@ -12,10 +12,11 @@ import fcntl import os -import pipes import sys import argparse +from sage_bootstrap.compat import quote + class FileType(argparse.FileType): """ @@ -105,7 +106,7 @@ def run(argv=None): kind = "exclusive" sys.stderr.write("Waiting for {0} lock to run {1} ... ".format( - kind, ' '.join(pipes.quote(arg) for arg in command))) + kind, ' '.join(quote(arg) for arg in command))) fcntl.flock(lock, locktype) sys.stderr.write("ok\n") diff --git a/configure.ac b/configure.ac index df6a785251e..5c12ff36ac9 100644 --- a/configure.ac +++ b/configure.ac @@ -450,7 +450,7 @@ AC_ARG_ENABLE([cvxopt], AC_ARG_ENABLE([notebook], AS_HELP_STRING([--disable-notebook], [disable build of the Jupyter notebook and related packages]), [ - for pkg in notebook nbconvert beautifulsoup4 sagenb_export nbformat nbclient terminado send2trash prometheus_client mistune pandocfilters bleach defusedxml jsonschema jupyter_jsmol argon2_cffi argon2_cffi_bindings webencodings tinycss2 ipympl soupsieve fastjsonschema anyio arrow async_lru fqdn isoduration json5 jsonpointer jsonschema_specifications jupyter_events jupyter_lsp jupyter_server jupyter_server_terminals jupyterlab jupyterlab_server jupyterlab_pygments jupyterlab_mathjax2 notebook_shim overrides python_json_logger pyyaml referencing rfc3339_validator rfc3986_validator sniffio types_python_dateutil uri_template webcolors websocket_client; do + for pkg in notebook nbconvert beautifulsoup4 sagenb_export nbformat nbclient terminado send2trash prometheus_client mistune pandocfilters bleach defusedxml jsonschema jupyter_jsmol argon2_cffi argon2_cffi_bindings webencodings tinycss2 ipympl soupsieve fastjsonschema anyio arrow async_lru fqdn isoduration json5 jsonpointer jsonschema_specifications jupyter_events jupyter_lsp jupyter_server jupyter_server_terminals jupyterlab jupyterlab_server jupyterlab_pygments jupyterlab_mathjax2 jupyter_sphinx notebook_shim overrides python_json_logger pyyaml referencing rfc3339_validator rfc3986_validator sniffio types_python_dateutil uri_template webcolors websocket_client httpx httpcore h11; do AS_VAR_SET([SAGE_ENABLE_$pkg], [$enableval]) done ]) @@ -463,6 +463,12 @@ AC_ARG_ENABLE([r], done ]) +AC_ARG_ENABLE([sagetex], + AS_HELP_STRING([--disable-sagetex], + [don't build SageTeX]), [ + AS_VAR_SET([SAGE_ENABLE_sagetex], [$enableval]) + ]) + AC_ARG_ENABLE([doc], AS_HELP_STRING([--disable-doc], [disable build of the Sage documentation and packages depending on it]), [ diff --git a/constraints_pkgs.txt b/constraints_pkgs.txt new file mode 100644 index 00000000000..9e4fe969fbc --- /dev/null +++ b/constraints_pkgs.txt @@ -0,0 +1,39 @@ +# This "constraints file" can be used for forcing pip +# (and any tools that delegate to pip, such as pypa/build) +# to install the distribution packages included in +# the SageMath monorepository only from their source trees +# in SAGE_ROOT/pkgs/ instead of from PyPI. +# +# Example: Building a sagemath-standard wheel +# +# [alice@localhost sage]$ ./bootstrap +# [alice@localhost sage]$ ./configure +# [alice@localhost sage]$ export MAKE="make -j16" SAGE_NUM_THREADS=16 +# [alice@localhost sage]$ make all-sage-local +# [alice@localhost sage]$ export PIP_CONSTRAINT="$(pwd)/constraints_pkgs.txt" +# [alice@localhost sage]$ ./sage -sh -c 'python3 -m build -v -v pkgs/sagemath-standard' +# +# Non-example: Installing the built wheel using the same +# constraints file will fail because sagemath-standard is one +# of the distribution packages listed below. It will conflict +# with the built wheel for sagemath-standard! +# Use "pkgs/sagemath-standard/constraints_pkgs.txt" instead. + +# Reference on the format: +# https://pip.pypa.io/en/stable/user_guide/#constraints-files +# +sage_conf @ file://${SAGE_ROOT}/pkgs/sage-conf +sage_docbuild @ file://${SAGE_ROOT}/pkgs/sage-docbuild +sage_setup @ file://${SAGE_ROOT}/pkgs/sage-setup +sage_sws2rst @ file://${SAGE_ROOT}/pkgs/sage-sws2rst +sagemath-bliss @ file://${SAGE_ROOT}/pkgs/sagemath-bliss +sagemath-categories @ file://${SAGE_ROOT}/pkgs/sagemath-categories +sagemath-coxeter3 @ file://${SAGE_ROOT}/pkgs/sagemath-coxeter3 +sagemath-environment @ file://${SAGE_ROOT}/pkgs/sagemath-environment +sagemath-mcqd @ file://${SAGE_ROOT}/pkgs/sagemath-mcqd +sagemath-meataxe @ file://${SAGE_ROOT}/pkgs/sagemath-meataxe +sagemath-objects @ file://${SAGE_ROOT}/pkgs/sagemath-objects +sagemath-repl @ file://${SAGE_ROOT}/pkgs/sagemath-repl +sagemath-sirocco @ file://${SAGE_ROOT}/pkgs/sagemath-sirocco +sagemath-standard @ file://${SAGE_ROOT}/pkgs/sagemath-standard +sagemath-tdlib @ file://${SAGE_ROOT}/pkgs/sagemath-tdlib diff --git a/docker/README.md b/docker/README.md index 190793c202d..404cf973490 100644 --- a/docker/README.md +++ b/docker/README.md @@ -49,7 +49,7 @@ Have a look at `.gitlab-ci.yml` if you want to setup GitLab CI for your own fork # Report bugs and issues -Please tell us of any bugs or omissions at our [Issue Tracker](https://trac.sagemath.org) or contact us through the [sage-support](https://groups.google.com/forum/#!forum/sage-support) or the [sage-devel](https://groups.google.com/forum/#!forum/sage-devel) mailing lists. +Please tell us of any bugs or omissions at our [Issue Tracker](https://github.com/sagemath/sage/issues) or contact us through the [sage-support](https://groups.google.com/forum/#!forum/sage-support) or the [sage-devel](https://groups.google.com/forum/#!forum/sage-devel) mailing lists. # License diff --git a/m4/pyproject_toml_metadata.m4 b/m4/pyproject_toml_metadata.m4 index 0d9824b1f2e..5f39bd00077 100644 --- a/m4/pyproject_toml_metadata.m4 +++ b/m4/pyproject_toml_metadata.m4 @@ -17,5 +17,12 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering :: Mathematics", ] -urls = {Homepage = "https://www.sagemath.org"} requires-python = ">=3.9, <3.13" + +[project.urls] +download = "https://doc.sagemath.org/html/en/installation/index.html" +"release notes" = "https://github.com/sagemath/sage/releases" +source = "https://github.com/sagemath/sage" +documentation = "https://doc.sagemath.org" +homepage = "https://www.sagemath.org" +tracker = "https://github.com/sagemath/sage/issues" diff --git a/meson.build b/meson.build new file mode 100644 index 00000000000..8bf73d23d55 --- /dev/null +++ b/meson.build @@ -0,0 +1,215 @@ +project( + 'SageMath', + ['c', 'cpp', 'cython'], + version: files('src/VERSION.txt'), + license: 'GPL v3', + default_options: ['c_std=c17', 'cpp_std=c++17'], +) + +# Python module +# https://mesonbuild.com/Python-module.html +py_module = import('python') +py = py_module.find_installation(pure: false) +py_dep = py.dependency() + +# Additional targets +py_with_pytest = py_module.find_installation( + required: false, + modules: ['pytest'], +) +if py_with_pytest.found() + test( + 'pytest', + py_with_pytest, + args: [ + '-m', + 'pytest', + '-c', + meson.current_source_dir() / 'tox.ini', + '--doctest', + meson.current_source_dir() / 'src' / 'sage' / 'categories', + ], + timeout: 0, + ) +endif + +# Workaround for missing init files (Cython doesn't handle namespace packages well) +create_files_command = [ + 'python3', + '-c', + ''' +import os +content = "# Here so that cython creates the correct module name" +file_paths = [ + 'src/sage/interfaces/__init__.py', + 'src/sage/crypto/block_cipher/__init__.py', + 'src/sage/crypto/public_key/__init__.py', + 'src/sage/logic/__init__.py', + 'src/sage/parallel/__init__.py', + 'src/sage/dynamics/cellular_automata/__init__.py', + 'src/sage/dynamics/arithmetic_dynamics/__init__.py', + 'src/sage/dynamics/__init__.py', + 'src/sage/dynamics/complex_dynamics/__init__.py', + 'src/sage/knots/__init__.py', + 'src/sage/topology/__init__.py', + 'src/sage/functions/__init__.py', + 'src/sage/manifolds/subsets/__init__.py', + 'src/sage/manifolds/__init__.py', + 'src/sage/manifolds/differentiable/examples/__init__.py', + 'src/sage/manifolds/differentiable/__init__.py', + 'src/sage/coding/source_coding/__init__.py', + 'src/sage/coding/guruswami_sudan/__init__.py', + 'src/sage/coding/__init__.py', + 'src/sage/coding/codecan/__init__.py', + 'src/sage/games/__init__.py', + 'src/sage/quivers/__init__.py', + 'src/sage/schemes/cyclic_covers/__init__.py', + 'src/sage/schemes/plane_conics/__init__.py', + 'src/sage/schemes/curves/__init__.py', + 'src/sage/schemes/plane_quartics/__init__.py', + 'src/sage/schemes/jacobians/__init__.py', + 'src/sage/schemes/toric/sheaf/__init__.py', + 'src/sage/schemes/toric/__init__.py', + 'src/sage/schemes/product_projective/__init__.py', + 'src/sage/schemes/elliptic_curves/__init__.py', + 'src/sage/schemes/riemann_surfaces/__init__.py', + 'src/sage/schemes/hyperelliptic_curves/__init__.py', + 'src/sage/schemes/berkovich/__init__.py', + 'src/sage/schemes/generic/__init__.py', + 'src/sage/schemes/projective/__init__.py', + 'src/sage/schemes/__init__.py', + 'src/sage/schemes/affine/__init__.py', + 'src/sage/modular/hecke/__init__.py', + 'src/sage/modular/pollack_stevens/__init__.py', + 'src/sage/modular/overconvergent/__init__.py', + 'src/sage/modular/modform/__init__.py', + 'src/sage/modular/quasimodform/__init__.py', + 'src/sage/modular/modsym/__init__.py', + 'src/sage/modular/local_comp/__init__.py', + 'src/sage/modular/quatalg/__init__.py', + 'src/sage/modular/ssmod/__init__.py', + 'src/sage/modular/abvar/__init__.py', + 'src/sage/modular/__init__.py', + 'src/sage/modular/btquotients/__init__.py', + 'src/sage/modular/arithgroup/__init__.py', + 'src/sage/modular/modform_hecketriangle/__init__.py', + 'src/sage/combinat/cluster_algebra_quiver/__init__.py', + 'src/sage/combinat/root_system/__init__.py', + 'src/sage/combinat/species/__init__.py', + 'src/sage/combinat/designs/__init__.py', + 'src/sage/combinat/posets/__init__.py', + 'src/sage/combinat/matrices/__init__.py', + 'src/sage/combinat/rigged_configurations/__init__.py', + 'src/sage/combinat/ncsf_qsym/__init__.py', + 'src/sage/combinat/path_tableaux/__init__.py', + 'src/sage/combinat/sf/__init__.py', + 'src/sage/combinat/__init__.py', + 'src/sage/combinat/chas/__init__.py', + 'src/sage/combinat/ncsym/__init__.py', + 'src/sage/combinat/words/__init__.py', + 'src/sage/combinat/crystals/__init__.py', + 'src/sage/tensor/modules/__init__.py', + 'src/sage/tensor/__init__.py', + 'src/sage/groups/matrix_gps/__init__.py', + 'src/sage/groups/semimonomial_transformations/__init__.py', + 'src/sage/groups/perm_gps/partn_ref2/__init__.py', + 'src/sage/groups/perm_gps/partn_ref/__init__.py', + 'src/sage/groups/perm_gps/__init__.py', + 'src/sage/groups/__init__.py', + 'src/sage/groups/affine_gps/__init__.py', + 'src/sage/groups/abelian_gps/__init__.py', + 'src/sage/groups/additive_abelian/__init__.py', + 'src/sage/groups/lie_gps/__init__.py', + 'src/sage/groups/misc_gps/__init__.py', + 'src/sage/symbolic/__init__.py', + 'src/sage/symbolic/integration/__init__.py', + 'src/sage/lfunctions/__init__.py', + 'src/sage/arith/__init__.py', + 'src/sage/ext/__init__.py', + 'src/sage/ext/interpreters/__init__.py', + 'src/sage/categories/examples/__init__.py', + 'src/sage/categories/__init__.py', + 'src/sage/modules/fg_pid/__init__.py', + 'src/sage/modules/__init__.py', + 'src/sage/modules/with_basis/__init__.py', + 'src/sage/modules/fp_graded/steenrod/__init__.py', + 'src/sage/modules/fp_graded/__init__.py', + 'src/sage/misc/__init__.py', + 'src/sage/rings/convert/__init__.py', + 'src/sage/rings/invariants/__init__.py', + 'src/sage/rings/finite_rings/__init__.py', + 'src/sage/rings/function_field/__init__.py', + 'src/sage/rings/function_field/drinfeld_modules/__init__.py', + 'src/sage/rings/semirings/__init__.py', + 'src/sage/rings/number_field/__init__.py', + 'src/sage/rings/__init__.py', + 'src/sage/rings/padics/__init__.py', + 'src/sage/rings/valuation/__init__.py', + 'src/sage/rings/asymptotic/__init__.py', + 'src/sage/rings/polynomial/weil/__init__.py', + 'src/sage/rings/polynomial/__init__.py', + 'src/sage/rings/polynomial/padics/__init__.py', + 'src/sage/monoids/__init__.py', + 'src/sage/matrix/__init__.py', + 'src/sage/matroids/__init__.py', + 'src/sage/interacts/__init__.py', + 'src/sage/__init__.py', + 'src/sage/plot/__init__.py', + 'src/sage/plot/plot3d/__init__.py', + 'src/sage/typeset/__init__.py', + 'src/sage/algebras/lie_conformal_algebras/__init__.py', + 'src/sage/algebras/fusion_rings/__init__.py', + 'src/sage/algebras/letterplace/__init__.py', + 'src/sage/algebras/quatalg/__init__.py', + 'src/sage/algebras/steenrod/__init__.py', + 'src/sage/algebras/finite_dimensional_algebras/__init__.py', + 'src/sage/algebras/__init__.py', + 'src/sage/algebras/hecke_algebras/__init__.py', + 'src/sage/algebras/lie_algebras/__init__.py', + 'src/sage/algebras/quantum_groups/__init__.py', + 'src/sage/quadratic_forms/genera/__init__.py', + 'src/sage/quadratic_forms/__init__.py', + 'src/sage/game_theory/__init__.py', + 'src/sage/sandpiles/__init__.py', + 'src/sage/sat/__init__.py', + 'src/sage/homology/__init__.py', + 'src/sage/geometry/riemannian_manifolds/__init__.py', + 'src/sage/geometry/hyperplane_arrangement/__init__.py', + 'src/sage/geometry/triangulation/__init__.py', + 'src/sage/geometry/polyhedron/modules/__init__.py', + 'src/sage/geometry/polyhedron/__init__.py', + 'src/sage/geometry/polyhedron/combinatorial_polyhedron/__init__.py', + 'src/sage/geometry/__init__.py', + 'src/sage/geometry/hyperbolic_space/__init__.py', + 'src/sage/sets/__init__.py', + 'src/sage/probability/__init__.py', + 'src/sage/numerical/backends/__init__.py', + 'src/sage/numerical/__init__.py', + 'src/sage/data_structures/__init__.py', + 'src/sage/graphs/graph_decompositions/__init__.py', + 'src/sage/graphs/generators/__init__.py', + 'src/sage/graphs/__init__.py', + 'src/sage/graphs/base/__init__.py', + 'src/sage/databases/__init__.py', + 'src/sage/stats/hmm/__init__.py', + 'src/sage/stats/__init__.py', + 'src/sage/stats/distributions/__init__.py', + 'src/sage/libs/gap/__init__.py', + 'src/sage/libs/mpfi/__init__.py', + 'src/sage/libs/__init__.py', + 'src/sage/libs/polybori/__init__.py', + 'src/sage/libs/mpfr/__init__.py', + 'src/sage/libs/mpc/__init__.py', + 'src/sage/calculus/transforms/__init__.py', + 'src/sage/calculus/__init__.py', +] +for path in file_paths: + path = "''' + meson.current_source_dir() + '''/" + path + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, 'w') as f: + f.write(content) + ''', +] +run_command(create_files_command, check: true) + +subdir('src') diff --git a/meson.format b/meson.format new file mode 100644 index 00000000000..f56718e583f --- /dev/null +++ b/meson.format @@ -0,0 +1 @@ +indent_by: ' ' diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sage-conf/_sage_conf/_conf.py.in b/pkgs/sage-conf/_sage_conf/_conf.py.in index 54a59fdbacf..e5386cf89cf 100644 --- a/pkgs/sage-conf/_sage_conf/_conf.py.in +++ b/pkgs/sage-conf/_sage_conf/_conf.py.in @@ -8,6 +8,7 @@ VERSION = "@PACKAGE_VERSION@" # to it. SAGE_LOCAL = "@prefix@" SAGE_ROOT = "@SAGE_ROOT@" +SAGE_SHARE = "@SAGE_SHARE@" # The semicolon-separated list of GAP root paths. This is the list of # locations that are searched for GAP packages. This is passed directly diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sage-setup/pyproject.toml b/pkgs/sage-setup/pyproject.toml index 96b079cec62..a8cac2c6a64 100644 --- a/pkgs/sage-setup/pyproject.toml +++ b/pkgs/sage-setup/pyproject.toml @@ -39,7 +39,8 @@ packages = [ "sage_setup", "sage_setup.autogen", "sage_setup.autogen.interpreters", - "sage_setup.autogen.interpreters.specs", + "sage_setup.autogen.interpreters.internal", + "sage_setup.autogen.interpreters.internal.specs", "sage_setup.command", ] include-package-data = false diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-repl/MANIFEST.in b/pkgs/sagemath-repl/MANIFEST.in index ba331ec2931..0d9a0289491 100644 --- a/pkgs/sagemath-repl/MANIFEST.in +++ b/pkgs/sagemath-repl/MANIFEST.in @@ -7,6 +7,12 @@ include sage/misc/sagedoc.py include sage/misc/sage_input.py include sage/misc/sage_eval.py +# expect_objects from sage.interfaces.quit is needed to compute the +# CPU time used by each doctest. We include sage.interfaces.cleaner +# because it is conditionally imported by sage.interfaces.quit. +include sage/interfaces/quit.py +include sage/interfaces/cleaner.py + include VERSION.txt global-exclude all__*.py diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-repl/pyproject.toml.m4 b/pkgs/sagemath-repl/pyproject.toml.m4 index 459a599f1fb..f0ed5af473c 100644 --- a/pkgs/sagemath-repl/pyproject.toml.m4 +++ b/pkgs/sagemath-repl/pyproject.toml.m4 @@ -42,6 +42,7 @@ py-modules = [ ] packages = [ "sage.doctest", + "sage.interfaces", "sage.repl", "sage.repl.display", "sage.repl.ipython_kernel", diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pkgs/sagemath-standard/README.rst b/pkgs/sagemath-standard/README.rst index f429984aa4c..b5d44eb1f9b 100644 --- a/pkgs/sagemath-standard/README.rst +++ b/pkgs/sagemath-standard/README.rst @@ -8,24 +8,26 @@ About SageMath "Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, and MATLAB" - Copyright (C) 2005-2020 The Sage Development Team + Copyright (C) 2005-2024 The Sage Development Team https://www.sagemath.org SageMath fully supports all major Linux distributions, recent versions of macOS, and Windows (Windows Subsystem for Linux). -The traditional and recommended way to install SageMath is from source via Sage-the-distribution (https://www.sagemath.org/download-source.html). Sage-the-distribution first builds a large number of open source packages from source (unless it finds suitable versions installed in the system) and then installs the Sage Library (sagelib, implemented in Python and Cython). +See https://doc.sagemath.org/html/en/installation/index.html +for general installation instructions. -About this experimental pip-installable source distribution ------------------------------------------------------------ +About this pip-installable distribution package +----------------------------------------------- -This pip-installable source distribution `sagemath-standard` is an experimental distribution of the Sage Library. Use at your own risk. +This pip-installable source distribution `sagemath-standard` is a +distribution of the Sage Library. Building `sagemath-standard` has a large number of system packages as prerequisites. See https://doc.sagemath.org/html/en/installation/source.html#linux-recommended-installation for partial lists for various systems. -The connection to the system environment is facilitated through the https://pypi.org/project/sage-conf/ distribution package. +The connection to the system environment is facilitated through the https://pypi.org/project/sage-conf/ distribution package; for step-by-step installation instructions, see https://github.com/sagemath/sage/blob/develop/README.md#alternative-installation-using-pypi A modularization effort is in progress with the goal of making it possible to install parts of the Sage Library with fewer prerequisites. https://github.com/sagemath/sage/issues/29705 diff --git a/pkgs/sagemath-standard/constraints_pkgs.txt b/pkgs/sagemath-standard/constraints_pkgs.txt new file mode 100644 index 00000000000..139aae58f86 --- /dev/null +++ b/pkgs/sagemath-standard/constraints_pkgs.txt @@ -0,0 +1,21 @@ +# This "constraints file" can be used for forcing pip +# (and any tools that delegate to pip, such as pypa/build) +# to install the dependencies of sagemath-standard that are +# distribution packages included in the SageMath monorepository +# only from their source trees in SAGE_ROOT/pkgs/ +# instead of from PyPI. +# +# Example: +# +# [alice@localhost sage]$ ./bootstrap +# [alice@localhost sage]$ ./configure +# [alice@localhost sage]$ export MAKE="make -j16" SAGE_NUM_THREADS=16 +# [alice@localhost sage]$ make all-sage-local +# [alice@localhost sage]$ export PIP_CONSTRAINT="$(pwd)/pkgs/sagemath-standard/constraints_pkgs.txt" +# [alice@localhost sage]$ ./sage -sh -c 'python3 -m build -v -v pkgs/sagemath-standard' +# +# Reference on the format: +# https://pip.pypa.io/en/stable/user_guide/#constraints-files +# +sage_conf @ file://${SAGE_ROOT}/pkgs/sage-conf +sage_setup @ file://${SAGE_ROOT}/pkgs/sage-setup diff --git a/pkgs/sagemath-standard/tox.ini b/pkgs/sagemath-standard/tox.ini index c197f49d9be..0ac97f8a021 100644 --- a/pkgs/sagemath-standard/tox.ini +++ b/pkgs/sagemath-standard/tox.ini @@ -31,6 +31,19 @@ envlist = # # ./sage -sh -c '(cd pkgs/sagemath-standard && tox -v -v -v -e sagepython-sagewheels-nopypi)' # + # Build and test without using the concrete dependencies specified by requirements.txt, + # using the dependencies declared in pyproject.toml and setup.cfg (install-requires) only: + # Install the distribution packages included in the SageMath monorepository only from + # their source trees in SAGE_ROOT/pkgs/ (not from PyPI). + # + # ./sage -sh -c '(cd pkgs/sagemath-standard && tox -v -v -v -e sagepython-constraints_pkgs-norequirements)' + # + # Build dependencies according to requirements.txt (all versions fixed). + # Install the distribution packages included in the SageMath monorepository only from + # their source trees in SAGE_ROOT/pkgs/ (not from PyPI). + # + # ./sage -sh -c '(cd pkgs/sagemath-standard && tox -v -v -v -e sagepython-constraints_pkgs)' + # # EXPERIMENTAL ENVIRONMENTS: # # Build dependencies according to requirements.txt (all versions fixed). @@ -80,6 +93,8 @@ passenv = MAKEFLAGS # SAGE_VENV only for referring to the basepython or finding the wheels sagepython, sagewheels: SAGE_VENV + # Used as an environment variable in constraints_pkgs.txt + constraints_pkgs: SAGE_ROOT # Location of the wheels sagewheels: SAGE_SPKG_WHEELS @@ -88,6 +103,7 @@ setenv = # apply both to the installation of the dependencies and of the package sagewheels: PIP_FIND_LINKS=file://{env:SAGE_SPKG_WHEELS:{env:SAGE_VENV:{toxinidir}/../../../../venv}/var/lib/sage/wheels} nopypi: PIP_NO_INDEX=true + constraints_pkgs: PIP_CONSTRAINT={toxinidir}/constraints_pkgs.txt # No build isolation for PEP 517 packages - use what is already in the environment # Note that this pip env "NO" variable uses inverted logic: # PIP_NO_BUILD_ISOLATION=False means don't use build isolation. @@ -153,6 +169,17 @@ setenv = {[pkgenv]setenv} basepython = {env:SAGE_VENV}/bin/python3 +[testenv:.pkg-sagepython-constraints_pkgs] +passenv = {[pkgenv]passenv} + SAGE_VENV + # Location of constraints_pkgs.txt + SAGE_ROOT + +setenv = {[pkgenv]setenv} + PIP_CONSTRAINT={env:SAGE_ROOT}/constraints_pkgs.txt + +basepython = {env:SAGE_VENV}/bin/python3 + [testenv:sagepython] basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython @@ -177,6 +204,14 @@ package_env = .pkg-sagepython basepython = {env:SAGE_VENV}/bin/python3 package_env = .pkg-sagepython +[testenv:sagepython-constraints_pkgs] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-constraints_pkgs + +[testenv:sagepython-constraints_pkgs-norequirements] +basepython = {env:SAGE_VENV}/bin/python3 +package_env = .pkg-sagepython-constraints_pkgs + [testenv:sagepython-sagewheels-nopypi-norequirements] basepython = {env:SAGE_VENV}/bin/python3 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..47c125c4e26 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,155 @@ +[build-system] +build-backend = 'mesonpy' +# Minimum requirements for the build system to execute. +requires = [ + 'meson-python', + 'cypari2 >=2.1.1', + 'cysignals >=1.11.4', + # Exclude 3.0.3 because of https://github.com/cython/cython/issues/5748 + 'cython >=3.0, != 3.0.3', + 'gmpy2 ~=2.1.b999', + 'memory_allocator', + 'numpy >=1.19', + 'jinja2' +] +[project] +name = "sagemath" +description = "Sage: Open Source Mathematics Software: Standard Python Library" +dependencies = [ + 'six >=1.15.0', + 'conway-polynomials >=0.8', + 'cypari2 >=2.1.1', + 'cysignals >=1.10.2', + 'cython >=3.0, != 3.0.3', + 'gmpy2 ~=2.1.b999', + 'lrcalc ~=2.1', + 'memory_allocator', + 'numpy >=1.19', + # Issue #30922: pplpy 0.8.4 and earlier do not declare dependencies correctly + 'pplpy >=0.8.6', + 'primecountpy', + 'requests >=2.13.0', + # According to https://github.com/python/typing_extensions/blob/main/CHANGELOG.md, + # version 4.4.0 adds another Python 3.11 typing backport + 'typing_extensions >= 4.4.0; python_version<"3.11"', + 'ipython >=7.13.0', + 'pexpect >=4.8.0', + 'sphinx >=5.2, <9', + 'networkx >=2.4', + # 1.8 is known good version. + # Per https://docs.scipy.org/doc/scipy/dev/core-dev/index.html#version-numbering + # and https://docs.scipy.org/doc/scipy/dev/core-dev/index.html#deprecations, + # deprecations cannot be introduced in micro releases. + # SciPy devs wait "at least 6 months", "in practice two (minor) releases" + # from deprecation to removal of a feature. + 'scipy >=1.5', + 'sympy >=1.6, <2.0', + # Issue #33642: Set lower bound for use of matplotlib color maps introduced in #33491, + # and to suppress deprecation warnings (https://github.com/matplotlib/matplotlib/pull/21073) + 'matplotlib >=3.5.1', + 'pillow >=7.2.0', + 'mpmath >=1.1.0', + 'ipykernel >=5.2.1', + 'jupyter-client', + 'ipywidgets >=7.5.1', + 'fpylll >=0.5.9', + 'ptyprocess > 0.5', +] +dynamic = ["version"] +license = {text = "GNU General Public License (GPL) v2 or later"} +authors = [{name = "The Sage Developers", email = "sage-support@googlegroups.com"}] +classifiers = [ + "Development Status :: 6 - Mature", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", + "Operating System :: POSIX", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Scientific/Engineering :: Mathematics", +] +urls = {Homepage = "https://www.sagemath.org"} +requires-python = ">=3.9, <3.13" + +[project.optional-dependencies] +R = [ + 'rpy2 >=3.3', +] + +[project.readme] +file = "README.md" +content-type = "text/markdown" + +[tool.conda-lock] +platforms = [ + 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64' +] + +[external] +# External dependencies in the format proposed by https://peps.python.org/pep-0725 +build-requires = [ + "virtual:compiler/c", + "virtual:compiler/cpp", + "pkg:generic/pkg-config" +] + +host-requires = [ + "virtual:interface/blas", + "pkg:generic/boost", + "pkg:generic/brial", + "pkg:generic/cddlib", + "pkg:generic/cliquer", + "pkg:generic/ecl", + "pkg:generic/eclib", + "pkg:generic/ecm", + "pkg:generic/fflas-ffpack", + "pkg:generic/fplll", + "pkg:generic/flint", + "pkg:generic/libgd", + "pkg:generic/gap", + "pkg:generic/gfan", + "pkg:generic/giac", + "pkg:generic/givaro", + "pkg:generic/glpk", + "pkg:generic/gmp", + "pkg:generic/gsl", + "pkg:generic/iml", + "pkg:generic/lcalc", + "pkg:generic/libbraiding", + "pkg:generic/libhomfly", + "pkg:generic/linbox", + "pkg:generic/lrcalc", + "pkg:generic/m4ri", + "pkg:generic/m4rie", + "pkg:generic/maxima", + "pkg:generic/mpc", + "pkg:generic/mpfi", + "pkg:generic/mpfr", + "pkg:generic/nauty", + "pkg:generic/ntl", + "pkg:generic/palp", + "pkg:generic/pari", + "pkg:generic/pari-elldata", + "pkg:generic/pari-galdata", + "pkg:generic/pari-seadata", + "pkg:generic/planarity", + "pkg:generic/ppl", + "pkg:generic/primesieve", + "pkg:generic/primecount", + "pkg:generic/qhull", + "pkg:generic/rw", + "pkg:generic/singular", + "pkg:generic/symmetrica", + "pkg:generic/sympow", +] + +dependencies = [ + "pkg:generic/tachyon", + "pkg:generic/sagemath-polytopes-db", + "pkg:generic/sagemath-elliptic-curves", +] diff --git a/src/VERSION.txt b/src/VERSION.txt index c4777d00d87..99613807a73 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.5.beta2 +10.5.beta9 diff --git a/src/bin/sage b/src/bin/sage index df4ff198426..d102056b6c6 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -428,7 +428,7 @@ usage_advanced() { echo " --initial -- only show the first failure per block" echo " --debug -- drop into PDB after an unexpected error" echo " --failed -- only test files that failed last test" - echo " --warn-long [timeout] -- warning if doctest is slow" + echo " --warn-long [timeout] -- warn if tests take too much CPU time" echo " --only-errors -- only output failures, not successes" echo " --gc=GC -- control garbarge collection (ALWAYS:" echo " collect garbage before every test; NEVER:" diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 81efd01a4d8..8ab309685c7 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -1,206 +1,9 @@ #!/usr/bin/env sage-python -import argparse -import os import sys -# Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env -DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), - '.sage')) - -# Override to not pick up user configuration, see Issue #20270 -os.environ['SAGE_STARTUP_FILE'] = os.path.join(DOT_SAGE, 'init-doctests.sage') - - -def _get_optional_defaults(): - """Return the default value for the --optional flag.""" - optional = ['sage', 'optional'] - - return ','.join(optional) +from sage.doctest.__main__ import main if __name__ == "__main__": - parser = argparse.ArgumentParser(usage="sage -t [options] filenames", - description="Run all tests in a file or a list of files whose extensions " - "are one of the following: " - ".py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst.") - parser.add_argument("-p", "--nthreads", dest="nthreads", - type=int, nargs='?', const=0, default=1, metavar="N", - help="test in parallel using N threads, with 0 interpreted as max(2, min(8, cpu_count())); " - "when run under the control of the GNU make jobserver (make -j), request as most N job slots") - parser.add_argument("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout") - what = parser.add_mutually_exclusive_group() - what.add_argument("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") - what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library") - parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE") - - parser.add_argument("--format", choices=["sage", "github"], default="sage", - help="set format of error messages and warnings") - parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") - parser.add_argument("-s", "--short", dest="target_walltime", nargs='?', - type=int, default=-1, const=300, metavar="SECONDS", - help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)") - parser.add_argument("--warn-long", dest="warn_long", nargs='?', - type=float, default=-1.0, const=1.0, metavar="SECONDS", - help="warn if tests take more time than SECONDS") - # By default, include all tests marked 'sagemath_doc_html' -- see - # https://github.com/sagemath/sage/issues/25345 and - # https://github.com/sagemath/sage/issues/26110: - parser.add_argument("--optional", metavar="FEATURES", default=_get_optional_defaults(), - help='only run tests including one of the "# optional" tags listed in FEATURES (separated by commas); ' - 'if "sage" is listed, will also run the standard doctests; ' - 'if "sagemath_doc_html" is listed, will also run the tests relying on the HTML documentation; ' - 'if "optional" is listed, will also run tests for installed optional packages or detected features; ' - 'if "external" is listed, will also run tests for available external software; ' - 'if set to "all", then all tests will be run; ' - 'use "!FEATURE" to disable tests marked "# optional - FEATURE". ' - 'Note that "!" needs to be quoted or escaped in the shell.') - parser.add_argument("--hide", metavar="FEATURES", default="", - help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' - 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' - 'if "optional" is listed, will also hide features corresponding to optional packages.') - parser.add_argument("--probe", metavar="FEATURES", default="", - help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' - 'report the tests that pass nevertheless') - parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") - parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", - default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) - parser.add_argument("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") - parser.add_argument("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") - parser.add_argument("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests") - - parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") - parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") - parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") - parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") - parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") - parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") - parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") - parser.add_argument("--only-errors", action="store_true", default=False, help="only output failures, not test successes") - - parser.add_argument("--gdb", action="store_true", default=False, help="run doctests under the control of gdb") - parser.add_argument("--lldb", action="store_true", default=False, help="run doctests under the control of lldb") - parser.add_argument("--valgrind", "--memcheck", action="store_true", default=False, - help="run doctests using Valgrind's memcheck tool. The log " - "files are named sage-memcheck.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--massif", action="store_true", default=False, - help="run doctests using Valgrind's massif tool. The log " - "files are named sage-massif.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--cachegrind", action="store_true", default=False, - help="run doctests using Valgrind's cachegrind tool. The log " - "files are named sage-cachegrind.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--omega", action="store_true", default=False, - help="run doctests using Valgrind's omega tool. The log " - "files are named sage-omega.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - - parser.add_argument("-f", "--failed", action="store_true", default=False, - help="doctest only those files that failed in the previous run") - what.add_argument("-n", "--new", action="store_true", default=False, - help="doctest only those files that have been changed in the repository and not yet been committed") - parser.add_argument("--show-skipped", "--show_skipped", action="store_true", default=False, - help="print a summary at the end of each file of optional tests that were skipped") - - parser.add_argument("--stats_path", "--stats-path", default=os.path.join(DOT_SAGE, "timings2.json"), - help="path to a json dictionary for timings and failure status for each file from previous runs; it will be updated in this run") - parser.add_argument("--baseline_stats_path", "--baseline-stats-path", default=None, - help="path to a json dictionary for timings and failure status for each file, to be used as a baseline; it will not be updated") - - class GCAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - gcopts = dict(DEFAULT=0, ALWAYS=1, NEVER=-1) - new_value = gcopts[values] - setattr(namespace, self.dest, new_value) - - parser.add_argument("--gc", - choices=["DEFAULT", "ALWAYS", "NEVER"], - default=0, - action=GCAction, - help="control garbarge collection " - "(ALWAYS: collect garbage before every test; NEVER: disable gc; DEFAULT: Python default)") - - # The --serial option is only really for internal use, better not - # document it. - parser.add_argument("--serial", action="store_true", default=False, help=argparse.SUPPRESS) - # Same for --die_timeout - parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS) - - parser.add_argument("filenames", help="file names", nargs='*') - - # custom treatment to separate properly - # one or several file names at the end - new_arguments = [] - need_filenames = True - in_filenames = False - afterlog = False - for arg in sys.argv[1:]: - if arg in ('-n', '--new', '-a', '--all', '--installed'): - need_filenames = False - elif need_filenames and not (afterlog or in_filenames) and os.path.exists(arg): - in_filenames = True - new_arguments.append('--') - new_arguments.append(arg) - afterlog = arg in ['--logfile', '--stats_path', '--stats-path', - '--baseline_stats_path', '--baseline-stats-path'] - - args = parser.parse_args(new_arguments) - - if not args.filenames and not (args.all or args.new or args.installed): - print('either use --new, --all, --installed, or some filenames') - sys.exit(2) - - # Limit the number of threads to 2 to save system resources. - # See Issue #23713, #23892, #30351 - if sys.platform == 'darwin': - os.environ["OMP_NUM_THREADS"] = "1" - else: - os.environ["OMP_NUM_THREADS"] = "2" - - os.environ["SAGE_NUM_THREADS"] = "2" - - from sage.doctest.control import DocTestController - DC = DocTestController(args, args.filenames) - err = DC.run() - - # Issue #33521: Do not run pytest if the pytest configuration is not available. - # This happens when the source tree is not available and SAGE_SRC falls back - # to SAGE_LIB. - from sage.env import SAGE_SRC - if not all(os.path.isfile(os.path.join(SAGE_SRC, f)) - for f in ["conftest.py", "tox.ini"]): - sys.exit(err) - - try: - exit_code_pytest = 0 - import pytest - pytest_options = [] - if args.verbose: - pytest_options.append("-v") - - # #35999: no filename in arguments defaults to "src" - if not args.filenames: - filenames = [SAGE_SRC] - else: - # #31924: Do not run pytest on individual Python files unless - # they match the pytest file pattern. However, pass names - # of directories. We use 'not os.path.isfile(f)' for this so that - # we do not silently hide typos. - filenames = [f for f in args.filenames - if f.endswith("_test.py") or not os.path.isfile(f)] - if filenames: - print(f"Running pytest on {filenames} with options {pytest_options}") - exit_code_pytest = pytest.main(filenames + pytest_options) - if exit_code_pytest == 5: - # Exit code 5 means there were no test files, pass in this case - exit_code_pytest = 0 - - except ModuleNotFoundError: - print("pytest is not installed in the venv, skip checking tests that rely on it") - - if err == 0: - sys.exit(exit_code_pytest) - else: - sys.exit(err) + sys.exit(main()) diff --git a/src/bin/sage-valgrind b/src/bin/sage-valgrind index ae0d5dae389..9c668c3bdd0 100755 --- a/src/bin/sage-valgrind +++ b/src/bin/sage-valgrind @@ -20,6 +20,7 @@ fi SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/pyalloc.supp" SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/sage.supp" SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/sage-additional.supp" +SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/valgrind-python.supp" MEMCHECK_FLAGS="--leak-resolution=high --leak-check=full --num-callers=25 $SUPP" diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 92d914d9c6e..e420ab80e2f 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.5.beta2' -SAGE_RELEASE_DATE='2024-08-10' -SAGE_VERSION_BANNER='SageMath version 10.5.beta2, Release Date: 2024-08-10' +SAGE_VERSION='10.5.beta9' +SAGE_RELEASE_DATE='2024-11-03' +SAGE_VERSION_BANNER='SageMath version 10.5.beta9, Release Date: 2024-11-03' diff --git a/src/doc/ca/intro/conf.py b/src/doc/ca/intro/conf.py index 71d81dcdbf3..877eba7eeb7 100644 --- a/src/doc/ca/intro/conf.py +++ b/src/doc/ca/intro/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css new file mode 100644 index 00000000000..b38f14b1e3d --- /dev/null +++ b/src/doc/common/static/custom-tabs.css @@ -0,0 +1,13 @@ +/* Additional css for tabs */ + +.tab-set { + margin-top: 0px; +} + +.tab-set > label { + padding: 0.1em var(--tabs--pading-x); +} + +p.with-sage-tab { + margin-bottom:0.25rem +} diff --git a/src/doc/de/a_tour_of_sage/conf.py b/src/doc/de/a_tour_of_sage/conf.py index 71fc7ef9012..fb960f06ffd 100644 --- a/src/doc/de/a_tour_of_sage/conf.py +++ b/src/doc/de/a_tour_of_sage/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/de/thematische_anleitungen/conf.py b/src/doc/de/thematische_anleitungen/conf.py index 337f1a98c68..b8ec2573f90 100644 --- a/src/doc/de/thematische_anleitungen/conf.py +++ b/src/doc/de/thematische_anleitungen/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/de/tutorial/conf.py b/src/doc/de/tutorial/conf.py index 399a915e9f4..da1c19e0ffc 100644 --- a/src/doc/de/tutorial/conf.py +++ b/src/doc/de/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/de/tutorial/interactive_shell.rst b/src/doc/de/tutorial/interactive_shell.rst index 6e313da4b52..8915be27d07 100644 --- a/src/doc/de/tutorial/interactive_shell.rst +++ b/src/doc/de/tutorial/interactive_shell.rst @@ -356,9 +356,9 @@ dem man nachgehen sollte. sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -742,7 +742,7 @@ nicht erlaubt. :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/el/a_tour_of_sage/conf.py b/src/doc/el/a_tour_of_sage/conf.py index 5548c31525e..ff4e9436313 100644 --- a/src/doc/el/a_tour_of_sage/conf.py +++ b/src/doc/el/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/a_tour_of_sage/conf.py b/src/doc/en/a_tour_of_sage/conf.py index 7cf31297409..689eed59af3 100644 --- a/src/doc/en/a_tour_of_sage/conf.py +++ b/src/doc/en/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/constructions/conf.py b/src/doc/en/constructions/conf.py index b6bbbbaab3b..a28d011781c 100644 --- a/src/doc/en/constructions/conf.py +++ b/src/doc/en/constructions/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/constructions/groups.rst b/src/doc/en/constructions/groups.rst index da90efecfaa..a7ce106c831 100644 --- a/src/doc/en/constructions/groups.rst +++ b/src/doc/en/constructions/groups.rst @@ -124,31 +124,25 @@ You can compute conjugacy classes of a finite group using "natively":: You can use the Sage-GAP interface:: - sage: gap.eval("G := Group((1,2)(3,4),(1,2,3))") - 'Group([ (1,2)(3,4), (1,2,3) ])' - sage: gap.eval("CG := ConjugacyClasses(G)") - '[ ()^G, (2,3,4)^G, (2,4,3)^G, (1,2)(3,4)^G ]' - sage: gap.eval("gamma := CG[3]") - '(2,4,3)^G' - sage: gap.eval("g := Representative(gamma)") - '(2,4,3)' + sage: libgap.eval("G := Group((1,2)(3,4),(1,2,3))") + Group([ (1,2)(3,4), (1,2,3) ]) + sage: libgap.eval("CG := ConjugacyClasses(G)") + [ ()^G, (2,3,4)^G, (2,4,3)^G, (1,2)(3,4)^G ] + sage: libgap.eval("gamma := CG[3]") + (2,4,3)^G + sage: libgap.eval("g := Representative(gamma)") + (2,4,3) Or, here's another (more "pythonic") way to do this type of computation:: - sage: G = gap.Group('[(1,2,3), (1,2)(3,4), (1,7)]') + sage: G = libgap.eval("Group([(1,2,3), (1,2)(3,4), (1,7)])") sage: CG = G.ConjugacyClasses() sage: gamma = CG[2] sage: g = gamma.Representative() sage: CG; gamma; g - [ ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), () ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (4,7) ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (3,4,7) ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (2,3)(4,7) ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (2,3,4,7) ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (1,2)(3,4,7) ), - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (1,2,3,4,7) ) ] - ConjugacyClass( SymmetricGroup( [ 1, 2, 3, 4, 7 ] ), (4,7) ) - (4,7) + [ ()^G, (4,7)^G, (3,4,7)^G, (2,3)(4,7)^G, (2,3,4,7)^G, (1,2)(3,4,7)^G, (1,2,3,4,7)^G ] + (3,4,7)^G + (3,4,7) .. index:: pair: group; normal subgroups @@ -162,29 +156,29 @@ If you want to find all the normal subgroups of a permutation group :math:`G` (up to conjugacy), you can use Sage's interface to GAP:: sage: G = AlternatingGroup( 5 ) - sage: gap(G).NormalSubgroups() - [ AlternatingGroup( [ 1 .. 5 ] ), Group( () ) ] + sage: libgap(G).NormalSubgroups() + [ Alt( [ 1 .. 5 ] ), Group(()) ] or :: - sage: G = gap("AlternatingGroup( 5 )") + sage: G = libgap.AlternatingGroup( 5 ) sage: G.NormalSubgroups() - [ AlternatingGroup( [ 1 .. 5 ] ), Group( () ) ] + [ Alt( [ 1 .. 5 ] ), Group(()) ] Here's another way, working more directly with GAP:: - sage: print(gap.eval("G := AlternatingGroup( 5 )")) + sage: libgap.eval("G := AlternatingGroup( 5 )") Alt( [ 1 .. 5 ] ) - sage: print(gap.eval("normal := NormalSubgroups( G )")) + sage: libgap.eval("normal := NormalSubgroups( G )") [ Alt( [ 1 .. 5 ] ), Group(()) ] - sage: G = gap.new("DihedralGroup( 10 )") + sage: G = libgap.eval("DihedralGroup( 10 )") sage: G.NormalSubgroups().SortedList() - [ Group( of ... ), Group( [ f2 ] ), Group( [ f1, f2 ] ) ] - sage: print(gap.eval("G := SymmetricGroup( 4 )")) + [ Group([ ]), Group([ f2 ]), ] + sage: libgap.eval("G := SymmetricGroup( 4 )") Sym( [ 1 .. 4 ] ) - sage: print(gap.eval("normal := NormalSubgroups( G );")) + sage: libgap.eval("normal := NormalSubgroups( G );") [ Sym( [ 1 .. 4 ] ), Alt( [ 1 .. 4 ] ), Group([ (1,4)(2,3), ... ]), Group(()) ] @@ -201,7 +195,7 @@ How do you compute the center of a group in Sage? Although Sage calls GAP to do the computation of the group center, ``center`` is "wrapped" (i.e., Sage has a class PermutationGroup with associated class method "center"), so the user does not need to use -the ``gap`` command. Here's an example:: +the ``libgap`` command. Here's an example:: sage: G = PermutationGroup(['(1,2,3)(4,5)', '(3,4)']) sage: G.center() diff --git a/src/doc/en/constructions/interface_issues.rst b/src/doc/en/constructions/interface_issues.rst index c5df32a2a5f..f8b62866da1 100644 --- a/src/doc/en/constructions/interface_issues.rst +++ b/src/doc/en/constructions/interface_issues.rst @@ -92,7 +92,7 @@ Sage and other computer algebra systems If ``foo`` is a Pari, GAP ( without ending semicolon), Singular, Maxima command, resp., enter ``gp("foo")`` for Pari, -``gap.eval("foo")}`` ``singular.eval("foo")``, ``maxima("foo")``, resp.. +``libgap.eval("foo")}`` ``singular.eval("foo")``, ``maxima("foo")``, resp.. These programs merely send the command string to the external program, execute it, and read the result back into Sage. Therefore, these will not work if the external program is not installed and in diff --git a/src/doc/en/constructions/linear_algebra.rst b/src/doc/en/constructions/linear_algebra.rst index 4e76c65ad0a..b25cdf94634 100644 --- a/src/doc/en/constructions/linear_algebra.rst +++ b/src/doc/en/constructions/linear_algebra.rst @@ -306,11 +306,11 @@ Finally, you can use Sage's GAP interface as well to compute :: - sage: print(gap.eval("A := [[1,2,3],[4,5,6],[7,8,9]]")) + sage: A = libgap([[1,2,3],[4,5,6],[7,8,9]]); A [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] - sage: print(gap.eval("v := Eigenvectors( Rationals,A)")) + sage: libgap(QQ).Eigenvectors(A) [ [ 1, -2, 1 ] ] - sage: print(gap.eval("lambda := Eigenvalues( Rationals,A)")) + sage: libgap(QQ).Eigenvalues(A) [ 0 ] .. _section-rref: diff --git a/src/doc/en/constructions/polynomials.rst b/src/doc/en/constructions/polynomials.rst index a928c355ae2..8824bcdc99d 100644 --- a/src/doc/en/constructions/polynomials.rst +++ b/src/doc/en/constructions/polynomials.rst @@ -42,23 +42,24 @@ Another approach to this: sage: a = S.gen() sage: a^20062006 80*a - sage: print(gap.eval("R:= PolynomialRing( GF(97))")) + sage: libgap.eval("R:= PolynomialRing( GF(97))") GF(97)[x_1] - sage: print(gap.eval("i:= IndeterminatesOfPolynomialRing(R)")) + sage: libgap.eval("i:= IndeterminatesOfPolynomialRing(R)") [ x_1 ] - sage: gap.eval("x:= i[1];; f:= x;;") - '' - sage: print(gap.eval("PowerMod( R, x, 20062006, x^3+7 );")) + sage: libgap.eval("x:= i[1]"); libgap.eval("f:= x;") + x_1 + x_1 + sage: libgap.eval("PowerMod( R, x, 20062006, x^3+7 );") Z(97)^41*x_1 - sage: print(gap.eval("PowerMod( R, x, 20062006, x^3+7 );")) + sage: libgap.eval("PowerMod( R, x, 20062006, x^3+7 );") Z(97)^41*x_1 - sage: print(gap.eval("PowerMod( R, x, 2006200620062006, x^3+7 );")) + sage: libgap.eval("PowerMod( R, x, 2006200620062006, x^3+7 );") Z(97)^4*x_1^2 sage: a^2006200620062006 43*a^2 - sage: print(gap.eval("PowerMod( R, x, 2006200620062006, x^3+7 );")) + sage: libgap.eval("PowerMod( R, x, 2006200620062006, x^3+7 );") Z(97)^4*x_1^2 - sage: print(gap.eval("Int(Z(97)^4)")) + sage: libgap.eval("Int(Z(97)^4)") 43 .. index:: @@ -136,11 +137,11 @@ interface. :: - sage: R = gap.PolynomialRing(gap.GF(2)); R - PolynomialRing( GF(2), ["x_1"] ) + sage: R = libgap.PolynomialRing(GF(2)); R + GF(2)[x_1] sage: i = R.IndeterminatesOfPolynomialRing(); i [ x_1 ] - sage: x_1 = i[1] + sage: x_1 = i[0] sage: f = (x_1^3 - x_1 + 1)*(x_1 + x_1^2); f x_1^5+x_1^4+x_1^3+x_1 sage: g = (x_1^3 - x_1 + 1)*(x_1 + 1); g diff --git a/src/doc/en/constructions/rep_theory.rst b/src/doc/en/constructions/rep_theory.rst index daf02719826..c9c5233136f 100644 --- a/src/doc/en/constructions/rep_theory.rst +++ b/src/doc/en/constructions/rep_theory.rst @@ -16,7 +16,7 @@ Sage-GAP interface can be used to compute character tables. You can construct the table of character values of a permutation group :math:`G` as a Sage matrix, using the method ``character_table`` of the PermutationGroup class, or via the -pexpect interface to the GAP command ``CharacterTable``. +interface to the GAP command ``CharacterTable``. :: @@ -29,8 +29,8 @@ pexpect interface to the GAP command ``CharacterTable``. [ 1 -1 1 -1 1] [ 1 1 -1 -1 1] [ 2 0 0 0 -2] - sage: CT = gap(G).CharacterTable() - sage: print(gap.eval("Display(%s)"%CT.name())) + sage: CT = libgap(G).CharacterTable() + sage: CT.Display() CT1 2 3 2 2 2 3 @@ -55,11 +55,10 @@ Here is another example: [ 1 -zeta3 - 1 zeta3 1] [ 1 zeta3 -zeta3 - 1 1] [ 3 0 0 -1] - sage: gap.eval("G := Group((1,2)(3,4),(1,2,3))") - 'Group([ (1,2)(3,4), (1,2,3) ])' - sage: gap.eval("T := CharacterTable(G)") - 'CharacterTable( Alt( [ 1 .. 4 ] ) )' - sage: print(gap.eval("Display(T)")) + sage: G = libgap.eval("Group((1,2)(3,4),(1,2,3))"); G + Group([ (1,2)(3,4), (1,2,3) ]) + sage: T = G.CharacterTable() + sage: T.Display() CT2 2 2 . . 2 @@ -82,28 +81,30 @@ denotes a square root of :math:`-3`, say :math:`i\sqrt{3}`, and :math:`b3 = \frac{1}{2}(-1+i \sqrt{3})`. Note the added ``print`` Python command. This makes the output look much nicer. +.. link + :: - sage: print(gap.eval("irr := Irr(G)")) + sage: irr = G.Irr(); irr [ Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, 1, 1, 1 ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, E(3)^2, E(3), 1 ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, E(3), E(3)^2, 1 ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 3, 0, 0, -1 ] ) ] - sage: print(gap.eval("Display(irr)")) + sage: irr.Display() [ [ 1, 1, 1, 1 ], [ 1, E(3)^2, E(3), 1 ], [ 1, E(3), E(3)^2, 1 ], [ 3, 0, 0, -1 ] ] - sage: gap.eval("CG := ConjugacyClasses(G)") - '[ ()^G, (2,3,4)^G, (2,4,3)^G, (1,2)(3,4)^G ]' - sage: gap.eval("gamma := CG[3]") - '(2,4,3)^G' - sage: gap.eval("g := Representative(gamma)") - '(2,4,3)' - sage: gap.eval("chi := irr[2]") - 'Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, E(3)^2, E(3), 1 ] )' - sage: gap.eval("g^chi") - 'E(3)' + sage: CG = G.ConjugacyClasses(); CG + [ ()^G, (2,3,4)^G, (2,4,3)^G, (1,2)(3,4)^G ] + sage: gamma = CG[2]; gamma + (2,4,3)^G + sage: g = gamma.Representative(); g + (2,4,3) + sage: chi = irr[1]; chi + Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, E(3)^2, E(3), 1 ] ) + sage: g^chi + E(3) This last quantity is the value of the character ``chi`` at the group element ``g``. @@ -117,11 +118,11 @@ table prints nicely. sage: %Pprint Pretty printing has been turned OFF - sage: gap.eval("G := Group((1,2)(3,4),(1,2,3))") - 'Group([ (1,2)(3,4), (1,2,3) ])' - sage: gap.eval("T := CharacterTable(G)") - 'CharacterTable( Alt( [ 1 .. 4 ] ) )' - sage: gap.eval("Display(T)") + sage: G = libgap.eval("Group((1,2)(3,4),(1,2,3))"); G + Group([ (1,2)(3,4), (1,2,3) ]) + sage: T = G.CharacterTable(); T + CharacterTable( Alt( [ 1 .. 4 ] ) ) + sage: T.Display() CT3 2 2 2 . . @@ -138,12 +139,12 @@ table prints nicely. A = E(3)^2 = (-1-Sqrt(-3))/2 = -1-b3 - sage: gap.eval("irr := Irr(G)") + sage: irr = G.Irr(); irr [ Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, 1, 1, 1 ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, 1, E(3)^2, E(3) ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 1, 1, E(3), E(3)^2 ] ), Character( CharacterTable( Alt( [ 1 .. 4 ] ) ), [ 3, -1, 0, 0 ] ) ] - sage: gap.eval("Display(irr)") + sage: irr.Display() [ [ 1, 1, 1, 1 ], [ 1, 1, E(3)^2, E(3) ], [ 1, 1, E(3), E(3)^2 ], @@ -162,15 +163,15 @@ Brauer characters The Brauer character tables in GAP do not yet have a "native" interface. To access them you can directly interface with GAP using -pexpect and the ``gap.eval`` command. +the ``libgap.eval`` command. The example below using the GAP interface illustrates the syntax. :: - sage: print(gap.eval("G := Group((1,2)(3,4),(1,2,3))")) + sage: G = libgap.eval("Group((1,2)(3,4),(1,2,3))"); G Group([ (1,2)(3,4), (1,2,3) ]) - sage: print(gap.eval("irr := IrreducibleRepresentations(G,GF(7))")) # random arch. dependent output + sage: irr = G.IrreducibleRepresentations(GF(7)); irr # random arch. dependent output [ [ (1,2)(3,4), (1,2,3) ] -> [ [ [ Z(7)^0 ] ], [ [ Z(7)^4 ] ] ], [ (1,2)(3,4), (1,2,3) ] -> [ [ [ Z(7)^0 ] ], [ [ Z(7)^2 ] ] ], [ (1,2)(3,4), (1,2,3) ] -> [ [ [ Z(7)^0 ] ], [ [ Z(7)^0 ] ] ], @@ -179,16 +180,15 @@ The example below using the GAP interface illustrates the syntax. [ Z(7), Z(7)^5, Z(7)^2 ] ], [ [ 0*Z(7), Z(7)^0, 0*Z(7) ], [ 0*Z(7), 0*Z(7), Z(7)^0 ], [ Z(7)^0, 0*Z(7), 0*Z(7) ] ] ] ] - sage: gap.eval("brvals := List(irr,chi->List(ConjugacyClasses(G),c->BrauerCharacterValue(Image(chi,Representative(c)))))") - '' - sage: print(gap.eval("Display(brvals)")) # random architecture dependent output + sage: brvals = [[chi.Image(c.Representative()).BrauerCharacterValue() + ....: for c in G.ConjugacyClasses()] for chi in irr] + sage: brvals # random architecture dependent output [ [ 1, 1, E(3)^2, E(3) ], [ 1, 1, E(3), E(3)^2 ], [ 1, 1, 1, 1 ], [ 3, -1, 0, 0 ] ] - sage: print(gap.eval("T := CharacterTable(G)")) - CharacterTable( Alt( [ 1 .. 4 ] ) ) - sage: print(gap.eval("Display(T)")) + sage: T = G.CharacterTable() + sage: T.Display() CT3 2 2 . . 2 diff --git a/src/doc/en/constructions/rings.rst b/src/doc/en/constructions/rings.rst index 54a23a080b5..65557b229e1 100644 --- a/src/doc/en/constructions/rings.rst +++ b/src/doc/en/constructions/rings.rst @@ -72,16 +72,13 @@ Here is another approach using GAP: :: - sage: R = gap.new("PolynomialRing(GF(97), 4)"); R - PolynomialRing( GF(97), ["x_1", "x_2", "x_3", "x_4"] ) + sage: R = libgap.PolynomialRing(GF(97), 4); R + GF(97)[x_1,x_2,x_3,x_4] sage: I = R.IndeterminatesOfPolynomialRing(); I [ x_1, x_2, x_3, x_4 ] - sage: vars = (I.name(), I.name(), I.name(), I.name()) - sage: _ = gap.eval( - ....: "x_0 := %s[1];; x_1 := %s[2];; x_2 := %s[3];;x_3 := %s[4];;" - ....: % vars) - sage: f = gap.new("x_1*x_2+x_3"); f - x_2*x_3+x_4 + sage: x1, x2, x3, x4 = I + sage: f = x1*x2 + x3; f + x_1*x_2+x_3 sage: f.Value(I,[1,1,1,1]) Z(97)^34 diff --git a/src/doc/en/developer/coding_in_other.rst b/src/doc/en/developer/coding_in_other.rst index d1abfcb65cb..3410c9e6edc 100644 --- a/src/doc/en/developer/coding_in_other.rst +++ b/src/doc/en/developer/coding_in_other.rst @@ -161,10 +161,11 @@ convert output from PARI to Sage objects: GAP === -Wrapping a GAP function in Sage is a matter of writing a program in -Python that uses the pexpect interface to pipe various commands to GAP -and read back the input into Sage. This is sometimes easy, sometimes -hard. +Wrapping a GAP function in Sage can be done in two different ways. The +first one uses the ``pexpect`` interface to pipe various commands to +GAP and read back the input into Sage. The second way is to access GAP +through its library interface ``libgap``. We recommend the second one +for the sake of efficiency. For example, suppose we want to make a wrapper for the computation of the Cartan matrix of a simple Lie algebra. The Cartan matrix of `G_2` @@ -180,32 +181,30 @@ is available in GAP using the commands: In Sage, one can access these commands by typing:: - sage: L = gap.SimpleLieAlgebra('"G"', 2, 'Rationals'); L - Algebra( Rationals, [ v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9, v.10, - v.11, v.12, v.13, v.14 ] ) + sage: L = libgap.SimpleLieAlgebra("G", 2, QQ); L + sage: R = L.RootSystem(); R sage: R.CartanMatrix() [ [ 2, -1 ], [ -3, 2 ] ] -Note the ``'"G"'`` which is evaluated in GAP as the string ``"G"``. - The purpose of this section is to use this example to show how one might write a Python/Sage program whose input is, say, ``('G',2)`` and whose output is the matrix above (but as a Sage Matrix---see the code in the directory :sage_root:`src/sage/matrix/` and the corresponding parts of the Sage reference manual). -First, the input must be converted into strings consisting of legal -GAP commands. Then the GAP output, which is also a string, must be -parsed and converted if possible to a corresponding Sage/Python -object. +First, the input must be converted into a libgap object, either by +applying ``libgap.eval`` on a string, or by using a GAP command +``CMD`` as a ``libgap.CMD`` method. Then one can work with these +object using other GAP commands as ``libgap`` methods. At the end, one +can convert back to sage using the method ``sage`` if it works. .. skip .. CODE-BLOCK:: python - def cartan_matrix(type, rank): + def cartan_matrix(typ, rank): """ Return the Cartan matrix of given Chevalley type and rank. @@ -213,7 +212,7 @@ object. - type -- a Chevalley letter name, as a string, for a family type of simple Lie algebras - - rank -- an integer (legal for that type). + - rank -- an integer (legal for that type) EXAMPLES:: @@ -227,23 +226,16 @@ object. [ 2 -1] [-3 2] """ - L = gap.SimpleLieAlgebra('"%s"' % type, rank, 'Rationals') + L = libgap.SimpleLieAlgebra(typ, rank, libgap.Rationals) R = L.RootSystem() sM = R.CartanMatrix() - ans = eval(str(sM)) + ans = sM.sage() MS = MatrixSpace(QQ, rank) return MS(ans) The output ``ans`` is a Python list. The last two lines convert that list to an instance of the Sage class ``Matrix``. -Alternatively, one could replace the first line of the above function -with this: - -.. CODE-BLOCK:: python - - L = gap.new('SimpleLieAlgebra("%s", %s, Rationals);'%(type, rank)) - Defining "easy" and "hard" is subjective, but here is one definition. Wrapping a GAP function is "easy" if there is already a corresponding class in Python or Sage for the output data type of the GAP function diff --git a/src/doc/en/developer/conf.py b/src/doc/en/developer/conf.py index e8877d61770..6e80447f4c7 100644 --- a/src/doc/en/developer/conf.py +++ b/src/doc/en/developer/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 75cc659934f..71145b8082a 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -745,10 +745,10 @@ In order to run the long tests as well, do the following:: cpu time: 25.2 seconds cumulative wall time: 34.7 seconds -To find tests that take longer than the allowed time use the -``--warn-long`` flag. Without any options it will cause tests to -print a warning if they take longer than 1.0 second. Note that this is -a warning, not an error:: +To find tests that take longer than a specified amount of CPU time, +use the ``--warn-long`` flag. Without any options, it will cause a +warning to be printed if any tests take longer than one +cpu-second. Note that this is a warning, not an error:: [roed@localhost sage]$ ./sage -t --warn-long src/sage/rings/factorint.pyx Running doctests with ID 2012-07-14-03-27-03-2c952ac1. @@ -758,22 +758,22 @@ a warning, not an error:: File "src/sage/rings/factorint.pyx", line 125, in sage.rings.factorint.base_exponent Failed example: base_exponent(-4) - Test ran for 4.09 s + Test ran for 4.09 cpu seconds ********************************************************************** File "src/sage/rings/factorint.pyx", line 153, in sage.rings.factorint.factor_aurifeuillian Failed example: fa(2^6+1) - Test ran for 2.22 s + Test ran for 2.22 cpu seconds ********************************************************************** File "src/sage/rings/factorint.pyx", line 155, in sage.rings.factorint.factor_aurifeuillian Failed example: fa(2^58+1) - Test ran for 2.22 s + Test ran for 2.22 cpu seconds ********************************************************************** File "src/sage/rings/factorint.pyx", line 163, in sage.rings.factorint.factor_aurifeuillian Failed example: fa(2^4+1) - Test ran for 2.25 s + Test ran for 2.25 cpu seconds ********************************************************************** ---------------------------------------------------------------------- All tests passed! @@ -792,12 +792,12 @@ You can also pass in an explicit amount of time:: File "tests.py", line 240, in sage.rings.tests.test_random_elements Failed example: sage.rings.tests.test_random_elements(trials=1000) # long time (5 seconds) - Test ran for 13.36 s + Test ran for 13.36 cpu seconds ********************************************************************** File "tests.py", line 283, in sage.rings.tests.test_random_arith Failed example: sage.rings.tests.test_random_arith(trials=1000) # long time (5 seconds?) - Test ran for 12.42 s + Test ran for 12.42 cpu seconds ********************************************************************** ---------------------------------------------------------------------- All tests passed! diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 7dc5cefd440..bc4f982ffe0 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -4,14 +4,6 @@ Welcome to Sage Developer Guide =============================== -.. NOTE:: - - Sage development moved to `GitHub `_ in - February 2023, from `the Sage Trac server `_, - which had been the center of Sage development for a long time. After the - transition, this guide was updated accordingly. However, the legacy is - still with us in many aspects of the current Sage development. - Everybody who uses Sage is encouraged to contribute something back to Sage at some point. You could: diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index d59e5dc07e9..2b6903ca466 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -1010,11 +1010,11 @@ to refer to the dot-separated components of a version by ``VERSION_MAJOR``, ``VERSION_MINOR``, and ``VERSION_MICRO``. For Python packages available from PyPI, you should use an -``upstream_url`` from ``pypi.io``, which follows the format +``upstream_url`` from ``files.pythonhosted.org``, which follows the format .. CODE-BLOCK:: bash - upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz + upstream_url=https://files.pythonhosted.org/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz Developers who wish to test a package update from a PR branch before the archive is available on a Sage mirror. Sage falls back to diff --git a/src/doc/en/developer/portability_platform_table.rst b/src/doc/en/developer/portability_platform_table.rst index 617dfd0f868..d8d56329d4d 100644 --- a/src/doc/en/developer/portability_platform_table.rst +++ b/src/doc/en/developer/portability_platform_table.rst @@ -1564,59 +1564,167 @@ .. |codespace-fedora-40-maximal| image:: https://github.com/codespaces/badge.svg :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-fedora-40-maximal%2Fdevcontainer.json -.. |image-centos-stream-9-python3.9-minimal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-system-packages +.. |image-fedora-41-minimal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-minimal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-minimal-with-system-packages -.. |image-centos-stream-9-python3.9-minimal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-minimal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-minimal-configured +.. |image-fedora-41-minimal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-minimal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-minimal-configured -.. |image-centos-stream-9-python3.9-minimal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%23677895 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets-pre +.. |image-fedora-41-minimal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-minimal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%23677895 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-minimal-with-targets-pre -.. |image-centos-stream-9-python3.9-minimal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%236686c1 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets +.. |image-fedora-41-minimal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-minimal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%236686c1 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-minimal-with-targets -.. |image-centos-stream-9-python3.9-minimal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%236495ed - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-minimal-with-targets-optional +.. |image-fedora-41-minimal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-minimal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%236495ed + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-minimal-with-targets-optional -.. |codespace-centos-stream-9-python3.9-minimal| image:: https://github.com/codespaces/badge.svg - :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.9-minimal%2Fdevcontainer.json +.. |codespace-fedora-41-minimal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-fedora-41-minimal%2Fdevcontainer.json -.. |image-centos-stream-9-python3.9-standard-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-system-packages +.. |image-fedora-41-standard-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-standard-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-standard-with-system-packages -.. |image-centos-stream-9-python3.9-standard-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-standard-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-standard-configured +.. |image-fedora-41-standard-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-standard-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-standard-configured -.. |image-centos-stream-9-python3.9-standard-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%235d8a4c - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets-pre +.. |image-fedora-41-standard-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-standard-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%235d8a4c + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-standard-with-targets-pre -.. |image-centos-stream-9-python3.9-standard-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%2350ab2e - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets +.. |image-fedora-41-standard-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-standard-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%2350ab2e + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-standard-with-targets -.. |image-centos-stream-9-python3.9-standard-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%2344cc11 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-standard-with-targets-optional +.. |image-fedora-41-standard-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-standard-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%2344cc11 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-standard-with-targets-optional -.. |codespace-centos-stream-9-python3.9-standard| image:: https://github.com/codespaces/badge.svg - :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.9-standard%2Fdevcontainer.json +.. |codespace-fedora-41-standard| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-fedora-41-standard%2Fdevcontainer.json -.. |image-centos-stream-9-python3.9-maximal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-system-packages +.. |image-fedora-41-maximal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-maximal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-maximal-with-system-packages -.. |image-centos-stream-9-python3.9-maximal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-maximal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-maximal-configured +.. |image-fedora-41-maximal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-maximal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-maximal-configured -.. |image-centos-stream-9-python3.9-maximal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%238f6b8d - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets-pre +.. |image-fedora-41-maximal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-maximal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%238f6b8d + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-maximal-with-targets-pre -.. |image-centos-stream-9-python3.9-maximal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%23b46eb2 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets +.. |image-fedora-41-maximal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-maximal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%23b46eb2 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-maximal-with-targets -.. |image-centos-stream-9-python3.9-maximal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%23da70d6 - :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.9-maximal-with-targets-optional +.. |image-fedora-41-maximal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-fedora-41-maximal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%23da70d6 + :target: https://ghcr.io/sagemath/sage/sage-fedora-41-maximal-with-targets-optional -.. |codespace-centos-stream-9-python3.9-maximal| image:: https://github.com/codespaces/badge.svg - :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.9-maximal%2Fdevcontainer.json +.. |codespace-fedora-41-maximal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-fedora-41-maximal%2Fdevcontainer.json + +.. |image-centos-stream-9-minimal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-minimal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-minimal-with-system-packages + +.. |image-centos-stream-9-minimal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-minimal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-minimal-configured + +.. |image-centos-stream-9-minimal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-minimal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%23677895 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-minimal-with-targets-pre + +.. |image-centos-stream-9-minimal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-minimal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%236686c1 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-minimal-with-targets + +.. |image-centos-stream-9-minimal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-minimal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%236495ed + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-minimal-with-targets-optional + +.. |codespace-centos-stream-9-minimal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-minimal%2Fdevcontainer.json + +.. |image-centos-stream-9-standard-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-standard-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-standard-with-system-packages + +.. |image-centos-stream-9-standard-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-standard-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-standard-configured + +.. |image-centos-stream-9-standard-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-standard-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%235d8a4c + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-standard-with-targets-pre + +.. |image-centos-stream-9-standard-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-standard-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%2350ab2e + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-standard-with-targets + +.. |image-centos-stream-9-standard-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-standard-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%2344cc11 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-standard-with-targets-optional + +.. |codespace-centos-stream-9-standard| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-standard%2Fdevcontainer.json + +.. |image-centos-stream-9-maximal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-maximal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-maximal-with-system-packages + +.. |image-centos-stream-9-maximal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-maximal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-maximal-configured + +.. |image-centos-stream-9-maximal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-maximal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%238f6b8d + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-maximal-with-targets-pre + +.. |image-centos-stream-9-maximal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-maximal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%23b46eb2 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-maximal-with-targets + +.. |image-centos-stream-9-maximal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-maximal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%23da70d6 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-maximal-with-targets-optional + +.. |codespace-centos-stream-9-maximal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-maximal%2Fdevcontainer.json + +.. |image-centos-stream-9-python3.12-minimal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-system-packages + +.. |image-centos-stream-9-python3.12-minimal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-minimal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-minimal-configured + +.. |image-centos-stream-9-python3.12-minimal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%23677895 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets-pre + +.. |image-centos-stream-9-python3.12-minimal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%236686c1 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets + +.. |image-centos-stream-9-python3.12-minimal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%236495ed + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-minimal-with-targets-optional + +.. |codespace-centos-stream-9-python3.12-minimal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.12-minimal%2Fdevcontainer.json + +.. |image-centos-stream-9-python3.12-standard-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-system-packages + +.. |image-centos-stream-9-python3.12-standard-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-standard-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-standard-configured + +.. |image-centos-stream-9-python3.12-standard-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%235d8a4c + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets-pre + +.. |image-centos-stream-9-python3.12-standard-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%2350ab2e + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets + +.. |image-centos-stream-9-python3.12-standard-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%2344cc11 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-standard-with-targets-optional + +.. |codespace-centos-stream-9-python3.12-standard| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.12-standard%2Fdevcontainer.json + +.. |image-centos-stream-9-python3.12-maximal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-system-packages + +.. |image-centos-stream-9-python3.12-maximal-configured| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-maximal-configured/latest_tag?ignore=latest,dev,*-failed&label=configured&color=%23696969 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-maximal-configured + +.. |image-centos-stream-9-python3.12-maximal-with-targets-pre| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets-pre/latest_tag?ignore=latest,dev,*-failed&label=with-targets-pre&color=%238f6b8d + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets-pre + +.. |image-centos-stream-9-python3.12-maximal-with-targets| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets/latest_tag?ignore=latest,dev,*-failed&label=with-targets&color=%23b46eb2 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets + +.. |image-centos-stream-9-python3.12-maximal-with-targets-optional| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets-optional/latest_tag?ignore=latest,dev,*-failed&label=with-targets-optional&color=%23da70d6 + :target: https://ghcr.io/sagemath/sage/sage-centos-stream-9-python3.12-maximal-with-targets-optional + +.. |codespace-centos-stream-9-python3.12-maximal| image:: https://github.com/codespaces/badge.svg + :target: https://codespaces.new/sagemath/sage?devcontainer_path=.devcontainer%2Fportability-centos-stream-9-python3.12-maximal%2Fdevcontainer.json .. |image-almalinux-8-python3.9-minimal-with-system-packages| image:: https://ghcr-badge.egpl.dev/sagemath/sage/sage-almalinux-8-python3.9-minimal-with-system-packages/size?tag=dev&label=with-system-packages&color=%23696969 :target: https://ghcr.io/sagemath/sage/sage-almalinux-8-python3.9-minimal-with-system-packages @@ -2593,16 +2701,38 @@ * -    ‑*maximal* - |image-fedora-40-maximal-with-system-packages| |image-fedora-40-maximal-with-targets-pre| - - * - **centos**-stream-9-python3.9 + * - **fedora**-41 + +    ‑*minimal* + - |image-fedora-41-minimal-with-system-packages| |image-fedora-41-minimal-with-targets-pre| |image-fedora-41-minimal-with-targets| |image-fedora-41-minimal-with-targets-optional| + - |codespace-fedora-41-minimal| + * -    ‑*standard* + - |image-fedora-41-standard-with-system-packages| |image-fedora-41-standard-with-targets-pre| |image-fedora-41-standard-with-targets| |image-fedora-41-standard-with-targets-optional| + - |codespace-fedora-41-standard| + * -    ‑*maximal* + - |image-fedora-41-maximal-with-system-packages| |image-fedora-41-maximal-with-targets-pre| + - + * - **centos**-stream-9 + +    ‑*minimal* + - |image-centos-stream-9-minimal-with-system-packages| |image-centos-stream-9-minimal-with-targets-pre| |image-centos-stream-9-minimal-with-targets| |image-centos-stream-9-minimal-with-targets-optional| + - |codespace-centos-stream-9-minimal| + * -    ‑*standard* + - |image-centos-stream-9-standard-with-system-packages| |image-centos-stream-9-standard-with-targets-pre| |image-centos-stream-9-standard-with-targets| |image-centos-stream-9-standard-with-targets-optional| + - |codespace-centos-stream-9-standard| + * -    ‑*maximal* + - |image-centos-stream-9-maximal-with-system-packages| |image-centos-stream-9-maximal-with-targets-pre| + - + * - **centos**-stream-9-python3.12    ‑*minimal* - - |image-centos-stream-9-python3.9-minimal-with-system-packages| |image-centos-stream-9-python3.9-minimal-with-targets-pre| |image-centos-stream-9-python3.9-minimal-with-targets| |image-centos-stream-9-python3.9-minimal-with-targets-optional| - - |codespace-centos-stream-9-python3.9-minimal| + - |image-centos-stream-9-python3.12-minimal-with-system-packages| |image-centos-stream-9-python3.12-minimal-with-targets-pre| |image-centos-stream-9-python3.12-minimal-with-targets| |image-centos-stream-9-python3.12-minimal-with-targets-optional| + - |codespace-centos-stream-9-python3.12-minimal| * -    ‑*standard* - - |image-centos-stream-9-python3.9-standard-with-system-packages| |image-centos-stream-9-python3.9-standard-with-targets-pre| |image-centos-stream-9-python3.9-standard-with-targets| |image-centos-stream-9-python3.9-standard-with-targets-optional| - - |codespace-centos-stream-9-python3.9-standard| + - |image-centos-stream-9-python3.12-standard-with-system-packages| |image-centos-stream-9-python3.12-standard-with-targets-pre| |image-centos-stream-9-python3.12-standard-with-targets| |image-centos-stream-9-python3.12-standard-with-targets-optional| + - |codespace-centos-stream-9-python3.12-standard| * -    ‑*maximal* - - |image-centos-stream-9-python3.9-maximal-with-system-packages| |image-centos-stream-9-python3.9-maximal-with-targets-pre| + - |image-centos-stream-9-python3.12-maximal-with-system-packages| |image-centos-stream-9-python3.12-maximal-with-targets-pre| - * - **almalinux**-8-python3.9 diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst index 0a55393955b..cdcbd42c375 100644 --- a/src/doc/en/developer/review.rst +++ b/src/doc/en/developer/review.rst @@ -76,6 +76,13 @@ The following should generally be checked while reading and testing the code: optional doctests related to the functionality. See :ref:`chapter-doctesting` for more information. +For changes that affect the **user interface**, in particular, upgrades to +IPython and Jupyter component packages, manual testing is crucial because +our automatic tests do not cover the user interface. We recommend to use +a `Jupyter notebook with comprehensive tests of graphics and typesetting +`_, +some of which is Sage-specific. + You are now ready to change the PR's status (see :ref:`section-github-pr-status`): diff --git a/src/doc/en/faq/conf.py b/src/doc/en/faq/conf.py index 4c8a71bd9d1..a175e9cf0ee 100644 --- a/src/doc/en/faq/conf.py +++ b/src/doc/en/faq/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index d03881c36a2..393a9c369e5 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -479,35 +479,6 @@ How do I run sage in daemon mode, i.e. as a service? There are several possibilities. Use ``screen``, ``nohup`` or ``disown``. -The show command for plotting 3-D objects does not work. -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -The default live 3-D plotting for Sage 6.4+ uses -`Jmol/JSmol `_ -for viewing. From the command line the Jmol Java application is used, -and for in browser viewing either pure javascript or a Java applet -is used. By default in browsers pure javascript is used to avoid -the problems with some browsers that do not support java applet -plugins (namely Chrome). On each browser worksheet there is a -checkbox which must be checked before a 3-D plot is generated if -the user wants to use the Java applet (the applet is a little faster -with complex plots). - -The most likely reason for a malfunction is that you do not have -a Java Run Time Environment (JRE) installed or you have one older than -version 1.7. If things work from the command line another possibility -is that your browser does not have the proper plugin to support Java -applets (at present, 2014, plugins do not work with most versions of -Chrome). Make sure you have installed either the IcedTea browser -plugin (for linux see your package manager), see: -`IcedTea `_, -or the Oracle Java plugin see: -`Java `_. - -If you are using a Sage server over the web and even javascript rendering -does not work, you may have a problem with your browser's javascript -engine or have it turned off. - May I use Sage tools in a commercial environment? """"""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/doc/en/installation/conda.rst b/src/doc/en/installation/conda.rst index 566fea4ff1a..69369767854 100644 --- a/src/doc/en/installation/conda.rst +++ b/src/doc/en/installation/conda.rst @@ -7,7 +7,7 @@ SageMath can be installed on Linux and macOS via Conda from the `conda-forge `_ conda channel. Both the ``x86_64`` (Intel) architecture and the ``arm64``/``aarch64`` -architectures (including Apple Silicon, M1) are supported. +architectures (including Apple Silicon, M1, M2, M3, M4) are supported. You will need a working Conda installation: either Miniforge (or Mambaforge), Miniconda or Anaconda. If you don't have one yet, we recommend installing diff --git a/src/doc/en/installation/conf.py b/src/doc/en/installation/conf.py index ea6956d2678..7e6174b0d90 100644 --- a/src/doc/en/installation/conf.py +++ b/src/doc/en/installation/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/installation/index.rst b/src/doc/en/installation/index.rst index f88a92dc5c7..5e60d65ebb0 100644 --- a/src/doc/en/installation/index.rst +++ b/src/doc/en/installation/index.rst @@ -11,8 +11,7 @@ was made. More up-to-date information and details regarding supported platforms may have become available afterwards and can be found in the section "Availability and installation help" of the -`release tour `_ for each -SageMath release. +`release tour for each SageMath release `_. **Where would you like to run SageMath?** Pick one of the following sections. @@ -218,6 +217,7 @@ More information: binary conda source + meson launching troubles diff --git a/src/doc/en/installation/meson.rst b/src/doc/en/installation/meson.rst new file mode 100644 index 00000000000..b8e44bc12fc --- /dev/null +++ b/src/doc/en/installation/meson.rst @@ -0,0 +1,122 @@ +.. _build-source-meson: + +================================ +Building from source using Meson +================================ + +This is a short guide on how to build the Sage from source using Meson. + +Walkthrough +=========== + +Assume we're starting from a clean repo and a fully set up conda environment: + +.. CODE-BLOCK:: shell-session + + $ ./bootstrap-conda + $ mamba env create --file src/environment-dev-3.11.yml --name sage-dev + $ conda activate sage-dev + +Alternatively, install all build requirements as described in section +:ref:`section-prereqs`. In the likely case that you have to install some +dependencies manually, set the correct environment variables to point +to the installed libraries: + +.. CODE-BLOCK:: shell-session + + $ export C_INCLUDE_PATH=$C_INCLUDE_PATH:/your/path/to/include + $ export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/your/path/to/include + $ export LIBRARY_PATH=$LIBRARY_PATH:/your/path/to/lib + +.. NOTE:: + + If you have previously build Sage in-place, you first have to delete the + already compiled files, e.g. with ``shopt -s globstar`` followed by + ``rm src/**/*.so`` or ``for f in src/**/*.so ; do mv "$f" "$f.old"; done``. + Moreover, remove the old generated files with + ``find src/sage/ext/interpreters -type f ! -name 'meson.build' -delete``. + Also uninstall the 'old' sage packages with ``pip uninstall sage-conf sage-setup sagemath-standard``. + +To compile and install the project in editable install, just use: + +.. CODE-BLOCK:: shell-session + + $ pip install --no-build-isolation --editable . + +This will install Sage in the current Python environment. +In a Conda environment, the ``--no-build-isolation`` flag is necessary to +allow the build system to reuse the already installed build dependencies. +If you don't use Conda, you can omit this flag. + +You can then start Sage from the command line with ``./sage`` +or run the tests with ``./sage -t``. + +.. NOTE:: + + By using ``pip install --editable`` in the above steps, the Sage library + is installed in editable mode. This means that when you only edit source + files, there is no need to rebuild the library; it suffices to restart Sage. + Note that this even works when you edit Cython files, so you no longer need + to manually compile after editing Cython files. + +.. NOTE:: + + Note that ``make`` is not used at all, nor is ``configure``. + This means that any Sage-the-distribution commands such as ``sage -i`` + will not work. + +Background information +====================== + +Under the hood, pip invokes meson to configure and build the project. +We can also use meson directly as follows. + +To configure the project, we need to run the following command: + +.. CODE-BLOCK:: shell-session + + $ meson setup builddir --prefix=$PWD/build-install + +This will create a build directory ``builddir`` that will hold the build artifacts. +The ``--prefix`` option specifies the directory where the Sage will be installed. +To compile the project, run the following command: + +.. CODE-BLOCK:: shell-session + + $ meson compile -C builddir + +Installing is done with the following command: + +.. CODE-BLOCK:: shell-session + + $ meson install -C builddir + +This will then install in the directory specified by ``--prefix``, e.g. +``build-install/lib/python3.11/site-packages/sage``. +Usually, this directory is not on your Python path, so you have to use: + +.. CODE-BLOCK:: shell-session + + $ PYTHONPATH=build-install/lib/python3.11/site-packages ./sage + +Alternatively, we can still use pip to install: + +.. CODE-BLOCK:: shell-session + + $ pip install --no-build-isolation --config-settings=builddir=builddir --editable . + +.. tip:: + + Package maintainers may want to specify further build options or need + to install to a different directory than the install prefix. + Both are supported naturally by Meson: + + .. CODE-BLOCK:: shell-session + + $ meson setup builddir --prefix=/usr --libdir=... -Dcpp_args=... + $ meson compile -C builddir + $ DESTDIR=/path/to/staging/root meson install -C builddir + + See `Meson's quick guide `_ + and `Meson's install guide `_ + for more information. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index f2e40ed3982..12b1483dfa2 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -370,6 +370,12 @@ does not raise an :class:`ImportError`, then it worked. Installation steps ------------------ +.. hint:: + + The following steps use the classical ``./configure && make`` build + process. The modern Meson build system is also supported, see + :ref:`build-source-meson`. + #. Follow the procedure in the file `README.md `_ in ``SAGE_ROOT``. diff --git a/src/doc/en/installation/troubles.rst b/src/doc/en/installation/troubles.rst index 4a7c6d5581b..22d4e4772da 100644 --- a/src/doc/en/installation/troubles.rst +++ b/src/doc/en/installation/troubles.rst @@ -8,8 +8,8 @@ the :ref:`sec-installation-from-sources` or use one of the alternatives proposed at the end of :ref:`installation-guide`. If you have any problems building or running Sage, please take a look -at the Installation FAQ in the `Sage Release Tour -`_ corresponding to the version +at the `release tour +`_ corresponding to the version that you are installing. It may offer version-specific installation help that has become available after the release was made and is therefore not covered by this manual. diff --git a/src/doc/en/prep/Calculus.rst b/src/doc/en/prep/Calculus.rst index c2f636be558..d33b3082040 100644 --- a/src/doc/en/prep/Calculus.rst +++ b/src/doc/en/prep/Calculus.rst @@ -316,8 +316,9 @@ help it look nicer in the browser? - 1/10*(sqrt(5) - 3)*log(2*x^2 + x*(sqrt(5) - 1) + 2)/(sqrt(5) - 1) + 1/5*log(x + 1) -Some integrals are a little tricky, of course. Sage tries hard to integrate using Maxima, Giac and Sympy:: +Some integrals are a little tricky, of course. Sage tries hard to integrate using Maxima, Sympy, and (optionally, if installed) Giac. The following can only be integrated by Giac:: + sage: # needs sage.libs.giac sage: integral(1/(1+x^10),x) ...1/20*(sqrt(5) + 1)*arctan((4*x + sqrt(-2*sqrt(5) + 10))/(sqrt(5) + 1)) + 1/20*(sqrt(5) + 1)*arctan((4*x - sqrt(-2*sqrt(5) + 10))/(sqrt(5) + 1)) diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index 3bc9308669b..c97efdecee4 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -384,11 +384,6 @@ Below, you can experiment with several of the plotting options. Basic 3D Plotting ----------------- -There are several mechanisms for viewing three\-dimensional plots in -Sage, but we will stick to the default option in the notebook interface, -which is via javascript applets from the program `Jmol/JSmol -`_ . - Plotting a 3D plot is similar to plotting a 2D plot, but we need to specify ranges for two variables instead of one. diff --git a/src/doc/en/prep/conf.py b/src/doc/en/prep/conf.py index 645d451644d..03ff6ed7afc 100644 --- a/src/doc/en/prep/conf.py +++ b/src/doc/en/prep/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/reference/conf.py b/src/doc/en/reference/conf.py index 984c43363f2..92b8a8d45b4 100644 --- a/src/doc/en/reference/conf.py +++ b/src/doc/en/reference/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release, latex_elements, exclude_patterns from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py index 479e9c306d2..c7adc1da994 100644 --- a/src/doc/en/reference/conf_sub.py +++ b/src/doc/en/reference/conf_sub.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release, exclude_patterns from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index f681c083a08..34c5f134839 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -84,6 +84,7 @@ Libraries of algorithms sage/graphs/spanning_tree sage/graphs/pq_trees sage/graphs/trees + sage/graphs/matching sage/graphs/matchpoly sage/graphs/genus sage/graphs/lovasz_theta @@ -98,6 +99,7 @@ Libraries of algorithms sage/graphs/graph_decompositions/bandwidth sage/graphs/graph_decompositions/cutwidth sage/graphs/graph_decompositions/graph_products + sage/graphs/graph_decompositions/slice_decomposition sage/graphs/graph_decompositions/modular_decomposition sage/graphs/graph_decompositions/clique_separators sage/graphs/convexity_properties diff --git a/src/doc/en/reference/knots/index.rst b/src/doc/en/reference/knots/index.rst index ed42964e5a5..999a75a97dc 100644 --- a/src/doc/en/reference/knots/index.rst +++ b/src/doc/en/reference/knots/index.rst @@ -7,5 +7,6 @@ Knot Theory sage/knots/knot sage/knots/link sage/knots/knotinfo + sage/knots/free_knotinfo_monoid .. include:: ../footer.txt diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index 0035c50021f..a3b32b51ce7 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -62,6 +62,16 @@ Infinite Polynomial Rings sage/rings/polynomial/symmetric_ideal sage/rings/polynomial/symmetric_reduction +Tropical Polynomials +-------------------- + +.. toctree:: + :maxdepth: 1 + + sage/rings/semirings/tropical_polynomial + sage/rings/semirings/tropical_mpolynomial + sage/rings/semirings/tropical_variety + Boolean Polynomials ------------------- diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 872f054f67f..3d548b48511 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -857,6 +857,9 @@ REFERENCES: .. [BrHu2019] Petter Brändén, June Huh. *Lorentzian polynomials*. Ann. Math. (2) 192, No. 3, 821-891 (2020). :arxiv:`1902.03719`, :doi:`10.4007/annals.2020.192.3.4`. + +.. [Bru2014] Erwan Brugalle and Kristin Shaw. *A bit of tropical geometry*. + Amer. Math. Monthly, 121(7):563-589, 2014. .. [BHNR2004] \S. Brlek, S. Hamel, M. Nivat, C. Reutenauer, On the Palindromic Complexity of Infinite Words, @@ -1323,6 +1326,9 @@ REFERENCES: no. 1 (2003): 97-111, http://www.moi.math.bas.bg/moiuser/~iliya/pdf_site/gf5srev.pdf. +.. [BS2007] \R. Bröker and P. Stevenhagen. *Constructing elliptic curves of + prime order*. [math.NT] (2007), :arXiv:`0712.2022`. + .. [BS2010] \P. Baseilhac and K. Shigechi. *A new current algebra and the reflection equation*. Lett. Math. Phys. **92** (2010), pp. 47-65. :arxiv:`0906.1482`. @@ -1437,6 +1443,11 @@ REFERENCES: for closed Riemannian manifolds*, Ann. of Math. (2) 45 (1944), 747–752. +.. [CHNP2020] Kieran Clancy, Michael Haythorpe, Alex Newcombe and Ed Pegg Jr, + *There Are No Cubic Graphs on 26 Vertices with Crossing Number 10 + or 11*, Graphs and Combinatorics 36, pages: 1713 -- 1721, (2020), + :doi:`10.1007/s00373-020-02204-6`. + .. [CP2023] \M. Cati and D.V. Pasechnik. *Implementing Hadamard Matrices in SageMath*. Preprint, :arxiv:`2306.16812`, (2023). @@ -1694,6 +1705,10 @@ REFERENCES: bi-matrix games*. http://vknight.org/unpeudemath/code/2015/06/25/on_testing_degeneracy_of_games/ (2015) +.. [CKWL2019] Marcelo H. de Carvalho, Nishad Kothari, Xiumei Wang and Yixun + Linc. *Birkhoff-von Neumann graphs that are PM-compact*. 2019. + :doi:`10.48550/arXiv.1807.07339`. + .. [CL1996] Chartrand, G. and Lesniak, L.: *Graphs and Digraphs*. Chapman and Hall/CRC, 1996. @@ -1710,12 +1725,20 @@ REFERENCES: Yokonuma-Hecke algebras and the HOMFLYPT polynomial*. (2015) :arxiv:`1204.1871v4`. +.. [CL2023] Xavier Caruso and Antoine Leudière. + *Algorithms for computing norms and characteristic polynomials on general Drinfeld modules*, (2023) :arxiv:`2307.02879`. + .. [Cle1872] Alfred Clebsch, *Theorie der binären algebraischen Formen*, Teubner, 1872. .. [CLG1997] Frank Celler and C. R. Leedham-Green, *Calculating the Order of an Invertible Matrix*, 1997 +.. [CLM2006] Marcelo H. de Carvalho, Cláudio L. Lucchesi and U.S.R. Murty, + *How to build a brick*, Discrete Mathematics, Volume 306, + Issues 19--20, Pages 2383--2410,ISSN 0012--365X, (2006), + :doi:`10.1016/j.disc.2005.12.032`. + .. [CLRS2001] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein, *Section 22.4: Topological sort*, Introduction to Algorithms (2nd ed.), MIT Press and @@ -1976,6 +1999,10 @@ REFERENCES: .. [CS2018] Craig Costello and Benjamin Smith: Montgomery curves and their arithmetic. J. Cryptogr. Eng. 8 (2018), 227-240. +.. [CS2022] \Logan Crew, and Sophie Spirkl. *Modular relations of the Tutte symmetric function*, + Journal of Combinatorial Theory, Series A, Volume 187, 2022, 105572, + :doi:`10.1016/j.jcta.2021.105572.` + .. [CST2010] Tullio Ceccherini-Silberstein, Fabio Scarabotti, Filippo Tolli. *Representation Theory of the Symmetric Groups: The @@ -2612,6 +2639,15 @@ REFERENCES: Wehler K3 Surfaces over finite fields*. New Zealand Journal of Mathematics 45 (2015), 19–31. +.. [FiLi2001] Ilse Fischer and Charles H.C. Little, *A Characterisation of + Pfaffian Near Bipartite Graphs*, Journal of Combinatorial Theory, + Series B, vol. 82, issue 2, (2001), pages: 175 -- 222, ISSN: + 0095 -- 8956, :doi:`10.1006/jctb.2000.2025`. + +.. [Fil2017] Ivana Filipan, *An Invitation to Combinatorial Tropical Geometry*. + Conference: 1st Croatian Combinatorial Days. 2017. + :doi:`10.5592/CO/CCD.2016.05`. + .. [FIV2012] \H. Fournier, A. Ismail, and A. Vigneron. *Computing the Gromov hyperbolicity of a discrete metric space*. 2012. :arxiv:`1210.3323`. @@ -2804,6 +2840,10 @@ REFERENCES: .. [Gek1991] \E.-U. Gekeler. On finite Drinfeld modules. Journal of algebra, 1(141):187–203, 1991. +.. [Gek2008] \E.-U. Gekeler. Frobenius Distributions of Drinfeld Modules over + Finite Fields. Transactions of the American Mathematical Society, + Volume 360 (2008), no. 4. + .. [Gek2017] \E.-U. Gekeler. On Drinfeld modular forms of higher rank. Journal de théorie des nombres de Bordeaux, Volume 29 (2017) no. 3, pp. 875-902. :doi:`10.5802/jtnb.1005` @@ -2881,6 +2921,9 @@ REFERENCES: "Pyjamask" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/Pyjamask-spec.pdf +.. [Gou2020] Fernando Q. Gouvêa, *$p$-adic Numbers: An Introduction*, + Third Edition. Springer Nature Switzerland AG, 2020. + .. [GJK+2014] Dimitar Grantcharov, Ji Hye Jung, Seok-Jin Kang, Masaki Kashiwara, Myungho Kim. *Crystal bases for the quantum queer superalgebra and semistandard decomposition tableaux.*; Trans. Amer. Math. Soc., @@ -3926,6 +3969,10 @@ REFERENCES: 47:95-104, 1997. :doi:`10.1023/A:1022444205860` +.. [KM2015] Nishad Kothari and U.S.R. Murty. *K4-free and C6¯-free Planar + Matching Covered Graphs.* Journal of Graph Theory. 82. (2015) + :doi:`10.1002/jgt.21882`. + .. [KMAUTOM2000] Masayuki Kanda, Shiho Moriai, Kazumaro Aoki, Hiroki Ueda, Youichi Takashima, Kazuo Ohta, and Tsutomu Matsumoto, *E2 - a new 128-bit block cipher*; in IEICE Transactions on @@ -4467,6 +4514,11 @@ REFERENCES: IEEE Trans. Inf. Th. 25(1979), 1-7. :doi:`10.1109/TIT.1979.1055985`. +.. [Lov1983] László Lovász, + *Ear-decompositions of matching-covered graphs*, + Combinatorica 3, 105--117 (1983) + :doi:`10.1007/BF02579346`. + .. [LP2007] \G. Leander and A. Poschmann, *On the Classification of 4 Bit S-boxes*; in WAIFI, (2007), pp. 159-176. @@ -4621,6 +4673,12 @@ REFERENCES: measures*. Publications Mathématiques de l'Institut des Hautes Études Scientifiques 98(1) (2003), pp. 167-212. +.. [LZ2001] Dingjun Lou and Ning Zhong. *A highly efficient algorithm to + determine bicritical graphs.* In: Wang, J. (eds) Computing and + Combinatorics. Lecture Notes in Computer Science, vol 2108, + pages 349--356. Springer, Berlin, Heidelberg, 2001, + :doi:`10.1007/3-540-44679-6_38`. + .. [LZ2004] S. Lando and A. Zvonkine, "Graphs on surfaces and their applications", Springer-Verlag, 2004. @@ -5147,6 +5205,10 @@ REFERENCES: :doi:`10.1007/s00453-006-1225-y`, http://www.cs.uoi.gr/~stavros/C-Papers/C-2004-SODA.pdf +.. [NT2007] Serguei Norine and Robin Thomas. *Minimally Non-Pfaffian Graphs*. + Combinatorica, vol. 27, no. 5, pages: 587 -- 600, Springer. 2007. + :doi:`10.1016/j.jctb.2007.12.005`. + .. [Nur2004] K. Nurmela. *Upper bounds for covering arrays by tabu search*. Discrete Applied Math., 138 (2004), 143-152. @@ -5658,6 +5720,11 @@ REFERENCES: .. [RS2012] G. Rudolph and M. Schmidt, "Differential Geometry and Mathematical Physics. Part I. Manifolds, Lie Groups and Hamiltonian Systems", Springer, 2012. +.. [RST2019] Neil Robertson, Paul Seymour and Robin Thomas, *Excluded minors in + cubic graphs*, Journal of Combinatorial Theory, Series B, vol. 138, + (2019), pages: 219 -- 285, ISSN: 0095 -- 8956, + :doi:`10.1016/j.jctb.2019.02.002`. + .. [RSW2011] Victor Reiner, Franco Saliola, Volkmar Welker. *Spectra of Symmetrized Shuffling Operators*. :arxiv:`1102.2460v2`. @@ -6091,6 +6158,11 @@ REFERENCES: of the chromatic polynomial of a graph*, Adv. Math., ***111*** no.1 (1995), 166-194. :doi:`10.1006/aima.1995.1020`. +.. [Sta1998] \R. P. Stanley, *Graph colorings and related symmetric functions: + ideas and applications A description of results, interesting applications, + & notable open problems*, Discrete Mathematics, 193, no.1-3, (1998), + 267-286. :doi:`10.1016/S0012-365X(98)00146-0`. + .. [Sta2002] Richard P. Stanley, *The rank and minimal border strip decompositions of a skew partition*, @@ -6398,7 +6470,7 @@ REFERENCES: .. [TCHP2008] Marc Tedder, Derek Corneil, Michel Habib and Christophe Paul, *Simple, linear-time modular decomposition*, 2008. - :arxiv:`0710.3901`. + :arxiv:`0710.3901v3`. .. [Tee1997] Tee, Garry J. "Continuous branches of inverses of the 12 Jacobi elliptic functions for real @@ -6456,6 +6528,10 @@ REFERENCES: .. [TTWL2009] Trebst, Troyer, Wang and Ludwig, A short introduction to Fibonacci anyon models, :arxiv:`0902.3275`. +.. [Tut1947] W.T. Tutte. *The factorization of linear graphs.* Journal of the + London Mathematical Society, vol. s1-22, issue 2, pages 107--111, + April 1947. :doi:`10.1112/jlms/s1-22.2.107`. + .. [Tur1974] \R. J. Turyn *Hadamard matrices, Baumert-Hall units, four-symbol sequences, pulse compression, and surface wave encodings*. Journal of Combinatorial Theory, Series A 16.3 (1974), pp 313–333. diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 8da15b32efa..5e9fcf77db6 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -121,6 +121,7 @@ This base class provides a lot more methods than a general parent:: '_pseudo_fraction_field', '_zero_element', 'algebraic_closure', + 'an_embedding', 'base_extend', 'divides', 'epsilon', @@ -128,14 +129,10 @@ This base class provides a lot more methods than a general parent:: 'fraction_field', 'gen', 'gens', - 'ideal', 'integral_closure', 'is_commutative', 'is_field', 'is_integrally_closed', - 'is_noetherian', - 'is_prime_field', - 'is_subring', 'krull_dimension', 'localization', 'ngens', diff --git a/src/doc/en/thematic_tutorials/conf.py b/src/doc/en/thematic_tutorials/conf.py index cb6d1662f47..581a8e89fc6 100644 --- a/src/doc/en/thematic_tutorials/conf.py +++ b/src/doc/en/thematic_tutorials/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py index b8eef385e4f..9de7aa68adb 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_orders.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_orders.rst index 1c03f15d526..47b8e5f7fc6 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_orders.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_orders.rst @@ -99,7 +99,6 @@ with ideals in non-maximal orders. sage: K. = NumberField(x^3 + 2) sage: R = K.order(3*a) sage: R.ideal(5) - doctest:warning ... FutureWarning: ... Ideal (5, 15*a, 45*a^2) of Order generated by 3*a in Number Field in a with defining polynomial x^3 + 2 sage: R.ideal(5).factor() Traceback (most recent call last): diff --git a/src/doc/en/thematic_tutorials/lie/lie_basics.rst b/src/doc/en/thematic_tutorials/lie/lie_basics.rst index e8be4590c3f..190ef6515c0 100644 --- a/src/doc/en/thematic_tutorials/lie/lie_basics.rst +++ b/src/doc/en/thematic_tutorials/lie/lie_basics.rst @@ -789,7 +789,7 @@ and for describing the affine Weyl group. In particular, the hyperplane for the reflection `r_0`, used in generating the affine Weyl group, is translated off the origin (so it becomes an affine hyperplane). Now the root system is not described as linear transformations -on an Euclidean space, but instead by *affine* transformations. Thus the +on a Euclidean space, but instead by *affine* transformations. Thus the dominant chamber has finite volume and tiles the Eucledian space. Moreover, each such tile corresponds to a unique element in the affine Weyl group. @@ -848,7 +848,7 @@ the twisting procedure defined in [Kac]_. It has the following properties: Further Generalizations ----------------------- -If a root system (on an Euclidean space) has only the angles +If a root system (on a Euclidean space) has only the angles `\pi/2, 2\pi/3, 3\pi/4, 5\pi/6` between its roots, then we call the root system *crystallographic* (on :wikipedia:`Root_system`, this condition is called integrality since for any two roots we have diff --git a/src/doc/en/thematic_tutorials/numerical_sage/conf.py b/src/doc/en/thematic_tutorials/numerical_sage/conf.py index 5772289f6fa..c8e220f8f99 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/conf.py +++ b/src/doc/en/thematic_tutorials/numerical_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/tutorial/afterword.rst b/src/doc/en/tutorial/afterword.rst index 84dd2891b71..390e67bac07 100644 --- a/src/doc/en/tutorial/afterword.rst +++ b/src/doc/en/tutorial/afterword.rst @@ -107,10 +107,8 @@ behaves differently from Python in several ways. 10 - **Integer division:** The Python expression ``2/3`` does not - behave the way mathematicians might expect. In Python2, if ``m`` and - ``n`` are ints, then ``m/n`` is also an int, namely the quotient of ``m`` - divided by ``n``. Therefore ``2/3=0``. In Python3, ``2/3`` returns the - floating point number ``0.6666...``. In both Python2 and Python3, ``//`` + behave the way mathematicians might expect: ``2/3`` returns the + floating point number ``0.6666...``. Note that ``//`` is the Euclidean division and ``2//3`` returns ``0``. We deal with this in the Sage interpreter, by wrapping integer @@ -125,16 +123,11 @@ behaves differently from Python in several ways. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # not tested, python2 - 0 - **Long integers:** Python has native support for arbitrary precision integers, in addition to C-int's. These are significantly - slower than what GMP provides, and have the property that they - print with an ``L`` at the end to distinguish them from int's (and - this won't change any time soon). Sage implements arbitrary - precision integers using the GMP C-library, and these print without - an ``L``. + slower than what GMP provides. Sage implements arbitrary + precision integers using the GMP C-library. Rather than modifying the Python interpreter (as some people have diff --git a/src/doc/en/tutorial/conf.py b/src/doc/en/tutorial/conf.py index b566ec19677..52be3420ed1 100644 --- a/src/doc/en/tutorial/conf.py +++ b/src/doc/en/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/en/tutorial/interactive_shell.rst b/src/doc/en/tutorial/interactive_shell.rst index 8a630a5d9fc..84c20840589 100644 --- a/src/doc/en/tutorial/interactive_shell.rst +++ b/src/doc/en/tutorial/interactive_shell.rst @@ -363,9 +363,9 @@ this may indicate a performance issue worth looking into. sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -793,7 +793,7 @@ allowed. :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index a173afd75c5..73ef2df3a31 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -49,7 +49,7 @@ output of the entered commands automatically. You can start this automatic rendering by executing ``%display latex`` (and stop by executing ``%display plain``). -.. ONLY:: html +.. ONLY:: html and feature_jupyter_sphinx Thus, in the Jupyter notebook, you get diff --git a/src/doc/en/website/conf.py b/src/doc/en/website/conf.py index 38f31e5d46f..b238169bb3c 100644 --- a/src/doc/en/website/conf.py +++ b/src/doc/en/website/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/es/a_tour_of_sage/conf.py b/src/doc/es/a_tour_of_sage/conf.py index dee2afa4d66..801f7cadb95 100644 --- a/src/doc/es/a_tour_of_sage/conf.py +++ b/src/doc/es/a_tour_of_sage/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/es/tutorial/conf.py b/src/doc/es/tutorial/conf.py index 43e23fdc568..dcb44232ca0 100644 --- a/src/doc/es/tutorial/conf.py +++ b/src/doc/es/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/fr/a_tour_of_sage/conf.py b/src/doc/fr/a_tour_of_sage/conf.py index af1f453f3e4..9cca4b7d1f8 100644 --- a/src/doc/fr/a_tour_of_sage/conf.py +++ b/src/doc/fr/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/fr/tutorial/conf.py b/src/doc/fr/tutorial/conf.py index af70d3eb4ff..38e0160f3ba 100644 --- a/src/doc/fr/tutorial/conf.py +++ b/src/doc/fr/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/fr/tutorial/interactive_shell.rst b/src/doc/fr/tutorial/interactive_shell.rst index 5283104c277..eb8829ceec1 100644 --- a/src/doc/fr/tutorial/interactive_shell.rst +++ b/src/doc/fr/tutorial/interactive_shell.rst @@ -366,9 +366,9 @@ qui mérite d'être examiné. sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -809,7 +809,7 @@ reconstruire, mais d'autres non, aussi la reconstruction d'objets GAP :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/hu/a_tour_of_sage/conf.py b/src/doc/hu/a_tour_of_sage/conf.py index b097f56dfe0..2e5215fcf5d 100644 --- a/src/doc/hu/a_tour_of_sage/conf.py +++ b/src/doc/hu/a_tour_of_sage/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/it/a_tour_of_sage/conf.py b/src/doc/it/a_tour_of_sage/conf.py index d06280ece36..48a568cc4d1 100644 --- a/src/doc/it/a_tour_of_sage/conf.py +++ b/src/doc/it/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/it/faq/conf.py b/src/doc/it/faq/conf.py index e3898184a1f..84a8ac93eab 100644 --- a/src/doc/it/faq/conf.py +++ b/src/doc/it/faq/conf.py @@ -15,6 +15,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/it/faq/faq-contribute.rst b/src/doc/it/faq/faq-contribute.rst index 63c67a8e264..3b6c1612083 100644 --- a/src/doc/it/faq/faq-contribute.rst +++ b/src/doc/it/faq/faq-contribute.rst @@ -158,8 +158,8 @@ Consulta anche la Guida dello Sviluppo Sage, specialmente il capitolo `Convenzioni per scrivere codice sorgente in Sage `_. -Ho inviato al server trac una correzione molte settimane fa. Perché la state ignorando? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +Ho inviato al GitHub Sage repo una correzione molte settimane fa. Perché la state ignorando? +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Non stiamo cercando di ignorare la tua correzione. Le persone che lavorano su Sage lo fanno nel loro tempo libero. diff --git a/src/doc/it/faq/faq-usage.rst b/src/doc/it/faq/faq-usage.rst index 5d3183769f7..677d1a24bc2 100644 --- a/src/doc/it/faq/faq-usage.rst +++ b/src/doc/it/faq/faq-usage.rst @@ -469,37 +469,6 @@ Ci sono parecchie possibilità. Puoi usare i programmi a riga di comando ``screen``, ``nohup`` o ``disown``. -Il comando show (mostra) per la visualizzazione di oggetti 3D non funziona. -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -La visualizzazione 3D in tempo reale per Sage dalla versione 6.4 in -avanti usa il pacchetto `Jmol/JSmol `_. -Dalla linea di comando viene utilizzata l'applicazione Java Jmol, -mentre per la visualizzazione dal browser viene usato puro javascript -oppure una Java applet. In genere nei browser è usato javascript puro -per evitare problemi con quei browser che non supportano i plugin per -le applet Java (ad esempio Chrome). In ogni worksheet su browser c'è -una casella da spuntare prima di generare una vista tridimensionale -qualora l'utente voglia usare l'applet Java (essa è un po' più veloce -con viste complicate). - -La ragione più probabile di un malfunzionamento è che non hai -installato l'ambiente runtime di Java (JRE) o che è più vecchio della -versione 1.7. Se le cose funzionano dalla riga di comando, -un'altra possibilità è che il tuo browser non abbia il plugin giusto -per supportare le Java applet (al momento, nel 2014, tali plugin non -lavorano con la maggior parte delle versioni di Chrome). Assicurati di -aver installato il plugin IcedTea (su Linux vedi il tuo gestore dei -pacchetti) o il plugin di Oracle Java -(vedi: `IcedTea `_ -e `Java `_). - -Se stai usando un server Sage sul web e anche la visualizzazione -tramite javascript non funziona, potresti avere un problema con la -funzionalità javascript del tuo browser, o potresti aver disabilitato -javascript. - - Posso usare gli strumenti di Sage in un ambiente commerciale? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/doc/it/tutorial/conf.py b/src/doc/it/tutorial/conf.py index 15186edb4ba..ea77f3199b0 100644 --- a/src/doc/it/tutorial/conf.py +++ b/src/doc/it/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/ja/a_tour_of_sage/conf.py b/src/doc/ja/a_tour_of_sage/conf.py index e8ec7cca39b..eef0eba83b3 100644 --- a/src/doc/ja/a_tour_of_sage/conf.py +++ b/src/doc/ja/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/ja/tutorial/conf.py b/src/doc/ja/tutorial/conf.py index 9002593de99..eaaa4558a34 100644 --- a/src/doc/ja/tutorial/conf.py +++ b/src/doc/ja/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release, latex_elements from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/ja/tutorial/interactive_shell.rst b/src/doc/ja/tutorial/interactive_shell.rst index 6a845fb3bf6..4ef6131d1b7 100644 --- a/src/doc/ja/tutorial/interactive_shell.rst +++ b/src/doc/ja/tutorial/interactive_shell.rst @@ -333,9 +333,9 @@ GMPの方が速いが,その差はわずかだ(Sage用にビルドされたPAR sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -735,7 +735,7 @@ GAPでは,相当数のオブジェクトが再構成に使える印字形式 :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/pt/a_tour_of_sage/conf.py b/src/doc/pt/a_tour_of_sage/conf.py index 267d499ca64..806bc1b77c8 100644 --- a/src/doc/pt/a_tour_of_sage/conf.py +++ b/src/doc/pt/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/pt/tutorial/conf.py b/src/doc/pt/tutorial/conf.py index a5cabc5b0d1..26368567ee4 100644 --- a/src/doc/pt/tutorial/conf.py +++ b/src/doc/pt/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/pt/tutorial/interactive_shell.rst b/src/doc/pt/tutorial/interactive_shell.rst index 8f39dbe25d7..67d8c9823e3 100644 --- a/src/doc/pt/tutorial/interactive_shell.rst +++ b/src/doc/pt/tutorial/interactive_shell.rst @@ -353,9 +353,9 @@ indicar algum problema de performance que vale a pena investigar. sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -781,7 +781,7 @@ representações impressas não é permitido. :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/ru/tutorial/conf.py b/src/doc/ru/tutorial/conf.py index 5d39f7d2600..f2f44113045 100644 --- a/src/doc/ru/tutorial/conf.py +++ b/src/doc/ru/tutorial/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/doc/ru/tutorial/interactive_shell.rst b/src/doc/ru/tutorial/interactive_shell.rst index 213b2964fb0..49d36fd3784 100644 --- a/src/doc/ru/tutorial/interactive_shell.rst +++ b/src/doc/ru/tutorial/interactive_shell.rst @@ -341,9 +341,9 @@ Wall time. Однако, если существует существенная sage: time g = maple('1938^99484') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.11 - sage: gap(0) + sage: libgap(0) 0 - sage: time g = gap.eval('1938^99484;;') + sage: time g = libgap.eval('1938^99484;') CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 1.02 @@ -720,7 +720,7 @@ Sage не может сохранять и загружать объекты, с :: - sage: a = gap(2) + sage: a = libgap(2) sage: a.save('a') sage: load('a') Traceback (most recent call last): diff --git a/src/doc/tr/a_tour_of_sage/conf.py b/src/doc/tr/a_tour_of_sage/conf.py index b7536470bd4..9d2a503d78d 100644 --- a/src/doc/tr/a_tour_of_sage/conf.py +++ b/src/doc/tr/a_tour_of_sage/conf.py @@ -13,6 +13,11 @@ from sage_docbuild.conf import release from sage_docbuild.conf import * # NOQA + +for tag in feature_tags(): + tags.add(tag) + + # Add any paths that contain custom static files (such as style sheets), # relative to this directory to html_static_path. They are copied after the # builtin static files, so a file named "default.css" will overwrite the diff --git a/src/environment-3.10-linux-aarch64.yml b/src/environment-3.10-linux-aarch64.yml index 4e20820eec1..7c6026a6b00 100644 --- a/src/environment-3.10-linux-aarch64.yml +++ b/src/environment-3.10-linux-aarch64.yml @@ -38,7 +38,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py310hbb3657e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -320,6 +319,7 @@ dependencies: - pyrsistent=0.20.0=py310h7c1f4a2_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.10.14=hbbe8eec_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.10-linux.yml b/src/environment-3.10-linux.yml index 73a318f1073..929b40139c5 100644 --- a/src/environment-3.10-linux.yml +++ b/src/environment-3.10-linux.yml @@ -39,7 +39,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py310hc6cd4ac_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -273,6 +272,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=hed6455c_2 - memory-allocator=0.1.3=py310h2372a71_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h59595ed_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=hfe3b2da_0 @@ -358,6 +359,7 @@ dependencies: - pyrsistent=0.20.0=py310h2372a71_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.10.14=hd12c33a_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.10-macos-x86_64.yml b/src/environment-3.10-macos-x86_64.yml index ef12b1c101c..268e619f1ff 100644 --- a/src/environment-3.10-macos-x86_64.yml +++ b/src/environment-3.10-macos-x86_64.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py310h9e9d8ca_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -324,6 +323,7 @@ dependencies: - pyrsistent=0.20.0=py310hb372a2b_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.10.14=h00d2728_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.10-macos.yml b/src/environment-3.10-macos.yml index 1e7bd5209bc..87692b1e2ee 100644 --- a/src/environment-3.10-macos.yml +++ b/src/environment-3.10-macos.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py310h1253130_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -245,6 +244,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=h2bbcd85_2 - memory-allocator=0.1.3=py310h2aa6e3c_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h13dd4ca_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=h91ba8db_0 @@ -324,6 +325,7 @@ dependencies: - pyrsistent=0.20.0=py310hd125d64_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.10.14=h2469fbe_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.11-linux-aarch64.yml b/src/environment-3.11-linux-aarch64.yml index e03e0ed8864..87cee9aad2a 100644 --- a/src/environment-3.11-linux-aarch64.yml +++ b/src/environment-3.11-linux-aarch64.yml @@ -38,7 +38,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py311h8715677_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -320,6 +319,7 @@ dependencies: - pyrsistent=0.20.0=py311hc8f2f60_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.11.9=hddfb980_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.11-linux.yml b/src/environment-3.11-linux.yml index 32d1541e494..7fee7689a28 100644 --- a/src/environment-3.11-linux.yml +++ b/src/environment-3.11-linux.yml @@ -39,7 +39,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py311hb755f60_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -273,6 +272,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=hed6455c_2 - memory-allocator=0.1.3=py311h459d7ec_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h59595ed_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=hfe3b2da_0 @@ -358,6 +359,7 @@ dependencies: - pyrsistent=0.20.0=py311h459d7ec_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.11.9=hb806964_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.11-macos-x86_64.yml b/src/environment-3.11-macos-x86_64.yml index 6d62ac79ca3..d0fc9f07c5c 100644 --- a/src/environment-3.11-macos-x86_64.yml +++ b/src/environment-3.11-macos-x86_64.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py311hdf8f085_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -324,6 +323,7 @@ dependencies: - pyrsistent=0.20.0=py311he705e18_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.11.9=h657bba9_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.11-macos.yml b/src/environment-3.11-macos.yml index a96a5142606..4111aa010f2 100644 --- a/src/environment-3.11-macos.yml +++ b/src/environment-3.11-macos.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py311ha891d26_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -245,6 +244,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=h2bbcd85_2 - memory-allocator=0.1.3=py311heffc1b2_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h13dd4ca_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=h91ba8db_0 @@ -324,6 +325,7 @@ dependencies: - pyrsistent=0.20.0=py311h05b510d_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.11.9=h932a869_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.9-linux-aarch64.yml b/src/environment-3.9-linux-aarch64.yml index cd423b040c4..43382444aae 100644 --- a/src/environment-3.9-linux-aarch64.yml +++ b/src/environment-3.9-linux-aarch64.yml @@ -38,7 +38,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py39h387a81e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -320,6 +319,7 @@ dependencies: - pyrsistent=0.20.0=py39h7cc1d5f_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.9.19=h4ac3b42_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.9-linux.yml b/src/environment-3.9-linux.yml index 36d7a6fab66..83a922eb4f4 100644 --- a/src/environment-3.9-linux.yml +++ b/src/environment-3.9-linux.yml @@ -39,7 +39,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py39h3d6467e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -273,6 +272,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=hed6455c_2 - memory-allocator=0.1.3=py39hd1e30aa_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h59595ed_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=hfe3b2da_0 @@ -358,6 +359,7 @@ dependencies: - pyrsistent=0.20.0=py39hd1e30aa_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.9.19=h0755675_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.9-macos-x86_64.yml b/src/environment-3.9-macos-x86_64.yml index 566775ddd11..c3026a8fbc0 100644 --- a/src/environment-3.9-macos-x86_64.yml +++ b/src/environment-3.9-macos-x86_64.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py39h840bb9f_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -324,6 +323,7 @@ dependencies: - pyrsistent=0.20.0=py39ha09f3b3_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.9.19=h7a9c478_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-3.9-macos.yml b/src/environment-3.9-macos.yml index 4cf9ff6e7bc..906b3b79f22 100644 --- a/src/environment-3.9-macos.yml +++ b/src/environment-3.9-macos.yml @@ -32,7 +32,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py39hb198ff7_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -245,6 +244,8 @@ dependencies: - matplotlib-inline=0.1.7=pyhd8ed1ab_0 - maxima=5.47.0=h2bbcd85_2 - memory-allocator=0.1.3=py39h0f82c59_0 + - meson=1.5.2=pyhd8ed1ab_0 + - meson-python=0.15.0=pyh0c530f3_0 - metis=5.1.0=h13dd4ca_1007 - mistune=3.0.2=pyhd8ed1ab_0 - mpc=1.3.1=h91ba8db_0 @@ -324,6 +325,7 @@ dependencies: - pyrsistent=0.20.0=py39h17cfd9d_0 - pysocks=1.7.1=pyha2e5f31_6 - python=3.9.19=hd7ebdb9_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.10-linux-aarch64.yml b/src/environment-dev-3.10-linux-aarch64.yml index e0a7428c353..5cc9a611a2a 100644 --- a/src/environment-dev-3.10-linux-aarch64.yml +++ b/src/environment-dev-3.10-linux-aarch64.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py310hbb3657e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -367,6 +366,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.10.14=hbbe8eec_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.10-linux.yml b/src/environment-dev-3.10-linux.yml index 98b9bb546f9..90d8df80567 100644 --- a/src/environment-dev-3.10-linux.yml +++ b/src/environment-dev-3.10-linux.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py310hc6cd4ac_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -402,6 +401,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.10.14=hd12c33a_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.10-macos-x86_64.yml b/src/environment-dev-3.10-macos-x86_64.yml index 2013ecba6a1..6541d3ef7c8 100644 --- a/src/environment-dev-3.10-macos-x86_64.yml +++ b/src/environment-dev-3.10-macos-x86_64.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py310h9e9d8ca_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.10.14=h00d2728_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.10-macos.yml b/src/environment-dev-3.10-macos.yml index 5e68c311f97..014df8bdc08 100644 --- a/src/environment-dev-3.10-macos.yml +++ b/src/environment-dev-3.10-macos.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py310h1253130_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.10.14=h2469fbe_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.11-linux-aarch64.yml b/src/environment-dev-3.11-linux-aarch64.yml index b6877adff0c..9772d14d173 100644 --- a/src/environment-dev-3.11-linux-aarch64.yml +++ b/src/environment-dev-3.11-linux-aarch64.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py311h8715677_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -367,6 +366,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.11.9=hddfb980_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.11-linux.yml b/src/environment-dev-3.11-linux.yml index 61335e6a853..f59609c4b25 100644 --- a/src/environment-dev-3.11-linux.yml +++ b/src/environment-dev-3.11-linux.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py311hb755f60_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -402,6 +401,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.11.9=hb806964_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.11-macos-x86_64.yml b/src/environment-dev-3.11-macos-x86_64.yml index ac36b8f0f89..3368f995b8c 100644 --- a/src/environment-dev-3.11-macos-x86_64.yml +++ b/src/environment-dev-3.11-macos-x86_64.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py311hdf8f085_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.11.9=h657bba9_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.11-macos.yml b/src/environment-dev-3.11-macos.yml index 584fcb8acc4..ba84799917e 100644 --- a/src/environment-dev-3.11-macos.yml +++ b/src/environment-dev-3.11-macos.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py311ha891d26_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.11.9=h932a869_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.9-linux-aarch64.yml b/src/environment-dev-3.9-linux-aarch64.yml index d975d72ca98..39923130bc5 100644 --- a/src/environment-dev-3.9-linux-aarch64.yml +++ b/src/environment-dev-3.9-linux-aarch64.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=h31becfc_1 - brotli-bin=1.1.0=h31becfc_1 - brotli-python=1.1.0=py39h387a81e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h8af1aa0_1 - bzip2=1.0.8=h31becfc_5 - c-ares=1.28.1=h31becfc_0 @@ -367,6 +366,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.9.19=h4ac3b42_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.9-linux.yml b/src/environment-dev-3.9-linux.yml index 6aaffa90cab..451a093512e 100644 --- a/src/environment-dev-3.9-linux.yml +++ b/src/environment-dev-3.9-linux.yml @@ -42,7 +42,6 @@ dependencies: - brotli=1.1.0=hd590300_1 - brotli-bin=1.1.0=hd590300_1 - brotli-python=1.1.0=py39h3d6467e_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=ha770c72_1 - bzip2=1.0.8=hd590300_5 - c-ares=1.28.1=hd590300_0 @@ -402,6 +401,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.9.19=h0755675_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.9-macos-x86_64.yml b/src/environment-dev-3.9-macos-x86_64.yml index d34360440bd..92632f26f85 100644 --- a/src/environment-dev-3.9-macos-x86_64.yml +++ b/src/environment-dev-3.9-macos-x86_64.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=h0dc2134_1 - brotli-bin=1.1.0=h0dc2134_1 - brotli-python=1.1.0=py39h840bb9f_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=h694c41f_1 - bzip2=1.0.8=h10d778d_5 - c-ares=1.28.1=h10d778d_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.9.19=h7a9c478_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/environment-dev-3.9-macos.yml b/src/environment-dev-3.9-macos.yml index 934855f53d6..bcc3c684e61 100644 --- a/src/environment-dev-3.9-macos.yml +++ b/src/environment-dev-3.9-macos.yml @@ -35,7 +35,6 @@ dependencies: - brotli=1.1.0=hb547adb_1 - brotli-bin=1.1.0=hb547adb_1 - brotli-python=1.1.0=py39hb198ff7_1 - - build=0.7.0=pyhd8ed1ab_0 - bwidget=1.9.14=hce30654_1 - bzip2=1.0.8=h93a5062_5 - c-ares=1.28.1=h93a5062_0 @@ -365,6 +364,7 @@ dependencies: - pytest=8.2.2=pyhd8ed1ab_0 - pytest-xdist=3.6.1=pyhd8ed1ab_0 - python=3.9.19=hd7ebdb9_0_cpython + - python-build=1.2.1=pyhd8ed1ab_0 - python-dateutil=2.9.0=pyhd8ed1ab_0 - python-fastjsonschema=2.20.0=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000000..12d82fa91c3 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,154 @@ +# Compilers +cc = meson.get_compiler('c') +cpp = meson.get_compiler('cpp') +cython = meson.get_compiler('cython') + +# Setup dependencies that are needed by many modules +inc_numpy = run_command( + py, + [ + '-c', + ''' +import numpy +print(numpy.get_include()) + '''.strip(), + ], + check: true, +).stdout().strip() +numpy = declare_dependency(include_directories: inc_numpy) + +inc_cysignals = run_command( + py, + [ + '-c', + ''' +import cysignals +print(cysignals.__file__.replace('__init__.py', '')) + '''.strip(), + ], + check: true, +).stdout().strip() +cysignals = declare_dependency(include_directories: inc_cysignals) + +inc_gmpy2 = run_command( + py, + [ + '-c', + ''' +import gmpy2 +print(gmpy2.__file__.replace('__init__.py', '')) + '''.strip(), + ], + check: true, +).stdout().strip() +gmpy2 = declare_dependency(include_directories: inc_gmpy2) +gmp = dependency('gmp') + +inc_cypari2 = run_command( + py, + [ + '-c', + ''' +import cypari2 +print(cypari2.__file__.replace('__init__.py', '')) + '''.strip(), + ], + check: true, +).stdout().strip() +cypari2 = declare_dependency(include_directories: inc_cypari2) +pari = cc.find_library('pari') + +mpfr = cc.find_library('mpfr') + +flint = dependency('flint', version: '>=3.0.0') +if flint.version().version_compare('<3.1') + # In older versions of flint, pkg-config file is broken, so we manually use find_library + # This has been fixed in flint v3.1: https://github.com/flintlib/flint/pull/1647 + flint = cc.find_library('flint') +endif + +blas_order = [] +if host_machine.system() == 'darwin' + blas_order += 'accelerate' +endif +if host_machine.cpu_family() == 'x86_64' + blas_order += 'mkl' +endif +# pkg-config uses a lower-case name while CMake uses a capitalized name, so try +# that too to make the fallback detection with CMake work +blas_order += ['cblas', 'openblas', 'OpenBLAS', 'flexiblas', 'blis', 'blas'] +blas = dependency(blas_order) +gsl = dependency( + 'gsl', + fallback: ['gsl', 'gsl_dep'], + version: '>=2.5', + required: true, +) +gd = cc.find_library('gd') +m = cc.find_library('m') +m4ri = cc.find_library('m4ri') +m4rie = cc.find_library('m4rie') +mtx = cc.find_library('mtx', required: false, disabler: true) +png = cc.find_library('png') +zlib = cc.find_library('z') +# Cannot be found via pkg-config +ec = cc.find_library('ec') +ecm = cc.find_library('ecm') +ppl = cc.find_library('ppl') +gmpxx = cc.find_library('gmpxx') +fflas = dependency('fflas-ffpack') +fplll = dependency('fplll') +givaro = cc.find_library('givaro') +linbox = dependency('linbox', required: false) +if not linbox.found() + linbox = cc.find_library('linbox') +endif +mpc = cc.find_library('mpc') +mpfi = cc.find_library('mpfi') +# Cannot be found via pkg-config (pkg-config file will be added in 4.13) +# Test for common.h header that was added in 4.12 as a indirect version check +gap = cc.find_library('gap', has_headers: ['gap/common.h']) +singular = dependency('Singular') +maxima = find_program('maxima', required: true) +# Cannot be found via pkg-config +ntl = cc.find_library('ntl') +# Cannot be found via pkg-config +meataxe = cc.find_library('meataxe', required: false, disabler: true) + +# Meson currently ignores include_directories for Cython modules, so we +# have to add them manually. +# https://github.com/mesonbuild/meson/issues/9562 +add_project_arguments('-I', meson.current_source_dir(), language: 'cython') +add_project_arguments('-I', meson.current_build_dir(), language: 'cython') + +# Add global compiler flags +add_project_arguments('-X auto_pickle=False', language: 'cython') +add_project_arguments('-X autotestdict=False', language: 'cython') +add_project_arguments('-X binding=False', language: 'cython') +add_project_arguments('-X c_api_binop_methods=True', language: 'cython') +add_project_arguments('-X cdivision=True', language: 'cython') +add_project_arguments('-X cpow=True', language: 'cython') +add_project_arguments('-X embedsignature=True', language: 'cython') +add_project_arguments('--embed-positions', language: 'cython') +add_project_arguments('-X fast_getattr=True', language: 'cython') +#add_project_arguments('-X language_level="3"', language : 'cython') +add_project_arguments('-X legacy_implicit_noexcept=True', language: 'cython') +add_project_arguments( + '-X preliminary_late_includes_cy28=True', + language: 'cython', +) + +inc_cpython = include_directories('sage/cpython') +inc_rings = include_directories('sage/rings') +inc_rings_finite = include_directories('sage/rings/finite_rings') +inc_flint = include_directories('sage/libs/flint') +inc_gsl = include_directories('sage/libs/gsl') +inc_ntl = include_directories('sage/libs/ntl') +inc_arb = include_directories('sage/libs/arb') +inc_data_structures = include_directories('sage/data_structures') +inc_ext = include_directories('sage/ext') +inc_partn_ref2 = include_directories('sage/groups/perm_gps/partn_ref2') +inc_src = include_directories('.') + +# Submodules +subdir('sage') diff --git a/src/pyproject.toml b/src/pyproject.toml index 4d2704f9469..4b70dc133d1 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -7,13 +7,14 @@ requires = [ # 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.11.4', + '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', + 'jinja2', ] build-backend = "setuptools.build_meta" diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 2fe4a873ec6..92ad6c34d64 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -2914,7 +2914,7 @@ def __richcmp__(self, other, op): return contained and contains if op == op_NE: return not (contained and contains) - # remaining case < + # remaining case < return contained and not contains def __mul__(self, other): diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 43a7bba9816..6772673cbcb 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -242,7 +242,7 @@ sage: S.cluster_variable(3) (x2 + 1)/x3 -Walking around by mutating ``S`` updates the informations stored in ``A``:: +Walking around by mutating ``S`` updates the information stored in ``A``:: sage: len(A.g_vectors_so_far()) 10 @@ -355,6 +355,7 @@ # **************************************************************************** from copy import copy +from typing import Any from sage.arith.misc import binomial from sage.categories.homset import Hom @@ -461,7 +462,7 @@ def d_vector(self) -> tuple: sage: x.d_vector() (1, 1, 2, -2) """ - monomials = self.lift().dict() + monomials = self.lift().monomial_coefficients() minimal = map(min, zip(*monomials)) return tuple(-vector(minimal))[:self.parent().rank()] @@ -562,7 +563,7 @@ def homogeneous_components(self) -> dict: """ deg_matrix = block_matrix([[identity_matrix(self.parent().rank()), -self.parent().b_matrix()]]) - components = {} + components: dict[tuple, Any] = {} x = self.lift() monomials = x.monomials() for m in monomials: @@ -614,8 +615,9 @@ def theta_basis_decomposition(self): f_poly = components[g_vect].F_polynomial() g_vect = vector(g_vect) while f_poly != zero_U: - y_exp = min(f_poly.dict()) - coeff = f_poly.dict()[y_exp] + coeffs = f_poly.monomial_coefficients() + y_exp = min(coeffs) + coeff = coeffs[y_exp] g_theta = tuple(g_vect + B * vector(y_exp)) out[g_theta] = out.get(g_theta, zero_A) + A({zero_t + tuple(y_exp): coeff}) f_poly -= U({y_exp: coeff}) * A.theta_basis_F_polynomial(g_theta) @@ -1432,11 +1434,11 @@ def _repr_(self) -> str: and coefficients y0, y1 over Integer Ring """ var_names = self.initial_cluster_variable_names() - var_names = (" " if len(var_names) == 1 else "s ") + ", ".join(var_names) + var_names_str = (" " if len(var_names) == 1 else "s ") + ", ".join(var_names) coeff_names = self.coefficient_names() coeff_prefix = " and" + (" " if len(coeff_names) > 0 else " no ") + "coefficient" coeff = coeff_prefix + (" " if len(coeff_names) == 1 else "s ") + ", ".join(coeff_names) + (" " if len(coeff_names) > 0 else "") - return "A Cluster Algebra with cluster variable" + var_names + coeff + "over " + repr(self.scalars()) + return "A Cluster Algebra with cluster variable" + var_names_str + coeff + "over " + repr(self.scalars()) def _an_element_(self): r""" diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 60b80719c53..fdcaeedaad8 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -222,7 +222,7 @@ def image_monomial(exponent): return A.zero() for g in I.gens(): - d = g.dict() + d = g.monomial_coefficients() res = A.sum(d[ex] * image_monomial(ex) for ex in d) if not res.is_zero(): raise ValueError("the differential does not preserve the ideal") @@ -303,7 +303,7 @@ def _call_(self, x): if x.is_zero(): return self.codomain().zero() res = self.codomain().zero() - dic = x.dict() + dic = x.monomial_coefficients() for key in dic: keyl = list(key) coef = dic[key] @@ -392,11 +392,11 @@ def differential_matrix(self, n): A = self.domain() dom = A.basis(n) cod = A.basis(n + 1) - cokeys = [next(iter(a.lift().dict().keys())) for a in cod] + cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod] m = matrix(A.base_ring(), len(dom), len(cod)) for i, domi in enumerate(dom): im = self(domi) - dic = im.lift().dict() + dic = im.lift().monomial_coefficients() for j in dic.keys(): k = cokeys.index(j) m[i, k] = dic[j] @@ -670,11 +670,11 @@ def differential_matrix_multigraded(self, n, total=False): n = G(vector(n)) dom = A.basis(n) cod = A.basis(n + self._degree_of_differential) - cokeys = [next(iter(a.lift().dict().keys())) for a in cod] + cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod] m = matrix(self.base_ring(), len(dom), len(cod)) for i, domi in enumerate(dom): im = self(domi) - dic = im.lift().dict() + dic = im.lift().monomial_coefficients() for j in dic.keys(): k = cokeys.index(j) m[i, k] = dic[j] @@ -1182,7 +1182,7 @@ def basis(self, n): basis = [] for v in free_basis: el = prod([self.gen(i)**v[i] for i in range(len(v))]) - di = el.dict() + di = el.monomial_coefficients() if len(di) == 1: k, = di.keys() if tuple(k) == v: @@ -1477,7 +1477,7 @@ def degree(self, total=False): """ if self.is_zero(): raise ValueError("the zero element does not have a well-defined degree") - exps = self.lift().dict().keys() + exps = self.monomial_coefficients().keys() degrees = self.parent()._degrees n = self.parent().ngens() l = [sum(e[i] * degrees[i] for i in range(n)) for e in exps] @@ -1548,7 +1548,7 @@ def homogeneous_parts(self): sage: a.homogeneous_parts() {1: -2*e3 + e5, 2: e1*e2, 3: e1*e3*e5 - 3*e2*e3*e5} """ - dic = self.dict() + dic = self.monomial_coefficients() terms = [self.parent()({t: dic[t]}) for t in dic.keys()] res = {} for term in terms: @@ -1559,7 +1559,7 @@ def homogeneous_parts(self): res[deg] = term return {i: res[i] for i in sorted(res.keys())} - def dict(self): + def monomial_coefficients(self): r""" A dictionary that determines the element. @@ -1569,11 +1569,18 @@ def dict(self): EXAMPLES:: sage: A. = GradedCommutativeAlgebra(QQ, degrees=(1, 2, 2, 3)) - sage: dic = (x*y - 5*y*z + 7*x*y^2*z^3*t).dict() - sage: sorted(dic.items()) + sage: elt = x*y - 5*y*z + 7*x*y^2*z^3*t + sage: sorted(elt.monomial_coefficients().items()) + [((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)] + + ``dict`` is an alias:: + + sage: sorted(elt.dict().items()) [((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)] """ - return self.lift().dict() + return self.lift().monomial_coefficients() + + dict = monomial_coefficients def __call__(self, *values, **kwargs): r""" @@ -1645,8 +1652,8 @@ def __call__(self, *values, **kwargs): if gstr in kwargs: images[i] = kwargs[gstr] res = 0 - for (m, c) in self.dict().items(): - term = prod((gen**y for (y, gen) in zip(m, images)), c) + for m, c in self.monomial_coefficients().items(): + term = prod((gen ** y for y, gen in zip(m, images)), c) res += term return res @@ -2000,7 +2007,7 @@ def degree(self, total=False): raise ValueError("the zero element does not have a well-defined degree") degrees = self.parent()._degrees_multi n = self.parent().ngens() - exps = self.lift().dict().keys() + exps = self.monomial_coefficients().keys() l = [sum(exp[i] * degrees[i] for i in range(n)) for exp in exps] if len(set(l)) == 1: return l[0] @@ -2543,7 +2550,7 @@ def minimal_model(self, i=3, max_iterations=3, partial_result=False): or the partial result computed up to that point is returned, deppending on the ``partial_result`` flag. - - ``partial_result`` -- boolean (default: ``False``); wether to return + - ``partial_result`` -- boolean (default: ``False``); whether to return the partial result if the ``max_iterations`` limit is reached OUTPUT: @@ -3861,7 +3868,7 @@ def _call_(self, x): """ codomain = self.codomain() result = codomain.zero() - for mono, coeff in x.dict().items(): + for mono, coeff in x.monomial_coefficients().items(): term = prod([gen**y for (y, gen) in zip(mono, self.im_gens())], codomain.one()) result += coeff * term diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index 1bb35730014..48e24c4fa2d 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -806,7 +806,6 @@ def is_weight_vector(self): P = self.parent() R = P.base_ring() - weights = P._weights def get_wt(n): if not n: @@ -853,9 +852,8 @@ def weight(self): P = self.parent() R = P.base_ring() V = FreeModule(R, 2) - weights = P._weights it = iter(self._monomial_coefficients) n = next(it) if not n: return V([P._weights[0], R.zero()]) - return V([P._weights[n], P._weights[n-1]]) + return V([P._weights[n], P._weights[n - 1]]) diff --git a/src/sage/algebras/exterior_algebra_groebner.pyx b/src/sage/algebras/exterior_algebra_groebner.pyx index cad3574c8a2..9704bcbbfb0 100644 --- a/src/sage/algebras/exterior_algebra_groebner.pyx +++ b/src/sage/algebras/exterior_algebra_groebner.pyx @@ -373,7 +373,7 @@ cdef class GroebnerStrategy: else: constructed[f0.lsi] = set([f0]) G.append(f0) - # Find the degress of the new pairs + # Find the degrees of the new pairs for j in range(n, len(G)): f1 = G[j] p1 = f1.ls diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py index da5e18db630..3ba4bc658cb 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py @@ -1,7 +1,7 @@ """ Finite-Dimensional Algebras """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2011 Johan Bosman # Copyright (C) 2011, 2013 Peter Bruin # Copyright (C) 2011 Michiel Kosters @@ -9,8 +9,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# http:s//www.gnu.org/licenses/ +# *************************************************************************** from .finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement from .finite_dimensional_algebra_ideal import FiniteDimensionalAlgebraIdeal @@ -20,15 +20,15 @@ from sage.categories.magmatic_algebras import MagmaticAlgebras from sage.matrix.constructor import matrix from sage.structure.element import Matrix -from sage.rings.ring import Algebra from sage.structure.category_object import normalize_names +from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method from functools import reduce -class FiniteDimensionalAlgebra(UniqueRepresentation, Algebra): +class FiniteDimensionalAlgebra(UniqueRepresentation, Parent): r""" Create a finite-dimensional `k`-algebra from a multiplication table. @@ -187,7 +187,7 @@ def __init__(self, k, table, names='e', category=None): self._table = table self._assume_associative = "Associative" in category.axioms() # No further validity checks necessary! - Algebra.__init__(self, base_ring=k, names=names, category=category) + Parent.__init__(self, base=k, names=names, category=category) def _repr_(self): """ @@ -303,10 +303,10 @@ def basis(self): sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), ....: Matrix([[0, 1], [0, 0]])]) sage: A.basis() - Family (e0, e1) + Finite family {0: e0, 1: e1} """ from sage.sets.family import Family - return Family(self.gens()) + return Family({i: self.gen(i) for i in range(self.ngens())}) def __iter__(self): """ diff --git a/src/sage/algebras/finite_dimensional_algebras/meson.build b/src/sage/algebras/finite_dimensional_algebras/meson.build new file mode 100644 index 00000000000..075f0b8cebd --- /dev/null +++ b/src/sage/algebras/finite_dimensional_algebras/meson.build @@ -0,0 +1,26 @@ +py.install_sources( + 'all.py', + 'finite_dimensional_algebra.py', + 'finite_dimensional_algebra_element.pxd', + 'finite_dimensional_algebra_ideal.py', + 'finite_dimensional_algebra_morphism.py', + subdir: 'sage/algebras/finite_dimensional_algebras', +) + +extension_data = { + 'finite_dimensional_algebra_element' : files( + 'finite_dimensional_algebra_element.pyx', + ), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/finite_dimensional_algebras', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index 6080b82a396..8b73482e7bf 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -651,7 +651,7 @@ def exp_to_monomial(T): return M([(i % ngens, Ti) for i, Ti in enumerate(T) if Ti]) return self.element_class(self, {exp_to_monomial(T): c - for T, c in x.letterplace_polynomial().dict().items()}) + for T, c in x.letterplace_polynomial().monomial_coefficients().items()}) # ok, not a free algebra element (or should not be viewed as one). if isinstance(x, str): from sage.misc.sage_eval import sage_eval diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 52e9cfdeb98..b832a520fec 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -189,7 +189,7 @@ class FMatrix(SageObject): sage: f.f_from(s, s, s, s), f.f_to(s, s, s, s) ([i0, p], [i0, p]) - The last two statments show that the possible values of + The last two statements show that the possible values of `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`. The F-matrix is computed by solving the so-called diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index 2627a6f11b1..ba2a626c9f0 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -273,7 +273,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): if i % n_proc == child_id: he = req_cy(basis, r_matrix, fvars, _Nk_ij, id_anyon, sextuple) if he: - red = reduce_poly_dict(he.dict(), _nnz, _ks, one) + red = reduce_poly_dict(he.monomial_coefficients(), _nnz, _ks, one) # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -341,7 +341,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): if i % n_proc == child_id: pe = feq_cy(basis, fvars, _Nk_ij, id_anyon, zero, nonuple, prune=True) if pe: - red = reduce_poly_dict(pe.dict(), _nnz, _ks, one) + red = reduce_poly_dict(pe.monomial_coefficients(), _nnz, _ks, one) # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -399,7 +399,7 @@ cdef list compute_gb(factory, tuple args): cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(), len(sorted_vars), 'a', order=term_order) # Zip tuples into R and compute Groebner basis - cdef idx_map = {old : new for new, old in enumerate(sorted_vars)} + cdef idx_map = {old: new for new, old in enumerate(sorted_vars)} nvars = len(sorted_vars) F = factory.field() cdef list polys = list() @@ -455,7 +455,7 @@ cpdef executor(tuple params): Execute a function defined in this module (``sage.algebras.fusion_rings.fast_parallel_fmats_methods``) in a worker process, and supply the factory parameter by constructing a reference - to the ``FMatrix`` object in the worker's memory adress space from + to the ``FMatrix`` object in the worker's memory address space from its ``id``. INPUT: diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index ad6e8a1621e..deaa4517406 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -268,7 +268,7 @@ cpdef executor(tuple params): Execute a function registered in this module's ``mappers`` in a worker process, and supply the ``FusionRing`` parameter by constructing a reference to the FMatrix object in the worker's memory - adress space from its ``id``. + address space from its ``id``. .. NOTE:: @@ -310,7 +310,7 @@ cpdef _unflatten_entries(fusion_ring, list entries): Restore cyclotomic coefficient object from its tuple of rational coefficients representation. - Used to circumvent pickling issue introduced by PARI settigs + Used to circumvent pickling issue introduced by PARI settings in :issue:`30537`. EXAMPLES:: diff --git a/src/sage/algebras/fusion_rings/fusion_double.py b/src/sage/algebras/fusion_rings/fusion_double.py index 71277561331..3b681d75d3b 100644 --- a/src/sage/algebras/fusion_rings/fusion_double.py +++ b/src/sage/algebras/fusion_rings/fusion_double.py @@ -133,7 +133,7 @@ class FusionDouble(CombinatorialFreeModule): sage: G = SmallPermutationGroup(16,9) sage: F = FusionDouble(G, prefix='b', inject_variables=True) sage: b13^2 # long time (4s) - b0 + b2 + b4 + b15 + b16 + b17 + b18 + b24 + b26 + b27 + b0 + b3 + b4 """ @staticmethod def __classcall_private__(cls, G, prefix='s', inject_variables=False): diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 1ce6b920daa..56045d2dce6 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1113,7 +1113,7 @@ def is_multiplicity_free(self): return k <= 2 ################################### - ### Braid group representations ### + # Braid group representations # ################################### def get_computational_basis(self, a, b, n_strands): diff --git a/src/sage/algebras/fusion_rings/meson.build b/src/sage/algebras/fusion_rings/meson.build new file mode 100644 index 00000000000..281460a066a --- /dev/null +++ b/src/sage/algebras/fusion_rings/meson.build @@ -0,0 +1,47 @@ +py.install_sources( + 'all.py', + 'f_matrix.py', + 'fast_parallel_fmats_methods.pxd', + 'fast_parallel_fusion_ring_braid_repn.pxd', + 'fusion_double.py', + 'fusion_ring.py', + 'poly_tup_engine.pxd', + 'shm_managers.pxd', + subdir: 'sage/algebras/fusion_rings', +) + +extension_data = { + 'fast_parallel_fusion_ring_braid_repn' : files( + 'fast_parallel_fusion_ring_braid_repn.pyx', + ), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/fusion_rings', + install: true, + include_directories: [inc_cpython, inc_ntl, inc_numpy, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +extension_data_cpp = { + 'fast_parallel_fmats_methods': files('fast_parallel_fmats_methods.pyx'), + 'poly_tup_engine': files('poly_tup_engine.pyx'), + 'shm_managers': files('shm_managers.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/fusion_rings', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ntl, inc_numpy, inc_rings], + dependencies: [py_dep, cysignals, gmp, singular], + ) +endforeach + diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index f263e47d562..8d6869bd484 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -26,7 +26,7 @@ cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): sage: poly_to_tup(x**2*y**4 - 4/5*x*y**2 + 1/3 * y) (((2, 4), 1), ((1, 2), -4/5), ((0, 1), 1/3)) """ - return tuple(poly.dict().items()) + return tuple(poly.monomial_coefficients().items()) cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): r""" @@ -93,7 +93,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): Restore cyclotomic coefficient object from its tuple of rational coefficients representation. - Used to circumvent pickling issue introduced by PARI settigs + Used to circumvent pickling issue introduced by PARI settings in :issue:`30537`. EXAMPLES:: @@ -447,7 +447,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of each variable - ``val_dict`` -- dictionary of ``(var_idx, poly_tup)`` key-value pairs - - ``poly_tup`` -- tuple of ``(ETuple, coeff)`` pairs reperesenting a + - ``poly_tup`` -- tuple of ``(ETuple, coeff)`` pairs representing a multivariate polynomial EXAMPLES:: diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 94bc24eb4ce..3ce832d499f 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -400,7 +400,7 @@ cdef class FvarsHandler: known squares for initialization, e.g., from a solver checkpoint - ``use_mp`` -- integer indicating the number of child processes used for multiprocessing; if running serially, use 0 - - ``pids_name`` -- the name of a ``ShareableList`` contaning the + - ``pids_name`` -- the name of a ``ShareableList`` containing the process ``pid``'s for every process in the pool (including the parent process) - ``name`` -- the name of a shared memory object @@ -500,7 +500,7 @@ cdef class FvarsHandler: else: self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t) self.child_id = 0 - # Populate with initialziation data + # Populate with initialization data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): fvar = _flatten_coeffs(poly_to_tup(fvar)) diff --git a/src/sage/algebras/group_algebra.py b/src/sage/algebras/group_algebra.py index eb70b0c699f..8dc952f23b9 100644 --- a/src/sage/algebras/group_algebra.py +++ b/src/sage/algebras/group_algebra.py @@ -222,8 +222,9 @@ def _coerce_map_from_(self, S): hom_G = G.coerce_map_from(S_G) if hom_K is not None and hom_G is not None: return SetMorphism(S.Hom(self, category=self.category() | S.category()), - lambda x: self.sum_of_terms( (hom_G(g), hom_K(c)) for g,c in x )) + lambda x: self.sum_of_terms((hom_G(g), hom_K(c)) for g, c in x)) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.algebras.group_algebras', 'GroupAlgebra', GroupAlgebra_class) +register_unpickle_override('sage.algebras.group_algebras', 'GroupAlgebra', + GroupAlgebra_class) diff --git a/src/sage/algebras/hall_algebra.py b/src/sage/algebras/hall_algebra.py index 84bebb13e38..bac7fa4f2fa 100644 --- a/src/sage/algebras/hall_algebra.py +++ b/src/sage/algebras/hall_algebra.py @@ -349,7 +349,7 @@ def coproduct_on_basis(self, la): S = self.tensor_square() if all(x == 1 for x in la): n = len(la) - return S.sum_of_terms([( (Partition([1]*r), Partition([1]*(n-r))), self._q**(-r*(n-r)) ) + return S.sum_of_terms([((Partition([1]*r), Partition([1]*(n-r))), self._q**(-r*(n-r))) for r in range(n+1)], distinct=True) I = HallAlgebraMonomials(self.base_ring(), self._q) @@ -482,9 +482,9 @@ def scalar(self, y): (4*q^2 + 9)/(q^2 - q) """ q = self.parent()._q - f = lambda la: ~( q**(sum(la) + 2*la.weighted_size()) + f = lambda la: ~(q**(sum(la) + 2*la.weighted_size()) * prod(prod((1 - q**-i) for i in range(1,k+1)) - for k in la.to_exp()) ) + for k in la.to_exp())) y = self.parent()(y) ret = q.parent().zero() for mx, cx in self: @@ -687,7 +687,7 @@ def coproduct_on_basis(self, a): + (q^-1)*I[1, 1] # I[1] + I[2] # I[1] + I[2, 1] # I[] """ S = self.tensor_square() - return S.prod(S.sum_of_terms([( (Partition([r]), Partition([n-r]) ), self._q**(-r*(n-r)) ) + return S.prod(S.sum_of_terms([((Partition([r]), Partition([n-r])), self._q**(-r*(n-r))) for r in range(n+1)], distinct=True) for n in a) def antipode_on_basis(self, a): diff --git a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py index 3177185c088..8d674d0aaf1 100644 --- a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py +++ b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py @@ -738,10 +738,10 @@ def algebra_generators(self): for i in range(self._n): r = list(self._zero_tuple) # Make a copy r[i] = 1 - d['L%s' % (i+1)] = self.monomial( (tuple(r), self._one_perm) ) + d['L%s' % (i+1)] = self.monomial((tuple(r), self._one_perm)) G = self._Pn.group_generators() for i in range(1, self._n): - d['T%s' % i] = self.monomial( (self._zero_tuple, G[i]) ) + d['T%s' % i] = self.monomial((self._zero_tuple, G[i])) return Family(sorted(d), lambda i: d[i]) def T(self, i=None): @@ -896,9 +896,9 @@ def product_on_basis(self, m1, m2): # combination of standard basis elements using the method and then, # recursively, multiply on the left and right by L1 and T2, # respectively. In other words, we multiply as L1*(T1*L2)*T2. - return ( self.monomial((L1, self._one_perm)) + return (self.monomial((L1, self._one_perm)) * self._product_Tw_L(T1, L2) - * self.monomial((self._zero_tuple, T2)) ) + * self.monomial((self._zero_tuple, T2))) def _product_LTwTv(self, L, w, v): r""" @@ -1023,7 +1023,7 @@ def _product_Tw_L(self, w, L): iaxpy(c, self._product_LTwTv(tuple(L), self._Pn.simple_reflections()[i], v), iL) # need T_i*T_v if a < b: - Ls = [ list(L) for k in range(b-a) ] # make copies of L + Ls = [list(L) for k in range(b-a)] # make copies of L for k in range(b-a): Ls[k][i-1] = a + k Ls[k][i] = b - k @@ -1031,7 +1031,7 @@ def _product_Tw_L(self, w, L): iaxpy(1, {(tuple(l), v): c for l in Ls}, iL) elif a > b: - Ls = [ list(L) for k in range(a-b) ] # make copies of L + Ls = [list(L) for k in range(a-b)] # make copies of L for k in range(a-b): Ls[k][i-1] = b + k Ls[k][i] = a - k @@ -1110,25 +1110,26 @@ def Ltuple(a, b): # return "small" powers of the generators without change if m < self._r: - return self.monomial( (Ltuple(0, m), self._one_perm) ) + return self.monomial((Ltuple(0, m), self._one_perm)) if i > 1: si = self._Pn.simple_reflections()[i-1] qsum = self.base_ring().one() - self._q**-1 # by calling _Li_power we avoid infinite recursion here - return ( self.sum_of_terms( ((Ltuple(c, m-c), si), qsum) for c in range(1, m) ) - + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1) ) + return (self.sum_of_terms(((Ltuple(c, m-c), si), qsum) for c in range(1, m)) + + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1)) # now left with the case i = 1 and m >= r if m > self._r: return self.monomial((Ltuple(0, 1), self._one_perm)) * self._Li_power(i,m-1) z = PolynomialRing(self.base_ring(), 'DUMMY').gen() - p = list(prod(z - val for val in self._u))#[:-1] - p.pop() # remove the highest power + p = list(prod(z - val for val in self._u)) # [:-1] + p.pop() # remove the highest power zero = self.base_ring().zero() return self._from_dict({(Ltuple(0, exp), self._one_perm): -coeff - for exp,coeff in enumerate(p) if coeff != zero}, + for exp, coeff in enumerate(p) + if coeff != zero}, remove_zeros=False, coerce=False) @cached_method @@ -1294,7 +1295,7 @@ def _from_LT_basis(self, m): True """ ret = self.prod(self.L(i+1)**k for i,k in enumerate(m[0])) - return ret * self.monomial( (self._zero_tuple, m[1]) ) + return ret * self.monomial((self._zero_tuple, m[1])) @cached_method def algebra_generators(self): @@ -1337,9 +1338,9 @@ def T(self, i=None): return [self.T(j) for j in range(self._n)] if i == 0: - return self.monomial( ((1,) + self._zero_tuple[1:], self._one_perm) ) + return self.monomial(((1,) + self._zero_tuple[1:], self._one_perm)) s = self._Pn.simple_reflections() - return self.monomial( (self._zero_tuple, s[i]) ) + return self.monomial((self._zero_tuple, s[i])) @cached_method def L(self, i=None): @@ -1514,7 +1515,7 @@ def product_on_basis(self, m1, m2): return L * M * R # The current product of T's and the type A Hecke algebra - tprod = [( [(k, a) for k, a in enumerate(t2) if a != 0], {s2: one} )] + tprod = [([(k, a) for k, a in enumerate(t2) if a != 0], {s2: one})] # s1 through t2 for i in reversed(s1.reduced_word()): diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py index d96b05f43a8..9aa6fa63ff2 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py @@ -1322,7 +1322,7 @@ def get_order(self): sage: len(CHA3.get_order()) 24 """ - # The reason we have overriden this is that we have to care about + # The reason we have overridden this is that we have to care about # the dynamical growth of thefinite sub basis used for the # calculation in case of more than 4 strands. @@ -3047,7 +3047,7 @@ def cubic_equation_parameters(self, generic=False): - ``generic`` -- boolean (default: ``False``); if set to ``True`` the coefficients are returned as elements of the generic base ring - OUTPUT: a tripple consisting of the coefficients + OUTPUT: a triple consisting of the coefficients EXAMPLES:: diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py index 7afbe1432dd..b980e2fa80c 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py @@ -103,7 +103,7 @@ def register_ring_hom(ring_hom): try: codomain.register_conversion(ring_hom) except ValueError: - verbose('\nthe map:\n%s\ncannot be registerd as conversion\n' % ring_hom) + verbose('\nthe map:\n%s\ncannot be registered as conversion\n' % ring_hom) return @@ -148,7 +148,7 @@ def _act_(self, perm, pol): if not self.is_left(): perm, pol = pol, perm pol_dict = {} - for key, value in pol.dict().items(): + for key, value in pol.monomial_coefficients().items(): newkey = [0] * len(key) for pos, k in enumerate(key): newkey[perm(pos + 1) - 1] = k @@ -317,7 +317,7 @@ def _element_constructor_(self, x, mon=None): def _coerce_map_from_(self, R): r""" The rings that canonically coerce to ``self`` ar the ones from - inheritence and the base ring of definition of the cubic Hecke algebra. + inheritance and the base ring of definition of the cubic Hecke algebra. EXAMPLES:: @@ -385,7 +385,7 @@ def hom(self, im_gens, codomain=None, check=True, base_map=None): return super().hom(im_remain, codomain=codomain, check=check, base_map=hom_cycl_gen) else: if base_map is None: - raise ValueError('number of images must be four (inculding a ' + raise ValueError('number of images must be four (including a ' 'third root of unity at first position) or a ' 'base_map (on %s) must be given' % self.base_ring()) return super().hom(im_gens, codomain=codomain, check=check, base_map=base_map) diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py b/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py index 9964fa4e287..59b0767fb48 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py @@ -491,7 +491,7 @@ def __getitem__(self, item): - ``item`` -- an :class:`AbsIrreducibeRep` specifying an absolute irreducible representation of the cubic Hecke algebra; alternatively, it can be specified by list index - (see :meth:`internal_index` repectively :meth:`gap_index`) + (see :meth:`internal_index` respectively :meth:`gap_index`) OUTPUT: @@ -732,7 +732,7 @@ def _element_constructor_(self, x): :class:`~sage.algebras.hecke_algebras.cubic_hecke_algebra.CubicHeckeAlgebra` or an element whose parent is a :class:`MatrixSpace` - EXAMLPES:: + EXAMPlES:: sage: import sage.algebras.hecke_algebras.cubic_hecke_matrix_rep as chmr sage: CHA3. = algebras.CubicHecke(3) @@ -786,7 +786,7 @@ def __call__(self, entries=None, coerce=True, copy=None): This method needs to be overloaded here since :class:`MatrixSpace` has an own implementation of it. - EXAMLPES:: + EXAMPLES:: sage: import sage.algebras.hecke_algebras.cubic_hecke_matrix_rep as chmr sage: CHA2. = algebras.CubicHecke(2) diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index b4dc71825af..01390f1a5a4 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -83,7 +83,7 @@ def normalized_laurent_polynomial(R, p): u + v^-1 + u^-1 """ try: - return R({k: R._base(c) for k, c in p.dict().items()}) + return R({k: R._base(c) for k, c in p.monomial_coefficients().items()}) except (AttributeError, TypeError): return R(p) @@ -1255,7 +1255,7 @@ def __init__(self, algebra, prefix=None): # This **must** match the name of the class in order for # specialize_to() to work - _basis_name = None + _basis_name = 'B' def _repr_term(self, t): r""" diff --git a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx index 5b99c87dd17..2a977b1a513 100644 --- a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx @@ -139,8 +139,8 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement): sage: sorted(p) # indirect doctest [((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)] """ - cdef dict d = self._poly.dict() - yield from d.iteritems() + cdef dict d = self._poly.monomial_coefficients() + yield from d.items() def _repr_(self): """ diff --git a/src/sage/algebras/letterplace/meson.build b/src/sage/algebras/letterplace/meson.build new file mode 100644 index 00000000000..1ada90927a7 --- /dev/null +++ b/src/sage/algebras/letterplace/meson.build @@ -0,0 +1,27 @@ +py.install_sources( + 'all.py', + 'free_algebra_element_letterplace.pxd', + 'free_algebra_letterplace.pxd', + subdir: 'sage/algebras/letterplace', +) + +extension_data_cpp = { + 'free_algebra_element_letterplace': files( + 'free_algebra_element_letterplace.pyx', + ), + 'free_algebra_letterplace': files('free_algebra_letterplace.pyx'), + 'letterplace_ideal': files('letterplace_ideal.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/letterplace', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp, singular], + ) +endforeach + diff --git a/src/sage/algebras/lie_algebras/affine_lie_algebra.py b/src/sage/algebras/lie_algebras/affine_lie_algebra.py index a637322e4d7..02fefb8e623 100644 --- a/src/sage/algebras/lie_algebras/affine_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/affine_lie_algebra.py @@ -838,10 +838,9 @@ def _test_classical_subalgebra(self, **options): tester = self._tester(**options) B = self.basis() roots = set(self._g._Q.roots()) - ac = list(self._g._Q.simple_coroots()) from sage.misc.misc import some_tuples for r, s in some_tuples(roots, 2, tester._max_runs): - ret = B[r,0].bracket(B[s,0]) + ret = B[r, 0].bracket(B[s, 0]) if r + s in roots: tester.assertEqual(list(ret.support()), [(r+s, 0)], f"obtained [{r}, {s}] == {ret}") elif r == -s: @@ -1125,7 +1124,6 @@ def __init__(self, cartan_type): Q = finite_ct.relabel({n-i: i for i in range(n)}).root_system().root_lattice() self._roots = tuple(Q.roots()) self._ac = tuple(Q.simple_coroots()) - CP = cartesian_product([range(3)] * n) if cartan_type.rank() == 2: self._short_roots = self._roots + tuple(2*r for r in Q.roots()) else: @@ -1200,7 +1198,6 @@ def __iter__(self): finite_ct = finite_ct.relabel({n-i: i for i in range(n)}) else: finite_ct = self._cartan_type.classical() - Q = finite_ct.root_system().root_lattice() P = self._facade_for[0] for i in ZZ: if i % 2: diff --git a/src/sage/algebras/lie_algebras/bgg_dual_module.py b/src/sage/algebras/lie_algebras/bgg_dual_module.py index 01517c4e51f..c4e060131da 100644 --- a/src/sage/algebras/lie_algebras/bgg_dual_module.py +++ b/src/sage/algebras/lie_algebras/bgg_dual_module.py @@ -423,7 +423,7 @@ def _acted_upon_(self, scalar, self_on_left=False): ##################################################################### -## Simple modules +# Simple modules # This is an abuse as the monoid is not free. @@ -1153,6 +1153,7 @@ def _acted_upon_(self, scalar, self_on_left=True): _lmul_ = _acted_upon_ _rmul_ = _acted_upon_ + class FiniteDimensionalSimpleModule(SimpleModule): """ A finite dimensional simple module. diff --git a/src/sage/algebras/lie_algebras/bgg_resolution.py b/src/sage/algebras/lie_algebras/bgg_resolution.py index 89ccb3d2079..906f8d43f95 100644 --- a/src/sage/algebras/lie_algebras/bgg_resolution.py +++ b/src/sage/algebras/lie_algebras/bgg_resolution.py @@ -22,6 +22,7 @@ from sage.rings.integer_ring import ZZ from sage.homology.chain_complex import ChainComplex_class + class BGGResolution(UniqueRepresentation, ChainComplex_class): r""" The BGG resolution of a simple module. @@ -81,7 +82,7 @@ def __init__(self, L): self._cartan_type = ct self._simple = L self._W = WeylGroup(ct, prefix='s') - # finish intialization + # finish initialization R = self._simple.base_ring() differentials, mod_order = build_differentials(self._W) differentials = {deg: mat.change_ring(R) for deg, mat in differentials.items()} @@ -212,7 +213,7 @@ def build_differentials(W): vind = dv[0] vpind = dvp[0] for uind in dv[1] & dvp[1]: - # set the entires corresponding to the square + # set the entries corresponding to the square if not mat[j, vind]: if not mat[j, vpind]: mat[j, vpind] = one diff --git a/src/sage/algebras/lie_algebras/center_uea.py b/src/sage/algebras/lie_algebras/center_uea.py index 61f5c6e05d7..2799356a4c4 100644 --- a/src/sage/algebras/lie_algebras/center_uea.py +++ b/src/sage/algebras/lie_algebras/center_uea.py @@ -6,22 +6,22 @@ - Travis Scrimshaw (2024-01-02): Initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2024 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** -#from sage.structure.unique_representation import UniqueRepresentation -#from sage.structure.parent import Parent +# from sage.structure.unique_representation import UniqueRepresentation +# from sage.structure.parent import Parent from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.integer_lists.invlex import IntegerListsLex from sage.matrix.constructor import matrix -from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid #, IndexedFreeAbelianMonoidElement +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid from sage.monoids.indexed_free_monoid import IndexedMonoid from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.integer_vector_weighted import iterator_fast as intvecwt_iterator @@ -419,7 +419,6 @@ def __iter__(self): n = len(self._gen_degrees) wts = sorted(self._gen_degrees.values(), reverse=True) while True: - total = 0 for exps in intvecwt_iterator(deg, wts): yield self.element_class(self, {n-1-i: e for i, e in enumerate(exps) if e}) deg += 1 @@ -742,7 +741,7 @@ def retract(self, elt): True """ # This should work except it needs the monomials of the PBW basis to be - # compariable. However, this does not work for, e.g., Lie algebras + # comparable. However, this does not work for, e.g., Lie algebras # in the Chevalley basis as ee are unable to pass a key for the # module morphism. Additionally, the implementation below does more # operations in-place than the module morphism. diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index ebe892d3436..e4e2eb75e68 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -379,15 +379,15 @@ def build_assoc(row): continue basis_pivots.add(p) if self._sparse: - added.append(self.element_class( self, build_assoc(cur_mat[i]) )) + added.append(self.element_class(self, build_assoc(cur_mat[i]))) else: - added.append(self.element_class( self, self._assoc(cur_mat[i].list()) )) + added.append(self.element_class(self, self._assoc(cur_mat[i].list()))) cur_mat = cur_mat.submatrix(nrows=len(pivots)) if self._sparse: - basis = [self.element_class( self, build_assoc(cur_mat[i]) ) + basis = [self.element_class(self, build_assoc(cur_mat[i])) for i in range(cur_mat.rank())] else: - basis = [self.element_class( self, self._assoc(cur_mat[i].list()) ) + basis = [self.element_class(self, self._assoc(cur_mat[i].list())) for i in range(cur_mat.rank())] return Family(basis) @@ -1077,7 +1077,7 @@ def __init__(self, R): ####################################### -## Compact real form +# Compact real form class MatrixCompactRealForm(FinitelyGeneratedLieAlgebra): r""" @@ -1558,7 +1558,7 @@ def monomial_coefficients(self, copy=False): ####################################### -## Chevalley Basis +# Chevalley Basis class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): r""" @@ -2054,7 +2054,7 @@ def _weight_action(self, m, wt): # enough in the ambient space to correctly convert things to do # the scalar product. alc = wt.parent().simple_coroots() - return R(wt.scalar( alc[aci[m]] )) + return R(wt.scalar(alc[aci[m]])) def affine(self, kac_moody=True): r""" @@ -2359,7 +2359,6 @@ def _construct_struct_coeffs(self, R, p_roots): for ii, ca in r._monomial_coefficients.items() for jj, cb in s._monomial_coefficients.items()) s_coeffs[r, s] = {r+s: coeff} - ht = sum(r.coefficients()) + sum(s.coefficients()) s_coeffs[-r, -s] = {-r-s: -coeff} if r - s in p_roots_set or s - r in p_roots_set: coeff = R.prod((-1)**(ca*cb) if (ii, jj) in self._epsilon or ii == jj else 1 diff --git a/src/sage/algebras/lie_algebras/meson.build b/src/sage/algebras/lie_algebras/meson.build new file mode 100644 index 00000000000..f50959cb44b --- /dev/null +++ b/src/sage/algebras/lie_algebras/meson.build @@ -0,0 +1,42 @@ +py.install_sources( + 'abelian.py', + 'affine_lie_algebra.py', + 'all.py', + 'bch.py', + 'bgg_dual_module.py', + 'bgg_resolution.py', + 'center_uea.py', + 'classical_lie_algebra.py', + 'examples.py', + 'free_lie_algebra.py', + 'heisenberg.py', + 'lie_algebra.py', + 'lie_algebra_element.pxd', + 'morphism.py', + 'nilpotent_lie_algebra.py', + 'onsager.py', + 'poincare_birkhoff_witt.py', + 'quotient.py', + 'rank_two_heisenberg_virasoro.py', + 'representation.py', + 'structure_coefficients.py', + 'subalgebra.py', + 'symplectic_derivation.py', + 'verma_module.py', + 'virasoro.py', + subdir: 'sage/algebras/lie_algebras', +) + +extension_data = {'lie_algebra_element' : files('lie_algebra_element.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/lie_algebras', + install: true, + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/algebras/lie_algebras/onsager.py b/src/sage/algebras/lie_algebras/onsager.py index d4d224ea847..0de70c0a630 100644 --- a/src/sage/algebras/lie_algebras/onsager.py +++ b/src/sage/algebras/lie_algebras/onsager.py @@ -318,7 +318,7 @@ def alternating_central_extension(self): Element = LieAlgebraElement ##################################################################### -## q-Onsager algebra (the quantum group) +# q-Onsager algebra (the quantum group) class QuantumOnsagerAlgebra(CombinatorialFreeModule): @@ -795,10 +795,10 @@ def a(m, p): assert m > 0 terms = q**-2 * self.monomial(B[kr] * B[kl]) terms -= self.monomial(B[1,m]) - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,kr[1]-p]) * self.monomial(B[0,p+kl[1]]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * temp else: r = -kr[1] - 1 @@ -812,10 +812,10 @@ def a(m, p): terms -= (q**2-q**-2) * sum(q**(2*(r-1-k)) * self.monomial(B[0,-(k+1)]) * self.monomial(B[0,-r+kl[1]+k]) for k in range(r)) m = -r + kl[1] + 1 - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,m-p-1]) * self.monomial(B[0,p-1]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * q**(2*r) * temp else: # [B[rd+a0], B[sd+a1]] r > s @@ -826,10 +826,10 @@ def a(m, p): terms -= (q**2-q**-2) * sum(q**(2*(kl[1]-1-k)) * self.monomial(B[0,-(r-kl[1]+k+1)]) * self.monomial(B[0,k]) for k in range(kl[1])) m = r - kl[1] + 1 - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,-p]) * self.monomial(B[0,p-m]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * q**(2*kl[1]) * temp terms = -q**2 * terms elif kl[0] == 1 and kr[0] == 0: @@ -877,7 +877,7 @@ def a(m, p): for h in range(1, ell)) - q**(2*(ell-1)) * self.monomial(B[0,-(p-ell+1)] * B[1,kl[1]-ell]) for ell in range(1, kl[1])) - else: #kl[0] == 0 and kr[0] == 1: + else: # kl[0] == 0 and kr[0] == 1: terms = self.monomial(B[kr] * B[kl]) if kl[1] < kr[1]: # [B[pd+a1], B[md]] with p < m @@ -923,7 +923,7 @@ def a(m, p): return self.monomial(lhs // B[kl]) * terms * self.monomial(rhs // B[kr]) ##################################################################### -## ACE of the Onsager algebra +# ACE of the Onsager algebra class OnsagerAlgebraACE(InfinitelyGeneratedLieAlgebra, IndexedGenerators): diff --git a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py index 813a5a45293..41dc9cd8159 100644 --- a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py +++ b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py @@ -795,7 +795,7 @@ def transpose(self): r""" Return the transpose map of ``self``. - This is the tranpose map on the Lie algebra extended + This is the transpose map on the Lie algebra extended as an anti-involution of ``self``. EXAMPLES:: diff --git a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py index 4957ecf7bc9..b3fa2b0c7f4 100644 --- a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +++ b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py @@ -211,7 +211,7 @@ def K(self, i=None): """ if i is None: return Family(self._KI, self.K) - return self.monomial( ('K', i) ) + return self.monomial(('K', i)) def t(self, a, b): r""" @@ -225,7 +225,7 @@ def t(self, a, b): """ if a == b == 0: raise ValueError("no t(0, 0) element") - return self.monomial( ('t', self._v(a,b)) ) + return self.monomial(('t', self._v(a,b))) def E(self, a, b): r""" @@ -239,7 +239,7 @@ def E(self, a, b): """ if a == b == 0: raise ValueError("no E(0, 0) element") - return self.monomial( ('E', self._v(a,b)) ) + return self.monomial(('E', self._v(a,b))) def _v(self, a, b): r""" @@ -324,10 +324,10 @@ def _an_element_(self): d = self.monomial v = self._v return ( - d( ('E',v(1,-3)) ) - - self.base_ring().an_element() * d( ('t',v(-1,3)) ) - + d( ('E',v(2,2)) ) - + d( ('K',3) ) + d(('E',v(1,-3))) + - self.base_ring().an_element() * d(('t',v(-1,3))) + + d(('E',v(2,2))) + + d(('K',3)) ) def some_elements(self): @@ -345,9 +345,9 @@ def some_elements(self): """ d = self.monomial v = self._v - return [d( ('E',v(1,1)) ), d( ('E',v(-2,-2)) ), d( ('E',v(0,1)) ), - d( ('t',v(1,1)) ), d( ('t',v(4,-1)) ), d( ('t',v(2,3)) ), - d( ('K',2) ), d( ('K',4) ), self.an_element()] + return [d(('E',v(1,1))), d(('E',v(-2,-2))), d(('E',v(0,1))), + d(('t',v(1,1))), d(('t',v(4,-1))), d(('t',v(2,3))), + d(('K',2)), d(('K',4)), self.an_element()] class Element(LieAlgebraElement): pass diff --git a/src/sage/algebras/lie_algebras/representation.py b/src/sage/algebras/lie_algebras/representation.py index 73c492d382f..af05b3c5ab5 100644 --- a/src/sage/algebras/lie_algebras/representation.py +++ b/src/sage/algebras/lie_algebras/representation.py @@ -474,7 +474,7 @@ def _acted_upon_(self, scalar, self_on_left=False): class FaithfulRepresentationNilpotentPBW(CombinatorialFreeModule, Representation_abstract): r""" - Return a faithful reprensetation of a nilpotent Lie algebra + Return a faithful representation of a nilpotent Lie algebra constructed using the PBW basis. Let `L` be a `k`-step nilpotent Lie algebra. Define a weight function diff --git a/src/sage/algebras/lie_algebras/symplectic_derivation.py b/src/sage/algebras/lie_algebras/symplectic_derivation.py index f9a2e483330..af294c4c4b0 100644 --- a/src/sage/algebras/lie_algebras/symplectic_derivation.py +++ b/src/sage/algebras/lie_algebras/symplectic_derivation.py @@ -261,9 +261,9 @@ def _an_element_(self): """ d = self.monomial return ( - d( _Partitions([2,1]) ) - - self.base_ring().an_element() * d( _Partitions([5,2,2,1]) ) - + d( _Partitions([2*self._g-1, self._g+1, 2, 1, 1]) ) + d(_Partitions([2,1])) + - self.base_ring().an_element() * d(_Partitions([5,2,2,1])) + + d(_Partitions([2*self._g-1, self._g+1, 2, 1, 1])) ) def some_elements(self): @@ -279,8 +279,8 @@ def some_elements(self): """ d = self.monomial g = self._g - return [d( _Partitions([2,1]) ), d( _Partitions([g+3,g+1]) ), d( _Partitions([2,1,1])), - d( _Partitions([2*g-1,2*g-2]) ), d( _Partitions([2*g-2,g-1,1]) ), + return [d(_Partitions([2,1])), d(_Partitions([g+3,g+1])), d(_Partitions([2,1,1])), + d(_Partitions([2*g-1,2*g-2])), d(_Partitions([2*g-2,g-1,1])), self.an_element()] class Element(LieAlgebraElement): diff --git a/src/sage/algebras/lie_algebras/verma_module.py b/src/sage/algebras/lie_algebras/verma_module.py index 778a9486707..01320616c5a 100644 --- a/src/sage/algebras/lie_algebras/verma_module.py +++ b/src/sage/algebras/lie_algebras/verma_module.py @@ -843,7 +843,7 @@ def _acted_upon_(self, scalar, self_on_left=False): ##################################################################### -## Morphisms and Homset +# Morphisms and Homset class VermaModuleMorphism(Morphism): diff --git a/src/sage/algebras/lie_algebras/virasoro.py b/src/sage/algebras/lie_algebras/virasoro.py index 0284a10e5e5..961c6fb9bbd 100644 --- a/src/sage/algebras/lie_algebras/virasoro.py +++ b/src/sage/algebras/lie_algebras/virasoro.py @@ -652,7 +652,7 @@ class Element(LieAlgebraElement): pass ##################################################################### -## Representations +# Representations class ChargelessRepresentation(CombinatorialFreeModule): diff --git a/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py index a1ccb69653d..e9f697e8257 100644 --- a/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py @@ -96,7 +96,7 @@ def __init__(self, R, ct, names=None, prefix=None, bracket=None): ct = CartanType(ct) except IndexError: raise ValueError("ct must be a valid Cartan Type") - if not (ct.is_finite() and ct.is_irreducible ): + if not (ct.is_finite() and ct.is_irreducible): raise ValueError("only affine algebras of simple finite dimensional" "Lie algebras are implemented") hv = Integer(ct.dual_coxeter_number()) diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py index 3d520e449db..1db194445be 100644 --- a/src/sage/algebras/lie_conformal_algebras/examples.py +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -20,15 +20,15 @@ - Reimundo Heluani (2020-06-15): Initial implementation. """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2020 Reimundo Heluani # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from .abelian_lie_conformal_algebra import AbelianLieConformalAlgebra as Abelian from .affine_lie_conformal_algebra import AffineLieConformalAlgebra as Affine @@ -40,14 +40,3 @@ from .neveu_schwarz_lie_conformal_algebra import NeveuSchwarzLieConformalAlgebra as NeveuSchwarz from .virasoro_lie_conformal_algebra import VirasoroLieConformalAlgebra as Virasoro from .weyl_lie_conformal_algebra import WeylLieConformalAlgebra as Weyl - -assert Abelian -assert Affine -assert BosonicGhosts -assert FermionicGhosts -assert FreeBosons -assert FreeFermions -assert N2 -assert NeveuSchwarz -assert Virasoro -assert Weyl diff --git a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py index fe7954e1aea..e66489d49ca 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py @@ -134,7 +134,7 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, names,index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) - bosondict = { (i,j): {1: {('K',0): gram_matrix[index_set.rank(i), + bosondict = {(i,j): {1: {('K',0): gram_matrix[index_set.rank(i), index_set.rank(j)]}} for i in index_set for j in index_set} GradedLieConformalAlgebra.__init__(self,R,bosondict,names=names, diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index 4b8cb428f0b..40810602ac4 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -125,7 +125,7 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, names,index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) - fermiondict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + fermiondict = {(i,j): {0: {('K', 0): gram_matrix[index_set.rank(i), index_set.rank(j)]}} for i in index_set for j in index_set} from sage.rings.rational_field import QQ diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index 2631d965307..528a587d795 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -308,8 +308,9 @@ class LieConformalAlgebra(UniqueRepresentation, Parent): """ @staticmethod def __classcall_private__(cls, R=None, arg0=None, index_set=None, - central_elements=None, category=None, prefix=None, - names=None, latex_names=None, parity=None, weights=None, **kwds): + central_elements=None, category=None, + prefix=None, names=None, latex_names=None, + parity=None, weights=None, **kwds): """ Lie conformal algebra factory. diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index 4fe54a98963..3cb8f645cd5 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -275,7 +275,7 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, s_coeff = dict(s_coeff) self._s_coeff = Family({k: tuple((j, sum(c*self.monomial(i) - for i,c in v )) for j,v in s_coeff[k]) for k in s_coeff}) + for i,c in v)) for j,v in s_coeff[k]) for k in s_coeff}) self._parity = dict(zip(self.gens(),parity+(0,)*len(central_elements))) def structure_coefficients(self): diff --git a/src/sage/algebras/meson.build b/src/sage/algebras/meson.build new file mode 100644 index 00000000000..a7e74474c6b --- /dev/null +++ b/src/sage/algebras/meson.build @@ -0,0 +1,75 @@ +py.install_sources( + 'affine_nil_temperley_lieb.py', + 'algebra.py', + 'all.py', + 'askey_wilson.py', + 'associated_graded.py', + 'catalog.py', + 'cellular_basis.py', + 'clifford_algebra.py', + 'clifford_algebra_element.pxd', + 'cluster_algebra.py', + 'commutative_dga.py', + 'down_up_algebra.py', + 'exterior_algebra_groebner.pxd', + 'finite_gca.py', + 'free_algebra.py', + 'free_algebra_element.py', + 'free_algebra_quotient.py', + 'free_algebra_quotient_element.py', + 'free_zinbiel_algebra.py', + 'group_algebra.py', + 'hall_algebra.py', + 'iwahori_hecke_algebra.py', + 'jordan_algebra.py', + 'nil_coxeter_algebra.py', + 'octonion_algebra.pxd', + 'orlik_solomon.py', + 'orlik_terao.py', + 'q_commuting_polynomials.py', + 'q_system.py', + 'quantum_clifford.py', + 'quantum_matrix_coordinate_algebra.py', + 'quantum_oscillator.py', + 'quaternion_algebra.py', + 'quaternion_algebra_element.py', + 'rational_cherednik_algebra.py', + 'schur_algebra.py', + 'shuffle_algebra.py', + 'splitting_algebra.py', + 'tensor_algebra.py', + 'weyl_algebra.py', + 'yangian.py', + 'yokonuma_hecke_algebra.py', + subdir: 'sage/algebras', +) + +extension_data = { + 'clifford_algebra_element' : files('clifford_algebra_element.pyx'), + 'exterior_algebra_groebner' : files('exterior_algebra_groebner.pyx'), + 'octonion_algebra' : files('octonion_algebra.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +subdir('finite_dimensional_algebras') +subdir('fusion_rings') +install_subdir('hecke_algebras', install_dir: sage_install_dir / 'algebras') +subdir('letterplace') +subdir('lie_algebras') +install_subdir( + 'lie_conformal_algebras', + install_dir: sage_install_dir / 'algebras', +) +install_subdir('quantum_groups', install_dir: sage_install_dir / 'algebras') +subdir('quatalg') +install_subdir('steenrod', install_dir: sage_install_dir / 'algebras') diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 7302b430738..113bb29418f 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -791,9 +791,9 @@ def action(g, m): # computing the invariant will be a block matrix. To avoid dealing # with huge matrices, we can split it up into graded pieces. - max_deg = max([b.degree() for b in OS.basis()]) - B = [] #initialize the basis - for d in range(max_deg+1): + max_deg = max(b.degree() for b in OS.basis()) + B = [] # initialize the basis + for d in range(max_deg + 1): OS_d = OS.homogeneous_component(d) OSG_d = OS_d.invariant_module(G, action=action, category=category) B += [OS_d.lift(OSG_d.lift(b)) for b in OSG_d.basis()] diff --git a/src/sage/algebras/orlik_terao.py b/src/sage/algebras/orlik_terao.py index 707b7f51060..60c3c60e6db 100644 --- a/src/sage/algebras/orlik_terao.py +++ b/src/sage/algebras/orlik_terao.py @@ -292,7 +292,7 @@ def degree_on_basis(self, m): """ return len(m) - ## Multiplication + # Multiplication def product_on_basis(self, a, b): r""" diff --git a/src/sage/algebras/q_system.py b/src/sage/algebras/q_system.py index cea0332d4f4..a8342434847 100644 --- a/src/sage/algebras/q_system.py +++ b/src/sage/algebras/q_system.py @@ -446,12 +446,12 @@ def Q(self, a, m): if m == t[a] * self._level: return self.one() if m == 1: - return self.monomial( self._indices.gen((a,1)) ) + return self.monomial(self._indices.gen((a,1))) #if self._cartan_type.type() == 'A' and self._level is None: # return self._jacobi_trudy(a, m) I = self._cm.index_set() p = self._Q_poly(a, m) - return p.subs({ g: self.Q(I[i], 1) for i,g in enumerate(self._poly.gens()) }) + return p.subs({g: self.Q(I[i], 1) for i,g in enumerate(self._poly.gens())}) @cached_method def _Q_poly(self, a, m): diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index ff888ba5aaa..99c0c1e226f 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -533,7 +533,7 @@ def product_on_basis(self, m1, m2): poly *= self._w_poly.monomial(*v) poly = poly.reduce([vp[i]**(4*k) - (1 + q**(-2*k)) * vp[i]**(2*k) + q**(-2*k) for i in range(self._n)]) - pdict = poly.dict() + pdict = poly.monomial_coefficients() ret = {(self._psi(p), tuple(e)): pdict[e] * q**q_power * sign for e in pdict} @@ -610,7 +610,7 @@ def inverse(self): for wi in wp}) poly = poly.reduce([wi**(4*k) - (1 + q**(-2*k)) * wi**(2*k) + q**(-2*k) for wi in wp]) - pdict = poly.dict() + pdict = poly.monomial_coefficients() coeff = coeff.inverse_of_unit() ret = {(p, tuple(e)): coeff * c for e, c in pdict.items()} return Cl.element_class(Cl, ret) diff --git a/src/sage/algebras/quantum_groups/fock_space.py b/src/sage/algebras/quantum_groups/fock_space.py index 8a7ce3e9386..d1d98d3c184 100644 --- a/src/sage/algebras/quantum_groups/fock_space.py +++ b/src/sage/algebras/quantum_groups/fock_space.py @@ -749,7 +749,7 @@ def N_left(la, x, i): return (sum(1 for y in P._addable(la, i) if P._above(x, y)) - sum(1 for y in P._removable(la, i) if P._above(x, y))) q = P.realization_of()._q - return P.sum_of_terms(( la.remove_cell(*x), c * q**(-N_left(la, x, i)) ) + return P.sum_of_terms((la.remove_cell(*x), c * q**(-N_left(la, x, i))) for la,c in self for x in P._removable(la, i)) def e(self, *data): @@ -845,8 +845,8 @@ def N_right(la, x, i): return (sum(1 for y in P._addable(la, i) if P._above(y, x)) - sum(1 for y in P._removable(la, i) if P._above(y, x))) q = P.realization_of()._q - return P.sum_of_terms( (la.add_cell(*x), c * q**N_right(la, x, i)) - for la,c in self for x in P._addable(la, i) ) + return P.sum_of_terms((la.add_cell(*x), c * q**N_right(la, x, i)) + for la,c in self for x in P._addable(la, i)) def f(self, *data): r""" @@ -1384,7 +1384,7 @@ def _G_to_fock_basis(self, la): ############################################################################### -## Bases Category +# Bases Category class FockSpaceBases(Category_realization_of_parent): r""" @@ -1605,7 +1605,7 @@ def __getitem__(self, i): return self.monomial(i) ############################################################################### -## Truncated Fock space +# Truncated Fock space class FockSpaceTruncated(FockSpace): @@ -2178,7 +2178,7 @@ def _G_to_fock_basis(self, la, algorithm='GW'): mu = _Partitions([p - x for p in la]) def add_cols(nu): - return _Partitions([ v + x for v in list(nu) + [0]*(k - len(nu)) ]) + return _Partitions([v + x for v in list(nu) + [0]*(k - len(nu))]) return fock.sum_of_terms((add_cols(nu), c) for nu,c in self._G_to_fock_basis(mu)) # For critical partitions diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index 05e4e1b6982..931ee211ff1 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -838,7 +838,7 @@ def counit(self, elt): constant = R(str(ext_rep.pop(2 * i))) # Pop the coefficient break # To reconstruct, we need the following - F = libgap.eval('ElementsFamily')(libgap.eval('FamilyObj')(self._libgap)) + F = self._libgap.FamilyObj().ElementsFamily() elt = F.ObjByExtRep(ext_rep) co = self._libgap.CounitMap() return R(str(co(elt))) + constant @@ -2330,7 +2330,7 @@ def _construct_monomial(self, k): sage: B._construct_monomial((3,0,1)) F[a1]^(3)*F[a2] """ - F = libgap.eval('ElementsFamily')(libgap.eval('FamilyObj')(self._libgap)) + F = self._libgap.FamilyObj().ElementsFamily() one = self._libgap_base.One() data = [] for i, val in enumerate(k): @@ -2684,7 +2684,7 @@ def _unpickle_generic_element(parent, data): sage: loads(dumps(x)) == x # indirect doctest True """ - F = libgap.eval('ElementsFamily')(libgap.eval('FamilyObj')(parent._libgap)) + F = parent._libgap.FamilyObj().ElementsFamily() ret = [] # We need to multiply by this to get the right type in GAP one = parent._libgap_base.One() diff --git a/src/sage/algebras/quantum_groups/representations.py b/src/sage/algebras/quantum_groups/representations.py index 7042c5aba76..7d42aa78787 100644 --- a/src/sage/algebras/quantum_groups/representations.py +++ b/src/sage/algebras/quantum_groups/representations.py @@ -124,7 +124,7 @@ def K_on_basis(self, i, b, power=1): """ WLR = self.basis().keys().weight_lattice_realization() alc = WLR.simple_coroots() - return self.term( b, self._q**(b.weight().scalar(alc[i]) * self._d[i] * power) ) + return self.term(b, self._q**(b.weight().scalar(alc[i]) * self._d[i] * power)) class CyclicRepresentation(QuantumGroupRepresentation): diff --git a/src/sage/algebras/quantum_oscillator.py b/src/sage/algebras/quantum_oscillator.py index a688283705f..7ac3509238f 100644 --- a/src/sage/algebras/quantum_oscillator.py +++ b/src/sage/algebras/quantum_oscillator.py @@ -122,7 +122,7 @@ class QuantumOscillatorAlgebra(CombinatorialFreeModule): - [Kuniba2022]_ Section 3.2 """ @staticmethod - def __classcall_private__(cls, q=None, R=None): + def __classcall_private__(cls, q=None, R=None): r""" Standardize input to ensure a unique representation. diff --git a/src/sage/algebras/quatalg/meson.build b/src/sage/algebras/quatalg/meson.build new file mode 100644 index 00000000000..2ec5cd31f22 --- /dev/null +++ b/src/sage/algebras/quatalg/meson.build @@ -0,0 +1,24 @@ +py.install_sources( + 'all.py', + 'quaternion_algebra.py', + 'quaternion_algebra_element.pxd', + subdir: 'sage/algebras/quatalg', +) + +extension_data_cpp = { + 'quaternion_algebra_cython': files('quaternion_algebra_cython.pyx'), + 'quaternion_algebra_element': files('quaternion_algebra_element.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/algebras/quatalg', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_flint, inc_ntl, inc_rings], + dependencies: [py_dep, flint, gmp, m, ntl], + ) +endforeach + diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 32353a688b6..f85f282c0bb 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -861,7 +861,7 @@ def maximal_order(self, take_shortcuts=True, order_basis=None): # Since Voight's algorithm only works for a starting basis having 1 as # its first vector, we derive such a basis from the given order basis - basis = basis_for_quaternion_lattice(order_basis, reverse=True) + basis = basis_for_quaternion_lattice(order_basis) e_new_gens = [] @@ -939,7 +939,7 @@ def maximal_order(self, take_shortcuts=True, order_basis=None): h = (z*b)*e_n[1] - (y*a)*e_n[2] e_n[1:4] = [g, h, g * h] if (1 - a*y**2 - b*z**2 + a*b*w**2).valuation(2) > 2: - e_n = basis_for_quaternion_lattice(list(e) + e_n[1:], reverse=True) + e_n = basis_for_quaternion_lattice(list(e) + e_n[1:]) # e_n now contains elements that locally at p give a bigger order, # but the basis may be messed up at other primes (it might not @@ -953,7 +953,7 @@ def maximal_order(self, take_shortcuts=True, order_basis=None): e_new_gens.extend(e[1:]) - e_new = basis_for_quaternion_lattice(list(basis) + e_new_gens, reverse=True) + e_new = basis_for_quaternion_lattice(list(basis) + e_new_gens) return self.quaternion_order(e_new) def order_with_level(self, level): @@ -1620,8 +1620,8 @@ def __init__(self, A, basis, check=True): raise ValueError("lattice must contain 1") # check if multiplicatively closed - M1 = basis_for_quaternion_lattice(basis, reverse=False) - M2 = basis_for_quaternion_lattice(list(basis) + [x * y for x in basis for y in basis], reverse=False) + M1 = basis_for_quaternion_lattice(basis) + M2 = basis_for_quaternion_lattice(list(basis) + [x * y for x in basis for y in basis]) if M1 != M2: raise ValueError("given lattice must be a ring") @@ -2010,11 +2010,11 @@ def _left_ideal_basis(self, gens): sage: A. = QuaternionAlgebra(-17, -3) sage: A.maximal_order()._left_ideal_basis([i + j, i - j, 2*k, A(3)]) - [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k] + [1, i, 1/2 + 1/2*j, 1/2 + 1/2*i + 1/6*j + 1/6*k] sage: A.maximal_order()._left_ideal_basis([3*(i + j), 3*(i - j), 6*k, A(3)]) - [3/2 + 1/2*j + 2*k, 3/2*i + 3/2*k, j + k, 3*k] + [3, 3*i, 3/2 + 3/2*j, 3/2 + 3/2*i + 1/2*j + 1/2*k] """ - return basis_for_quaternion_lattice([b * g for b in self.basis() for g in gens], reverse=False) + return basis_for_quaternion_lattice([b * g for b in self.basis() for g in gens]) def _right_order_from_ideal_basis(self, basis): """ @@ -2030,12 +2030,12 @@ def _right_order_from_ideal_basis(self, basis): sage: A. = QuaternionAlgebra(17) sage: O = A.maximal_order() sage: basis = O._left_ideal_basis([1]); basis - [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] + [1, 1/2 + 1/2*i, j, 1/3*i + 1/2*j + 1/6*k] sage: O._right_order_from_ideal_basis(basis) Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) sage: basis = O._left_ideal_basis([i*j - j]); basis - [17 + 17/3*i + 4/3*k, 34/3*i + 2/3*k, j + k, 2*k] + [34, 17 + 17*i, 2*j, 17 + 17/3*i + j + 1/3*k] sage: O._right_order_from_ideal_basis(basis) Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) """ @@ -2089,9 +2089,9 @@ def left_ideal(self, gens, check=True, *, is_basis=False): or a single generator:: sage: R.left_ideal([i+j]) - Fractional ideal (1/2 + 1/2*i + 1/2*j + 13/2*k, i + j, 6*j + 6*k, 12*k) + Fractional ideal (12, 6 + 6*i, i + j, 13/2 + 1/2*i + 1/2*j + 1/2*k) sage: R.left_ideal(i+j) - Fractional ideal (1/2 + 1/2*i + 1/2*j + 13/2*k, i + j, 6*j + 6*k, 12*k) + Fractional ideal (12, 6 + 6*i, i + j, 13/2 + 1/2*i + 1/2*j + 1/2*k) sage: R.left_ideal([2, 1+j]) == R*2 + R*(1+j) True """ @@ -2102,7 +2102,7 @@ def left_ideal(self, gens, check=True, *, is_basis=False): else: if gens in self.quaternion_algebra(): gens = [gens] - basis = tuple(basis_for_quaternion_lattice([b * g for b in self.basis() for g in gens], reverse=False)) + basis = tuple(basis_for_quaternion_lattice([b * g for b in self.basis() for g in gens])) check = False return QuaternionFractionalIdeal_rational(self.quaternion_algebra(), basis, left_order=self, check=check) @@ -2132,9 +2132,9 @@ def right_ideal(self, gens, check=True, *, is_basis=False): or a single generator:: sage: R.right_ideal([i+j]) - Fractional ideal (1/2 + 1/2*i + 1/2*j + 11/2*k, i + j, 6*j + 6*k, 12*k) + Fractional ideal (12, 6 + 6*i, i + j, 11/2 + 1/2*i + 1/2*j + 1/2*k) sage: R.right_ideal(i+j) - Fractional ideal (1/2 + 1/2*i + 1/2*j + 11/2*k, i + j, 6*j + 6*k, 12*k) + Fractional ideal (12, 6 + 6*i, i + j, 11/2 + 1/2*i + 1/2*j + 1/2*k) sage: R.right_ideal([2, 1+j]) == 2*R + (1+j)*R True """ @@ -2145,7 +2145,7 @@ def right_ideal(self, gens, check=True, *, is_basis=False): else: if gens in self.quaternion_algebra(): gens = [gens] - basis = tuple(basis_for_quaternion_lattice([g * b for b in self.basis() for g in gens], reverse=False)) + basis = tuple(basis_for_quaternion_lattice([g * b for b in self.basis() for g in gens])) check = False return QuaternionFractionalIdeal_rational(self.quaternion_algebra(), basis, right_order=self, check=check) @@ -2210,7 +2210,7 @@ def __mul__(self, other): sage: Q. = QuaternionAlgebra(-1,-11) sage: O = Q.maximal_order() sage: I = O*j; I - Fractional ideal (-11/2 + 1/2*j, -11/2*i + 1/2*k, -11, -11*i) + Fractional ideal (11, 11*i, 11/2 + 1/2*j, 11/2*i + 1/2*k) """ return self.unit_ideal() * other @@ -2346,11 +2346,13 @@ def isomorphism_to(self, other, *, conjugator=False, B=10): sage: iso = O0.isomorphism_to(O1) sage: iso Ring morphism: - From: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, i, 1/2*i + 1/2*j, 1/2 + 1/2*k) - To: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, 667*i, 1/2 + 9*i + 1/2*j, 222075/1334*i + 333/667*j + 1/1334*k) + From: Order of Quaternion Algebra (-1, -19) with base ring Rational Field + with basis (1, i, 1/2*i + 1/2*j, 1/2 + 1/2*k) + To: Order of Quaternion Algebra (-1, -19) with base ring Rational Field + with basis (1, 667*i, 1/2 + 9*i + 1/2*j, 222075/1334*i + 333/667*j + 1/1334*k) Defn: i |--> 629/667*i + 36/667*j - 36/667*k - j |--> 684/667*i - 648/667*j - 19/667*k - k |--> -684/667*i - 19/667*j - 648/667*k + j |--> -684/667*i + 648/667*j + 19/667*k + k |--> 684/667*i + 19/667*j + 648/667*k sage: iso(1) 1 sage: iso(i) @@ -2363,7 +2365,7 @@ def isomorphism_to(self, other, *, conjugator=False, B=10): :: sage: gamma = O0.isomorphism_to(O1, conjugator=True); gamma - -36*i - j + k + -36 + j + k sage: gamma in O0 True sage: gamma in O1 @@ -2410,7 +2412,7 @@ def isomorphism_to(self, other, *, conjugator=False, B=10): sage: Oconj = j.inverse() * O * j sage: Oconj = Quat.quaternion_order(Oconj.basis()) sage: O.isomorphism_to(Oconj, conjugator=True) - -j + j Test error cases:: @@ -2571,7 +2573,7 @@ def __init__(self, Q, basis, left_order=None, right_order=None, check=True): sage: R = QuaternionAlgebra(-11,-1).maximal_order() sage: R.right_ideal(R.basis()) - Fractional ideal (1/2 + 1/2*i, i, 1/2*j + 1/2*k, k) + Fractional ideal (1, 1/2 + 1/2*i, j, 1/2*j + 1/2*k) sage: R.right_ideal(tuple(R.basis()), check=False, is_basis=True) Fractional ideal (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) @@ -2618,17 +2620,17 @@ def scale(self, alpha, left=False): sage: B = BrandtModule(5,37); I = B.right_ideals()[0] sage: i,j,k = B.quaternion_algebra().gens(); I - Fractional ideal (2 + 2*j + 106*k, i + 2*j + 105*k, 4*j + 64*k, 148*k) + Fractional ideal (4, 148*i, 2 + 106*i + 2*j, 2 + 147*i + k) sage: I.scale(i) - Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) + Fractional ideal (296, 4*i, 2 + 2*i + 2*j, 212 + 2*i + 2*k) sage: I.scale(i, left=True) - Fractional ideal (2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j) + Fractional ideal (296, 4*i, 294 + 2*i + 2*j, 84 + 2*i + 2*k) sage: I.scale(i, left=False) - Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) + Fractional ideal (296, 4*i, 2 + 2*i + 2*j, 212 + 2*i + 2*k) sage: i * I.gens()[0] - 2*i - 212*j + 2*k + 4*i sage: I.gens()[0] * i - 2*i + 212*j - 2*k + 4*i TESTS: @@ -2656,9 +2658,9 @@ def scale(self, alpha, left=False): Q = self.quaternion_algebra() alpha = Q(alpha) if left: - gens = [alpha * b for b in self.basis()] + gens = basis_for_quaternion_lattice([alpha * b for b in self.basis()]) else: - gens = [b * alpha for b in self.basis()] + gens = basis_for_quaternion_lattice([b * alpha for b in self.basis()]) left_order = self.__left_order if alpha in QQ or not left else None right_order = self.__right_order if alpha in QQ or left else None return Q.ideal(gens, check=False, @@ -2673,7 +2675,7 @@ def quaternion_algebra(self): EXAMPLES:: sage: I = BrandtModule(3, 5).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k) + Fractional ideal (8, 40*i, 6 + 28*i + 2*j, 4 + 18*i + 2*k) sage: I.quaternion_algebra() Quaternion Algebra (-1, -3) with base ring Rational Field """ @@ -2781,7 +2783,7 @@ def right_order(self): EXAMPLES:: sage: I = BrandtModule(389).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 2*k, i + 2*j + k, 8*j, 8*k) + Fractional ideal (8, 8*i, 2 + 6*i + 2*j, 6 + 3*i + k) sage: I.right_order() Order of Quaternion Algebra (-2, -389) with base ring Rational Field with basis (1/2 + 1/2*j + 1/2*k, 1/4*i + 1/2*j + 1/4*k, j, k) @@ -2818,7 +2820,7 @@ def __repr__(self): sage: type(I) sage: I.__repr__() - 'Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 2*k, 8*j, 8*k)' + 'Fractional ideal (8, 8*i, 6 + 4*i + 2*j, 4 + 2*i + 2*k)' """ return 'Fractional ideal %s' % (self.gens(),) @@ -2956,9 +2958,9 @@ def reduced_basis(self): sage: B = BrandtModule(2,37); I = B.right_ideals()[0] sage: I - Fractional ideal (2 + 2*i + 2*j + 2*k, 4*i + 108*k, 4*j + 44*k, 148*k) + Fractional ideal (4, 148*i, 108*i + 4*j, 2 + 2*i + 2*j + 2*k) sage: I.reduced_basis() - (2 + 2*i + 2*j + 2*k, 4, -2 - 2*i - 14*j + 14*k, -16*i + 12*k) + (4, 2 + 2*i + 2*j + 2*k, 2 - 14*i + 14*j - 2*k, 2 - 2*i - 14*j + 14*k) sage: l = I.reduced_basis() sage: assert all(l[i].reduced_norm() <= l[i+1].reduced_norm() for i in range(len(l) - 1)) @@ -2990,7 +2992,7 @@ def theta_series_vector(self, B): EXAMPLES:: sage: I = BrandtModule(37).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 2*k, i + 2*j + k, 8*j, 8*k) + Fractional ideal (8, 8*i, 2 + 6*i + 2*j, 6 + 3*i + k) sage: I.theta_series_vector(5) (1, 0, 2, 2, 6) sage: I.theta_series_vector(10) @@ -3022,10 +3024,10 @@ def quadratic_form(self): sage: I = BrandtModule(11).right_ideals()[1] sage: Q = I.quadratic_form(); Q Quadratic form in 4 variables over Rational Field with coefficients: - [ 18 22 33 22 ] - [ * 7 22 11 ] - [ * * 22 0 ] - [ * * * 22 ] + [ 2 0 3 2 ] + [ * 2 2 1 ] + [ * * 3 2 ] + [ * * * 2 ] sage: Q.theta_series(10) 1 + 12*q^2 + 12*q^3 + 12*q^4 + 12*q^5 + 24*q^6 + 24*q^7 + 36*q^8 + 36*q^9 + O(q^10) sage: I.theta_series(10) @@ -3055,7 +3057,7 @@ def minimal_element(self): sage: O = Quat.maximal_order(); O Order of Quaternion Algebra (-3, -101) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) sage: (O * 5).minimal_element() - 5/2 + 5/2*i + 5 sage: alpha = 1/2 + 1/6*i + j + 55/3*k sage: I = O*141 + O*alpha; I.norm() 141 @@ -3091,7 +3093,7 @@ def theta_series(self, B, var='q'): EXAMPLES:: sage: I = BrandtModule(11).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 2*k, 8*j, 8*k) + Fractional ideal (8, 8*i, 6 + 4*i + 2*j, 4 + 2*i + 2*k) sage: I.norm() 32 sage: I.theta_series(5) @@ -3126,12 +3128,12 @@ def gram_matrix(self): EXAMPLES:: sage: I = BrandtModule(3,5).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k) + Fractional ideal (8, 40*i, 6 + 28*i + 2*j, 4 + 18*i + 2*k) sage: I.gram_matrix() - [ 640 1920 2112 1920] - [ 1920 14080 13440 16320] - [ 2112 13440 13056 15360] - [ 1920 16320 15360 19200] + [ 256 0 192 128] + [ 0 6400 4480 2880] + [ 192 4480 3328 2112] + [ 128 2880 2112 1408] """ A = self.gens() two = QQ(2) @@ -3187,7 +3189,7 @@ def conjugate(self): EXAMPLES:: sage: I = BrandtModule(3,5).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k) + Fractional ideal (8, 40*i, 6 + 28*i + 2*j, 4 + 18*i + 2*k) sage: I.conjugate() Fractional ideal (2 + 2*j + 28*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k) """ @@ -3206,13 +3208,13 @@ def __mul__(self, right): EXAMPLES:: sage: I = BrandtModule(3,5).right_ideals()[1]; I - Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k) + Fractional ideal (8, 40*i, 6 + 28*i + 2*j, 4 + 18*i + 2*k) sage: I*I - Fractional ideal (8 + 24*j + 16*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k) + Fractional ideal (32, 160*i, 24 + 112*i + 8*j, 16 + 72*i + 8*k) sage: I*I.conjugate() - Fractional ideal (16 + 16*j + 224*k, 8*i + 16*j + 136*k, 32*j + 128*k, 320*k) + Fractional ideal (32, 320*i, 16 + 224*i + 16*j, 16 + 232*i + 8*k) sage: I.multiply_by_conjugate(I) - Fractional ideal (16 + 16*j + 224*k, 8*i + 16*j + 136*k, 32*j + 128*k, 320*k) + Fractional ideal (32, 320*i, 16 + 224*i + 16*j, 16 + 232*i + 8*k) """ if isinstance(right, QuaternionOrder): right = right.unit_ideal() @@ -3222,7 +3224,7 @@ def __mul__(self, right): # if self.__right_order == right.__left_order: # left_order = self.__left_order # right_order = right.__right_order - basis = tuple(basis_for_quaternion_lattice(gens, reverse=False)) + basis = tuple(basis_for_quaternion_lattice(gens)) A = self.quaternion_algebra() return A.ideal(basis, check=False) @@ -3233,9 +3235,9 @@ def __add__(self, other): EXAMPLES:: sage: I = BrandtModule(11,5).right_ideals()[1]; I - Fractional ideal (2 + 2*j + 20*k, 2*i + 4*j + 6*k, 8*j, 40*k) + Fractional ideal (8, 40*i, 2 + 20*i + 2*j, 4 + 14*i + 2*k) sage: J = BrandtModule(11,5).right_ideals()[2]; J - Fractional ideal (2 + 6*j + 20*k, 2*i + 4*j + 26*k, 8*j, 40*k) + Fractional ideal (8, 40*i, 6 + 20*i + 2*j, 4 + 34*i + 2*k) sage: I + J Fractional ideal (2 + 2*j, 2*i + 6*k, 4*j, 20*k) """ @@ -3276,7 +3278,7 @@ def free_module(self): sage: X = BrandtModule(3,5).right_ideals() sage: X[0] - Fractional ideal (2 + 2*j + 8*k, 2*i + 18*k, 4*j + 16*k, 20*k) + Fractional ideal (4, 20*i, 2 + 8*i + 2*j, 18*i + 2*k) sage: X[0].free_module() Free module of degree 4 and rank 4 over Integer Ring Echelon basis matrix: @@ -3356,13 +3358,13 @@ def multiply_by_conjugate(self, J): sage: R = BrandtModule(3,5).right_ideals() sage: R[0].multiply_by_conjugate(R[1]) - Fractional ideal (8 + 8*j + 112*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k) + Fractional ideal (32, 160*i, 8 + 112*i + 8*j, 16 + 72*i + 8*k) sage: R[0]*R[1].conjugate() - Fractional ideal (8 + 8*j + 112*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k) + Fractional ideal (32, 160*i, 8 + 112*i + 8*j, 16 + 72*i + 8*k) """ Jbar = [b.conjugate() for b in J.basis()] gens = [a * b for a in self.basis() for b in Jbar] - basis = tuple(basis_for_quaternion_lattice(gens, reverse=False)) + basis = tuple(basis_for_quaternion_lattice(gens)) R = self.quaternion_algebra() return R.ideal(basis, check=False) @@ -3392,7 +3394,7 @@ def pushforward(self, J, side=None): 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) + Fractional ideal (3, 15*i, 3/2 + 10*i + 1/2*j, 1 + 9/10*i + 1/10*k) TESTS:: @@ -3404,11 +3406,11 @@ def pushforward(self, J, side=None): ... 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) + Fractional ideal (1, i, 1/2 + 1/2*j, 1/2*i + 1/2*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) + Fractional ideal (3, 15*i, 3/2 + 10*i + 1/2*j, 1 + 9/10*i + 1/10*k) sage: I1.pushforward(I2, side='right') Traceback (most recent call last): ... @@ -3648,7 +3650,7 @@ def is_right_equivalent(self, J, B=10, certificate=False): sage: OO = R[0].left_order() sage: S = OO.right_ideal([3*a for a in R[0].basis()]) sage: R[0].is_right_equivalent(S, certificate=True) - (True, -1/3) + (True, 1/3) sage: -1/3*S == R[0] True @@ -3785,39 +3787,39 @@ def cyclic_right_subideals(self, p, alpha=None): sage: B = BrandtModule(2,37); I = B.right_ideals()[0] sage: I.cyclic_right_subideals(3) - [Fractional ideal (2 + 2*i + 10*j + 90*k, 4*i + 4*j + 152*k, 12*j + 132*k, 444*k), - Fractional ideal (2 + 2*i + 2*j + 150*k, 4*i + 8*j + 196*k, 12*j + 132*k, 444*k), - Fractional ideal (2 + 2*i + 6*j + 194*k, 4*i + 8*j + 344*k, 12*j + 132*k, 444*k), - Fractional ideal (2 + 2*i + 6*j + 46*k, 4*i + 4*j + 4*k, 12*j + 132*k, 444*k)] + [Fractional ideal (12, 444*i, 8 + 404*i + 4*j, 2 + 150*i + 2*j + 2*k), + Fractional ideal (12, 444*i, 4 + 256*i + 4*j, 10 + 150*i + 2*j + 2*k), + Fractional ideal (12, 444*i, 8 + 256*i + 4*j, 6 + 298*i + 2*j + 2*k), + Fractional ideal (12, 444*i, 4 + 404*i + 4*j, 6 + 2*i + 2*j + 2*k)] sage: B = BrandtModule(5,389); I = B.right_ideals()[0] sage: C = I.cyclic_right_subideals(3); C - [Fractional ideal (2 + 10*j + 546*k, i + 6*j + 133*k, 12*j + 3456*k, 4668*k), - Fractional ideal (2 + 2*j + 2910*k, i + 6*j + 3245*k, 12*j + 3456*k, 4668*k), - Fractional ideal (2 + i + 2295*k, 3*i + 2*j + 3571*k, 4*j + 2708*k, 4668*k), - Fractional ideal (2 + 2*i + 2*j + 4388*k, 3*i + 2*j + 2015*k, 4*j + 4264*k, 4668*k)] + [Fractional ideal (12, 4668*i, 10 + 3426*i + 2*j, 6 + 379*i + k), + Fractional ideal (12, 4668*i, 2 + 3426*i + 2*j, 6 + 3491*i + k), + Fractional ideal (12, 4 + 1556*i, 6 + 942*i + 6*j, 693*i + 2*j + k), + Fractional ideal (12, 8 + 1556*i, 6 + 942*i + 6*j, 2 + 1007*i + 4*j + k)] sage: [(I.free_module()/J.free_module()).invariants() for J in C] [(3, 3), (3, 3), (3, 3), (3, 3)] sage: I.scale(3).cyclic_right_subideals(3) - [Fractional ideal (6 + 30*j + 1638*k, 3*i + 18*j + 399*k, 36*j + 10368*k, 14004*k), - Fractional ideal (6 + 6*j + 8730*k, 3*i + 18*j + 9735*k, 36*j + 10368*k, 14004*k), - Fractional ideal (6 + 3*i + 6885*k, 9*i + 6*j + 10713*k, 12*j + 8124*k, 14004*k), - Fractional ideal (6 + 6*i + 6*j + 13164*k, 9*i + 6*j + 6045*k, 12*j + 12792*k, 14004*k)] + [Fractional ideal (36, 14004*i, 30 + 10278*i + 6*j, 18 + 1137*i + 3*k), + Fractional ideal (36, 14004*i, 6 + 10278*i + 6*j, 18 + 10473*i + 3*k), + Fractional ideal (36, 12 + 4668*i, 18 + 2826*i + 18*j, 2079*i + 6*j + 3*k), + Fractional ideal (36, 24 + 4668*i, 18 + 2826*i + 18*j, 6 + 3021*i + 12*j + 3*k)] sage: C = I.scale(1/9).cyclic_right_subideals(3); C - [Fractional ideal (2/9 + 10/9*j + 182/3*k, 1/9*i + 2/3*j + 133/9*k, 4/3*j + 384*k, 1556/3*k), - Fractional ideal (2/9 + 2/9*j + 970/3*k, 1/9*i + 2/3*j + 3245/9*k, 4/3*j + 384*k, 1556/3*k), - Fractional ideal (2/9 + 1/9*i + 255*k, 1/3*i + 2/9*j + 3571/9*k, 4/9*j + 2708/9*k, 1556/3*k), - Fractional ideal (2/9 + 2/9*i + 2/9*j + 4388/9*k, 1/3*i + 2/9*j + 2015/9*k, 4/9*j + 4264/9*k, 1556/3*k)] + [Fractional ideal (4/3, 1556/3*i, 10/9 + 1142/3*i + 2/9*j, 2/3 + 379/9*i + 1/9*k), + Fractional ideal (4/3, 1556/3*i, 2/9 + 1142/3*i + 2/9*j, 2/3 + 3491/9*i + 1/9*k), + Fractional ideal (4/3, 4/9 + 1556/9*i, 2/3 + 314/3*i + 2/3*j, 77*i + 2/9*j + 1/9*k), + Fractional ideal (4/3, 8/9 + 1556/9*i, 2/3 + 314/3*i + 2/3*j, 2/9 + 1007/9*i + 4/9*j + 1/9*k)] sage: [(I.scale(1/9).free_module()/J.free_module()).invariants() for J in C] [(3, 3), (3, 3), (3, 3), (3, 3)] sage: Q. = QuaternionAlgebra(-2,-5) sage: I = Q.ideal([Q(1),i,j,k]) sage: I.cyclic_right_subideals(3) - [Fractional ideal (1 + 2*j, i + k, 3*j, 3*k), - Fractional ideal (1 + j, i + 2*k, 3*j, 3*k), - Fractional ideal (1 + 2*i, 3*i, j + 2*k, 3*k), - Fractional ideal (1 + i, 3*i, j + k, 3*k)] + [Fractional ideal (3, 3*i, 2 + j, i + k), + Fractional ideal (3, 3*i, 1 + j, 2*i + k), + Fractional ideal (3, 2 + i, 3*j, 2*j + k), + Fractional ideal (3, 1 + i, 3*j, j + k)] The general algorithm is not yet implemented here:: @@ -3829,17 +3831,18 @@ def cyclic_right_subideals(self, p, alpha=None): """ R = self.right_order() Q = self.quaternion_algebra() + basis = basis_for_quaternion_lattice(self.basis(), reverse=False) f = Q.modp_splitting_map(p) if alpha is not None: alpha = f(alpha) W = GF(p)**4 try: - A = W.span_of_basis([W(f(a).list()) for a in self.basis()]) + A = W.span_of_basis([W(f(a).list()) for a in basis]) scale = 1 - IB = self.basis_matrix() + IB = matrix(map(list, basis)) except (ValueError, ZeroDivisionError): # try rescaling the ideal. - B, d = self.basis_matrix()._clear_denom() + B, d = matrix(map(list, basis))._clear_denom() g = gcd(B.list()) IB = B / g scale = g / d @@ -3851,7 +3854,7 @@ def cyclic_right_subideals(self, p, alpha=None): # However, I haven't implemented that algorithm yet. raise NotImplementedError("general algorithm not implemented (%s)" % msg) - Ai = A.basis_matrix()**(-1) + Ai = ~A.basis_matrix() AiB = Ai.change_ring(QQ) * IB # Do not care about the denominator since we're really working in I/p*I. @@ -3942,7 +3945,7 @@ def primitive_decomposition(self): sage: Jequiv*g == J True sage: Jequiv, g - (Fractional ideal (1/2 + 1/2*i + 7/2*j + 13/2*k, i + 3*k, 5*j + 5*k, 10*k), 7) + (Fractional ideal (10, 5 + 5*i, 3 + j, 13/2 + 7/2*i + 1/2*j + 1/2*k), 7) TESTS: @@ -4004,7 +4007,7 @@ def is_primitive(self) -> bool: ####################################################################### -def basis_for_quaternion_lattice(gens, reverse=None): +def basis_for_quaternion_lattice(gens, reverse=True): r""" Return a basis for the `\ZZ`-lattice in a quaternion algebra spanned by the given gens. @@ -4014,26 +4017,20 @@ def basis_for_quaternion_lattice(gens, reverse=None): - ``gens`` -- list of elements of a single quaternion algebra - ``reverse`` -- when computing the HNF do it on the basis - `(k,j,i,1)` instead of `(1,i,j,k)`; this ensures - that if ``gens`` are the generators for an order, - the first returned basis vector is 1 + `(k,j,i,1)` instead of `(1,i,j,k)`; this ensures that if + ``gens`` are the generators for a fractional ideal (in + particular, an order), the first returned basis vector + equals the norm of the ideal (in case of an order, `1`) EXAMPLES:: sage: from sage.algebras.quatalg.quaternion_algebra import basis_for_quaternion_lattice sage: A. = QuaternionAlgebra(-1,-7) sage: basis_for_quaternion_lattice([i+j, i-j, 2*k, A(1/3)]) - doctest:warning ... DeprecationWarning: ... - [1/3, i + j, 2*j, 2*k] - + [1/3, 2*i, i + j, 2*k] sage: basis_for_quaternion_lattice([A(1),i,j,k]) [1, i, j, k] """ - if reverse is None: - from sage.misc.superseded import deprecation - deprecation(34880, 'The default value for the "reverse" argument to basis_for_quaternion_lattice() will' - ' change from False to True. Pass the argument explicitly to silence this warning.') - reverse = False if not gens: return [] Z, d = quaternion_algebra_cython.integral_matrix_and_denom_from_rational_quaternions(gens, reverse) diff --git a/src/sage/algebras/rational_cherednik_algebra.py b/src/sage/algebras/rational_cherednik_algebra.py index 58b3ce5441d..1ded26a1112 100644 --- a/src/sage/algebras/rational_cherednik_algebra.py +++ b/src/sage/algebras/rational_cherednik_algebra.py @@ -245,19 +245,19 @@ def algebra_generators(self): def gen_map(k): if k[0] == 's': i = int(k[1:]) - return self.monomial( (self._hd.one(), + return self.monomial((self._hd.one(), self._weyl.group_generators()[i], - self._h.one()) ) + self._h.one())) if k[1] == 'c': i = int(k[2:]) - return self.monomial( (self._hd.one(), + return self.monomial((self._hd.one(), self._weyl.one(), - self._h.monoid_generators()[i]) ) + self._h.monoid_generators()[i])) i = int(k[1:]) - return self.monomial( (self._hd.monoid_generators()[i], + return self.monomial((self._hd.monoid_generators()[i], self._weyl.one(), - self._h.one()) ) + self._h.one())) return Family(keys, gen_map) @cached_method @@ -328,11 +328,11 @@ def product_on_basis(self, left, right): def commute_w_hd(w, al): # al is given as a dictionary ret = P.one() for k in al: - x = sum(c * gens_dict[i] for i,c in alpha[k].weyl_action(w)) + x = sum(c * gens_dict[i] for i, c in alpha[k].weyl_action(w)) ret *= x**al[k] - ret = ret.dict() + ret = ret.monomial_coefficients() for k in ret: - yield (self._hd({I[i]: e for i,e in enumerate(k) if e != 0}), ret[k]) + yield (self._hd({I[i]: e for i, e in enumerate(k) if e != 0}), ret[k]) # Do Lac Ra if they are both non-trivial if dl and dr: @@ -351,16 +351,16 @@ def commute_w_hd(w, al): # al is given as a dictionary del dr[ir] # We now commute right roots past the left reflections: s Ra = Ra' s - cur = self._from_dict({ (hd, s*right[1], right[2]): c * cc + cur = self._from_dict({(hd, s*right[1], right[2]): c * cc for s,c in terms - for hd, cc in commute_w_hd(s, dr) }) - cur = self.monomial( (left[0], left[1], self._h(dl)) ) * cur + for hd, cc in commute_w_hd(s, dr)}) + cur = self.monomial((left[0], left[1], self._h(dl))) * cur # Add back in the commuted h and hd elements - rem = self.monomial( (left[0], left[1], self._h(dl)) ) - rem = rem * self.monomial( (self._hd({ir:1}), self._weyl.one(), - self._h({il:1})) ) - rem = rem * self.monomial( (self._hd(dr), right[1], right[2]) ) + rem = self.monomial((left[0], left[1], self._h(dl))) + rem = rem * self.monomial((self._hd({ir:1}), self._weyl.one(), + self._h({il:1}))) + rem = rem * self.monomial((self._hd(dr), right[1], right[2])) return cur + rem @@ -374,19 +374,19 @@ def commute_w_hd(w, al): # al is given as a dictionary for i,c in alphacheck[k].weyl_action(right[1].reduced_word(), inverse=True)) ret *= x**dl[k] - ret = ret.dict() + ret = ret.monomial_coefficients() w = left[1]*right[1] - return self._from_dict({ (left[0], w, + return self._from_dict({(left[0], w, self._h({I[i]: e for i,e in enumerate(k) if e != 0}) * right[2] ): ret[k] - for k in ret }) + for k in ret}) # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac, # so we must commute Ls Ra = Ra' Ls w = left[1]*right[1] - return self._from_dict({ (left[0] * hd, w, right[2]): c - for hd, c in commute_w_hd(left[1], dr) }) + return self._from_dict({(left[0] * hd, w, right[2]): c + for hd, c in commute_w_hd(left[1], dr)}) @cached_method def _product_coroot_root(self, i, j): @@ -429,12 +429,12 @@ def _product_coroot_root(self, i, j): al = Q.simple_root(j) R = self.base_ring() - terms = [( self._weyl.one(), self._t * R(ac.scalar(al)) )] + terms = [(self._weyl.one(), self._t * R(ac.scalar(al)))] for s in self._reflections: # p[0] is the root, p[1] is the coroot, p[2] the value c_s pr, pc, c = self._reflections[s] - terms.append(( s, c * R(ac.scalar(pr) * pc.scalar(al) - / pc.scalar(pr)) )) + terms.append((s, c * R(ac.scalar(pr) * pc.scalar(al) + / pc.scalar(pr)))) return tuple(terms) def degree_on_basis(self, m): diff --git a/src/sage/algebras/splitting_algebra.py b/src/sage/algebras/splitting_algebra.py index 6fe2880d825..43d72ed7470 100644 --- a/src/sage/algebras/splitting_algebra.py +++ b/src/sage/algebras/splitting_algebra.py @@ -102,7 +102,7 @@ def is_unit(self): return super().is_unit() - def dict(self): + def monomial_coefficients(self): r""" Return the dictionary of ``self`` according to its lift to the cover. @@ -110,11 +110,18 @@ def dict(self): sage: from sage.algebras.splitting_algebra import SplittingAlgebra sage: CR3. = SplittingAlgebra(cyclotomic_polynomial(3)) - sage: (e3 + 42).dict() + sage: f = e3 + 42 + sage: f.monomial_coefficients() + {0: 42, 1: 1} + + ``dict`` is an alias:: + + sage: f.dict() {0: 42, 1: 1} """ - return self.lift().dict() + return self.lift().monomial_coefficients() + dict = monomial_coefficients # ------------------------------------------------------------------------------------------------------------------ # Parent class of the splitting algebra @@ -282,7 +289,7 @@ def __init__(self, monic_polynomial, names='X', iterate=True, warning=True): root_names_reduces.remove(root_name) P = base_ring_step[root_names_reduces[0]] - p = P(monic_polynomial.dict()) + p = P(monic_polynomial.monomial_coefficients()) q, _ = p.quo_rem(P.gen() - first_root) verbose("Invoking recursion with: %s" % (q,)) @@ -349,10 +356,10 @@ def __init__(self, monic_polynomial, names='X', iterate=True, warning=True): if not check.is_zero(): continue root_inv = self.one() - for pos in range(deg_cf-1 ): - root_inv = (-1 )**(pos+1 ) * cf[deg_cf-pos-1 ] - root_inv * root + for pos in range(deg_cf-1): + root_inv = (-1)**(pos+1) * cf[deg_cf-pos-1] - root_inv * root verbose("inverse %s of root %s" % (root_inv, root)) - root_inv = (-1 )**(deg_cf) * cf0_inv * root_inv + root_inv = (-1)**(deg_cf) * cf0_inv * root_inv self._invertible_elements.update({root:root_inv}) verbose("adding inverse %s of root %s" % (root_inv, root)) invert_items = list(self._invertible_elements.items()) diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index c087bb65ba4..053290bc5ed 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -312,9 +312,9 @@ def milnor_multiplication(r,s): else: sum = sum + M[i][j] * 2**j else: - sum = sum + M[i][j] * 2**j - j = j + 1 - i = i + 1 + sum = sum + M[i][j] * 2**j + j += 1 + i += 1 return result @@ -784,7 +784,7 @@ def adem(a, b, c=0, p=2, generic=None): return result # p odd if a == 0 and b == 0: - return {(c,): 1} + return {(c,): 1} if c == 0: bockstein = 0 A = a diff --git a/src/sage/algebras/tensor_algebra.py b/src/sage/algebras/tensor_algebra.py index 0d323a6ebb7..3a5f8d9c033 100644 --- a/src/sage/algebras/tensor_algebra.py +++ b/src/sage/algebras/tensor_algebra.py @@ -583,7 +583,7 @@ def coproduct_on_basis(self, m): # for w in Word(range(p)).shuffle(range(p, k)) ) ##################################################################### -## TensorAlgebra functor +# TensorAlgebra functor class TensorAlgebraFunctor(ConstructionFunctor): @@ -684,7 +684,7 @@ def _apply_functor_to_morphism(self, f): return D.module_morphism(phi, codomain=C) ##################################################################### -## Lift map from the base ring +# Lift map from the base ring class BaseRingLift(Morphism): diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index ac554795cfd..8eae4797c7d 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -786,7 +786,7 @@ def _element_constructor_(self, x): return self.element_class(self, {i: R(c) for i, c in x if R(c) != zero}) x = self._poly_ring(x) return self.element_class(self, {(tuple(m), t): c - for m, c in x.dict().items()}) + for m, c in x.monomial_coefficients().items()}) def _coerce_map_from_(self, R): """ diff --git a/src/sage/algebras/yangian.py b/src/sage/algebras/yangian.py index b17a1de0554..748586a30b9 100644 --- a/src/sage/algebras/yangian.py +++ b/src/sage/algebras/yangian.py @@ -575,13 +575,13 @@ def product_on_gens(self, a, b): # This is the special term of x = 1 x1 = self.zero() if b[1] == a[2]: - x1 += self.monomial( I.gen((a[0]+b[0]-1, a[1], b[2])) ) + x1 += self.monomial(I.gen((a[0]+b[0]-1, a[1], b[2]))) if a[1] == b[2]: - x1 -= self.monomial( I.gen((a[0]+b[0]-1, b[1], a[2])) ) + x1 -= self.monomial(I.gen((a[0]+b[0]-1, b[1], a[2]))) return self.monomial(I.gen(b) * I.gen(a)) + x1 + self.sum( - self.monomial( I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2])) ) - - self.product_on_gens( (a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2]) ) + self.monomial(I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2]))) + - self.product_on_gens((a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2])) for x in range(2, b[0]+1)) def coproduct_on_basis(self, m): @@ -610,9 +610,9 @@ def coproduct_on_basis(self, m): """ T = self.tensor_square() I = self._indices - return T.prod(T.monomial( (I.one(), I.gen((a[0],a[1],a[2]))) ) - + T.monomial( (I.gen((a[0],a[1],a[2])), I.one()) ) - + T.sum_of_terms([(( I.gen((s,a[1],k)), I.gen((a[0]-s,k,a[2])) ), 1) + return T.prod(T.monomial((I.one(), I.gen((a[0],a[1],a[2])))) + + T.monomial((I.gen((a[0],a[1],a[2])), I.one())) + + T.sum_of_terms([((I.gen((s,a[1],k)), I.gen((a[0]-s,k,a[2]))), 1) for k in range(1, self._n+1) for s in range(1, a[0])]) for a,exp in m._sorted_items() for p in range(exp)) @@ -881,12 +881,12 @@ def product_on_gens(self, a, b): x1 = self.zero() if a[0]+b[0]-1 <= self._level: if b[1] == a[2]: - x1 += self.monomial( I.gen((a[0]+b[0]-1, a[1], b[2])) ) + x1 += self.monomial(I.gen((a[0]+b[0]-1, a[1], b[2]))) if a[1] == b[2]: - x1 -= self.monomial( I.gen((a[0]+b[0]-1, b[1], a[2])) ) + x1 -= self.monomial(I.gen((a[0]+b[0]-1, b[1], a[2]))) return self.monomial(I.gen(b) * I.gen(a)) + x1 + self.sum( - self.monomial( I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2])) ) + self.monomial(I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2]))) - self.product_on_gens((a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2])) for x in range(2, b[0]+1) if a[0]+b[0]-x <= self._level) @@ -1043,8 +1043,8 @@ def antipode_on_basis(self, m): + 10*tbar(1)[1,2]*tbar(1)[1,3]^3*tbar(3)[1,2] + 15*tbar(1)[1,2]^2*tbar(1)[1,3]^2*tbar(3)[1,3] """ - return self.prod( (-1)**exp * self.monomial(a**exp) - for a,exp in reversed(list(m)) ) + return self.prod((-1)**exp * self.monomial(a**exp) + for a,exp in reversed(list(m))) def coproduct_on_basis(self, m): """ diff --git a/src/sage/algebras/yokonuma_hecke_algebra.py b/src/sage/algebras/yokonuma_hecke_algebra.py index 8f366ff3401..7de8ff07798 100644 --- a/src/sage/algebras/yokonuma_hecke_algebra.py +++ b/src/sage/algebras/yokonuma_hecke_algebra.py @@ -245,10 +245,10 @@ def algebra_generators(self): for i in range(self._n): r = list(zero) # Make a copy r[i] = 1 - d['t%s' % (i+1)] = self.monomial( (tuple(r), one) ) + d['t%s' % (i+1)] = self.monomial((tuple(r), one)) G = self._Pn.group_generators() for i in range(1, self._n): - d['g%s' % i] = self.monomial( (tuple(zero), G[i]) ) + d['g%s' % i] = self.monomial((tuple(zero), G[i])) return Family(sorted(d), lambda i: d[i]) @cached_method @@ -494,5 +494,5 @@ def __invert__(self): H = self.parent() t,w = self.support_of_term() c = ~self.coefficients()[0] - telt = H.monomial( (tuple((H._d - e) % H._d for e in t), H._Pn.one()) ) + telt = H.monomial((tuple((H._d - e) % H._d for e in t), H._Pn.one())) return c * telt * H.prod(H.inverse_g(i) for i in reversed(w.reduced_word())) diff --git a/src/sage/all.py b/src/sage/all.py index 916e80eef0d..7c505cd3242 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -28,7 +28,8 @@ sage: def is_not_allowed(frame): ....: module = inspect.getmodule(frame) ....: if module is None: return False - ....: return not any(module.__name__.startswith(name) for name in allowed) + ....: return not any(module.__name__.startswith(name) + ....: for name in allowed) sage: [inspect.getmodule(f).__name__ for f in frames if is_not_allowed(f)] [] @@ -57,97 +58,98 @@ import operator import math -################ end setup warnings ############################### +# ############### end setup warnings ############################### -from sage.all__sagemath_repl import * # includes .all__sagemath_objects, .all__sagemath_environment +from sage.all__sagemath_repl import * +# this includes .all__sagemath_objects, .all__sagemath_environment -################################################################### +# ################################################################## # This import also sets up the interrupt handler from cysignals.signals import (AlarmInterrupt, SignalError, - sig_on_reset as sig_on_count) + sig_on_reset as sig_on_count) -from time import sleep +from time import sleep from functools import reduce # in order to keep reduce in python3 import sage.misc.lazy_import -from sage.misc.all import * # takes a while -from sage.typeset.all import * +from sage.misc.all import * # takes a while +from sage.typeset.all import * from sage.misc.sh import sh -from sage.libs.all import * +from sage.libs.all import * from sage.data_structures.all import * -from sage.structure.all import * -from sage.rings.all import * -from sage.arith.all import * -from sage.matrix.all import * - -from sage.symbolic.all import * -from sage.modules.all import * -from sage.monoids.all import * -from sage.algebras.all import * -from sage.modular.all import * -from sage.sat.all import * -from sage.schemes.all import * -from sage.graphs.all import * -from sage.groups.all import * -from sage.arith.power import generic_power as power -from sage.databases.all import * +from sage.structure.all import * +from sage.rings.all import * +from sage.arith.all import * +from sage.matrix.all import * + +from sage.symbolic.all import * +from sage.modules.all import * +from sage.monoids.all import * +from sage.algebras.all import * +from sage.modular.all import * +from sage.sat.all import * +from sage.schemes.all import * +from sage.graphs.all import * +from sage.groups.all import * +from sage.arith.power import generic_power as power +from sage.databases.all import * from sage.categories.all import * -from sage.sets.all import * +from sage.sets.all import * from sage.probability.all import * from sage.interfaces.all import * -from sage.functions.all import * -from sage.calculus.all import * +from sage.functions.all import * +from sage.calculus.all import * -from sage.cpython.all import * +from sage.cpython.all import * -from sage.crypto.all import * +from sage.crypto.all import * import sage.crypto.mq as mq -from sage.plot.all import * -from sage.plot.plot3d.all import * +from sage.plot.all import * +from sage.plot.plot3d.all import * -from sage.coding.all import * -from sage.combinat.all import * +from sage.coding.all import * +from sage.combinat.all import * from sage.lfunctions.all import * -from sage.geometry.all import * -from sage.geometry.triangulation.all import * -from sage.geometry.riemannian_manifolds.all import * +from sage.geometry.all import * +from sage.geometry.triangulation.all import * +from sage.geometry.riemannian_manifolds.all import * -from sage.dynamics.all import * +from sage.dynamics.all import * -from sage.homology.all import * +from sage.homology.all import * -from sage.topology.all import * +from sage.topology.all import * from sage.quadratic_forms.all import * -from sage.games.all import * +from sage.games.all import * -from sage.logic.all import * +from sage.logic.all import * -from sage.numerical.all import * +from sage.numerical.all import * -from sage.stats.all import * +from sage.stats.all import * import sage.stats.all as stats -from sage.parallel.all import * +from sage.parallel.all import * -from sage.ext.fast_callable import fast_callable -from sage.ext.fast_eval import fast_float +from sage.ext.fast_callable import fast_callable +from sage.ext.fast_eval import fast_float from sage.sandpiles.all import * -from sage.tensor.all import * +from sage.tensor.all import * -from sage.matroids.all import * +from sage.matroids.all import * from sage.game_theory.all import * @@ -167,7 +169,7 @@ _init_qqbar() ########################################################### -#### WARNING: +# WARNING: # DO *not* import numpy / matplotlib / networkx here!! # Each takes a surprisingly long time to initialize, # and that initialization should be done more on-the-fly @@ -191,20 +193,29 @@ from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.categories.category', 'Sets', Sets) -register_unpickle_override('sage.categories.category_types', 'HeckeModules', HeckeModules) -register_unpickle_override('sage.categories.category_types', 'Objects', Objects) -register_unpickle_override('sage.categories.category_types', 'Rings', Rings) -register_unpickle_override('sage.categories.category_types', 'Fields', Fields) -register_unpickle_override('sage.categories.category_types', 'VectorSpaces', VectorSpaces) -register_unpickle_override('sage.categories.category_types', 'Schemes_over_base', sage.categories.schemes.Schemes_over_base) -register_unpickle_override('sage.categories.category_types', 'ModularAbelianVarieties', ModularAbelianVarieties) +register_unpickle_override('sage.categories.category_types', 'HeckeModules', + HeckeModules) +register_unpickle_override('sage.categories.category_types', 'Objects', + Objects) +register_unpickle_override('sage.categories.category_types', 'Rings', + Rings) +register_unpickle_override('sage.categories.category_types', 'Fields', + Fields) +register_unpickle_override('sage.categories.category_types', 'VectorSpaces', + VectorSpaces) +register_unpickle_override('sage.categories.category_types', + 'Schemes_over_base', + sage.categories.schemes.Schemes_over_base) +register_unpickle_override('sage.categories.category_types', + 'ModularAbelianVarieties', + ModularAbelianVarieties) register_unpickle_override('sage.libs.pari.gen_py', 'pari', pari) # Cache the contents of star imports. sage.misc.lazy_import.save_cache_file() -### Debugging for Singular, see trac #10903 +# ##### Debugging for Singular, see issue #10903 # from sage.libs.singular.ring import poison_currRing # sys.settrace(poison_currRing) @@ -226,7 +237,7 @@ sage.misc.lazy_import.finish_startup() -### Python broke large ints; see trac #34506 +# Python broke large ints; see trac #34506 if hasattr(sys, "set_int_max_str_digits"): sys.set_int_max_str_digits(0) diff --git a/src/sage/all__sagemath_repl.py b/src/sage/all__sagemath_repl.py index e2866352cf6..c830950c26b 100644 --- a/src/sage/all__sagemath_repl.py +++ b/src/sage/all__sagemath_repl.py @@ -22,7 +22,7 @@ warnings.filterwarnings('ignore', category=DeprecationWarning, module='(IPython|ipykernel|jupyter_client|jupyter_core|nbformat|notebook|ipywidgets|storemagic|jedi)') -# scipy 1.18 introduced reprecation warnings on a number of things they are moving to +# scipy 1.18 introduced deprecation warnings on a number of things they are moving to # numpy, e.g. DeprecationWarning: scipy.array is deprecated # and will be removed in SciPy 2.0.0, use numpy.array instead # This affects networkx 2.2 up and including 2.4 (cf. :issue:29766) diff --git a/src/sage/arith/meson.build b/src/sage/arith/meson.build new file mode 100644 index 00000000000..3c3656c5738 --- /dev/null +++ b/src/sage/arith/meson.build @@ -0,0 +1,34 @@ +py.install_sources( + 'all.py', + 'all__sagemath_objects.py', + 'constants.pxd', + 'functions.pxd', + 'long.pxd', + 'misc.py', + 'multi_modular.pxd', + 'numerical_approx.pxd', + 'power.pxd', + 'rational_reconstruction.pxd', + subdir: 'sage/arith', +) + +extension_data = { + 'functions' : files('functions.pyx'), + 'multi_modular' : files('multi_modular.pyx'), + 'numerical_approx' : files('numerical_approx.pyx'), + 'power' : files('power.pyx'), + 'rational_reconstruction' : files('rational_reconstruction.pyx'), + 'srange' : files('srange.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/arith', + install: true, + include_directories: [inc_cpython, inc_ext, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index b7f60bc4251..745d5fcbbe7 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -6227,8 +6227,8 @@ def dedekind_sum(p, q, algorithm='default'): return flint_dedekind_sum(p, q) if algorithm == 'pari': - import sage.interfaces.gp - x = sage.interfaces.gp.gp('sumdedekind(%s,%s)' % (p, q)) + from sage.libs.pari import pari + x = pari.sumdedekind(p, q) return Rational(x) raise ValueError('unknown algorithm') @@ -6374,3 +6374,75 @@ def dedekind_psi(N): """ N = Integer(N) return Integer(N * prod(1 + 1 / p for p in N.prime_divisors())) + +def smooth_part(x, base): + r""" + Given an element ``x`` of a Euclidean domain and a factor base ``base``, + return a :class:`~sage.structure.factorization.Factorization` object + corresponding to the largest divisor of ``x`` that splits completely + over ``base``. + + The factor base can be specified in the following ways: + + - A sequence of elements. + + - A :class:`~sage.rings.generic.ProductTree` built from such a sequence. + (Caching the tree in the caller will speed things up if this function + is called multiple times with the same factor base.) + + EXAMPLES:: + + sage: from sage.arith.misc import smooth_part + sage: from sage.rings.generic import ProductTree + sage: smooth_part(10^77+1, primes(1000)) + 11^2 * 23 * 463 + sage: tree = ProductTree(primes(1000)) + sage: smooth_part(10^77+1, tree) + 11^2 * 23 * 463 + sage: smooth_part(10^99+1, tree) + 7 * 11^2 * 13 * 19 * 23 + """ + from sage.rings.generic import ProductTree + if isinstance(base, ProductTree): + tree = base + else: + tree = ProductTree(base) + fs = [] + rems = tree.remainders(x) + for j,(p,r) in enumerate(zip(tree, rems)): + if not r: + x //= p + v = 1 + while True: + y,r = divmod(x, p) + if r: + break + x = y + v += 1 + fs.append((p,v)) + from sage.structure.factorization import Factorization + return Factorization(fs) + +def coprime_part(x, base): + r""" + Given an element ``x`` of a Euclidean domain and a factor base ``base``, + return the largest divisor of ``x`` that is not divisible by any element + of ``base``. + + ALGORITHM: Divide `x` by the :func:`smooth_part`. + + EXAMPLES:: + + sage: from sage.arith.misc import coprime_part, smooth_part + sage: from sage.rings.generic import ProductTree + sage: coprime_part(10^77+1, primes(10000)) + 2159827213801295896328509719222460043196544298056155507343412527 + sage: tree = ProductTree(primes(10000)) + sage: coprime_part(10^55+1, tree) + 6426667196963538873896485804232411 + sage: coprime_part(10^55+1, tree).factor() + 20163494891 * 318727841165674579776721 + sage: prod(smooth_part(10^55+1, tree)) * coprime_part(10^55+1, tree) + 10000000000000000000000000000000000000000000000000000001 + """ + return x // prod(smooth_part(x, base)) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 6d765144e23..ef4854db676 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -385,7 +385,7 @@ 2*pi Ensure that :issue:`25626` is fixed. As the form of the answer is dependent of -the giac version, we simplify it (see :issue:`34037`). :: +the giac version, we simplify it (see :issue:`34037`):: sage: t = SR.var('t') sage: integrate(exp(t)/(t + 1)^2, t, algorithm='giac').full_simplify() @@ -407,7 +407,7 @@ 0.6321205588285577 sage: result = integral(exp(-300.0/(-0.064*x+14.0)),x,0.0,120.0) ... - sage: result + sage: result # abs tol 1e-10 4.62770039817000e-9 To check that :issue:`27092` is fixed:: @@ -568,6 +568,7 @@ def symbolic_sum(expression, v, a, b, algorithm='maxima', hold=False): An example of this summation with Giac:: + sage: # needs giac sage: symbolic_sum(1/(1+k^2), k, -oo, oo, algorithm='giac').factor() pi*(e^(2*pi) + 1)/((e^pi + 1)*(e^pi - 1)) @@ -843,7 +844,7 @@ def symbolic_product(expression, v, a, b, algorithm='maxima', hold=False): - ``'maxima'`` -- use Maxima (the default) - - ``'giac'`` -- use Giac + - ``'giac'`` -- use Giac (optional) - ``'sympy'`` -- use SymPy @@ -1296,7 +1297,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): With the standard package Giac:: - sage: from sage.libs.giac.giac import libgiac # random + sage: # needs sage.libs.giac sage: (exp(-x)/(2+sin(x))).limit(x=oo, algorithm='giac') 0 sage: limit(e^(-1/x), x=0, dir='right', algorithm='giac') @@ -1564,7 +1565,7 @@ def laplace(ex, t, s, algorithm='maxima'): - ``'sympy'`` -- use SymPy - - ``'giac'`` -- use Giac + - ``'giac'`` -- use Giac (optional) .. NOTE:: @@ -1668,7 +1669,7 @@ def laplace(ex, t, s, algorithm='maxima'): (0, True) sage: F # random - sympy <1.9 includes undefined heaviside(0) in answer 1 - sage: laplace(dirac_delta(t), t, s, algorithm='giac') + sage: laplace(dirac_delta(t), t, s, algorithm='giac') # needs giac 1 Heaviside step function can be handled with different interfaces. @@ -1677,8 +1678,9 @@ def laplace(ex, t, s, algorithm='maxima'): sage: laplace(heaviside(t-1), t, s) e^(-s)/s - Try with giac:: + Try with giac, if it is installed:: + sage: # needs giac sage: laplace(heaviside(t-1), t, s, algorithm='giac') e^(-s)/s @@ -1691,6 +1693,7 @@ def laplace(ex, t, s, algorithm='maxima'): Testing Giac:: + sage: # needs giac sage: var('t, s') (t, s) sage: laplace(5*cos(3*t-2)*heaviside(t-2), t, s, algorithm='giac') @@ -1699,13 +1702,14 @@ def laplace(ex, t, s, algorithm='maxima'): Check unevaluated expression from Giac (it is locale-dependent, see :issue:`22833`):: - sage: var('n') - n + sage: # needs giac + sage: n = SR.var('n') sage: laplace(t^n, t, s, algorithm='giac') laplace(t^n, t, s) Testing SymPy:: + sage: n = SR.var('n') sage: F, a, cond = laplace(t^n, t, s, algorithm='sympy') sage: a, cond (0, re(n) > -1) @@ -1806,7 +1810,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): - ``'sympy'`` -- use SymPy - - ``'giac'`` -- use Giac + - ``'giac'`` -- use Giac (optional) .. SEEALSO:: @@ -1843,11 +1847,13 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): The same instance with Giac:: + sage: # needs giac sage: inverse_laplace(1/s^2*exp(-s), s, t, algorithm='giac') (t - 1)*heaviside(t - 1) Transform a rational expression:: + sage: # needs giac sage: inverse_laplace((2*s^2*exp(-2*s) - exp(-s))/(s^3+1), s, t, ....: algorithm='giac') -1/3*(sqrt(3)*e^(1/2*t - 1/2)*sin(1/2*sqrt(3)*(t - 1)) @@ -1864,7 +1870,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): dirac_delta(t) sage: inverse_laplace(1, s, t, algorithm='sympy') dirac_delta(t) - sage: inverse_laplace(1, s, t, algorithm='giac') + sage: inverse_laplace(1, s, t, algorithm='giac') # needs giac dirac_delta(t) TESTS: @@ -1878,6 +1884,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): Testing Giac:: + sage: # needs giac sage: inverse_laplace(exp(-s)/s, s, t, algorithm='giac') heaviside(t - 1) @@ -1888,12 +1895,14 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): Testing unevaluated expression from Giac:: - sage: n = var('n') + sage: # needs giac + sage: n = SR.var('n') sage: inverse_laplace(1/s^n, s, t, algorithm='giac') ilt(1/(s^n), t, s) Try with Maxima:: + sage: n = SR.var('n') sage: inverse_laplace(1/s^n, s, t, algorithm='maxima') ilt(1/(s^n), s, t) @@ -1909,6 +1918,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): Testing the same with Giac:: + sage: # needs giac sage: inverse_laplace(cos(s), s, t, algorithm='giac') ilt(cos(s), t, s) """ @@ -2220,7 +2230,7 @@ def _is_function(v): x^2 + 1 """ # note that Sage variables are callable, so we only check the type - return isinstance(v, Function) or isinstance(v, FunctionType) + return isinstance(v, (Function, FunctionType)) def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): @@ -2472,13 +2482,18 @@ def _find_var(name, interface=None): EXAMPLES:: - sage: y = var('y') + sage: y = SR.var('y') sage: sage.calculus.calculus._find_var('y') y sage: sage.calculus.calculus._find_var('I') I sage: sage.calculus.calculus._find_var(repr(maxima(y)), interface='maxima') y + + :: + + sage: # needs giac + sage: y = SR.var('y') sage: sage.calculus.calculus._find_var(repr(giac(y)), interface='giac') y """ @@ -2585,6 +2600,7 @@ def symbolic_expression_from_string(s, syms=None, accept_sequence=False, *, pars The Giac interface uses a different parser (:issue:`30133`):: + sage: # needs giac sage: from sage.calculus.calculus import SR_parser_giac sage: symbolic_expression_from_string(repr(giac(SR.var('e'))), parser=SR_parser_giac) e diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index 48dad835671..d17f5a14e90 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -1357,14 +1357,10 @@ def desolve_rk4_inner(de, dvar): XMAX = XMIN YMAX = YMIN for s, t in sol: - if s > XMAX: - XMAX = s - if s < XMIN: - XMIN = s - if t > YMAX: - YMAX = t - if t < YMIN: - YMIN = t + XMAX = max(s, XMAX) + XMIN = min(s, XMIN) + YMAX = max(t, YMAX) + YMIN = min(t, YMIN) return plot_slope_field(de, (ivar, XMIN, XMAX), (dvar, YMIN, YMAX)) + R if not (isinstance(dvar, Expression) and dvar.is_symbol()): diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index 8747e1df790..557ce362fd3 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -53,7 +53,7 @@ def simplify(f, algorithm='maxima', **kwds): sage: ex = 1/2*I*x + 1/2*I*sqrt(x^2 - 1) + 1/2/(I*x + I*sqrt(x^2 - 1)) sage: simplify(ex) 1/2*I*x + 1/2*I*sqrt(x^2 - 1) + 1/(2*I*x + 2*I*sqrt(x^2 - 1)) - sage: simplify(ex, algorithm='giac') + sage: simplify(ex, algorithm='giac') # needs giac I*sqrt(x^2 - 1) """ try: diff --git a/src/sage/calculus/functions.py b/src/sage/calculus/functions.py index c044cc0a968..46479be8b0b 100644 --- a/src/sage/calculus/functions.py +++ b/src/sage/calculus/functions.py @@ -139,12 +139,12 @@ def jacobian(functions, variables): [ 0 e^x] """ if isinstance(functions, Matrix) and (functions.nrows() == 1 - or functions.ncols() == 1): + or functions.ncols() == 1): functions = functions.list() - elif not (isinstance(functions, (tuple, list)) or isinstance(functions, Vector)): + elif not isinstance(functions, (tuple, list, Vector)): functions = [functions] - if not isinstance(variables, (tuple, list)) and not isinstance(variables, Vector): + if not isinstance(variables, (tuple, list, Vector)): variables = [variables] return matrix([[diff(f, v) for v in variables] for f in functions]) diff --git a/src/sage/calculus/meson.build b/src/sage/calculus/meson.build new file mode 100644 index 00000000000..541d7d86d75 --- /dev/null +++ b/src/sage/calculus/meson.build @@ -0,0 +1,37 @@ +py.install_sources( + 'all.py', + 'calculus.py', + 'desolvers.py', + 'expr.py', + 'functional.py', + 'functions.py', + 'interpolation.pxd', + 'ode.pxd', + 'predefined.py', + 'test_sympy.py', + 'tests.py', + 'wester.py', + subdir: 'sage/calculus', +) + +extension_data = { + 'integration' : files('integration.pyx'), + 'interpolation' : files('interpolation.pyx'), + 'interpolators' : files('interpolators.pyx'), + 'ode' : files('ode.pyx'), + 'riemann' : files('riemann.pyx'), + 'var' : files('var.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/calculus', + install: true, + include_directories: [inc_numpy], + dependencies: [py_dep, cysignals, gmp, gsl, interpreters_dep], + ) +endforeach + +subdir('transforms') diff --git a/src/sage/calculus/transforms/meson.build b/src/sage/calculus/transforms/meson.build new file mode 100644 index 00000000000..05d3fb59637 --- /dev/null +++ b/src/sage/calculus/transforms/meson.build @@ -0,0 +1,21 @@ +py.install_sources( + 'all.py', + 'dft.py', + 'dwt.pxd', + 'fft.pxd', + subdir: 'sage/calculus/transforms', +) + +extension_data = {'dwt' : files('dwt.pyx'), 'fft' : files('fft.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/calculus/transforms', + install: true, + include_directories: [inc_gsl], + dependencies: [py_dep, cysignals, gmp, gsl], + ) +endforeach + diff --git a/src/sage/categories/additive_magmas.py b/src/sage/categories/additive_magmas.py index 691486e03d4..ccfda512014 100644 --- a/src/sage/categories/additive_magmas.py +++ b/src/sage/categories/additive_magmas.py @@ -971,8 +971,8 @@ def extra_super_categories(self): [Category of unital magmas] sage: C.super_categories() - [Category of unital algebras with basis over Rational Field, - Category of additive magma algebras over Rational Field] + [Category of additive magma algebras over Rational Field, + Category of unital algebras with basis over Rational Field] """ from sage.categories.magmas import Magmas return [Magmas().Unital()] diff --git a/src/sage/categories/algebra_functor.py b/src/sage/categories/algebra_functor.py index 0d6cb19b0e1..ae5ef793296 100644 --- a/src/sage/categories/algebra_functor.py +++ b/src/sage/categories/algebra_functor.py @@ -128,7 +128,6 @@ Category of semigroup algebras over Rational Field, ... Category of unital magma algebras over Rational Field, - ... Category of magma algebras over Rational Field, ... Category of set algebras over Rational Field, diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index eab8a1ad43d..5658cd5e514 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -102,7 +102,7 @@ def _make_named_class_key(self, name): sage: Bimodules(Fields(), Rings())._make_named_class_key('element_class') (Category of fields, Category of rings) """ - return (self._left_base_ring if isinstance(self._left_base_ring, Category) else self._left_base_ring.category(), + return (self._left_base_ring if isinstance(self._left_base_ring, Category) else self._left_base_ring.category(), self._right_base_ring if isinstance(self._right_base_ring, Category) else self._right_base_ring.category()) @classmethod diff --git a/src/sage/categories/category_cy_helper.pyx b/src/sage/categories/category_cy_helper.pyx index 28600da136f..ca29f3f9042 100644 --- a/src/sage/categories/category_cy_helper.pyx +++ b/src/sage/categories/category_cy_helper.pyx @@ -8,15 +8,15 @@ AUTHOR: - Simon King (initial version) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2014 Simon King # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** ####################################### # Sorting diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index ae38bdd2462..0423ab9d83a 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -16,6 +16,8 @@ from sage.categories.category import Category from sage.structure.category_object cimport CategoryObject from sage.structure.dynamic_class import DynamicMetaclass +# I have no idea why this is necessary, but otherwise the type import fails (maybe because its shadowed by sage's cpython module?) +from cpython.method cimport PyMethod_Check from cpython.type cimport PyType_IsSubtype # This helper class is used to implement Category_singleton.__contains__ @@ -62,11 +64,11 @@ cdef class Category_contains_method_by_parent_class: TESTS: - The following used to segfault in a preliminary version of the - code:: + The following used to segfault in a preliminary version of the + code:: - sage: None in Rings() - False + sage: None in Rings() + False """ if x is None: return False @@ -76,7 +78,7 @@ cdef class Category_contains_method_by_parent_class: return PyType_IsSubtype(((y._category or y.category()).parent_class), self._parent_class_of_category) except AttributeError: return False - except TypeError: # this is for objects that aren't CategoryObjects + except TypeError: # this is for objects that are not CategoryObjects try: return PyType_IsSubtype((x.category().parent_class), self._parent_class_of_category) except AttributeError: @@ -293,7 +295,8 @@ class Category_singleton(Category): sage: Category_singleton() Traceback (most recent call last): ... - AssertionError: is not a direct subclass of + AssertionError: is not a direct subclass of + Instantiating a subclass of a subclass of :class:`Category_singleton` also triggers an assertion error:: diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index d5bd98633a3..766ec9b64cb 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1908,7 +1908,7 @@ def _base_category_class_and_axiom(cls): sage: CommutativeRings()._base_category_class_and_axiom (, 'Commutative') sage: CommutativeRings()._base_category_class_and_axiom_origin - 'deduced by base_category_class_and_axiom' + 'set by __classget__' ``Sets.Infinite`` is a nested class, so the attribute is set by :meth:`CategoryWithAxiom.__classget__` the first time diff --git a/src/sage/categories/coalgebras.py b/src/sage/categories/coalgebras.py index c037ed0c6a1..f8dddc6e398 100644 --- a/src/sage/categories/coalgebras.py +++ b/src/sage/categories/coalgebras.py @@ -49,7 +49,7 @@ def super_categories(self): """ return [Modules(self.base_ring())] - WithBasis = LazyImport('sage.categories.coalgebras_with_basis', 'CoalgebrasWithBasis') + WithBasis = LazyImport('sage.categories.coalgebras_with_basis', 'CoalgebrasWithBasis') Graded = LazyImport('sage.categories.graded_coalgebras', 'GradedCoalgebras') class ParentMethods: diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 9f71cfdcf90..fe663d6eb97 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -2671,7 +2671,7 @@ def bruhat_le(self, other): reduced word for ``other`` contains a reduced word for ``self`` as subword. See Stembridge, A short derivation of the Möbius function for the Bruhat order. J. Algebraic - Combin. 25 (2007), no. 2, 141--148, Proposition 1.1. + Combinatoric 25 (2007), no. 2, 141--148, Proposition 1.1. Complexity: `O(l * c)`, where `l` is the minimum of the lengths of `u` and of `v`, and `c` is the cost of the low diff --git a/src/sage/categories/euclidean_domains.py b/src/sage/categories/euclidean_domains.py index 4c0459c8cdd..93a834c9cfc 100644 --- a/src/sage/categories/euclidean_domains.py +++ b/src/sage/categories/euclidean_domains.py @@ -130,7 +130,7 @@ def refine(a, b): def _test_euclidean_degree(self, **options): r""" - Test that the assumptions on an Euclidean degree are met. + Test that the assumptions on a Euclidean degree are met. EXAMPLES:: @@ -195,7 +195,7 @@ class ElementMethods: @abstract_method def euclidean_degree(self): r""" - Return the degree of this element as an element of an Euclidean + Return the degree of this element as an element of a Euclidean domain, i.e., for elements `a`, `b` the euclidean degree `f` satisfies the usual properties: diff --git a/src/sage/categories/examples/commutative_additive_semigroups.py b/src/sage/categories/examples/commutative_additive_semigroups.py index 78f0eedb2d0..78701a16be3 100644 --- a/src/sage/categories/examples/commutative_additive_semigroups.py +++ b/src/sage/categories/examples/commutative_additive_semigroups.py @@ -166,11 +166,10 @@ def __init__(self, parent, iterable): {'a': 2, 'b': 0, 'c': 1, 'd': 5} """ d = {a: 0 for a in parent.alphabet} - for a, c in iterable: - d[a] = c + d.update(iterable) ElementWrapper.__init__(self, parent, d) - def _repr_(self): + def _repr_(self) -> str: """ EXAMPLES:: @@ -182,8 +181,9 @@ def _repr_(self): 0 """ d = self.value - result = ' + '.join( ("%s*%s" % (d[a],a) if d[a] != 1 else a) for a in sorted(d.keys()) if d[a] != 0) - return '0' if result == '' else result + result = ' + '.join(("%s*%s" % (d[a], a) if d[a] != 1 else a) + for a in sorted(d.keys()) if d[a] != 0) + return '0' if not result else result def __hash__(self): """ diff --git a/src/sage/categories/examples/crystals.py b/src/sage/categories/examples/crystals.py index dedad2c47d5..5d3fcc5ed9f 100644 --- a/src/sage/categories/examples/crystals.py +++ b/src/sage/categories/examples/crystals.py @@ -189,7 +189,8 @@ def __init__(self): self.n = 2 self._cartan_type = CartanType(['A', 2]) self.G = DiGraph(5) - self.G.add_edges([ [0,1,1], [1,2,1], [2,3,1], [3,5,1], [0,4,2], [4,5,2] ]) + self.G.add_edges([[0, 1, 1], [1, 2, 1], [2, 3, 1], + [3, 5, 1], [0, 4, 2], [4, 5, 2]]) self.module_generators = [self(0)] def __repr__(self): diff --git a/src/sage/categories/examples/meson.build b/src/sage/categories/examples/meson.build new file mode 100644 index 00000000000..ecb63c913ba --- /dev/null +++ b/src/sage/categories/examples/meson.build @@ -0,0 +1,49 @@ +py.install_sources( + 'algebras_with_basis.py', + 'all.py', + 'commutative_additive_monoids.py', + 'commutative_additive_semigroups.py', + 'coxeter_groups.py', + 'crystals.py', + 'cw_complexes.py', + 'facade_sets.py', + 'filtered_algebras_with_basis.py', + 'filtered_modules_with_basis.py', + 'finite_coxeter_groups.py', + 'finite_dimensional_algebras_with_basis.py', + 'finite_dimensional_lie_algebras_with_basis.py', + 'finite_enumerated_sets.py', + 'finite_monoids.py', + 'finite_semigroups.py', + 'finite_weyl_groups.py', + 'graded_connected_hopf_algebras_with_basis.py', + 'graded_modules_with_basis.py', + 'graphs.py', + 'hopf_algebras_with_basis.py', + 'infinite_enumerated_sets.py', + 'lie_algebras.py', + 'lie_algebras_with_basis.py', + 'magmas.py', + 'manifolds.py', + 'monoids.py', + 'posets.py', + 'semigroups.py', + 'sets_cat.py', + 'sets_with_grading.py', + 'with_realizations.py', + subdir: 'sage/categories/examples', +) + +extension_data = {'semigroups_cython' : files('semigroups_cython.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/categories/examples', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 33a86e33244..e1f5c3d68f6 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -193,7 +193,7 @@ def _call_(self, x): class ParentMethods: - def is_field( self, proof=True ): + def is_field(self, proof=True): r""" Return ``True`` as ``self`` is a field. @@ -458,6 +458,38 @@ def fraction_field(self): """ return self + def ideal(self, *gens, **kwds): + """ + Return the ideal generated by ``gens``. + + INPUT: + + - an element or a list/tuple/sequence of elements, the generators + + Any named arguments are ignored. + + EXAMPLES:: + + sage: QQ.ideal(2) + Principal ideal (1) of Rational Field + sage: QQ.ideal(0) + Principal ideal (0) of Rational Field + + TESTS:: + + sage: QQ.ideal(2, 4) + Principal ideal (1) of Rational Field + + sage: QQ.ideal([2, 4]) + Principal ideal (1) of Rational Field + """ + if len(gens) == 1 and isinstance(gens[0], (list, tuple)): + gens = gens[0] + for x in gens: + if not self(x).is_zero(): + return self.unit_ideal() + return self.zero_ideal() + def _squarefree_decomposition_univariate_polynomial(self, f): r""" Return the square-free decomposition of ``f`` over this field. @@ -550,7 +582,7 @@ def vector_space(self, *args, **kwds): class ElementMethods: def euclidean_degree(self): r""" - Return the degree of this element as an element of an Euclidean + Return the degree of this element as an element of a Euclidean domain. In a field, this returns 0 for all but the zero element (for @@ -585,7 +617,7 @@ def quo_rem(self, other): raise ZeroDivisionError return (self/other, self.parent().zero()) - def is_unit( self ): + def is_unit(self): r""" Return ``True`` if ``self`` has a multiplicative inverse. @@ -602,7 +634,7 @@ def is_unit( self ): # Of course, in general gcd and lcm in a field are not very interesting. # However, they should be implemented! @coerce_binop - def gcd(self,other): + def gcd(self, other): """ Greatest common divisor. diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 454f31e5070..256203abc3f 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -1197,7 +1197,6 @@ def hilbert_series(self, prec=None): from collections import defaultdict from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = self.base_ring() PR = PolynomialRing(ZZ, 't') dims = defaultdict(ZZ) for b in self.basis(): diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 0ed2e0b5994..2df5d054dd0 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -274,7 +274,7 @@ def bhz_poset(self): was defined in [BHZ2005]_. This partial order is not a lattice, as there is no unique - maximal element. It can be succintly defined as follows. + maximal element. It can be succinctly defined as follows. Let `u` and `v` be two elements of the Coxeter group `W`. Let `S(u)` be the support of `u`. Then `u \leq v` if and only diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 03ae127de8f..187fe3d675a 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -145,6 +145,7 @@ def radical_basis(self): We compute the radical basis in a subalgebra using the inherited product:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -155,14 +156,14 @@ def radical_basis(self): TESTS:: - sage: A = KleinFourGroup().algebra(GF(2)) # needs sage.groups sage.modules - sage: A.radical_basis() # needs sage.groups sage.modules + sage: # needs sage.groups sage.modules + sage: A = KleinFourGroup().algebra(GF(2)) + sage: A.radical_basis() (() + (1,2)(3,4), (3,4) + (1,2)(3,4), (1,2) + (1,2)(3,4)) - - sage: A = KleinFourGroup().algebra(QQ, category=Monoids()) # needs sage.groups sage.modules - sage: A.radical_basis.__module__ # needs sage.groups sage.modules + sage: A = KleinFourGroup().algebra(QQ, category=Monoids()) + sage: A.radical_basis.__module__ 'sage.categories.finite_dimensional_algebras_with_basis' - sage: A.radical_basis() # needs sage.groups sage.modules + sage: A.radical_basis() () """ F = self.base_ring() @@ -206,7 +207,6 @@ def root_fcn(s, x): s = 1 n = self.dimension() B = [b.on_left_matrix() for b in self.basis()] - I = B[0].parent().one() while s <= n: # we use that p_{AB}(x) = p_{BA}(x) here data = [[None]*(len(B)+1) for _ in B] @@ -421,6 +421,7 @@ def subalgebra(self, gens, category=None, *args, **opts): EXAMPLES:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -429,6 +430,7 @@ def subalgebra(self, gens, category=None, *args, **opts): sage: A.dimension() 7 + sage: # needs sage.modules sage: L. = LieAlgebra(GF(3), {('x','z'): {'x':1, 'y':1}, ('y','z'): {'y':1}}) sage: MS = MatrixSpace(L.base_ring(), L.dimension()) sage: gens = [b.adjoint_matrix() for b in L.basis()] @@ -473,6 +475,7 @@ def ideal_submodule(self, gens, side='left', category=None, *args, **opts): EXAMPLES:: + sage: # needs sage.modules sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1}, ....: ('c','d'): {'a':1}, ('c','e'): {'c':1}} sage: L. = LieAlgebra(QQ, scoeffs) @@ -1537,18 +1540,21 @@ def simple_module_parameterization(self): EXAMPLES:: + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(5, 30, QQ) # semisimple sage: len(TL.radical_basis()) 0 sage: TL.simple_module_parameterization() (1, 3, 5) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(5, 1, QQ) # not semisimple sage: len(TL.radical_basis()) 24 sage: TL.simple_module_parameterization() (1, 3, 5) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(6, 30, QQ) # semisimple sage: all(TL.cell_module(la).dimension() ....: == TL.cell_module(la).simple_module().dimension() @@ -1557,6 +1563,7 @@ def simple_module_parameterization(self): sage: TL.simple_module_parameterization() (0, 2, 4, 6) + sage: # needs sage.modules sage: TL = TemperleyLiebAlgebra(6, 0, QQ) # not semisimple sage: TL.simple_module_parameterization() (2, 4, 6) diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 270ea045898..4e5212885ce 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -605,13 +605,13 @@ def normalizer_basis(self, S): sc[k[1], k[0]] = -v X = self.basis().keys() d = len(X) - ret = [] t = m.nrows() c_mat = matrix(self.base_ring(), - [[sum(m[i,j] * sc[x,xp][k] for j, xp in enumerate(X) - if (x, xp) in sc) + [[sum(m[i, j] * sc[x, xp][k] + for j, xp in enumerate(X) if (x, xp) in sc) for x in X] - + [0]*(i*t) + [-m[j,k] for j in range(t)] + [0]*((t-i-1)*t) + + [0]*(i*t) + [-m[j, k] for j in range(t)] + + [0]*((t-i-1)*t) for i in range(t) for k in range(d)]) C = c_mat.right_kernel().basis_matrix() return [self.from_vector(c[:d]) for c in C] @@ -2430,7 +2430,7 @@ def faithful_representation(self, algorithm=None): General case - * ``'generic'`` -- generic algortihm (only implemented currently + * ``'generic'`` -- generic algorithm (only implemented currently for positive characteristic) Note that the algorithm for any more generic cases can be used diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 04eff9e9205..37369269cf2 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -1479,8 +1479,7 @@ def rowmotion_orbits_plots(self): max_orbit_size = 0 for orb in self.rowmotion_orbits(): orb_plots = [] - if len(orb) > max_orbit_size: - max_orbit_size = len(orb) + max_orbit_size = max(len(orb), max_orbit_size) for oi in orb: oiplot = self.order_ideal_plot(oi) orb_plots.append(oiplot) @@ -1565,8 +1564,7 @@ def toggling_orbits_plots(self, vs): max_orbit_size = 0 for orb in self.toggling_orbits(vs): orb_plots = [] - if len(orb) > max_orbit_size: - max_orbit_size = len(orb) + max_orbit_size = max(len(orb), max_orbit_size) for oi in orb: oiplot = self.order_ideal_plot(oi) orb_plots.append(oiplot) diff --git a/src/sage/categories/map.pxd b/src/sage/categories/map.pxd index 6f3b3bb8a91..8738d4cf042 100644 --- a/src/sage/categories/map.pxd +++ b/src/sage/categories/map.pxd @@ -16,9 +16,9 @@ cdef class Map(Element): cpdef Element _call_(self, x) cpdef Element _call_with_args(self, x, args=*, kwds=*) - cdef public domain # will be either a weakref or a constant map - cdef public codomain # will be a constant map - cdef Parent _codomain # for accessing the codomain directly + cdef public domain # will be either a weakref or a constant map + cdef public codomain # will be a constant map + cdef Parent _codomain # for accessing the codomain directly cdef object _category_for # category in which this is a morphism cdef public _repr_type_str diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index 3ee42fc0157..43e476847d0 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -183,7 +183,7 @@ cdef class Map(Element): cdef Map out = Element.__copy__(self) # Element.__copy__ updates the __dict__, but not the slots. # Let's do this now, but with strong references. - out._parent = self.parent() # self._parent might be None + out._parent = self.parent() # self._parent might be None out._update_slots(self._extra_slots()) return out @@ -462,10 +462,10 @@ cdef class Map(Element): Return a dict with attributes to pickle and copy this map. """ return dict( - _domain=self.domain(), - _codomain=self._codomain, - _is_coercion=self._is_coercion, - _repr_type_str=self._repr_type_str) + _domain=self.domain(), + _codomain=self._codomain, + _is_coercion=self._is_coercion, + _repr_type_str=self._repr_type_str) def _extra_slots_test(self): """ @@ -655,9 +655,11 @@ cdef class Map(Element): sage: f = R.hom([x+y, x-y], R) sage: f.category_for() Join of Category of unique factorization domains - and Category of commutative algebras - over (number fields and quotient fields and metric spaces) - and Category of infinite sets + and Category of algebras with basis over + (number fields and quotient fields and metric spaces) + and Category of commutative algebras over + (number fields and quotient fields and metric spaces) + and Category of infinite sets sage: f.category() Category of endsets of unital magmas and right modules over (number fields and quotient fields and metric spaces) @@ -801,7 +803,7 @@ cdef class Map(Element): """ P = parent(x) cdef Parent D = self.domain() - if P is D: # we certainly want to call _call_/with_args + if P is D: # we certainly want to call _call_/with_args if not args and not kwds: return self._call_(x) return self._call_with_args(x, args, kwds) @@ -1215,7 +1217,7 @@ cdef class Map(Element): def is_surjective(self): """ - Tells whether the map is surjective (not implemented in the base class). + Tell whether the map is surjective (not implemented in the base class). TESTS:: diff --git a/src/sage/categories/meson.build b/src/sage/categories/meson.build new file mode 100644 index 00000000000..132037fe7fd --- /dev/null +++ b/src/sage/categories/meson.build @@ -0,0 +1,224 @@ +py.install_sources( + 'action.pxd', + 'additive_groups.py', + 'additive_magmas.py', + 'additive_monoids.py', + 'additive_semigroups.py', + 'affine_weyl_groups.py', + 'algebra_functor.py', + 'algebra_ideals.py', + 'algebra_modules.py', + 'algebras.py', + 'algebras_with_basis.py', + 'all.py', + 'all__sagemath_objects.py', + 'aperiodic_semigroups.py', + 'associative_algebras.py', + 'basic.py', + 'bialgebras.py', + 'bialgebras_with_basis.py', + 'bimodules.py', + 'cartesian_product.py', + 'category.py', + 'category_cy_helper.pxd', + 'category_singleton.pxd', + 'category_types.py', + 'category_with_axiom.py', + 'chain_complexes.py', + 'classical_crystals.py', + 'coalgebras.py', + 'coalgebras_with_basis.py', + 'commutative_additive_groups.py', + 'commutative_additive_monoids.py', + 'commutative_additive_semigroups.py', + 'commutative_algebra_ideals.py', + 'commutative_algebras.py', + 'commutative_ring_ideals.py', + 'commutative_rings.py', + 'complete_discrete_valuation.py', + 'complex_reflection_groups.py', + 'complex_reflection_or_generalized_coxeter_groups.py', + 'covariant_functorial_construction.py', + 'coxeter_group_algebras.py', + 'coxeter_groups.py', + 'crystals.py', + 'cw_complexes.py', + 'dedekind_domains.py', + 'discrete_valuation.py', + 'distributive_magmas_and_additive_magmas.py', + 'division_rings.py', + 'domains.py', + 'drinfeld_modules.py', + 'dual.py', + 'enumerated_sets.py', + 'euclidean_domains.py', + 'facade_sets.py', + 'fields.py', + 'filtered_algebras.py', + 'filtered_algebras_with_basis.py', + 'filtered_hopf_algebras_with_basis.py', + 'filtered_modules.py', + 'filtered_modules_with_basis.py', + 'finite_complex_reflection_groups.py', + 'finite_coxeter_groups.py', + 'finite_crystals.py', + 'finite_dimensional_algebras_with_basis.py', + 'finite_dimensional_bialgebras_with_basis.py', + 'finite_dimensional_coalgebras_with_basis.py', + 'finite_dimensional_graded_lie_algebras_with_basis.py', + 'finite_dimensional_hopf_algebras_with_basis.py', + 'finite_dimensional_lie_algebras_with_basis.py', + 'finite_dimensional_modules_with_basis.py', + 'finite_dimensional_nilpotent_lie_algebras_with_basis.py', + 'finite_dimensional_semisimple_algebras_with_basis.py', + 'finite_enumerated_sets.py', + 'finite_fields.py', + 'finite_groups.py', + 'finite_lattice_posets.py', + 'finite_monoids.py', + 'finite_permutation_groups.py', + 'finite_posets.py', + 'finite_semigroups.py', + 'finite_sets.py', + 'finite_weyl_groups.py', + 'finitely_generated_lambda_bracket_algebras.py', + 'finitely_generated_lie_conformal_algebras.py', + 'finitely_generated_magmas.py', + 'finitely_generated_semigroups.py', + 'function_fields.py', + 'functor.pxd', + 'g_sets.py', + 'gcd_domains.py', + 'generalized_coxeter_groups.py', + 'graded_algebras.py', + 'graded_algebras_with_basis.py', + 'graded_bialgebras.py', + 'graded_bialgebras_with_basis.py', + 'graded_coalgebras.py', + 'graded_coalgebras_with_basis.py', + 'graded_hopf_algebras.py', + 'graded_hopf_algebras_with_basis.py', + 'graded_lie_algebras.py', + 'graded_lie_algebras_with_basis.py', + 'graded_lie_conformal_algebras.py', + 'graded_modules.py', + 'graded_modules_with_basis.py', + 'graphs.py', + 'group_algebras.py', + 'groupoid.py', + 'groups.py', + 'h_trivial_semigroups.py', + 'hecke_modules.py', + 'highest_weight_crystals.py', + 'homset.py', + 'homsets.py', + 'hopf_algebras.py', + 'hopf_algebras_with_basis.py', + 'infinite_enumerated_sets.py', + 'integral_domains.py', + 'isomorphic_objects.py', + 'j_trivial_semigroups.py', + 'kac_moody_algebras.py', + 'l_trivial_semigroups.py', + 'lambda_bracket_algebras.py', + 'lambda_bracket_algebras_with_basis.py', + 'lattice_posets.py', + 'left_modules.py', + 'lie_algebras.py', + 'lie_algebras_with_basis.py', + 'lie_conformal_algebras.py', + 'lie_conformal_algebras_with_basis.py', + 'lie_groups.py', + 'loop_crystals.py', + 'magmas.py', + 'magmas_and_additive_magmas.py', + 'magmatic_algebras.py', + 'manifolds.py', + 'map.pxd', + 'matrix_algebras.py', + 'metric_spaces.py', + 'modular_abelian_varieties.py', + 'modules.py', + 'modules_with_basis.py', + 'monoid_algebras.py', + 'monoids.py', + 'morphism.pxd', + 'noetherian_rings.py', + 'number_fields.py', + 'objects.py', + 'partially_ordered_monoids.py', + 'permutation_groups.py', + 'pointed_sets.py', + 'polyhedra.py', + 'poor_man_map.py', + 'posets.py', + 'primer.py', + 'principal_ideal_domains.py', + 'pushout.py', + 'quantum_group_representations.py', + 'quotient_fields.py', + 'quotients.py', + 'r_trivial_semigroups.py', + 'realizations.py', + 'regular_crystals.py', + 'regular_supercrystals.py', + 'right_modules.py', + 'ring_ideals.py', + 'rings.py', + 'rngs.py', + 'schemes.py', + 'semigroups.py', + 'semirings.py', + 'semisimple_algebras.py', + 'sets_cat.py', + 'sets_with_grading.py', + 'sets_with_partial_maps.py', + 'shephard_groups.py', + 'signed_tensor.py', + 'simplicial_complexes.py', + 'simplicial_sets.py', + 'subobjects.py', + 'subquotients.py', + 'super_algebras.py', + 'super_algebras_with_basis.py', + 'super_hopf_algebras_with_basis.py', + 'super_lie_conformal_algebras.py', + 'super_modules.py', + 'super_modules_with_basis.py', + 'supercommutative_algebras.py', + 'supercrystals.py', + 'tensor.py', + 'topological_spaces.py', + 'triangular_kac_moody_algebras.py', + 'tutorial.py', + 'unique_factorization_domains.py', + 'unital_algebras.py', + 'vector_bundles.py', + 'vector_spaces.py', + 'weyl_groups.py', + 'with_realizations.py', + subdir: 'sage/categories', +) + +extension_data = { + 'action' : files('action.pyx'), + 'category_cy_helper' : files('category_cy_helper.pyx'), + 'category_singleton' : files('category_singleton.pyx'), + 'coercion_methods' : files('coercion_methods.pyx'), + 'functor' : files('functor.pyx'), + 'map' : files('map.pyx'), + 'morphism' : files('morphism.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/categories', + install: true, + include_directories: [inc_cpython, inc_ext], + dependencies: [py_dep, gmp], + ) +endforeach + +subdir('examples') diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index d6d63b4df0a..f9bc036910f 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -893,7 +893,7 @@ def __init_extra__(self): (Vector space of dimension 2 over Rational Field, Univariate Polynomial Ring in x over Rational Field) sage: A.category() # needs sage.modules - Category of Cartesian products of vector spaces + Category of Cartesian products of vector spaces with basis over (number fields and quotient fields and metric spaces) sage: A.base_ring() # needs sage.modules Rational Field diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 2ed9514a469..34bcd1ca5f5 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -798,7 +798,7 @@ cdef class SetIsomorphism(SetMorphism): raise RuntimeError('inverse morphism has not been set') return self._inverse - cdef dict _extra_slots(self) noexcept: + cdef dict _extra_slots(self): """ Extend the dictionary with extra slots for this class. @@ -823,7 +823,7 @@ cdef class SetIsomorphism(SetMorphism): slots['_inverse'] = self._inverse return slots - cdef _update_slots(self, dict _slots) noexcept: + cdef _update_slots(self, dict _slots): """ Update the slots of ``self`` from the data in the dictionary. diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 6afdc8f31d9..dbae2b41049 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -58,7 +58,7 @@ - Thousands of different kinds of objects (classes): Integers, polynomials, matrices, groups, number fields, elliptic - curves, permutations, morphisms, languages, ... and a few racoons ... + curves, permutations, morphisms, languages, ... and a few raccoons ... - Tens of thousands methods and functions: @@ -1277,7 +1277,7 @@ class naming and introspection. Sage currently works around the The meaning of each axiom is described in the documentation of the corresponding method, which can be obtained as usual by -instrospection:: +introspection:: sage: C = Groups() sage: C.Finite? # not tested diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 0b5f0a85cdc..b67f37ed524 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -535,7 +535,7 @@ def __ne__(self, other): def __mul__(self, other): """ - Compose construction functors to a composit construction functor, unless one of them is the identity. + Compose construction functors to a composite construction functor, unless one of them is the identity. .. NOTE:: @@ -688,7 +688,7 @@ def __ne__(self, other): def __mul__(self, other): """ - Compose construction functors to a composit construction functor, unless one of them is the identity. + Compose construction functors to a composite construction functor, unless one of them is the identity. .. NOTE:: @@ -1420,10 +1420,10 @@ def __mul__(self, other): if x.count('_') == 1: g, n = x.split('_') if n.isdigit(): - if g.isalnum(): # we can interprete x in any InfinitePolynomialRing - if g in self._gens: # we can interprete x in self, hence, we will not use it as a variable anymore. + if g.isalnum(): # we can interpret x in any InfinitePolynomialRing + if g in self._gens: # we can interpret x in self, hence, we will not use it as a variable anymore. RemainingVars.pop(RemainingVars.index(x)) - IsOverlap = True # some variables of other can be interpreted in self. + IsOverlap = True # some variables of other can be interpreted in self. if OverlappingVars: # Is OverlappingVars in the right order? g0, n0 = OverlappingVars[-1].split('_') @@ -3055,13 +3055,11 @@ def merge(self, other): codomain = self.codomain().join([self.codomain(), other.codomain()]) # Get the optional arguments: as_field = self.as_field or other.as_field - kwds = {} - for k,v in self.kwds.items(): - kwds[k] = v - for k,v in other.kwds.items(): + kwds = dict(self.kwds) + for k, v in other.kwds.items(): if k == 'category': if kwds[k] is not None: - kwds[k] = v.join([v,kwds[k]]) + kwds[k] = v.join([v, kwds[k]]) else: kwds[k] = v continue diff --git a/src/sage/categories/realizations.py b/src/sage/categories/realizations.py index 762bb277e51..26d5125f9e7 100644 --- a/src/sage/categories/realizations.py +++ b/src/sage/categories/realizations.py @@ -27,7 +27,7 @@ class RealizationsCategory(RegressiveCovariantConstructionCategory): """ An abstract base class for all categories of realizations category. - Relization are implemented as + Realization are implemented as :class:`~sage.categories.covariant_functorial_construction.RegressiveCovariantConstructionCategory`. See there for the documentation of how the various bindings such as ``Sets().Realizations()`` and ``P.Realizations()``, where ``P`` diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 781f1a1462b..8db71edfb9c 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -12,12 +12,14 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** from functools import reduce +from types import GeneratorType from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.rngs import Rngs from sage.structure.element import Element +from sage.structure.parent import Parent class Rings(CategoryWithAxiom): @@ -434,6 +436,30 @@ def is_noetherian(self): """ return False + def is_prime_field(self): + r""" + Return ``True`` if this ring is one of the prime fields `\QQ` or + `\GF{p}`. + + EXAMPLES:: + + sage: QQ.is_prime_field() + True + sage: GF(3).is_prime_field() + True + sage: GF(9, 'a').is_prime_field() # needs sage.rings.finite_rings + False + sage: ZZ.is_prime_field() + False + sage: QQ['x'].is_prime_field() + False + sage: Qp(19).is_prime_field() # needs sage.rings.padics + False + """ + # the case of QQ is handled by QQ itself + from sage.categories.finite_fields import FiniteFields + return self in FiniteFields() and self.degree() == 1 + def is_zero(self) -> bool: """ Return ``True`` if this is the zero ring. @@ -457,6 +483,54 @@ def is_zero(self) -> bool: """ return self.one() == self.zero() + def is_subring(self, other): + """ + Return ``True`` if the canonical map from ``self`` to ``other`` is + injective. + + This raises a :exc:`NotImplementedError` if not known. + + EXAMPLES:: + + sage: ZZ.is_subring(QQ) + True + sage: ZZ.is_subring(GF(19)) + False + + TESTS:: + + sage: QQ.is_subring(QQ['x']) + True + sage: QQ.is_subring(GF(7)) + False + sage: QQ.is_subring(CyclotomicField(7)) # needs sage.rings.number_field + True + sage: QQ.is_subring(ZZ) + False + + Every ring is a subring of itself, :issue:`17287`:: + + sage: QQbar.is_subring(QQbar) # needs sage.rings.number_field + True + sage: RR.is_subring(RR) + True + sage: CC.is_subring(CC) # needs sage.rings.real_mpfr + True + sage: x = polygen(ZZ, 'x') + sage: K. = NumberField(x^3 - x + 1/10) # needs sage.rings.number_field + sage: K.is_subring(K) # needs sage.rings.number_field + True + sage: R. = RR[] + sage: R.is_subring(R) + True + """ + if self is other: + return True + try: + return self.Hom(other).natural_map().is_injective() + except (TypeError, AttributeError): + return False + def bracket(self, x, y): """ Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`. @@ -815,32 +889,34 @@ def ideal(self, *args, **kwds): """ Create an ideal of this ring. - .. NOTE:: + INPUT: - The code is copied from the base class - :class:`~sage.rings.ring.Ring`. This is - because there are rings that do not inherit - from that class, such as matrix algebras. - See :issue:`7797`. + - an element or a list/tuple/sequence of elements, the generators - INPUT: + - ``coerce`` -- boolean (default: ``True``); whether to first coerce + the elements into this ring. This must be a keyword + argument. Only set it to ``False`` if you are certain that each + generator is already in the ring. - - an element or a list/tuple/sequence of elements - - ``coerce`` -- boolean (default: ``True``); - first coerce the elements into this ring - - ``side`` -- (optional) string, one of ``'twosided'`` - (default), ``'left'``, ``'right'``; determines - whether the resulting ideal is twosided, a left - ideal or a right ideal + - ``ideal_class`` -- callable (default: ``self._ideal_class_()``); + this must be a keyword argument. A constructor for ideals, taking + the ring as the first argument and then the generators. + Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or + :class:`~sage.rings.noncommutative_ideals.Ideal_nc`. - EXAMPLES:: + - Further named arguments (such as ``side`` in the case of + non-commutative rings) are forwarded to the ideal class. + + The keyword ``side`` can be one of ``'twosided'``, + ``'left'``, ``'right'``. It determines whether + the resulting ideal is twosided, a left ideal or a right ideal. + + EXAMPLES: + + Matrix rings:: sage: # needs sage.modules sage: MS = MatrixSpace(QQ, 2, 2) - sage: isinstance(MS, Ring) - False - sage: MS in Rings() - True sage: MS.ideal(2) Twosided Ideal ( @@ -858,6 +934,36 @@ def ideal(self, *args, **kwds): [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field + + Polynomial rings:: + + sage: R. = QQ[] + sage: R.ideal(x,y) + Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field + sage: R.ideal(x+y^2) + Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field + sage: R.ideal( [x^3,y^3+x^3] ) + Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field + + Non-commutative rings:: + + sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules + sage: A.ideal(A.1, A.2^2) # needs sage.combinat sage.modules + Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis + sage: A.ideal(A.1, A.2^2, side='left') # needs sage.combinat sage.modules + Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis + + TESTS: + + Make sure that :issue:`11139` is fixed:: + + sage: R. = QQ[] + sage: R.ideal([]) + Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field + sage: R.ideal(()) + Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field + sage: R.ideal() + Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field """ if 'coerce' in kwds: coerce = kwds['coerce'] @@ -866,8 +972,7 @@ def ideal(self, *args, **kwds): coerce = True from sage.rings.ideal import Ideal_generic - from types import GeneratorType - if len(args) == 0: + if not args: gens = [self(0)] else: gens = args @@ -889,26 +994,23 @@ def ideal(self, *args, **kwds): elif isinstance(first, (list, tuple, GeneratorType)): gens = first else: - try: - if self.has_coerce_map_from(first): - gens = first.gens() # we have a ring as argument - elif isinstance(first, Element): - gens = [first] - else: - raise ArithmeticError("there is no coercion from %s to %s" % (first, self)) - except TypeError: # first may be a ring element - pass break - if coerce: + + if not gens: + gens = [self.zero()] + elif coerce: gens = [self(g) for g in gens] + from sage.categories.principal_ideal_domains import PrincipalIdealDomains if self in PrincipalIdealDomains(): # Use GCD algorithm to obtain a principal ideal g = gens[0] if len(gens) == 1: try: - g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. - except (AttributeError, NotImplementedError): + # note: we set g = gcd(g, g) to "canonicalize" the generator: + # make polynomials monic, etc. + g = g.gcd(g) + except (AttributeError, NotImplementedError, IndexError): pass else: for h in gens[1:]: diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 7114761aaac..1635296450b 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -511,6 +511,7 @@ def representation(self, module, on_basis, side='left', *args, **kwargs): EXAMPLES:: + sage: # needs sage.groups sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: def on_basis(g, m): # cyclically permute generators @@ -1059,6 +1060,7 @@ def representation(self, module, on_basis, side='left', *args, **kwargs): EXAMPLES:: + sage: # needs sage.groups sage: G = groups.permutation.Dihedral(5) sage: CFM = CombinatorialFreeModule(GF(2), [1, 2, 3, 4, 5]) sage: A = G.algebra(GF(2)) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 2d58d1c6514..9d63ac2fa30 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2802,7 +2802,7 @@ def _register_realization(self, realization): sage: R in A.realizations() # indirect doctest True - Note: the test above uses ``QQ[x]`` to not interfer + Note: the test above uses ``QQ[x]`` to not interfere with other tests. """ assert realization.realization_of() is self diff --git a/src/sage/categories/simplicial_sets.py b/src/sage/categories/simplicial_sets.py index aabd3cdbb4f..76030be831b 100644 --- a/src/sage/categories/simplicial_sets.py +++ b/src/sage/categories/simplicial_sets.py @@ -407,13 +407,13 @@ def universal_cover_map(self): EXAMPLES:: sage: RP2 = simplicial_sets.RealProjectiveSpace(2) # needs sage.graphs sage.groups - sage: phi = RP2.universal_cover_map(); phi # needs sage.graphs sage.groups + sage: phi = RP2.universal_cover_map(); phi # needs sage.graphs sage.groups gap_package_polenta Simplicial set morphism: From: Simplicial set with 6 non-degenerate simplices To: RP^2 Defn: [(1, 1), (1, e), (f, 1), (f, e), (f * f, 1), (f * f, e)] --> [1, 1, f, f, f * f, f * f] - sage: phi.domain().face_data() # needs sage.graphs sage.groups + sage: phi.domain().face_data() # needs sage.graphs sage.groups gap_package_polenta {(1, 1): None, (1, e): None, (f, 1): ((1, e), (1, 1)), @@ -484,7 +484,7 @@ def covering_map(self, character): char[s] = G.one() for d in range(1, self.dimension() + 1): - for s in self.n_cells(d): + for s in self.all_n_simplices(d): if s not in char.keys(): if d == 1 and s.is_degenerate(): char[s] = G.one() @@ -499,7 +499,6 @@ def covering_map(self, character): for g in G: cell = AbstractSimplex(d, name="({}, {})".format(s, g)) cells_dict[(s, g)] = cell - fd = [] faces = self.faces(s) f0 = faces[0] for h in G: @@ -564,7 +563,7 @@ def universal_cover(self): sage: RP3 = simplicial_sets.RealProjectiveSpace(3) sage: C = RP3.universal_cover(); C Simplicial set with 8 non-degenerate simplices - sage: C.face_data() + sage: C.face_data() # needs gap_package_polenta {(1, 1): None, (1, e): None, (f, 1): ((1, e), (1, 1)), @@ -575,6 +574,16 @@ def universal_cover(self): (f * f * f, e): ((f * f, 1), s_0 (f, e), s_1 (f, e), (f * f, e))} sage: C.fundamental_group() Finitely presented group < | > + + TESTS:: + + sage: RP2 = simplicial_sets.RealProjectiveSpace(2) + sage: S3 = simplicial_sets.Sphere(3) + sage: X = S3.wedge(RP2) + sage: XU = X.universal_cover() + sage: [XU.homology(i) for i in range(5)] + [0, 0, Z, Z x Z, 0] + """ return self.universal_cover_map().domain() @@ -591,9 +600,9 @@ def _canonical_twisting_operator(self): sage: X = simplicial_sets.Torus() sage: d = X._canonical_twisting_operator() sage: d - {(s_0 v_0, sigma_1): f3, (sigma_1, s_0 v_0): f2*f3^-1, (sigma_1, sigma_1): f2} + {(s_0 v_0, sigma_1): f2, (sigma_1, s_0 v_0): f1*f2^-1, (sigma_1, sigma_1): f1} sage: list(d.values())[0].parent() - Multivariate Laurent Polynomial Ring in f2, f3 over Integer Ring + Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring sage: Y = simplicial_sets.RealProjectiveSpace(2) sage: d2 = Y._canonical_twisting_operator() sage: d2 @@ -602,7 +611,6 @@ def _canonical_twisting_operator(self): Quotient of Univariate Laurent Polynomial Ring in F1 over Integer Ring by the ideal (-1 + F1^2) """ G, d = self._universal_cover_dict() - phi = G.abelianization_map() abelG, R, I, images = G.abelianization_to_algebra(ZZ) QRP = R.quotient_ring(I) res = {} @@ -674,10 +682,10 @@ def twisted_chain_complex(self, twisting_operator=None, dimensions=None, augment sage: X = simplicial_sets.Torus() sage: C = X.twisted_chain_complex() sage: C.differential(1) - [ f3 - 1 f2*f3^-1 - 1 f2 - 1] + [ f2 - 1 f1*f2^-1 - 1 f1 - 1] sage: C.differential(2) - [ 1 f2*f3^-1] - [ f3 1] + [ 1 f1*f2^-1] + [ f2 1] [ -1 -1] sage: C.differential(3) [] @@ -844,29 +852,29 @@ def twisted_homology(self, n, reduced=False): sage: # needs sage.graphs sage: Y = simplicial_sets.Torus() sage: Y.twisted_homology(1) - Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f1, f1inv, f2, f2inv over Integer Ring Generated by the rows of the matrix: [ 1 0 0 0 0] [ 0 1 0 0 0] [ 0 0 1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 1] + [f1*f1inv - 1 0 0 0 0] + [ 0 f1*f1inv - 1 0 0 0] + [ 0 0 f1*f1inv - 1 0 0] + [ 0 0 0 f1*f1inv - 1 0] + [ 0 0 0 0 f1*f1inv - 1] [f2*f2inv - 1 0 0 0 0] [ 0 f2*f2inv - 1 0 0 0] [ 0 0 f2*f2inv - 1 0 0] [ 0 0 0 f2*f2inv - 1 0] [ 0 0 0 0 f2*f2inv - 1] - [f3*f3inv - 1 0 0 0 0] - [ 0 f3*f3inv - 1 0 0 0] - [ 0 0 f3*f3inv - 1 0 0] - [ 0 0 0 f3*f3inv - 1 0] - [ 0 0 0 0 f3*f3inv - 1] sage: Y.twisted_homology(2) - Quotient module by Submodule of Ambient free module of rank 0 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Quotient module by Submodule of Ambient free module of rank 0 over the integral domain Multivariate Polynomial Ring in f1, f1inv, f2, f2inv over Integer Ring Generated by the rows of the matrix: [] sage: Y.twisted_homology(1, reduced=True) - Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f1, f1inv, f2, f2inv over Integer Ring Generated by the rows of the matrix: [1 0 0 0 0] [0 1 0 0 0] @@ -898,7 +906,6 @@ def twisted_homology(self, n, reduced=False): singred = singular_function("reduce") singlift = singular_function("lift") G, d = self._universal_cover_dict() - phi = G.abelianization_map() abelG, R, I, images = G.abelianization_to_algebra(ZZ) CC = self.twisted_chain_complex() M1 = CC.differential(n).T diff --git a/src/sage/categories/triangular_kac_moody_algebras.py b/src/sage/categories/triangular_kac_moody_algebras.py index 19ff3846b67..26b94d34b9d 100644 --- a/src/sage/categories/triangular_kac_moody_algebras.py +++ b/src/sage/categories/triangular_kac_moody_algebras.py @@ -215,6 +215,7 @@ def _triangular_key(self, x): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: L = lie_algebras.sl(QQ, 3) sage: La = L.cartan_type().root_system().weight_lattice().fundamental_weights() sage: sorted(L.basis().keys(), key=L._basis_key) @@ -352,6 +353,7 @@ def _transpose_basis_mapping(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['A', 2]) sage: g._transpose_basis_mapping {-alpha[1]: alpha[1], @@ -363,7 +365,6 @@ def _transpose_basis_mapping(self): alphacheck[1]: alphacheck[1], alphacheck[2]: alphacheck[2]} """ - Q = self.cartan_type().root_system().root_lattice() K = self.basis().keys() deg_map = {} ret = {} @@ -384,6 +385,7 @@ def _transpose_on_basis(self, m): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['B', 2]) sage: B = g.basis() sage: [(B[k], g._transpose_on_basis(k)) for k in B.keys()] @@ -407,6 +409,7 @@ def transpose(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['B', 2]) sage: g.transpose Generic endomorphism of Lie algebra of ['B', 2] in the Chevalley basis @@ -425,6 +428,7 @@ def transpose(self): EXAMPLES:: + sage: # needs sage.combinat sage.modules sage: g = LieAlgebra(QQ, cartan_type=['G', 2]) sage: for b in g.basis(): ....: for bp in g.basis(): diff --git a/src/sage/coding/bch_code.py b/src/sage/coding/bch_code.py index aeecdc01318..ebd9d7bc3b4 100644 --- a/src/sage/coding/bch_code.py +++ b/src/sage/coding/bch_code.py @@ -35,6 +35,7 @@ from .grs_code import GeneralizedReedSolomonCode from .decoder import Decoder + class BCHCode(CyclicCode): r""" Representation of a BCH code seen as a cyclic code. diff --git a/src/sage/coding/channel.py b/src/sage/coding/channel.py index 0b584c3c117..e297a6fe68e 100644 --- a/src/sage/coding/channel.py +++ b/src/sage/coding/channel.py @@ -95,6 +95,7 @@ def random_error_vector(n, F, error_positions): vect[i] = F._random_nonzero_element() return vector(F, vect) + def format_interval(t): r""" Return a formatted string representation of ``t``. diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index a4771995a47..4bb33d9565f 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -360,6 +360,7 @@ def gilbert_lower_bound(n, q, d): ans = q**n/volume_hamming(n,q,d-1) return ans + def plotkin_upper_bound(n,q,d, algorithm=None): r""" Return the Plotkin upper bound. @@ -398,6 +399,7 @@ def plotkin_upper_bound(n,q,d, algorithm=None): fact = int(fact) + 1 return int(d/( d - t * fact)) * q**(n - fact) + def griesmer_upper_bound(n,q,d,algorithm=None): r""" Return the Griesmer upper bound. @@ -735,7 +737,7 @@ def elias_bound_asymp(delta, q): 0.39912396330... """ r = 1 - 1 / q - return RDF((1-entropy(r-sqrt(r*(r-delta)), q))) + return RDF(1-entropy(r-sqrt(r*(r-delta)), q)) def mrrw1_bound_asymp(delta, q): diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 5767872b3e2..be1c087c280 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -178,6 +178,7 @@ def _is_a_splitting(S1, S2, n, return_automorphism=False): else: return False + def _lift2smallest_field(a): """ INPUT: @@ -327,6 +328,7 @@ def walsh_matrix(m0): ##################### main constructions ##################### + def DuadicCodeEvenPair(F,S1,S2): r""" Construct the "even pair" of duadic codes associated to the @@ -376,6 +378,7 @@ def DuadicCodeEvenPair(F,S1,S2): C2 = CyclicCode(length=n, generator_pol=gg2) return C1,C2 + def DuadicCodeOddPair(F,S1,S2): """ Construct the "odd pair" of duadic codes associated to the @@ -431,6 +434,7 @@ def DuadicCodeOddPair(F,S1,S2): C2 = CyclicCode(length=n, generator_pol=gg2) return C1,C2 + def ExtendedQuadraticResidueCode(n,F): r""" The extended quadratic residue code (or XQR code) is obtained from @@ -471,6 +475,7 @@ def ExtendedQuadraticResidueCode(n,F): C = QuadraticResidueCodeOddPair(n,F)[0] return C.extended_code() + def from_parity_check_matrix(H): r""" Return the linear code that has ``H`` as a parity check matrix. @@ -494,6 +499,7 @@ def from_parity_check_matrix(H): Cd = LinearCode(H) return Cd.dual_code() + def QuadraticResidueCode(n,F): r""" A quadratic residue code (or QR code) is a cyclic code whose @@ -537,6 +543,7 @@ def QuadraticResidueCode(n,F): """ return QuadraticResidueCodeOddPair(n,F)[0] + def QuadraticResidueCodeEvenPair(n,F): r""" Quadratic residue codes of a given odd prime length and base ring @@ -688,6 +695,7 @@ def random_linear_code(F, length, dimension): if G.rank() == dimension: return LinearCode(G) + def ToricCode(P,F): r""" Let `P` denote a list of lattice points in diff --git a/src/sage/coding/codecan/autgroup_can_label.pyx b/src/sage/coding/codecan/autgroup_can_label.pyx index e54e64a20a9..b6496249786 100644 --- a/src/sage/coding/codecan/autgroup_can_label.pyx +++ b/src/sage/coding/codecan/autgroup_can_label.pyx @@ -223,7 +223,7 @@ class LinearCodeAutGroupCanLabel: from sage.coding.linear_code import LinearCode, AbstractLinearCode if not isinstance(C, AbstractLinearCode): - raise TypeError("%s is not a linear code"%C) + raise TypeError("%s is not a linear code" % C) self.C = C mat = C.generator_matrix() diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index c66a93a0f7d..47311e16260 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -608,7 +608,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): self._init_partition_stack(P) self._init_point_hyperplane_incidence() - self._start_Sn_backtrack() #start the main computation + self._start_Sn_backtrack() # start the main computation # up to now, we just computed the permutational part of the group action # compute the other components of the transporter @@ -916,8 +916,8 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): if self._inner_group.rank < 2: return True - lower = iter(self._matrix[ : self._inner_group.rank].columns()) - upper = iter(self._matrix[self._inner_group.rank : ].columns()) + lower = iter(self._matrix[:self._inner_group.rank].columns()) + upper = iter(self._matrix[self._inner_group.rank:].columns()) for i in range(self._n): l = next(lower) diff --git a/src/sage/coding/codecan/meson.build b/src/sage/coding/codecan/meson.build new file mode 100644 index 00000000000..ac3a94a1edd --- /dev/null +++ b/src/sage/coding/codecan/meson.build @@ -0,0 +1,23 @@ +py.install_sources('all.py', 'codecan.pxd', subdir: 'sage/coding/codecan') + +extension_data = { + 'autgroup_can_label' : files('autgroup_can_label.pyx'), + 'codecan' : files('codecan.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/coding/codecan', + install: true, + include_directories: [ + inc_cpython, + inc_data_structures, + inc_partn_ref2, + inc_rings, + ], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index d5b112b821d..05089583389 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -23,6 +23,7 @@ from sage.misc.abstract_method import abstract_method from sage.structure.sage_object import SageObject + class Decoder(SageObject): r""" Abstract top-class for :class:`Decoder` objects. diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 6ad764935c7..459d6c82ebc 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -23,6 +23,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject + class Encoder(SageObject): r""" Abstract top-class for :class:`Encoder` objects. @@ -380,6 +381,7 @@ def generator_matrix(self): [1 1 0 1 0 0 1] """ + class EncodingError(Exception): r""" Special exception class to indicate an error during encoding or unencoding. diff --git a/src/sage/coding/extended_code.py b/src/sage/coding/extended_code.py index 45557b1277e..46335b19bdc 100644 --- a/src/sage/coding/extended_code.py +++ b/src/sage/coding/extended_code.py @@ -31,6 +31,7 @@ from sage.modules.free_module_element import vector from copy import copy + class ExtendedCode(AbstractLinearCode): r""" Representation of an extended code. diff --git a/src/sage/coding/gabidulin_code.py b/src/sage/coding/gabidulin_code.py index cce8fcfc7d5..3db66e4af7b 100644 --- a/src/sage/coding/gabidulin_code.py +++ b/src/sage/coding/gabidulin_code.py @@ -207,7 +207,7 @@ def __init__(self, base_field, length, dimension, sub_field=None, if not len(evaluation_points) == length: raise ValueError("the number of evaluation points should be equal to the length of the code") for i in range(length): - if not evaluation_points[i] in base_field: + if evaluation_points[i] not in base_field: raise ValueError("evaluation point does not belong to the 'base field'") basis = self.matrix_form_of_vector(vector(evaluation_points)) if basis.rank() != length: diff --git a/src/sage/coding/golay_code.py b/src/sage/coding/golay_code.py index 3d52dade619..233c754df97 100644 --- a/src/sage/coding/golay_code.py +++ b/src/sage/coding/golay_code.py @@ -32,6 +32,7 @@ from .linear_code import (AbstractLinearCode, LinearCodeGeneratorMatrixEncoder) + class GolayCode(AbstractLinearCode): r""" Representation of a Golay Code. diff --git a/src/sage/coding/goppa_code.py b/src/sage/coding/goppa_code.py index f61be9e7afb..aede3782597 100644 --- a/src/sage/coding/goppa_code.py +++ b/src/sage/coding/goppa_code.py @@ -35,6 +35,7 @@ from sage.modules.free_module_element import vector from sage.coding.all import codes + def _columnize(element): """ Convert a finite field element to a column vector over the prime field. diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index ee1c47e07eb..2fada75c4a9 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -1531,7 +1531,7 @@ def _polynomial_vanishing_at_alphas(self, PolRing): def _partial_xgcd(self, a, b, PolRing): r""" - Perform an Euclidean algorithm on ``a`` and ``b`` until a remainder + Perform a Euclidean algorithm on ``a`` and ``b`` until a remainder has degree less than `\frac{n+k}{2}`, `n` being the dimension of the code, `k` its dimension, and returns `(r, s)` such that in the step just before termination, `r = a s + b t`. diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index 6a91949234f..54a624375bd 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -36,6 +36,7 @@ from sage.functions.other import floor from sage.misc.functional import sqrt + def n_k_params(C, n_k): r""" Internal helper function for the :class:`GRSGuruswamiSudanDecoder` class for @@ -85,6 +86,7 @@ def n_k_params(C, n_k): elif n_k is not None: return n_k + def roth_ruckenstein_root_finder(p, maxd=None, precision=None): """ Wrapper for Roth-Ruckenstein algorithm to compute the roots of a polynomial @@ -104,6 +106,7 @@ def roth_ruckenstein_root_finder(p, maxd=None, precision=None): p = p.polynomial(gens[1]) return p.roots(multiplicities=False, degree_bound=maxd, algorithm='Roth-Ruckenstein') + def alekhnovich_root_finder(p, maxd=None, precision=None): """ Wrapper for Alekhnovich's algorithm to compute the roots of a polynomial @@ -123,6 +126,7 @@ def alekhnovich_root_finder(p, maxd=None, precision=None): p = p.polynomial(gens[1]) return p.roots(multiplicities=False, degree_bound=maxd, algorithm='Alekhnovich') + class GRSGuruswamiSudanDecoder(Decoder): r""" The Guruswami-Sudan list-decoding algorithm for decoding Generalized diff --git a/src/sage/coding/guruswami_sudan/interpolation.py b/src/sage/coding/guruswami_sudan/interpolation.py index c7855af3381..b1334307e79 100644 --- a/src/sage/coding/guruswami_sudan/interpolation.py +++ b/src/sage/coding/guruswami_sudan/interpolation.py @@ -25,6 +25,8 @@ from sage.misc.misc_c import prod ####################### Linear algebra system solving ############################### + + def _flatten_once(lstlst): r""" Flattens a list of list into a list, but only flattening one layer and @@ -52,6 +54,7 @@ def _flatten_once(lstlst): # Linear algebraic Interpolation algorithm, helper functions #************************************************************* + def _monomial_list(maxdeg, l, wy): r""" Return a list of all nonnegative integer pairs `(i,j)` such that ``i + wy @@ -156,6 +159,7 @@ def _interpolation_max_weighted_deg(n, tau, s): """ return (n-tau) * s + def _interpolation_matrix_problem(points, tau, parameters, wy): r""" Return the linear system of equations which ``Q`` should be a solution to. @@ -287,6 +291,7 @@ def gs_interpolation_linalg(points, tau, parameters, wy): ####################### Lee-O'Sullivan's method ############################### + def lee_osullivan_module(points, parameters, wy): r""" Return the analytically straight-forward basis for the `\GF{q}[x]` module diff --git a/src/sage/coding/information_set_decoder.py b/src/sage/coding/information_set_decoder.py index 00f4816c37a..c059b185816 100644 --- a/src/sage/coding/information_set_decoder.py +++ b/src/sage/coding/information_set_decoder.py @@ -68,6 +68,7 @@ def _format_decoding_interval(decoding_interval): return "exactly {0}".format(decoding_interval[0]) return "between {0} and {1}".format(decoding_interval[0], decoding_interval[1]) + class InformationSetAlgorithm(SageObject): r""" Abstract class for algorithms for diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index ff5f0c897b3..97a37e96c9b 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -176,6 +176,7 @@ def to_matrix_representation(v, sub_field=None, basis=None): extension, to_big_field, from_big_field = base_field.vector_space(sub_field, basis, map=True) return matrix(sub_field, m, n, lambda i, j: from_big_field(v[j])[i]) + def from_matrix_representation(w, base_field=None, basis=None): r""" Return a vector representation of a matrix ``w`` over ``base_field`` in terms @@ -255,6 +256,7 @@ def rank_weight(c, sub_field=None, basis=None): c = to_matrix_representation(c, sub_field, basis) return c.rank() + def rank_distance(a, b, sub_field=None, basis=None): r""" Return the rank of ``a`` - ``b`` as a matrix over ``sub_field``. diff --git a/src/sage/coding/meson.build b/src/sage/coding/meson.build new file mode 100644 index 00000000000..65b2e0d8eb1 --- /dev/null +++ b/src/sage/coding/meson.build @@ -0,0 +1,59 @@ +py.install_sources( + 'abstract_code.py', + 'ag_code.py', + 'all.py', + 'bch_code.py', + 'binary_code.pxd', + 'bounds_catalog.py', + 'channel.py', + 'channels_catalog.py', + 'code_bounds.py', + 'code_constructions.py', + 'codes_catalog.py', + 'cyclic_code.py', + 'databases.py', + 'decoder.py', + 'decoders_catalog.py', + 'delsarte_bounds.py', + 'encoder.py', + 'encoders_catalog.py', + 'extended_code.py', + 'gabidulin_code.py', + 'golay_code.py', + 'goppa_code.py', + 'grs_code.py', + 'guava.py', + 'hamming_code.py', + 'information_set_decoder.py', + 'linear_code.py', + 'linear_code_no_metric.py', + 'linear_rank_metric.py', + 'parity_check_code.py', + 'punctured_code.py', + 'reed_muller_code.py', + 'self_dual_codes.py', + 'subfield_subcode.py', + 'two_weight_db.py', + subdir: 'sage/coding', +) + +extension_data = { + 'ag_code_decoders' : files('ag_code_decoders.pyx'), + 'binary_code' : files('binary_code.pyx'), + 'kasami_codes' : files('kasami_codes.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/coding', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +subdir('codecan') +install_subdir('guruswami_sudan', install_dir: sage_install_dir / 'coding') +install_subdir('source_coding', install_dir: sage_install_dir / 'coding') diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index c56dccf3df3..c8be67993f8 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -27,6 +27,7 @@ from sage.rings.finite_rings.finite_field_constructor import GF from copy import copy + def _puncture(v, points): r""" Return v punctured as the positions listed in ``points``. @@ -58,6 +59,7 @@ def _puncture(v, points): new_v = [v[i] for i in range(len(v)) if i not in points] return S(new_v) + def _insert_punctured_positions(l, punctured_points, value=None): r""" Return ``l`` with ``value`` inserted in the corresponding diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 64258f48927..e690534c5e3 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -584,16 +584,11 @@ def __init__(self, code): ... ValueError: the code has to be a Reed-Muller code """ - if not ( - isinstance( - code, - QAryReedMullerCode) or isinstance( - code, - BinaryReedMullerCode)): + if not isinstance(code, (QAryReedMullerCode, BinaryReedMullerCode)): raise ValueError("the code has to be a Reed-Muller code") super().__init__(code) - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -775,9 +770,7 @@ def __init__(self, code, polynomial_ring=None): ... ValueError: The Polynomial ring should be on Finite Field of size 59 and should have 3 variables """ - if not ( - isinstance(code, QAryReedMullerCode) - or isinstance(code, BinaryReedMullerCode)): + if not isinstance(code, (QAryReedMullerCode, BinaryReedMullerCode)): raise ValueError("the code has to be a Reed-Muller code") super().__init__(code) if polynomial_ring is None: diff --git a/src/sage/coding/self_dual_codes.py b/src/sage/coding/self_dual_codes.py index 9270aa5f240..10565d9e1a0 100644 --- a/src/sage/coding/self_dual_codes.py +++ b/src/sage/coding/self_dual_codes.py @@ -141,6 +141,7 @@ def _matA(n): A.append(I+O) return A + def _matId(n): r""" For internal use; returns a list of identity matrices over GF(2) @@ -177,6 +178,7 @@ def _MS2(n): n2 = n.quo_rem(2)[0] return MatrixSpace(_F, n2, n2) + def _I2(n): r""" Internal function. @@ -196,6 +198,7 @@ def _I2(n): """ return _MS2(n).identity_matrix() + @cached_function def _And7(): """ @@ -253,6 +256,7 @@ def _H8(): ############## main functions ############## + def self_dual_binary_codes(n): r""" Return the dictionary of inequivalent binary self dual codes of length `n`. diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py index df4e0b96eff..2755e033632 100644 --- a/src/sage/coding/source_coding/huffman.py +++ b/src/sage/coding/source_coding/huffman.py @@ -394,7 +394,7 @@ def encode(self, string): 'Sage is my most favorite general purpose computer algebra system' """ if self._character_to_code: - return "".join((self._character_to_code[x] for x in string)) + return "".join(self._character_to_code[x] for x in string) def decode(self, string): r""" diff --git a/src/sage/combinat/SJT.py b/src/sage/combinat/SJT.py index 03883eeb401..893f7735183 100644 --- a/src/sage/combinat/SJT.py +++ b/src/sage/combinat/SJT.py @@ -26,6 +26,7 @@ # **************************************************************************** from sage.combinat.combinat import CombinatorialElement + class SJT(CombinatorialElement): r""" A representation of a list permuted using the Steinhaus-Johnson-Trotter diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 230c9a8aae7..69a529054af 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -262,13 +262,12 @@ def to_monotone_triangle(self): True """ n = self._matrix.nrows() - triangle = [None] * n - prev = zero_vector(ZZ, n) + triangle = [0] * n + add_row = zero_vector(ZZ, n) for j, row in enumerate(self._matrix): - add_row = row + prev + add_row = row + add_row triangle[n - 1 - j] = [i + 1 for i in range(n - 1, -1, -1) if add_row[i] == 1] - prev = add_row return MonotoneTriangles(n)(triangle) @combinatorial_map(name='rotate counterclockwise') diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 93cdfef5c8a..7e07835b935 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -232,7 +232,8 @@ def __init__(self, data, frozen=None, user_labels=None): values)) # constructs a quiver from a mutation type - if type( data ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]: + if isinstance(data, (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)): if frozen is not None: print('The input specifies a mutation type, so the' ' additional parameter frozen is ignored.' @@ -1950,10 +1951,15 @@ def is_mutation_finite( self, nr_of_checks=None, return_path=False ): path = None elif not return_path and self._mutation_type == 'undetermined infinite mutation type': is_finite = False - elif type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and self._mutation_type.is_mutation_finite(): + elif (isinstance(self._mutation_type, (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)) + and self._mutation_type.is_mutation_finite()): is_finite = True path = None - elif not return_path and type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and not self._mutation_type.is_mutation_finite(): + elif (not return_path and isinstance(self._mutation_type, + (QuiverMutationType_Irreducible, + QuiverMutationType_Reducible)) + and not self._mutation_type.is_mutation_finite()): is_finite = False else: # turning dg_component into a canonical form diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py index 25610dff117..c9f06a4761f 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py @@ -67,7 +67,9 @@ def __call__(self, *args): _mutation_type_error(data) # check for reducible types - if all(type(data_component) in [list, tuple, QuiverMutationType_Irreducible] for data_component in data): + if all(isinstance(data_component, (list, tuple, + QuiverMutationType_Irreducible)) + for data_component in data): if len(data) == 1: return QuiverMutationType(data[0]) else: diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 19677532d98..03c1797abfb 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -164,7 +164,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator from sage.arith.misc import bernoulli, factorial from sage.rings.integer_ring import ZZ @@ -1571,6 +1571,7 @@ def __init__(self, parent, *args, **kwds): ##################################################### # combinatorial sets/lists + def tuples(S, k, algorithm='itertools'): r""" Return a list of all `k`-tuples of elements of a given set ``S``. diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index e90fe55f6a5..1e05da84c4e 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -211,10 +211,7 @@ def descent_set(self): sage: CompositionTableau([[1],[3,2],[4,4]]).descent_set() [1, 3] """ - cols = {} - for row in self: - for col, i in enumerate(row): - cols[i] = col + cols = {i: col for row in self for col, i in enumerate(row)} return sorted(i for i in cols if i + 1 in cols and cols[i + 1] >= cols[i]) def descent_composition(self): diff --git a/src/sage/combinat/core.py b/src/sage/combinat/core.py index d6775040c48..21ce49f57f1 100644 --- a/src/sage/combinat/core.py +++ b/src/sage/combinat/core.py @@ -102,7 +102,7 @@ def __init__(self, parent, core): raise ValueError("%s is not a %s-core" % (part, k)) CombinatorialElement.__init__(self, parent, core) - def __eq__(self, other): + def __eq__(self, other) -> bool: """ Test for equality. @@ -123,7 +123,7 @@ def __eq__(self, other): self.parent().k == other.parent().k) return False - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Test for un-equality. @@ -169,7 +169,7 @@ def __hash__(self): self._hash = hash(tuple(self._list)) + hash(self.parent().k) return self._hash - def _latex_(self): + def _latex_(self) -> str: r""" Output the LaTeX representation of this core as a partition. @@ -449,7 +449,7 @@ def weak_le(self, other): ... ValueError: the two cores do not have the same k """ - if type(self) is type(other): + if isinstance(other, Core): if self.k() != other.k(): raise ValueError("the two cores do not have the same k") else: @@ -504,14 +504,14 @@ def strong_le(self, other): ... ValueError: the two cores do not have the same k """ - if type(self) is type(other): + if isinstance(other, Core): if self.k() != other.k(): raise ValueError("the two cores do not have the same k") else: other = Core(other, self.k()) return other.contains(self) - def contains(self, other): + def contains(self, other) -> bool: r""" Check whether ``self`` contains ``other``. diff --git a/src/sage/combinat/crystals/meson.build b/src/sage/combinat/crystals/meson.build new file mode 100644 index 00000000000..96ff9f4e19e --- /dev/null +++ b/src/sage/combinat/crystals/meson.build @@ -0,0 +1,58 @@ +py.install_sources( + 'affine.py', + 'affine_factorization.py', + 'affinization.py', + 'alcove_path.py', + 'all.py', + 'bkk_crystals.py', + 'catalog.py', + 'catalog_elementary_crystals.py', + 'catalog_infinity_crystals.py', + 'catalog_kirillov_reshetikhin.py', + 'crystals.py', + 'direct_sum.py', + 'elementary_crystals.py', + 'fast_crystals.py', + 'fully_commutative_stable_grothendieck.py', + 'generalized_young_walls.py', + 'highest_weight_crystals.py', + 'induced_structure.py', + 'infinity_crystals.py', + 'kac_modules.py', + 'kirillov_reshetikhin.py', + 'kyoto_path_model.py', + 'letters.pxd', + 'littelmann_path.py', + 'monomial_crystals.py', + 'multisegments.py', + 'mv_polytopes.py', + 'pbw_crystal.py', + 'pbw_datum.pxd', + 'polyhedral_realization.py', + 'spins.pxd', + 'star_crystal.py', + 'subcrystal.py', + 'tensor_product.py', + 'tensor_product_element.pxd', + 'virtual_crystal.py', + subdir: 'sage/combinat/crystals', +) + +extension_data = { + 'letters' : files('letters.pyx'), + 'pbw_datum' : files('pbw_datum.pyx'), + 'spins' : files('spins.pyx'), + 'tensor_product_element' : files('tensor_product_element.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/crystals', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 1ccd1a02ada..49c930ccb38 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -362,6 +362,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa else: raise NotImplementedError("I don't know how to build a ({},{},{})-BIBD!".format(v, k, lambd)) + def BruckRyserChowla_check(v, k, lambd): r""" Check whether the parameters passed satisfy the Bruck-Ryser-Chowla theorem. @@ -433,6 +434,7 @@ def BruckRyserChowla_check(v, k, lambd): return flag + def steiner_triple_system(n): r""" Return a Steiner Triple System. @@ -746,6 +748,7 @@ def BIBD_from_difference_family(G, D, lambd=None, check=True): # (v,4,1)-BIBD # ################ + def v_4_1_BIBD(v, check=True): r""" Return a `(v,4,1)`-BIBD. @@ -890,6 +893,7 @@ def BIBD_from_PBD(PBD, v, k, check=True, base_cases=None): return bibd + def _relabel_bibd(B,n,p=None): r""" Relabel the BIBD on `n` points and blocks of size k such that @@ -1016,6 +1020,7 @@ def PBD_4_5_8_9_12(v, check=True): return PBD + def _PBD_4_5_8_9_12_closure(B): r""" Makes sure all blocks of `B` have size in `\{4,5,8,9,12\}`. @@ -1175,6 +1180,7 @@ def v_5_1_BIBD(v, check=True): return bibd + def _get_r_s_t_u(v): r""" Implement the table from [ClaytonSmith]_. @@ -1247,6 +1253,7 @@ def PBD_from_TD(k,t,u): TD.append(list(range(k*t,k*t+u))) return TD + def BIBD_5q_5_for_q_prime_power(q): r""" Return a `(5q,5,1)`-BIBD with `q\equiv 1\pmod 4` a prime power. @@ -1348,10 +1355,10 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): :doi:`10.1016/S0021-9800(69)80095-5` """ q = (n-1)//(k-1)-1 - if (k % 2 or - q % 2 or - q <= k or - n != (k-1)*(q+1)+1 or + if (k % 2 or + q % 2 or + q <= k or + n != (k-1)*(q+1)+1 or not is_prime_power(k) or not is_prime_power(q)): if existence: @@ -1384,16 +1391,15 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): # [Denniston69] is the set of all elements of K of degree < log_n # (seeing elements of K as polynomials in 'a') - K_iter = list(K) # faster iterations - log_n = is_prime_power(n,get_data=True)[1] - C = [(x,y,one) - for x in K_iter - for y in K_iter - if Q(x,y).polynomial().degree() < log_n] + K_iter = list(K) # faster iterations + log_n = is_prime_power(n, get_data=True)[1] + C = [(x, y, one) for x in K_iter for y in K_iter + if Q(x, y).polynomial().degree() < log_n] from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks + class PairwiseBalancedDesign(GroupDivisibleDesign): r""" Pairwise Balanced Design (PBD). diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index e5faaa11602..0c8b5b3fd38 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -42,7 +42,7 @@ --------------------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Peter Dobcsanyi # Copyright (C) 2007 David Joyner # @@ -51,7 +51,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.arith.misc import binomial, integer_floor, is_prime_power from sage.categories.sets_cat import EmptySetError from sage.misc.lazy_import import lazy_import @@ -69,7 +69,8 @@ BlockDesign = IncidenceStructure -### utility functions ------------------------------------------------------- +# utility functions ----------------------------------------------------- + def tdesign_params(t, v, k, L): """ @@ -91,6 +92,7 @@ def tdesign_params(t, v, k, L): r = integer_floor(L * x/y) return (t, v, b, r, k, L) + def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parameters=False): r""" Return ``True`` if the parameters ``(v,k,lmbda)`` are the one of hyperplanes in @@ -167,6 +169,7 @@ def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parame return (True, (q,d)) if return_parameters else True + def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, check=True): r""" Return a projective geometry design. @@ -335,9 +338,10 @@ def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, check=True): """ K = FiniteField(n, 'a') n2 = n**2 - relabel = {x:i for i,x in enumerate(K)} - Kiter = relabel # it is much faster to iterate through a dict than through - # the finite field K + relabel = {x: i for i, x in enumerate(K)} + Kiter = relabel + # it is much faster to iterate through a dict than through + # the finite field K # we decompose the (equivalence class) of points [x:y:z] of the projective # plane into an affine plane, an affine line and a point. At the same time, @@ -392,6 +396,7 @@ def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, check=True): return B + def q3_minus_one_matrix(K): r""" Return a companion matrix in `GL(3, K)` whose multiplicative order is `q^3 - 1`. @@ -438,6 +443,7 @@ def q3_minus_one_matrix(K): if m.multiplicative_order() == q**3 - 1: return m + def normalize_hughes_plane_point(p, q): r""" Return the normalized form of point ``p`` as a 3-tuple. @@ -481,6 +487,7 @@ def normalize_hughes_plane_point(p, q): else: return ((p[0] * k)**q,(p[1]*k)**q,(p[2]*k)**q) + def HughesPlane(q2, check=True): r""" Return the Hughes projective plane of order ``q2``. @@ -620,6 +627,7 @@ def HughesPlane(q2, check=True): from .bibd import BalancedIncompleteBlockDesign return BalancedIncompleteBlockDesign(q2**2+q2+1, blcks, check=check) + def projective_plane_to_OA(pplane, pt=None, check=True): r""" Return the orthogonal array built from the projective plane ``pplane``. @@ -775,6 +783,7 @@ def projective_plane(n, check=True, existence=False): else: return DesarguesianProjectivePlaneDesign(n, point_coordinates=False, check=check) + def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True): r""" Return an affine geometry design. @@ -988,14 +997,14 @@ def HadamardDesign(n): """ from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix - H = hadamard_matrix(n+1) #assumed to be normalised. + H = hadamard_matrix(n + 1) # assumed to be normalised. H1 = H.matrix_from_columns(range(1,n+1)) H2 = H1.matrix_from_rows(range(1,n+1)) J = matrix(ZZ,n,n,[1]*n*n) MS = J.parent() - A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ + A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ # A is the incidence matrix of the block design - return IncidenceStructure(incidence_matrix=A,name='HadamardDesign') + return IncidenceStructure(incidence_matrix=A, name='HadamardDesign') def Hadamard3Design(n): @@ -1052,10 +1061,10 @@ def Hadamard3Design(n): raise ValueError("The Hadamard design with n = %s does not extend to a three design." % n) from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix, block_matrix - H = hadamard_matrix(n) #assumed to be normalised. + H = hadamard_matrix(n) # assumed to be normalised. H1 = H.matrix_from_columns(range(1, n)) J = matrix(ZZ, n, n-1, [1]*(n-1)*n) - A1 = (H1+J)/2 - A2 = (J-H1)/2 - A = block_matrix(1, 2, [A1, A2]) #the incidence matrix of the design. + A1 = (H1 + J) / 2 + A2 = (J - H1) / 2 + A = block_matrix(1, 2, [A1, A2]) # the incidence matrix of the design. return IncidenceStructure(incidence_matrix=A, name='HadamardThreeDesign') diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 30bbeafb708..7977de93d9d 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -21,7 +21,7 @@ REFERENCES: .. [1] La Jolla Covering Repository, - https://ljcr.dmgordon.org/cover.html + https://dmgordon.org/cover .. [2] Daniel M. Gordon and Douglas R. Stinson, *Coverings*, Chapter 1 in: Charles J. Colbourn and Jeffrey H. Dinitz, @@ -516,7 +516,7 @@ def best_known_covering_design_www(v, k, t, verbose=False): k = int(k) t = int(t) param = "?v=%s&k=%s&t=%s" % (v, k, t) - url = "https://ljcr.dmgordon.org/cover/get_cover.php" + param + url = "https://ljcr.dmgordon.org/get_cover.php" + param if verbose: print("Looking up the bounds at %s" % url) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index ba4972ec5c9..19eb9d26165 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -68,6 +68,7 @@ # Cyclic shift of a list cyclic_shift = lambda l,i : l[-i:]+l[:-i] + def _MOLS_from_string(s,k): r""" Return MOLS from a string. @@ -91,6 +92,7 @@ def _MOLS_from_string(s,k): matrices[i % k].append(l) return [Matrix(_) for _ in matrices] + def MOLS_10_2(): r""" Return a pair of MOLS of order 10. @@ -134,6 +136,7 @@ def MOLS_10_2(): [5,6,7,1,2,3,4,0,9,8], [7,1,2,3,4,5,6,9,8,0]])] + def MOLS_12_5(): r""" Return 5 MOLS of order 12. @@ -165,6 +168,7 @@ def MOLS_12_5(): return _MOLS_from_string(M,5) + def MOLS_14_4(): r""" Return four MOLS of order 14. @@ -210,6 +214,7 @@ def MOLS_14_4(): return _MOLS_from_string(M,4) + def MOLS_15_4(): r""" Return 4 MOLS of order 15. @@ -249,6 +254,7 @@ def MOLS_15_4(): return _MOLS_from_string(M,4) + def MOLS_18_3(): r""" Return 3 MOLS of order 18. @@ -310,6 +316,7 @@ def MOLS_18_3(): LIST_OF_MOLS_CONSTRUCTIONS = ", ".join(":func:`{} MOLS of order {} `".format(k,n,n,k) for n,(k,_) in MOLS_constructions.items()) + def OA_7_18(): r""" Return an OA(7,18). @@ -364,6 +371,7 @@ def OA_7_18(): M = [M[i] for i in range(len(M)) if i % 18 < 9] # only develop w.r.t the last two coordinates return M + def OA_9_40(): r""" Return an OA(9,40). @@ -404,6 +412,7 @@ def OA_9_40(): return OA_n_times_2_pow_c_from_matrix(9,3,FiniteField(5),A,Y,check=False) + def OA_7_66(): r""" Return an OA(7,66). @@ -444,6 +453,7 @@ def OA_7_66(): PBD = [[rel[x] for x in B] for B in PBD] return OA_from_PBD(7,66,PBD,check=False) + def OA_7_68(): r""" Return an OA(7,68). @@ -484,6 +494,7 @@ def OA_7_68(): PBD = [[rel[x] for x in B] for B in PBD] return OA_from_PBD(7,68,PBD,check=False) + def OA_8_69(): r""" Return an OA(8,69). @@ -556,6 +567,7 @@ def OA_8_69(): OA = [[relabel[x] for x in B] for B in OA] return OA + def OA_7_74(): r""" Return an OA(7,74). @@ -596,6 +608,7 @@ def OA_7_74(): PBD = [[rel[x] for x in B] for B in PBD] return OA_from_PBD(7,74,PBD,check=False) + def OA_8_76(): r""" Return an OA(8,76). @@ -663,6 +676,7 @@ def OA_8_76(): OA = [[relabel[x] for x in B] for B in OA] return OA + def OA_11_80(): r""" Return an OA(11,80). @@ -705,6 +719,7 @@ def OA_11_80(): return OA_n_times_2_pow_c_from_matrix(11,4,FiniteField(5),A,Y,check=False) + def OA_15_112(): r""" Return an OA(15,112). @@ -751,6 +766,7 @@ def OA_15_112(): return OA_n_times_2_pow_c_from_matrix(15,4,FiniteField(7),list(zip(*A)),Y,check=False) + def OA_9_120(): r""" Return an OA(9,120). @@ -797,6 +813,7 @@ def OA_9_120(): return OA + def OA_9_135(): r""" Return an OA(9,135). @@ -882,6 +899,7 @@ def OA_9_135(): # And call Wilson's construction return wilson_construction(truncated_OA, 9, 16, 8, (1,)*7, check=False) + def OA_11_160(): r""" Return an OA(11,160). @@ -925,6 +943,7 @@ def OA_11_160(): return OA_n_times_2_pow_c_from_matrix(11,5,FiniteField(5),list(zip(*A)),Y,check=False) + def OA_16_176(): r""" Return an OA(16,176). @@ -979,6 +998,7 @@ def OA_16_176(): Y = [None, 0, 1, 2, 8, 6, 9, 4, 10, 3, 5, 11, 13, 14, 12] return OA_n_times_2_pow_c_from_matrix(16,4,FiniteField(11),list(zip(*A)),Y,check=False) + def OA_11_185(): r""" Return an OA(11,185). @@ -1042,6 +1062,7 @@ def OA_11_185(): OA.extend([[special_set[x] for x in B] for B in orthogonal_array(11,17)]) return OA + def OA_10_205(): r""" Return an `OA(10,205)`. @@ -1088,7 +1109,7 @@ def OA_10_205(): baer_subplane_size = 4**2+4+1 B = [0, 1, 22, 33, 83, 122, 135, 141, 145, 159, 175, 200, 226, 229, 231, 238, 246] - pplane = [[(xx+i) % pplane_size for xx in B] for i in range(pplane_size)] + pplane = [[(xx+i) % pplane_size for xx in B] for i in range(pplane_size)] baer_subplane = set([i*pplane_size/baer_subplane_size for i in range(baer_subplane_size)]) p = list(baer_subplane)[0] @@ -1123,6 +1144,7 @@ def OA_10_205(): return OA + def OA_16_208(): r""" Return an OA(16,208). @@ -1182,6 +1204,7 @@ def OA_16_208(): return OA_n_times_2_pow_c_from_matrix(16,4,FiniteField(13),list(zip(*A)),Y,check=False) + def OA_15_224(): r""" Return an OA(15,224). @@ -1229,6 +1252,7 @@ def OA_15_224(): return OA_n_times_2_pow_c_from_matrix(15,5,FiniteField(7),list(zip(*A)),Y,check=False) + def OA_11_254(): r""" Return an OA(11,254). @@ -1260,7 +1284,7 @@ def OA_11_254(): # Base block of a PG(2,19) B = (0,1,19,28,96,118,151,153,176,202,240,254,290,296,300,307,337,361,366,369) - BIBD = [[(x+i) % 381 for x in B] for i in range(381)] + BIBD = [[(x+i) % 381 for x in B] for i in range(381)] # We only keep points congruent to 0,1 mod 3 and relabel the PBD. The result is # a (254,{11,13,16})-PBD @@ -1268,6 +1292,7 @@ def OA_11_254(): return OA_from_PBD(11,254,BIBD,check=False) + def OA_20_352(): r""" Return an OA(20,352). @@ -1327,6 +1352,7 @@ def OA_20_352(): return OA_n_times_2_pow_c_from_matrix(20,5,FiniteField(11),list(zip(*A)),Y,check=False) + def OA_20_416(): r""" Return an OA(20,416). @@ -1387,6 +1413,7 @@ def OA_20_416(): return OA_n_times_2_pow_c_from_matrix(20,5,FiniteField(13),list(zip(*A)),Y,check=False) + def OA_20_544(): r""" Return an OA(20,544). @@ -1456,6 +1483,7 @@ def OA_20_544(): return OA_n_times_2_pow_c_from_matrix(20,5,FiniteField(17),list(zip(*A)),Y,check=False) + def OA_17_560(): r""" Return an OA(17,560). @@ -1515,6 +1543,7 @@ def OA_17_560(): return wilson_construction(OA,k,n,m,[p**beta]*3,check=False) + def OA_11_640(): r""" Return an OA(11,640). @@ -1557,6 +1586,7 @@ def OA_11_640(): return OA_n_times_2_pow_c_from_matrix(11,7,FiniteField(5),list(zip(*A)),Y,check=False) + def OA_10_796(): r""" Return an OA(10,796). @@ -1628,6 +1658,7 @@ def OA_10_796(): return OA + def OA_10_469(): r""" Return an OA(10,469). @@ -1677,7 +1708,7 @@ def OA_10_469(): 731,824,837,848,932,1002,1051,1055,1089,1105,1145,1165,1196,1217,1226, 1274,1281,1309,1405) - BIBD = [[(x+i) % 1407 for x in B] for i in range(1407)] + BIBD = [[(x+i) % 1407 for x in B] for i in range(1407)] # Only keep points v congruent to 0 mod 3 and relabel PBD = [[x//3 for x in B if x % 3 == 0] for B in BIBD] @@ -1702,6 +1733,7 @@ def OA_10_469(): return OA + def OA_520_plus_x(x): r""" Return an `OA(10+x,520+x)`. @@ -1775,6 +1807,7 @@ def OA_520_plus_x(x): OA.append([relabel[new_point]]*k) return OA + def OA_10_520(): r""" Return an OA(10,520). @@ -1797,6 +1830,7 @@ def OA_10_520(): """ return OA_520_plus_x(0) + def OA_12_522(): r""" Return an OA(12,522). @@ -1819,6 +1853,7 @@ def OA_12_522(): """ return OA_520_plus_x(2) + def OA_14_524(): r""" Return an OA(14,524). @@ -1841,6 +1876,7 @@ def OA_14_524(): """ return OA_520_plus_x(4) + def OA_15_896(): r""" Return an OA(15,896). @@ -1888,6 +1924,7 @@ def OA_15_896(): return OA_n_times_2_pow_c_from_matrix(15,7,FiniteField(7),list(zip(*A)),Y,check=False) + def OA_9_1078(): r""" Return an OA(9,1078). @@ -1918,6 +1955,7 @@ def OA_9_1078(): """ return wilson_construction(None,9,11,89,[[(11,9)]]) + def OA_25_1262(): r""" Return an OA(25,1262). @@ -1955,6 +1993,7 @@ def OA_25_1262(): return OA_from_PBD(25,1262,PBD,check=False) + def OA_9_1612(): r""" Return an OA(9,1612). @@ -1985,6 +2024,7 @@ def OA_9_1612(): """ return wilson_construction(None,9,17,89,[[(11,9)]]) + def OA_10_1620(): r""" Return an OA(10,1620). @@ -2061,6 +2101,7 @@ def OA_10_1620(): LIST_OF_OA_CONSTRUCTIONS = ", ".join(":func:`OA({},{}) `".format(k,n,k,n) for n,(k,_) in OA_constructions.items()) + def QDM_19_6_1_1_1(): r""" Return a `(19,6;1,1;1)`-quasi-difference matrix. @@ -2143,6 +2184,7 @@ def QDM_21_5_1_1_1(): return G, Mb + def QDM_21_6_1_1_5(): r""" Return a `(21,6;1,1;5)`-quasi-difference matrix. @@ -2183,6 +2225,7 @@ def QDM_21_6_1_1_5(): return G, Mb + def QDM_25_6_1_1_5(): r""" Return a `(25,6;1,1;5)`-quasi-difference matrix. @@ -2228,6 +2271,7 @@ def QDM_25_6_1_1_5(): return G, Mb + def QDM_33_6_1_1_1(): r""" Return a `(33,6;1,1;1)`-quasi-difference matrix. @@ -2271,6 +2315,7 @@ def QDM_33_6_1_1_1(): return G, Mb + def QDM_37_6_1_1_1(): r""" Return a `(37,6;1,1;1)`-quasi-difference matrix. @@ -2309,6 +2354,7 @@ def QDM_37_6_1_1_1(): return G, Mb + def QDM_35_7_1_1_7(): r""" Return a `(35,7;1,1;7)`-quasi-difference matrix. @@ -2346,6 +2392,7 @@ def QDM_35_7_1_1_7(): return G, Mb + def QDM_45_7_1_1_9(): r""" Return a `(45,7;1,1;9)`-quasi-difference matrix. @@ -2383,6 +2430,7 @@ def QDM_45_7_1_1_9(): return G, Mb + def QDM_54_7_1_1_8(): r""" Return a `(54,7;1,1;8)`-quasi-difference matrix. @@ -2420,6 +2468,7 @@ def QDM_54_7_1_1_8(): return G, Mb + def QDM_57_9_1_1_8(): r""" Return a `(57,9;1,1;8)`-quasi-difference matrix. @@ -3094,15 +3143,58 @@ def QDM_57_9_1_1_8(): [0,2,13,18,28,30,44,48,50,51,57,61], [0,4,21,26,29,33,35,36,47,55,56,60]]}, -# a 133-cyclic set from Ken Smith database -# see https://math.ccrwest.org/diffsets/diff_sets/DS_133_33_8_133.html +# a (133,33,8)-cyclic difference set +# see https://dmgordon.org/diffset (133,33, 8): - {(133,): [[0,4,7,8,15,17,19,22,24,25,29,30,38, - 47,49,50,55,58,61,62,71,73,76,77,78, - 82,95,111,113,114,121,123,127]]}, - -# a 901-cyclic -# see https://math.ccrwest.org/diffsets/diff_sets/DS_901_225_56_901.html + {(133,): [[1,5,14,22,25,27,29,32,34,38, + 46,64,65,66,76,78,81,82,84,89, + 92,93,99,103,104,106,107,112,113,122, + 126,128,129]]}, + +# a (144,66,30) non-cyclic difference set in AbelianGroup([2,8,3,3]) +# given in unpublished paper by Kroeger, Miller, Mooney, Shepard and Smith +# see https://dmgordon.org/diffset +(144,66,30): + {(2,8,3,3): [[(0,1,0,0),(0,7,0,2),(0,5,0,1),(0,3,0,0),(0,6,0,1), + (0,1,0,2),(0,4,0,0),(0,2,0,2),(0,6,0,0),(0,1,0,1), + (0,4,0,2),(0,2,0,1),(1,2,2,0),(1,3,2,0),(1,4,2,0), + (1,5,2,0),(1,6,2,0),(1,7,2,0),(0,6,1,2),(0,1,1,0), + (0,4,1,1),(0,3,1,0),(0,1,1,2),(0,4,1,0),(0,7,1,1), + (0,2,1,2),(0,6,1,0),(0,1,1,1),(0,2,1,1),(0,5,1,2), + (1,0,0,0),(1,6,0,2),(1,1,0,0),(1,4,0,1),(1,7,0,2), + (1,2,0,0),(1,5,0,1),(1,0,0,2),(1,3,0,0),(1,1,0,2), + (1,0,0,1),(1,1,0,1),(0,0,2,0),(0,6,2,2),(0,4,2,1), + (0,0,2,2),(0,3,2,0),(0,6,2,1),(0,2,2,2),(0,5,2,0), + (0,0,2,1),(0,4,2,2),(0,7,2,0),(0,2,2,1),(1,0,1,0), + (1,1,1,0),(1,2,1,0),(1,0,1,2),(1,3,1,0),(1,6,1,1), + (1,1,1,2),(1,7,1,1),(1,0,1,1),(1,1,1,1),(1,4,1,2), + (1,5,1,2)]]}, + +# a (320,88,24) non-cyclic difference set in AbelianGroup([4,4,4,5]), +# given in Arasu and Chen, Designs, Codes and Cryptography 2001 +# see https://dmgordon.org/diffset +(320,88,24): + {(4,4,4,5): [[(3,3,3,0),(2,3,2,0),(3,1,3,0),(2,2,3,0),(1,3,3,0), + (3,2,1,0),(2,2,2,0),(2,2,1,0),(2,1,2,0),(0,3,2,0), + (2,0,3,0),(1,1,3,0),(0,2,3,0),(3,0,1,0),(1,2,1,0), + (2,0,2,0),(0,2,2,0),(2,0,1,0),(0,2,1,0),(0,1,2,0), + (0,0,3,0),(1,0,1,0),(0,0,2,0),(0,0,1,0),(3,3,3,1), + (3,3,1,1),(3,0,3,1),(0,3,3,1),(3,0,1,1),(0,3,1,1), + (1,1,2,1),(1,0,2,1),(0,1,2,1),(0,0,3,1),(1,1,0,1), + (0,0,2,1),(1,0,0,1),(0,1,0,1),(0,0,1,1),(0,0,0,1), + (1,1,3,2),(3,1,1,2),(2,3,3,2),(2,2,3,2),(0,3,2,2), + (0,3,1,2),(0,2,1,2),(3,2,2,2),(3,1,2,2),(3,0,3,2), + (2,3,0,2),(2,0,2,2),(1,2,0,2),(1,1,0,2),(1,0,1,2), + (0,0,0,2),(1,1,1,3),(1,3,3,3),(3,2,1,3),(2,2,3,3), + (3,0,0,3),(3,0,3,3),(1,3,0,3),(2,0,1,3),(3,2,2,3), + (2,3,2,3),(0,3,3,3),(1,1,2,3),(0,2,2,3),(2,1,0,3), + (0,1,1,3),(0,0,0,3),(2,0,3,4),(1,1,2,4),(0,2,1,4), + (0,1,3,4),(3,2,3,4),(3,2,2,4),(2,3,2,4),(3,1,3,4), + (3,3,0,4),(2,3,1,4),(1,0,1,4),(2,2,2,4),(1,3,1,4), + (1,0,0,4),(0,1,0,4),(0,0,0,4)]]}, + +# a (901,225,56)-cyclic difference set +# see https://dmgordon.org/diffset (901,225,56): {(901,): [[ 0, 1, 5, 9, 12, 13, 14, 16, 22, 25, 41, 43, 45, 47, 53, 59, 60, 65, 69, 70, 71, 79, 80, 81, @@ -3174,6 +3266,7 @@ def DM_12_6_1(): return G,M + def DM_21_6_1(): r""" Return a `(21,6,1)`-difference matrix. @@ -3209,6 +3302,7 @@ def DM_21_6_1(): return AdditiveCyclic(21), Mb + def DM_24_8_1(): r""" Return a `(24,8,1)`-difference matrix. @@ -3256,6 +3350,7 @@ def DM_24_8_1(): return G, Mb + def DM_28_6_1(): r""" Return a `(28,6,1)`-difference matrix. @@ -3299,6 +3394,7 @@ def DM_28_6_1(): return G, Mb + def DM_33_6_1(): r""" Return a `(33,6,1)`-difference matrix. @@ -3341,6 +3437,7 @@ def DM_33_6_1(): return G, Mb + def DM_35_6_1(): r""" Return a `(35,6,1)`-difference matrix. @@ -3373,6 +3470,7 @@ def DM_35_6_1(): return G, list(zip(*M)) + def DM_36_9_1(): r""" Return a `(36,9,1)`-difference matrix. @@ -3426,6 +3524,7 @@ def DM_36_9_1(): return G, Mb + def DM_39_6_1(): r""" Return a `(39,6,1)`-difference matrix. @@ -3470,6 +3569,7 @@ def DM_39_6_1(): return G,Mb + def DM_44_6_1(): r""" Return a `(44,6,1)`-difference matrix. @@ -3529,6 +3629,7 @@ def DM_44_6_1(): return G2211, Mb + def DM_45_7_1(): r""" Return a `(45,7,1)`-difference matrix. @@ -3585,6 +3686,7 @@ def DM_45_7_1(): return G533, Mb + def DM_48_9_1(): r""" Return a `(48,9,1)`-difference matrix. @@ -3634,6 +3736,7 @@ def DM_48_9_1(): return F3F16, Mb + def DM_51_6_1(): r""" Return a `(51,6,1)`-difference matrix. @@ -3748,6 +3851,7 @@ def t2(i,R): return G, Mb + def DM_55_7_1(): r""" Return a `(55,7,1)`-difference matrix. @@ -3787,6 +3891,7 @@ def DM_55_7_1(): return G, Mb + def DM_56_8_1(): r""" Return a `(56,8,1)`-difference matrix. @@ -3834,6 +3939,7 @@ def DM_56_8_1(): return G, Mb + def DM_57_8_1(): r""" Return a `(57,8,1)`-difference matrix. @@ -3862,6 +3968,7 @@ def DM_57_8_1(): G = AdditiveCyclic(57) return G, M + def DM_60_6_1(): r""" Return a `(60,6,1)`-difference matrix. @@ -3913,6 +4020,7 @@ def DM_60_6_1(): return G, M60b + def DM_75_8_1(): r""" Return a `(75,8,1)`-difference matrix. @@ -3961,6 +4069,7 @@ def DM_75_8_1(): return G, Mb + def DM_273_17_1(): r""" Return a `(273,17,1)`-difference matrix. @@ -3989,6 +4098,7 @@ def DM_273_17_1(): G = AdditiveCyclic(273) return G, M + def DM_993_32_1(): r""" Return a `(993,32,1)`-difference matrix. @@ -4104,7 +4214,7 @@ def RBIBD_120_8_1(): # A (precomputed) set that every block of the BIBD intersects on 0 or 2 points hyperoval = [128, 192, 194, 4, 262, 140, 175, 48, 81, 180, 245, 271, 119, 212, 249, 189, 62, 255] - #for B in BIBD: + # for B in BIBD: # len_trace = sum(x in hyperoval for x in B) # assert len_trace == 0 or len_trace == 2 @@ -4131,6 +4241,7 @@ def RBIBD_120_8_1(): equiv = [[M.nonzero_positions_in_row(x) for x in S] for S in equiv] return [B for S in equiv for B in S] + def BIBD_45_9_8(from_code=False): r""" Return a `(45,9,1)`-BIBD. @@ -4215,7 +4326,7 @@ def BIBD_66_6_1(): Return a (66,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: @@ -4239,7 +4350,7 @@ def BIBD_76_6_1(): Return a (76,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: @@ -4263,7 +4374,7 @@ def BIBD_96_6_1(): Return a (96,6,1)-BIBD. This BIBD was obtained from La Jolla covering repository - (https://math.ccrwest.org/cover.html) where it is attributed to Colin Barker. + (https://dmgordon.org/cover) where it is attributed to Colin Barker. EXAMPLES:: @@ -4304,6 +4415,7 @@ def BIBD_106_6_1(): return [[((x+i) % 53+y*53) for x,y in B] for i in range(53) for B in bibd] + def BIBD_111_6_1(): r""" Return a (111,6,1)-BIBD. @@ -4362,6 +4474,7 @@ def BIBD_126_6_1(): return [[x+y*5+z*25 for x,y,z in B] for B in bibd] + def BIBD_136_6_1(): r""" Return a (136,6,1)-BIBD. @@ -4507,13 +4620,9 @@ def HigmanSimsDesign(): from sage.combinat.designs.block_design import WittDesign from .incidence_structures import IncidenceStructure W = WittDesign(24) - a,b = 0,1 - Wa = [set(B) for B in W - if (a in B and - b not in B)] - Wb = [set(B) for B in W - if (b in B and - a not in B)] + a, b = 0, 1 + Wa = [set(B) for B in W if a in B and b not in B] + Wb = [set(B) for B in W if b in B and a not in B] H = [[i for i, A in enumerate(Wa) if len(A & B) != 2] for B in Wb] @@ -4669,6 +4778,7 @@ def BIBD_79_13_2(): libgap.unset_global("p4Act") return [[int(t)-1 for t in y] for y in blocks] + def BIBD_56_11_2(): r""" Return a symmetric `(56,11,2)`-BIBD. diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 8bf7f14fd0b..b5135151866 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -72,7 +72,7 @@ REFERENCES: .. [1] La Jolla Covering Repository, - https://math.ccrwest.org/cover.html + https://dmgordon.org/cover """ from sage.misc.lazy_import import lazy_import diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index 20839d9c8aa..c862821330e 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -289,14 +289,14 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O for R in OA: if len(R) != k: if verbose: - print({"OA" : "Some row does not have length "+str(k), - "MOLS" : "The number of squares is not "+str(k-2)}[terminology]) + print({"OA": "Some row does not have length "+str(k), + "MOLS": "The number of squares is not "+str(k-2)}[terminology]) return False if len(OA) != n2: if verbose: - print({"OA" : "The number of rows is {} instead of {}^2={}".format(len(OA),n,n2), - "MOLS" : "All squares do not have dimension n^2={}^2".format(n)}[terminology]) + print({"OA": "The number of rows is {} instead of {}^2={}".format(len(OA),n,n2), + "MOLS": "All squares do not have dimension n^2={}^2".format(n)}[terminology]) return False if n == 0: @@ -319,8 +319,8 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O for j,x in enumerate(R): if x < 0 or x >= n: if verbose: - print({"OA" : "{} is not in the interval [0..{}]".format(x,n-1), - "MOLS" : "Entry {} was expected to be in the interval [0..{}]".format(x,n-1)}[terminology]) + print({"OA": "{} is not in the interval [0..{}]".format(x,n-1), + "MOLS": "Entry {} was expected to be in the interval [0..{}]".format(x,n-1)}[terminology]) sig_free(OAc) return False OAc[j*n2+i] = x @@ -341,8 +341,8 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O sig_free(OAc) bitset_free(seen) if verbose: - print({"OA" : "Columns {} and {} are not orthogonal".format(i,j), - "MOLS" : "Squares {} and {} are not orthogonal".format(i,j)}[terminology]) + print({"OA": "Columns {} and {} are not orthogonal".format(i,j), + "MOLS": "Squares {} and {} are not orthogonal".format(i,j)}[terminology]) return False sig_free(OAc) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index bb8bb30d5bc..49b25c12dd0 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -85,6 +85,7 @@ def group_law(G): else: raise ValueError("%s does not seem to be a group" % G) + def block_stabilizer(G, B): r""" Compute the left stabilizer of the block ``B`` under the action of ``G``. @@ -122,8 +123,8 @@ def block_stabilizer(G, B): S = [] for b in B: # fun: if we replace +(-b) with -b it completely fails!! - bb0 = op(b,b0) # bb0 = b-B[0] - if all(op(bb0,c) in B for c in B): + bb0 = op(b, b0) # bb0 = b-B[0] + if all(op(bb0, c) in B for c in B): S.append(bb0) return S @@ -278,7 +279,7 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): for c in d: if b == c: continue - gg = mul(b,inv(c)) # = b-c or bc^{-1} + gg = mul(b, inv(c)) # = b-c or bc^{-1} if gg not in tmp_counter: tmp_counter[gg] = 0 where[gg].add(i) @@ -318,7 +319,7 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): g, counter[g], sorted(where[g]))) if too_much: print("Too much:") - for g in too_much: + for g in too_much: print(" {} is obtained {} times in blocks {}".format( g, counter[g], sorted(where[g]))) if too_few or too_much: @@ -328,6 +329,7 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): print("It is a ({},{},{})-difference family".format(v, k, l)) return True + def singer_difference_set(q,d): r""" Return a difference set associated to the set of hyperplanes in a projective @@ -386,9 +388,10 @@ def singer_difference_set(q,d): # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a # GF(q**(d+1)) and such that x is a multiplicative generator. p,e = q.factor()[0] - c = conway_polynomial(p,e*(d+1)) - if e != 1: # i.e. q is not a prime, so we factorize c over GF(q) and pick - # one of its factor + c = conway_polynomial(p, e*(d+1)) + if e != 1: + # i.e. q is not a prime, so we factorize c over GF(q) and pick + # one of its factor K = GF(q,'z') c = c.change_ring(K).factor()[0][0] else: @@ -411,6 +414,7 @@ def singer_difference_set(q,d): return Zmod((q**(d+1)-1)//(q-1)), [powers] + def df_q_6_1(K, existence=False, check=True): r""" Return a `(q,6,1)`-difference family over the finite field `K`. @@ -451,7 +455,7 @@ def df_q_6_1(K, existence=False, check=True): xx = x**5 to_coset = {x**i * xx**j: i for i in range(5) for j in range((v-1)/5)} - for c in to_coset: # the loop runs through all nonzero elements of K + for c in to_coset: # the loop runs through all nonzero elements of K if c == one or c == r or c == r2: continue if len(set(to_coset[elt] for elt in (r-one, c*(r-one), c-one, c-r, c-r**2))) == 5: @@ -470,6 +474,7 @@ def df_q_6_1(K, existence=False, check=True): return D + def radical_difference_set(K, k, l=1, existence=False, check=True): r""" Return a difference set made of a cyclotomic coset in the finite field @@ -635,6 +640,7 @@ def radical_difference_set(K, k, l=1, existence=False, check=True): return D + def one_cyclic_tiling(A,n): r""" Given a subset ``A`` of the cyclic additive group `G = Z / nZ` return @@ -698,6 +704,7 @@ def one_cyclic_tiling(A,n): for c in M: return [i-1 for i in c] + def one_radical_difference_family(K, k): r""" Search for a radical difference family on ``K`` using dancing links @@ -790,13 +797,13 @@ def one_radical_difference_family(K, k): A = [r**i - 1 for i in range(1,m+1)] else: m = k // 2 - r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity + r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity A = [r**i - 1 for i in range(1,m)] A.append(K.one()) # instead of the complicated multiplicative group K^*/(±C) we use the # discrete logarithm to convert everything into the additive group Z/cZ - c = m * (q-1) // e # cardinal of ±C + c = m * (q-1) // e # cardinal of ±C from sage.groups.generic import discrete_log logA = [discrete_log(a,x) % c for a in A] @@ -815,6 +822,7 @@ def one_radical_difference_family(K, k): d.insert(K.zero(),0) return D + def radical_difference_family(K, k, l=1, existence=False, check=True): r""" Return a ``(v,k,l)``-radical difference family. @@ -925,6 +933,7 @@ def radical_difference_family(K, k, l=1, existence=False, check=True): return D + def twin_prime_powers_difference_set(p, check=True): r""" Return a difference set on `GF(p) \times GF(p+2)`. @@ -980,6 +989,7 @@ def twin_prime_powers_difference_set(p, check=True): return G, [d] + def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): r""" Test whether ``(v,k,lmbda)`` is a triple that can be obtained from the @@ -1027,18 +1037,18 @@ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): 96 20 4 4 1 """ if v <= k or k <= lmbda: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False k = ZZ(k) lmbda = ZZ(lmbda) - qs,r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s + qs, r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s if r or (qs*(qs-1)) % lmbda: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False q = qs*(qs-1) // lmbda + 1 if (q <= 1 or - v * (q-1) != qs*q * (qs*q+q-2) or + v * (q-1) != qs*q * (qs*q+q-2) or k * (q-1) != qs * (qs*q-1)): - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False # NOTE: below we compute the value of s so that qs = q^s. If the method # is_power_of of integers would be able to return the exponent, we could use @@ -1048,10 +1058,11 @@ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): p2,a2 = q.is_prime_power(get_data=True) if a1 == 0 or a2 == 0 or p1 != p2 or a1 % a2: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False return (True, (q, a1//a2)) if return_parameters else True + def mcfarland_1973_construction(q, s): r""" Return a difference set. @@ -1115,6 +1126,7 @@ def mcfarland_1973_construction(q, s): return G,[D] + def are_hadamard_difference_set_parameters(v, k, lmbda): r""" Check whether ``(v,k,lmbda)`` is of the form ``(4N^2, 2N^2 - N, N^2 - N)``. @@ -1135,6 +1147,7 @@ def are_hadamard_difference_set_parameters(v, k, lmbda): N2 = N*N return v == 4*N2 and k == 2*N2 - N and lmbda == N2 - N + @cached_function def hadamard_difference_set_product_parameters(N): r""" @@ -1178,6 +1191,7 @@ def hadamard_difference_set_product_parameters(N): return None + def hadamard_difference_set_product(G1, D1, G2, D2): r""" Make a product of two Hadamard difference sets. @@ -1217,6 +1231,7 @@ def hadamard_difference_set_product(G1, D1, G2, D2): return G, [[s for s in G if s not in D]] + def turyn_1965_3x3xK(k=4): r""" Return a difference set in either `C_3 \times C_3 \times C_4` or `C_3 \times @@ -1251,10 +1266,10 @@ def turyn_1965_3x3xK(k=4): else: raise ValueError("k must be 2 or 4") - L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0 - [(0,0),(1,1),(2,2)], # x-y=0 - [(0,0),(1,2),(2,1)], # x+y=0 - [(0,0),(0,1),(0,2)]] # x=0 + L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0 + [(0,0),(1,1),(2,2)], # x-y=0 + [(0,0),(1,2),(2,1)], # x+y=0 + [(0,0),(0,1),(0,2)]] # x=0 return G, [[G(v + k) for l, k in zip(L, K) for v in l]] @@ -1296,6 +1311,7 @@ def _is_periodic_sequence(seq, period): return False return True + def _create_m_sequence(q, n, check=True): r""" Create an m-sequence over GF(q) with period `q^n - 1`. @@ -1358,6 +1374,7 @@ def _create_m_sequence(q, n, check=True): assert _is_periodic_sequence(seq, period) return seq[:period] + def _get_submodule_of_order(G, order): r""" Construct a submodule of the given order from group ``G``. @@ -1387,6 +1404,7 @@ def _get_submodule_of_order(G, order): return H return None + def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False): r""" Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where ``q`` is a prime power and `N\ge 2`. @@ -1462,6 +1480,7 @@ def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False return G, set1 return set1 + def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group=False): r""" Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}d)` where `nd = q-1`. @@ -1817,7 +1836,7 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing P = PolynomialRing(ZZ, 'x') - #Compute psi3, psi4 + # Compute psi3, psi4 hall = 0 for d in set1: hall += P.monomial(d[0]) @@ -3671,7 +3690,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch 3 Turyn 1965 construction 4 McFarland 1973 construction 5 False - 6 Unknown + 6 The database contains a (144,66,30)-difference family 7 False 8 McFarland 1973 construction 9 Unknown diff --git a/src/sage/combinat/designs/difference_matrices.py b/src/sage/combinat/designs/difference_matrices.py index 5cebb84e439..11fe16bb8d5 100644 --- a/src/sage/combinat/designs/difference_matrices.py +++ b/src/sage/combinat/designs/difference_matrices.py @@ -19,6 +19,7 @@ from .designs_pyx import is_difference_matrix from .database import DM as DM_constructions + @cached_function def find_product_decomposition(g, k, lmbda=1): r""" @@ -71,6 +72,7 @@ def find_product_decomposition(g, k, lmbda=1): return False + def difference_matrix_product(k, M1, G1, lmbda1, M2, G2, lmbda2, check=True): r""" Return the product of the ``(G1, k, lmbda1)`` and ``(G2, k, lmbda2)`` @@ -121,6 +123,7 @@ def difference_matrix_product(k, M1, G1, lmbda1, M2, G2, lmbda2, check=True): return G,M + def difference_matrix(g,k,lmbda=1,existence=False,check=True): r""" Return a `(g,k,\lambda)`-difference matrix. diff --git a/src/sage/combinat/designs/evenly_distributed_sets.pyx b/src/sage/combinat/designs/evenly_distributed_sets.pyx index 744cb4e3513..1d1a7acb8ec 100644 --- a/src/sage/combinat/designs/evenly_distributed_sets.pyx +++ b/src/sage/combinat/designs/evenly_distributed_sets.pyx @@ -218,24 +218,24 @@ cdef class EvenlyDistributedSetsBacktracker: raise ValueError(f"{K} is not a field") cdef unsigned int q = K.cardinality() cdef unsigned int e = k*(k-1)/2 - if (q-1) % (2*e) != 0: + if (q-1) % (2*e): raise ValueError("k(k-1)={} does not divide q-1={}".format(k*(k-1),q-1)) - cdef unsigned int m = (q-1)/e + cdef unsigned int m = (q - 1) // e self.q = q self.e = e self.k = k - self.m = (q-1) / e + self.m = (q - 1) // e self.K = K self.diff = check_calloc(q, sizeof(unsigned int *)) self.diff[0] = check_malloc(q*q*sizeof(unsigned int)) - for i in range(1,self.q): + for i in range(1, self.q): self.diff[i] = self.diff[i-1] + q self.ratio = check_calloc(q, sizeof(unsigned int *)) self.ratio[0] = check_malloc(q*q*sizeof(unsigned int)) - for i in range(1,self.q): + for i in range(1, self.q): self.ratio[i] = self.ratio[i-1] + q self.B = check_malloc(k*sizeof(unsigned int)) diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index 9d04b4f5c66..93f55f6daed 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -466,6 +466,7 @@ """ + def dump_to_tmpfile(s): """ Utility function to dump a string to a temporary file. @@ -483,6 +484,7 @@ def dump_to_tmpfile(s): f.close() return file_loc + def check_dtrs_protocols(input_name, input_pv): """ Check that the XML data is in a valid format. We can currently @@ -506,6 +508,7 @@ def check_dtrs_protocols(input_name, input_pv): msg = ('''Incompatible dtrs_protocols: program: %s %s: %s''' % (program_pv, input_name, input_pv)) raise RuntimeError(msg) + def open_extrep_file(fname): """ Try to guess the compression type from extension @@ -534,6 +537,7 @@ def open_extrep_file(fname): f = open(fname, 'rb') return f + def open_extrep_url(url): """ Try to guess the compression type from extension @@ -569,6 +573,7 @@ def open_extrep_url(url): pattern_decimal = re.compile(r'-?\d+\.\d+$') pattern_rational = re.compile(r'-?\d+/\d+$') + def _encode_attribute(string): """ Convert numbers in attributes into binary format. @@ -600,6 +605,7 @@ def _encode_attribute(string): else: return string + class XTree: ''' A lazy class to wrap a rooted tree representing an XML document. @@ -773,6 +779,7 @@ def __len__(self): return len(self.xt_children) + class XTreeProcessor: ''' An incremental event-driven parser for ext-rep documents. @@ -862,11 +869,11 @@ def _start_element(self, name, attrs): check_dtrs_protocols('source', attrs['dtrs_protocol']) if self.list_of_designs_start_proc: self.list_of_designs_start_proc(attrs) - #self.outf.write('<%s' % name) - #pp_attributes(self.outf, attrs, indent='', precision_stack=[]) - #self.outf.write('>\n') + # self.outf.write('<%s' % name) + # pp_attributes(self.outf, attrs, indent='', precision_stack=[]) + # self.outf.write('>\n') elif name == 'designs': - pass # self.outf.write(' <%s>\n' % name) + pass # self.outf.write(' <%s>\n' % name) if self.in_item: for k, v in attrs.items(): attrs[k] = _encode_attribute(v) @@ -909,7 +916,7 @@ def _end_element(self, name): if name == 'block' or name == 'permutation' \ or name == 'preimage' or name == 'ksubset' \ or name == 'cycle_type' or name == 'row': - # these enclose lists of numbers + # these enclose lists of numbers children.append(ps) else: # the rest is a single number @@ -922,19 +929,19 @@ def _end_element(self, name): if self.save_designs: init_bd = XTree(self.current_node[2][0]) self.list_of_designs.append((init_bd.v, list(init_bd.blocks))) - #print_subxt(self.current_node[2][0], level=2, outf=self.outf) + # print_subxt(self.current_node[2][0], level=2, outf=self.outf) self._init() elif name == 'info': if self.info_proc: self.info_proc(self.current_node[2][0]) - #print_subxt(self.current_node[2][0], level=1, outf=self.outf) + # print_subxt(self.current_node[2][0], level=1, outf=self.outf) self._init() else: if name == 'designs': if self.designs_end_proc: self.designs_end_proc() - #self.outf.write(' ') - #self.outf.write('\n' % name) + # self.outf.write(' ') + # self.outf.write('\n' % name) def _char_data(self, data): """ @@ -955,9 +962,8 @@ def _char_data(self, data): {'b': 26, 'id': 't2-v13-b26-r6-k3-L1-0', 'v': 13}, ['[ DESIGN-1.1, GRAPE-4.2, GAPDoc-0.9999, GAP-4.4.3]']) """ - if self.in_item: - #@ this stripping may distort char data in the subtree + # @ this stripping may distort char data in the subtree # if they are not bracketed in some way. data = data.strip() if data: @@ -1030,6 +1036,7 @@ def designs_from_XML(fname): return proc.list_of_designs + def designs_from_XML_url(url): """ Return a list of designs contained in an XML file named by a URL. diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 148cc11fdfa..e9964af900c 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -142,7 +142,7 @@ class IncidenceStructure: True """ def __init__(self, points=None, blocks=None, incidence_matrix=None, - name=None, check=True, copy=True): + name=None, check=True, copy=True): r""" TESTS:: @@ -271,7 +271,7 @@ def __repr__(self): Incidence structure with 7 points and 7 blocks """ return 'Incidence structure with {} points and {} blocks'.format( - self.num_points(), self.num_blocks()) + self.num_points(), self.num_blocks()) __str__ = __repr__ @@ -309,7 +309,7 @@ def __eq__(self, other): return self._blocks == other._blocks if (self.num_points() != other.num_points() or - self.num_blocks() != other.num_blocks()): + self.num_blocks() != other.num_blocks()): return False p_to_i = self._point_to_index if self._point_to_index else list(range(self.num_points())) @@ -409,12 +409,12 @@ def canonical_label(self): from sage.graphs.graph import Graph g = Graph() n = self.num_points() - g.add_edges((i+n,x) for i,b in enumerate(self._blocks) for x in b) - canonical_label = g.canonical_label([list(range(n)),list(range(n,n+self.num_blocks()))],certificate=True)[1] + g.add_edges((i+n, x) for i, b in enumerate(self._blocks) for x in b) + canonical_label = g.canonical_label([list(range(n)), list(range(n, n+self.num_blocks()))], certificate=True)[1] canonical_label = [canonical_label[x] for x in range(n)] self._canonical_label = canonical_label - return dict(zip(self._points,self._canonical_label)) + return dict(zip(self._points, self._canonical_label)) def is_isomorphic(self, other, certificate=False): r""" @@ -475,25 +475,25 @@ def is_isomorphic(self, other, certificate=False): """ if (self.num_points() != other.num_points() or self.num_blocks() != other.num_blocks() or - sorted(self.block_sizes()) != sorted(other.block_sizes())): + sorted(self.block_sizes()) != sorted(other.block_sizes())): return {} if certificate else False A_canon = self.canonical_label() B_canon = other.canonical_label() - A = self.relabel(A_canon,inplace=False) - B = other.relabel(B_canon,inplace=False) + A = self.relabel(A_canon, inplace=False) + B = other.relabel(B_canon, inplace=False) if A == B: if certificate: - B_canon_rev = {y:x for x,y in B_canon.items()} - return {x:B_canon_rev[xint] for x,xint in A_canon.items()} + B_canon_rev = {y: x for x, y in B_canon.items()} + return {x: B_canon_rev[xint] for x, xint in A_canon.items()} else: return True else: return {} if certificate else False - def isomorphic_substructures_iterator(self, H2,induced=False): + def isomorphic_substructures_iterator(self, H2, induced=False): r""" Iterate over all copies of ``H2`` contained in ``self``. @@ -560,7 +560,7 @@ def isomorphic_substructures_iterator(self, H2,induced=False): 5616 """ from sage.combinat.designs.subhypergraph_search import SubHypergraphSearch - return SubHypergraphSearch(self,H2,induced=induced) + return SubHypergraphSearch(self, H2, induced=induced) def copy(self): r""" @@ -581,7 +581,7 @@ def copy(self): IS = IncidenceStructure(self._blocks, name=self._name, check=False) - IS.relabel(dict(zip(range(self.num_points()),self._points))) + IS.relabel(dict(zip(range(self.num_points()), self._points))) IS._canonical_label = None if self._canonical_label is None else self._canonical_label[:] return IS @@ -723,7 +723,7 @@ def trace(self, points, min_size=1, multiset=True): if not multiset: blocks = set(blocks) IS = IncidenceStructure(blocks) - IS.relabel({i:self._points[i] for i in int_points}) + IS.relabel({i: self._points[i] for i in int_points}) return IS def ground_set(self): @@ -830,7 +830,7 @@ def degree(self, p=None, subset=False): # degree of a point if not subset: if self._point_to_index: - p = self._point_to_index.get(p,-1) + p = self._point_to_index.get(p, -1) else: p = p if (p >= 0 and p < len(self._points)) else -1 return sum((p in b) for b in self._blocks) if p != -1 else 0 @@ -838,7 +838,7 @@ def degree(self, p=None, subset=False): # degree of a set else: if self._point_to_index: - p = set(self._point_to_index.get(x,-1) for x in p) + p = set(self._point_to_index.get(x, -1) for x in p) else: p = set(p) if all(x >= 0 and x < len(self._points) for x in p) else set([-1]) @@ -888,12 +888,12 @@ def degrees(self, size=None): return {p: d[i] for i, p in enumerate(self._points)} else: from itertools import combinations - d = {t:0 for t in combinations(range(self.num_points()),size)} + d = {t: 0 for t in combinations(range(self.num_points()), size)} for b in self._blocks: - for s in combinations(b,size): + for s in combinations(b, size): d[s] += 1 if self._point_to_index: - return {tuple([self._points[x] for x in s]):v for s,v in d.items()} + return {tuple([self._points[x] for x in s]): v for s, v in d.items()} else: return d @@ -1112,8 +1112,8 @@ def intersection_graph(self, sizes=None): sizes = PositiveIntegers() elif sizes in PositiveIntegers(): sizes = (sizes,) - V = [Set(v) for v in self] - return Graph([V, lambda x,y: len(x & y) in sizes], loops=False) + V = [Set(v) for v in self] + return Graph([V, lambda x, y: len(x & y) in sizes], loops=False) def incidence_matrix(self): r""" @@ -1150,7 +1150,7 @@ def incidence_matrix(self): A[i, j] = 1 return A - def incidence_graph(self,labels=False): + def incidence_graph(self, labels=False): r""" Return the incidence graph of the incidence structure. @@ -1201,7 +1201,7 @@ def incidence_graph(self,labels=False): for b in self.blocks(): b = Set(b) G.add_vertex(b) - G.add_edges((b,x) for x in b) + G.add_edges((b, x) for x in b) return G else: @@ -1243,7 +1243,7 @@ def is_berge_cyclic(self): return not self.incidence_graph().is_forest() - def complement(self,uniform=False): + def complement(self, uniform=False): r""" Return the complement of the incidence structure. @@ -1303,7 +1303,7 @@ def complement(self,uniform=False): num_blocks = self.num_blocks() i = 0 from itertools import combinations - for B in combinations(range(self.num_points()),k): + for B in combinations(range(self.num_points()), k): B = list(B) while i < num_blocks and self._blocks[i] < B: i += 1 @@ -1311,12 +1311,12 @@ def complement(self,uniform=False): i += 1 continue blocks.append(B) - I = IncidenceStructure(blocks,copy=False) + I = IncidenceStructure(blocks, copy=False) else: X = set(range(self.num_points())) I = IncidenceStructure([X.difference(B) for B in self._blocks]) - I.relabel({i:self._points[i] for i in range(self.num_points())}) + I.relabel({i: self._points[i] for i in range(self.num_points())}) return I def relabel(self, perm=None, inplace=True): @@ -1387,7 +1387,7 @@ def relabel(self, perm=None, inplace=True): self._point_to_index = None return - if isinstance(perm, (list,tuple)): + if isinstance(perm, (list, tuple)): perm = dict(zip(self._points, perm)) if not isinstance(perm, dict): @@ -1608,32 +1608,32 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): b = self.num_blocks() # Trivial wrong answers - if (any(len(block) != k for block in self._blocks) or # non k-uniform - v != self.num_points()): - return (False, (0,0,0,0)) if return_parameters else False + if (any(len(block) != k for block in self._blocks) or # non k-uniform + v != self.num_points()): + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case t>k if (t is not None and t > k): if (l is None or l == 0): - return (True, (t,v,k,0)) if return_parameters else True + return (True, (t, v, k, 0)) if return_parameters else True else: - return (False, (0,0,0,0)) if return_parameters else False + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case k=0 if k == 0: if (l is None or l == 0): - return (True, (0,v,k,b)) if return_parameters else True + return (True, (0, v, k, b)) if return_parameters else True else: - return (False, (0,0,0,0)) if return_parameters else False + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case k=v (includes v=0) if k == v: if t is None: t = v if l is None or b == l: - return (True, (t,v,k,b)) if return_parameters else True + return (True, (t, v, k, b)) if return_parameters else True else: - return (True, (0,0,0,0)) if return_parameters else False + return (True, (0, 0, 0, 0)) if return_parameters else False # Handbook of combinatorial design theorem II.4.8: # @@ -1642,30 +1642,30 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): # # We look for the largest t such that self is a t-design from itertools import combinations - for tt in (range(1,k+1) if t is None else [t]): + for tt in (range(1, k + 1) if t is None else [t]): # is lambda an integer? - if (b*binomial(k,tt)) % binomial(v,tt) != 0: + if (b * binomial(k, tt)) % binomial(v, tt): tt -= 1 break s = {} for block in self._blocks: - for i in combinations(block,tt): - s[i] = s.get(i,0) + 1 + for i in combinations(block, tt): + s[i] = s.get(i, 0) + 1 if len(set(s.values())) != 1: tt -= 1 break - ll = b*binomial(k,tt) // binomial(v,tt) + ll = (b * binomial(k, tt)) // binomial(v, tt) if ((t is not None and t != tt) or - (l is not None and l != ll)): - return (False, (0,0,0,0)) if return_parameters else False + (l is not None and l != ll)): + return (False, (0, 0, 0, 0)) if return_parameters else False else: if tt == 0: ll = b - return (True, (tt,v,k,ll)) if return_parameters else True + return (True, (tt, v, k, ll)) if return_parameters else True def is_generalized_quadrangle(self, verbose=False, parameters=False): r""" @@ -1758,9 +1758,9 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): if parameters: s = self.is_uniform() t = self.is_regular() - s = s-1 if (s is not False and s >= 2) else False - t = t-1 if (t is not False and t >= 2) else False - return (s,t) + s = s - 1 if (s is not False and s >= 2) else False + t = t - 1 if (t is not False and t >= 2) else False + return (s, t) else: return True @@ -1812,10 +1812,10 @@ def dual(self, algorithm=None): v = DD['v'].sage() gB = [[x - 1 for x in b] for b in DD['blocks'].sage()] return IncidenceStructure(list(range(v)), gB, name=None, check=False) - else: - return IncidenceStructure( - incidence_matrix=self.incidence_matrix().transpose(), - check=False) + + return IncidenceStructure( + incidence_matrix=self.incidence_matrix().transpose(), + check=False) def automorphism_group(self): r""" @@ -1856,9 +1856,9 @@ def automorphism_group(self): from sage.groups.perm_gps.permgroup import PermutationGroup g = Graph() n = self.num_points() - g.add_edges((i+n,x) for i,b in enumerate(self._blocks) for x in b) + g.add_edges((i + n, x) for i, b in enumerate(self._blocks) for x in b) ag = g.automorphism_group(partition=[list(range(n)), - list(range(n,n+self.num_blocks()))]) + list(range(n, n + self.num_blocks()))]) if self._point_to_index: gens = [[tuple([self._points[i] for i in cycle if (not cycle or cycle[0] < n)]) @@ -1971,18 +1971,18 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, # Lists of blocks containing i for every i dual = [[] for _ in domain] - for i,B in enumerate(self._blocks): + for i, B in enumerate(self._blocks): for x in B: dual[x].append(i) # Each class is a partition for t in range(n_classes): for x in domain: - p.add_constraint(p.sum(b[t,i] for i in dual[x]) == 1) + p.add_constraint(p.sum(b[t, i] for i in dual[x]) == 1) # Each set appears exactly once for i in range(len(self._blocks)): - p.add_constraint(p.sum(b[t,i] for t in range(n_classes)) == 1) + p.add_constraint(p.sum(b[t, i] for t in range(n_classes)) == 1) try: p.solve(log=verbose) @@ -1998,8 +1998,8 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, if check and self._classes is not False: assert sorted(id(c) for cls in self._classes for c in cls) == sorted(id(b) for b in self._blocks), "some set does not appear exactly once" domain = list(range(self.num_points())) - for i,c in enumerate(self._classes): - assert sorted(sum(c,[])) == domain, "class {} is not a partition".format(i) + for i, c in enumerate(self._classes): + assert sorted(sum(c, [])) == domain, "class {} is not a partition".format(i) if self._classes is False: return (False, []) if certificate else False @@ -2069,7 +2069,7 @@ def coloring(self, k=None, solver=None, verbose=0, 3 """ if k is None: - for k in range(self.num_points()+1): + for k in range(self.num_points() + 1): try: return self.coloring(k) except ValueError: @@ -2093,11 +2093,11 @@ def coloring(self, k=None, solver=None, verbose=0, b = p.new_variable(binary=True) for x in range(self.num_points()): - p.add_constraint(p.sum(b[x,i] for i in range(k)) == 1) + p.add_constraint(p.sum(b[x, i] for i in range(k)) == 1) for s in self._blocks: for i in range(k): - p.add_constraint(p.sum(b[x,i] for x in s) <= len(s)-1) + p.add_constraint(p.sum(b[x, i] for x in s) <= len(s) - 1) try: p.solve(log=verbose) @@ -2106,13 +2106,13 @@ def coloring(self, k=None, solver=None, verbose=0, col = [[] for _ in range(k)] - for (x,i),v in p.get_values(b, convert=bool, tolerance=integrality_tolerance).items(): + for (x, i), v in p.get_values(b, convert=bool, tolerance=integrality_tolerance).items(): if v: col[i].append(self._points[x]) return col - def edge_coloring(self): + def edge_coloring(self) -> list: r""" Compute a proper edge-coloring. @@ -2185,13 +2185,13 @@ def _spring_layout(self): for x in s: g.add_edge((0, s), (1, x)) - _ = g.plot(iterations=50000,save_pos=True) + _ = g.plot(iterations=50000, save_pos=True) # The values are rounded as TikZ does not like accuracy. return {k[1]: (round(x, 3), round(y, 3)) for k, (x, y) in g.get_pos().items()} - def _latex_(self): + def _latex_(self) -> str: r""" Return a TikZ representation of the incidence structure. @@ -2246,11 +2246,12 @@ def _latex_(self): pos = self._spring_layout() tex = "\\begin{tikzpicture}[scale=3]\n" - colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"] - colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S] + colors = ["black", "red", "green", "blue", "cyan", + "magenta", "yellow", "pink", "brown"] + colored_sets = [(s, i) for i, S in enumerate(self.edge_coloring()) for s in S] # Prints each set with its color - for s,i in colored_sets: + for s, i in colored_sets: current_color = colors[i % len(colors)] if len(s) == 2: @@ -2283,7 +2284,7 @@ def _latex_(self): tex += "\\end{tikzpicture}" return tex - def is_spread(self, spread): + def is_spread(self, spread) -> bool: r""" Check whether the input is a spread for ``self``. @@ -2340,10 +2341,7 @@ def is_spread(self, spread): points.difference_update(sblock) - if points: - return False - - return True + return not points from sage.misc.rest_index_of_methods import gen_rest_table_index diff --git a/src/sage/combinat/designs/meson.build b/src/sage/combinat/designs/meson.build new file mode 100644 index 00000000000..fd3a8896bb3 --- /dev/null +++ b/src/sage/combinat/designs/meson.build @@ -0,0 +1,45 @@ +py.install_sources( + 'MOLS_handbook_data.py', + 'all.py', + 'bibd.py', + 'block_design.py', + 'covering_array.py', + 'covering_design.py', + 'database.py', + 'design_catalog.py', + 'designs_pyx.pxd', + 'difference_family.py', + 'difference_matrices.py', + 'ext_rep.py', + 'group_divisible_designs.py', + 'incidence_structures.py', + 'latin_squares.py', + 'orthogonal_arrays.py', + 'orthogonal_arrays_build_recursive.py', + 'resolvable_bibd.py', + 'steiner_quadruple_systems.py', + 'twographs.py', + subdir: 'sage/combinat/designs', +) + +extension_data = { + 'designs_pyx' : files('designs_pyx.pyx'), + 'evenly_distributed_sets' : files('evenly_distributed_sets.pyx'), + 'gen_quadrangles_with_spread' : files('gen_quadrangles_with_spread.pyx'), + 'orthogonal_arrays_find_recursive' : files( + 'orthogonal_arrays_find_recursive.pyx', + ), + 'subhypergraph_search' : files('subhypergraph_search.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/designs', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index ae471d37065..daac0f322cf 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -955,12 +955,12 @@ def orthogonal_array(k,n,t=2,resolvable=False, check=True,existence=False,explai # Constructions from the database III (Quasi-difference matrices) elif (may_be_available and - (n,1) in QDM and + (n, 1) in QDM and any(kk >= k and mu <= lmbda and (orthogonal_array(k,u,existence=True) is True) for (_,lmbda,mu,u),(kk,_) in QDM[n,1].items())): _OA_cache_set(k,n,True) - for (nn,lmbda,mu,u),(kk,f) in QDM[n,1].items(): - if (kk >= k and + for (nn, lmbda, mu, u), (kk, f) in QDM[n,1].items(): + if (kk >= k and mu <= lmbda and (orthogonal_array(k,u,existence=True) is True)): if existence: @@ -1236,11 +1236,11 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): raise EmptySetError("The total size of holes must be smaller or equal than the size of the ground set") if (max_hole == 1 and - resolvable and + resolvable and sum_of_holes != n): if existence: return False - raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {} equivalent to OA(k+1,n) if max_hole == 1 and resolvable: @@ -1366,11 +1366,11 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): # Equal holes [h,h,...] with h>1 through OA product construction # # (i.e. OA(k,n1)-x.OA(k,1) and OA(k,n2) ==> OA(k,n1.n2)-x.OA(k,n2) ) - elif (min_hole > 1 and - max_hole == min_hole and - n % min_hole == 0 and # h divides n + elif (min_hole > 1 and + max_hole == min_hole and + n % min_hole == 0 and # h divides n orthogonal_array(k,min_hole,existence=True) and # OA(k,h) - incomplete_orthogonal_array(k,n//min_hole,[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) + incomplete_orthogonal_array(k,n//min_hole,[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) if existence: return True h = min_hole @@ -1540,7 +1540,7 @@ def OA_relabel(OA, k, n, blocks=tuple(), matrix=None, symbol_list=None): """ if blocks: l = [] - for i,B in enumerate(zip(*blocks)): # the blocks are disjoint + for i, B in enumerate(zip(*blocks)): # the blocks are disjoint if len(B) != len(set(B)): raise RuntimeError("Two block have the same coordinate for one of the k dimensions") @@ -1941,7 +1941,7 @@ def QDM_from_Vmt(m,t,V): for e in V: L.append(e*wm**i) for ii in range(m+2): - M.append(L[-ii:]+L[:-ii]) # cyclic shift + M.append(L[-ii:]+L[:-ii]) # cyclic shift M.append([0]*(m+2)) @@ -2054,6 +2054,7 @@ def OA_from_wider_OA(OA,k): return OA return [L[:k] for L in OA] + class OAMainFunctions: r""" Functions related to orthogonal arrays. diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 5d71fbb12c8..937076ebb07 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -96,6 +96,7 @@ def construction_3_3(k,n,m,i,explain_construction=False): assert is_orthogonal_array(OA,k,n*m+i) return OA + def construction_3_4(k,n,m,r,s,explain_construction=False): r""" Return a `OA(k,nm+rs)`. @@ -178,6 +179,7 @@ def construction_3_4(k,n,m,r,s,explain_construction=False): OA = wilson_construction(OA,k,n,m,[1]*r+[s],check=False) return OA + def construction_3_5(k,n,m,r,s,t,explain_construction=False): r""" Return an `OA(k,nm+r+s+t)`. @@ -272,6 +274,7 @@ def construction_3_5(k,n,m,r,s,t,explain_construction=False): OA = wilson_construction(OA,k,q,m,[r,s,t], check=False) return OA + def construction_3_6(k,n,m,i,explain_construction=False): r""" Return a `OA(k,nm+i)`. @@ -328,6 +331,7 @@ def construction_3_6(k,n,m,i,explain_construction=False): assert is_orthogonal_array(OA,k,n*m+i) return OA + def OA_and_oval(q, *, solver=None, integrality_tolerance=1e-3): r""" Return a `OA(q+1,q)` whose blocks contains `\leq 2` zeroes in the last `q` @@ -533,9 +537,9 @@ def construction_q_x(k, q, x, check=True, explain_construction=False): # Add rows, extended with p1 and p2 p1 = q**2 - p2 = p1+1 - TD.extend([[ii*q+i for ii in range(q)]+[p1] for i in range(1,q)]) - TD.append( [ii*q for ii in range(q)]+[p1,p2]) + p2 = p1 + 1 + TD.extend([ii*q + i for ii in range(q)] + [p1] for i in range(1, q)) + TD.append([ii*q for ii in range(q)] + [p1, p2]) # Add Columns. We do not add some columns which would have size 1 after we # delete points. @@ -752,6 +756,7 @@ def thwart_lemma_3_5(k,n,m,a,b,c,d=0,complement=False,explain_construction=False return wilson_construction(OA,k,n,m,sizes, check=False) + def thwart_lemma_4_1(k,n,m,explain_construction=False): r""" Return an `OA(k,nm+4(n-2))`. @@ -880,6 +885,7 @@ def thwart_lemma_4_1(k,n,m,explain_construction=False): return wilson_construction(OA,k,n,m,[n-2,]*4,check=False) + def three_factor_product(k,n1,n2,n3,check=False,explain_construction=False): r""" Return an `OA(k+1,n_1n_2n_3)`. @@ -1145,6 +1151,7 @@ def product_with_parallel_classes(OA1,k,g1,g2,g1_parall,parall,check=True): return OA + def _reorder_matrix(matrix): r""" Return a matrix which is obtained from ``matrix`` by permutation of each row @@ -1195,6 +1202,7 @@ def _reorder_matrix(matrix): return list(zip(*matrix)) + def brouwer_separable_design(k,t,q,x,check=False,verbose=False,explain_construction=False): r""" Return a `OA(k,t(q^2+q+1)+x)` using Brouwer's result on separable designs. @@ -1445,9 +1453,9 @@ def brouwer_separable_design(k,t,q,x,check=False,verbose=False,explain_construct else: partition_of_blocks_of_size_t[plane-t].append([relabel[xx] for xx in B if xx % m < t]) - ############################################################################### + ########################################################################### # Separable design built ! - #------------------------- + # ------------------------ # # At this point we have a PBD on t*(q**2+q+1) points. Its blocks are # split into: @@ -1458,7 +1466,7 @@ def brouwer_separable_design(k,t,q,x,check=False,verbose=False,explain_construct # - blocks_of_size_q_plus_t : contains all t*(q**2+q+1)blocks of size q+t, # covering the same number of points: it is a # symmetric design. - ############################################################################### + ########################################################################### ############################################## # Part 2: Build an OA on t(q^2+q+1)+x points # diff --git a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx index 2fa1c414e91..f7f3502548b 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +++ b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx @@ -145,7 +145,7 @@ cpdef find_product_decomposition(int k,int n): sage: _ = f(*args) """ cdef int n1,n2 - for n1 in range(2,n): + for n1 in range(2, n): n2 = n/n1 # n2 is decreasing along the loop if n2 < n1: break @@ -194,15 +194,13 @@ cpdef find_wilson_decomposition_with_one_truncated_group(int k,int n): if u == 0 or (u>1 and k >= u+2): continue - m = n/r + m = n // r # If there exists a TD(k,m) then k= m+2: break - if (is_available(k ,m ) and - is_available(k ,m+1) and - is_available(k+1,r ) and - is_available(k ,u )): + if (is_available(k, m) and is_available(k, m + 1) and + is_available(k + 1, r) and is_available(k, u)): from sage.combinat.designs.orthogonal_arrays import wilson_construction return wilson_construction, (None,k,r,m,(u,),False) @@ -294,22 +292,20 @@ cpdef find_construction_3_3(int k,int n): sage: find_construction_3_3(12,11) """ cdef int mm,nn,i - for mm in range(k-1,n/2+1): - if (not is_available(k ,mm ) or - not is_available(k ,mm+1)): + for mm in range(k-1, n//2+1): + if not(is_available(k, mm) and is_available(k, mm + 1)): continue - for nn in range(2,n/mm+1): + for nn in range(2, n//mm+1): i = n-nn*mm - if i<=0: + if i <= 0: continue - if (is_available(k+i, nn ) and - is_available(k , mm+i)): + if is_available(k + i, nn) and is_available(k, mm + i): from sage.combinat.designs.orthogonal_arrays_build_recursive import construction_3_3 - return construction_3_3, (k,nn,mm,i) + return construction_3_3, (k, nn, mm, i) -cpdef find_construction_3_4(int k,int n): +cpdef find_construction_3_4(int k, int n): r""" Find a decomposition for construction 3.4 from [AC07]_. @@ -339,20 +335,20 @@ cpdef find_construction_3_4(int k,int n): not is_available(k,mm+2)): continue - for nn in range(2,n/mm+1): + for nn in range(2, n//mm+1): i = n-nn*mm if i<=0: continue for s in range(1,min(i,nn)): r = i-s - if (is_available(k+r+1,nn) and - is_available(k , s) and - (is_available(k,mm+r) or is_available(k,mm+r+1))): + if (is_available(k + r + 1, nn) and + is_available(k, s) and + (is_available(k, mm + r) or is_available(k, mm + r + 1))): from sage.combinat.designs.orthogonal_arrays_build_recursive import construction_3_4 - return construction_3_4, (k,nn,mm,r,s) + return construction_3_4, (k, nn, mm, r, s) -cpdef find_construction_3_5(int k,int n): +cpdef find_construction_3_5(int k, int n): r""" Find a decomposition for construction 3.5 from [AC07]_. @@ -376,14 +372,14 @@ cpdef find_construction_3_5(int k,int n): sage: find_construction_3_5(9,24) """ cdef int mm,i,nn,r,s,t - for mm in range(2,n/2+1): + for mm in range(2, n//2+1): if (mm+3 >= n or not is_available(k,mm+1) or not is_available(k,mm+2) or not is_available(k,mm+3)): continue - for nn in range(2,n/mm+1): + for nn in range(2, n//mm+1): i = n-nn*mm if i<=0: continue @@ -433,7 +429,7 @@ cpdef find_construction_3_6(int k,int n): not is_available(k,mm+2)): continue - for nn in range(2,n/mm+1): + for nn in range(2, n//mm+1): i = n-nn*mm if i<=0: continue @@ -913,7 +909,7 @@ cpdef find_brouwer_van_rees_with_one_truncated_column(int k,int n): cdef tuple values # We write n=rm+remainder - for m in range(2,n//2): + for m in range(2, n//2): if not is_available(k,m): continue @@ -935,7 +931,7 @@ cpdef find_brouwer_van_rees_with_one_truncated_column(int k,int n): continue max_multiplier = max(available_multipliers) - for r in range(2,n//m+1): + for r in range(2, n//m+1): remainder = n-r*m if (remainder > r*max_multiplier or not is_available(k+1,r) or diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 27a5bdcae32..ccc11176d54 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -311,14 +311,14 @@ def kirkman_triple_system(v,existence=False): b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v:i for i,v in enumerate(X)}) - gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes + gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v:i for i,v in enumerate(X)}) - gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes + gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. @@ -573,7 +573,7 @@ def PBD_4_7(v,check=True, existence=False): # On these groups a (15+7,{4,7})-PBD is pasted, in such a way that the 7 # new points are a set of the final PBD PBD22 = PBD_4_7(15+7) - S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7 + S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7 PBD22.relabel({v:i for i,v in enumerate([i for i in range(15+7) if i not in S] + S)}) for B in PBD22: @@ -741,19 +741,19 @@ def PBD_4_7_from_Y(gdd,check=True): raise RuntimeError("A group has size {} but I do not know how to " "build a ({},[4,7])-PBD".format(gs,3*gs+1)) - GDD = {} # the GDD we will need + GDD = {} # the GDD we will need if 4 in block_sizes: - #GDD[4] = GDD_from_BIBD(3*4,4) + # GDD[4] = GDD_from_BIBD(3*4,4) GDD[4] = group_divisible_design(3*4,K=[4],G=[3]) if 5 in block_sizes: - #GDD[5] = GDD_from_BIBD(3*5,4) + # GDD[5] = GDD_from_BIBD(3*5,4) GDD[5] = group_divisible_design(3*5,K=[4],G=[3]) if 7 in block_sizes: # It is obtained from a PBD_4_7(22) by removing a point only contained # in sets of size 4 GDD[7] = PBD_4_7(22) x = set(range(22)).difference(*[S for S in GDD[7] if len(S) != 4]).pop() - relabel = sum((S for S in GDD[7] if x in S),[]) # the groups must be 012,345,... + relabel = sum((S for S in GDD[7] if x in S),[]) # the groups must be 012,345,... relabel = [xx for xx in relabel if xx != x]+[x] GDD[7].relabel({v:i for i,v in enumerate(relabel)}) GDD[7] = [S for S in GDD[7] if 21 not in S] diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index cf1834e88db..9576ae5f5aa 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -64,6 +64,8 @@ from sage.combinat.designs.incidence_structures import IncidenceStructure # Construction 1 + + def two_n(B): r""" Return a Steiner Quadruple System on `2n` points. @@ -100,6 +102,8 @@ def two_n(B): return IncidenceStructure(2*n,Y,check=False,copy=False) # Construction 2 + + def three_n_minus_two(B): """ Return a Steiner Quadruple System on `3n-2` points. @@ -155,6 +159,8 @@ def three_n_minus_two(B): return IncidenceStructure(3*n-2,Y,check=False,copy=False) # Construction 3 + + def three_n_minus_eight(B): r""" Return a Steiner Quadruple System on `3n-8` points. @@ -214,6 +220,8 @@ def three_n_minus_eight(B): return IncidenceStructure(3*n-8,Y,check=False,copy=False) # Construction 4 + + def three_n_minus_four(B): r""" Return a Steiner Quadruple System on `3n-4` points. @@ -278,6 +286,8 @@ def three_n_minus_four(B): return IncidenceStructure(3*n-4,Y,check=False,copy=False) # Construction 5 + + def four_n_minus_six(B): """ Return a Steiner Quadruple System on `4n-6` points. @@ -349,6 +359,8 @@ def four_n_minus_six(B): return IncidenceStructure(4*n-6,Y,check=False,copy=False) # Construction 6 + + def twelve_n_minus_ten(B): """ Return a Steiner Quadruple System on `12n-6` points. @@ -446,6 +458,7 @@ def twelve_n_minus_ten(B): Y.append([r(x,a), r(y,aa), r(z,aaa), r(t,aaaa)]) return IncidenceStructure(12*n-10,Y,check=False,copy=False) + def relabel_system(B): r""" Relabel the set so that `\{n-4, n-3, n-2, n-1\}` is in `B`. @@ -482,6 +495,7 @@ def get_label(x): B = [[get_label(_) for _ in s] for s in B] return IncidenceStructure(n,B) + def P(alpha, m): r""" Return the collection of pairs `P_{\alpha}(m)`. @@ -525,6 +539,7 @@ def P(alpha, m): pairs += [(y,m+y)] return pairs + def _missing_pair(n,l): r""" Return the smallest `(x,x+1)` that is not contained in `l`. @@ -559,6 +574,7 @@ def barP(eps, m): """ return barP_system(m)[eps] + @cached_function def barP_system(m): r""" @@ -671,6 +687,7 @@ def barP_system(m): return pairs + @cached_function def steiner_quadruple_system(n, check=False): r""" @@ -738,6 +755,7 @@ def steiner_quadruple_system(n, check=False): return sqs + def _SQS14(): r""" Return a Steiner Quadruple System on 14 points. @@ -771,6 +789,7 @@ def _SQS14(): [6, 8, 10, 12], [6, 9, 11, 12], [7, 8, 10, 13], [7, 8, 11, 12], [7, 9, 10, 12], [8, 9, 10, 11]] + def _SQS38(): r""" Return a Steiner Quadruple System on 14 points. diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index b7a71a17b86..8de300efe2a 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -133,7 +133,7 @@ cdef inline int bs_get(uint64_t * bitset, int index) noexcept: r""" Return a bit of a bitset """ - return (bitset[index/64]>>(index%64))&1 + return (bitset[index//64]>>(index%64))&1 cdef inline void bs_set(uint64_t * bitset, int index, int bit) noexcept: r""" @@ -142,8 +142,8 @@ cdef inline void bs_set(uint64_t * bitset, int index, int bit) noexcept: "bit" *MUST* be equal to either 0 or to 1. The code does not involve any "if". """ - bitset[index/64] &= ~(( 1)< bit)< 1)< bit)< sig_malloc(sizeof(int)*n) h.sets = sig_malloc(h.m*sizeof(uint64_t *)) h.set_space = sig_calloc(h.m*(h.limbs+1),sizeof(uint64_t)) diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 281f4fac153..131916b89c9 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -70,7 +70,7 @@ class TwoGraph(IncidenceStructure): :mod:`~sage.combinat.designs.twographs` module. """ def __init__(self, points=None, blocks=None, incidence_matrix=None, - name=None, check=False, copy=True): + name=None, check=False, copy=True): r""" Constructor of the class. diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index f5b8f9ad864..25741ac1833 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -336,12 +336,9 @@ def _latex_(self): lr = r'\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}' - array = [] - for i in range(self._n_rows): - row = [] - for j in range(self._n_cols): - row.append("\\phantom{x}" if (i, j) in self else None) - array.append(row) + array = [[("\\phantom{x}" if (i, j) in self else None) + for j in range(self._n_cols)] + for i in range(self._n_rows)] def end_line(r): # give the line ending to row ``r`` @@ -1201,7 +1198,7 @@ class NorthwestDiagrams(Diagrams): Combinatorial northwest diagrams Additionally, there are natural constructions of a northwest diagram - given the data of a permutation (Rothe diagrams are the protypical example + given the data of a permutation (Rothe diagrams are the prototypical example of northwest diagrams), or the data of a partition of an integer, or a skew partition. @@ -1225,7 +1222,7 @@ class NorthwestDiagrams(Diagrams): To turn a Ferrers diagram into a northwest diagram, we may call :meth:`from_partition`. This will return a Ferrer's diagram in the set of all northwest diagrams. For many use-cases it is probably better - to get Ferrer's diagrams by the corresponding method on partitons, namely + to get Ferrer's diagrams by the corresponding method on partitions, namely :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`:: sage: mu = Partition([7,3,1,1]) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 364a55c74c9..6410a4a93ea 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -3013,7 +3013,7 @@ def jucys_murphy_element(self, i): sage: L = [P.L(i/2) for i in range(1,2*k+1)] sage: all(x.dual() == x for x in L) True - sage: all(x * y == y * x for x in L for y in L) # long time + sage: all(x * y == y * x for x, y in Subsets(L, 2)) # long time True sage: Lsum = sum(L) sage: gens = [P.s(i) for i in range(1,k)] @@ -3045,13 +3045,13 @@ def jucys_murphy_element(self, i): The same tests for a half integer partition algebra:: - sage: k = 9/2 + sage: k = 7/2 sage: R. = QQ[] sage: P = PartitionAlgebra(k, n) sage: L = [P.L(i/2) for i in range(1,2*k+1)] sage: all(x.dual() == x for x in L) True - sage: all(x * y == y * x for x in L for y in L) # long time + sage: all(x * y == y * x for x, y in Subsets(L, 2)) # long time True sage: Lsum = sum(L) sage: gens = [P.s(i) for i in range(1,k-1/2)] @@ -5847,11 +5847,9 @@ def to_Brauer_partition(l, k=None): True """ L = to_set_partition(l, k=k) - L2 = [] paired = [] not_paired = [] - for i in L: - L2.append(list(i)) + L2 = (list(i) for i in L) for i in L2: if len(i) > 2: raise ValueError("blocks must have size at most 2, but {} has {}".format(i, len(i))) diff --git a/src/sage/combinat/dlx.py b/src/sage/combinat/dlx.py index 383dda056be..d25f7414d0d 100644 --- a/src/sage/combinat/dlx.py +++ b/src/sage/combinat/dlx.py @@ -484,10 +484,7 @@ def AllExactCovers(M): ones = [] r = 1 # damn 1-indexing for R in M.rows(): - row = [] - for i in range(len(R)): - if R[i]: - row.append(i + 1) # damn 1-indexing + row = [i for i, Ri in enumerate(R, start=1) if Ri] ones.append([r, row]) r += 1 for s in DLXMatrix(ones): diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 612ffa33ed6..df841984f31 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -77,7 +77,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator from .combinat import CombinatorialElement, catalan_number from sage.combinat.combinatorial_map import combinatorial_map diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 96d35859873..2750ea15e9c 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -9437,9 +9437,9 @@ def graph(self, edge_labels='words_in_out'): transitions = state.transitions if not transitions: isolated_vertices.append(state.label()) - for t in transitions: - graph_data.append((t.from_state.label(), t.to_state.label(), - label_fct(t))) + graph_data.extend((t.from_state.label(), t.to_state.label(), + label_fct(t)) + for t in transitions) G = DiGraph(graph_data, multiedges=True, loops=True) G.add_vertices(isolated_vertices) diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index 2acfee1b337..4be2c514778 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -939,14 +939,12 @@ def merge(self, other): return self ret = list(self.vars) cur_vars = set(ret) - for v in other.vars: - if v not in cur_vars: - ret.append(v) + ret.extend(v for v in other.vars if v not in cur_vars) return DendriformFunctor(Alphabet(ret)) - else: - return None - def _repr_(self): + return None + + def _repr_(self) -> str: """ TESTS:: diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index 1ea7d3274a1..6e7525d8b23 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -1023,14 +1023,12 @@ def merge(self, other): return self ret = list(self.vars) cur_vars = set(ret) - for v in other.vars: - if v not in cur_vars: - ret.append(v) + ret.extend(v for v in other.vars if v not in cur_vars) return PreLieFunctor(Alphabet(ret)) - else: - return None - def _repr_(self): + return None + + def _repr_(self) -> str: """ TESTS:: diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index be4e236b099..5c3c485e549 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -302,11 +302,9 @@ def boxed_entries(self) -> tuple: sage: G.boxed_entries() ((1, 0),) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] == self[i - 1][j]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij == self[i - 1][j]] return tuple(ret) @cached_method @@ -324,11 +322,9 @@ def circled_entries(self) -> tuple: sage: G.circled_entries() ((1, 1), (2, 0)) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] == self[i - 1][j + 1]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij == self[i - 1][j + 1]] return tuple(ret) @cached_method @@ -349,11 +345,9 @@ def special_entries(self) -> tuple: sage: G.special_entries() ((2, 0),) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i-1][j] > self[i][j] and self[i][j] > self[i-1][j+1]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if self[i - 1][j] > selfij > self[i - 1][j + 1]] return tuple(ret) def number_of_boxes(self) -> int: diff --git a/src/sage/combinat/graph_path.py b/src/sage/combinat/graph_path.py index 2fb255579dd..df84b1acdf7 100644 --- a/src/sage/combinat/graph_path.py +++ b/src/sage/combinat/graph_path.py @@ -236,11 +236,7 @@ def paths_from_source_to_target(self, source, target): [[2, 3, 4], [2, 4]] """ source_paths = self.outgoing_paths(source) - paths = [] - for path in source_paths: - if path[-1] == target: - paths.append(path) - return paths + return [path for path in source_paths if path[-1] == target] def paths(self): """ diff --git a/src/sage/combinat/integer_lists/meson.build b/src/sage/combinat/integer_lists/meson.build new file mode 100644 index 00000000000..ac9aab23386 --- /dev/null +++ b/src/sage/combinat/integer_lists/meson.build @@ -0,0 +1,22 @@ +py.install_sources( + '__init__.py', + 'base.pxd', + 'invlex.pxd', + 'lists.py', + 'nn.py', + subdir: 'sage/combinat/integer_lists', +) + +extension_data = {'base' : files('base.pyx'), 'invlex' : files('invlex.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/integer_lists', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 516d2114ffc..6848609cf5d 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -1174,19 +1174,17 @@ def _list_rec(self, n, k): EXAMPLES:: sage: IV = IntegerVectors(2,3) - sage: IV._list_rec(2,3) + sage: list(IV._list_rec(2,3)) [(2, 0, 0), (1, 1, 0), (1, 0, 1), (0, 2, 0), (0, 1, 1), (0, 0, 2)] """ - res = [] - if k == 1: - return [(n, )] + yield (n,) + return for nbar in range(n + 1): n_diff = n - nbar for rest in self._list_rec(nbar, k - 1): - res.append((n_diff,) + rest) - return res + yield (n_diff,) + rest def __iter__(self): """ diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index 24084d03b82..046d2ba3a7f 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -3533,8 +3533,7 @@ def profil(gr, vertex): liste = clockwise_labelling(graph0, -1)[1:] relabelling = {l: i for i, l in enumerate(liste)} - for l in [-1, -2, -3]: - relabelling[l] = l + relabelling.update((i, i) for i in [-1, -2, -3]) new_graph = graph.relabel(relabelling, inplace=False) dyckword_top = [] diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index e1e4f746f42..f7a7c513c52 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -2064,12 +2064,9 @@ def _fill_piece(self, nw_label, ne_label, pieces) -> list[PuzzlePiece]: sage: ps._fill_piece('0', '0', ps._bottom_deltas) [0/0\0] """ - output = [] - for piece in pieces: - if (piece['north_west'] == nw_label and - piece['north_east'] == ne_label): - output.append(piece) - return output + return [piece for piece in pieces + if (piece['north_west'] == nw_label and + piece['north_east'] == ne_label)] @cached_method def _fill_strip(self, nw_labels, ne_label, pieces, final_pieces=None): diff --git a/src/sage/combinat/lr_tableau.py b/src/sage/combinat/lr_tableau.py index 9a5e7a0ef03..5952f911a74 100644 --- a/src/sage/combinat/lr_tableau.py +++ b/src/sage/combinat/lr_tableau.py @@ -28,7 +28,7 @@ # http://www.gnu.org/licenses/ #**************************************************************************** -from itertools import zip_longest +from itertools import zip_longest, accumulate from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux @@ -277,14 +277,15 @@ def is_littlewood_richardson(t, heights): False """ from sage.combinat.words.word import Word - partial = [sum(heights[i] for i in range(j)) for j in range(len(heights)+1)] try: w = t.to_word() except AttributeError: # Not an instance of Tableau w = sum(reversed(t), []) + + partial = list(accumulate(heights, initial=0)) for i in range(len(heights)): subword = Word([j for j in w if partial[i]+1 <= j <= partial[i+1]], - alphabet=list(range(partial[i]+1,partial[i+1]+1))) + alphabet=list(range(partial[i]+1, partial[i+1]+1))) if not subword.is_yamanouchi(): return False return True @@ -305,5 +306,5 @@ def _tableau_join(t1, t2, shift=0): sage: _tableau_join([[1,2]],[[None,None,2],[3]],shift=5) [[1, 2, 7], [8]] """ - return [list(row1) + [e2+shift for e2 in row2 if e2 is not None] - for (row1, row2) in zip_longest(t1, t2, fillvalue=[])] + 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 c08812c5a83..5f9f24239d4 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -320,6 +320,88 @@ def hadamard_matrix_paleyII(n): return normalise_hadamard(H) +def hadamard_matrix_from_symmetric_conference_matrix(n, existence=False, check=True): + r""" + Construct a Hadamard matrix of order `n` from a symmetric conference matrix + of order `n/2`. + + The construction is described in Theorem 4.3.24 of [IS2006]_. + The symmetric conference matrices are obtained from + :func:`sage.combinat.matrices.hadamard_matrix.symmetric_conference_matrix`. + + INPUT: + + - ``n`` -- integer; the order of the matrix to be constructed + - ``existence`` -- boolean (default: ``False``); if ``True``, only check if + the matrix exists + - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix + is a Hadamard before returning + + OUTPUT: + + If ``existence=False``, returns the Hadamard matrix of order `n`. It raises + an error if no data is available to construct the matrix of the given order, + or if `n` does not satisfies the constraints. + If ``existence=True``, returns a boolean representing whether the matrix + can be constructed or not. + + EXAMPLES: + + By default the function returns the Hadamard matrix :: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_from_symmetric_conference_matrix + sage: hadamard_matrix_from_symmetric_conference_matrix(20) + 20 x 20 dense matrix over Integer Ring... + + If ``existence`` is set to True, the function returns True if the matrix exists, + False if the conference matrix does not exist, and Unknown if the conference + matrix cannot be constructed yet :: + + sage: hadamard_matrix_from_symmetric_conference_matrix(12, existence=True) + True + sage: hadamard_matrix_from_symmetric_conference_matrix(4*787, existence=True) + True + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix + sage: is_hadamard_matrix(hadamard_matrix_from_symmetric_conference_matrix(60, check=False)) + True + sage: hadamard_matrix_from_symmetric_conference_matrix(64, existence=True) + False + sage: hadamard_matrix_from_symmetric_conference_matrix(4*787, existence=True) + True + sage: hadamard_matrix_from_symmetric_conference_matrix(64) + Traceback (most recent call last): + ... + ValueError: Cannot construct Hadamard matrix of order 64, a symmetric conference matrix of order 32 is not available in sage. + sage: hadamard_matrix_from_symmetric_conference_matrix(14) + Traceback (most recent call last): + ... + ValueError: No Hadamard matrix of order 14 exists. + """ + if n < 0 or n % 4 != 0: + raise ValueError(f'No Hadamard matrix of order {n} exists.') + + m = n//2 + exists = symmetric_conference_matrix(m, existence=True) + + if existence: + return exists + + if not exists: + raise ValueError(f'Cannot construct Hadamard matrix of order {n}, a symmetric conference matrix of order {m} is not available in sage.') + + C = symmetric_conference_matrix(m) + + H = block_matrix([[C + I(m), C - I(m)], + [C - I(m), -C - I(m)]]) + + if check: + assert is_hadamard_matrix(H) + return H + + def hadamard_matrix_miyamoto_construction(n, existence=False, check=True): r""" Construct Hadamard matrix using the Miyamoto construction. @@ -363,6 +445,10 @@ def hadamard_matrix_miyamoto_construction(n, existence=False, check=True): True sage: hadamard_matrix_miyamoto_construction(64, existence=True) False + sage: hadamard_matrix_miyamoto_construction(4*65, existence=True) + True + sage: is_hadamard_matrix(hadamard_matrix_miyamoto_construction(4*65, check=False)) + True sage: hadamard_matrix_miyamoto_construction(64) Traceback (most recent call last): ... @@ -377,14 +463,16 @@ def hadamard_matrix_miyamoto_construction(n, existence=False, check=True): q = n // 4 if existence: - return is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True) is True + # return is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True) is True + return symmetric_conference_matrix(q+1, existence=True) and hadamard_matrix(q-1, existence=True) is True - if not (is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True)): + # if not (is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True)): + if not (symmetric_conference_matrix(q+1, existence=True) and hadamard_matrix(q-1, existence=True)): raise ValueError(f'The order {n} is not covered by Miyamoto construction.') m = (q-1) // 2 - C = symmetric_conference_matrix_paley(q + 1) + C = symmetric_conference_matrix(q + 1) neg = [i for i in range(2, m+2) if C[1, i] == -1] pos = [i for i in range(m+2, 2*m+2) if C[1, i] == 1] @@ -1027,7 +1115,7 @@ def hadamard_matrix_from_sds(n, existence=False, check=True): def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): r""" - Create a Hadamard matrix using the contruction detailed in [CW1972]_. + Create a Hadamard matrix using the construction detailed in [CW1972]_. Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) such that the entrywise product of two distinct matrices is always equal to `0` and that @@ -1047,7 +1135,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check - ``C`` -- the matrix described above - ``D`` -- the matrix described above - ``check`` -- boolean (default: ``True``); if ``True``, check that the resulting - matrix is Hadamard before returing it + matrix is Hadamard before returning it EXAMPLES:: @@ -1214,7 +1302,7 @@ def _get_baumert_hall_units(n, existence=False): - ``n`` -- integer; the size of the Baumert-Hall units - ``existence`` -- boolean (default: ``False``); if ``True``, only check whether - the units can be contructed + the units can be constructed OUTPUT: @@ -1818,6 +1906,11 @@ def report_name(nam): if existence: return report_name(name) M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1) + elif hadamard_matrix_from_symmetric_conference_matrix(n, existence=True) is True: + name = "Construction from symmetric conference matrix " + name + if existence: + return report_name(name) + M = hadamard_matrix_from_symmetric_conference_matrix(n, check=False) else: if existence: return Unknown @@ -2302,7 +2395,7 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): def skew_hadamard_matrix_spence_construction(n, check=True): r""" - Construct skew Hadamard matrix of order `n` using Spence constrution. + Construct skew Hadamard matrix of order `n` using Spence construction. This function will construct skew Hadamard matrix of order `n=2(q+1)` where `q` is a prime power with `q = 5` (mod 8). The construction is taken from [Spe1977]_, and the @@ -3262,7 +3355,7 @@ def true(nam): return M -def symmetric_conference_matrix(n, check=True): +def symmetric_conference_matrix(n, check=True, existence=False): r""" Try to construct a symmetric conference matrix. @@ -3279,6 +3372,8 @@ def symmetric_conference_matrix(n, check=True): - ``check`` -- boolean (default: ``True``); whether to check that output is correct before returning it. As this is expected to be useless, you may want to disable it whenever you want speed. + - ``existence`` -- boolean (default: ``False``); if true, only check that such + a matrix exists. EXAMPLES:: @@ -3299,9 +3394,11 @@ def symmetric_conference_matrix(n, check=True): """ from sage.graphs.strongly_regular_db import strongly_regular_graph as srg try: - m = srg(n-1, (n-2)/2, (n-6)/4, (n-2)/4) + m = srg(n-1, (n-2)/2, (n-6)/4, (n-2)/4, existence=existence) except ValueError: raise + if existence: + return m C = matrix([0]+[1]*(n-1)).stack(matrix([1]*(n-1)).stack(m.seidel_adjacency_matrix()).T) if check: assert (C == C.T and C**2 == (n-1)*I(n)) diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index e2d818e9480..29fbe2f960e 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -1212,8 +1212,7 @@ def disjoint_mate_dlxcpp_rows_and_map(self, allow_subtrade): dlx_rows.append([c_OFFSET, r_OFFSET, xy_OFFSET]) - if max_column_nr < max(c_OFFSET, r_OFFSET, xy_OFFSET): - max_column_nr = max(c_OFFSET, r_OFFSET, xy_OFFSET) + max_column_nr = max(max_column_nr, max(c_OFFSET, r_OFFSET, xy_OFFSET)) # We will have missed some columns. We # have to add 'dummy' rows so that the C++ DLX solver will find @@ -2444,7 +2443,7 @@ def p3_group_bitrade_generators(p): EXAMPLES:: sage: from sage.combinat.matrices.latin import * - sage: p3_group_bitrade_generators(3) + sage: p3_group_bitrade_generators(3) # random output ((2,6,7)(3,8,9), (1,2,3)(4,7,8)(5,6,9), (1,9,2)(3,7,4)(5,8,6), diff --git a/src/sage/combinat/matrices/meson.build b/src/sage/combinat/matrices/meson.build new file mode 100644 index 00000000000..86021f8d376 --- /dev/null +++ b/src/sage/combinat/matrices/meson.build @@ -0,0 +1,22 @@ +py.install_sources( + 'all.py', + 'dlxcpp.py', + 'hadamard_matrix.py', + 'latin.py', + subdir: 'sage/combinat/matrices', +) + +extension_data_cpp = {'dancing_links': files('dancing_links.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/matrices', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/combinat/meson.build b/src/sage/combinat/meson.build new file mode 100644 index 00000000000..c0e9fe15d8b --- /dev/null +++ b/src/sage/combinat/meson.build @@ -0,0 +1,175 @@ +py.install_sources( + 'SJT.py', + 'abstract_tree.py', + 'affine_permutation.py', + 'algebraic_combinatorics.py', + 'all.py', + 'alternating_sign_matrix.py', + 'backtrack.py', + 'baxter_permutations.py', + 'bijectionist.py', + 'binary_recurrence_sequences.py', + 'binary_tree.py', + 'blob_algebra.py', + 'cartesian_product.py', + 'catalog_partitions.py', + 'cluster_complex.py', + 'colored_permutations.py', + 'combinat.py', + 'combinat_cython.pxd', + 'combination.py', + 'combinatorial_map.py', + 'composition.py', + 'composition_signed.py', + 'composition_tableau.py', + 'constellation.py', + 'core.py', + 'counting.py', + 'cyclic_sieving_phenomenon.py', + 'decorated_permutation.py', + 'derangements.py', + 'descent_algebra.py', + 'diagram.py', + 'diagram_algebras.py', + 'dlx.py', + 'dyck_word.py', + 'e_one_star.py', + 'enumerated_sets.py', + 'enumeration_mod_permgroup.pxd', + 'family.py', + 'finite_state_machine.py', + 'finite_state_machine_generators.py', + 'fqsym.py', + 'free_dendriform_algebra.py', + 'free_module.py', + 'free_prelie_algebra.py', + 'fully_commutative_elements.py', + 'fully_packed_loop.py', + 'gelfand_tsetlin_patterns.py', + 'graph_path.py', + 'gray_codes.py', + 'grossman_larson_algebras.py', + 'growth.py', + 'hall_polynomial.py', + 'hillman_grassl.py', + 'integer_matrices.py', + 'integer_vector.py', + 'integer_vector_weighted.py', + 'integer_vectors_mod_permgroup.py', + 'interval_posets.py', + 'k_tableau.py', + 'kazhdan_lusztig.py', + 'key_polynomial.py', + 'knutson_tao_puzzles.py', + 'lr_tableau.py', + 'misc.py', + 'multiset_partition_into_sets_ordered.py', + 'necklace.py', + 'non_decreasing_parking_function.py', + 'nu_dyck_word.py', + 'nu_tamari_lattice.py', + 'ordered_tree.py', + 'output.py', + 'parallelogram_polyomino.py', + 'parking_functions.py', + 'partition.py', + 'partition_algebra.py', + 'partition_kleshchev.py', + 'partition_shifting_algebras.py', + 'partition_tuple.py', + 'perfect_matching.py', + 'permutation.py', + 'permutation_cython.pxd', + 'plane_partition.py', + 'q_analogues.py', + 'quickref.py', + 'ranker.py', + 'recognizable_series.py', + 'regular_sequence.py', + 'restricted_growth.py', + 'ribbon.py', + 'ribbon_shaped_tableau.py', + 'ribbon_tableau.py', + 'rooted_tree.py', + 'rsk.py', + 'schubert_polynomial.py', + 'set_partition.py', + 'set_partition_ordered.py', + 'shard_order.py', + 'shifted_primed_tableau.py', + 'shuffle.py', + 'sidon_sets.py', + 'similarity_class_type.py', + 'sine_gordon.py', + 'six_vertex_model.py', + 'skew_partition.py', + 'skew_tableau.py', + 'sloane_functions.py', + 'specht_module.py', + 'subset.py', + 'subsets_hereditary.py', + 'subsets_pairwise.py', + 'subword.py', + 'subword_complex.py', + 'super_tableau.py', + 'superpartition.py', + 'symmetric_group_algebra.py', + 'symmetric_group_representations.py', + 't_sequences.py', + 'tableau.py', + 'tableau_residues.py', + 'tableau_tuple.py', + 'tamari_lattices.py', + 'tiling.py', + 'tools.py', + 'triangles_FHM.py', + 'tuple.py', + 'tutorial.py', + 'vector_partition.py', + 'yang_baxter_graph.py', + subdir: 'sage/combinat', +) + +extension_data = { + 'combinat_cython' : files('combinat_cython.pyx'), + 'debruijn_sequence' : files('debruijn_sequence.pyx'), + 'degree_sequences' : files('degree_sequences.pyx'), + 'enumeration_mod_permgroup' : files('enumeration_mod_permgroup.pyx'), + 'expnums' : files('expnums.pyx'), + 'fast_vector_partitions' : files('fast_vector_partitions.pyx'), + 'partitions' : files('partitions.pyx'), + 'permutation_cython' : files('permutation_cython.pyx'), + 'q_bernoulli' : files('q_bernoulli.pyx'), + 'set_partition_iterator' : files('set_partition_iterator.pyx'), + 'subword_complex_c' : files('subword_complex_c.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +install_subdir('chas', install_dir: sage_install_dir / 'combinat') +install_subdir( + 'cluster_algebra_quiver', + install_dir: sage_install_dir / 'combinat', +) +subdir('crystals') +subdir('designs') +subdir('integer_lists') +subdir('matrices') +install_subdir('ncsf_qsym', install_dir: sage_install_dir / 'combinat') +install_subdir('ncsym', install_dir: sage_install_dir / 'combinat') +install_subdir('path_tableaux', install_dir: sage_install_dir / 'combinat') +subdir('posets') +subdir('rigged_configurations') +subdir('root_system') +install_subdir('sf', install_dir: sage_install_dir / 'combinat') +install_subdir('species', install_dir: sage_install_dir / 'combinat') +subdir('words') diff --git a/src/sage/combinat/misc.py b/src/sage/combinat/misc.py index 404c032a8dc..d04b615be80 100644 --- a/src/sage/combinat/misc.py +++ b/src/sage/combinat/misc.py @@ -202,11 +202,7 @@ def _monomial_exponent_to_lower_factorial(me, x): sage: _monomial_exponent_to_lower_factorial(([2,2,2]),a) x^2*y^2*z^2 - x^2*y^2*z - x^2*y*z^2 - x*y^2*z^2 + x^2*y*z + x*y^2*z + x*y*z^2 - x*y*z """ - terms = [] - for i in range(len(me)): - for j in range(me[i]): - terms.append( x[i]-j ) - return prod(terms) + return prod(x[i] - j for i, mei in enumerate(me) for j in range(mei)) def umbral_operation(poly): @@ -235,7 +231,7 @@ def umbral_operation(poly): exponents = poly.exponents() coefficients = poly.coefficients() length = len(exponents) - return sum( [coefficients[i]*_monomial_exponent_to_lower_factorial(exponents[i],x) for i in range(length)] ) + return sum(coefficients[i]*_monomial_exponent_to_lower_factorial(exponents[i], x) for i in range(length)) class IterableFunctionCall: diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index aae1cd569a2..211837f0b05 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -251,8 +251,8 @@ def _repr_normal(self): string_parts = (str(sorted(k)) for k in self) else: string_parts = (str(sorted(k, key=str)) for k in self) - string_parts = ", ".join(string_parts).replace("[","{").replace("]","}") - return "[" + string_parts + "]" + string = ", ".join(string_parts).replace("[", "{").replace("]", "}") + return "[" + string + "]" def _repr_tight(self): r""" @@ -670,7 +670,7 @@ def split_blocks(self, k=2): if not self: return {tuple([self]*k): 1} - out = {} + out: dict[tuple, int] = {} for t in product(*[_split_block(block, k) for block in self]): tt = tuple([P([l for l in c if l]) for c in zip(*t)]) out[tt] = out.get(tt, 0) + 1 diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 26b029810cd..af82a1569f3 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -697,7 +697,7 @@ def from_polynomial(self, f, check=True): -F[1, 1] + F[2] """ assert self.base_ring() == f.base_ring() - exponent_coefficient = f.dict() + exponent_coefficient = f.monomial_coefficients() z = {} for e, c in exponent_coefficient.items(): I = Compositions()([ei for ei in e if ei]) @@ -2039,7 +2039,7 @@ def on_basis(comp, i): return P.zero() else: return x[i-1]**comp[-1] * on_basis(comp[:-1], i-1) + \ - on_basis(comp, i-1) + on_basis(comp, i-1) return M._apply_module_morphism(self, lambda comp: on_basis(comp,n), codomain=P) diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index 1e2725448f9..3a369e7005d 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -738,7 +738,7 @@ def get_len(e): st += ' ' if E_box: st_num = str_tab[k-j][j] - ln_left = int((len(st_num) - (len(st_num) % 2))/2) + ln_left = len(st_num) // 2 st += st_num.rjust(row_height - 1 - ln_left + len(st_num), ' ').ljust(diag_length, ' ') else: st += ' ' * diag_length @@ -761,12 +761,12 @@ def get_len(e): import re mm = min(len(re.search('^ +', l)[0]) for l in str_list) - 1 str_list = [l[mm:].rstrip() for l in str_list] - while str_list[-1] == '': + while not str_list[-1]: str_list.pop() return "\n".join(str_list) -def box_exists(tab, i, j): +def box_exists(tab, i, j) -> bool: r""" Return ``True`` if ``tab[i][j]`` exists and is not ``None``; in particular this allows for `tab[i][j]` to be ``''`` or ``0``. diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index ed9308e82e9..ac2e7ea6e4d 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -1974,12 +1974,9 @@ def widths(self) -> list: sage: pp.widths() [] """ - widths = [] uw = self.upper_widths() lw = self.lower_widths() - for i in range(len(lw)): - widths.append(uw[i] - lw[i]) - return widths + return [up - lo for up, lo in zip(uw, lw)] def degree_convexity(self) -> int: r""" @@ -3046,7 +3043,7 @@ def get_node_position_from_box(self, box_position, direction, nb_crossed_nodes=N INPUT: - - ``box_position`` -- the position of the statring cell + - ``box_position`` -- the position of the starting cell - ``direction`` -- the direction (0 or 1) @@ -3387,11 +3384,10 @@ def get_BS_nodes(self): sage: pp.set_options(drawing_components=dict(tree=True)) sage: view(pp) # not tested """ - result = [] - for h in range(1, self.height()): - result.append(self._get_node_position_at_row(h)) - for w in range(1, self.width()): - result.append(self._get_node_position_at_column(w)) + result = [self._get_node_position_at_row(h) + for h in range(1, self.height())] + result.extend(self._get_node_position_at_column(w) + for w in range(1, self.width())) return result def get_right_BS_nodes(self): diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index c42787d9d28..00ec41337d6 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -63,7 +63,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator from sage.rings.integer import Integer from sage.rings.rational_field import QQ diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index defa29342dc..6b6a7edfa4c 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -8229,8 +8229,7 @@ def _fast_iterator(self, n, max_part): yield [] return - if n < max_part: - max_part = n + max_part = min(n, max_part) bdry = self._ell - 1 for i in reversed(range(1, max_part + 1)): @@ -8421,8 +8420,7 @@ def _fast_iterator(self, n, max_part, depth=0): yield [n] return - if n < max_part: - max_part = n + max_part = min(n, max_part) bdry = self._ell - 1 for i in reversed(range(1, max_part + 1)): @@ -9066,8 +9064,7 @@ def _fast_iterator(self, n, max_part): yield [] return - if n < max_part: - max_part = n + max_part = min(n, max_part) for i in range(max_part, 0, -1): for p in self._fast_iterator(n-i, i): diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index f0d15115ac9..91e79be4cbc 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -364,9 +364,7 @@ def __iter__(self): True """ for p in Permutations(self.k): - res = [] - for i in range(self.k): - res.append(Set([i + 1, -p[i]])) + res = [Set([i, -pi]) for i, pi in enumerate(p, start=1)] yield self.element_class(self, res) @@ -433,10 +431,7 @@ def __iter__(self): {{1, -3}, {2, -2}, {4, -4}, {3, -1}}] """ for p in Permutations(self.k): - res = [] - for i in range(self.k): - res.append(Set([i + 1, -p[i]])) - + res = [Set([i, -pi]) for i, pi in enumerate(p, start=1)] res.append(Set([self.k + 1, -self.k - 1])) yield self.element_class(self, res) @@ -1941,8 +1936,8 @@ def to_set_partition(l, k=None): to_be_added -= spart sp.append(spart) - for singleton in to_be_added: - sp.append(Set([singleton])) + sp.extend(Set([singleton]) + for singleton in to_be_added) return Set(sp) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 7cf80d0fe92..53901bb3c17 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -510,9 +510,8 @@ def __classcall_private__(cls, l, algorithm='lex', sjt=None, check=True): return RSK_inverse(P, Q, 'permutation') # if it's a tuple or nonempty list of tuples, also assume cycle # notation - elif isinstance(l, tuple) or \ - (isinstance(l, list) and l and - all(isinstance(x, tuple) for x in l)): + elif isinstance(l, tuple) or (isinstance(l, list) and l and + all(isinstance(x, tuple) for x in l)): if l and (isinstance(l[0], (int, Integer)) or len(l[0]) > 0): if isinstance(l[0], tuple): n = max(max(x) for x in l) @@ -596,7 +595,7 @@ def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): raise ValueError("unsupported algorithm %s; expected 'lex' or 'sjt'" % self._algorithm) - if check and len(l) > 0: + if check and l: # Make a copy to sort later lst = list(l) @@ -615,14 +614,14 @@ def __init__(self, parent, l, algorithm='lex', sjt=None, check=True): # Is the maximum element of the permutation the length of input, # or is some integer missing ? if int(lst[-1]) != len(lst): - raise ValueError("the permutation has length "+str(len(lst)) + - " but its maximal element is " + - str(int(lst[-1])) + ". Some element may be " + + raise ValueError(f"the permutation has length {len(lst)} " + + f"but its maximal element is {int(lst[-1])}" + + ". Some element may be " + "repeated, or an element is missing, but " + "there is something wrong with its length.") # Do the elements appear only once ? - previous = lst[0]-1 + previous = lst[0] - 1 for i in lst: if i == previous: @@ -1653,7 +1652,7 @@ def _to_inversion_vector_orig(self): [2, 3, 6, 4, 0, 2, 2, 1, 0] """ p = self._list - iv = [0]*len(p) + iv = [0] * len(p) for i in range(len(p)): for pj in p: if pj > i+1: @@ -1682,8 +1681,8 @@ def _to_inversion_vector_small(self): """ p = self._list l = len(p)+1 - iv = [0]*l - checked = [1]*l + iv = [0] * l + checked = [1] * l for pi in reversed(p): checked[pi] = 0 iv[pi] = sum(checked[pi:]) @@ -1738,8 +1737,8 @@ def merge_and_countv(ivA_A, ivB_B): def base_case(L): s = sorted(L) d = {j: i for i, j in enumerate(s)} - iv = [0]*len(L) - checked = [1]*len(L) + iv = [0] * len(L) + checked = [1] * len(L) for pi in reversed(L): dpi = d[pi] checked[dpi] = 0 @@ -1902,7 +1901,7 @@ def show(self, representation='cycles', orientation='landscape', **args): L = line([r(1, 1)]) for i in range(len(p)): - L += line([r(i, 1.0), r(p[i]-1, 0)]) + L += line([r(i, 1.0), r(p[i] - 1, 0)]) L += text(str(i), r(i, 1.05)) + text(str(i), r(p[i]-1, -.05)) return L.show(axes=False, **args) @@ -2433,9 +2432,9 @@ def longest_increasing_subsequences(self): for i in columns[0]: D.add_edge(0, i) # 0 is source for i in columns[-1]: - D.add_edge(i, n+1) # n+1 is sink + D.add_edge(i, n + 1) # n+1 is sink - return sorted([p[1:-1] for p in D.all_paths(0, n+1)], reverse=True) + return sorted([p[1:-1] for p in D.all_paths(0, n + 1)], reverse=True) def longest_increasing_subsequences_number(self): r""" @@ -2659,7 +2658,7 @@ def foata_bijection(self) -> Permutation: continue a = M[-1] - M_prime = [0]*(k + 1) + M_prime = [0] * (k + 1) # Locate the positions of the vertical lines. if a > e: index_list = [-1] + [i for i, val in enumerate(M) if val > e] @@ -2711,7 +2710,7 @@ def foata_bijection_inverse(self) -> Permutation: k = len(L) if k <= 1: continue - L_prime = [0]*(k) + L_prime = [0] * (k) a = L[0] # Locate the positions of the vertical lines. if a > e: @@ -2920,14 +2919,14 @@ def destandardize(self, weight, ordered_alphabet=None): if not set(ides).issubset(set(partial)): raise ValueError(f"Standardization with weight {weight} is not possible!") if ordered_alphabet is None: - ordered_alphabet = list(range(1,len(weight)+1)) + ordered_alphabet = list(range(1, len(weight)+1)) else: if len(weight) > len(ordered_alphabet): raise ValueError("Not enough letters in the alphabet are specified compared to the weight") q = self.inverse() - s = [0]*len(self) + s = [0] * len(self) for i in range(len(partial)-1): - for j in range(partial[i],partial[i+1]): + for j in range(partial[i], partial[i+1]): s[q[j]-1] = ordered_alphabet[i] from sage.combinat.words.word import Word return Word(s) @@ -2993,7 +2992,7 @@ def _to_lehmer_code_small(self) -> list: p = self._list l = len(p) lehmer = [] - checked = [1]*l + checked = [1] * l for pi in p: checked[pi-1] = 0 lehmer.append(sum(checked[:pi])) @@ -3577,29 +3576,19 @@ def descent_polynomial(self): EXAMPLES:: sage: Permutation([2,1,3]).descent_polynomial() - z1 + z2 sage: Permutation([4,3,2,1]).descent_polynomial() - z1*z2^2*z3^3 + z2*z3*z4 .. TODO:: - This docstring needs to be fixed. First, the definition - does not match the implementation (or the examples). - Second, this doesn't seem to be defined in [GS1984]_ + This docstring needs to be fixed. This is not defined in [GS1984]_ (the descent monomial in their (7.23) is different). """ p = self - z = [] - P = PolynomialRing(ZZ, len(p), 'z') + P = PolynomialRing(ZZ, len(p) + 1, 'z') z = P.gens() - result = 1 - pol = 1 - for i in range(len(p)-1): - pol *= z[p[i]-1] - if p[i] > p[i+1]: - result *= pol - - return result + return P.prod(z[p[i]] for i in range(len(p) - 1) if p[i] > p[i + 1]) ############## # Major Code # @@ -3729,7 +3718,7 @@ def to_major_code(self, final_descent=False): """ p = self n = len(p) - major_indices = [0]*(n+1) + major_indices = [0] * (n + 1) smaller = p[:] P = Permutations() for i in range(n): @@ -4232,7 +4221,7 @@ def permutohedron_succ(self, side='right'): pp[i+1] = p[i] succ.append(P(pp)) else: - advance = lambda perm: [i for i in range(1,n) if perm.index(i) < perm.index(i+1)] + advance = lambda perm: [i for i in range(1, n) if perm.index(i) < perm.index(i+1)] for i in advance(p): pp = p[:] pp[p.index(i)] = i+1 @@ -4272,7 +4261,7 @@ def permutohedron_pred(self, side='right') -> list: pp[d] = p[d - 1] pred.append(P(pp)) else: - recoil = lambda perm: [i for i in range(1,n) if perm.index(i) > perm.index(i+1)] + recoil = lambda perm: [i for i in range(1, n) if perm.index(i) > perm.index(i+1)] for i in recoil(p): pp = p[:] pp[p.index(i)] = i+1 @@ -4683,7 +4672,7 @@ def pattern_positions(self, patt) -> list: if to_standard([p[z] for z in pos]) == patt] @combinatorial_map(name='Simion-Schmidt map') - def simion_schmidt(self, avoid=[1,2,3]): + def simion_schmidt(self, avoid=[1, 2, 3]): r""" Implement the Simion-Schmidt map which sends an arbitrary permutation to a pattern avoiding permutation, where the permutation pattern is one @@ -4713,7 +4702,7 @@ def simion_schmidt(self, avoid=[1,2,3]): targetPermutation = [self[0]] extreme = self[0] nonMinima = [] - if avoid == [1,2,3] or avoid == [1,3,2]: + if avoid == [1, 2, 3] or avoid == [1, 3, 2]: for i in range(1, len(list(self))): if self[i] < extreme: targetPermutation.append(self[i]) @@ -4722,9 +4711,9 @@ def simion_schmidt(self, avoid=[1,2,3]): targetPermutation.append(None) nonMinima.append(self[i]) nonMinima.sort() - if avoid == [1,3,2]: + if avoid == [1, 3, 2]: nonMinima.reverse() - if avoid == [3,2,1] or avoid == [3,1,2]: + if avoid == [3, 2, 1] or avoid == [3, 1, 2]: for i in range(1, len(list(self))): if self[i] > extreme: targetPermutation.append(self[i]) @@ -4733,7 +4722,7 @@ def simion_schmidt(self, avoid=[1,2,3]): targetPermutation.append(None) nonMinima.append(self[i]) nonMinima.sort() - if avoid == [3,2,1]: + if avoid == [3, 2, 1]: nonMinima.reverse() for i in range(1, len(list(self))): @@ -5400,7 +5389,7 @@ def hyperoctahedral_double_coset_type(self): n = len(self) if n % 2 == 1: raise ValueError("%s is a permutation of odd size and has no coset-type" % self) - S = PerfectMatchings(n)([(2*i+1,2*i+2) for i in range(n//2)]) + S = PerfectMatchings(n)([(2*i+1, 2*i+2) for i in range(n//2)]) return S.loop_type(S.apply_permutation(self)) ##################### @@ -6019,13 +6008,13 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): else: return StandardPermutations_n(n) else: - return Permutations_nk(n,k) + return Permutations_nk(n, k) else: # In this case, we have that n is a list # Because of UniqueRepresentation, we require the elements # to be hashable if len(set(n)) == len(n): - if list(n) == list(range(1, len(n)+1)): + if all(i == j for i, j in enumerate(n, start=1)): if k is None: return StandardPermutations_n(len(n)) else: @@ -6034,7 +6023,7 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): if k is None: return Permutations_set(n) else: - return Permutations_setk(n,k) + return Permutations_setk(n, k) else: if k is None: return Permutations_mset(n) @@ -6110,7 +6099,7 @@ class options(GlobalOptions): 'description': "Specifies how the permutations should be printed", 'values': {'list': "the permutations are displayed in list notation" " (aka 1-line notation)", - 'cycle': "the permutations are displayed in cycle notation" + 'cycle': "the permutations are displayed in cycle notation" " (i. e., as products of disjoint cycles)", 'singleton': "the permutations are displayed in cycle notation" " with singleton cycles shown as well", @@ -6131,7 +6120,7 @@ class options(GlobalOptions): 'checker': lambda char: isinstance(char, str)} generator_name = {'default': "s", 'description': "the letter used in latexing the reduced word", - 'checker': lambda char: isinstance(char, str)} + 'checker': lambda char: isinstance(char, str)} mult = {'default': "l2r", 'description': "The multiplication of permutations", 'values': {'l2r': r"left to right: `(p_1 \cdot p_2)(x) = p_2(p_1(x))`", @@ -6250,7 +6239,7 @@ def random_element(self): sage: s in Permutations(3,2) True """ - return sample(range(1, self.n+1), self._k) + return sample(range(1, self.n + 1), self._k) class Permutations_mset(Permutations): @@ -7391,7 +7380,7 @@ def identity(self): sage: Permutations(0).identity() [] """ - return self.element_class(self, range(1,self.n+1), check=False) + return self.element_class(self, range(1, self.n + 1), check=False) one = identity @@ -7449,7 +7438,7 @@ def random_element(self): sage: s in Permutations(4) True """ - return self.element_class(self, sample(range(1, self.n+1), self.n), + return self.element_class(self, sample(range(1, self.n + 1), self.n), check=False) def cardinality(self): @@ -7511,7 +7500,7 @@ def degrees(self): sage: Permutations(7).degrees() (2, 3, 4, 5, 6, 7) """ - return tuple(Integer(i) for i in range(2, self.n+1)) + return tuple(Integer(i) for i in range(2, self.n + 1)) def codegrees(self): """ @@ -7717,11 +7706,57 @@ def simple_reflection(self, i): sage: P.simple_reflections() Finite family {1: [2, 1, 3, 4], 2: [1, 3, 2, 4], 3: [1, 2, 4, 3]} """ - g = list(range(1, self.n+1)) - g[i-1] = i+1 + g = list(range(1, self.n + 1)) + g[i - 1] = i + 1 g[i] = i return self.element_class(self, g, check=False) + @cached_method + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections` + + EXAMPLES:: + + sage: P = Permutations(4) + sage: P.reflection_index_set() + ((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)) + """ + return tuple([tuple(c) for c in itertools.combinations(range(1, self.n + 1), 2)]) + + def reflection(self, i): + r""" + Return the reflection indexed by ``i`` of ``self``. + + This returns the permutation with cycle `i = (a, b)`. + + .. SEEALSO:: + + - :meth:`reflections_index_set` + - :meth:`reflections` + + EXAMPLES:: + + sage: P = Permutations(4) + sage: for i in P.reflection_index_set(): + ....: print('%s %s'%(i, P.reflection(i))) + (1, 2) [2, 1, 3, 4] + (1, 3) [3, 2, 1, 4] + (1, 4) [4, 2, 3, 1] + (2, 3) [1, 3, 2, 4] + (2, 4) [1, 4, 3, 2] + (3, 4) [1, 2, 4, 3] + """ + data = list(range(1, self.n + 1)) + data[i[0]-1] = i[1] + data[i[1]-1] = i[0] + return self.element_class(self, data, check=False) + class Element(Permutation): def has_left_descent(self, i, mult=None): r""" @@ -8111,13 +8146,13 @@ def from_cycles(n, cycles, parent=None): # check that the values are valid if (k < 1) or (pk < 1): raise ValueError("all elements should be strictly positive " - f"integers, but I found {min(k, pk)}") + f"integers, but I found {min(k, pk)}") if (k > n) or (pk > n): raise ValueError("you claimed that this is a permutation on " - f"1...{n}, but it contains {max(k, pk)}") + f"1...{n}, but it contains {max(k, pk)}") if p[k - 1] is not None: raise ValueError(f"the element {k} appears more than once" - " in the input") + " in the input") p[k - 1] = pk # values that are not in any cycle are fixed points of the permutation @@ -8483,7 +8518,7 @@ def d(l): if not self._d: return one - l_ops = [1]*(self.n-1) + l_ops = [1] * (self.n-1) for i in self._d: l_ops[i] = 0 l = [one] @@ -9140,7 +9175,7 @@ def to_standard(p, key=None): We check against the naive method:: sage: def std(p): - ....: s = [0]*len(p) + ....: s = [0] * len(p) ....: c = p[:] ....: biggest = max(p) + 1 ....: i = 1 @@ -9700,7 +9735,7 @@ def __iter__(self): sage: Permutations(3, avoiding=[2,1]).list() [[1, 2, 3]] """ - yield self.element_class(self, range(1, self.n+1), check=False) + yield self.element_class(self, range(1, self.n + 1), check=False) def cardinality(self): """ diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index c238ce77248..e51fcd03186 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -25,7 +25,8 @@ # **************************************************************************** from __future__ import annotations -from typing import NewType, Iterator +from typing import NewType +from collections.abc import Iterator from sage.structure.richcmp import richcmp, richcmp_method from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -1627,7 +1628,7 @@ def from_antichain(self, A) -> PP: pp_matrix[x][y] = z + 1 # For each value in current antichain, fill in the rest of the matrix by - # rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichiain is now in plane partition format + # rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is now in plane partition format if A: for i in range(a): i = a - (i + 1) @@ -2160,7 +2161,7 @@ def from_antichain(self, acl) -> PP: pp_matrix[x][y] = (z+1) # For each value in current antichain, fill in the rest of the - # matrix by rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichiain is + # matrix by rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is # now in plane partition format. if acl != []: for i in range(b): @@ -2367,7 +2368,7 @@ def from_antichain(self, acl) -> PP: pp_matrix[y][x] = z + 1 # z,y,x # for each value in current antichain, fill in the rest of the matrix by - # rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichiain is now in plane partition format + # rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is now in plane partition format if acl != []: for i in range(b): i = b - (i + 1) @@ -3165,7 +3166,7 @@ def from_antichain(self, acl) -> PP: n = a N = n // 2 pp_matrix = [[0] * (c) for i in range(b)] - # creates a matrix for the plane parition populated by 0s + # creates a matrix for the plane partition populated by 0s # EX: [[0,0,0], [0,0,0], [0,0,0]] width = N - 1 height = N - 1 diff --git a/src/sage/combinat/posets/hasse_cython_flint.pyx b/src/sage/combinat/posets/hasse_cython_flint.pyx index e2b4589b238..28f3f026430 100644 --- a/src/sage/combinat/posets/hasse_cython_flint.pyx +++ b/src/sage/combinat/posets/hasse_cython_flint.pyx @@ -20,6 +20,53 @@ from sage.libs.flint.fmpz_mat cimport * from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import ZZ +from sage.libs.flint.fmpz_poly_sage cimport Fmpz_poly + + +cpdef Fmpz_poly chain_poly(list positions): + r""" + Return the chain polynomial of a poset. + + INPUT: + + - ``positions`` -- a list of sets of integers describing the poset, as + given by the lazy attribute ``_leq_storage`` of Hasse diagrams + + OUTPUT: a Flint polynomial in one variable over `\ZZ`. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_cython_flint import chain_poly + sage: D = [{0, 1}, {1}] + sage: chain_poly(D) + 3 1 2 1 + sage: P = posets.TamariLattice(5) + sage: H = P._hasse_diagram + sage: D = H._leq_storage + sage: chain_poly(D) + 12 1 42 357 1385 3133 4635 4758 3468 1778 612 127 12 + """ + cdef Py_ssize_t n = len(positions) + cdef Py_ssize_t i, j + + q = Fmpz_poly([0, 1]) + zero = Fmpz_poly(0) + one = Fmpz_poly(1) + + cdef list chain_polys = [zero] * n + + # chain_polys[i] will be the generating function for the + # chains with lowest vertex i (in the labelling of the + # Hasse diagram). + for i in range(n - 1, -1, -1): + cpi = q + for j in positions[i]: + cpi += q * chain_polys[j] + chain_polys[i] = cpi + total = one + for i in range(n): + total += chain_polys[i] + return total cpdef Matrix_integer_dense moebius_matrix_fast(list positions): diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 0056ad37eed..56b51e96371 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -27,7 +27,8 @@ from sage.rings.integer_ring import ZZ lazy_import('sage.combinat.posets.hasse_cython_flint', - ['moebius_matrix_fast', 'coxeter_matrix_fast']) + ['moebius_matrix_fast', 'coxeter_matrix_fast', + 'chain_poly']) lazy_import('sage.matrix.constructor', 'matrix') lazy_import('sage.rings.finite_rings.finite_field_constructor', 'GF') @@ -2413,6 +2414,23 @@ def chains(self, element_class=list, exclude=None, conversion=None): """ return IncreasingChains(self._leq_storage, element_class, exclude, conversion) + def chain_polynomial(self): + """ + Return the chain polynomial of the poset. + + The coefficient of `q^k` is the number of chains of `k` + elements in the poset. List of coefficients of this polynomial + is also called a *f-vector* of the poset. + + EXAMPLES:: + + sage: P = posets.ChainPoset(3) + sage: H = P._hasse_diagram + sage: t = H.chain_polynomial(); t + q^3 + 3*q^2 + 3*q + 1 + """ + return chain_poly(self._leq_storage)._sage_('q') # noqa: F821 + def is_linear_interval(self, t_min, t_max) -> bool: """ Return whether the interval ``[t_min, t_max]`` is linear. diff --git a/src/sage/combinat/posets/meson.build b/src/sage/combinat/posets/meson.build new file mode 100644 index 00000000000..07837832519 --- /dev/null +++ b/src/sage/combinat/posets/meson.build @@ -0,0 +1,34 @@ +py.install_sources( + 'all.py', + 'cartesian_product.py', + 'd_complete.py', + 'elements.py', + 'forest.py', + 'hasse_diagram.py', + 'incidence_algebras.py', + 'lattices.py', + 'linear_extensions.py', + 'mobile.py', + 'moebius_algebra.py', + 'poset_examples.py', + 'posets.py', + subdir: 'sage/combinat/posets', +) + +extension_data = { + 'hasse_cython' : files('hasse_cython.pyx'), + 'hasse_cython_flint' : files('hasse_cython_flint.pyx'), + 'linear_extension_iterator' : files('linear_extension_iterator.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/posets', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index a111609b4b0..e565eb3d2ee 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -7682,18 +7682,7 @@ def chain_polynomial(self): sage: R.chain_polynomial() q + 1 """ - hasse = self._hasse_diagram - q = polygen(ZZ, 'q') - one = q.parent().one() - hasse_size = hasse.cardinality() - chain_polys = [0] * hasse_size - # chain_polys[i] will be the generating function for the - # chains with topmost vertex i (in the labelling of the - # Hasse diagram). - for i in range(hasse_size): - chain_polys[i] = q + sum(q * chain_polys[j] - for j in hasse.principal_order_ideal(i)) - return one + sum(chain_polys) + return self._hasse_diagram.chain_polynomial() def order_polynomial(self): r""" @@ -8895,11 +8884,11 @@ class FinitePosets_n(UniqueRepresentation, Parent): sage: P.cardinality() 5 sage: for p in P: print(p.cover_relations()) - [] - [[1, 2]] + [[1, 2], [0, 2]] [[0, 1], [0, 2]] + [[0, 2]] [[0, 1], [1, 2]] - [[1, 2], [0, 2]] + [] """ def __init__(self, n) -> None: @@ -8941,27 +8930,40 @@ def __contains__(self, P) -> bool: return P in FinitePosets() and P.cardinality() == self._n def __iter__(self): - """ + r""" Return an iterator of representatives of the isomorphism classes of finite posets of a given size. .. NOTE:: - This uses the DiGraph iterator as a backend to construct - transitively-reduced, acyclic digraphs. + If the size `n\leq 16`, this uses an iterator from + ``nauty`` as a backend to construct only transitively-reduced, + acyclic digraphs. Otherwise it uses a slow naive iterator, + as the ``nauty`` iterator is not available. EXAMPLES:: sage: P = Posets(2) sage: list(P) [Finite poset containing 2 elements, Finite poset containing 2 elements] + + TESTS:: + + sage: it = iter(Posets(17)) + sage: next(it) + Finite poset containing 17 elements """ - from sage.graphs.digraph_generators import DiGraphGenerators - for dig in DiGraphGenerators()(self._n, is_poset): + if self._n <= 16: + it = digraphs.nauty_posetg(f"{self._n} o") + else: + it = digraphs(self._n, is_poset) + + for dig in it: # We need to relabel the digraph since range(self._n) must be a linear # extension. Too bad we need to compute this again. TODO: Fix this. - label_dict = dict(zip(dig.topological_sort(), range(dig.order()))) - yield FinitePoset(dig.relabel(label_dict, inplace=False)) + label_dict = dict(zip(dig.topological_sort(), range(self._n))) + dig.relabel(label_dict, inplace=True) + yield FinitePoset(dig) def cardinality(self, from_iterator=False): r""" diff --git a/src/sage/combinat/ribbon_shaped_tableau.py b/src/sage/combinat/ribbon_shaped_tableau.py index d33cc115396..dd7218c899c 100644 --- a/src/sage/combinat/ribbon_shaped_tableau.py +++ b/src/sage/combinat/ribbon_shaped_tableau.py @@ -350,18 +350,17 @@ def from_permutation(self, p): [[1, 2], [3]], [[1], [2], [3]]] """ - if p == []: + if not p: return self.element_class(self, []) comp = p.descents() - if comp == []: + if not comp: return self.element_class(self, [p[:]]) - r = [] - r.append([p[j] for j in range(comp[0])]) - for i in range(len(comp) - 1): - r.append([p[j] for j in range(comp[i], comp[i + 1])]) + r = [[p[j] for j in range(comp[0])]] + r.extend([p[j] for j in range(comp[i], comp[i + 1])] + for i in range(len(comp) - 1)) r.append([p[j] for j in range(comp[-1], len(p))]) r.reverse() return self.element_class(self, r) diff --git a/src/sage/combinat/rigged_configurations/bij_type_D.py b/src/sage/combinat/rigged_configurations/bij_type_D.py index e1d84d29e4b..a6a5c8316a7 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D.py @@ -238,8 +238,7 @@ def next_state(self, val): if tableau_height <= n - 2: max_width2 = self.ret_rig_con[n - 2].insert_cell(max_width) max_width = self.ret_rig_con[n - 1].insert_cell(max_width) - if max_width2 < max_width: - max_width = max_width2 + max_width = min(max_width2, max_width) elif pos_val <= self.cur_dims[0][0]: # Special case when the height will become n max_width = self.ret_rig_con[self.cur_dims[0][0] - 1].insert_cell(max_width) @@ -603,8 +602,7 @@ def next_state(self, height): temp_size = self.cur_partitions[n - 2][ell[n - 2]] if ell[n - 1] is not None: last_size = self.cur_partitions[n - 1][ell[n - 1]] - if temp_size > last_size: - last_size = temp_size + last_size = max(temp_size, last_size) else: b = n else: diff --git a/src/sage/combinat/rigged_configurations/meson.build b/src/sage/combinat/rigged_configurations/meson.build new file mode 100644 index 00000000000..6b12159dfda --- /dev/null +++ b/src/sage/combinat/rigged_configurations/meson.build @@ -0,0 +1,40 @@ +py.install_sources( + 'all.py', + 'bij_abstract_class.py', + 'bij_infinity.py', + 'bij_type_A.py', + 'bij_type_A2_dual.py', + 'bij_type_A2_even.py', + 'bij_type_A2_odd.py', + 'bij_type_B.py', + 'bij_type_C.py', + 'bij_type_D.py', + 'bij_type_D_tri.py', + 'bij_type_D_twisted.py', + 'bij_type_E67.py', + 'bijection.py', + 'kleber_tree.py', + 'kr_tableaux.py', + 'rc_crystal.py', + 'rc_infinity.py', + 'rigged_configuration_element.py', + 'rigged_configurations.py', + 'rigged_partition.pxd', + 'tensor_product_kr_tableaux.py', + 'tensor_product_kr_tableaux_element.py', + subdir: 'sage/combinat/rigged_configurations', +) + +extension_data = {'rigged_partition' : files('rigged_partition.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/rigged_configurations', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/combinat/rigged_configurations/rigged_partition.pyx b/src/sage/combinat/rigged_configurations/rigged_partition.pyx index 84d98c90f3e..c8dc350bd73 100644 --- a/src/sage/combinat/rigged_configurations/rigged_partition.pyx +++ b/src/sage/combinat/rigged_configurations/rigged_partition.pyx @@ -191,7 +191,7 @@ cdef class RiggedPartition(SageObject): from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations if RiggedConfigurations.options.convention == 'English': - ret_string += "\\cline{2-%s} "%(1+num_cols) + latex(self.vacancy_numbers[0]) + ret_string += "\\cline{2-%s} " % (1+num_cols) + latex(self.vacancy_numbers[0]) for i, row_len in enumerate(self._list): ret_string += " &" + "\\phantom{|}&"*row_len @@ -210,7 +210,7 @@ cdef class RiggedPartition(SageObject): ret_string += "\n\\end{array}\n}" else: for i, row_len in enumerate(reversed(self._list)): - ret_string += "\\cline{2-%s} "%(1 + row_len) + latex(self.vacancy_numbers[-i-1]) + ret_string += "\\cline{2-%s} " % (1 + row_len) + latex(self.vacancy_numbers[-i-1]) ret_string += " &" + "\\phantom{|}&"*row_len if num_cols == row_len: @@ -220,7 +220,7 @@ cdef class RiggedPartition(SageObject): ret_string += "}{l}{" + latex(self.rigging[-i-1]) + "}" ret_string += " \\\\\n" - ret_string += "\\cline{2-%s}\n\\end{array}\n}"%(1 + num_cols) + ret_string += "\\cline{2-%s}\n\\end{array}\n}" % (1 + num_cols) return ret_string @@ -645,7 +645,7 @@ cdef class RiggedPartitionTypeB(RiggedPartition): ret_string = "{\n\\begin{array}[t]{r|" + "c|"*num_cols + "l}\n" if RiggedConfigurations.options.convention == 'English': - ret_string += "\\cline{2-%s} "%(1+num_cols) + latex(self.vacancy_numbers[0]) + ret_string += "\\cline{2-%s} " % (1+num_cols) + latex(self.vacancy_numbers[0]) for i, row_len in enumerate(self._list): ret_string += " &" + box_str*row_len @@ -663,7 +663,7 @@ cdef class RiggedPartitionTypeB(RiggedPartition): ret_string += "\n\\end{array}\n}" else: for i, row_len in enumerate(reversed(self._list)): - ret_string += "\\cline{2-%s} "%(1 + row_len) + ret_string += "\\cline{2-%s} " % (1 + row_len) ret_string += latex(self.vacancy_numbers[-i-1]) ret_string += " &" + box_str*row_len @@ -674,6 +674,6 @@ cdef class RiggedPartitionTypeB(RiggedPartition): ret_string += "}{l}{" + latex(self.rigging[-i-1]) + "}" ret_string += " \\\\\n" - ret_string += "\\cline{2-%s}\n\\end{array}\n}"%(1 + num_cols) + ret_string += "\\cline{2-%s}\n\\end{array}\n}" % (1 + num_cols) return ret_string diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 5fe5e161575..0d00c45273f 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -369,8 +369,7 @@ def _repr_(self): def inner_product(self, lambdacheck): """ - The scalar product with elements of the coroot lattice - embedded in the ambient space. + The scalar product with elements of the ambient space. EXAMPLES:: @@ -379,15 +378,32 @@ def inner_product(self, lambdacheck): (-1, 0, 0) sage: a.inner_product(a) 2 + + TESTS: + + Verify that :issue:`15325` (A) is fixed:: + + sage: rt = RootSystem(['E', 8]) + sage: lat = rt.root_lattice() + sage: spc = rt.ambient_space() + sage: spc.simple_root(1).scalar(lat.simple_coroot(2)) + 0 """ + if self.parent() is not lambdacheck.parent(): + try: + # see if lambdacheck can be converted to the ambient space + lambdacheck = self.parent()(lambdacheck) + except (TypeError, ValueError): + raise TypeError(f"unable to coerce {lambdacheck} into {self.parent()}") + + # self and lambdacheck both belong to the same ambient space, so use the inner product there self_mc = self._monomial_coefficients lambdacheck_mc = lambdacheck._monomial_coefficients result = self.parent().base_ring().zero() for t,c in lambdacheck_mc.items(): - if t not in self_mc: - continue - result += c*self_mc[t] + if t in self_mc: + result += c*self_mc[t] return result scalar = inner_product diff --git a/src/sage/combinat/root_system/cartan_matrix.py b/src/sage/combinat/root_system/cartan_matrix.py index c3151013ed7..c8e665fcaa2 100644 --- a/src/sage/combinat/root_system/cartan_matrix.py +++ b/src/sage/combinat/root_system/cartan_matrix.py @@ -963,7 +963,7 @@ def coxeter_matrix(self): sage: ct.cartan_matrix().coxeter_matrix() == ct.coxeter_matrix() True """ - scalarproducts_to_order = {0: 2, 1: 3, 2: 4, 3: 6} + scalarproducts_to_order = {0: 2, 1: 3, 2: 4, 3: 6} from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix I = self.index_set() n = len(I) diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index 69e9acdb135..ce5762719e2 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -438,7 +438,7 @@ The data essentially consists of a description of the Dynkin/Coxeter diagram and, when relevant, of the natural embedding of the root -system in an Euclidean space. Everything else is reconstructed from +system in a Euclidean space. Everything else is reconstructed from this data. - :ref:`sage.combinat.root_system.type_A` diff --git a/src/sage/combinat/root_system/coxeter_group.py b/src/sage/combinat/root_system/coxeter_group.py index 0f8fd84fdea..656c933c3e5 100644 --- a/src/sage/combinat/root_system/coxeter_group.py +++ b/src/sage/combinat/root_system/coxeter_group.py @@ -157,4 +157,4 @@ def CoxeterGroup(data, implementation='reflection', base_ring=None, index_set=No from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.coxeter_group', 'CoxeterGroupAsPermutationGroup', ReflectionGroup) +register_unpickle_override('sage.combinat.root_system.coxeter_group', 'CoxeterGroupAsPermutationGroup', ReflectionGroup) diff --git a/src/sage/combinat/root_system/dynkin_diagram.py b/src/sage/combinat/root_system/dynkin_diagram.py index 76411c66158..b190601f0bf 100644 --- a/src/sage/combinat/root_system/dynkin_diagram.py +++ b/src/sage/combinat/root_system/dynkin_diagram.py @@ -834,7 +834,7 @@ def coxeter_diagram(self): True """ from sage.rings.infinity import infinity - scalarproducts_to_order = {0: 2, 1: 3, 2: 4, 3: 6} + scalarproducts_to_order = {0: 2, 1: 3, 2: 4, 3: 6} from sage.graphs.graph import Graph coxeter_diagram = Graph(multiedges=False) I = self.index_set() diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index b7d4b163242..0cda82af318 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -1046,7 +1046,7 @@ def modular_characteristic(self, mu=None): sage: [V.modular_characteristic(x) for x in V.dominant_maximal_weights()] [11/56, -1/280, 111/280] """ - if type(mu) is tuple: + if isinstance(mu, tuple): n = mu else: n = self.from_weight(mu) diff --git a/src/sage/combinat/root_system/meson.build b/src/sage/combinat/root_system/meson.build new file mode 100644 index 00000000000..a8827403c7e --- /dev/null +++ b/src/sage/combinat/root_system/meson.build @@ -0,0 +1,76 @@ +py.install_sources( + 'all.py', + 'ambient_space.py', + 'associahedron.py', + 'braid_move_calculator.py', + 'branching_rules.py', + 'cartan_matrix.py', + 'cartan_type.py', + 'coxeter_group.py', + 'coxeter_matrix.py', + 'coxeter_type.py', + 'dynkin_diagram.py', + 'extended_affine_weyl_group.py', + 'fundamental_group.py', + 'hecke_algebra_representation.py', + 'integrable_representations.py', + 'non_symmetric_macdonald_polynomials.py', + 'pieri_factors.py', + 'plot.py', + 'reflection_group_complex.py', + 'reflection_group_element.pxd', + 'reflection_group_real.py', + 'root_lattice_realization_algebras.py', + 'root_lattice_realizations.py', + 'root_space.py', + 'root_system.py', + 'type_A.py', + 'type_A_affine.py', + 'type_A_infinity.py', + 'type_B.py', + 'type_BC_affine.py', + 'type_B_affine.py', + 'type_C.py', + 'type_C_affine.py', + 'type_D.py', + 'type_D_affine.py', + 'type_E.py', + 'type_E_affine.py', + 'type_F.py', + 'type_F_affine.py', + 'type_G.py', + 'type_G_affine.py', + 'type_H.py', + 'type_I.py', + 'type_Q.py', + 'type_affine.py', + 'type_dual.py', + 'type_folded.py', + 'type_marked.py', + 'type_reducible.py', + 'type_relabel.py', + 'type_super_A.py', + 'weight_lattice_realizations.py', + 'weight_space.py', + 'weyl_characters.py', + 'weyl_group.py', + subdir: 'sage/combinat/root_system', +) + +extension_data = { + 'braid_orbit' : files('braid_orbit.pyx'), + 'reflection_group_c' : files('reflection_group_c.pyx'), + 'reflection_group_element' : files('reflection_group_element.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/root_system', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index 030b108c774..970f134ba90 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -121,7 +121,7 @@ def from_polynomial(self, p): """ L = self.basis().keys() return self.sum_of_terms((L.from_vector(vector(t)), c) - for (t,c) in p.dict().items()) + for t, c in p.monomial_coefficients().items()) @cached_method def divided_difference_on_basis(self, weight, i): diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index 96a68eacc0f..14f5ed4ab23 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -260,13 +260,34 @@ def scalar(self, lambdacheck): [-1 2 -1 0] [ 0 -1 2 -1] [ 0 0 -2 2] + + TESTS: + + Verify that :issue:`15325` (A) is fixed:: + + sage: rt = RootSystem(['E', 8]) + sage: lat = rt.root_lattice() + sage: spc = rt.ambient_space() + sage: lat.simple_root(1).scalar(spc.simple_coroot(2)) + 0 + + Verify that directionality is correct for roots of different lengths:: + + sage: lat = RootSystem(['B', 3]).root_lattice() + sage: lat.simple_root(2).scalar(lat.simple_coroot(3)) + -2 """ - # Find some better test - if not (lambdacheck in self.parent().coroot_lattice() or lambdacheck in self.parent().coroot_space()): - raise TypeError("%s is not in a coroot lattice/space" % (lambdacheck)) - zero = self.parent().base_ring().zero() - cartan_matrix = self.parent().dynkin_diagram() - return sum( (sum( (lambdacheck[i]*s for i,s in cartan_matrix.column(j)), zero) * c for j,c in self), zero) + if lambdacheck in self.parent().coroot_lattice() or lambdacheck in self.parent().coroot_space(): + # This is the mathematically canonical case, where we use the Cartan matrix to find the scalar product + zero = self.parent().base_ring().zero() + cartan_matrix = self.parent().dynkin_diagram() + return sum( (sum( (lambdacheck[i]*s for i,s in cartan_matrix.column(j)), zero) * c for j,c in self), zero) + + if lambdacheck in self.parent().root_system.ambient_space(): + # lambdacheck lives in the ambient space of the root space, so we take the usual dot product in the ambient space + return self.to_ambient().dot_product(lambdacheck) + + raise TypeError(f"{lambdacheck} is not in a coroot lattice/space") def is_positive_root(self): """ diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index ac70314eae1..06210a749e7 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -787,6 +787,55 @@ def coambient_space(self, base_ring=QQ): """ return self.dual.ambient_space(base_ring) + def coxeter_number(self): + """ + Return the Coxeter number of an irreducible finite root system. + + .. SEEALSO:: + + :meth:`~sage.combinat.root_system.cartan_type.CartanType_standard_finite.coxeter_number`. + + EXAMPLES:: + + sage: rt = RootSystem(['C', 5]) + sage: rt.coxeter_number() + 10 + """ + # Check if RootSystem is finite and irreducible + if not (self.is_finite() and self.is_irreducible()): + raise ValueError("the Coxeter number is defined only for finite and irreducible root systems") + # Hand over to CartanType method + return self._cartan_type.coxeter_number() + + def dual_coxeter_number(self): + """ + Return the dual Coxeter number of a irreducible finite root system. + + The dual Coxeter number is equal to 1 plus the sum of the coefficients + of simple roots in the highest short root of the dual root system. + + .. SEEALSO:: :meth:`~sage.combinat.root_system.cartan_type.CartanType_standard_finite.dual_coxeter_number` + + EXAMPLES:: + + sage: rt = RootSystem(['C', 5]) + sage: rt.dual_coxeter_number() + 6 + + The dual Coxeter number is not the same concept as the Coxeter number + of the dual root system:: + + sage: rt.dual + Dual of root system of type ['C', 5] + sage: rt.dual.coxeter_number() + 10 + """ + # Check if RootSystem is finite and irreducible + if not (self.is_finite() and self.is_irreducible()): + raise ValueError("the dual Coxeter number is defined only for finite and irreducible root systems") + # Hand over to CartanType method + return self._cartan_type.dual_coxeter_number() + def WeylDim(ct, coeffs): """ diff --git a/src/sage/combinat/root_system/type_A.py b/src/sage/combinat/root_system/type_A.py index 26c6154a5bd..91907976f8c 100644 --- a/src/sage/combinat/root_system/type_A.py +++ b/src/sage/combinat/root_system/type_A.py @@ -336,11 +336,12 @@ def ascii_art(self, label=None, node=None): label = lambda i: i if node is None: node = self._ascii_art_node - ret = "---".join(node(label(i)) for i in range(1,n+1)) + "\n" - ret += "".join("{!s:4}".format(label(i)) for i in range(1,n+1)) + ret = "---".join(node(label(i)) for i in range(1, n + 1)) + "\n" + ret += "".join("{!s:4}".format(label(i)) for i in range(1, n + 1)) return ret # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_A', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_A', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_C.py b/src/sage/combinat/root_system/type_C.py index ff0288d5780..d7c6b67194f 100644 --- a/src/sage/combinat/root_system/type_C.py +++ b/src/sage/combinat/root_system/type_C.py @@ -306,10 +306,11 @@ def _default_folded_cartan_type(self): """ from sage.combinat.root_system.type_folded import CartanTypeFolded n = self.n - return CartanTypeFolded(self, ['A', 2*n-1], - [[i, 2*n-i] for i in range(1, n)] + [[n]]) + return CartanTypeFolded(self, ['A', 2*n - 1], + [[i, 2*n - i] for i in range(1, n)] + [[n]]) # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_C', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_C', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_D.py b/src/sage/combinat/root_system/type_D.py index 7d7988c8891..49e9d952a2f 100644 --- a/src/sage/combinat/root_system/type_D.py +++ b/src/sage/combinat/root_system/type_D.py @@ -10,6 +10,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from . import ambient_space +from sage.misc.persist import register_unpickle_override class AmbientSpace(ambient_space.AmbientSpace): @@ -116,9 +117,6 @@ def fundamental_weight(self, i): return self.sum(self.monomial(j) for j in range(i)) -from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_A', 'ambient_space', AmbientSpace) - from sage.misc.cachefunc import cached_method from .cartan_type import CartanType_standard_finite, CartanType_simply_laced, CartanType_simple @@ -349,10 +347,10 @@ def ascii_art(self, label=None, node=None): ret = (4*(n-3))*" "+"{} {}\n".format(node(label(n)), label(n)) ret += ((4*(n-3))*" " + "|\n")*2 ret += "---".join(node(label(i)) for i in range(1, n)) + "\n" - ret += "".join("{!s:4}".format(label(i)) for i in range(1,n)) + ret += "".join("{!s:4}".format(label(i)) for i in range(1, n)) return ret # For unpickling backward compatibility (Sage <= 4.1) -from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_D', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_D', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_E.py b/src/sage/combinat/root_system/type_E.py index 5ac0041d5ca..a7fbb8aeb39 100644 --- a/src/sage/combinat/root_system/type_E.py +++ b/src/sage/combinat/root_system/type_E.py @@ -636,4 +636,5 @@ def ascii_art(self, label=None, node=None): # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_E', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_E', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_F.py b/src/sage/combinat/root_system/type_F.py index 744de2679a4..9a13a2e7a23 100644 --- a/src/sage/combinat/root_system/type_F.py +++ b/src/sage/combinat/root_system/type_F.py @@ -365,7 +365,7 @@ def dual(self): 4 3 2 1 F4 relabelled by {1: 4, 2: 3, 3: 2, 4: 1} """ - return self.relabel({1:4, 2:3, 3:2, 4:1}) + return self.relabel({1: 4, 2: 3, 3: 2, 4: 1}) def _default_folded_cartan_type(self): """ @@ -382,4 +382,5 @@ def _default_folded_cartan_type(self): # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_F', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_F', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_G.py b/src/sage/combinat/root_system/type_G.py index 1ae705e5bec..057d8d17bd1 100644 --- a/src/sage/combinat/root_system/type_G.py +++ b/src/sage/combinat/root_system/type_G.py @@ -271,7 +271,7 @@ def dual(self): 2 1 G2 relabelled by {1: 2, 2: 1} """ - return self.relabel({1:2, 2:1}) + return self.relabel({1: 2, 2: 1}) def _default_folded_cartan_type(self): """ @@ -288,4 +288,5 @@ def _default_folded_cartan_type(self): # For unpickling backward compatibility (Sage <= 4.1) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.root_system.type_G', 'ambient_space', AmbientSpace) +register_unpickle_override('sage.combinat.root_system.type_G', + 'ambient_space', AmbientSpace) diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index da337ebaa96..53dadb9864d 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -525,7 +525,7 @@ def inject_weights(self, i, v): [(1, 1, 0, 0, 0), (0, 0, 0, 1/2, 1/2)] """ shift = self.root_system.cartan_type()._shifts[i] - return self._from_dict( dict([(shift+k, c) for (k,c) in v ])) + return self._from_dict({shift + k: c for k, c in v}) @cached_method def simple_root(self, i): diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index 92ecf0ee33b..7751520ca79 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -1090,8 +1090,18 @@ def iteration(self, algorithm='breadth', tracking_words=True): (1,7,5,3)(2,4,6,8) (2,8)(3,7)(4,6) (1,5)(2,6)(3,7)(4,8) + + TESTS:: + + sage: W = WeylGroup(["A",0], implementation='permutation') + sage: list(W) + [()] + sage: W[0] + () """ from sage.combinat.root_system.reflection_group_c import Iterator + if self.rank() == 0: + return iter([self.one()]) return iter(Iterator(self, N=self.number_of_reflections(), algorithm=algorithm, tracking_words=tracking_words)) diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index 5e276d5ebcf..1fa0ba62b4e 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -2957,12 +2957,10 @@ def _backward_format_output(self, obj1, obj2, output): if j == 0: df.append([]) if j > 0 and obj1[j] < obj1[j-1]: - for _ in range(obj1[j-1]-obj1[j]): - df.append([]) + df.extend([] for _ in range(obj1[j-1]-obj1[j])) df[-1].append(obj2[j]) if obj1: - for a in range(obj1[-1]-1): - df.append([]) + df.extend([] for a in range(obj1[-1]-1)) # If biword is empty, return a decreasing factorization with 1 factor else: df.append([]) diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index ce0f9f6bd1c..5feb90c385b 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -913,6 +913,8 @@ def expand(self, n, alphabet='x'): from sage.combinat.sf.sfa import SymmetricFunctionsFunctor + + class DualBasisFunctor(SymmetricFunctionsFunctor): """ A constructor for algebras of symmetric functions constructed by @@ -986,4 +988,6 @@ def _repr_(self): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.dual', 'SymmetricFunctionAlgebraElement_dual', SymmetricFunctionAlgebra_dual.Element) +register_unpickle_override('sage.combinat.sf.dual', + 'SymmetricFunctionAlgebraElement_dual', + SymmetricFunctionAlgebra_dual.Element) diff --git a/src/sage/combinat/sf/elementary.py b/src/sage/combinat/sf/elementary.py index f2905d818bd..675c02604fd 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -521,4 +521,6 @@ def f(partition): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.elementary', 'SymmetricFunctionAlgebraElement_elementary', SymmetricFunctionAlgebra_elementary.Element) +register_unpickle_override('sage.combinat.sf.elementary', + 'SymmetricFunctionAlgebraElement_elementary', + SymmetricFunctionAlgebra_elementary.Element) diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index fffc4a1873c..e6bc361f3b0 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -435,4 +435,6 @@ def f(partition): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.homogeneous', 'SymmetricFunctionAlgebraElement_homogeneous', SymmetricFunctionAlgebra_homogeneous.Element) +register_unpickle_override('sage.combinat.sf.homogeneous', + 'SymmetricFunctionAlgebraElement_homogeneous', + SymmetricFunctionAlgebra_homogeneous.Element) diff --git a/src/sage/combinat/sf/llt.py b/src/sage/combinat/sf/llt.py index c301e295721..d0a0a040672 100644 --- a/src/sage/combinat/sf/llt.py +++ b/src/sage/combinat/sf/llt.py @@ -764,5 +764,5 @@ class Element(LLT_generic.Element): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.llt', 'LLTElement_spin', LLT_spin.Element) -register_unpickle_override('sage.combinat.sf.llt', 'LLTElement_cospin', LLT_cospin.Element) +register_unpickle_override('sage.combinat.sf.llt', 'LLTElement_spin', LLT_spin.Element) +register_unpickle_override('sage.combinat.sf.llt', 'LLTElement_cospin', LLT_cospin.Element) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 423329b76c6..395906bada4 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -1998,16 +1998,16 @@ def qt_kostka(lam, mu): for p2 in parts: res = s(H(p2)) for p1 in parts: - _qt_kostka_cache[(p1,p2)] = QQqt(res.coefficient(p1).numerator()) + _qt_kostka_cache[(p1, p2)] = QQqt(res.coefficient(p1).numerator()) - return _qt_kostka_cache[(lam,mu)] + return _qt_kostka_cache[(lam, mu)] # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_h', MacdonaldPolynomials_h.Element) +register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_h', MacdonaldPolynomials_h.Element) register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_ht', MacdonaldPolynomials_ht.Element) -register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_j', MacdonaldPolynomials_j.Element) -register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_p', MacdonaldPolynomials_p.Element) -register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_q', MacdonaldPolynomials_q.Element) -register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_s', MacdonaldPolynomials_s.Element) +register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_j', MacdonaldPolynomials_j.Element) +register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_p', MacdonaldPolynomials_p.Element) +register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_q', MacdonaldPolynomials_q.Element) +register_unpickle_override('sage.combinat.sf.macdonald', 'MacdonaldPolynomial_s', MacdonaldPolynomials_s.Element) diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 0afecfa4b89..bb7b54bae4e 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -135,21 +135,25 @@ def product(self, left, right): return self._from_dict(z_elt) def from_polynomial(self, f, check=True): - """ - Return the symmetric function in the monomial basis corresponding to the polynomial ``f``. + r""" + Return the symmetric function in the monomial basis corresponding + to the polynomial ``f``. INPUT: - ``self`` -- a monomial symmetric function basis - - ``f`` -- a polynomial in finitely many variables over the same base ring as ``self``; - it is assumed that this polynomial is symmetric - - ``check`` -- boolean (default: ``True``); checks whether the polynomial is indeed symmetric + - ``f`` -- a polynomial in finitely many variables over the + same base ring as ``self``; it is assumed that this + polynomial is symmetric + - ``check`` -- boolean (default: ``True``); checks whether + the polynomial is indeed symmetric OUTPUT: - - This function converts a symmetric polynomial `f` in a polynomial ring in finitely - many variables to a symmetric function in the monomial - basis of the ring of symmetric functions over the same base ring. + - This function converts a symmetric polynomial `f` in a + polynomial ring in finitely many variables to a symmetric + function in the monomial basis of the ring of symmetric + functions over the same base ring. EXAMPLES:: @@ -173,12 +177,13 @@ def from_polynomial(self, f, check=True): sage: f = (2*m[2,1]+m[1,1]+3*m[3]).expand(3) sage: m.from_polynomial(f) m[1, 1] + 2*m[2, 1] + 3*m[3] + """ assert self.base_ring() == f.base_ring() if check and not f.is_symmetric(): raise ValueError("%s is not a symmetric polynomial" % f) out = self._from_dict({_Partitions.element_class(_Partitions, list(e)): c - for (e,c) in f.dict().items() + for e, c in f.monomial_coefficients().items() if all(e[i+1] <= e[i] for i in range(len(e)-1))}, remove_zeros=False) return out diff --git a/src/sage/combinat/sf/orthotriang.py b/src/sage/combinat/sf/orthotriang.py index 685674f0a3a..926213f7c0e 100644 --- a/src/sage/combinat/sf/orthotriang.py +++ b/src/sage/combinat/sf/orthotriang.py @@ -49,6 +49,7 @@ class SymmetricFunctionAlgebra_orthotriang(sfa.SymmetricFunctionAlgebra_generic) class Element(sfa.SymmetricFunctionAlgebra_generic.Element): pass + @staticmethod def __classcall__(cls, Sym, base, scalar, prefix, basis_name, leading_coeff=None): """ @@ -282,6 +283,8 @@ def product(self, left, right): from sage.combinat.sf.sfa import SymmetricFunctionsFunctor + + class OrthotriangBasisFunctor(SymmetricFunctionsFunctor): """ A constructor for algebras of symmetric functions constructed by @@ -345,4 +348,4 @@ def _apply_functor(self, R): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.orthotriang', 'SymmetricFunctionAlgebraElement_orthotriang', SymmetricFunctionAlgebra_orthotriang.Element) +register_unpickle_override('sage.combinat.sf.orthotriang', 'SymmetricFunctionAlgebraElement_orthotriang', SymmetricFunctionAlgebra_orthotriang.Element) diff --git a/src/sage/combinat/sf/powersum.py b/src/sage/combinat/sf/powersum.py index 961e7553450..ce64edef000 100644 --- a/src/sage/combinat/sf/powersum.py +++ b/src/sage/combinat/sf/powersum.py @@ -325,9 +325,9 @@ def scalar(self, x, zee=None): parent = self.parent() x = parent(x) if zee is None: - f = lambda part1, part2: sfa.zee(part1) + f = lambda part1, part2: sfa.zee(part1) else: - f = lambda part1, part2: zee(part1) + f = lambda part1, part2: zee(part1) return parent._apply_multi_module_morphism(self, x, f, orthogonal=True) def _derivative(self, part): @@ -933,4 +933,6 @@ def f(partition): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.powersum', 'SymmetricFunctionAlgebraElement_power', SymmetricFunctionAlgebra_power.Element) +register_unpickle_override('sage.combinat.sf.powersum', + 'SymmetricFunctionAlgebraElement_power', + SymmetricFunctionAlgebra_power.Element) diff --git a/src/sage/combinat/sf/schur.py b/src/sage/combinat/sf/schur.py index fb170a25370..6d68365f270 100644 --- a/src/sage/combinat/sf/schur.py +++ b/src/sage/combinat/sf/schur.py @@ -843,4 +843,6 @@ def f(partition): # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.sf.schur', 'SymmetricFunctionAlgebraElement_schur', SymmetricFunctionAlgebra_schur.Element) +register_unpickle_override('sage.combinat.sf.schur', + 'SymmetricFunctionAlgebraElement_schur', + SymmetricFunctionAlgebra_schur.Element) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 9e45bdb2270..794f7a0cb42 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -1982,7 +1982,7 @@ def _change_by_plethysm(self, x, expr, deg_one): # Convert to the power sum p = self.realization_of().power() p_x = p(x) - expr_k = lambda k: expr.subs(**dict([(str(x),x**k) for x in deg_one])) + expr_k = lambda k: expr.subs(**{str(x): x**k for x in deg_one}) f = lambda m,c: (m, c*prod([expr_k(k) for k in m])) return self(p_x.map_item(f)) @@ -6469,6 +6469,7 @@ def exponential_specialization(self, t=None, q=1): from sage.categories.commutative_rings import CommutativeRings from sage.categories.functor import Functor + class SymmetricFunctionsFunctor(ConstructionFunctor): """ A constructor for algebras of symmetric functions. @@ -6899,7 +6900,7 @@ def _from_polynomial(p, f): n = p.parent().ngens() if n == 1: d = {_Partitions.from_exp([e]): c - for e, c in p.dict().items()} + for e, c in p.monomial_coefficients().items()} else: d = {_Partitions.from_exp(e): c for e, c in p.iterator_exp_coeff(False)} diff --git a/src/sage/combinat/shard_order.py b/src/sage/combinat/shard_order.py index 4bcc75d0871..002983ef747 100644 --- a/src/sage/combinat/shard_order.py +++ b/src/sage/combinat/shard_order.py @@ -88,7 +88,7 @@ def __init__(self, p): Digraph on 3 vertices """ self.runs = p.decreasing_runs(as_tuple=True) - self.run_indices = [None] * (len(p) + 1) + self.run_indices = [0] * (len(p) + 1) for i, bloc in enumerate(self.runs): for j in bloc: self.run_indices[j] = i @@ -127,7 +127,7 @@ def __le__(self, other): sage: e1 <= e0 False """ - if type(self) is not type(other) or len(self) != len(other): + if not isinstance(other, ShardPosetElement) or len(self) != len(other): raise TypeError("these are not comparable") if self.runs == other.runs: return True diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 37bbd402c14..6d889425d28 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -2716,9 +2716,11 @@ def _add_strip(sub_tab, full_tab, length): if sub_tab and len(sub_tab) < len(full_tab): plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, full_tab[len(sub_tab)])) - for row in reversed(range(1, len(sub_tab))): - plat_list.append(min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) - - sub_tab[row] - primed_strip[row]) + plat_list.extend( + min(sub_tab[row-1] + primed_strip[row-1] - 1, full_tab[row]) + - sub_tab[row] - primed_strip[row] + for row in reversed(range(1, len(sub_tab)))) + if sub_tab: plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) else: diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index bee333dc9a5..aad38f9afbf 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1032,12 +1032,9 @@ def cells(self): """ outer = self.outer() inner = self.inner()[:] - inner += [0]*(len(outer)-len(inner)) - res = [] - for i in range(len(outer)): - for j in range(inner[i], outer[i]): - res.append( (i,j) ) - return res + inner += [0] * (len(outer) - len(inner)) + return [(i, j) for i, outi in enumerate(outer) + for j in range(inner[i], outi)] def to_list(self): """ @@ -1148,15 +1145,13 @@ def rows_intersection_set(self): sage: skp.rows_intersection_set() == cells True """ - res = [] outer = self.outer() inner = self.inner() - inner += [0] * int(len(outer)-len(inner)) + inner += [0] * (len(outer) - len(inner)) - for i in range(len(outer)): - for j in range(outer[i]): - if outer[i] != inner[i]: - res.append((i,j)) + res = [(i, j) for i, outi in enumerate(outer) + for j in range(outi) + if outi != inner[i]] return Set(res) def columns_intersection_set(self): @@ -1238,7 +1233,7 @@ def jacobi_trudi(self): h = SymmetricFunctions(QQ).homogeneous() H = MatrixSpace(h, nn) - q = q + [0]*int(nn-len(q)) + q = q + [0] * (nn - len(q)) m = [] for i in range(1,nn+1): row = [] diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index ca57594ed71..44189013d5c 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1172,10 +1172,10 @@ def row_stabilizer(self): # tableau, by including the identity permutation on the set [1..k]. k = self.size() gens = [list(range(1, k + 1))] - for row in self: - for j in range(len(row) - 1): - if row[j] is not None: - gens.append((row[j], row[j + 1])) + gens.extend((row[j], row[j + 1]) + for row in self + for j in range(len(row) - 1) + if row[j] is not None) return PermutationGroup(gens) def column_stabilizer(self): @@ -1777,12 +1777,10 @@ def cells(self): sage: s.cells() [(0, 1), (0, 2), (1, 0), (2, 0)] """ - res = [] - for i in range(len(self)): - for j in range(len(self[i])): - if self[i][j] is not None: - res.append((i, j)) - return res + return [(i, j) + for i, selfi in enumerate(self) + for j in range(len(selfi)) + if selfi[j] is not None] def cells_containing(self, i): r""" @@ -1950,12 +1948,11 @@ def from_expr(self, expr): sage: SkewTableaux().from_expr([[1,1],[[5],[3,4],[1,2]]]) [[None, 1, 2], [None, 3, 4], [5]] """ - skp = [] outer = expr[1] inner = expr[0] + [0] * (len(outer) - len(expr[0])) - for i in range(len(outer)): - skp.append([None] * (inner[i]) + outer[-(i + 1)]) + skp = [[None] * (inner[i]) + outer[-(i + 1)] + for i in range(len(outer))] return self.element_class(self, skp) diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 7e35baa2976..08897c8b6da 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -6794,8 +6794,7 @@ def _powerful_numbers_in_range(self, n, m): # This is naive -- too slow; too much overhead # return [i for i in range(self._n, self._n+how_many) if self.is_powerful(i)] - if n < 4: - n = 4 + n = max(n, 4) # Use PARI directly -- much faster. from sage.libs.pari.all import pari L = pari('v=listcreate(); for(i=%s,%s,if(vecmin(factor(i)[,2])>1,listput(v,i))); v' % (n, m)) @@ -8315,8 +8314,7 @@ def _eval(self, n): continue # d is odd divisor k = min(d, 2 * n // d) - if k > m: - m = k + m = max(k, m) return ZZ(m) diff --git a/src/sage/combinat/specht_module.py b/src/sage/combinat/specht_module.py index 49f3ac01e40..e48e34419fe 100644 --- a/src/sage/combinat/specht_module.py +++ b/src/sage/combinat/specht_module.py @@ -37,6 +37,7 @@ from sage.modules.free_module_element import vector from sage.categories.modules_with_basis import ModulesWithBasis + class SymmetricGroupRepresentation(Representation_abstract): """ Mixin class for symmetric group (algebra) representations. @@ -747,14 +748,15 @@ def retract(self): B = self.basis() COB = matrix([b.lift().to_vector() for b in B]).T P, L, U = COB.LU() - # Since U is upper triangular, the nonzero entriesm must be in the - # upper square portiion of the matrix + # Since U is upper triangular, the nonzero entries must be in the + # upper square portion of the matrix n = len(B) Uinv = U.matrix_from_rows(range(n)).inverse() - # This is a slight abuse as the codomain should be a module with a different - # S_n action, but we only use it internally, so there isn't any problems - PLinv = (P*L).inverse() + # This is a slight abuse as the codomain should be a module + # with a different + # S_n action, but we only use it internally, so there is no problem + PLinv = (P * L).inverse() def retraction(elt): vec = PLinv * elt.to_vector(order=self._support_order) @@ -1145,18 +1147,12 @@ def _to_diagram(D): if isinstance(D, Diagram): return D if D in _Partitions: - D = _Partitions(D).cells() - elif D in SkewPartitions(): - D = SkewPartitions()(D).cells() - elif D in IntegerVectors(): - cells = [] - for i, row in enumerate(D): - for j in range(row): - cells.append((i, j)) - D = cells - else: - D = [tuple(cell) for cell in D] - return D + return _Partitions(D).cells() + if D in SkewPartitions(): + return SkewPartitions()(D).cells() + if D in IntegerVectors(): + return [(i, j) for i, row in enumerate(D) for j in range(row)] + return [tuple(cell) for cell in D] def specht_module_spanning_set(D, SGA=None): diff --git a/src/sage/combinat/species/misc.py b/src/sage/combinat/species/misc.py index 12a9b2c8f70..04211b19d0c 100644 --- a/src/sage/combinat/species/misc.py +++ b/src/sage/combinat/species/misc.py @@ -41,7 +41,10 @@ def change_support(perm, support, change_perm=None): (3,4,5) """ if change_perm is None: - change_perm = prod([PermutationGroupElement((i+1,support[i])) for i in range(len(support)) if i+1 != support[i]], PermutationGroupElement([], SymmetricGroup(support))) + change_perm = prod([PermutationGroupElement((i+1, support[i])) + for i in range(len(support)) + if i+1 != support[i]], + PermutationGroupElement([], SymmetricGroup(support))) if isinstance(perm, PermutationGroup_generic): return PermutationGroup([change_support(g, support, change_perm) for g in perm.gens()]) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 5fea2f5f97b..6c8d923ce9f 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -321,8 +321,10 @@ def __contains__(self, value): True sage: 2 in S False + sage: {1, 2} in S + True """ - if value not in Sets(): + if value not in Sets() and not isinstance(value, (set, frozenset)): return False return all(v in self._s for v in value) diff --git a/src/sage/combinat/subword.py b/src/sage/combinat/subword.py index e5a9afcb804..ff67d63343d 100644 --- a/src/sage/combinat/subword.py +++ b/src/sage/combinat/subword.py @@ -55,7 +55,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator import itertools from sage.structure.parent import Parent diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index b9ea545cd60..47820fc23a5 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -1043,10 +1043,8 @@ def central_orthogonal_idempotents(self): - :meth:`central_orthogonal_idempotent` """ - out = [] - for key in sorted(self._blocks_dictionary, reverse=True): - out.append(self.central_orthogonal_idempotent(key)) - return out + return [self.central_orthogonal_idempotent(key) + for key in sorted(self._blocks_dictionary, reverse=True)] def central_orthogonal_idempotent(self, la, block=True): r""" @@ -2016,9 +2014,9 @@ def seminormal_basis(self, mult='l2r'): basis = [] for part in Partitions_n(self.n): stp = StandardTableaux_shape(part) - for t1 in stp: - for t2 in stp: - basis.append(self.epsilon_ik(t1, t2, mult=mult)) + basis.extend(self.epsilon_ik(t1, t2, mult=mult) + for t1 in stp + for t2 in stp) return basis def dft(self, form=None, mult='l2r'): diff --git a/src/sage/combinat/symmetric_group_representations.py b/src/sage/combinat/symmetric_group_representations.py index dbaebfe3f01..f64e68fecad 100644 --- a/src/sage/combinat/symmetric_group_representations.py +++ b/src/sage/combinat/symmetric_group_representations.py @@ -1024,6 +1024,8 @@ def partition_to_vector_of_contents(partition, reverse=False): from sage.rings.quotient_ring import QuotientRing_generic from sage.combinat.specht_module import SymmetricGroupRepresentation as SymmetricGroupRepresentation_mixin + + class GarsiaProcesiModule(UniqueRepresentation, QuotientRing_generic, SymmetricGroupRepresentation_mixin): r""" A Garsia-Procesi module. diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 19f9033fcea..80e3391b279 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1148,12 +1148,10 @@ def descents(self): sage: Tableau( [[1,2,3],[4,5]] ).descents() [(1, 0), (1, 1)] """ - descents = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] > self[i-1][j]: - descents.append((i, j)) - return descents + return [(i, j) + for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij > self[i-1][j]] def major_index(self): """ @@ -1214,15 +1212,15 @@ def inversions(self): for j, entry in enumerate(row): # c is in position (i,j) # find the d that satisfy condition 1 - for k in range(j+1, len(row)): - if entry > row[k]: - inversions.append(((i, j), (i, k))) + inversions.extend(((i, j), (i, k)) + for k in range(j + 1, len(row)) + if entry > row[k]) # find the d that satisfy condition 2 if i == 0: continue - for k in range(j): - if entry > previous_row[k]: - inversions.append(((i, j), (i-1, k))) + inversions.extend(((i, j), (i - 1, k)) + for k in range(j) + if entry > previous_row[k]) previous_row = row return inversions diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index a24ac5ca809..fd847c4ad1e 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -1084,10 +1084,8 @@ def row_stabilizer(self): # tableau, by including the identity permutation on the set [1..n]. n = max(self.entries()) gens = [list(range(1, n + 1))] - for t in self: - for i in range(len(t)): - for j in range(len(t[i]) - 1): - gens.append((t[i][j], t[i][j + 1])) + gens.extend((ti[j], ti[j + 1]) for t in self + for ti in t for j in range(len(ti) - 1)) return PermutationGroup(gens) def column_stabilizer(self): @@ -2644,11 +2642,10 @@ def an_element(self): ([[1, 2]], [], []) """ if self.size() == 0: - return self.element_class(self, [[] for _ in range(self.level())]) + return self.element_class(self, [[]] * self.level()) tab = [[list(range(1, self.size() + 1))]] - for _ in range(self.level() - 1): - tab.append([]) + tab.extend([] for _ in range(self.level() - 1)) return self.element_class(self, tab) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index e38fcfbc7ed..89a940182d7 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -49,6 +49,7 @@ from __future__ import annotations from sage.combinat.posets.lattices import LatticePoset, MeetSemilattice + def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: r""" Return all Dyck paths from `(0,0)` to `(i,j)` in the `(a \times diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index ebd457d7487..c8248bdc5f2 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -381,7 +381,7 @@ def dual(self): A = self._poly.parent() dict_dual = {(n - dy, n - dx): coeff - for (dx, dy), coeff in self._poly.dict().items()} + for (dx, dy), coeff in self._poly.monomial_coefficients().items()} return M_triangle(A(dict_dual), variables=(x, y)) def transmute(self): @@ -484,7 +484,7 @@ def transpose(self): A = self._poly.parent() dict_dual = {(n - dy, n - dx): coeff - for (dx, dy), coeff in self._poly.dict().items()} + for (dx, dy), coeff in self._poly.monomial_coefficients().items()} return H_triangle(A(dict_dual), variables=(x, y)) def m(self): diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 1b8555f4565..ef77bf66d4e 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -844,15 +844,15 @@ Partial orders on a set of `8` elements, up to isomorphism:: - sage: C = Posets(8); C - Posets containing 8 elements + sage: C = Posets(7); C + Posets containing 7 elements sage: C.cardinality() - 16999 + 2045 :: sage: C.unrank(20).plot() - Graphics object consisting of 20 graphics primitives + Graphics object consisting of ... graphics primitives .. image:: ../../media/a_poset.png diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index c754b029692..23e02ad61fb 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -1110,8 +1110,7 @@ def good_suffix_table(self): res = [l - p[-1]]*(l+1) for i in range(1, l+1): j = l - p[i - 1] - if res[j] > (i - p[i-1]): - res[j] = i - p[i-1] + res[j] = min(res[j], i - p[i-1]) return res @cached_method @@ -3455,8 +3454,7 @@ def critical_exponent(self): current_pos = k-j+l-1 pft[current_pos] = m current_exp = QQ((current_pos+1, current_pos+1-m)) - if current_exp > best_exp: - best_exp = current_exp + best_exp = max(current_exp, best_exp) for ((i, j), u) in st._transition_function[v].items(): if j is None: j = self.length() @@ -6319,8 +6317,7 @@ def delta(self): for s in self: if s == ss: c += 1 - if c > max_c: - max_c = c + max_c = max(c, max_c) else: v.append(c) ss = s diff --git a/src/sage/combinat/words/meson.build b/src/sage/combinat/words/meson.build new file mode 100644 index 00000000000..bb12f65d28f --- /dev/null +++ b/src/sage/combinat/words/meson.build @@ -0,0 +1,37 @@ +py.install_sources( + 'abstract_word.py', + 'all.py', + 'alphabet.py', + 'finite_word.py', + 'infinite_word.py', + 'lyndon_word.py', + 'morphic.py', + 'morphism.py', + 'paths.py', + 'shuffle_product.py', + 'suffix_trees.py', + 'word.py', + 'word_datatypes.pxd', + 'word_generators.py', + 'word_infinite_datatypes.py', + 'word_options.py', + 'words.py', + subdir: 'sage/combinat/words', +) + +extension_data = { + 'word_char' : files('word_char.pyx'), + 'word_datatypes' : files('word_datatypes.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/combinat/words', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 79c00b5c20e..a17331b7b82 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -1801,10 +1801,8 @@ def height_vector(self): y_min = y y_max = y else: - if y > y_max: - y_max = y - if y < y_min: - y_min = y + y_max = max(y, y_max) + y_min = min(y, y_min) h_vec.append(y_max - y_min) return h_vec @@ -1866,10 +1864,8 @@ def width_vector(self): x_min = x x_max = x else: - if x > x_max: - x_max = x - if x < x_min: - x_min = x + x_max = max(x, x_max) + x_min = min(x, x_min) w_vec.append(x_max - x_min) return w_vec diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index c230c90057d..e940582f83c 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -293,7 +293,7 @@ def _sortkey_letters(self, letter1): rk = self.alphabet().rank return rk(letter1) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" TESTS:: @@ -309,7 +309,7 @@ def __eq__(self, other): return self is other or (type(self) is type(other) and self.alphabet() == other.alphabet()) - def __ne__(self, other): + def __ne__(self, other) -> bool: r""" TESTS:: @@ -479,7 +479,8 @@ def _word_from_word(self, data): return self._element_classes['list'](self, data) from sage.combinat.words.word_datatypes import (WordDatatype_str, - WordDatatype_list, WordDatatype_tuple) + WordDatatype_list, + WordDatatype_tuple) if isinstance(data, WordDatatype_str): return self._element_classes['str'](self, data._data) if isinstance(data, WordDatatype_tuple): @@ -487,8 +488,8 @@ def _word_from_word(self, data): if isinstance(data, WordDatatype_list): return self._element_classes['list'](self, data._data) - from sage.combinat.words.word_infinite_datatypes import (WordDatatype_callable, - WordDatatype_iter) + from sage.combinat.words.word_infinite_datatypes import \ + (WordDatatype_callable, WordDatatype_iter) if isinstance(data, WordDatatype_callable): length = data.length() data = data._func @@ -849,7 +850,7 @@ def __call__(self, data=None, length=None, datatype=None, caching=True, check=Tr self._check(w) return w - def _repr_(self): + def _repr_(self) -> str: """ EXAMPLES:: @@ -875,14 +876,14 @@ def _an_element_(self): """ try: some_letters = list(self.alphabet().some_elements()) - except Exception: + except (TypeError, ValueError, AttributeError, NotImplementedError): return self([]) if len(some_letters) == 1: return self([some_letters[0]] * 3) - else: - a, b = some_letters[:2] - return self([b, a, b]) + + a, b = some_letters[:2] + return self([b, a, b]) def iterate_by_length(self, l=1): r""" @@ -912,10 +913,20 @@ def iterate_by_length(self, l=1): Traceback (most recent call last): ... TypeError: the parameter l (='a') must be an integer + + TESTS:: + + sage: W = FiniteWords(NN) + sage: list(W.iterate_by_length(1)) + Traceback (most recent call last): + ... + NotImplementedError: cannot iterate over words for infinite alphabets """ if not isinstance(l, (int, Integer)): raise TypeError("the parameter l (=%r) must be an integer" % l) cls = self._element_classes['tuple'] + if not self.alphabet().is_finite(): + raise NotImplementedError("cannot iterate over words for infinite alphabets") for w in itertools.product(self.alphabet(), repeat=l): yield cls(self, w) @@ -961,7 +972,7 @@ def __iter__(self): for l in itertools.count(): yield from self.iterate_by_length(l) - def __contains__(self, x): + def __contains__(self, x) -> bool: """ Test whether ``self`` contains ``x``. @@ -1216,8 +1227,7 @@ def iter_morphisms(self, arg=None, codomain=None, min_length=1): TypeError: codomain (=a) must be an instance of FiniteWords """ n = self.alphabet().cardinality() - if min_length < 0: - min_length = 0 + min_length = max(min_length, 0) # create an iterable of compositions (all "compositions" if arg is # None, or [arg] otherwise) if arg is None: diff --git a/src/sage/cpython/_py2_random.py b/src/sage/cpython/_py2_random.py index a86f66c5ba0..e5f160b11cc 100644 --- a/src/sage/cpython/_py2_random.py +++ b/src/sage/cpython/_py2_random.py @@ -45,6 +45,7 @@ import _random + class Random(_random.Random): """Random number generator base class used by bound module functions. diff --git a/src/sage/cpython/meson.build b/src/sage/cpython/meson.build new file mode 100644 index 00000000000..fdd99770782 --- /dev/null +++ b/src/sage/cpython/meson.build @@ -0,0 +1,44 @@ +py.install_sources( + '__init__.py', + '_py2_random.py', + 'all.py', + 'cython_metaclass.h', + 'cython_metaclass.pxd', + 'dict_del_by_value.pxd', + 'dict_internal.h', + 'getattr.pxd', + 'pycore_long.h', + 'pycore_long.pxd', + 'python_debug.h', + 'python_debug.pxd', + 'pyx_visit.h', + 'string.pxd', + 'string_impl.h', + 'type.pxd', + 'wrapperdescr.pxd', + subdir: 'sage/cpython', +) + +extension_data = { + 'atexit' : files('atexit.pyx'), + 'builtin_types' : files('builtin_types.pyx'), + 'cython_metaclass' : files('cython_metaclass.pyx'), + 'debug' : files('debug.pyx'), + 'dict_del_by_value' : files('dict_del_by_value.pyx'), + 'getattr' : files('getattr.pyx'), + 'string' : files('string.pyx'), + 'type' : files('type.pyx'), + 'wrapperdescr' : files('wrapperdescr.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/cpython', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/crypto/block_cipher/miniaes.py b/src/sage/crypto/block_cipher/miniaes.py index babb4fec8b0..0a421c17e5e 100644 --- a/src/sage/crypto/block_cipher/miniaes.py +++ b/src/sage/crypto/block_cipher/miniaes.py @@ -35,6 +35,7 @@ from sage.rings.integer import Integer from sage.structure.sage_object import SageObject + class MiniAES(SageObject): r""" This class implements the Mini Advanced Encryption Standard (Mini-AES) diff --git a/src/sage/crypto/block_cipher/sdes.py b/src/sage/crypto/block_cipher/sdes.py index a474530199b..9b29d25ac15 100644 --- a/src/sage/crypto/block_cipher/sdes.py +++ b/src/sage/crypto/block_cipher/sdes.py @@ -31,6 +31,7 @@ from sage.monoids.string_monoid import BinaryStrings from sage.structure.sage_object import SageObject + class SimplifiedDES(SageObject): r""" This class implements the Simplified Data Encryption Standard (S-DES) diff --git a/src/sage/crypto/cipher.py b/src/sage/crypto/cipher.py index 09f9fe902cb..98a00db47dc 100644 --- a/src/sage/crypto/cipher.py +++ b/src/sage/crypto/cipher.py @@ -16,6 +16,7 @@ from sage.structure.element import Element + class Cipher(Element): """ Cipher class @@ -52,6 +53,7 @@ def domain(self): def codomain(self): return self.parent().cipher_codomain() + class SymmetricKeyCipher(Cipher): """ Symmetric key cipher class @@ -62,6 +64,7 @@ def __init__(self, parent, key): """ Cipher.__init__(self, parent, key) + class PublicKeyCipher(Cipher): """ Public key cipher class diff --git a/src/sage/crypto/classical.py b/src/sage/crypto/classical.py index 3e2e618ea29..82811ab56a8 100644 --- a/src/sage/crypto/classical.py +++ b/src/sage/crypto/classical.py @@ -2969,6 +2969,7 @@ def random_key(self): from sage.misc.prandom import randint return Integer(randint(0, self.alphabet_size() - 1)) + class SubstitutionCryptosystem(SymmetricKeyCryptosystem): """ Create a substitution cryptosystem. @@ -3193,6 +3194,7 @@ def enciphering(self, K, M): e = self(K) return e(M) + class TranspositionCryptosystem(SymmetricKeyCryptosystem): """ Create a transposition cryptosystem of block length ``n``. diff --git a/src/sage/crypto/classical_cipher.py b/src/sage/crypto/classical_cipher.py index b03aedff480..cb2a03ede29 100644 --- a/src/sage/crypto/classical_cipher.py +++ b/src/sage/crypto/classical_cipher.py @@ -232,6 +232,7 @@ def inverse(self): raise ValueError("Argument\n\n%s\n\nmust be an invertible cipher." % self) return E(B) + class ShiftCipher(SymmetricKeyCipher): r""" Shift cipher class. This is the class that does the actual work of @@ -374,6 +375,7 @@ def _repr_(self): # as the alphabet used for the plaintext and ciphertext spaces. return "Shift cipher on %s" % self.parent().cipher_domain() + class SubstitutionCipher(SymmetricKeyCipher): """ Substitution cipher class @@ -447,6 +449,7 @@ def inverse(self): K = E.inverse_key(self.key()) return E(K) + class TranspositionCipher(SymmetricKeyCipher): """ Transition cipher class @@ -517,6 +520,7 @@ def inverse(self): K = E.inverse_key(self.key()) return E(K) + class VigenereCipher(SymmetricKeyCipher): """ Vigenere cipher class diff --git a/src/sage/crypto/key_exchange/diffie_hellman.py b/src/sage/crypto/key_exchange/diffie_hellman.py index bdd25affe3b..47b6bd392a8 100644 --- a/src/sage/crypto/key_exchange/diffie_hellman.py +++ b/src/sage/crypto/key_exchange/diffie_hellman.py @@ -33,6 +33,7 @@ from typing import Union + class DiffieHellman(KeyExchangeScheme): @experimental(37305) diff --git a/src/sage/crypto/key_exchange/key_exchange_scheme.py b/src/sage/crypto/key_exchange/key_exchange_scheme.py index ff250357eb1..8ddf0c89de6 100644 --- a/src/sage/crypto/key_exchange/key_exchange_scheme.py +++ b/src/sage/crypto/key_exchange/key_exchange_scheme.py @@ -23,6 +23,7 @@ from sage.misc.superseded import experimental from sage.structure.sage_object import SageObject + class KeyExchangeScheme(SageObject): """ Abstract base class for key exchange schemes. diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index 355bfd1c3ba..430ab98f3ae 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -24,6 +24,7 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_general + def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): r""" diff --git a/src/sage/crypto/lwe.py b/src/sage/crypto/lwe.py index 4eee100c205..7403260c514 100644 --- a/src/sage/crypto/lwe.py +++ b/src/sage/crypto/lwe.py @@ -399,6 +399,7 @@ def __init__(self, n, secret_dist='uniform', m=None): D = DiscreteGaussianDistributionIntegerSampler(s/sqrt(2*pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m) + class LindnerPeikert(LWE): """ LWE oracle with parameters as in [LP2011]_. @@ -512,6 +513,7 @@ def __init__(self, n, instance='key', m=None): else: raise TypeError("Parameter instance=%s not understood." % (instance)) + class RingLWE(SageObject): """ Ring Learning with Errors oracle. @@ -607,6 +609,7 @@ def __call__(self): a = self.R_q.random_element() return vector(a), vector(a * (self.__s) + self.D()) + class RingLindnerPeikert(RingLWE): """ Ring-LWE oracle with parameters as in [LP2011]_. @@ -650,6 +653,7 @@ def __init__(self, N, delta=0.01, m=None): D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m) + class RingLWEConverter(SageObject): """ Wrapper callable to convert Ring-LWE oracles into LWE oracles by @@ -712,6 +716,7 @@ def _repr_(self): """ return "RingLWEConverter(%s)" % str(self.ringlwe) + def samples(m, n, lwe, seed=None, balanced=False, **kwds): """ Return ``m`` LWE samples. diff --git a/src/sage/crypto/meson.build b/src/sage/crypto/meson.build new file mode 100644 index 00000000000..633c048a59c --- /dev/null +++ b/src/sage/crypto/meson.build @@ -0,0 +1,38 @@ +py.install_sources( + '__init__.py', + 'all.py', + 'boolean_function.pxd', + 'cipher.py', + 'classical.py', + 'classical_cipher.py', + 'cryptosystem.py', + 'lattice.py', + 'lfsr.py', + 'lwe.py', + 'sboxes.py', + 'stream.py', + 'stream_cipher.py', + 'util.py', + subdir: 'sage/crypto', +) + +extension_data = { + 'boolean_function' : files('boolean_function.pyx'), + 'sbox' : files('sbox.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/crypto', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +install_subdir('block_cipher', install_dir: sage_install_dir / 'crypto') +install_subdir('key_exchange', install_dir: sage_install_dir / 'crypto') +install_subdir('mq', install_dir: sage_install_dir / 'crypto') +install_subdir('public_key', install_dir: sage_install_dir / 'crypto') diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index dc75f5d165b..132e7356d24 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -613,8 +613,8 @@ def __call__(self, text, key, algorithm='encrypt', format='hex'): elif algorithm == 'decrypt': return self.decrypt(text, key, format) else: - raise ValueError(("keyword 'algorithm' must be either 'encrypt' " - "or 'decrypt'")) + raise ValueError("keyword 'algorithm' must be either 'encrypt' " + "or 'decrypt'") def __repr__(self): r""" @@ -981,8 +981,8 @@ def encrypt(self, plain, key, format='hex'): key_state = self._bin_to_GF(key) roundKeys = self.expand_key(key_state) else: - raise ValueError(("'format' keyword must be either 'hex' or " - "'binary'")) + raise ValueError("'format' keyword must be either 'hex' or " + "'binary'") state = self.add_round_key(state, roundKeys[0]) for r in range(self._Nr-1): @@ -1057,8 +1057,8 @@ def decrypt(self, ciphertext, key, format='hex'): elif format == 'binary': if not isinstance(ciphertext, str) or \ any(c not in '01' for c in ciphertext): - raise TypeError(("'ciphertext' keyword must be a binary " - "string")) + raise TypeError("'ciphertext' keyword must be a binary " + "string") if len(ciphertext) != 32 * self._Nb: msg = "'ciphertext' keyword's length must be {0}, not {1}" raise ValueError(msg.format(32 * self._Nb, len(ciphertext))) @@ -1072,8 +1072,8 @@ def decrypt(self, ciphertext, key, format='hex'): key_state = self._bin_to_GF(key) roundKeys = self.expand_key(key_state) else: - raise ValueError(("'format' keyword must be either \'hex\' or " - "'binary'")) + raise ValueError("'format' keyword must be either \'hex\' or " + "'binary'") state = self.add_round_key(state, roundKeys[self._Nr]) state = self.shift_rows(state, algorithm='decrypt') @@ -1874,8 +1874,8 @@ def _sub_bytes_pc(self, row, col, algorithm='encrypt', no_inversion=False): else: return result ** 254 else: - raise ValueError(("keyword 'algorithm' must be either 'encrypt' " - "or 'decrypt'")) + raise ValueError("keyword 'algorithm' must be either 'encrypt' " + "or 'decrypt'") def _srd(self, el, algorithm='encrypt'): r""" @@ -1910,8 +1910,8 @@ def _srd(self, el, algorithm='encrypt'): state = [el] + [self._F.zero()]*((4 * self._Nb)-1) return p(state) ** 254 else: - raise ValueError(("keyword 'algorithm' must be either 'encrypt' " - "or 'decrypt'")) + raise ValueError("keyword 'algorithm' must be either 'encrypt' " + "or 'decrypt'") def sub_bytes(self, state, algorithm='encrypt'): r""" @@ -2010,8 +2010,8 @@ def _mix_columns_pc(self, row, col, algorithm='encrypt'): elif algorithm == 'decrypt': coeffs = self._mixcols_D else: - raise ValueError(("keyword 'algorithm' must be either 'encrypt' " - "or 'decrypt'")) + raise ValueError("keyword 'algorithm' must be either 'encrypt' " + "or 'decrypt'") return sum([coeffs[row,k] * self.state_vrs[k,col] for k in range(4)]) def mix_columns(self, state, algorithm='encrypt'): @@ -2109,8 +2109,8 @@ def _shift_rows_pc(self, row, col, algorithm='encrypt'): elif algorithm == 'decrypt': offs = self._shiftrows_offsets_D else: - raise ValueError(("keyword 'algorithm' must be either 'encrypt' " - "or 'decrypt'")) + raise ValueError("keyword 'algorithm' must be either 'encrypt' " + "or 'decrypt'") return self.state_vrs[row, (col + offs[4 - self._Nb][row]) % 4] def shift_rows(self, state, algorithm='encrypt'): diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 8c372a22e92..610a97b05fb 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -2214,9 +2214,9 @@ def antiphi(self, l): True """ if isinstance(l, Matrix): - ret = [e for e in l.transpose().list()[0:-1:self.e]] + ret = l.transpose().list()[0:-1:self.e] else: - ret = [e for e in l[0:-1:self.e]] + ret = l[0:-1:self.e] if isinstance(l, list): return ret @@ -2448,6 +2448,7 @@ def field_polynomials(self, name, i, l=None): _vars = self.vars(name, i, l, e) return [_vars[e*j+k]**2 - _vars[e*j+(k+1) % e] for j in range(l) for k in range(e)] + class SR_gf2(SR_generic): def __init__(self, n=1, r=1, c=1, e=4, star=False, **kwargs): r""" @@ -3162,6 +3163,7 @@ def field_polynomials(self, name, i, l=None): _vars = self.vars(name, i, l, e) return [_vars[e*j+k]**2 - _vars[e*j+k] for j in range(l) for k in range(e)] + class SR_gf2_2(SR_gf2): """ This is an example how to customize the SR constructor. @@ -3246,6 +3248,7 @@ def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, F = S.polynomials(w, x, degree=e-2, groebner=groebner) return F + class AllowZeroInversionsContext: """ Temporarily allow zero inversion. @@ -3294,6 +3297,7 @@ def __exit__(self, typ, value, tb): """ self.sr._allow_zero_inversions = self.allow_zero_inversions + def test_consistency(max_n=2, **kwargs): r""" Test all combinations of ``r``, ``c``, ``e`` and ``n`` in ``(1, diff --git a/src/sage/crypto/public_key/blum_goldwasser.py b/src/sage/crypto/public_key/blum_goldwasser.py index c4338ae46af..cf09d4038c8 100644 --- a/src/sage/crypto/public_key/blum_goldwasser.py +++ b/src/sage/crypto/public_key/blum_goldwasser.py @@ -44,6 +44,7 @@ floor = Function_floor() IntegerModRing = IntegerModFactory("IntegerModRing") + class BlumGoldwasser(PublicKeyCryptosystem): r""" The Blum-Goldwasser probabilistic public-key encryption scheme. diff --git a/src/sage/crypto/sbox.pyx b/src/sage/crypto/sbox.pyx index 85c8a562ddd..30b7a3cd8c9 100644 --- a/src/sage/crypto/sbox.pyx +++ b/src/sage/crypto/sbox.pyx @@ -445,7 +445,6 @@ cdef class SBox(SageObject): return K(self._S_list[ X]) except TypeError: raise TypeError("cannot apply SBox to %s" % (X,)) - raise TypeError("the characteristic of the base field must be 2") V = None try: V = K.vector_space(map=False) diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index 4a9ef986b6e..a9e94d63b24 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -25,6 +25,7 @@ IntegerModRing = IntegerModFactory("IntegerModRing") + class LFSRCryptosystem(SymmetricKeyCryptosystem): """ Linear feedback shift register cryptosystem class @@ -101,6 +102,7 @@ def encoding(self,M): except Exception: raise TypeError("Argument M = %s does not encode in the cipher domain" % M) + class ShrinkingGeneratorCryptosystem(SymmetricKeyCryptosystem): """ Shrinking generator cryptosystem class @@ -165,6 +167,7 @@ def encoding(self,M): except Exception: raise TypeError("Argument M = %s does not encode in the cipher domain" % M) + def blum_blum_shub(length, seed=None, p=None, q=None, lbound=None, ubound=None, ntries=100): r""" diff --git a/src/sage/crypto/stream_cipher.py b/src/sage/crypto/stream_cipher.py index ef1b44e6f2b..4b6d36ad925 100644 --- a/src/sage/crypto/stream_cipher.py +++ b/src/sage/crypto/stream_cipher.py @@ -14,6 +14,7 @@ from .cipher import SymmetricKeyCipher from sage.monoids.string_monoid_element import StringMonoidElement + class LFSRCipher(SymmetricKeyCipher): def __init__(self, parent, poly, IS): """ @@ -143,6 +144,7 @@ def initial_state(self): """ return self.key()[1] + class ShrinkingGeneratorCipher(SymmetricKeyCipher): def __init__(self, parent, e1, e2): """ diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index 199d09c2758..66ffb78ef2b 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -93,6 +93,7 @@ def ascii_integer(B): return sum([L[7], L[6]*2, L[5]*4, L[4]*8, L[3]*16, L[2]*32, L[1]*64, L[0]*128]) + def ascii_to_bin(A): r""" Return the binary representation of the ASCII string ``A``. @@ -159,6 +160,7 @@ def ascii_to_bin(A): bin = BinaryStrings() return bin.encoding("".join(list(A))) + def bin_to_ascii(B): r""" Return the ASCII representation of the binary string ``B``. @@ -347,6 +349,7 @@ def has_blum_prime(lbound, ubound): return True return False + def is_blum_prime(n): r""" Determine whether or not ``n`` is a Blum prime. @@ -384,6 +387,7 @@ def is_blum_prime(n): else: return False + def least_significant_bits(n, k): r""" Return the ``k`` least significant bits of ``n``. @@ -438,6 +442,7 @@ def least_significant_bits(n, k): """ return [int(_) for _ in list(n.binary()[-k:])] + def random_blum_prime(lbound, ubound, ntries=100): r""" A random Blum prime within the specified bounds. diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 1b23b5ed8a7..44635ad2364 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -1205,7 +1205,7 @@ cdef class BoundedIntegerSequence: if other is None or self is None: raise TypeError('cannot concatenate bounded integer sequence and None') myself = self # may result in a type error - right = other # --"-- + right = other # --"-- if right.data.itembitsize != myself.data.itembitsize: raise ValueError("can only concatenate bounded integer sequences of compatible bounds") out = BoundedIntegerSequence.__new__(BoundedIntegerSequence, 0, None) diff --git a/src/sage/data_structures/meson.build b/src/sage/data_structures/meson.build new file mode 100644 index 00000000000..8a94548917b --- /dev/null +++ b/src/sage/data_structures/meson.build @@ -0,0 +1,41 @@ +py.install_sources( + 'all.py', + 'binary_matrix.pxd', + 'binary_search.pxd', + 'bitset.pxd', + 'bitset_base.pxd', + 'bitset_intrinsics.h', + 'blas_dict.pxd', + 'bounded_integer_sequences.pxd', + 'list_of_pairs.pxd', + 'mutable_poset.py', + 'sparse_bitset.pxd', + 'stream.py', + subdir: 'sage/data_structures', +) + +extension_data = { + 'binary_search' : files('binary_search.pyx'), + 'bitset' : files('bitset.pyx'), + 'bitset_base' : files('bitset_base.pyx'), + 'blas_dict' : files('blas_dict.pyx'), + 'bounded_integer_sequences' : files('bounded_integer_sequences.pyx'), + 'list_of_pairs' : files('list_of_pairs.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/data_structures', + install: true, + include_directories: [ + inc_cpython, + inc_data_structures, + inc_flint, + inc_rings, + ], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index ae3cbb71ecd..ddca8fb26b2 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -107,7 +107,7 @@ lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) -class Stream(): +class Stream: """ Abstract base class for all streams. @@ -2410,7 +2410,7 @@ def stretched_power_restrict_degree(self, i, m, d): _raise_variables(c, i, self._degree_one) for mon, c in power_d} else: - terms = {tuple((mu.stretch(i) for mu in mon)): + terms = {tuple(mu.stretch(i) for mu in mon): _raise_variables(c, i, self._degree_one) for mon, c in power_d} return self._basis(self._p.element_class(self._p, terms)) diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index 38458806b76..e782676fde9 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -15,6 +15,7 @@ # **************************************************************************** from collections.abc import Mapping + class DictInMapping(Mapping): def __init__(self, dict): """ diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 0349afb2518..44c8fbcdc7f 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -567,8 +567,8 @@ def cremona_to_lmfdb(cremona_label, CDB=None): if CDB is None: CDB = CremonaDatabase() classes = CDB.isogeny_classes(N) - ft = int(53) - tff = int(255) # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example) + ft = 53 + tff = 255 # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example) isos = [] for i, iso in enumerate(classes): alist = iso[0][0] @@ -617,8 +617,8 @@ def lmfdb_to_cremona(lmfdb_label, CDB=None): if CDB is None: CDB = CremonaDatabase() classes = CDB.isogeny_classes(N) - ft = int(53) - tff = int(255) # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example) + ft = 53 + tff = 255 # This should be enough to distinguish between curves (using heuristics from Sato-Tate for example) isos = [] for i, iso in enumerate(classes): alist = iso[0][0] @@ -818,7 +818,7 @@ def curves(self, N): ret[iso+str(num)] = [eval(c[1]),c[2],c[3]] if N == 990: del ret['h1'] - ret['h3'] = [[1,-1,1,-1568,-4669],int(1),int(6)] + ret['h3'] = [[1,-1,1,-1568,-4669],1,6] return ret def coefficients_and_data(self, label): @@ -1122,10 +1122,10 @@ def iter_optimal(self, conductors): if N == 990: for c in self.__connection__.cursor().execute('SELECT class ' + 'FROM t_class WHERE conductor=990'): - if c[0][-1] == u'h': - yield self.elliptic_curve(c[0]+u'3') + if c[0][-1] == 'h': + yield self.elliptic_curve(c[0]+'3') else: - yield self.elliptic_curve(c[0]+u'1') + yield self.elliptic_curve(c[0]+'1') continue for c in self.__connection__.cursor().execute('SELECT curve ' + 'FROM t_curve,t_class USING(class) WHERE curve=class||1 ' @@ -1643,6 +1643,7 @@ def _init_allgens(self, ftpdata, largest_conductor=0): con.executemany("UPDATE t_curve SET gens=? WHERE curve=?", curve_data) print("Committing...") + self.commit() if largest_conductor and int(v[0]) > largest_conductor: break diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index 2f94439e6f0..668af9abacc 100644 --- a/src/sage/databases/cubic_hecke_db.py +++ b/src/sage/databases/cubic_hecke_db.py @@ -117,7 +117,9 @@ def simplify(mat): d = mat.dict() if isinstance(B, CubicHeckeExtensionRing): # Laurent polynomial cannot be reconstructed from string - res = {k: {tuple(j): u.dict() for j, u in v.dict().items()} for k, v in d.items()} + res = {k: {tuple(j): u.monomial_coefficients() + for j, u in v.monomial_coefficients().items()} + for k, v in d.items()} else: res = {k: str(v) for k, v in d.items()} return res @@ -661,7 +663,7 @@ class section(Enum): in the case of cubic Hecke algebras on more than 4 strands - ``markov_trace`` -- file cache for intermediate results of long calculations in order to recover the results already obtained by - preboius attemps of calculation until the corresponding intermediate + previous attemps of calculation until the corresponding intermediate step EXAMPLES:: @@ -1285,6 +1287,7 @@ def read_basis(num_strands=3): -1], [2, -1, 2], [1, 2, -1, 2], [-1, 2, -1, 2]] return data[num_strands] + def read_irr(variables, num_strands=3): r""" Return precomputed data of Ivan Marin. @@ -1328,6 +1331,7 @@ def read_irr(variables, num_strands=3): 1/b, (1, 1): 1/b, (1, 2): a/b + b/c, (2, 2): 1/c}]]) return data[num_strands] + def read_regl(variables, num_strands=3): r""" Return precomputed data of Ivan Marin. @@ -1401,6 +1405,7 @@ def read_regl(variables, num_strands=3): (22, 6): 1/w, (22, 13): u/w, (22, 22): v/w, (23, 13): 1}]]) return data[num_strands] + def read_regr(variables, num_strands=3): r""" Return precomputed data of Ivan Marin. diff --git a/src/sage/databases/db_class_polynomials.py b/src/sage/databases/db_class_polynomials.py index dd436fe5db7..57acde7c05a 100644 --- a/src/sage/databases/db_class_polynomials.py +++ b/src/sage/databases/db_class_polynomials.py @@ -106,6 +106,7 @@ def __repr__(self): # None of the following are implemented yet. ###################################################### + class AtkinClassPolynomialDatabase(ClassPolynomialDatabase): """ The database of Atkin class polynomials. diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 85c06bf06cd..811a190db8f 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -307,6 +307,8 @@ def mapping(sigma): FINDSTAT_FORM_FOOTER = '' ###################################################################### + + class FindStat(UniqueRepresentation, SageObject): r""" The Combinatorial Statistic Finder. @@ -438,6 +440,7 @@ def _get_json(url, **kwargs): return result raise ConnectionError(response.text) + def _post_json(url, data, **kwargs): """ Return the json response or raise an error. @@ -1793,6 +1796,8 @@ def statistic(x): ###################################################################### # statistics ###################################################################### + + class FindStatCombinatorialStatistic(SageObject): """ A class providing methods to retrieve the first terms of a statistic. @@ -2206,7 +2211,7 @@ def _fetch_data(self): included = _get_json(url)["included"] # slightly simplify the representation - data = {key: val for key, val in included["Statistics"][self.id_str()].items()} + data = dict(included["Statistics"][self.id_str()].items()) # we replace the list of identifiers in Bibliography with the dictionary data["Bibliography"] = included["References"] return data @@ -2393,6 +2398,8 @@ def info(self): _all_statistics = {} + + class FindStatStatistics(UniqueRepresentation, Parent): r""" The class of FindStat statistics. @@ -2740,6 +2747,7 @@ def __len__(self): """ return len(self._result) + class FindStatCompoundStatistic(Element, FindStatCombinatorialStatistic): def __init__(self, id, domain=None, check=True): """ @@ -3036,6 +3044,8 @@ def info(self): ###################################################################### # maps ###################################################################### + + class FindStatCombinatorialMap(SageObject): """ A class serving as common ancestor of :class:`FindStatStatistic` @@ -3043,6 +3053,7 @@ class FindStatCombinatorialMap(SageObject): """ pass + class FindStatMap(Element, FindStatFunction, FindStatCombinatorialMap, @@ -3305,6 +3316,8 @@ def info(self): _all_maps = {} + + class FindStatMaps(UniqueRepresentation, Parent): r""" The class of FindStat maps. @@ -3623,6 +3636,7 @@ def __len__(self): """ return len(self._result) + class FindStatCompoundMap(Element, FindStatCombinatorialMap): def __init__(self, id, domain=None, codomain=None, check=True): """ @@ -3914,6 +3928,8 @@ def _finite_irreducible_cartan_types_by_rank(n): return cartan_types # helper for generation of PlanePartitions + + def _plane_partitions_by_size_aux(n, outer=None): """ Iterate over the plane partitions with `n` boxes, as lists. @@ -3941,6 +3957,7 @@ def _plane_partitions_by_size_aux(n, outer=None): pp = [la] + pp yield pp + def _plane_partitions_by_size(n): """ Iterate over the plane partitions with `n` boxes. @@ -3970,6 +3987,8 @@ def _plane_partitions_by_size(n): yield PlanePartition(pp) # helper for generation of Lattices + + def _finite_lattices(n): """ Iterate over the lattices with `n` elements. @@ -3983,16 +4002,17 @@ def _finite_lattices(n): TESTS:: sage: from sage.databases.findstat import _finite_lattices - sage: [L.cover_relations() for L in _finite_lattices(4)] - [[['bottom', 0], ['bottom', 1], [0, 'top'], [1, 'top']], - [['bottom', 0], [0, 1], [1, 'top']]] + sage: sorted((L.cover_relations() for L in _finite_lattices(4)), + ....: key=len) + [[['bottom', 0], [0, 1], [1, 'top']], + [['bottom', 0], ['bottom', 1], [0, 'top'], [1, 'top']]] """ if n <= 2: for P in Posets(n): if P.is_lattice(): yield LatticePoset(P) else: - for P in Posets(n-2): + for P in Posets(n - 2): Q = P.with_bounds() if Q.is_lattice(): yield LatticePoset(Q) @@ -4332,7 +4352,7 @@ def first_terms(self, function, level=None): g = (x for x in self._sageconstructor_overridden if self.element_level(x) == level) - return lazy_list(((x, function(x)) for x in g)) + return lazy_list((x, function(x)) for x in g) def id(self): r""" diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 779ee127467..89fd66c6078 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -44,6 +44,7 @@ columns_white_list = ['knot_atlas_anon', 'knotilus_page_anon'] columns_black_list = ['homfly_polynomial_old'] + class KnotInfoColumnTypes(Enum): r""" Enum class to specify if a column from the table of knots and links provided @@ -792,7 +793,7 @@ def _test_database(self, **options): sage: from sage.databases.knotinfo_db import KnotInfoDataBase sage: ki_db = KnotInfoDataBase() - sage: TestSuite(ki_db).run() # long time indirect doctest + sage: TestSuite(ki_db).run() # optional - database_knotinfo, long time, indirect doctest """ from sage.knots.knotinfo import KnotInfo from sage.misc.misc import some_tuples @@ -837,6 +838,8 @@ def _test_database(self, **options): 'fibered': ['Fibered', KnotInfoColumnTypes.OnlyKnots], 'unoriented': ['Unoriented', KnotInfoColumnTypes.OnlyLinks], 'symmetry_type': ['Symmetry Type', KnotInfoColumnTypes.OnlyKnots], + 'geometric_type': ['Geometric Type', KnotInfoColumnTypes.OnlyKnots], + 'cosmetic_crossing': ['Cosmetic Crossing', KnotInfoColumnTypes.OnlyKnots], 'width': ['Width', KnotInfoColumnTypes.OnlyKnots], 'arc_notation': ['Arc Notation', KnotInfoColumnTypes.OnlyLinks], 'dt_code': ['DT code', KnotInfoColumnTypes.OnlyLinks] @@ -1018,6 +1021,18 @@ def _test_database(self, **options): 'reversible', 'reversible' ], + dc.geometric_type: [ + '', + 'torus knot T(2,3)', + 'hyperbolic', + 'torus knot T(2,5)', + 'hyperbolic', + 'hyperbolic', + 'hyperbolic', + 'hyperbolic', + 'torus knot T(2,7)', + 'hyperbolic'], + dc.cosmetic_crossing: ['', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'], dc.homfly_polynomial: [ '', '(2*v^2-v^4)+v^2*z^2', diff --git a/src/sage/databases/odlyzko.py b/src/sage/databases/odlyzko.py index 510edb4a8f6..1e19fda8619 100644 --- a/src/sage/databases/odlyzko.py +++ b/src/sage/databases/odlyzko.py @@ -31,6 +31,7 @@ from sage.misc.persist import load from sage.env import SAGE_SHARE + def zeta_zeros(): r""" List of the imaginary parts of the first 2,001,052 zeros of the diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index edef584b6d4..fad05a0add9 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -98,6 +98,7 @@ 'UNIQUE','UPDATE','USING','VACUUM','VALUES','VIEW','VIRTUAL','WHEN', 'WHERE'] + def regexp(expr, item): """ Function to define regular expressions in pysqlite. @@ -179,6 +180,7 @@ def verify_column(col_dict): d['sql'] = col_dict['sql'] return d + def verify_operator(operator): """ Check that ``operator`` is one of the allowed strings. @@ -252,7 +254,7 @@ def construct_skeleton(database): exe1 = cur.execute("PRAGMA table_info(%s)" % table[0]) for col in exe1.fetchall(): if not col[2]: - typ = u'NOTYPE' + typ = 'NOTYPE' else: typ = col[2] skeleton[table[0]][col[1]] = {'sql':typ, @@ -274,6 +276,8 @@ def construct_skeleton(database): p = 0 + + def _create_print_table(cur, col_titles, **kwds): r""" Create a nice printable table from the cursor given with the given @@ -397,6 +401,7 @@ def row_str(row, html): ret += '\n'.join([row_str(row, False) for row in cur]) return ret + class SQLQuery(SageObject): def __init__(self, database, *args, **kwds): """ @@ -477,7 +482,7 @@ def __init__(self, database, *args, **kwds): else: self.__query_string__ = kwds['query_string'] if 'param_tuple' in kwds: - self.__param_tuple__ = tuple((str(x) for x in kwds['param_tuple'])) + self.__param_tuple__ = tuple(str(x) for x in kwds['param_tuple']) else: self.__param_tuple__ = tuple() return @@ -2159,7 +2164,7 @@ def add_rows(self, table_name, rows, entry_order=None): if self.__read_only__: raise RuntimeError('Cannot add rows to read only database.') quest = '(' + ', '.join('?' for i in rows[0]) + ')' - strows = [tuple((str(entry) for entry in row)) for row in rows] + strows = [tuple(str(entry) for entry in row) for row in rows] if entry_order is not None: self.__connection__.executemany('INSERT INTO ' + table_name diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index c30bf4d914f..45c0a6e7e9c 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -347,7 +347,7 @@ def ecdb_num_curves(max_level=200000): """ i = 0 d = SteinWatkinsAllData(i) - v = [int(0) for _ in range(max_level + 1)] + v = [0 for _ in range(max_level + 1)] while True: try: C = next(d) diff --git a/src/sage/doctest/__main__.py b/src/sage/doctest/__main__.py new file mode 100644 index 00000000000..1af1b22ce1d --- /dev/null +++ b/src/sage/doctest/__main__.py @@ -0,0 +1,235 @@ +import argparse +import os +import sys + +# Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env +DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), + '.sage')) + +# Override to not pick up user configuration, see Issue #20270 +os.environ['SAGE_STARTUP_FILE'] = os.path.join(DOT_SAGE, 'init-doctests.sage') + + +def _get_optional_defaults(): + """Return the default value for the --optional flag.""" + optional = ['sage', 'optional'] + + return ','.join(optional) + + +def _make_parser(): + r""" + Return the :class:`argparse.ArgumentParser`. + + TESTS: + + Test that the defaults are the consistent:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.__main__ import _make_parser + sage: os.environ.pop('SAGE_DOCTEST_RANDOM_SEED', None) + ... + sage: parser = _make_parser() + sage: args = parser.parse_args([]) + sage: DD = DocTestDefaults(runtest_default=True); DD + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, + stats_path='.../timings2.json') + sage: D = copy(args.__dict__) + sage: del D['filenames'] + sage: DA = DocTestDefaults(runtest_default=True, **D); DA + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, + stats_path='.../timings2.json') + """ + parser = argparse.ArgumentParser(usage="sage -t [options] filenames", + description="Run all tests in a file or a list of files whose extensions " + "are one of the following: " + ".py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst.") + parser.add_argument("-p", "--nthreads", dest="nthreads", + type=int, nargs='?', const=0, default=1, metavar="N", + help="test in parallel using N threads, with 0 interpreted as max(2, min(8, cpu_count())); " + "when run under the control of the GNU make jobserver (make -j), request as most N job slots") + parser.add_argument("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout") + what = parser.add_mutually_exclusive_group() + what.add_argument("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") + what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library") + parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE") + + parser.add_argument("--format", choices=["sage", "github"], default="sage", + help="set format of error messages and warnings") + parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") + parser.add_argument("-s", "--short", dest="target_walltime", nargs='?', + type=int, default=-1, const=300, metavar="SECONDS", + help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)") + parser.add_argument("--warn-long", dest="warn_long", nargs='?', + type=float, default=-1.0, const=1.0, metavar="SECONDS", + help="warn if tests take more CPU time than SECONDS") + # By default, include all tests marked 'sagemath_doc_html' -- see + # https://github.com/sagemath/sage/issues/25345 and + # https://github.com/sagemath/sage/issues/26110: + parser.add_argument("--optional", metavar="FEATURES", default=_get_optional_defaults(), + help='only run tests including one of the "# optional" tags listed in FEATURES (separated by commas); ' + 'if "sage" is listed, will also run the standard doctests; ' + 'if "sagemath_doc_html" is listed, will also run the tests relying on the HTML documentation; ' + 'if "optional" is listed, will also run tests for installed optional packages or detected features; ' + 'if "external" is listed, will also run tests for available external software; ' + 'if set to "all", then all tests will be run; ' + 'use "!FEATURE" to disable tests marked "# optional - FEATURE". ' + 'Note that "!" needs to be quoted or escaped in the shell.') + parser.add_argument("--hide", metavar="FEATURES", default="", + help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' + 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' + 'if "optional" is listed, will also hide features corresponding to optional packages.') + parser.add_argument("--probe", metavar="FEATURES", default="", + help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' + 'report the tests that pass nevertheless') + parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") + parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", + default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) + parser.add_argument("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") + parser.add_argument("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") + parser.add_argument("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests") + + parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") + parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") + parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") + parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") + parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") + parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") + parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") + parser.add_argument("--only-errors", action="store_true", default=False, help="only output failures, not test successes") + + parser.add_argument("--gdb", action="store_true", default=False, help="run doctests under the control of gdb") + parser.add_argument("--lldb", action="store_true", default=False, help="run doctests under the control of lldb") + parser.add_argument("--valgrind", "--memcheck", action="store_true", default=False, + help="run doctests using Valgrind's memcheck tool. The log " + "files are named sage-memcheck.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--massif", action="store_true", default=False, + help="run doctests using Valgrind's massif tool. The log " + "files are named sage-massif.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--cachegrind", action="store_true", default=False, + help="run doctests using Valgrind's cachegrind tool. The log " + "files are named sage-cachegrind.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--omega", action="store_true", default=False, + help="run doctests using Valgrind's omega tool. The log " + "files are named sage-omega.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + + parser.add_argument("-f", "--failed", action="store_true", default=False, + help="doctest only those files that failed in the previous run") + what.add_argument("-n", "--new", action="store_true", default=False, + help="doctest only those files that have been changed in the repository and not yet been committed") + parser.add_argument("--show-skipped", "--show_skipped", action="store_true", default=False, + help="print a summary at the end of each file of optional tests that were skipped") + + parser.add_argument("--stats_path", "--stats-path", default=os.path.join(DOT_SAGE, "timings2.json"), + help="path to a json dictionary for timings and failure status for each file from previous runs; it will be updated in this run") + parser.add_argument("--baseline_stats_path", "--baseline-stats-path", default=None, + help="path to a json dictionary for timings and failure status for each file, to be used as a baseline; it will not be updated") + + class GCAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + gcopts = dict(DEFAULT=0, ALWAYS=1, NEVER=-1) + new_value = gcopts[values] + setattr(namespace, self.dest, new_value) + + parser.add_argument("--gc", + choices=["DEFAULT", "ALWAYS", "NEVER"], + default=0, + action=GCAction, + help="control garbarge collection " + "(ALWAYS: collect garbage before every test; NEVER: disable gc; DEFAULT: Python default)") + + # The --serial option is only really for internal use, better not + # document it. + parser.add_argument("--serial", action="store_true", default=False, help=argparse.SUPPRESS) + # Same for --die_timeout + parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS) + + parser.add_argument("filenames", help="file names", nargs='*') + return parser + + +def main(): + parser = _make_parser() + # custom treatment to separate properly + # one or several file names at the end + new_arguments = [] + need_filenames = True + in_filenames = False + afterlog = False + for arg in sys.argv[1:]: + if arg in ('-n', '--new', '-a', '--all', '--installed'): + need_filenames = False + elif need_filenames and not (afterlog or in_filenames) and os.path.exists(arg): + in_filenames = True + new_arguments.append('--') + new_arguments.append(arg) + afterlog = arg in ['--logfile', '--stats_path', '--stats-path', + '--baseline_stats_path', '--baseline-stats-path'] + + args = parser.parse_args(new_arguments) + + if not args.filenames and not (args.all or args.new or args.installed): + print('either use --new, --all, --installed, or some filenames') + return 2 + + # Limit the number of threads to 2 to save system resources. + # See Issue #23713, #23892, #30351 + if sys.platform == 'darwin': + os.environ["OMP_NUM_THREADS"] = "1" + else: + os.environ["OMP_NUM_THREADS"] = "2" + + os.environ["SAGE_NUM_THREADS"] = "2" + + from sage.doctest.control import DocTestController + DC = DocTestController(args, args.filenames) + err = DC.run() + + # Issue #33521: Do not run pytest if the pytest configuration is not available. + # This happens when the source tree is not available and SAGE_SRC falls back + # to SAGE_LIB. + from sage.env import SAGE_SRC + if not all(os.path.isfile(os.path.join(SAGE_SRC, f)) + for f in ["conftest.py", "tox.ini"]): + return err + + try: + exit_code_pytest = 0 + import pytest + pytest_options = [] + if args.verbose: + pytest_options.append("-v") + + # #35999: no filename in arguments defaults to "src" + if not args.filenames: + filenames = [SAGE_SRC] + else: + # #31924: Do not run pytest on individual Python files unless + # they match the pytest file pattern. However, pass names + # of directories. We use 'not os.path.isfile(f)' for this so that + # we do not silently hide typos. + filenames = [f for f in args.filenames + if f.endswith("_test.py") or not os.path.isfile(f)] + if filenames: + print(f"Running pytest on {filenames} with options {pytest_options}") + exit_code_pytest = pytest.main(filenames + pytest_options) + if exit_code_pytest == 5: + # Exit code 5 means there were no test files, pass in this case + exit_code_pytest = 0 + + except ModuleNotFoundError: + print("pytest is not installed in the venv, skip checking tests that rely on it") + + if err == 0: + return exit_code_pytest + return err + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 34c7e1299c5..fff35be8307 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -63,28 +63,39 @@ class DocTestDefaults(SageObject): """ This class is used for doctesting the Sage doctest module. - It fills in attributes to be the same as the defaults defined in - ``sage-runtests``, expect for a few places, - which is mostly to make doctesting more predictable. + INPUT: + + - ``runtest_default`` -- (boolean, default ``False``); if ``True``, + fills in attribute to be the same as the defaults defined in + ``sage-runtests``. If ``False``, change defaults in a few places + for use in doctests of the doctester, which is mostly to make + doctesting more predictable. + + - ``**kwds`` -- attributes to override defaults EXAMPLES:: sage: from sage.doctest.control import DocTestDefaults - sage: D = DocTestDefaults() - sage: D + sage: D = DocTestDefaults(); D DocTestDefaults() sage: D.timeout -1 Keyword arguments become attributes:: - sage: D = DocTestDefaults(timeout=100) - sage: D + sage: D = DocTestDefaults(timeout=100); D DocTestDefaults(timeout=100) sage: D.timeout 100 + + The defaults for ``sage-runtests``:: + + sage: D = DocTestDefaults(runtest_default=True); D + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, + stats_path='.../timings2.json') """ - def __init__(self, **kwds): + def __init__(self, runtest_default=False, **kwds): """ Edit these parameters after creating an instance. @@ -109,15 +120,15 @@ def __init__(self, **kwds): self.long = False self.warn_long = -1.0 self.randorder = None - self.random_seed = 0 - self.global_iterations = 1 # sage-runtests default is 0 - self.file_iterations = 1 # sage-runtests default is 0 + self.random_seed = None if runtest_default else 0 + self.global_iterations = 0 if runtest_default else 1 + self.file_iterations = 0 if runtest_default else 1 self.environment = "sage.repl.ipython_kernel.all_jupyter" self.initial = False self.exitfirst = False self.force_lib = False self.if_installed = False - self.abspath = True # sage-runtests default is False + self.abspath = not runtest_default self.verbose = False self.debug = False self.only_errors = False @@ -139,7 +150,10 @@ def __init__(self, **kwds): # automatically anyway. However, this default is still used for # displaying user-defined optional tags and we don't want to see # the auto_optional_tags there. - self.optional = {'sage'} | auto_optional_tags + if runtest_default: + self.optional = ','.join(['sage', 'optional']) + else: + self.optional = {'sage'} | auto_optional_tags self.hide = '' self.probe = '' @@ -149,7 +163,8 @@ def __init__(self, **kwds): # We don't want to use the real stats file by default so that # we don't overwrite timings for the actual running doctests. - self.stats_path = os.path.join(DOT_SAGE, "timings_dt_test.json") + self.stats_path = os.path.join( + DOT_SAGE, "timings2.json" if runtest_default else "timings_dt_test.json") self.__dict__.update(kwds) def _repr_(self): @@ -267,10 +282,14 @@ def skipfile(filename, tested_optional_tags=False, *, sage: skipfile(filename, True) False """ + if filename.endswith('__main__.py'): + if log: + log(f"Skipping '{filename}' because it is a __main__.py file") + return True if filename.endswith('.rst.txt'): ext = '.rst.txt' else: - base, ext = os.path.splitext(filename) + _ , ext = os.path.splitext(filename) # .rst.txt appear in the installed documentation in subdirectories named "_sources" if ext not in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst', '.tex', '.rst.txt'): if log: @@ -323,7 +342,7 @@ def skipfile(filename, tested_optional_tags=False, *, return False -class Logger(): +class Logger: r""" File-like object which implements writing to multiple files at once. @@ -573,36 +592,38 @@ def __del__(self): def _init_warn_long(self): """ - Pick a suitable default for the ``--warn-long`` option if not specified. + Pick a suitable default for the ``--warn-long`` option if not + specified. It is desirable to have all tests (even ``# long`` ones) finish in less than about 5 seconds. Longer tests typically don't add coverage, they just make testing slow. - The default used here is 60 seconds on a modern computer. It - should eventually be lowered to 5 seconds, but its best to - boil the frog slowly. + The default used here is 5 seconds, unless `--long` was used, + in which case it is 30 seconds. - The stored timings are used to adjust this limit according to - the machine running the tests. + TESTS: - EXAMPLES:: + Ensure that the user's command-line options are not changed:: - sage: from sage.doctest.control import DocTestDefaults, DocTestController + sage: from sage.doctest.control import (DocTestDefaults, + ....: DocTestController) sage: DC = DocTestController(DocTestDefaults(), []) sage: DC.options.warn_long = 5.0 sage: DC._init_warn_long() - sage: DC.options.warn_long # existing command-line options are not changed + sage: DC.options.warn_long 5.00000000000000 """ # default is -1.0 if self.options.warn_long >= 0: # Specified on the command line return - try: - self.options.warn_long = 60.0 * self.second_on_modern_computer() - except RuntimeError as err: - if not sage.doctest.DOCTEST_MODE: - print(err) # No usable timing information + + # The developer's guide says that even a "long time" test + # should ideally complete in under five seconds, so we're + # being rather generous here. + self.options.warn_long = 5.0 + if self.options.long: + self.options.warn_long = 30.0 def second_on_modern_computer(self): """ @@ -622,6 +643,9 @@ def second_on_modern_computer(self): sage: DC = DocTestController(DocTestDefaults(), []) sage: DC.second_on_modern_computer() # not tested """ + from sage.misc.superseded import deprecation + deprecation(32981, "this method is no longer used by the sage library and will eventually be removed") + if len(self.stats) == 0: raise RuntimeError('no stored timings available') success = [] @@ -1136,7 +1160,7 @@ def run_doctests(self): sage: DC.run_doctests() Doctesting 1 file. sage -t .../sage/rings/homset.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1213,7 +1237,7 @@ def cleanup(self, final=True): Running doctests with ID ... Doctesting 1 file. sage -t .../rings/all.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1259,25 +1283,33 @@ def _optional_tags_string(self): def _assemble_cmd(self): """ - Assembles a shell command used in running tests under gdb, lldb, or valgrind. + Assemble a shell command used in running tests under gdb, lldb, or valgrind. EXAMPLES:: sage: from sage.doctest.control import DocTestDefaults, DocTestController sage: DC = DocTestController(DocTestDefaults(timeout=123), ["hello_world.py"]) sage: print(DC._assemble_cmd()) - sage-runtests --serial --timeout=123 hello_world.py + ...python... -m sage.doctest --serial... --timeout=123... hello_world.py """ - cmd = "sage-runtests --serial " - opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - if "all" in opt: - raise ValueError("You cannot run gdb/lldb/valgrind on the whole sage library") - for o in ("all", "long", "force_lib", "verbose", "failed", "new"): + cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " + opt = dict_difference(self.options.__dict__, DocTestDefaults(runtest_default=True).__dict__) + # Options with no argument + for o in ("all", "installed", "long", "initial", "exitfirst", + "force_lib", "if_installed", "abspath", "verbose", + "debug", "only_errors", "failed", "new", + "show_skipped"): if o in opt: - cmd += "--%s " % o - for o in ("timeout", "randorder", "stats_path"): + cmd += "--%s " % o.replace('_', '-') + # Options with one argument + for o in ("timeout", "die_timeout", "logfile", "warn_long", "randorder", + "random_seed", "global_iterations", "file_iterations", + "environment", "baseline_stats_path", "stats_path"): if o in opt: - cmd += "--%s=%s " % (o, opt[o]) + cmd += "--%s=%s " % (o.replace('_', '-'), opt[o]) + # One with a different dest + if "target_walltime" in opt: + cmd += "--%s=%s " % ("short", opt[o]) if "optional" in opt: cmd += "--optional={} ".format(self._optional_tags_string()) return cmd + " ".join(self.files) @@ -1301,14 +1333,14 @@ def run_val_gdb(self, testing=False): sage: DD = DocTestDefaults(gdb=True) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec gdb --eval-command="run" --args ...python... sage-runtests --serial --timeout=0 hello_world.py + exec gdb --eval-command="run" --args ...python... -m sage.doctest --serial... --timeout=0... hello_world.py :: sage: DD = DocTestDefaults(valgrind=True, optional='all', timeout=172800) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions="...valgrind/pyalloc.supp" --suppressions="...valgrind/sage.supp" --suppressions="...valgrind/sage-additional.supp" --log-file=.../valgrind/sage-memcheck.%p... sage-runtests --serial --timeout=172800 --optional=all hello_world.py + exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions=.../valgrind/pyalloc.supp --suppressions=.../valgrind/sage.supp --suppressions=.../valgrind/sage-additional.supp --suppressions=.../valgrind/valgrind-python.supp --log-file=.../valgrind/sage-memcheck.%p ...python... -m sage.doctest --serial... --timeout=172800... --optional=all hello_world.py """ try: sage_cmd = self._assemble_cmd() @@ -1318,13 +1350,12 @@ def run_val_gdb(self, testing=False): opt = self.options if opt.gdb: - cmd = f'''exec gdb --eval-command="run" --args {shlex.quote(sys.executable)} ''' + cmd = f'''exec gdb --eval-command="run" --args ''' flags = "" if opt.logfile: sage_cmd += f" --logfile {shlex.quote(opt.logfile)}" elif opt.lldb: - sage_cmd = sage_cmd.replace('sage-runtests', '$(command -v sage-runtests)') - cmd = f'''exec lldb --one-line "process launch" --one-line "cont" -- {sys.executable} ''' + cmd = f'''exec lldb --one-line "process launch" --one-line "cont" -- ''' flags = "" else: if opt.logfile is None: @@ -1343,9 +1374,9 @@ def run_val_gdb(self, testing=False): flags = os.getenv("SAGE_MEMCHECK_FLAGS") if flags is None: flags = "--leak-resolution=high --leak-check=full --num-callers=25 " - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "pyalloc.supp")) - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "sage.supp")) - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "sage-additional.supp")) + for supp in ["pyalloc.supp", "sage.supp", "sage-additional.supp", "valgrind-python.supp"]: + fname = os.path.join(SAGE_EXTCODE, "valgrind", supp) + flags += f"--suppressions={shlex.quote(fname)} " elif opt.massif: toolname = "massif" flags = os.getenv("SAGE_MASSIF_FLAGS", "--depth=6 ") @@ -1410,7 +1441,7 @@ def run(self): Running doctests with ID ... Doctesting 1 file. sage -t .../sage/sets/non_negative_integers.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1434,7 +1465,7 @@ def run(self): Features to be detected: ... Doctesting 1 file. sage -t ....py - [0 tests, ... s] + [0 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1460,7 +1491,7 @@ def run(self): Features to be detected: ... Doctesting 1 file. sage -t ....py - [4 tests, ... s] + [4 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1478,7 +1509,7 @@ def run(self): Features to be detected: ... Doctesting 1 file. sage -t ....py - [4 tests, ... s] + [4 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1496,7 +1527,7 @@ def run(self): Features to be detected: ... Doctesting 1 file. sage -t ....py - [4 tests, ... s] + [4 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -1604,7 +1635,7 @@ def run_doctests(module, options=None): Running doctests with ID ... Doctesting 1 file. sage -t .../sage/rings/all.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index a80b8ac555e..56727bd79f6 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -379,7 +379,7 @@ def external_software() -> list[str]: external_software = external_software() -class AvailableSoftware(): +class AvailableSoftware: """ This class keeps the set of available software whose availability is detected lazily from the list of external software. diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 033b0671c06..ee812a8ecc9 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -112,7 +112,7 @@ def sorted_pairs(iterable, pairs=False): return repr(val) -class AttributeAccessTracerHelper(): +class AttributeAccessTracerHelper: def __init__(self, delegate, prefix=" ", reads=True): r""" @@ -223,7 +223,7 @@ def set(self, name, val): setattr(self.delegate, name, val) -class AttributeAccessTracerProxy(): +class AttributeAccessTracerProxy: def __init__(self, delegate, **kwds): r""" diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 823a0d14a41..b69c842836c 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -150,14 +150,13 @@ def init_sage(controller=None): sage: from sympy.printing.pretty.pretty import PrettyPrinter sage: s = sympify('+x^'.join(str(i) for i in range(30))) sage: print(PrettyPrinter(settings={'wrap_line': True}).doprint(s)) - 29 28 27 26 25 24 23 22 21 20 19 18 17 - x + x + x + x + x + x + x + x + x + x + x + x + x + + 29 28 27 26 25 24 23 22 21 20 19 18 17... + x + x + x + x + x + x + x + x + x + x + x + x + x... - 16 15 14 13 12 11 10 9 8 7 6 5 4 3 - x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + ... 16 15 14 13 12 11 10 9 8 7 6 5 4 3... + ...x + x + x + x + x + x + x + x + x + x + x + x + x + x... - 2 - + x + ... The displayhook sorts dictionary keys to simplify doctesting of dictionary output:: @@ -710,27 +709,19 @@ def compiler(example): elif self.options.gc < 0: gc.disable() + from cysignals.signals import SignalError try: # Don't blink! This is where the user's code gets run. self.compile_and_execute(example, compiler, test.globs) - except SystemExit: + except (SignalError, SystemExit): + # Tests can be killed by signals in unexpected places. raise except BaseException: exception = sys.exc_info() - # On Python 2, the exception lives in sys.exc_info() as - # long we are in the same stack frame. To ensure that - # sig_occurred() works correctly, we need to clear the - # exception. This is not an issue on Python 3, where the - # exception is cleared as soon as we are outside of the - # "except" clause. - try: - sys.exc_clear() - except AttributeError: - pass # Python 3 finally: if self.debugger is not None: self.debugger.set_continue() # ==== Example Finished ==== - check_starttime = walltime() + check_timer = Timer().start() got = self._fakeout.getvalue() outcome = FAILURE # guilty until proved innocent or insane @@ -804,22 +795,22 @@ def compiler(example): f"and it succeeded (raised an exception as expected).") outcome = SUCCESS - check_duration = walltime(check_starttime) - self.total_walltime += example.walltime + check_duration + check_timer.stop() + self.total_walltime += example.walltime + check_timer.walltime # Report the outcome. if example.warnings: for warning in example.warnings: out(self._failure_header(test, example, f'Warning: {warning}')) if outcome is SUCCESS: - if self.options.warn_long > 0 and example.walltime + check_duration > self.options.warn_long: + if self.options.warn_long > 0 and example.cputime + check_timer.cputime > self.options.warn_long: self.report_overtime(out, test, example, got, - check_duration=check_duration) + check_timer=check_timer) elif example.warnings: pass elif not quiet: self.report_success(out, test, example, got, - check_duration=check_duration) + check_timer=check_timer) elif probed_tags: pass elif outcome is FAILURE: @@ -1327,7 +1318,7 @@ def report_start(self, out, test, example): start_txt += 'Expecting nothing\n' out(start_txt) - def report_success(self, out, test, example, got, *, check_duration=0): + def report_success(self, out, test, example, got, *, check_timer=None): """ Called when an example succeeds. @@ -1341,8 +1332,9 @@ def report_success(self, out, test, example, got, *, check_duration=0): - ``got`` -- string; the result of running ``example`` - - ``check_duration`` -- number (default: ``0``); time spent for checking - the test output + - ``check_timer`` -- a :class:`sage.doctest.util.Timer` (default: + ``None``) that measures the time spent checking whether or not + the output was correct OUTPUT: prints a report to ``out``; if in debugging mode, starts an IPython prompt at the point of the failure @@ -1353,20 +1345,27 @@ def report_success(self, out, test, example, got, *, check_duration=0): sage: from sage.doctest.forker import SageDocTestRunner sage: from sage.doctest.sources import FileDocTestSource sage: from sage.doctest.control import DocTestDefaults; DD = DocTestDefaults() - sage: from sage.misc.timing import walltime + sage: from sage.doctest.util import Timer sage: import doctest, sys, os sage: DTR = SageDocTestRunner(SageOutputChecker(), verbose=True, sage_options=DD, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS) sage: filename = sage.doctest.forker.__file__ sage: FDS = FileDocTestSource(filename, DD) sage: doctests, extras = FDS.create_doctests(globals()) sage: ex = doctests[0].examples[0] - sage: ex.walltime = 0.0r - sage: DTR.report_success(sys.stdout.write, doctests[0], ex, '1764') - ok [0.00 s] + sage: ex.cputime = 1.01 + sage: ex.walltime = 1.12 + sage: check = Timer() + sage: check.cputime = 2.14 + sage: check.walltime = 2.71 + sage: DTR.report_success(sys.stdout.write, doctests[0], ex, '1764', + ....: check_timer=check) + ok [3.83s wall] """ - # We completely replace doctest.DocTestRunner.report_success so that we can include time taken for the test + # We completely replace doctest.DocTestRunner.report_success + # so that we can include time taken for the test if self._verbose: - out("ok [%.2f s]\n" % (example.walltime + check_duration)) + out("ok [%.2fs wall]\n" % + (example.walltime + check_timer.walltime)) def report_failure(self, out, test, example, got, globs): r""" @@ -1496,7 +1495,7 @@ def report_failure(self, out, test, example, got, globs): self._fakeout.start_spoofing() return returnval - def report_overtime(self, out, test, example, got, *, check_duration=0): + def report_overtime(self, out, test, example, got, *, check_timer=None): r""" Called when the ``warn_long`` option flag is set and a doctest runs longer than the specified time. @@ -1511,8 +1510,9 @@ def report_overtime(self, out, test, example, got, *, check_duration=0): - ``got`` -- string; the result of running ``example`` - - ``check_duration`` -- number (default: ``0``); time spent for checking - the test output + - ``check_timer`` -- a :class:`sage.doctest.util.Timer` (default: + ``None``) that measures the time spent checking whether or not + the output was correct OUTPUT: prints a report to ``out`` @@ -1522,24 +1522,32 @@ def report_overtime(self, out, test, example, got, *, check_duration=0): sage: from sage.doctest.forker import SageDocTestRunner sage: from sage.doctest.sources import FileDocTestSource sage: from sage.doctest.control import DocTestDefaults; DD = DocTestDefaults() - sage: from sage.misc.timing import walltime + sage: from sage.doctest.util import Timer sage: import doctest, sys, os sage: DTR = SageDocTestRunner(SageOutputChecker(), verbose=True, sage_options=DD, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS) sage: filename = sage.doctest.forker.__file__ sage: FDS = FileDocTestSource(filename, DD) sage: doctests, extras = FDS.create_doctests(globals()) sage: ex = doctests[0].examples[0] - sage: ex.walltime = 1.23r - sage: DTR.report_overtime(sys.stdout.write, doctests[0], ex, 'BAD ANSWER\n', check_duration=2.34r) + sage: ex.cputime = 1.23 + sage: ex.walltime = 2.50 + sage: check = Timer() + sage: check.cputime = 2.34 + sage: check.walltime = 3.12 + sage: DTR.report_overtime(sys.stdout.write, doctests[0], ex, 'BAD ANSWER\n', check_timer=check) ********************************************************************** File ".../sage/doctest/forker.py", line 12, in sage.doctest.forker - Warning, slow doctest: + Warning: slow doctest: doctest_var = 42; doctest_var^2 - Test ran for 1.23 s, check ran for 2.34 s + Test ran for 1.23s cpu, 2.50s wall + Check ran for 2.34s cpu, 3.12s wall """ - out(self._failure_header(test, example, 'Warning, slow doctest:') + - ('Test ran for %.2f s, check ran for %.2f s\n' - % (example.walltime, check_duration))) + out(self._failure_header(test, example, 'Warning: slow doctest:') + + ('Test ran for %.2fs cpu, %.2fs wall\nCheck ran for %.2fs cpu, %.2fs wall\n' + % (example.cputime, + example.walltime, + check_timer.cputime, + check_timer.walltime))) def report_unexpected_exception(self, out, test, example, exc_info): r""" @@ -1742,9 +1750,9 @@ def serial_dispatch(self): sage: DC.timer = Timer().start() sage: DD.serial_dispatch() sage -t .../rings/homset.py - [... tests, ... s] + [... tests, ...s wall] sage -t .../rings/ideal.py - [... tests, ... s] + [... tests, ...s wall] """ for source in self.controller.sources: heading = self.controller.reporter.report_head(source) @@ -1788,9 +1796,9 @@ def parallel_dispatch(self): sage: DC.timer = Timer().start() sage: DD.parallel_dispatch() sage -t .../databases/cremona.py - [... tests, ... s] + [... tests, ...s wall] sage -t .../rings/big_oh.py - [... tests, ... s] + [... tests, ...s wall] If the ``exitfirst=True`` option is given, the results for a failing module will be immediately printed and any other ongoing tests @@ -1825,7 +1833,7 @@ def parallel_dispatch(self): ********************************************************************** 1 item had failures: 1 of 1 in ... - [1 test, 1 failure, ... s] + [1 test, 1 failure, ...s wall] Killing test ... """ opt = self.controller.options @@ -1943,8 +1951,7 @@ def sel_exit(): # has the messages pipe open). # Adjust deadline to read all messages: newdeadline = now + die_timeout - if w.deadline > newdeadline: - w.deadline = newdeadline + w.deadline = min(w.deadline, newdeadline) new_workers.append(w) else: # Save the result and output of the worker @@ -2041,8 +2048,7 @@ def sel_exit(): # The master pselect() call rlist = [w.rmessages for w in workers if w.rmessages is not None] tmout = min(w.deadline for w in workers) - now - if tmout > 5: # Wait at most 5 seconds - tmout = 5 + tmout = min(tmout, 5) rlist, _, _, _ = sel.pselect(rlist, timeout=tmout) # Read messages @@ -2121,9 +2127,9 @@ def dispatch(self): sage: DC.timer = Timer().start() sage: DD.dispatch() sage -t .../sage/modules/free_module_homspace.py - [... tests, ... s] + [... tests, ...s wall] sage -t .../sage/rings/big_oh.py - [... tests, ... s] + [... tests, ...s wall] """ if self.controller.options.serial: self.serial_dispatch() @@ -2173,7 +2179,7 @@ class should be accessed by the child process. sage: W.join() # Wait for worker to finish sage: result = W.result_queue.get() sage: reporter.report(FDS, False, W.exitcode, result, "") - [... tests, ... s] + [... tests, ...s wall] """ def __init__(self, source, options, funclist=[], baseline=None): """ @@ -2185,7 +2191,7 @@ def __init__(self, source, options, funclist=[], baseline=None): Running doctests with ID ... Doctesting 1 file. sage -t .../sage/rings/big_oh.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -2232,7 +2238,7 @@ def run(self): Running doctests with ID ... Doctesting 1 file. sage -t .../sage/symbolic/units.py - [... tests, ... s] + [... tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -2470,7 +2476,7 @@ def kill(self): return True -class DocTestTask(): +class DocTestTask: """ This class encapsulates the tests from a single source. diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index d9b054ae2dd..0bc4965cd09 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -652,7 +652,7 @@ def reduce_hex(fingerprints): return "%032x" % res -class OriginalSource(): +class OriginalSource: r""" Context swapping out the pre-parsed source with the original for better reporting. diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index d72fd18023b..e6bfd52bf33 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -403,7 +403,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): 0 sage: DTR.report(FDS, False, 0, (sum([len(t.examples) for t in doctests]), D), ....: "Good tests") - [... tests, ... s] + [... tests, ...s wall] sage: DTR.stats {'sage.doctest.reporting': {'ntests': ..., 'walltime': ...}} @@ -414,7 +414,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): 1 sage: DTR.report(FDS, False, 0, (sum([len(t.examples) for t in doctests]), D), ....: "Doctest output including the failure...") - [... tests, 1 failure, ... s] + [... tests, 1 failure, ...s wall] If the user has requested that we report on skipped doctests, we do so:: @@ -433,7 +433,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): 5 magma tests not run 2 not tested tests not run 0 tests not run because we ran out of time - [... tests, ... s] + [... tests, ...s wall] Test an internal error in the reporter:: @@ -466,7 +466,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): 1 sage: DTR.report(FDS, False, 0, (sum([len(t.examples) for t in doctests]), D), ....: "Failed test") - [... tests, 1 failure, ... s] + [... tests, 1 failure, ...s wall] """ log = self.controller.log process_name = 'process (pid={0})'.format(pid) if pid else 'process' @@ -625,7 +625,7 @@ def report(self, source, timeout, return_code, results, output, pid=None): else: total = count_noun(ntests, "test") if not (self.controller.options.only_errors and not f): - log(" [%s, %s%.2f s]" % (total, "%s, " % (count_noun(f, "failure")) if f else "", wall)) + log(" [%s, %s%.2fs wall]" % (total, "%s, " % (count_noun(f, "failure")) if f else "", wall)) self.sources_completed += 1 @@ -680,13 +680,13 @@ def finalize(self): 0 sage: DTR.report(FDS, False, 0, (sum([len(t.examples) for t in doctests]), D), ....: "Good tests") - [... tests, ... s] + [... tests, ...s wall] sage: runner.failures = 1 sage: runner.update_results(D) 1 sage: DTR.report(FDS, False, 0, (sum([len(t.examples) for t in doctests]), D), ....: "Doctest output including the failure...") - [... tests, 1 failure, ... s] + [... tests, 1 failure, ...s wall] Now we can show the output of finalize:: diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 68d95d1cf26..78c45195970 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -124,7 +124,7 @@ def get_basename(path): return basename -class DocTestSource(): +class DocTestSource: """ This class provides a common base class for different sources of doctests. diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index 2819b782a66..a6aa893bb22 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -29,7 +29,7 @@ Running doctests... Doctesting 1 file. sage -t --warn-long 0.0 --random-seed=0 longtime.rst - [0 tests, ...s] + [0 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -40,7 +40,7 @@ Running doctests... Doctesting 1 file. sage -t --long --warn-long 0.0 --random-seed=0 longtime.rst - [1 test, ...s] + [1 test, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -442,7 +442,7 @@ Running doctests... Doctesting 1 file... sage -t... 1second.rst... - [2 tests, ... s] + [2 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -471,7 +471,7 @@ 1 long test not run 1 not tested test not run 0 tests not run because we ran out of time - [2 tests, ... s] + [2 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -488,7 +488,7 @@ 2 tests not run due to known bugs 1 not tested test not run 0 tests not run because we ran out of time - [4 tests, ... s] + [4 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -504,7 +504,7 @@ 1 not tested test not run 2 sage tests not run 0 tests not run because we ran out of time - [2 tests, ... s] + [2 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -533,7 +533,7 @@ Running doctests... Doctesting 1 file. sage -t --warn-long 0.0 --random-seed=0 atexit.rst - [3 tests, ... s] + [3 tests, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -564,7 +564,7 @@ ********************************************************************** 1 item had failures: 1 of 2 in sage.doctest.tests.random_seed - [1 test, 1 failure, ...s] + [1 test, 1 failure, ...s wall] ---------------------------------------------------------------------- sage -t --warn-long 0.0 --random-seed=0 random_seed.rst # 1 doctest failed ---------------------------------------------------------------------- @@ -575,7 +575,7 @@ Running doctests... Doctesting 1 file. sage -t --warn-long 0.0 --random-seed=1 random_seed.rst - [1 test, ...s] + [1 test, ...s wall] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index 6bffb67156b..ed831598e65 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -23,8 +23,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.timing import walltime, cputime - +from time import time as walltime +from os import sysconf, times def count_noun(number, noun, plural=None, pad_number=False, pad_noun=False): """ @@ -104,6 +104,219 @@ class Timer: {} sage: TestSuite(Timer()).run() """ + + def _proc_stat_cpu_seconds(self, path): + r""" + Parse a "stat" file from the ``/proc`` filesystem to get + the cputime of a process. + + This also includes the times for child processes, but only + those that have already terminated and for which ``wait()`` + was called. It is important to note that pexpect processes DO + NOT fall into that category. + + The document ``Documentation/filesystems/proc.rst`` within the + Linux kernel source tree defines a "stat" file. + + INPUT: + + - ``path`` -- string; the path to a "stat" file on the ``/proc`` + filesystem, typically "/proc//stat", from which we will + read cputime information + + OUTPUT: + + A nonnegative float representing the number of cpu-seconds + used by the process associated with ``path``. An ``OSError`` is + raised if anything goes wrong, which typically happens on + platforms that don't store this information under ``/proc``. + + TESTS: + + About all we can say for certain is that this will return a + nonnegative float or raise an ``OSError``:: + + sage: from sage.doctest.util import Timer + sage: cputime = float(0.0) + sage: path = "/proc/1/stat" + sage: try: + ....: cputime = Timer()._proc_stat_cpu_seconds(path) + ....: except OSError: + ....: pass + sage: cputime >= 0.0 + True + sage: isinstance(cputime, float) + True + + We can force an ``OSError`` with an invalid PID:: + + sage: from sage.doctest.util import Timer + sage: path = "/proc/-1/stat" + sage: cputime = Timer()._proc_stat_cpu_seconds(path) + Traceback (most recent call last): + ... + OSError: unable to access /proc/-1/stat + + Or with an unparseable file (wrong number of fields, non-float + fields, et cetera):: + + sage: from tempfile import NamedTemporaryFile + sage: from os import unlink + sage: from sage.doctest.util import Timer + sage: with NamedTemporaryFile(delete=False, mode="w") as f: + ....: _ = f.write("1 2 3 4 5") + sage: cputime = Timer()._proc_stat_cpu_seconds(f.name) + Traceback (most recent call last): + ... + OSError: unable to parse ... + sage: os.unlink(f.name) + sage: with NamedTemporaryFile(delete=False, mode="w") as f: + ....: _ = f.write("1 2 3 4 5 6 7 8 9 10 11 12 w x y z 17") + sage: cputime = Timer()._proc_stat_cpu_seconds(f.name) + Traceback (most recent call last): + ... + OSError: unable to parse ... + sage: os.unlink(f.name) + + """ + try: + with open(path, "r") as statfile: + stats = statfile.read().split() + except (FileNotFoundError, PermissionError) as e: + # FileNotFoundError: bad PID, or no /proc support + # PermissionError: can't read the stat file + raise OSError(f"unable to access {path}") from e + + if len(stats) < 17: + raise OSError(f"unable to parse {path}") + + try: + # These fields used to be documented in the proc(5) man + # page, but are now most easily found in the Linux kernel + # documentation (Documentation/filesystems/proc.rst). The + # intent is to sum the user- and kernel-mode "jiffies" for + # both the given process and its children. + cputicks = sum( float(s) for s in stats[13:17] ) + except (ArithmeticError, TypeError, ValueError) as e: + # ArithmeticError: unexpected (non-numeric?) values in fields + # TypeError/ValueError: fields can't be converted to float + raise OSError(f"unable to parse {path}") from e + + try: + hertz = sysconf("SC_CLK_TCK") + except (ValueError) as e: + # ValueError: SC_CLK_TCK doesn't exist + raise OSError("SC_CLK_TCK sysconf not found") from e + + if hertz <= 0: + # The python documentation for os.sysconf() says, "If the + # configuration value specified by name isn’t defined, -1 + # is returned." Having tried this with a junk value, I + # don't believe it: I got a ValueError that was handled + # above. Nevertheless, we play it safe here and turn a -1 + # into an OSError. We check for zero, too, because we're + # about to divide by it. + raise OSError("SC_CLK_TCK sysconf is nonpositive") + + return (cputicks / hertz) + + def _quick_cputime(self, expect_objects): + r""" + A fast replacement for ``sage.misc.timing.cputime``. + + This is a "reliable" replacement (on Linux/BSD) that takes + subprocesses (particularly pexpect interfaces) into + account. The ``cputime()`` function from the ``misc`` module + can be passed ``subprocesses=True``, but this has a few + faults; mainly that it relies on each pexpect interface to + implement its own ``cputime()`` function. And most of our + pexpect interfaces either don't implement one, or implement + one in a way that requires the subprocess (being pexpected) to + be in perfect working condition -- that will often not be the + case at the end of a doctest line. + + INPUT: + + - ``expect_objects`` -- list; a list of + :class:`sage.interfaces.expect.Expect` instances whose CPU + times will be included in the total + + OUTPUT: + + A float measuring the cputime in seconds of the sage process + and all its subprocesses. + + TESTS: + + About all we can say for certain is that this will return a + nonnegative float:: + + sage: from sage.doctest.util import Timer + sage: from sage.interfaces.quit import expect_objects + sage: cputime = Timer()._quick_cputime(expect_objects) + sage: cputime >= 0.0 + True + sage: isinstance(cputime, float) + True + + If an error occurs in :meth:`_proc_stat_cpu_seconds`, this + function should still return a valid answer, albeit one that + is missing timing information for the PID that failed:: + + sage: class FakeExpect: + ....: def __call__(self): + ....: return self + ....: def is_running(self): + ....: return True + ....: def pid(self): + ....: return -1 + sage: e = FakeExpect() + sage: from sage.doctest.util import Timer + sage: cputime = Timer()._quick_cputime([e]) + sage: cputime >= 0.0 + True + sage: isinstance(cputime, float) + True + """ + # Start by using os.times() to get the cputime for sage itself + # and any subprocesses that have been wait()ed for and that + # have terminated. + cputime = sum( times()[:4] ) + + # Now try to get the times for any pexpect interfaces, since + # they do not fall into the category above. + for s in expect_objects: + S = s() + if S and S.is_running(): + try: + # This will fail anywhere but linux/BSD, but + # there's no good cross-platform way to get the + # cputimes from pexpect interfaces without totally + # mucking up the doctests. + path = f"/proc/{S.pid()}/stat" + cputime += self._proc_stat_cpu_seconds(path) + except OSError: + # If we're on macOS, we can fall back to using + # psutil, but only if it's installed. It's usually + # installed as a transitive dependency (ipython + # needs it), but it isn't explicitly listed as + # a dependency of sagelib. + try: + from psutil import (NoSuchProcess, + Process, + ZombieProcess) + try: + cputime += sum(Process(S.pid()).cpu_times()[0:2]) + except (ValueError, NoSuchProcess, ZombieProcess): + # ValueError: invalid (e.g. negative) PID + # NoSuchProcess: it's gone + # ZombieProcess: PID refers to a zombie + pass + except ImportError: + pass + + return cputime + def start(self): """ Start the timer. @@ -116,7 +329,8 @@ def start(self): sage: Timer().start() {'cputime': ..., 'walltime': ...} """ - self.cputime = cputime() + from sage.interfaces.quit import expect_objects + self.cputime = self._quick_cputime(expect_objects) self.walltime = walltime() return self @@ -134,8 +348,9 @@ def stop(self): sage: timer.stop() {'cputime': ..., 'walltime': ...} """ - self.cputime = cputime(self.cputime) - self.walltime = walltime(self.walltime) + from sage.interfaces.quit import expect_objects + self.cputime = self._quick_cputime(expect_objects) - self.cputime + self.walltime = walltime() - self.walltime return self def annotate(self, object): diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index c2ad0f04cea..2096d734c85 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -884,6 +884,7 @@ def degree(self): """ return self.as_scheme_morphism().degree() + class DynamicalSystem_affine_field(DynamicalSystem_affine, SchemeMorphism_polynomial_affine_space_field): @cached_method @@ -985,6 +986,7 @@ def reduce_base_field(self): """ return self.as_scheme_morphism().reduce_base_field().as_dynamical_system() + class DynamicalSystem_affine_finite_field(DynamicalSystem_affine_field, SchemeMorphism_polynomial_affine_space_finite_field): diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 6995f61f463..f84ab7e94ed 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -869,7 +869,7 @@ def __call__(self, x, type_3_pole_check=True): else: new_poly.append(ring_of_integers(i).mod(ideal)) new_poly = R(new_poly) - fraction.append((new_poly)) + fraction.append(new_poly) gcd = fraction[0].gcd(fraction[1]) num = fraction[0].quo_rem(gcd)[0] dem = fraction[1].quo_rem(gcd)[0] @@ -960,6 +960,7 @@ def __call__(self, x, type_3_pole_check=True): new_radius = max(new_radius, p**(-valuation/prime.absolute_ramification_index())*r**i) return self.domain()(new_center, new_radius) + class DynamicalSystem_Berkovich_affine(DynamicalSystem_Berkovich): r""" A dynamical system of the affine Berkovich line over `\CC_p`. diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index d10207210a5..196da8a081a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -335,6 +335,7 @@ def height_bound(polynomial): return (6*(L2norm_sq)**3) + def PGL_repn(rational_function): r""" Take a linear fraction transformation and represent it as a 2x2 matrix. @@ -365,6 +366,7 @@ def PGL_repn(rational_function): g = rational_function.denominator() return matrix(F, 2, [f[1], f[0], g[1], g[0]]) + def PGL_order(A): r""" Find the multiplicative order of a linear fractional transformation that @@ -402,6 +404,7 @@ def PGL_order(A): return n + def CRT_helper(automorphisms, moduli): r""" Lift the given list of automorphisms to `Zmod(M)`. @@ -449,6 +452,7 @@ def CRT_helper(automorphisms, moduli): return autos, modulus*moduli[0] + def CRT_automorphisms(automorphisms, order_elts, degree, moduli): r""" Compute a maximal list of automorphisms over `Zmod(M)`. @@ -495,6 +499,7 @@ def CRT_automorphisms(automorphisms, order_elts, degree, moduli): # get list of CRT'ed automorphisms return CRT_helper(degree_d_autos, moduli) + def valid_automorphisms(automorphisms_CRT, rational_function, ht_bound, M, return_functions=False): r""" @@ -904,6 +909,7 @@ def automorphism_group_FF(rational_function, absolute=False, iso_type=False, ret else: return G, which_group(G[1]) + def field_descent(sigma, y): r""" Function for descending an element in a field `E` to a subfield `F`. @@ -961,6 +967,7 @@ def field_descent(sigma, y): return x + F(quotient)*a**(steps) + def rational_function_coefficient_descent(rational_function, sigma, poly_ring): r""" Function for descending the coefficients of a rational function from field `E` @@ -1059,6 +1066,7 @@ def rational_function_coerce(rational_function, sigma, S_polys): else: return S_polys([sigma(a) for a in f]) / S_polys([sigma(b) for b in g]) + def rational_function_reduce(rational_function): r""" Force Sage to divide out common factors in numerator and denominator @@ -1084,6 +1092,7 @@ def rational_function_reduce(rational_function): comm_factor = gcd(F,G) return (F.quo_rem(comm_factor)[0]) / (G.quo_rem(comm_factor)[0]) + def three_stable_points(rational_function, invariant_list): r""" Implementation of Algorithm 1 for automorphism groups from @@ -1157,6 +1166,7 @@ def three_stable_points(rational_function, invariant_list): automorphisms.append(s) return automorphisms + def automorphism_group_FF_alg2(rational_function): r""" Implementation of algorithm for determining the absolute automorphism @@ -1387,7 +1397,7 @@ def order_p_automorphisms(rational_function, pre_image): if case == 'fix': T = [x[0] for x in pre_image] elif case == 'F-pre_images': - T = [x for x in pre_image[0][1]] + T = list(pre_image[0][1]) else: T = [] @@ -1396,10 +1406,10 @@ def order_p_automorphisms(rational_function, pre_image): pt = guy[0] # treat case of multiple F-rational fixed points or # 1 F-rational fixed point with F-rational pre-images - if T != []: + if T: M = [t for t in T if t != pt] m = len(M) - if pt == [F(1),F(0)]: + if pt == [F(1), F(0)]: for i in range(1, m): s = z + M[i][0] - M[0][0] if s(phi(z)) == phi(s(z)): @@ -1407,8 +1417,8 @@ def order_p_automorphisms(rational_function, pre_image): else: u = F(1) / (z - pt[0]) u_inv = pt[0] + F(1)/z - for i in range(1,m): - if M[0] == [F(1),F(0)]: + for i in range(1, m): + if M[0] == [F(1), F(0)]: uy1 = 0 else: uy1 = u(M[0][0]) @@ -1455,6 +1465,7 @@ def order_p_automorphisms(rational_function, pre_image): return automorphisms_p + def automorphisms_fixing_pair(rational_function, pair, quad): r""" Compute the set of automorphisms with order prime to the characteristic @@ -1535,6 +1546,7 @@ def automorphisms_fixing_pair(rational_function, pair, quad): return list(set(automorphisms_prime_to_p)) + def automorphism_group_FF_alg3(rational_function): r""" Implementation of Algorithm 3 in the paper by Faber/Manes/Viray [FMV]_ @@ -2125,6 +2137,7 @@ def greedy_independence_check(P, repeated_mult, point_to_mult): if len(source) == n+2: return source, corresponding + def conjugating_set_helper(f, g, num_cpus, source, possible_targets): r""" Return the set of elements in PGL over the base ring @@ -2270,6 +2283,7 @@ def find_conjugations_arrangement(tuples): Conj = find_conjugations_subset(product(*subset_iterators)) return Conj + def is_conjugate_helper(f, g, num_cpus, source, possible_targets): r""" Return if ``f`` is conjugate to ``g``. diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py b/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py index c993cab86db..902f83b55ae 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py @@ -473,6 +473,8 @@ def Min(Fun, p, ubRes, conj, all_orbits=False): ################################################### #modification of Bruin-Molnar for all representatives + + def BM_all_minimal(vp, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal @@ -590,7 +592,7 @@ def BM_all_minimal(vp, return_transformation=False, D=None): for M in all_M: new_map = mp.conjugate(M) new_map.normalize_coordinates() - if not [new_map, M] in all_maps: + if [new_map, M] not in all_maps: all_maps.append([new_map, M]) #Split into conjugacy classes @@ -623,6 +625,8 @@ def BM_all_minimal(vp, return_transformation=False, D=None): ################################################### #find minimal model + + def HS_minimal(f, return_transformation=False, D=None): r""" Compute a minimal model for the given projective dynamical system. @@ -713,6 +717,8 @@ def HS_minimal(f, return_transformation=False, D=None): return F #find all representatives of orbits for one prime + + def HS_all_minimal_p(p, f, m=None, return_transformation=False): r""" Find a representative in each distinct `SL(2,\ZZ)` orbit with @@ -814,6 +820,8 @@ def HS_all_minimal_p(p, f, m=None, return_transformation=False): return [funct for funct, matr in reps] #find all representatives of orbits + + def HS_all_minimal(f, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. @@ -906,6 +914,7 @@ def HS_all_minimal(f, return_transformation=False, D=None): # Ben Hutz July 2018 #####################################3 + def get_bound_dynamical(F, f, m=1, dynatomic=True, prec=53, emb=None): """ The hyperbolic distance from `j` which must contain the smallest map. @@ -1095,9 +1104,7 @@ def coshdelta(z): red_g = f.conjugate(M*MG) if G != pts_poly: R2 = get_bound_dynamical(G, red_g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) - if R2 < R: - # use the better bound - R = R2 + R = min(R2, R) red_g.normalize_coordinates() if red_g.global_height(prec=prec) == 0: return [red_g, M*MG] @@ -1134,8 +1141,7 @@ def coshdelta(z): if new_size == 1: # early exit return [current_min[1], current_min[4]] new_R = get_bound_dynamical(G, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb) - if new_R < R: - R = new_R + R = min(new_R, R) # add new points to check if label != 1 and min((rep+1).norm(), (rep-1).norm()) >= 1: # don't undo S diff --git a/src/sage/dynamics/arithmetic_dynamics/meson.build b/src/sage/dynamics/arithmetic_dynamics/meson.build new file mode 100644 index 00000000000..9e26a72c874 --- /dev/null +++ b/src/sage/dynamics/arithmetic_dynamics/meson.build @@ -0,0 +1,27 @@ +py.install_sources( + 'affine_ds.py', + 'all.py', + 'berkovich_ds.py', + 'dynamical_semigroup.py', + 'endPN_automorphism_group.py', + 'endPN_minimal_model.py', + 'generic_ds.py', + 'product_projective_ds.py', + 'projective_ds.py', + 'wehlerK3.py', + subdir: 'sage/dynamics/arithmetic_dynamics', +) + +extension_data = {'projective_ds_helper' : files('projective_ds_helper.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/dynamics/arithmetic_dynamics', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 42f0a304436..8cd24f6611d 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -2120,8 +2120,7 @@ def green_function(self, P, v, **kwds): h = max([(Res*c).local_height_arch(vindex, prec=prec) for c in poly.coefficients()]) else: #non-archimedean h = max([c.local_height(v, prec=prec) for c in poly.coefficients()]) - if h > maxh: - maxh = h + maxh = max(h, maxh) if maxh == 0: maxh = 1 #avoid division by 0 if isinstance(v, RingHomomorphism_im_gens): #archimedean @@ -2334,8 +2333,7 @@ def canonical_height(self, P, **kwds): if err is not None: err = err / 2 N = ceil((R(Res).log().log() - R(d-1).log() - R(err).log())/(R(d).log())) - if N < 1: - N = 1 + N = max(N, 1) kwds.update({'error_bound': err}) kwds.update({'N': N}) for n in range(N): @@ -2937,7 +2935,7 @@ def parallel_function(morphism): # Calling possible_periods for each prime in parallel parallel_data = [] for q in primes(primebound[0], primebound[1] + 1): - if not (q in badprimes): + if q not in badprimes: F = self.change_ring(GF(q)) parallel_data.append(((F,), {})) @@ -6770,7 +6768,7 @@ def is_Lattes(self): (crit_set, post_crit_set) = crit, list(post_crit) # All Lattes maps have 3 or 4 post critical values - if not len(post_crit_set) in [3, 4]: + if len(post_crit_set) not in [3, 4]: return False f = F_crit.dehomogenize(1)[0] @@ -7028,6 +7026,7 @@ def Lattes_to_curve(self, return_conjugation=False, check_lattes=False): return (M, E) return E + class DynamicalSystem_projective_field(DynamicalSystem_projective, SchemeMorphism_polynomial_projective_space_field): @@ -7230,7 +7229,7 @@ def lift_to_rational_periodic(self, points_modp, B=None): while not done and k <= n: newP = self(newP) if newP == P: - if not ([P, k] in good_points): + if [P, k] not in good_points: good_points.append([newP, k]) done = True k += 1 @@ -7501,7 +7500,7 @@ def all_periodic_points(self, **kwds): pos_points = [] # check period, remove duplicates for i in range(len(all_points)): - if all_points[i][1] in periods and not (all_points[i] in pos_points): + if all_points[i][1] in periods and all_points[i] not in pos_points: pos_points.append(all_points[i]) periodic_points = DS.lift_to_rational_periodic(pos_points,B) for p,n in periodic_points: @@ -7596,7 +7595,7 @@ def all_rational_preimages(self, points): P = points.pop() preimages = self.rational_preimages(P) for i in range(len(preimages)): - if not preimages[i] in preperiodic: + if preimages[i] not in preperiodic: points.append(preimages[i]) preperiodic.add(preimages[i]) return list(preperiodic) @@ -9075,7 +9074,7 @@ def is_newton(self, return_conjugation=False): """ if self.degree() == 1: raise NotImplementedError("degree one Newton maps are trivial") - if not self.base_ring() in NumberFields(): + if self.base_ring() not in NumberFields(): raise NotImplementedError("only implemented over number fields") # check if Newton map sigma_1 = self.sigma_invariants(1) @@ -9119,6 +9118,7 @@ def is_newton(self, return_conjugation=False): else: return Npoly.derivative(z) == (z - N_aff[0]).denominator() + class DynamicalSystem_projective_finite_field(DynamicalSystem_projective_field, SchemeMorphism_polynomial_projective_space_finite_field): diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx index fb3b268b0df..1da1efc2bd7 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx @@ -132,7 +132,7 @@ cpdef _fast_possible_periods(self, return_points=False): for r in rvalues: periods.add(period*r) points_periods.append([P_proj, period*r]) - if p == 2 or p == 3: #need e=1 for N=1, QQ + if p == 2 or p == 3: # need e=1 for N=1, QQ periods.add(period*r*p) points_periods.append([P_proj, period*r*p]) else: @@ -141,7 +141,7 @@ cpdef _fast_possible_periods(self, return_points=False): periods.add(period*r*p) points_periods.append([P_proj, period*r]) points_periods.append([P_proj, period*r*p]) - if p == 2: #need e=3 for N>1, QQ + if p == 2: # need e=3 for N>1, QQ periods.add(period*r*4) points_periods.append([P_proj, period*r*4]) periods.add(period*r*8) diff --git a/src/sage/dynamics/cellular_automata/elementary.py b/src/sage/dynamics/cellular_automata/elementary.py index 51f959ed237..1c91687db8c 100644 --- a/src/sage/dynamics/cellular_automata/elementary.py +++ b/src/sage/dynamics/cellular_automata/elementary.py @@ -25,6 +25,7 @@ lazy_import("sage.plot.matrix_plot", "matrix_plot") from sage.misc.constant_function import ConstantFunction + class ElementaryCellularAutomata(SageObject): r""" Elementary cellular automata. diff --git a/src/sage/dynamics/cellular_automata/glca.py b/src/sage/dynamics/cellular_automata/glca.py index dbd7bdc6fc3..929f1131456 100644 --- a/src/sage/dynamics/cellular_automata/glca.py +++ b/src/sage/dynamics/cellular_automata/glca.py @@ -20,6 +20,7 @@ from sage.typeset.ascii_art import AsciiArt from sage.typeset.unicode_art import UnicodeArt + class GraftalLaceCellularAutomata(SageObject): r""" Graftal Lace Cellular Automata (GLCA). @@ -363,24 +364,24 @@ def _unicode_art_(self, number=None): number = len(self._states) space = len(self._states[:number]) * 2 - 1 - ret = UnicodeArt([u' '*space + u'◾']) + ret = UnicodeArt([' '*space + '◾']) space += 1 for i,state in enumerate(self._states[:number]): - temp = u' '*(space-2) - last = u' ' + temp = ' '*(space-2) + last = ' ' for x in state: if x & 0x4: - if last == u'╱': - temp += u'╳' + if last == '╱': + temp += '╳' else: - temp += u'╲' + temp += '╲' else: temp += last - temp += u'│' if x & 0x2 else ' ' - last = u'╱' if x & 0x1 else ' ' + temp += '│' if x & 0x2 else ' ' + last = '╱' if x & 0x1 else ' ' ret *= UnicodeArt([temp + last]) space -= 1 - ret *= UnicodeArt([u' '*space + u' '.join(u'◾' for dummy in range(2*i+1))]) + ret *= UnicodeArt([' '*space + ' '.join('◾' for dummy in range(2*i+1))]) space -= 1 return ret diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia.py b/src/sage/dynamics/complex_dynamics/mandel_julia.py index 87e98ec084d..b95040c2da2 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia.py +++ b/src/sage/dynamics/complex_dynamics/mandel_julia.py @@ -58,6 +58,7 @@ EPS = 0.00001 + def mandelbrot_plot(f=None, **kwds): r""" Plot of the Mandelbrot set for a one parameter family of polynomial maps. @@ -430,6 +431,7 @@ def external_ray(theta, **kwds): pixel[int(k[0]), int(k[1])] = tuple(ray_color) return M + def kneading_sequence(theta): r""" Determines the kneading sequence for an angle theta in RR/ZZ which @@ -497,6 +499,7 @@ def kneading_sequence(theta): KS_str = ''.join(KS) + '*' return KS_str + def julia_plot(f=None, **kwds): r""" Plots the Julia set of a given polynomial ``f``. Users can specify whether diff --git a/src/sage/dynamics/complex_dynamics/meson.build b/src/sage/dynamics/complex_dynamics/meson.build new file mode 100644 index 00000000000..d3961275d3e --- /dev/null +++ b/src/sage/dynamics/complex_dynamics/meson.build @@ -0,0 +1,19 @@ +py.install_sources( + 'all.py', + 'mandel_julia.py', + subdir: 'sage/dynamics/complex_dynamics', +) + +extension_data = {'mandel_julia_helper' : files('mandel_julia_helper.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/dynamics/complex_dynamics', + install: true, + include_directories: [], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/dynamics/finite_dynamical_system.py b/src/sage/dynamics/finite_dynamical_system.py index d6cfc29feb7..441a498f608 100644 --- a/src/sage/dynamics/finite_dynamical_system.py +++ b/src/sage/dynamics/finite_dynamical_system.py @@ -678,6 +678,7 @@ def is_homomesic(self, h, average=None, find_average=False, elements=None): return True return orbavgs[0] == average + class InvertibleDiscreteDynamicalSystem(DiscreteDynamicalSystem): r""" An invertible discrete dynamical system. @@ -969,6 +970,7 @@ def inverse_evolution_default(self, x): """ return self.orbit(x)[-1] + class FiniteDynamicalSystem(DiscreteDynamicalSystem): r""" A finite discrete dynamical system. @@ -1113,6 +1115,7 @@ def cycles(self): break return cycs + class InvertibleFiniteDynamicalSystem(InvertibleDiscreteDynamicalSystem, FiniteDynamicalSystem): r""" An invertible finite discrete dynamical system. diff --git a/src/sage/dynamics/finite_dynamical_system_catalog.py b/src/sage/dynamics/finite_dynamical_system_catalog.py index 74217178a29..c00083f56c4 100755 --- a/src/sage/dynamics/finite_dynamical_system_catalog.py +++ b/src/sage/dynamics/finite_dynamical_system_catalog.py @@ -28,6 +28,7 @@ FiniteDynamicalSystem, InvertibleDiscreteDynamicalSystem, \ InvertibleFiniteDynamicalSystem + def permutation(pi, invertible=True): r""" Return the invertible finite discrete dynamical system @@ -64,6 +65,7 @@ def permutation(pi, invertible=True): X = range(1, n+1) return InvertibleFiniteDynamicalSystem(X, pi, inverse=pi.inverse(), create_tuple=True) + def one_line(xs): r""" Return the finite discrete dynamical system @@ -148,6 +150,7 @@ def bitstring_rotation(n, ones=None): psi = lambda x: (x[-1],) + x[:-1] return InvertibleFiniteDynamicalSystem(X, phi, inverse=psi) + def striker_sweep(E, predicate, elements, lazy=False): r""" Return the invertible finite discrete dynamical system diff --git a/src/sage/dynamics/meson.build b/src/sage/dynamics/meson.build new file mode 100644 index 00000000000..134cfd1a296 --- /dev/null +++ b/src/sage/dynamics/meson.build @@ -0,0 +1,11 @@ +py.install_sources( + 'all.py', + 'finite_dynamical_system.py', + 'finite_dynamical_system_catalog.py', + 'surface_dynamics_deprecation.py', + subdir: 'sage/dynamics', +) + +subdir('arithmetic_dynamics') +install_subdir('cellular_automata', install_dir: sage_install_dir / 'dynamics') +subdir('complex_dynamics') diff --git a/src/sage/dynamics/surface_dynamics_deprecation.py b/src/sage/dynamics/surface_dynamics_deprecation.py index 9da8f1d0be3..8f6e37fe1b2 100644 --- a/src/sage/dynamics/surface_dynamics_deprecation.py +++ b/src/sage/dynamics/surface_dynamics_deprecation.py @@ -9,6 +9,7 @@ "information at\n" \ " http://www.labri.fr/perso/vdelecro/surface-dynamics/latest/" + def surface_dynamics_deprecation(name): r""" TESTS:: diff --git a/src/sage/env.py b/src/sage/env.py index 3548c65f43e..2be366c6f3e 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -8,10 +8,14 @@ sage: env = {k:v for (k,v) in os.environ.items() if not k.startswith("SAGE_")} sage: from subprocess import check_output - sage: environment = "sage.all" - sage: cmd = f"from {environment} import SAGE_ROOT, SAGE_LOCAL; print((SAGE_ROOT, SAGE_LOCAL))" + sage: module_name = "sage.all" # hide .all import from the linter + sage: cmd = f"from {module_name} import SAGE_ROOT, SAGE_LOCAL;" + sage: cmd += "from os.path import samefile;" + sage: cmd += f"s1 = samefile(SAGE_ROOT, '{SAGE_ROOT}') if SAGE_ROOT else True;" + sage: cmd += f"s2 = samefile(SAGE_LOCAL, '{SAGE_LOCAL}');" + sage: cmd += "print(s1 and s2);" sage: out = check_output([sys.executable, "-c", cmd], env=env).decode().strip() # long time - sage: out == repr((SAGE_ROOT, SAGE_LOCAL)) # long time + sage: out == "True" # long time True AUTHORS: @@ -30,7 +34,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from typing import List, Optional +from typing import Optional import sage import os import socket @@ -144,7 +148,12 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st import sage_conf value = getattr(sage_conf, key, None) except ImportError: - pass + try: + import sage.config + value = getattr(sage.config, key, None) + except ImportError: + pass + # Try all fallbacks in order as long as we don't have a value for f in fallbacks: if value is not None: @@ -332,7 +341,7 @@ def get_cblas_pc_module_name() -> str: """ import pkgconfig cblas_pc_modules = CBLAS_PC_MODULES.split(':') - return next((blas_lib for blas_lib in cblas_pc_modules if pkgconfig.exists(blas_lib))) + return next(blas_lib for blas_lib in cblas_pc_modules if pkgconfig.exists(blas_lib)) default_required_modules = ('fflas-ffpack', 'givaro', 'gsl', 'linbox', 'Singular', diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index c7b4664ff1a..89c34b1f0fc 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -512,10 +512,10 @@ def _builder_and_stream(vars, domain): sage: from sage.ext.fast_callable import _builder_and_stream sage: _builder_and_stream(["x", "y"], ZZ) - (, + (, ) sage: _builder_and_stream(["x", "y"], RR) # needs sage.rings.real_mpfr - (, + (, ) Modularized test with sagemath-categories after :issue:`35095`, which has @@ -526,7 +526,7 @@ def _builder_and_stream(vars, domain): sage: domain = RDF sage: from sage.structure.element import Element as domain sage: _builder_and_stream(["x", "y"], domain) - (, + (, ) """ if isinstance(domain, sage.rings.abc.RealField): @@ -1804,7 +1804,7 @@ cpdef generate_code(Expression expr, InstructionStream stream): sage: instr_stream.instr('return') sage: v = Wrapper_py(instr_stream.get_current()) sage: type(v) - + sage: v(7) 8*pi + 56 diff --git a/src/sage/ext/interpreters/meson.build b/src/sage/ext/interpreters/meson.build new file mode 100644 index 00000000000..f965ce83da0 --- /dev/null +++ b/src/sage/ext/interpreters/meson.build @@ -0,0 +1,72 @@ + +interpreters = custom_target( + 'sage.ext.interpreters', + output: [ + 'all.py', + 'wrapper_cc.pxd', + 'wrapper_cdf.pxd', + 'wrapper_el.pxd', + 'wrapper_py.pxd', + 'wrapper_rdf.pxd', + 'wrapper_rr.pxd', + 'wrapper_cc.pyx', + 'interp_cc.c', + 'wrapper_cdf.pyx', + 'interp_cdf.c', + 'wrapper_el.pyx', + 'interp_el.c', + 'wrapper_py.pyx', + 'interp_py.c', + 'wrapper_rdf.pyx', + 'interp_rdf.c', + 'wrapper_rr.pyx', + 'interp_rr.c', + '__init__.py', + ], + input: '../../../sage_setup/autogen/interpreters/internal/__init__.py', + command: ['../../../sage_setup/autogen/interpreters/__main__.py', '@OUTDIR@'], + # Manually install the generated files instead of using install_sources + # this is a workaround for https://github.com/mesonbuild/meson/issues/7372 + install: true, + install_dir: py.get_install_dir() / 'sage/ext/interpreters', + install_tag: 'python-runtime', +) + +# Use this once https://github.com/mesonbuild/meson/issues/7372 is fixed +#foreach file : interpreters.to_list() +# py.install_sources( +# file.full_path(), +# subdir : 'sage/ext/interpreters' +# ) +#endforeach + +extension_data = { + 'wrapper_cc' : interpreters[7], + 'wrapper_cdf' : interpreters[9], + 'wrapper_el' : interpreters[11], + 'wrapper_py' : interpreters[13], + 'wrapper_rdf' : interpreters[15], + 'wrapper_rr' : interpreters[17], +} + +interpreters_dep = declare_dependency( + include_directories: include_directories('.'), + sources: [ + interpreters[1], + interpreters[2], + interpreters[3], + interpreters[4], + interpreters[5], + ], +) + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/ext/interpreters', + install: true, + include_directories: [inc_cpython, inc_ext, inc_rings, inc_src], + dependencies: [py_dep, cypari2, cysignals, gmp, gsl, mpc, mpfr, pari], + ) +endforeach diff --git a/src/sage/ext/meson.build b/src/sage/ext/meson.build new file mode 100644 index 00000000000..73d0e85101d --- /dev/null +++ b/src/sage/ext/meson.build @@ -0,0 +1,30 @@ +py.install_sources( + 'all__sagemath_objects.py', + 'ccobject.h', + 'cplusplus.pxd', + 'fast_callable.pxd', + 'fast_eval.pxd', + 'mod_int.h', + 'mod_int.pxd', + 'stdsage.pxd', + subdir: 'sage/ext', +) + +extension_data = { + 'fast_callable' : files('fast_callable.pyx'), + 'fast_eval' : files('fast_eval.pyx'), + 'memory' : files('memory.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/ext', + install: true, + include_directories: [inc_cpython, inc_ext], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +subdir('interpreters') diff --git a/src/sage/ext_data/nbconvert/postprocess.py b/src/sage/ext_data/nbconvert/postprocess.py index f3b208971c9..1774b9e1ca5 100755 --- a/src/sage/ext_data/nbconvert/postprocess.py +++ b/src/sage/ext_data/nbconvert/postprocess.py @@ -19,7 +19,7 @@ file_name = sys.argv[1] - with open(file_name, 'r') as f: + with open(file_name) as f: lines = f.readlines() # states of the parser diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index 81602180d80..e02015a5710 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -467,7 +467,7 @@ def __str__(self): return "\n".join(lines) -class FeatureTestResult(): +class FeatureTestResult: r""" The result of a :meth:`Feature.is_present` call. diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py index 22197942b00..609cb37c263 100644 --- a/src/sage/features/gap.py +++ b/src/sage/features/gap.py @@ -81,6 +81,7 @@ def all_features(): GapPackage("grape", spkg='gap_packages'), GapPackage("guava", spkg='gap_packages'), GapPackage("hap", spkg='gap_packages'), + GapPackage("polenta", spkg='gap_packages'), GapPackage("polycyclic", spkg='gap_packages'), GapPackage("qpa", spkg='gap_packages'), GapPackage("quagroup", spkg='gap_packages')] diff --git a/src/sage/features/giac.py b/src/sage/features/giac.py new file mode 100644 index 00000000000..6f9fe2ccfba --- /dev/null +++ b/src/sage/features/giac.py @@ -0,0 +1,30 @@ +# sage_setup: distribution = sagemath-environment +r""" +Feature for testing the presence of ``giac`` +""" + +from . import Executable, FeatureTestResult + +class Giac(Executable): + r""" + A :class:`~sage.features.Feature` describing the presence of :ref:`giac `. + + EXAMPLES:: + + sage: from sage.features.giac import Giac + sage: Giac().is_present() # needs giac + FeatureTestResult('giac', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.giac import Giac + sage: isinstance(Giac(), Giac) + True + """ + Executable.__init__(self, 'giac', executable='giac', + spkg='giac', type='standard') + +def all_features(): + return [Giac()] diff --git a/src/sage/features/jmol.py b/src/sage/features/jmol.py index 52104202a40..cf5780094bd 100644 --- a/src/sage/features/jmol.py +++ b/src/sage/features/jmol.py @@ -36,7 +36,7 @@ def __init__(self): filename='JmolData.jar', search_path=jmol_search_path, spkg='jmol', - type='standard', + type='optional', description="Java viewer for chemical structures in 3D") diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index b5e4c59b3bb..f536665d8cc 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -537,6 +537,32 @@ def __init__(self): spkg='sagemath_ntl', type='standard') +class sage__libs__giac(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.giac`. + + In addition to the modularization purposes that this tag serves, + it also provides attribution to the upstream project. + + TESTS:: + + sage: from sage.features.sagemath import sage__libs__giac + sage: sage__libs__giac().is_present() # needs sage.libs.giac + FeatureTestResult('sage.libs.giac', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__libs__giac + sage: isinstance(sage__libs__giac(), sage__libs__giac) + True + """ + JoinFeature.__init__(self, 'sage.libs.giac', + [PythonModule('sage.libs.giac.giac')], + spkg='sagemath_giac', type='standard') + + class sage__libs__homfly(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.homfly`. @@ -1155,6 +1181,7 @@ def all_features(): sage__libs__ecl(), sage__libs__flint(), sage__libs__gap(), + sage__libs__giac(), sage__libs__homfly(), sage__libs__linbox(), sage__libs__m4ri(), diff --git a/src/sage/features/sphinx.py b/src/sage/features/sphinx.py index ec7c8be17b6..672b826f59d 100644 --- a/src/sage/features/sphinx.py +++ b/src/sage/features/sphinx.py @@ -39,5 +39,33 @@ def __init__(self): PythonModule.__init__(self, 'sphinx', spkg='sphinx', type='standard') +class JupyterSphinx(PythonModule): + r""" + A :class:`sage.features.Feature` describing the presence of + :ref:`jupyter_sphinx `. + + It is provided by a standard package in the Sage distribution, + but it can be disabled by ``configure --disable-doc`` and + ``configure --disable-notebook``. + + EXAMPLES:: + + sage: from sage.features.sphinx import JupyterSphinx + sage: JupyterSphinx().is_present() # optional - jupyter_sphinx + FeatureTestResult('jupyter_sphinx', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sphinx import JupyterSphinx + sage: isinstance(JupyterSphinx(), JupyterSphinx) + True + """ + PythonModule.__init__(self, 'jupyter_sphinx', + spkg='jupyter_sphinx', type='standard') + + def all_features(): - return [Sphinx()] + return [Sphinx(), + JupyterSphinx()] diff --git a/src/sage/features/threejs.py b/src/sage/features/threejs.py index 56ad807babf..8698b84e0e5 100644 --- a/src/sage/features/threejs.py +++ b/src/sage/features/threejs.py @@ -1,5 +1,5 @@ # sage_setup: distribution = sagemath-environment -import os +from pathlib import Path from . import StaticFile @@ -26,16 +26,17 @@ def __init__(self): """ from sage.env import SAGE_SHARE, THREEJS_DIR + share_dir = Path(SAGE_SHARE) threejs_search_path = THREEJS_DIR or ( - os.path.join(SAGE_SHARE, "jupyter", "nbextensions", "threejs-sage"), - os.path.join(SAGE_SHARE, "sagemath", "threejs-sage"), - os.path.join(SAGE_SHARE, "sage", "threejs"), - os.path.join(SAGE_SHARE, "threejs-sage") + (share_dir / "jupyter" / "nbextensions" / "threejs-sage"), + (share_dir / "sagemath" / "threejs-sage"), + (share_dir / "sage" / "threejs"), + (share_dir / "threejs-sage") ) try: version = self.required_version() - filename = os.path.join(version, "three.min.js") + filename = Path(version) / "three.min.js" except FileNotFoundError: filename = 'unknown' @@ -64,7 +65,7 @@ def required_version(self): """ from sage.env import SAGE_EXTCODE - filename = os.path.join(SAGE_EXTCODE, 'threejs', 'threejs-version.txt') + filename = Path(SAGE_EXTCODE) / 'threejs' / 'threejs-version.txt' with open(filename) as f: return f.read().strip() diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 2eced09d619..1cd132b3a45 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1228,7 +1228,7 @@ def Bessel(*args, **kwds): _type = kwds['typ'] else: _type = 'J' - if not (_type in ['I', 'J', 'K', 'Y']): + if _type not in ['I', 'J', 'K', 'Y']: raise ValueError("type must be one of I, J, K, Y") # return the function diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index ac88a0146a1..e074c430808 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -800,7 +800,7 @@ def __init__(self): Si(x) sage: sin_integral(x)._fricas_init_() 'Si(x)' - sage: sin_integral(x)._giac_() # needs sage.libs.giac + sage: sin_integral(x)._giac_() # needs giac Si(sageVARx) """ BuiltinFunction.__init__(self, "sin_integral", nargs=1, @@ -975,7 +975,7 @@ def __init__(self): Ci(x) sage: cos_integral(x)._fricas_init_() 'Ci(x)' - sage: cos_integral(x)._giac_() # needs sage.libs.giac + sage: cos_integral(x)._giac_() # needs giac Ci(sageVARx) """ BuiltinFunction.__init__(self, "cos_integral", nargs=1, diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index f8f92b293a4..f43849d2043 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -253,7 +253,7 @@ def __init__(self): H\left(x\right) sage: heaviside(x)._sympy_() # needs sympy Heaviside(x) - sage: heaviside(x)._giac_() # needs sage.libs.giac + sage: heaviside(x)._giac_() # needs giac Heaviside(sageVARx) sage: h(x) = heaviside(x) sage: h(pi).numerical_approx() @@ -413,7 +413,7 @@ class FunctionSignum(BuiltinFunction): sign(x) sage: sgn(x)._fricas_init_() # needs sage.symbolic '(x+->abs(x)/x)(x)' - sage: sgn(x)._giac_() # needs sage.libs.giac sage.symbolic + sage: sgn(x)._giac_() # needs giac sage.symbolic sign(sageVARx) Test for :issue:`31085`:: diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index ade7836b310..5431d15ce18 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -123,8 +123,8 @@ 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 sympy sage.symbolic - hyper((5, 4), (4, 4), 3) + sage: hypergeometric((5,), (4,), 3)._sympy_() # needs sympy sage.symbolic + hyper((5,), (4,), 3) sage: hypergeometric((5, 4), (4, 4), 3)._mathematica_init_() # needs sage.symbolic 'HypergeometricPFQ[{5,4},{4,4},3]' @@ -295,6 +295,11 @@ def __call__(self, a, b, z, **kwargs): The only simplification that is done automatically is returning 1 if ``z`` is 0. For other simplifications use the ``simplify_hypergeometric`` method. + + TESTS:: + + sage: hypergeometric([2, 3, 4], [4, 1], 1) + hypergeometric((2, 3, 4), (4, 1), 1) """ return BuiltinFunction.__call__(self, SR._force_pyobject(a), @@ -401,7 +406,7 @@ def _tderivative_(self, a, b, z, *args, **kwargs): return (t * derivative(z, diff_param) * hypergeometric([c + 1 for c in a], [c + 1 for c in b], z)) - class EvaluationMethods(): + class EvaluationMethods: def _fast_callable_(self, a, b, z, etb): """ @@ -458,23 +463,21 @@ def eliminate_parameters(self, a, b, z): """ aa = list(a) # tuples are immutable bb = list(b) - p = pp = len(aa) q = qq = len(bb) i = 0 while i < qq and aa: - bbb = bb[i] - if bbb in aa: - aa.remove(bbb) - bb.remove(bbb) - pp -= 1 + bbi = bb[i] + if bbi in aa: + aa.remove(bbi) + bb.remove(bbi) qq -= 1 else: i += 1 - if (pp, qq) != (p, q): + if qq != q: return hypergeometric(aa, bb, z) return self - def is_termwise_finite(self, a, b, z): + def is_termwise_finite(self, a, b, z) -> bool: """ Determine whether all terms of ``self`` are finite. @@ -516,8 +519,6 @@ def is_termwise_finite(self, a, b, z): return 0 not in b if abs(z) == Infinity: return False - if abs(z) == Infinity: - return False for bb in b: if bb in ZZ and bb <= 0: if any((aa in ZZ) and (bb < aa <= 0) for aa in a): @@ -1025,7 +1026,7 @@ def _derivative_(self, a, b, z, diff_param): raise NotImplementedError('derivative of hypergeometric function ' 'with respect to parameters') - class EvaluationMethods(): + class EvaluationMethods: def generalized(self, a, b, z): """ Return as a generalized hypergeometric function. @@ -1131,7 +1132,7 @@ def _derivative_(self, a, b, z, diff_param): raise NotImplementedError('derivative of hypergeometric function ' 'with respect to parameters') - class EvaluationMethods(): + class EvaluationMethods: def generalized(self, a, b, z): """ Return in terms of the generalized hypergeometric function. diff --git a/src/sage/functions/meson.build b/src/sage/functions/meson.build new file mode 100644 index 00000000000..c2a77f0e238 --- /dev/null +++ b/src/sage/functions/meson.build @@ -0,0 +1,37 @@ +py.install_sources( + 'airy.py', + 'all.py', + 'bessel.py', + 'error.py', + 'exp_integral.py', + 'gamma.py', + 'generalized.py', + 'hyperbolic.py', + 'hypergeometric.py', + 'jacobi.py', + 'log.py', + 'min_max.py', + 'orthogonal_polys.py', + 'other.py', + 'piecewise.py', + 'special.py', + 'spike_function.py', + 'transcendental.py', + 'trig.py', + 'wigner.py', + subdir: 'sage/functions', +) + +extension_data = {'prime_pi' : files('prime_pi.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/functions', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 78f4c6bc712..4bf17b6c049 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -2343,10 +2343,10 @@ class Func_ultraspherical(GinacFunction): sage: # needs mpmath sage: from mpmath import gegenbauer as gegenbauer_mp sage: from mpmath import mp - sage: mp.pretty = True; mp.dps=25 - sage: gegenbauer_mp(-7,0.5,0.3) + sage: print(gegenbauer_mp(-7,0.5,0.3)) 0.1291811875 - sage: gegenbauer_mp(2+3j, -0.75, -1000j) + sage: with mp.workdps(25): + ....: print(gegenbauer_mp(2+3j, -0.75, -1000j)) (-5038991.358609026523401901 + 9414549.285447104177860806j) TESTS: diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index e57fbf14cd9..2f4c5c5b546 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1860,7 +1860,7 @@ def __init__(self): sage: isinstance(r.operator(), # known bug # needs sympy ....: sage.functions.other.Function_prod) True - sage: giac(sprod(m, m, 1, n)).sage() + sage: giac(sprod(m, m, 1, n)).sage() # needs giac factorial(n) """ BuiltinFunction.__init__(self, "product", nargs=4, diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 8a8cbcb62b2..fc69057ef6f 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -177,10 +177,10 @@ def _print_(self, parameters, variable): 'piecewise(x|-->-x on (-2, 0), x|-->x on [0, 4]; x)' """ s = 'piecewise(' - args = [] - for domain, func in parameters: - args.append('{0}|-->{1} on {2}'.format(str(variable), str(func), str(domain))) - s += ', '.join(args) + '; {0})'.format(str(variable)) + # NOTE : could use ⟼ instead of |--> + args = (f'{variable}|-->{func} on {domain}' + for domain, func in parameters) + s += ', '.join(args) + f'; {variable})' return s def _subs_(self, subs_map, options, parameters, x): @@ -209,25 +209,31 @@ def _subs_(self, subs_map, options, parameters, x): piecewise(x|-->-x^y on (-2, 0), x|-->x - y on [0, 2]; x) sage: p.subs(y=sin(y)) piecewise(x|-->-x^sin(y) on (-2, 0), x|-->x - sin(y) on [0, 2]; x) + + One can change the variable as follows:: + + sage: p = piecewise([((-2, 0), -x), ([0, 4], x)], var=x) + sage: y = SR.var('y') + sage: p(y) + piecewise(y|-->-y on (-2, 0), y|-->y on [0, 4]; y) """ point = subs_map.apply_to(x, 0) - if ((point.is_numeric() or point.is_constant()) and (point.is_real())): + + if point.is_symbol(): # avoid to compare with x (see #37925) + new_params = [(domain, subs_map.apply_to(func, 0)) + for domain, func in parameters] + return piecewise(new_params, var=point) + + 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) - else: - raise ValueError('substituting the piecewise variable must result in real number') + if domain.contains(point): + return subs_map.apply_to(func, 0) + raise ValueError(f'point {point} is not in the domain') - for domain, func in parameters: - if domain.contains(point): - return subs_map.apply_to(func, 0) - raise ValueError('point {} is not in the domain'.format(point)) + raise ValueError('substitution not allowed') @staticmethod def in_operands(ex): @@ -253,10 +259,12 @@ def in_operands(ex): False """ def is_piecewise(ex): - result = ex.operator() is piecewise + if ex.operator() is piecewise: + return True for op in ex.operands(): - result = result or is_piecewise(op) - return result + if is_piecewise(op): + return True + return False return is_piecewise(ex) @staticmethod @@ -266,7 +274,7 @@ def simplify(ex): OUTPUT: - A piecewise function whose operands are not piecewiese if + A piecewise function whose operands are not piecewise if possible, that is, as long as the piecewise variable is the same. EXAMPLES:: @@ -311,7 +319,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds): for domain, func in parameters], var=variable) - class EvaluationMethods(): + class EvaluationMethods: def __pow__(self, parameters, variable, n): """ @@ -592,8 +600,10 @@ def unextend_zero(self, parameters, variable): sage: bool(h == f) True """ - result = [(domain, func) for domain,func in parameters + result = [(domain, func) for domain, func in parameters if func != 0] + if len(result) == len(self): + return self return piecewise(result, var=variable) def pieces(self, parameters, variable): @@ -612,12 +622,10 @@ def pieces(self, parameters, variable): (piecewise(x|-->-x on (-1, 0); x), piecewise(x|-->x on [0, 1]; x)) """ - result = [] - for domain, func in parameters: - result.append(piecewise([(domain, func)], var=variable)) - return tuple(result) + return tuple(piecewise([(domain, func)], var=variable) + for domain, func in parameters) - def end_points(self, parameters, variable): + def end_points(self, parameters, variable) -> list: """ Return a list of all interval endpoints for this function. @@ -665,39 +673,40 @@ def piecewise_add(self, parameters, variable, other): funcs = [] contains_lower = False contains_upper = False - for i in range(len(points)-1): + for i in range(len(points) - 1): + a, b = points[i], points[i + 1] try: - contains_lower = (self.domain().contains(points[i]) or - other.domain().contains(points[i])) and not contains_upper - contains_upper = (self.domain().contains(points[i+1]) or - other.domain().contains(points[i+1])) + contains_lower = (self.domain().contains(a) or + other.domain().contains(a)) and not contains_upper + contains_upper = (self.domain().contains(b) or + other.domain().contains(b)) if contains_lower: if contains_upper: - rs = RealSet.closed(points[i],points[i+1]) + rs = RealSet.closed(a, b) else: - rs = RealSet.closed_open(points[i],points[i+1]) + rs = RealSet.closed_open(a, b) else: if contains_upper: - rs = RealSet.open_closed(points[i],points[i+1]) + rs = RealSet.open_closed(a, b) else: - rs = RealSet.open(points[i],points[i+1]) - point = (points[i+1] + points[i])/2 + rs = RealSet.open(a, b) + point = (b + a) / 2 except ValueError: - if points[i] == minus_infinity and points[i+1] == infinity: + if a == minus_infinity and b == infinity: rs = RealSet.open(minus_infinity, infinity) point = 0 - elif points[i] == minus_infinity: + elif a == minus_infinity: if contains_lower: - rs = RealSet.unbounded_below_closed(points[i+1]) + rs = RealSet.unbounded_below_closed(b) else: - rs = RealSet.unbounded_below_open(points[i+1]) - point = points[i+1]-1 - elif points[i+1] == infinity: + rs = RealSet.unbounded_below_open(b) + point = b - 1 + elif b == infinity: if contains_upper: - rs = RealSet.unbounded_above_closed(points[i]) + rs = RealSet.unbounded_above_closed(a) else: - rs = RealSet.unbounded_above_open(points[i]) - point = points[i]+1 + rs = RealSet.unbounded_above_open(a) + point = a + 1 else: raise try: @@ -828,9 +837,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='sympy') 199/100 - sage: integral(ex, x, algorithm='giac') + sage: integral(ex, x, algorithm='sympy') piecewise(x|-->x on [0, 1], x|-->-1/x + 2 on (1, +oo); x) """ if a is not None and b is not None: @@ -870,7 +879,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False, else: try: assume(start < x) - except ValueError: # Assumption is redundant + except ValueError: # Assumption is redundant pass fun_integrated = fun.integral(x, start, x, **kwds) + area forget(start < x) @@ -974,6 +983,17 @@ def convolution(self, parameters, variable, other): x|-->-x + 6 on (3, 4], x|-->-2*x + 10 on (4, 5]; x) + Some unbounded but convergent cases now work:: + + sage: p = piecewise([[(2,oo),exp(-x)]]) + sage: q = piecewise([[[2,3],x]]) + sage: p.convolution(q) + piecewise(x|-->(x - 3)*e^(-2) - e^(-x + 2) on (4, 5]; x) + sage: q.convolution(p) + piecewise(x|-->(x - 3)*e^(-2) - e^(-x + 2) on (4, 5]; x) + + TESTS: + Check that the bugs raised in :issue:`12123` are fixed:: sage: f = piecewise([[(-2, 2), 2]]) @@ -992,44 +1012,61 @@ def convolution(self, parameters, variable, other): from sage.symbolic.integration.integral import definite_integral f = self g = other - if len(f.end_points())*len(g.end_points()) == 0: + if not f.end_points() or not g.end_points(): raise ValueError('one of the piecewise functions is nowhere defined') fd, f0 = parameters[0] gd, g0 = next(other.items()) - if len(f) == 1 and len(g) == 1: - f = f.unextend_zero() - g = g.unextend_zero() + if len(f) == 1 == len(g): a1 = fd[0].lower() a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() - with SR.temp_var() as tt: - with SR.temp_var() as uu: - i1 = f0.subs({variable: uu}) - i2 = g0.subs({variable: tt-uu}) - fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs({tt:variable}) - fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs({tt:variable}) - fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs({tt:variable}) - fg4 = definite_integral(i1*i2, uu, a1, a2).subs({tt:variable}) - if a1-b1 < a2-b2: - if a2+b1 != a1+b2: - h = piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b1),fg2],[(a2+b1,a2+b2),fg3]]) - else: - h = piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b2),fg3]]) + a1b1 = a1 + b1 + a2b2 = a2 + b2 + delta_a = a2 - a1 + delta_b = b2 - b1 + + # this fails in some unbounded cases: + a1b2 = a1 + b2 + a2b1 = a2 + b1 + + todo = [] + if delta_a > delta_b: + if a1b2 is not minus_infinity: + todo.append((a1b1, a1b2, a1, variable - b1)) + todo.append((a1b2, a2b1, variable - b2, variable - b1)) + if a2b1 is not infinity: + todo.append((a2b1, a2b2, variable - b2, a2)) + elif delta_a < delta_b: + if a2b1 is not minus_infinity: + todo.append((a1b1, a2b1, a1, variable - b1)) + todo.append((a2b1, a1b2, a1, a2)) + if a1b2 is not infinity: + todo.append((a1b2, a2b2, variable - b2, a2)) else: - if a1+b2 != a2+b1: - h = piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a1+b2),fg4],[(a1+b2,a2+b2),fg3]]) - else: - h = piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a2+b2),fg3]]) - return (piecewise([[(minus_infinity,infinity),0]]).piecewise_add(h)).unextend_zero() - - if len(f) > 1 or len(g) > 1: - z = piecewise([[(0,0),0]]) - for fpiece in f.pieces(): - for gpiece in g.pieces(): - h = gpiece.convolution(fpiece) - z = z.piecewise_add(h) - return z.unextend_zero() + if a2b1 is not minus_infinity: + todo.append((a1b1, a2b1, a1, variable - b1)) + todo.append((a2b1, a2b2, variable - b2, a2)) + + if not todo: + raise ValueError("no domain of integration") + + with SR.temp_var() as uu: + i1 = f0.subs({variable: uu}) + i2 = g0.subs({variable: variable - uu}) + expr = i1 * i2 + h = piecewise([[(start, stop), + definite_integral(expr, uu, mini, maxi)] + for start, stop, mini, maxi in todo]) + flat_zero = piecewise([[(minus_infinity, infinity), 0]]) + return (flat_zero.piecewise_add(h)).unextend_zero() # why ? + + z = piecewise([[(0, 0), 0]]) + for fpiece in f.pieces(): + for gpiece in g.pieces(): + h = gpiece.convolution(fpiece) + z = z.piecewise_add(h) + return z.unextend_zero() def trapezoid(self, parameters, variable, N): """ @@ -1069,8 +1106,8 @@ def trapezoid(self, parameters, variable, N): """ def func(x0, x1): f0, f1 = self(x0), self(x1) - return [[(x0,x1), f0 + (f1-f0) * (x1-x0)**(-1) - * (self.default_variable()-x0)]] + return [[(x0, x1), f0 + (f1-f0) * (x1-x0)**(-1) + * (self.default_variable()-x0)]] rsum = [] for domain, f in parameters: for interval in domain: @@ -1124,17 +1161,17 @@ def laplace(self, parameters, variable, x='x', s='t'): (s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2 """ from sage.symbolic.assumptions import assume, forget - from sage.functions.log import exp x = SR.var(x) s = SR.var(s) assume(s > 0) + exp_sx = (-s * x).exp() result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() - result += (SR(f)*exp(-s*x)).integral(x,a,b) + result += (SR(f) * exp_sx).integral(x, a, b) forget(s > 0) return result @@ -1230,7 +1267,7 @@ def fourier_series_cosine_coefficient(self, parameters, a = interval.lower() b = interval.upper() result += (f*cos(pi*variable*n/L)).integrate(variable, a, b) - return SR(result/L0).simplify_trig() + return SR(result / L0).simplify_trig() def fourier_series_sine_coefficient(self, parameters, variable, n, L=None): @@ -1434,16 +1471,18 @@ def _giac_init_(self, parameters, variable): EXAMPLES:: + sage: # needs giac sage: ex = piecewise([((0, 1), pi), ([1, 2], x)]) - sage: f = ex._giac_(); f # needs sage.libs.giac + sage: f = ex._giac_(); f piecewise(((sageVARx>0) and (1>sageVARx)),pi,((sageVARx>=1) and (2>=sageVARx)),sageVARx) - sage: f.diff(x) # needs sage.libs.giac + sage: f.diff(x) piecewise(((sageVARx>0) and (1>sageVARx)),0,((sageVARx>=1) and (2>=sageVARx)),1) - sage: ex = piecewise([((-100, -2), 1/x), ((1, +oo), cos(x))]) # needs sage.libs.giac - sage: g = ex._giac_(); g # needs sage.libs.giac + sage: # needs giac + sage: ex = piecewise([((-100, -2), 1/x), ((1, +oo), cos(x))]) + sage: g = ex._giac_(); g piecewise(((sageVARx>-100) and ((-2)>sageVARx)),1/sageVARx,sageVARx>1,cos(sageVARx)) - sage: g.diff(x) # needs sage.libs.giac + sage: g.diff(x) piecewise(((sageVARx>-100) and ((-2)>sageVARx)),-1/sageVARx^2,sageVARx>1,-sin(sageVARx)) TESTS:: diff --git a/src/sage/functions/spike_function.py b/src/sage/functions/spike_function.py index d7e317ba615..6fa011434d8 100644 --- a/src/sage/functions/spike_function.py +++ b/src/sage/functions/spike_function.py @@ -147,7 +147,7 @@ def __call__(self, x): """ return self._eval(x)[0] - def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds): + def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds): """ Plot of (absolute values of) Fast Fourier Transform of the spike function with given number of samples. @@ -166,7 +166,7 @@ def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds): k = vector(RDF, [abs(z[i]) for i in range(len(z)//2)]) return k.plot(xmin=0, xmax=1, **kwds) - def plot_fft_arg(self, samples=2**12, xmin=None, xmax=None, **kwds): + def plot_fft_arg(self, samples=2**12, xmin=None, xmax=None, **kwds): """ Plot of (absolute values of) Fast Fourier Transform of the spike function with given number of samples. diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index 7cfa2640473..55e24a44cbd 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -449,7 +449,7 @@ def zeta_symmetric(s): - I copied the definition of xi from http://web.viu.ca/pughg/RiemannZeta/RiemannZetaLong.html """ - if not (isinstance(s, ComplexNumber) or isinstance(s, RealNumber)): + if not isinstance(s, (ComplexNumber, RealNumber)): s = ComplexField()(s) R = s.parent() diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index f912dd79e8f..8f390205cae 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -261,7 +261,9 @@ def __init__(self): sage: tan(2+I).imag().n() # needs sage.symbolic 1.16673625724092 """ - GinacFunction.__init__(self, 'tan', latex_name=r"\tan") + GinacFunction.__init__(self, 'tan', latex_name=r"\tan", + conversions=dict(maxima='tan', mathematica='Tan', + giac='tan', fricas='tan', sympy='tan')) tan = Function_tan() @@ -349,7 +351,9 @@ def __init__(self): sage: cot(1.+I) # needs sage.symbolic 0.217621561854403 - 0.868014142895925*I """ - GinacFunction.__init__(self, 'cot', latex_name=r"\cot") + GinacFunction.__init__(self, 'cot', latex_name=r"\cot", + conversions=dict(maxima='cot', mathematica='Cot', + giac='cot', fricas='cot', sympy='cot')) def _eval_numpy_(self, x): """ @@ -421,7 +425,9 @@ def __init__(self): sage: sec(complex(1,1)) # rel tol 1e-15 # needs sage.rings.complex_double (0.49833703055518686+0.5910838417210451j) """ - GinacFunction.__init__(self, 'sec', latex_name=r"\sec") + GinacFunction.__init__(self, 'sec', latex_name=r"\sec", + conversions=dict(maxima='sec', mathematica='Sec', + giac='sec', fricas='sec', sympy='sec')) def _eval_numpy_(self, x): """ @@ -493,7 +499,9 @@ def __init__(self): sage: csc(complex(1,1)) # rel tol 1e-15 # needs sage.rings.complex_double (0.6215180171704284-0.30393100162842646j) """ - GinacFunction.__init__(self, 'csc', latex_name=r"\csc") + GinacFunction.__init__(self, 'csc', latex_name=r"\csc", + conversions=dict(maxima='csc', mathematica='Csc', + giac='csc', fricas='csc', sympy='csc')) def _eval_numpy_(self, x): """ diff --git a/src/sage/game_theory/matching_game.py b/src/sage/game_theory/matching_game.py index 5b3f081bf10..7a51988baec 100644 --- a/src/sage/game_theory/matching_game.py +++ b/src/sage/game_theory/matching_game.py @@ -939,7 +939,7 @@ def solve(self, invert=False): return {key: self._sol_dict[key][0] for key in self._suitors} -class Player(): +class Player: r""" A class to act as a data holder for the players used of the matching games. diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 8f19e4842e1..e46e96c2b32 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -2783,7 +2783,7 @@ def _is_degenerate_pure(self, certificate=False): return False -class _Player(): +class _Player: def __init__(self, num_strategies): r""" TESTS:: diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index d528116f244..6ab62d5b7ff 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -13,7 +13,7 @@ # **************************************************************************** -class Parser(): +class Parser: r""" A class for parsing the outputs of different algorithms called in other software packages. @@ -296,7 +296,7 @@ def format_gambit(self, gambit_game): nice_stuff = [] for gambitstrategy in self.raw_string: gambitstrategy = list(gambitstrategy) - profile = [tuple(gambitstrategy[:len(gambit_game.players[int(0)].strategies)])] + profile = [tuple(gambitstrategy[:len(gambit_game.players[0].strategies)])] for player in list(gambit_game.players)[1:]: previousplayerstrategylength = len(profile[-1]) profile.append(tuple(gambitstrategy[previousplayerstrategylength: previousplayerstrategylength + len(player.strategies)])) diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index 5d3e2785900..6a8b5e178e5 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -466,15 +466,15 @@ def find_hexad3(self, pts, x0, x1): L = set(pts) H = {x0, x1} for i in range(18): - if (not (MINIMOG[0][2] in H) and L <= picture_set(self.picture21, self.square[i])): + if (MINIMOG[0][2] not in H and L <= picture_set(self.picture21, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[0][2])] H = H | picture_set(self.picture21, self.square[i]) return list(H), WHAT - if (not (MINIMOG[2][1] in H) and L <= picture_set(self.picture02, self.square[i])): + if (MINIMOG[2][1] not in H and L <= picture_set(self.picture02, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[2][1])] H = H | picture_set(self.picture02, self.square[i]) return list(H), WHAT - if (not (MINIMOG[0][0] in H) and L <= picture_set(self.picture00, self.square[i])): + if (MINIMOG[0][0] not in H and L <= picture_set(self.picture00, self.square[i])): WHAT = ["square " + str(i), "picture " + str(MINIMOG[0][0])] H = H | picture_set(self.picture00, self.square[i]) return list(H), WHAT diff --git a/src/sage/games/meson.build b/src/sage/games/meson.build new file mode 100644 index 00000000000..d0776c0c71a --- /dev/null +++ b/src/sage/games/meson.build @@ -0,0 +1,21 @@ +py.install_sources( + 'all.py', + 'hexad.py', + 'quantumino.py', + 'sudoku.py', + subdir: 'sage/games', +) + +extension_data = {'sudoku_backtrack' : files('sudoku_backtrack.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/games', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index c1b3fa8b62d..a500fef808e 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -468,9 +468,9 @@ def to_latex(self): for row in range(nsquare): for col in range(nsquare): entry = next(gen) - array.append((str(entry) if entry else ' ')) - array.append(('' if col == nsquare - 1 else '&')) - array.append(('\\\\\n' if (row+1) % n else '\\\\\\hline\n')) + array.append(str(entry) if entry else ' ') + array.append('' if col == nsquare - 1 else '&') + array.append('\\\\\n' if (row+1) % n else '\\\\\\hline\n') array.append('\\end{array}') return ''.join(array) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 17f58378373..c4b308adbfa 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1986,7 +1986,7 @@ def _some_elements_(self): """ V = self.ambient_vector_space() r_iter = iter(self._rays) - p = V(0) + p = V.zero() yield p for i in range(5): try: @@ -1995,7 +1995,7 @@ def _some_elements_(self): return yield p - def _sort_faces(self, faces): + def _sort_faces(self, faces): r""" Return sorted (if necessary) ``faces`` as a tuple. @@ -2109,7 +2109,7 @@ def adjacent(self): if superfaces: for superface in superfaces: for facet in facets: - adjacent.update(L.open_interval(facet, superface)) + adjacent.update(L.open_interval(facet, superface)) if adjacent: adjacent.remove(L(self)) return self._sort_faces(adjacent) @@ -2603,7 +2603,7 @@ def ConeFace(atoms, facets): faces.append(self) for face in dfaces: L.add_edge(face_to_index[face], next_index) - D = {i:f for i,f in enumerate(faces)} + D = dict(enumerate(faces)) L.relabel(D) self._face_lattice = FinitePoset(L, faces, key=id(self)) return self._face_lattice diff --git a/src/sage/geometry/cone_catalog.py b/src/sage/geometry/cone_catalog.py index da2ab39a0e6..d58302d5915 100644 --- a/src/sage/geometry/cone_catalog.py +++ b/src/sage/geometry/cone_catalog.py @@ -72,6 +72,7 @@ # the top-level non-underscore functions defined in this module. # + def _preprocess_args(ambient_dim, lattice): r""" Preprocess arguments for cone-constructing functions. diff --git a/src/sage/geometry/cone_critical_angles.py b/src/sage/geometry/cone_critical_angles.py index 55636a15da8..9e5252a4bf9 100644 --- a/src/sage/geometry/cone_critical_angles.py +++ b/src/sage/geometry/cone_critical_angles.py @@ -40,6 +40,7 @@ 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 diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index a020df85b83..86cd4c83c65 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -1503,9 +1503,7 @@ def FanFace(rays, cones): head.extend(rays_to_index[(n,)] for n in range(self.nrays())) new_order = head + [n for n in new_order if n not in head] # "Invert" this list to a dictionary - labels = {} - for new, old in enumerate(new_order): - labels[old] = new + labels = {old: new for new, old in enumerate(new_order)} L.relabel(labels) elements = [None] * next_index @@ -1516,7 +1514,7 @@ def FanFace(rays, cones): # ray incidence information to the total list, it would be # confused with the generating cone in the case of a single cone. elements[labels[0]] = FanFace(tuple(range(self.nrays())), ()) - D = {i: f for i, f in enumerate(elements)} + D = dict(enumerate(elements)) L.relabel(D) self._cone_lattice = FinitePoset(L, elements, key=id(self)) diff --git a/src/sage/geometry/hasse_diagram.py b/src/sage/geometry/hasse_diagram.py index 99dc7dca9b8..74a30abed6d 100644 --- a/src/sage/geometry/hasse_diagram.py +++ b/src/sage/geometry/hasse_diagram.py @@ -188,9 +188,7 @@ def default_face_constructor(atoms, coatoms, **kwds): if required_atoms is None or atom in required_atoms) new_order = head + [n for n in new_order if n not in head] # "Invert" this list to a dictionary - labels = {} - for new, old in enumerate(new_order): - labels[old] = new + labels = {old: new for new, old in enumerate(new_order)} L.relabel(labels) # Construct the actual poset elements elements = [None] * next_index @@ -198,6 +196,6 @@ def default_face_constructor(atoms, coatoms, **kwds): atoms, coatoms = face elements[labels[index]] = face_constructor( tuple(sorted(atoms)), tuple(sorted(coatoms)), **kwds) - D = {i: f for i, f in enumerate(elements)} + D = dict(enumerate(elements)) L.relabel(D) return FiniteLatticePoset(L, elements, key=key) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py index c88d573e0cb..1e2c7270b39 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py @@ -29,6 +29,7 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.misc.call', 'attrcall') + class HyperbolicModelCoercion(Morphism): """ Abstract base class for morphisms between the hyperbolic models. @@ -148,6 +149,7 @@ def __invert__(self): # From UHP # ############ + class CoercionUHPtoPD(HyperbolicModelCoercion): """ Coercion from the UHP to PD model. @@ -188,6 +190,7 @@ def image_isometry_matrix(self, x): return matrix([[1,-I],[-I,1]]) * x * matrix([[1,I],[I,1]]).conjugate()/Integer(2) return matrix([[1,-I],[-I,1]]) * x * matrix([[1,I],[I,1]])/Integer(2) + class CoercionUHPtoKM(HyperbolicModelCoercion): """ Coercion from the UHP to KM model. @@ -227,6 +230,7 @@ def image_isometry_matrix(self, x): """ return SL2R_to_SO21(x) + class CoercionUHPtoHM(HyperbolicModelCoercion): """ Coercion from the UHP to HM model. @@ -269,6 +273,7 @@ def image_isometry_matrix(self, x): # From PD # ########### + class CoercionPDtoUHP(HyperbolicModelCoercion): """ Coercion from the PD to UHP model. @@ -318,6 +323,7 @@ def image_isometry_matrix(self, x): return matrix([[1,I],[I,1]]) * x * matrix([[1,-I],[-I,1]]).conjugate() / Integer(2) return matrix([[1,I],[I,1]]) * x * matrix([[1,-I],[-I,1]]) / Integer(2) + class CoercionPDtoKM(HyperbolicModelCoercion): """ Coercion from the PD to KM model. @@ -444,6 +450,7 @@ def image_isometry_matrix(self, x): """ return SO21_to_SL2R(x) + class CoercionKMtoPD(HyperbolicModelCoercion): """ Coercion from the KM to PD model. @@ -482,6 +489,7 @@ def image_isometry_matrix(self, x): return (matrix(2,[1,-I,-I,1]) * SO21_to_SL2R(x) * matrix(2,[1,I,I,1])/Integer(2)) + class CoercionKMtoHM(HyperbolicModelCoercion): """ Coercion from the KM to HM model. @@ -524,6 +532,7 @@ def image_isometry_matrix(self, x): # From HM # ########### + class CoercionHMtoUHP(HyperbolicModelCoercion): """ Coercion from the HM to UHP model. @@ -560,6 +569,7 @@ def image_isometry_matrix(self, x): """ return SO21_to_SL2R(x) + class CoercionHMtoPD(HyperbolicModelCoercion): """ Coercion from the HM to PD model. @@ -596,6 +606,7 @@ def image_isometry_matrix(self, x): return (matrix(2,[1,-I,-I,1]) * SO21_to_SL2R(x) * matrix(2,[1,I,I,1])/Integer(2)) + class CoercionHMtoKM(HyperbolicModelCoercion): """ Coercion from the HM to KM model. @@ -635,6 +646,7 @@ def image_isometry_matrix(self, x): ##################################################################### ## Helper functions + def SL2R_to_SO21(A): r""" Given a matrix in `SL(2, \RR)` return its irreducible representation in diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index 03a646b7059..2dd20676215 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -620,6 +620,7 @@ def attracting_fixed_point(self): fp = self._cached_isometry.attracting_fixed_point() return self.domain().get_point(fp) + class HyperbolicIsometryUHP(HyperbolicIsometry): r""" Create a hyperbolic isometry in the UHP model. @@ -881,6 +882,7 @@ def attracting_fixed_point(self): # UHP return self.domain().get_point(infinity) return self.domain().get_point(v[0] / v[1]) + class HyperbolicIsometryPD(HyperbolicIsometry): r""" Create a hyperbolic isometry in the PD model. @@ -982,6 +984,7 @@ def _orientation_preserving(A): #PD return bool(A[1][0] == A[0][1].conjugate() and A[1][1] == A[0][0].conjugate() and abs(A[0][0]) - abs(A[0][1]) != 0) + class HyperbolicIsometryKM(HyperbolicIsometry): r""" Create a hyperbolic isometry in the KM model. diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py index 3c408e69d95..16e96692a92 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py @@ -74,6 +74,7 @@ from sage.geometry.hyperbolic_space.hyperbolic_isometry import HyperbolicIsometry + class HyperbolicPoint(Element): r""" Abstract base class for hyperbolic points. This class should never diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 274702e0322..8517bb36671 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -792,7 +792,7 @@ def intersection_poset(self, element_label='int'): [[0], [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]] By passing the argument ``element_label="subset"``, each element of the - intesection poset is labelled by the set of indices of the hyperplanes + intersection poset is labelled by the set of indices of the hyperplanes whose intersection is said element. The index of a hyperplane is its index in ``self.hyperplanes()``. :: diff --git a/src/sage/geometry/hyperplane_arrangement/check_freeness.py b/src/sage/geometry/hyperplane_arrangement/check_freeness.py index f1691634f69..42943d72669 100644 --- a/src/sage/geometry/hyperplane_arrangement/check_freeness.py +++ b/src/sage/geometry/hyperplane_arrangement/check_freeness.py @@ -26,6 +26,7 @@ from sage.matrix.constructor import matrix import sage.libs.singular.function_factory as fun_fact + def less_generators(X): """ Reduce the generator matrix of the module defined by ``X``. @@ -63,6 +64,7 @@ def less_generators(X): Kd = set(range(X.nrows())).difference(K) X = X.matrix_from_rows(sorted(Kd)) + def construct_free_chain(A): """ Construct the free chain for the hyperplanes ``A``. diff --git a/src/sage/geometry/hyperplane_arrangement/plot.py b/src/sage/geometry/hyperplane_arrangement/plot.py index 190bef9982f..7e550bfd472 100644 --- a/src/sage/geometry/hyperplane_arrangement/plot.py +++ b/src/sage/geometry/hyperplane_arrangement/plot.py @@ -212,14 +212,14 @@ def plot(hyperplane_arrangement, **kwds): if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') - if not type(ranges) in [list,tuple]: # ranges is a single number + if type(ranges) not in [list,tuple]: # ranges is a single number ranges = [ranges] * N # So ranges is some type of list. elif dim == 2: # arrangement of lines in the plane - if not type(ranges[0]) in [list,tuple]: # a single interval + if type(ranges[0]) not in [list,tuple]: # a single interval ranges = [ranges] * N elif dim == 3: # arrangement of planes in 3-space - if not type(ranges[0][0]) in [list,tuple]: + if type(ranges[0][0]) not in [list,tuple]: ranges = [ranges] * N elif dim not in [2,3]: # ranges is not an option unless dim is 2 or 3 ranges_set = False diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 67f6e74ea89..2869dcc7442 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -415,6 +415,7 @@ def ReflexivePolytope(dim, n): # Sequences of reflexive polytopes _rp = [None] * 4 + def ReflexivePolytopes(dim): r""" Return the sequence of all 2- or 3-dimensional reflexive polytopes. @@ -494,6 +495,7 @@ def is_LatticePolytope(x): deprecation(34307, "is_LatticePolytope is deprecated, use isinstance instead") return isinstance(x, LatticePolytopeClass) + @richcmp_method class LatticePolytopeClass(ConvexSet_compact, Hashable, sage.geometry.abc.LatticePolytope): r""" @@ -589,7 +591,6 @@ def _sage_input_(self, sib, coerced): """ if self._ambient is not self: raise NotImplementedError - data = self._vertices return sib.name('LatticePolytope')(sib(self._vertices), compute_vertices=False) def __contains__(self, point): @@ -1327,7 +1328,7 @@ def _repr_(self): parts.extend(["face of", str(self.ambient())]) return " ".join(parts) - def _sort_faces(self, faces): + def _sort_faces(self, faces): r""" Return sorted (if necessary) ``faces`` as a tuple. @@ -1402,7 +1403,7 @@ def adjacent(self): adjacent = set() for superface in self.facet_of(): for facet in self.facets(): - adjacent.update(L.open_interval(facet, superface)) + adjacent.update(L.open_interval(facet, superface)) adjacent.discard(self) return self._sort_faces(adjacent) @@ -2064,7 +2065,7 @@ def LPFace(vertices, facets): faces.append(self) for face in dfaces: L.add_edge(face_to_index[face], next_index) - D = {i:f for i,f in enumerate(faces)} + D = dict(enumerate(faces)) L.relabel(D) return FinitePoset(L, faces, key=id(self)) @@ -4982,6 +4983,7 @@ def part_of_point(self, i): _palp_dimension = None + def _palp(command, polytopes, reduce_dimension=False): r""" Run ``command`` on vertices of given diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index bef7fd7160b..28388cd4232 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -500,7 +500,7 @@ def basis(self): """ from sage.sets.family import Family gens = self.gens() - d = {i: g for i, g in enumerate(gens)} + d = dict(enumerate(gens)) d['b'] = self.element_class(self, self.ambient_module().zero(), self.base_ring().one()) return Family(list(range(len(gens))) + ['b'], lambda i: d[i]) diff --git a/src/sage/geometry/meson.build b/src/sage/geometry/meson.build new file mode 100644 index 00000000000..826ca9ef1db --- /dev/null +++ b/src/sage/geometry/meson.build @@ -0,0 +1,55 @@ +py.install_sources( + 'all.py', + 'cone.py', + 'cone_catalog.py', + 'cone_critical_angles.py', + 'convex_set.py', + 'fan.py', + 'fan_isomorphism.py', + 'fan_morphism.py', + 'hasse_diagram.py', + 'integral_points.py', + 'lattice_polytope.py', + 'linear_expression.py', + 'newton_polygon.py', + 'polyhedral_complex.py', + 'pseudolines.py', + 'relative_interior.py', + 'ribbon_graph.py', + 'toric_lattice.py', + 'toric_plotter.py', + 'voronoi_diagram.py', + subdir: 'sage/geometry', +) + +extension_data = { + 'abc' : files('abc.pyx'), + 'integral_points_generic_dense' : files('integral_points_generic_dense.pyx'), + 'integral_points_integer_dense' : files('integral_points_integer_dense.pyx'), + 'palp_normal_form' : files('palp_normal_form.pyx'), + 'point_collection' : files('point_collection.pyx'), + 'toric_lattice_element' : files('toric_lattice_element.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/geometry', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + +install_subdir('hyperbolic_space', install_dir: sage_install_dir / 'geometry') +install_subdir( + 'hyperplane_arrangement', + install_dir: sage_install_dir / 'geometry', +) +subdir('polyhedron') +install_subdir( + 'riemannian_manifolds', + install_dir: sage_install_dir / 'geometry', +) +subdir('triangulation') diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 5e7d99e7587..539e2021f88 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -2074,8 +2074,7 @@ def add_cell(self, cell): raise ValueError("the cell is not face-to-face with complex") # update dim and maximal cells d = cell.dimension() - if d > self._dim: - self._dim = d + self._dim = max(d, self._dim) maximal_cells = poset.maximal_elements() # a list self._maximal_cells = cells_list_to_cells_dict(maximal_cells) # update convexity if self was known to be convex, reset otherwise. diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index e52461a08b7..f93f9d84d81 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -807,7 +807,7 @@ def rays_subspace_lattice_ieqs_QQ(vertices, rays, lines, ieqs): from sage.matrix.constructor import Matrix lattice = Matrix(ZZ, nmz_vertices + nmz_rays + nmz_lines).saturation() - nmz_lattice = [[x for x in y] for y in lattice] + nmz_lattice = [list(y) for y in lattice] if Matrix(ZZ, nmz_vertices + nmz_rays).rank() == Matrix(ZZ, nmz_rays).rank() + 1: # The recession cone is full-dimensional. @@ -2300,7 +2300,7 @@ class functions. # A check on whether the character table has permuted columns tbl = G_perm_gap.CharacterTable() perm = tbl.IdentificationOfConjugacyClasses() - ident_perm = [i for i in range(1, 1 + n_classes)] + ident_perm = list(range(1, 1 + n_classes)) assert perm == ident_perm, "The conjugacy classes don't match with the character table" # Create fixed subpolytopes and their Ehrhart series diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index f461b041bae..84fdd48177b 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -717,13 +717,15 @@ def _test_polymake_pickling(self, tester=None, other=None, **options): P = self._polymake_polytope P1 = other._polymake_polytope - tester.assertEqual(P.F_VECTOR, P1.F_VECTOR) - tester.assertEqual(P.VERTICES, P1.VERTICES) + tester.assertEqual(P.F_VECTOR, P1.F_VECTOR) + tester.assertEqual(P.VERTICES, P1.VERTICES) tester.assertEqual(P.LINEALITY_SPACE, P1.LINEALITY_SPACE) - tester.assertEqual(P.FACETS, P1.FACETS) - tester.assertEqual(P.AFFINE_HULL, P1.AFFINE_HULL) + tester.assertEqual(P.FACETS, P1.FACETS) + tester.assertEqual(P.AFFINE_HULL, P1.AFFINE_HULL) ######################################################################### + + class Polyhedron_QQ_polymake(Polyhedron_polymake, Polyhedron_QQ): r""" Polyhedra over `\QQ` with polymake. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4261b13a292..7110f74a202 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -584,7 +584,7 @@ def is_inscribed(self, certificate=False): for vertex in affine_basis: vertex_vector = vertex.vector() raw_data += [[sum(i**2 for i in vertex_vector)] + - [i for i in vertex_vector] + [1]] + list(vertex_vector) + [1]] matrix_data = matrix(raw_data) # The determinant "a" should not be zero because diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py index 7ba3007ea35..c710c5dfa62 100644 --- a/src/sage/geometry/polyhedron/base0.py +++ b/src/sage/geometry/polyhedron/base0.py @@ -35,6 +35,7 @@ from sage.structure.element import Element import sage.geometry.abc + class Polyhedron_base0(Element, sage.geometry.abc.Polyhedron): """ Initialization and basic access for polyhedra. diff --git a/src/sage/geometry/polyhedron/base2.py b/src/sage/geometry/polyhedron/base2.py index f9a48e45b1c..f2626b6efa3 100644 --- a/src/sage/geometry/polyhedron/base2.py +++ b/src/sage/geometry/polyhedron/base2.py @@ -37,6 +37,7 @@ from sage.modules.free_module_element import vector from .base1 import Polyhedron_base1 + class Polyhedron_base2(Polyhedron_base1): """ Methods related to lattice points. diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py index 6347132e0fb..b0a4284a743 100644 --- a/src/sage/geometry/polyhedron/base3.py +++ b/src/sage/geometry/polyhedron/base3.py @@ -38,6 +38,7 @@ from sage.rings.rational_field import QQ from .base2 import Polyhedron_base2 + class Polyhedron_base3(Polyhedron_base2): """ Methods related to the combinatorics of a polyhedron. diff --git a/src/sage/geometry/polyhedron/base4.py b/src/sage/geometry/polyhedron/base4.py index a6fd6dad1ea..ea0423c60fe 100644 --- a/src/sage/geometry/polyhedron/base4.py +++ b/src/sage/geometry/polyhedron/base4.py @@ -36,6 +36,7 @@ from sage.misc.cachefunc import cached_method from .base3 import Polyhedron_base3 + class Polyhedron_base4(Polyhedron_base3): """ Methods relying on :mod:`sage.graphs`. diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index d683955b53a..c94efd9428c 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -42,6 +42,7 @@ from .base4 import Polyhedron_base4 + class Polyhedron_base5(Polyhedron_base4): """ Methods constructing new polyhedra @@ -2452,7 +2453,7 @@ def _test_lawrence(self, tester=None, **options): if tester is None: tester = self._tester(**options) - if self.backend() == 'normaliz' and not self.base_ring() in (ZZ, QQ): + if self.backend() == 'normaliz' and self.base_ring() not in (ZZ, QQ): # Speeds up the doctest for significantly. self = self.change_ring(self._internal_base_ring) diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index f9bc49d7821..bccb57a09f4 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -37,6 +37,7 @@ from sage.geometry.convex_set import AffineHullProjectionData from .base5 import Polyhedron_base5 + class Polyhedron_base6(Polyhedron_base5): r""" Methods related to plotting including affine hull projection. diff --git a/src/sage/geometry/polyhedron/base7.py b/src/sage/geometry/polyhedron/base7.py index 7b86d534017..1b232a00fa2 100644 --- a/src/sage/geometry/polyhedron/base7.py +++ b/src/sage/geometry/polyhedron/base7.py @@ -37,6 +37,7 @@ from sage.rings.rational_field import QQ from .base6 import Polyhedron_base6 + class Polyhedron_base7(Polyhedron_base6): r""" Methods related to triangulation and volume. diff --git a/src/sage/geometry/polyhedron/cdd_file_format.py b/src/sage/geometry/polyhedron/cdd_file_format.py index 9dfedcad58d..edc04b4cb8f 100644 --- a/src/sage/geometry/polyhedron/cdd_file_format.py +++ b/src/sage/geometry/polyhedron/cdd_file_format.py @@ -14,6 +14,8 @@ from .misc import _set_to_None_if_empty, _common_length_of, _to_space_separated_string ######################################################################### + + def cdd_Vrepresentation(cdd_type, vertices, rays, lines, file_output=None): r""" Return a string containing the V-representation in cddlib's ext format. @@ -92,6 +94,8 @@ def cdd_Vrepresentation(cdd_type, vertices, rays, lines, file_output=None): return s ######################################################################### + + def cdd_Hrepresentation(cdd_type, ieqs, eqns, file_output=None): r""" Return a string containing the H-representation in cddlib's ine format. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 73cc5be6428..4db4eb54171 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -3454,7 +3454,7 @@ cdef class CombinatorialPolyhedron(SageObject): num_threads = 1 if parallelization_depth > dim - 1: - # Is a very bad choice anyway, but prevent segmenation faults. + # Is a very bad choice anyway, but prevent segmentation faults. parallelization_depth = dim - 1 if dual == -1: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index e77abc9f33a..41f97c309ef 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -15,8 +15,8 @@ cdef struct iter_s: bint dual # if 1, then iterate over dual Polyhedron face_t face # the current face of the iterator FaceStatus face_status - size_t *atom_rep # a place where atom-representaion of face will be stored - size_t *coatom_rep # a place where coatom-representaion of face will be stored + size_t *atom_rep # a place where atom-representation of face will be stored + size_t *coatom_rep # a place where coatom-representation of face will be stored int current_dimension # dimension of current face, dual dimension if ``dual`` int dimension # dimension of the polyhedron int output_dimension # only faces of this (dual?) dimension are considered diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index ea68babbaf5..2156d4a8bf7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -147,7 +147,7 @@ cdef inline void face_list_delete_faces_by_array(face_list_t faces, bint *delete cdef inline void face_list_delete_faces_by_face(face_list_t faces, face_t face) noexcept: r""" Remove all faces such that the ``i``-th bit in ``face`` is not set - descreasing ``faces.n_faces``. + decreasing ``faces.n_faces``. .. WARNING:: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/meson.build b/src/sage/geometry/polyhedron/combinatorial_polyhedron/meson.build new file mode 100644 index 00000000000..4b4ea8df4e7 --- /dev/null +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/meson.build @@ -0,0 +1,34 @@ +py.install_sources( + 'all.py', + 'base.pxd', + 'combinatorial_face.pxd', + 'conversions.pxd', + 'face_data_structure.pxd', + 'face_iterator.pxd', + 'face_list_data_structure.pxd', + 'list_of_faces.pxd', + 'polyhedron_face_lattice.pxd', + subdir: 'sage/geometry/polyhedron/combinatorial_polyhedron', +) + +extension_data = { + 'base' : files('base.pyx'), + 'combinatorial_face' : files('combinatorial_face.pyx'), + 'conversions' : files('conversions.pyx'), + 'face_iterator' : files('face_iterator.pyx'), + 'face_list_data_structure' : files('face_list_data_structure.pyx'), + 'list_of_faces' : files('list_of_faces.pyx'), + 'polyhedron_face_lattice' : files('polyhedron_face_lattice.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/geometry/polyhedron/combinatorial_polyhedron', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/geometry/polyhedron/double_description.py b/src/sage/geometry/polyhedron/double_description.py index b38e108640f..82bb0685d61 100644 --- a/src/sage/geometry/polyhedron/double_description.py +++ b/src/sage/geometry/polyhedron/double_description.py @@ -704,6 +704,7 @@ def add_inequality(self, a): self.R = R_pos + R_nul + R_new self.A.append(a) + class StandardAlgorithm(Problem): """ Standard implementation of the double description algorithm. diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 9b72f1fc691..1f1969353a2 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -991,6 +991,7 @@ def stacking_locus(self): return parent.element_class(parent, None, [locus_ieqs, locus_eqns]) + def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): r""" Convert a combinatorial face to a face of a polyhedron. diff --git a/src/sage/geometry/polyhedron/generating_function.py b/src/sage/geometry/polyhedron/generating_function.py index 936c761273d..c4bece2af55 100644 --- a/src/sage/geometry/polyhedron/generating_function.py +++ b/src/sage/geometry/polyhedron/generating_function.py @@ -535,7 +535,7 @@ def ieqs_repr_lhs(pi): ieqs, repr_rhss = zip(*[(ieq(a, b), ieq_repr_rhs(a, b)) for a, b in zip(pi[:-1], pi[1:])]) - return Polyhedron(ieqs=ieqs), ieqs_repr_lhs(pi) + ''.join(repr_rhss) + return Polyhedron(ieqs=ieqs), ieqs_repr_lhs(pi) + ''.join(repr_rhss) split = (polyhedron_from_permutation(pi) for pi in Permutations(d)) parts = ZZ(d).factorial() @@ -680,7 +680,7 @@ def __generating_function_of_integral_points__( if sort_factors: def key(t): - D = t.dict().popitem()[0] + D = t.monomial_coefficients().popitem()[0] return (-sum(abs(d) for d in D), D) terms = sorted(terms, key=key, reverse=True) return Factorization([(numerator, 1)] + @@ -748,7 +748,7 @@ def _generating_function_via_Omega_(inequalities, B, skip_indices=()): logger.debug('terms denominator %s', terms) def decode_factor(factor): - D = factor.dict() + D = factor.monomial_coefficients() assert len(D) == 1 exponent, coefficient = next(iter(D.items())) return coefficient, exponent @@ -764,7 +764,7 @@ def decode_factor(factor): lambda factor: factor[1] == 0) other_factors = tuple(factor[0] for factor in other_factors) numerator, factors_denominator = \ - _Omega_(numerator.dict(), tuple(decoded_factors)) + _Omega_(numerator.monomial_coefficients(), tuple(decoded_factors)) terms = other_factors + factors_denominator return _simplify_(numerator, terms) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 97ca4131fe9..5c58abb9564 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -91,6 +91,7 @@ lazy_import('sage.graphs.graph', 'Graph') lazy_import('sage.combinat.root_system.associahedron', 'Associahedron') + def zero_sum_projection(d, base_ring=None): r""" Return a matrix corresponding to the projection on the orthogonal of @@ -319,6 +320,7 @@ def gale_transform_to_polytope(vectors, base_ring=None, backend=None): return P + def gale_transform_to_primal(vectors, base_ring=None, backend=None): r""" Return a point configuration dual to a totally cyclic vector configuration. diff --git a/src/sage/geometry/polyhedron/meson.build b/src/sage/geometry/polyhedron/meson.build new file mode 100644 index 00000000000..3b07bbdd9a5 --- /dev/null +++ b/src/sage/geometry/polyhedron/meson.build @@ -0,0 +1,43 @@ +py.install_sources( + 'all.py', + 'backend_cdd.py', + 'backend_cdd_rdf.py', + 'backend_field.py', + 'backend_normaliz.py', + 'backend_number_field.py', + 'backend_polymake.py', + 'backend_ppl.py', + 'base.py', + 'base0.py', + 'base1.py', + 'base2.py', + 'base3.py', + 'base4.py', + 'base5.py', + 'base6.py', + 'base7.py', + 'base_QQ.py', + 'base_RDF.py', + 'base_ZZ.py', + 'base_mutable.py', + 'base_number_field.py', + 'cdd_file_format.py', + 'constructor.py', + 'double_description.py', + 'double_description_inhomogeneous.py', + 'face.py', + 'generating_function.py', + 'lattice_euclidean_group_element.py', + 'library.py', + 'misc.py', + 'palp_database.py', + 'parent.py', + 'plot.py', + 'ppl_lattice_polygon.py', + 'ppl_lattice_polytope.py', + 'representation.py', + subdir: 'sage/geometry/polyhedron', +) + +subdir('combinatorial_polyhedron') +install_subdir('modules', install_dir: sage_install_dir / 'geometry/polyhedron') diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index ed6657c6826..7a0ad2a2153 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -769,7 +769,7 @@ def _init_area_2d(self, polyhedron): [[3, 0, 1, 2]] """ assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d" - vertices = [v for v in polyhedron.Vrep_generator()] + vertices = list(polyhedron.Vrep_generator()) vertices = cyclic_sort_vertices_2d(vertices) coords = [] @@ -802,7 +802,7 @@ def adjacent_vertices(i): coords[i] + shift, coords[i - 1] + shift]) if polyhedron.n_lines() == 2: - [line1, line2] = [l for l in polyhedron.lines()] + line1, line2 = polyhedron.lines() assert len(coords) == 1, "Can have only a single vertex!" v = coords[0] l1 = line1() @@ -848,7 +848,7 @@ def defining_equation(): # corresponding to a polygon faces = [] face_inequalities = [] for facet_equation in defining_equation(): - vertices = [v for v in facet_equation.incident()] + vertices = list(facet_equation.incident()) face_inequalities.append(facet_equation) vertices = cyclic_sort_vertices_2d(vertices) if len(vertices) >= 3: @@ -894,7 +894,7 @@ def adjacent_vertices(i): coords[1] + shift, coords[0] + shift]) if polyhedron.n_lines() == 2: - [line1, line2] = [l for l in polyhedron.line_generator()] + line1, line2 = polyhedron.line_generator() l1 = line1() l2 = line2() for v in polyhedron.vertex_generator(): diff --git a/src/sage/geometry/ribbon_graph.py b/src/sage/geometry/ribbon_graph.py index 6f10912ff79..5c2950519ad 100644 --- a/src/sage/geometry/ribbon_graph.py +++ b/src/sage/geometry/ribbon_graph.py @@ -32,6 +32,7 @@ #Auxiliary functions that will be used in the classes. + def _find(l, k): r""" Return the two coordinates of the element ``k`` in the list of @@ -1078,6 +1079,7 @@ def normalize(self): PermutationConstructor([tuple(x) for x in aux_rho]) ) + def make_ribbon(g, r): r""" Return a ribbon graph whose thickening has genus ``g`` and ``r`` @@ -1151,6 +1153,7 @@ def make_ribbon(g, r): return RibbonGraph(PermutationConstructor([tuple(x) for x in repr_sigma]), PermutationConstructor([tuple(x) for x in repr_rho])) + def bipartite_ribbon_graph(p, q): r""" Return the bipartite graph modeling the corresponding diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index 589be12b21d..398903fc76a 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -301,14 +301,12 @@ def adjust_options(self): for key in ["xmin", "ymin", "zmin"]: if round or sd[key] is None: sd[key] = - r - if sd[key] > - 0.5: - sd[key] = - 0.5 + sd[key] = min(sd[key], - 0.5) sd[key] = RDF(sd[key]) for key in ["xmax", "ymax", "zmax"]: if round or sd[key] is None: sd[key] = r - if sd[key] < 0.5: - sd[key] = 0.5 + sd[key] = max(sd[key], 0.5) sd[key] = RDF(sd[key]) if self.show_lattice is None: self.show_lattice = (r <= 5) if d <= 2 else r <= 3 diff --git a/src/sage/geometry/triangulation/meson.build b/src/sage/geometry/triangulation/meson.build new file mode 100644 index 00000000000..dec407d83d5 --- /dev/null +++ b/src/sage/geometry/triangulation/meson.build @@ -0,0 +1,26 @@ +py.install_sources( + 'all.py', + 'data.pxd', + 'element.py', + 'functions.pxd', + 'point_configuration.py', + 'triangulations.pxd', + subdir: 'sage/geometry/triangulation', +) + +extension_data_cpp = { + 'base': files('base.pyx', 'data.cc', 'functions.cc', 'triangulations.cc'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/geometry/triangulation', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 2056aa3d32c..7c904f777f3 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -147,8 +147,7 @@ def is_asteroidal_triple_free(G, certificate=False): # module sage.graphs.base.static_sparse_graph cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef bitset_t seen bitset_init(seen, n) diff --git a/src/sage/graphs/base/meson.build b/src/sage/graphs/base/meson.build new file mode 100644 index 00000000000..92e205ceb81 --- /dev/null +++ b/src/sage/graphs/base/meson.build @@ -0,0 +1,51 @@ +py.install_sources( + 'all.py', + 'boost_graph.pxd', + 'c_graph.pxd', + 'dense_graph.pxd', + 'graph_backends.pxd', + 'overview.py', + 'sparse_graph.pxd', + 'static_dense_graph.pxd', + 'static_sparse_backend.pxd', + 'static_sparse_graph.pxd', + subdir: 'sage/graphs/base', +) + +extension_data = { + 'dense_graph' : files('dense_graph.pyx'), + 'graph_backends' : files('graph_backends.pyx'), + 'sparse_graph' : files('sparse_graph.pyx'), + 'static_dense_graph' : files('static_dense_graph.pyx'), + 'static_sparse_backend' : files('static_sparse_backend.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs/base', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +extension_data_cpp = { + 'boost_graph': files('boost_graph.pyx'), + 'c_graph': files('c_graph.pyx'), + 'static_sparse_graph': files('static_sparse_graph.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs/base', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 9e4983ab718..e51fc238ac5 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -425,7 +425,7 @@ cdef class StaticSparseCGraph(CGraph): cdef class StaticSparseBackend(CGraphBackend): - def __init__(self, G, loops=False, multiedges=False): + def __init__(self, G, loops=False, multiedges=False, sort=True): """ A graph :mod:`backend ` for static sparse graphs. @@ -511,10 +511,11 @@ cdef class StaticSparseBackend(CGraphBackend): True """ vertices = list(G) - try: - vertices.sort() - except TypeError: - pass + if sort: + try: + vertices.sort() + except TypeError: + pass cdef StaticSparseCGraph cg = StaticSparseCGraph(G, vertices) self._cg = cg diff --git a/src/sage/graphs/base/static_sparse_graph.pxd b/src/sage/graphs/base/static_sparse_graph.pxd index 03adfe11322..6cb9e53f17d 100644 --- a/src/sage/graphs/base/static_sparse_graph.pxd +++ b/src/sage/graphs/base/static_sparse_graph.pxd @@ -7,27 +7,19 @@ ctypedef unsigned int uint cdef extern from "stdlib.h": ctypedef void const_void "const void" - void qsort(void *base, int nmemb, int size, - int(*compar)(const_void *, const_void *)) nogil - void *bsearch(const_void *key, const_void *base, size_t nmemb, size_t size, int(*compar)(const_void *, const_void *)) nogil -cdef extern from "search.h": - void *lfind(const_void *key, const_void *base, size_t *nmemb, - size_t size, int(*compar)(const_void *, const_void *)) nogil - ctypedef struct short_digraph_s: uint32_t * edges uint32_t ** neighbors PyObject * edge_labels int m int n - bint sorted_neighbors ctypedef short_digraph_s short_digraph[1] -cdef int init_short_digraph(short_digraph g, G, edge_labelled=?, vertex_list=?, sort_neighbors=?) except -1 +cdef int init_short_digraph(short_digraph g, G, edge_labelled=?, vertex_list=?) except -1 cdef void free_short_digraph(short_digraph g) noexcept cdef int init_reverse(short_digraph dst, short_digraph src) except -1 cdef int out_degree(short_digraph g, int u) noexcept diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 3bd7c9c9285..c27f0d9a8cc 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -47,6 +47,9 @@ five fields from `0` to `n-1`) is present so that it remains easy to enumerate the neighbors of vertex `n-1` : the last of them is the element addressed by ``neighbors[n]-1``. + The arrays ``neighbors[i]`` are guaranteed to be sorted so the time + complexity for deciding if ``g`` has edge `(u, v)` is `O(\log{m})` using + binary search. - ``edge_labels`` -- list; this cython list associates a label to each edge of the graph. If a given edge is represented by ``edges[i]``, this its @@ -116,7 +119,7 @@ Cython functions :widths: 30, 70 :delim: | - ``init_short_digraph(short_digraph g, G, edge_labelled, vertex_list, sort_neighbors)`` | Initialize ``short_digraph g`` from a Sage (Di)Graph. + ``init_short_digraph(short_digraph g, G, edge_labelled, vertex_list)`` | Initialize ``short_digraph g`` from a Sage (Di)Graph. ``int n_edges(short_digraph g)`` | Return the number of edges in ``g`` ``int out_degree(short_digraph g, int i)`` | Return the out-degree of vertex `i` in ``g`` ``has_edge(short_digraph g, int u, int v)`` | Test the existence of an edge. @@ -187,10 +190,10 @@ from libc.math cimport sqrt from libcpp.vector cimport vector from cysignals.memory cimport check_allocarray, check_calloc, sig_free from cysignals.signals cimport sig_on, sig_off +from cython.operator cimport postincrement from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * -from sage.graphs.base.c_graph cimport CGraph from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend @@ -205,7 +208,7 @@ cdef extern from "fenv.h": cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, - vertex_list=None, sort_neighbors=True) except -1: + vertex_list=None) except -1: r""" Initialize ``short_digraph g`` from a Sage (Di)Graph. @@ -223,63 +226,78 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, - ``vertex_list`` -- list (default: ``None``); list of all vertices of ``G`` in some order. When given, it is used to map the vertices of the graph to consecutive integers. Otherwise, the result of ``list(G)`` is used - instead. - - - ``sort_neighbors`` -- boolean (default: ``True``); whether to ensure that - the vertices in the list of neighbors of a vertex are sorted by increasing - vertex labels. This choice may have a non-negligeable impact on the time - complexity of some methods. More precisely: - - - When set to ``True``, the time complexity for initializing ``g`` is in - `O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})` for - ``DenseGraph``, and deciding if ``g`` has edge `(u, v)` can be done in - time `O(\log{m})` using binary search. - - - When set to ``False``, the time complexity for initializing ``g`` is - reduced to `O(n + m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph``, but the time complexity for deciding if ``g`` has - edge `(u, v)` increases to `O(m)`. - """ - g.edge_labels = NULL + instead. Beware that if ``vertex_list`` is not ``None``, it is not checked + and this function assumes that it contains a permutation of the vertices + of the graph ``G``. - if G.order() >= INT_MAX: - raise ValueError("this structure can handle at most " + str(INT_MAX) + " vertices") - else: - g.n = G.order() + COMPLEXITY: - cdef int isdigraph + The time complexity for initializing ``g`` is `O(n + m)` for ``SparseGraph`` + and `O(n^2)` for ``DenseGraph``. + TESTS: + + Indirect doctests for sorted output:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: G = graphs.CompleteGraph(5).relabel(list('abcde'), inplace=False) + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_nbrs('a')) + ['b', 'c', 'd', 'e'] + sage: G = graphs.CompleteGraph(5).relabel(list('badec'), inplace=False) + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_nbrs('a')) + ['b', 'd', 'e', 'c'] + + Same with labels:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: G = graphs.CompleteGraph(5).relabel(list('abcde'), inplace=False) + sage: for i, (u, v, _) in enumerate(G.edges()): + ....: G.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_edges('a', True)) + [('a', 'b', 'ab'), ('a', 'c', 'ac'), ('a', 'd', 'ad'), ('a', 'e', 'ae')] + sage: G = graphs.CompleteGraph(5).relabel(list('badec'), inplace=False) + sage: for i, (u, v, _) in enumerate(G.edges()): + ....: G.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(G, sort=False) + sage: list(B.iterator_edges('a', True)) + [('a', 'b', 'ab'), ('a', 'd', 'ad'), ('a', 'e', 'ae'), ('a', 'c', 'ac')] + """ from sage.graphs.graph import Graph from sage.graphs.digraph import DiGraph - if isinstance(G, DiGraph): - isdigraph = 1 - elif isinstance(G, Graph): - isdigraph = 0 - else: - raise ValueError("The source graph must be either a DiGraph or a Graph object !") + if not isinstance(G, (Graph, DiGraph)): + raise ValueError("The source graph must be either a DiGraph or a Graph" + "object !") - cdef int i, j, v_id + if G.order() >= INT_MAX: + raise ValueError(f"short_digraph can handle at most {INT_MAX} vertices") + + g.edge_labels = NULL + g.n = G.order() + g.m = G.size() + + cdef int isdigraph = G.is_directed() + cdef uint32_t i, v_id, j cdef list vertices = vertex_list if vertex_list is not None else list(G) cdef dict v_to_id = {v: i for i, v in enumerate(vertices)} cdef list neighbor_label cdef list edge_labels - - g.m = G.size() - cdef int n_edges = g.m if isdigraph else 2*g.m + # Loops are not stored twice for undirected graphs + cdef int n_edges = g.m if isdigraph else 2*g.m - G.number_of_loops() g.edges = check_allocarray(n_edges, sizeof(uint32_t)) g.neighbors = check_allocarray(1 + g.n, sizeof(uint32_t *)) # Initializing the value of neighbors g.neighbors[0] = g.edges - cdef CGraph cg = G._backend - g.sorted_neighbors = sort_neighbors if not G.has_loops(): # Normal case - for i in range(1, (g.n) + 1): - g.neighbors[i] = g.neighbors[i - 1] + (cg.out_degree(vertices[i - 1]) if isdigraph else G.degree(vertices[i - 1])) + for i, v in enumerate(vertices): + g.neighbors[i+1] = g.neighbors[i] + (G.out_degree(v) if isdigraph else G.degree(v)) else: # In the presence of loops. For a funny reason, if a vertex v has a loop # attached to it and no other incident edge, Sage declares that it has @@ -287,55 +305,40 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False, # the number of edges, but then the degree of a vertex is not the number # of its neighbors anymore. One should never try to think. It never ends # well. - for i in range(1, (g.n) + 1): - g.neighbors[i] = g.neighbors[i - 1] + len(G.edges_incident(vertices[i - 1])) - - if not edge_labelled: - for u, v in G.edge_iterator(labels=False, sort_vertices=False): - i = v_to_id[u] - j = v_to_id[v] - - g.neighbors[i][0] = j - g.neighbors[i] += 1 + for i, v in enumerate(vertices): + g.neighbors[i+1] = g.neighbors[i] + len(G.edges_incident(v)) - if not isdigraph and i != j: - g.neighbors[j][0] = i - g.neighbors[j] += 1 - - # Reinitializing the value of neighbors - for i in range(g.n - 1, 0, -1): - g.neighbors[i] = g.neighbors[i - 1] - - g.neighbors[0] = g.edges - - if sort_neighbors: - # Sorting the neighbors - for i in range(g.n): - qsort(g.neighbors[i], g.neighbors[i + 1] - g.neighbors[i], sizeof(int), compare_uint32_p) - - else: - from operator import itemgetter + if edge_labelled: edge_labels = [None] * n_edges - if sort_neighbors: - for v in G: - neighbor_label = [(v_to_id[uu], l) if uu != v else (v_to_id[u], l) - for u, uu, l in G.edges_incident(v)] - neighbor_label.sort(key=itemgetter(0)) - v_id = v_to_id[v] - - for i, (j, label) in enumerate(neighbor_label): - g.neighbors[v_id][i] = j - edge_labels[(g.neighbors[v_id] + i) - g.edges] = label + + # Note that neighbors[i] will be naturally sorted by increasing id, + # because the arrays will be built by appending vertices in the same + # order as they appear in ``vertices`` + for i, v in enumerate(vertices): + if isdigraph: + edge_iterator = G.incoming_edge_iterator(v, + labels=edge_labelled) else: - for v in G: - v_id = v_to_id[v] - for i, (u, uu, label) in enumerate(G.edges_incident(v)): - if v == uu: - g.neighbors[v_id][i] = v_to_id[u] - else: - g.neighbors[v_id][i] = v_to_id[uu] - edge_labels[(g.neighbors[v_id] + i) - g.edges] = label + edge_iterator = G.edge_iterator(v, labels=edge_labelled, + sort_vertices=False) + for e in edge_iterator: + u = e[0] if v == e[1] else e[1] + j = v_to_id[u] + # Handle the edge u -> v of G (= the edge j -> i of g) + g.neighbors[j][0] = i + # Note: cannot use the dereference Cython operator here, do not + # known why but the following line does not compile + #dereference(g.neighbors[j]) = i + if edge_labelled: + edge_labels[g.neighbors[j] - g.edges] = e[2] + postincrement(g.neighbors[j]) # increment pointer to next item + + # Reinitializing the value of neighbors + for i in range(g.n-1, 0, -1): + g.neighbors[i] = g.neighbors[i-1] + g.neighbors[0] = g.edges + if edge_labelled: g.edge_labels = edge_labels cpython.Py_XINCREF(g.edge_labels) @@ -365,7 +368,6 @@ cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1: """ dst.n = src.n dst.m = src.m - dst.sorted_neighbors = src.sorted_neighbors dst.edge_labels = NULL cdef list edge_labels @@ -382,6 +384,24 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: """ Initialize ``dst`` to a copy of ``src`` with all edges in the opposite direction. + + TESTS: + + Indirect doctests for sorted output:: + + sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend + sage: D = digraphs.Complete(5).relabel(list('abcde'), inplace=False) + sage: for i, (u, v, _) in enumerate(D.edges()): + ....: D.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(D, sort=False) + sage: list(B.iterator_in_edges('a', True)) + [('b', 'a', 'ba'), ('c', 'a', 'ca'), ('d', 'a', 'da'), ('e', 'a', 'ea')] + sage: D = digraphs.Complete(5).relabel(list('badec'), inplace=False) + sage: for i, (u, v, _) in enumerate(D.edges()): + ....: D.set_edge_label(u, v, f'{u}{v}') + sage: B = StaticSparseBackend(D, sort=False) + sage: list(B.iterator_in_edges('a', True)) + [('b', 'a', 'ba'), ('d', 'a', 'da'), ('e', 'a', 'ea'), ('c', 'a', 'ca')] """ cdef int i, j, v # Allocates memory for dst @@ -391,7 +411,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: if not dst.n: return 0 - # 1/4 + # 1/3 # # In a first pass, we count the in-degrees of each vertex and store it in a # vector. With this information, we can initialize dst.neighbors to its @@ -407,11 +427,14 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[i] = dst.neighbors[i - 1] + in_degree[i - 1] sig_free(in_degree) - # 2/4 + # 2/3 # # Second pass : we list the edges again, and add them in dst.edges. Doing # so, we will change the value of dst.neighbors, but that is not so bad as # we can fix it afterwards. + # Note that neighbors[i] will be naturally sorted by increasing id, + # because the arrays will be built by appending vertices in the same + # order as they appear in ``vertices`` for i in range(0, src.n): for j in range(out_degree(src, i)): v = src.neighbors[i][j] @@ -422,7 +445,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[v] += 1 - # 3/4 + # 3/3 # # Third step : set the correct values of dst.neighbors again. It is easy, as # the correct value of dst.neighbors[i] is actually dst.neighbors[i-1] @@ -430,20 +453,12 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[i] = dst.neighbors[i - 1] dst.neighbors[0] = dst.edges - # 4/4 - # - # Final step : if the neighbors of src are assumed to be sorted by - # increasing labels, we do the same for dst. - if src.sorted_neighbors: - for i in range(dst.n): - qsort(dst.neighbors[i], dst.neighbors[i + 1] - dst.neighbors[i], sizeof(int), compare_uint32_p) - return 0 cdef int compare_uint32_p(const_void *a, const_void *b) noexcept: """ - Comparison function needed for ``bsearch`` and ``lfind``. + Comparison function needed for ``bsearch``. """ return ( a)[0] - ( b)[0] @@ -454,17 +469,16 @@ cdef inline uint32_t * has_edge(short_digraph g, int u, int v) noexcept: Return a pointer to ``v`` in the list of neighbors of ``u`` if found and ``NULL`` otherwise. - """ - if g.sorted_neighbors: - # The neighbors of u are sorted by increasing label. We can use binary - # search to decide if g has edge (u, v) - return bsearch(&v, g.neighbors[u], g.neighbors[u + 1] - g.neighbors[u], - sizeof(uint32_t), compare_uint32_p) - # Otherwise, we use the linear time lfind method - cdef size_t nelem = g.neighbors[u + 1] - g.neighbors[u] - return lfind(&v, g.neighbors[u], &nelem, - sizeof(uint32_t), compare_uint32_p) + .. NOTE:: + + Use the fact that the array ``g.neighbors[u]`` is guaranteed to be sorted. + """ + # The neighbors of u are sorted by increasing label. We can use binary + # search to decide if g has edge (u, v) + return bsearch(&v, g.neighbors[u], + g.neighbors[u+1] - g.neighbors[u], + sizeof(uint32_t), compare_uint32_p) cdef inline object edge_label(short_digraph g, uint32_t * edge): @@ -797,7 +811,7 @@ def tarjan_strongly_connected_components(G): cdef MemoryAllocator mem = MemoryAllocator() cdef list int_to_vertex = list(G) cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef int * scc = mem.malloc(g.n * sizeof(int)) sig_on() cdef int nscc = tarjan_strongly_connected_components_C(g, scc) @@ -914,7 +928,7 @@ def strongly_connected_components_digraph(G): cdef MemoryAllocator mem = MemoryAllocator() cdef list int_to_vertex = list(G) cdef short_digraph g, scc_g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef int * scc = mem.malloc(g.n * sizeof(int)) cdef int i, j, nscc cdef list edges = [] @@ -979,7 +993,7 @@ def triangles_count(G): # g is a copy of G. If G is internally a static sparse graph, we use it. cdef list int_to_vertex = list(G) cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=True) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint64_t * count = check_calloc(G.order(), sizeof(uint64_t)) diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index d5bfe02b9c8..e21ec47e9cf 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -1753,7 +1753,7 @@ def load_afile(self, fname): """ # open the file try: - fi = open(fname, "r") + fi = open(fname) except OSError: print("unable to open file <<" + fname + ">>") return None @@ -2633,11 +2633,26 @@ class by some canonization function `c`. If `G` and `H` are graphs, sage: C.right {4, 5, 6} + TESTS: + + Check that :issue:`38832` is fixed:: + + sage: B = BipartiteGraph(matrix([[1, 1], [1, 1]])) + sage: B.canonical_label() + Bipartite graph on 4 vertices + sage: B.canonical_label(certificate=True)[0] + Bipartite graph on 4 vertices + sage: B.canonical_label(edge_labels=True) + Bipartite graph on 4 vertices + sage: B.allow_multiple_edges(True) + sage: B.add_edges(B.edges()) + sage: B.canonical_label() + Bipartite multi-graph on 4 vertices + .. SEEALSO:: :meth:`~sage.graphs.generic_graph.GenericGraph.canonical_label()` """ - if certificate: C, cert = GenericGraph.canonical_label(self, partition=partition, certificate=certificate, @@ -2669,6 +2684,8 @@ class by some canonization function `c`. If `G` and `H` are graphs, cert = {v: c[G_to[relabeling[v]]] for v in self} else: + if partition is None: + partition = self.bipartition() G_vertices = list(chain(*partition)) G_to = {u: i for i, u in enumerate(G_vertices)} H = Graph(len(G_vertices)) diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index b3165b69232..e249046ae70 100755 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -178,7 +178,7 @@ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True): mpq_init(mpq_tmp) try: - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) init_reverse(bfs_dag, g) queue = check_allocarray(n, sizeof(uint32_t)) @@ -689,7 +689,7 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef list V = list(G) - init_short_digraph(sd, G, edge_labelled=False, vertex_list=V, sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=V) cdef int n = sd.n cdef int* reachL = mem.malloc(n * sizeof(int)) cdef int* reachU @@ -939,7 +939,7 @@ def centrality_closeness_random_k(G, int k=1): # Copying the whole graph as a static_sparse_graph for fast shortest # paths computation in unweighted graph. This data structure is well # documented in module sage.graphs.base.static_sparse_graph - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) distance = mem.malloc(n * sizeof(uint32_t)) waiting_list = mem.malloc(n * sizeof(uint32_t)) bitset_init(seen, n) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 2d299e254fe..05138c68f49 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -23,8 +23,10 @@ Here is what the module can do: :meth:`connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. - :meth:`is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge. + :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. + :meth:`is_edge_cut` | Check whether the input edges form an edge cut. :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex. + :meth:`is_vertex_cut` | Check whether the input vertices form a vertex cut. :meth:`edge_connectivity` | Return the edge connectivity of the graph. :meth:`vertex_connectivity` | Return the vertex connectivity of the graph. @@ -70,6 +72,7 @@ Methods # **************************************************************************** from sage.misc.superseded import deprecation +from sage.sets.disjoint_set cimport DisjointSet def is_connected(G): @@ -732,13 +735,160 @@ def blocks_and_cuts_tree(G): return g +def is_edge_cut(G, edges): + """ + Check whether ``edges`` form an edge cut. + + A set of edges is an edge cut of a graph if its removal increases the number + of connected components. In a digraph, we consider the number of (weakly) + connected components. + + This method is not working for (di)graphs with multiple edges. Furthermore, + edge labels are ignored. + + INPUT: + + - ``G`` -- a (di)graph + + - ``edges`` -- a set of edges + + EXAMPLES: + + A cycle graph of order 4:: + + sage: from sage.graphs.connectivity import is_edge_cut + sage: G = graphs.CycleGraph(4) + sage: is_edge_cut(G, [(1, 2)]) + False + sage: is_edge_cut(G, [(1, 2), (2, 3)]) + True + sage: is_edge_cut(G, [(1, 2), (3, 0)]) + True + + A pending edge is a cut-edge:: + + sage: G.add_edge((0, 5, 'silly')) + sage: is_edge_cut(G, [(0, 5, 'silly')]) + True + + Edge labels are ignored, even if specified:: + + sage: G.add_edge((2, 5, 'xyz')) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz')]) + True + sage: is_edge_cut(G, [(0, 5, 'silly'), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 5, 'aa'), (2, 5, 'bb')]) + True + + The graph can have loops:: + + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 0), (0, 5), (2, 5)]) + True + + Multiple edges are not allowed:: + + sage: G.allow_multiple_edges(True) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + Traceback (most recent call last): + ... + ValueError: This method is not known to work on graphs with + multiedges. Perhaps this method can be updated to handle them, but in + the meantime if you want to use it please disallow multiedges using + allow_multiple_edges(). + + An error is raised if an element of ``edges`` is not an edge of `G`:: + + sage: G = graphs.CycleGraph(4) + sage: is_edge_cut(G, [(0, 2)]) + Traceback (most recent call last): + ... + ValueError: edge (0, 2) is not an edge of the graph + + For digraphs, this method considers the number of (weakly) connected + components:: + + sage: G = digraphs.Circuit(4) + sage: is_edge_cut(G, [(0, 1)]) + False + sage: G = digraphs.Circuit(4) + sage: is_edge_cut(G, [(0, 1), (1, 2)]) + True + + For disconnected (di)graphs, the method checks if the number of (weakly) + connected components increases:: + + sage: G = graphs.CycleGraph(4) * 2 + sage: is_edge_cut(G, [(1, 2), (2, 3)]) + True + sage: G = digraphs.Circuit(4) * 2 + sage: is_edge_cut(G, [(0, 1), (1, 2)]) + True + """ + G._scream_if_not_simple(allow_loops=True) + + cdef set C = set() # set of edges of the potential cut + cdef set S = set() # set of incident vertices + for e in edges: + u, v = e[0], e[1] + if not G.has_edge(u, v): + raise ValueError("edge {0} is not an edge of the graph".format(repr(e))) + if u == v: + # We ignore loops + continue + if G.degree(u) == 1 or G.degree(v) == 1: + # e is a pending edge and so a cut-edge + return True + S.add(u) + S.add(v) + C.add((u, v)) + if not G.is_directed(): + C.add((v, u)) + + cdef list queue + cdef set seen + DS = DisjointSet(G) + + for comp in G.connected_components(): + if not S.intersection(comp): + # This component is not involved in the cut + continue + + # We run a DFS in comp from any vertex and avoid edges in C + start = comp[0] + queue = [start] + seen = set(queue) + while queue: + v = queue.pop() + for e in G.edge_iterator(vertices=[v], labels=False, ignore_direction=True, sort_vertices=False): + if e in C: + continue + w = e[1] if e[0] == v else e[0] + if w not in seen: + seen.add(w) + DS.union(v, w) + queue.append(w) + + # We now check if some vertices of comp have not been reached + if len(set(DS.find(v) for v in comp)) > 1: + return True + + return False + + def is_cut_edge(G, u, v=None, label=None): """ - Return ``True`` if the input edge is a cut-edge or a bridge. + Check whether the edge ``(u, v)`` is a cut-edge or a bridge of graph ``G``. A cut edge (or bridge) is an edge that when removed increases - the number of connected components. This function works with - simple graphs as well as graphs with loops and multiedges. In + the number of connected components. This function works with + simple graphs as well as graphs with loops and multiedges. In a digraph, a cut edge is an edge that when removed increases the number of (weakly) connected components. @@ -787,20 +937,7 @@ def is_cut_edge(G, u, v=None, label=None): Traceback (most recent call last): ... ValueError: edge not in graph - - TESTS: - - If ``G`` is not a Sage graph, an error is raised:: - - sage: is_cut_edge('I am not a graph',0) - Traceback (most recent call last): - ... - TypeError: the input must be a Sage graph """ - from sage.graphs.generic_graph import GenericGraph - if not isinstance(G, GenericGraph): - raise TypeError("the input must be a Sage graph") - if label is None: if v is None: try: @@ -835,58 +972,64 @@ def is_cut_edge(G, u, v=None, label=None): return sol -def is_cut_vertex(G, u, weak=False): +def is_vertex_cut(G, cut, weak=False): r""" - Check whether the input vertex is a cut-vertex. + Check whether the input vertices form a vertex cut. - A vertex is a cut-vertex if its removal from the (di)graph increases the - number of (strongly) connected components. Isolated vertices or leafs are - not cut-vertices. This function works with simple graphs as well as graphs - with loops and multiple edges. + A set of vertices is a vertex cut if its removal from the (di)graph + increases the number of (strongly) connected components. This function works + with simple graphs as well as graphs with loops and multiple edges. INPUT: - ``G`` -- a Sage (Di)Graph - - ``u`` -- a vertex + - ``cut`` -- a set of vertices - ``weak`` -- boolean (default: ``False``); whether the connectivity of directed graphs is to be taken in the weak sense, that is ignoring edges orientations - OUTPUT: - - Return ``True`` if ``u`` is a cut-vertex, and ``False`` otherwise. - EXAMPLES: - Giving a LollipopGraph(4,2), that is a complete graph with 4 vertices with a - pending edge:: + Giving a cycle graph of order 4:: - sage: from sage.graphs.connectivity import is_cut_vertex - sage: G = graphs.LollipopGraph(4, 2) - sage: is_cut_vertex(G, 0) + sage: from sage.graphs.connectivity import is_vertex_cut + sage: G = graphs.CycleGraph(4) + sage: is_vertex_cut(G, [0, 1]) False - sage: is_cut_vertex(G, 3) + sage: is_vertex_cut(G, [0, 2]) True - sage: G.is_cut_vertex(3) + + Giving a disconnected graph:: + + sage: from sage.graphs.connectivity import is_vertex_cut + sage: G = graphs.CycleGraph(4) * 2 + sage: G.connected_components() + [[0, 1, 2, 3], [4, 5, 6, 7]] + sage: is_vertex_cut(G, [0, 2]) + True + sage: is_vertex_cut(G, [4, 6]) + True + sage: is_vertex_cut(G, [0, 6]) + False + sage: is_vertex_cut(G, [0, 4, 6]) True Comparing the weak and strong connectivity of a digraph:: - sage: from sage.graphs.connectivity import is_strongly_connected sage: D = digraphs.Circuit(6) - sage: is_strongly_connected(D) + sage: D.is_strongly_connected() True - sage: is_cut_vertex(D, 2) + sage: is_vertex_cut(D, [2]) True - sage: is_cut_vertex(D, 2, weak=True) + sage: is_vertex_cut(D, [2], weak=True) False Giving a vertex that is not in the graph:: sage: G = graphs.CompleteGraph(4) - sage: is_cut_vertex(G, 7) + sage: is_vertex_cut(G, [7]) Traceback (most recent call last): ... ValueError: vertex (7) is not a vertex of the graph @@ -895,7 +1038,7 @@ def is_cut_vertex(G, u, weak=False): If ``G`` is not a Sage graph, an error is raised:: - sage: is_cut_vertex('I am not a graph', 0) + sage: is_vertex_cut('I am not a graph', [0]) Traceback (most recent call last): ... TypeError: the input must be a Sage graph @@ -904,68 +1047,144 @@ def is_cut_vertex(G, u, weak=False): if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") - if u not in G: - raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(u))) - - # Initialization - cdef set CC - cdef list neighbors_func - if not G.is_directed() or weak: - # Weak connectivity + cdef set cutset = set(cut) + for u in cutset: + if u not in G: + raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(u))) - if G.degree(u) < 2: - # An isolated or a leaf vertex is not a cut vertex - return False - - neighbors_func = [G.neighbor_iterator] - start = next(G.neighbor_iterator(u)) - CC = set(G) + if len(cutset) >= G.order() - 1: + # A vertex cut must be of size at most n - 2 + return False + # We deal with graphs with multiple (strongly) connected components + cdef list CC + if G.is_directed() and not weak: + CC = G.strongly_connected_components() else: - # Strong connectivity for digraphs - - if not G.out_degree(u) or not G.in_degree(u): - # A vertex without in or out neighbors is not a cut vertex - return False + CC = G.connected_components(sort=False) + if len(CC) > 1: + for comp in CC: + subcut = cutset.intersection(comp) + if subcut and is_vertex_cut(G.subgraph(comp), subcut, weak=weak): + return True + return False - # We consider only the strongly connected component containing u - CC = set(strongly_connected_component_containing_vertex(G, u)) + cdef list boundary = G.vertex_boundary(cutset) + if not boundary: + # We need at least 1 vertex in the boundary of the cut + return False - # We perform two DFS starting from an out neighbor of u and avoiding - # u. The first DFS follows the edges directions, and the second is - # in the reverse order. If both allow to reach all neighbors of u, - # then u is not a cut vertex - neighbors_func = [G.neighbor_out_iterator, G.neighbor_in_iterator] - start = next(G.neighbor_out_iterator(u)) + cdef list cases = [(G.neighbor_iterator, boundary)] + if not weak and G.is_directed(): + # Strong connectivity for digraphs. + # We perform two DFS starting from an out neighbor of cut and avoiding + # cut. The first DFS follows the edges directions, and the second is + # in the reverse order. If both allow to reach all neighbors of cut, + # then it is not a vertex cut. + # We set data for the reverse order + in_boundary = set() + for u in cutset: + in_boundary.update(G.neighbor_in_iterator(u)) + in_boundary.difference_update(cutset) + if not in_boundary: + return False + cases.append((G.neighbor_in_iterator, list(in_boundary))) - CC.discard(u) - CC.discard(start) cdef list queue cdef set seen cdef set targets + start = boundary[0] - for neighbors in neighbors_func: + for neighbors, this_boundary in cases: - # We perform a DFS starting from a neighbor of u and avoiding u + # We perform a DFS starting from start and avoiding cut queue = [start] - seen = set(queue) - targets = CC.intersection(G.neighbor_iterator(u)) + seen = set(cutset) + seen.add(start) + targets = set(this_boundary) targets.discard(start) - while queue and targets: + while queue: v = queue.pop() for w in neighbors(v): - if w not in seen and w in CC: + if w not in seen: seen.add(w) queue.append(w) targets.discard(w) - # If some neighbors cannot be reached, u is a cut vertex. + # If some neighbors cannot be reached, we have a vertex cut if targets: return True return False +def is_cut_vertex(G, u, weak=False): + r""" + Check whether the input vertex is a cut-vertex. + + A vertex is a cut-vertex if its removal from the (di)graph increases the + number of (strongly) connected components. Isolated vertices or leaves are + not cut-vertices. This function works with simple graphs as well as graphs + with loops and multiple edges. + + INPUT: + + - ``G`` -- a Sage (Di)Graph + + - ``u`` -- a vertex + + - ``weak`` -- boolean (default: ``False``); whether the connectivity of + directed graphs is to be taken in the weak sense, that is ignoring edges + orientations + + OUTPUT: + + Return ``True`` if ``u`` is a cut-vertex, and ``False`` otherwise. + + EXAMPLES: + + Giving a LollipopGraph(4,2), that is a complete graph with 4 vertices with a + pending edge:: + + sage: from sage.graphs.connectivity import is_cut_vertex + sage: G = graphs.LollipopGraph(4, 2) + sage: is_cut_vertex(G, 0) + False + sage: is_cut_vertex(G, 3) + True + sage: G.is_cut_vertex(3) + True + + Comparing the weak and strong connectivity of a digraph:: + + sage: D = digraphs.Circuit(6) + sage: D.is_strongly_connected() + True + sage: is_cut_vertex(D, 2) + True + sage: is_cut_vertex(D, 2, weak=True) + False + + Giving a vertex that is not in the graph:: + + sage: G = graphs.CompleteGraph(4) + sage: is_cut_vertex(G, 7) + Traceback (most recent call last): + ... + ValueError: vertex (7) is not a vertex of the graph + + TESTS: + + If ``G`` is not a Sage graph, an error is raised:: + + sage: is_cut_vertex('I am not a graph', 0) + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + """ + return is_vertex_cut(G, [u], weak=weak) + + def edge_connectivity(G, value_only=True, implementation=None, @@ -1472,6 +1691,16 @@ def vertex_connectivity(G, value_only=True, sets=False, k=None, solver=None, ver sage: G.add_edge(0, 1) sage: G.vertex_connectivity(value_only=False, verbose=1) # needs sage.numerical.mip (3, []) + + Check that :issue:`38723` is fixed:: + + sage: G = graphs.SierpinskiGasketGraph(3) + sage: G.vertex_connectivity(k=1) # needs sage.numerical.mip + True + sage: G.vertex_connectivity(k=2) # needs sage.numerical.mip + True + sage: G.vertex_connectivity(k=3) # needs sage.numerical.mip + False """ from sage.graphs.generic_graph import GenericGraph if not isinstance(G, GenericGraph): @@ -1486,8 +1715,8 @@ def vertex_connectivity(G, value_only=True, sets=False, k=None, solver=None, ver # We follow the convention of is_connected, is_biconnected and # is_strongly_connected return k == 1 - if (g.is_directed() and k > min(min(g.in_degree()), min(g.out_degree()))) \ - or (not g.is_directed() and (k > min(g.degree()))): + if ((g.is_directed() and k > min(min(g.in_degree()), min(g.out_degree()))) + or (not g.is_directed() and (k > min(g.degree())))): return False value_only = True sets = False @@ -1519,7 +1748,7 @@ def vertex_connectivity(G, value_only=True, sets=False, k=None, solver=None, ver return 1 if k is None else (k == 1) if not G.is_triconnected(): - return 2 if k is None else (k == 2) + return 2 if k is None else (k <= 2) elif k == 3: return True @@ -2634,22 +2863,51 @@ def spqr_tree_to_graph(T): sage: H.is_isomorphic(G) True - TESTS:: + TESTS: + + Check that the method is working for the empty SPQR tree:: sage: H = spqr_tree_to_graph(Graph()) sage: H.is_isomorphic(Graph()) True + + Check that :issue:`38527` is fixed:: + + sage: from sage.graphs.connectivity import spqr_tree, spqr_tree_to_graph + sage: G = Graph('LlCG{O@?GBoMw?') + sage: T1 = spqr_tree(G, algorithm="Hopcroft_Tarjan") + sage: T2 = spqr_tree(G, algorithm="cleave") + sage: T1.is_isomorphic(T2) + True + sage: G1 = spqr_tree_to_graph(T1) + sage: G2 = spqr_tree_to_graph(T2) + sage: G.is_isomorphic(G1) + True + sage: G.is_isomorphic(G2) + True """ from sage.graphs.graph import Graph from collections import Counter count_G = Counter() count_P = Counter() + vertex_to_int = dict() for t, g in T: + for u in g: + if u not in vertex_to_int: + vertex_to_int[u] = len(vertex_to_int) if t in ['P', 'Q']: - count_P.update(g.edge_iterator()) + for u, v, label in g.edge_iterator(): + if vertex_to_int[u] < vertex_to_int[v]: + count_P[u, v, label] += 1 + else: + count_P[v, u, label] += 1 else: - count_G.update(g.edge_iterator()) + for u, v, label in g.edge_iterator(): + if vertex_to_int[u] < vertex_to_int[v]: + count_G[u, v, label] += 1 + else: + count_G[v, u, label] += 1 G = Graph(multiedges=True) for e, num in count_G.items(): diff --git a/src/sage/graphs/convexity_properties.pyx b/src/sage/graphs/convexity_properties.pyx index a51c3db0102..f292cae3eed 100644 --- a/src/sage/graphs/convexity_properties.pyx +++ b/src/sage/graphs/convexity_properties.pyx @@ -506,11 +506,9 @@ def geodetic_closure(G, S): each vertex `u \in S`, the algorithm first performs a breadth first search from `u` to get distances, and then identifies the vertices of `G` lying on a shortest path from `u` to any `v\in S` using a reversal traversal from - vertices in `S`. This algorithm has time complexity in - `O(|S|(n + m) + (n + m\log{m}))` for ``SparseGraph``, - `O(|S|(n + m) + n^2\log{m})` for ``DenseGraph`` and space complexity in - `O(n + m)` (the extra `\log` factor is due to ``init_short_digraph`` being - called with ``sort_neighbors=True``). + vertices in `S`. This algorithm has time complexity in `O(|S|(n + m))` for + ``SparseGraph``, `O(|S|(n + m) + n^2)` for ``DenseGraph`` and + space complexity in `O(n + m)`. INPUT: @@ -757,7 +755,7 @@ def is_geodetic(G): # Copy the graph as a short digraph cdef int n = G.order() cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # Allocate some data structures cdef MemoryAllocator mem = MemoryAllocator() diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 2454834bd51..54fa7ed4eb3 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -873,7 +873,7 @@ def dig6_string(self): r""" Return the ``dig6`` representation of the digraph as an ASCII string. - This is only valid for single (no multiple edges) digraphs on at most + This is only valid for simple (no multiple edges) digraphs on at most `2^{18} - 1 = 262143` vertices. .. NOTE:: @@ -893,6 +893,9 @@ def dig6_string(self): sage: D = DiGraph({0: [1, 2], 1: [2], 2: [3], 3: [0]}) sage: D.dig6_string() 'CW`_' + sage: L = DiGraph({0: [1, 2], 1: [2], 2: [3], 3: [3]}) + sage: L.dig6_string() + 'CW`C' TESTS:: @@ -1315,7 +1318,7 @@ def in_degree(self, vertices=None, labels=False): if vertices in self: return self._backend.in_degree(vertices) elif labels: - return {v: d for v, d in self.in_degree_iterator(vertices, labels=labels)} + return dict(self.in_degree_iterator(vertices, labels=labels)) return list(self.in_degree_iterator(vertices, labels=labels)) def in_degree_iterator(self, vertices=None, labels=False): @@ -1385,7 +1388,7 @@ def out_degree(self, vertices=None, labels=False): if vertices in self: return self._backend.out_degree(vertices) elif labels: - return {v: d for v, d in self.out_degree_iterator(vertices, labels=labels)} + return dict(self.out_degree_iterator(vertices, labels=labels)) return list(self.out_degree_iterator(vertices, labels=labels)) def out_degree_iterator(self, vertices=None, labels=False): diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 02590ac8f7c..b374392163e 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -28,6 +28,7 @@ :meth:`~DiGraphGenerators.ImaseItoh` | Return the digraph of Imase and Itoh of order `n` and degree `d`. :meth:`~DiGraphGenerators.Kautz` | Return the Kautz digraph of degree `d` and diameter `D`. :meth:`~DiGraphGenerators.nauty_directg` | Return an iterator yielding digraphs using nauty's ``directg`` program. + :meth:`~DiGraphGenerators.nauty_posetg` | Return an iterator yielding Hasse diagrams of posets using nauty's ``genposetg`` program. :meth:`~DiGraphGenerators.Paley` | Return a Paley digraph on `q` vertices. :meth:`~DiGraphGenerators.Path` | Return a directed path on `n` vertices. :meth:`~DiGraphGenerators.RandomDirectedAcyclicGraph` | Return a random (weighted) directed acyclic graph of order `n`. @@ -48,6 +49,7 @@ - Emily A. Kirkman (2006) - Michael C. Yurko (2009) - David Coudert (2012) +- Janmenjaya Panda (2024) Functions and methods --------------------- @@ -57,6 +59,7 @@ # and Emily A. Kirkman # Copyright (C) 2009 Michael C. Yurko # Copyright (C) 2012 David Coudert +# Copyright (C) 2024 Janmenjaya Panda # # 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 @@ -558,14 +561,14 @@ def tournaments_nauty(self, n, ``None`` (default), then the min/max out-degree is not constrained - ``debug`` -- boolean (default: ``False``); if ``True`` the first line - of genbg's output to standard error is captured and the first call to - the generator's ``next()`` function will return this line as a string. - A line leading with ">A" indicates a successful initiation of the - program with some information on the arguments, while a line beginning - with ">E" indicates an error with the input. + of gentourng's output to standard error is captured and the first call + to the generator's ``next()`` function will return this line as a + string. A line leading with ">A" indicates a successful initiation of + the program with some information on the arguments, while a line + beginning with ">E" indicates an error with the input. - ``options`` -- string; anything else that should be forwarded as input - to Nauty's genbg. See its documentation for more information : + to Nauty's gentourng. See its documentation for more information : ``_. EXAMPLES:: @@ -758,6 +761,63 @@ def nauty_directg(self, graphs, options='', debug=False): if line and line[0] == '&': yield DiGraph(line[1:], format='dig6') + def nauty_posetg(self, options='', debug=False): + r""" + Return a generator which creates all posets using ``nauty``. + + Here a poset is seen through its Hasse diagram, which is + an acyclic and transitively reduced digraph. + + INPUT: + + - ``options`` -- string (default: ``""``); a string passed to + ``genposetg`` as if it was run at a system command line. + At a minimum, you *must* pass the number of vertices you desire + and a choice between ``o`` and ``t`` for the output order. + + - ``debug`` -- boolean (default: ``False``); if ``True`` the first line + of ``genposetg``'s output to standard error is captured and the first + call to the generator's ``next()`` function will return this line as a + string. A line leading with ">A" indicates a successful initiation of + the program with some information on the arguments, while a line + beginning with ">E" indicates an error with the input. + + The possible options, obtained as output of ``genposetg --help``:: + + n: the number of vertices, between 0 and 16 + o: digraph6 output in arbitrary order + t: digraph6 output in topological order + + EXAMPLES:: + + sage: gen = digraphs.nauty_posetg("5 o") + sage: len(list(gen)) + 63 + + This coincides with :oeis:`A000112`. + """ + import shlex + from sage.features.nauty import NautyExecutable + geng_path = NautyExecutable("genposetg").absolute_filename() + sp = subprocess.Popen(shlex.quote(geng_path) + f" {options}", shell=True, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True, + encoding='latin-1') + msg = sp.stderr.readline() + if debug: + yield msg + elif msg.startswith('>E'): + raise ValueError('wrong format of parameter option') + gen = sp.stdout + while True: + try: + s = next(gen) + except StopIteration: + # Exhausted list of graphs from nauty genposetg + return + G = DiGraph(s[1:-1], format='dig6') + yield G + def Complete(self, n, loops=False): r""" Return the complete digraph on `n` vertices. @@ -833,6 +893,9 @@ def Circulant(self, n, integers): r""" Return a circulant digraph on `n` vertices from a set of integers. + A circulant digraph of order `n` has an arc from vertex `i` to + vertex `i+j \pmod{n}`, for each `j` in ``integers``. + INPUT: - ``n`` -- integer; number of vertices @@ -841,18 +904,30 @@ def Circulant(self, n, integers): that there is an edge from `i` to `j` if and only if `(j-i) \pmod{n}` is an integer - EXAMPLES:: + EXAMPLES: - sage: digraphs.Circulant(13,[3,5,7]) - Circulant graph ([3, 5, 7]): Digraph on 13 vertices + Construct and show the circulant graph [3, 5, 7], a digraph on 13 + vertices:: - TESTS:: + sage: g = digraphs.Circulant(13, [3, 5, 7]) + sage: g.show() # long time # needs sage.plot + + The Koh-Tindell digraph [LM2024]_ is the circulant digraph of order 7 + with parameters `[1, 5]`. This `2`-diregular digraph is + vertex-transitive but not arc-transitive. The associated bipartite + digraph of the Koh-Tindell digraph is a Pfaffian orientation of the + Heawood graph. Construct and show the Koh-Tindell digraph:: + + sage: kohTindellDigraph = digraphs.Circulant(7, [1, 5]) + sage: kohTindellDigraph.show() # long time # needs sage.plot + + TESTS: - sage: digraphs.Circulant(13,[3,5,7,"hey"]) + sage: digraphs.Circulant(13, [3, 5, 7, "hey"]) Traceback (most recent call last): ... ValueError: the list must contain only integers - sage: digraphs.Circulant(3,[3,5,7,3.4]) + sage: digraphs.Circulant(3, [3, 5, 7, 3.4]) Traceback (most recent call last): ... ValueError: the list must contain only integers @@ -1232,7 +1307,7 @@ def Kautz(self, k, D, vertices='strings'): raise ValueError("degree must be greater than or equal to one") # We start building the set of vertices - V = [i for i in my_alphabet] + V = list(my_alphabet) for i in range(D - 1): VV = [] for w in V: @@ -1486,7 +1561,9 @@ def RandomDirectedGNM(self, n, m, loops=False): sage: D.num_verts() 10 sage: D.loops() - [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), (8, 8, None), (9, 9, None)] + [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), + (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), + (8, 8, None), (9, 9, None)] TESTS:: @@ -1580,7 +1657,7 @@ def RandomDirectedGNM(self, n, m, loops=False): if is_dense: for u in range(n): for v in range(n): - if ((u != v) or loops) and (not (v in adj[u])): + if ((u != v) or loops) and (v not in adj[u]): D.add_edge(u, v) return D diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 63ad747ebd9..23d2e1c79d7 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -304,8 +304,7 @@ cdef inline all_pairs_shortest_path_BFS(gg, # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors cdef short_digraph sd - init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex) c_all_pairs_shortest_path_BFS(sd, predecessors, distances, eccentricity) @@ -1057,8 +1056,7 @@ def eccentricity(G, algorithm='standard', vertex_list=None): ecc = c_eccentricity(G, vertex_list=int_to_vertex) else: - init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) if algorithm == "DHV": ecc = c_eccentricity_DHV(sd) @@ -1836,8 +1834,7 @@ def diameter(G, algorithm=None, source=None): # module sage.graphs.base.static_sparse_graph cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef short_digraph rev_sd # to store copy of sd with edges reversed # and we map the source to an int in [0,n-1] @@ -1939,8 +1936,7 @@ def radius_DHV(G): cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t source, ecc_source cdef uint32_t antipode, ecc_antipode @@ -2057,8 +2053,7 @@ def wiener_index(G): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # allocated some data structures cdef bitset_t seen @@ -2280,14 +2275,6 @@ def szeged_index(G, algorithm=None): By default (``None``), the ``'low'`` algorithm is used for graphs and the ``'high'`` algorithm for digraphs. - .. NOTE:: - As the graph is converted to a short_digraph, the complexity for the - case ``algorithm == "high"`` has an extra `O(m+n)` for ``SparseGraph`` - and `O(n^2)` for ``DenseGraph``. If ``algorithm == "low"``, the extra - complexity is `O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})` - for ``DenseGraph`` (because ``init_short_digraph`` is called with - ``sort_neighbors=True``). - EXAMPLES: True for any connected graph [KRG1996]_:: @@ -2378,13 +2365,12 @@ def szeged_index(G, algorithm=None): return 0 cdef short_digraph sd + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) cdef uint64_t s if algorithm == "low": - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=True) s = c_szeged_index_low_memory(sd) else: - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G), sort_neighbors=False) s = c_szeged_index_high_memory(sd) free_short_digraph(sd) diff --git a/src/sage/graphs/edge_connectivity.pyx b/src/sage/graphs/edge_connectivity.pyx index aef8e43d98e..bfaedc3fdfa 100644 --- a/src/sage/graphs/edge_connectivity.pyx +++ b/src/sage/graphs/edge_connectivity.pyx @@ -452,7 +452,7 @@ cdef class GabowEdgeConnectivity: Allocate data structure for the new tree/forest. This method also initializes data structures for this tree index. Data - structures for a given tree index are allocatated only once. + structures for a given tree index are allocated only once. INPUT: diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index e231428445f..aef8ea49935 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -407,7 +407,7 @@ def CorrelationGraph(seqs, alpha, include_anticorrelation): Return a correlation graph with a node per sequence in ``seqs``. Edges are added between nodes where the corresponding sequences have a - correlation coeffecient greater than alpha. + correlation coefficient greater than alpha. If ``include_anticorrelation`` is ``True``, then edges are also added between nodes with correlation coefficient less than ``-alpha``. @@ -440,7 +440,7 @@ def CorrelationGraph(seqs, alpha, include_anticorrelation): from numpy import corrcoef from sage.matrix.constructor import Matrix - # compute pairwise correlation coeffecients + # compute pairwise correlation coefficients corrs = corrcoef(seqs) # compare against alpha to get adjacency matrix diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index f5dbad17169..8cd60e5862e 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1107,7 +1107,7 @@ def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): - if not len(HO.intersection(L)) in [0, 2]: + if len(HO.intersection(L)) not in [0, 2]: raise RuntimeError("incorrect hyperoval") L = [[y for y in z if y not in HO] diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 795bfa58dc2..e7e40f40e24 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1423,14 +1423,14 @@ def GeneralisedOctagonGraph(const int s, const int t): EXAMPLES:: sage: # needs sage.libs.gap - sage: G = graphs.GeneralisedOctagonGraph(1, 4) - sage: G.is_distance_regular(True) + sage: G = graphs.GeneralisedOctagonGraph(1, 4) # optional - database_graphs + sage: G.is_distance_regular(True) # optional - database_graphs ([5, 4, 4, 4, None], [None, 1, 1, 1, 5]) sage: G = graphs.GeneralisedOctagonGraph(2, 4) # optional - gap_package_atlasrep internet sage: G.is_distance_regular(True) # optional - gap_package_atlasrep internet ([10, 8, 8, 8, None], [None, 1, 1, 1, 5]) - sage: G = graphs.GeneralisedOctagonGraph(5, 1) - sage: G.is_distance_regular(True) + sage: G = graphs.GeneralisedOctagonGraph(5, 1) # optional - database_graphs + sage: G.is_distance_regular(True) # optional - database_graphs ([10, 5, 5, 5, None], [None, 1, 1, 1, 2]) .. NOTE:: @@ -1869,8 +1869,8 @@ def is_classical_parameters_graph(list array): graphs with classical parameters, then this function returns a tuple consisting of the parameters `(d, b, \alpha, \beta)` and a fourth parameter which is the enum ``CalssicalParametersGraph`` indicating the family with - the given itersection array. - If the array doesn't belong to any classical parameter graph, then this + the given intersection array. + If the array does not belong to any classical parameter graph, then this function returns ``False``. If the array belongs to a sporadic graph rather than a family of graphs, then the function returns ``False``. This is to reduce the overlap with @@ -2741,7 +2741,7 @@ def distance_regular_graph(list arr, existence=False, check=True): Hamming Graph with parameters 7,3: Graph on 2187 vertices sage: graphs.distance_regular_graph([66, 45, 28, 1, 6, 30]) Graph on 1024 vertices - sage: graphs.distance_regular_graph([6,5,5,5,1,1,1,6]) + sage: graphs.distance_regular_graph([6,5,5,5,1,1,1,6]) # optional - database_graphs Generalised octagon of order (1, 5): Graph on 312 vertices sage: graphs.distance_regular_graph([64, 60, 1, 1, 15, 64], check=True) Graph on 325 vertices diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index e6e5312b8a8..74e7dd01f28 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -3710,7 +3710,7 @@ def nauty_gentreeg(options='', debug=False): ... ValueError: wrong format of parameter options sage: list(graphs.nauty_gentreeg("3 -x", debug=True)) - ['>E Usage: ...gentreeg [-D#] [-Z#:#] [-ulps] [-q] n [res/mod] ... + ['>E Usage: ...gentreeg [-D#] [-Z#:#] [-ulps] [-q] n... [res/mod] ... sage: list(graphs.nauty_gentreeg("3", debug=True)) ['>A ...gentreeg ...\n', Graph on 3 vertices] """ @@ -4197,9 +4197,9 @@ def MuzychukS6Graph(n, d, Phi='fixed', Sigma='fixed', verbose=False): from sage.rings.integer_ring import ZZ from time import time - assert d > 1, 'd must be at least 2' + assert d > 1, 'd must be at least 2' assert is_even(n * (d-1)), 'n must be even or d must be odd' - assert is_prime_power(n), 'n must be a prime power' + assert is_prime_power(n), 'n must be a prime power' t = time() # build L, L_i and the design @@ -4741,4 +4741,4 @@ def TruncatedBiwheelGraph(n): G.add_path(list(range(2*n - 2))) G.add_edges(edges) - return G \ No newline at end of file + return G diff --git a/src/sage/graphs/generators/meson.build b/src/sage/graphs/generators/meson.build new file mode 100644 index 00000000000..44542f2631e --- /dev/null +++ b/src/sage/graphs/generators/meson.build @@ -0,0 +1,28 @@ +py.install_sources( + 'all.py', + 'basic.py', + 'chessboard.py', + 'classical_geometries.py', + 'degree_sequence.py', + 'families.py', + 'intersection.py', + 'platonic_solids.py', + 'random.py', + 'smallgraphs.py', + 'world_map.py', + subdir: 'sage/graphs/generators', +) + +extension_data = {'distance_regular' : files('distance_regular.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs/generators', + install: true, + include_directories: [], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 79753f418fd..d490eda7830 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -7,6 +7,7 @@ # Copyright (C) 2006 Robert L. Miller # and Emily A. Kirkman # Copyright (C) 2009 Michael C. Yurko +# Copyright (C) 2024 Janmenjaya Panda # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -1898,6 +1899,168 @@ def CoxeterGraph(): return g +def CubeplexGraph(embedding='LM'): + r""" + Return the Cubeplex graph. + + The Cubeplex graph is the cubic hamiltonian graph of order 12 that + corresponds to the graph labeled as `\Gamma_1` in Fischer and Little + [FiLi2001]_. It has LCF notation `[-6, -5, -3, -6, 3, 5, -6, -3, 5, -6, -5, + 3]`. + + The Fischer-Little Theorem [FiLi2001]_ may be stated as follows [LM2024]_: + + A near-bipartite graph is non-Pfaffian if and only if it contains one of + the graphs `K_{3, 3}`, `\Gamma_1` and `\Gamma_2` as an `S`-minor. + + Norine and Thomas [NT2007]_ use the term ``Cubeplex`` to describe one of + the 12-vertex cubic graphs, `\Gamma_1` and `\Gamma_2`, as defined by + Fischer and Little [FiLi2001]_. However, the figure in their paper that + supposedly provides embeddings for the graphs labeled Cubeplex and Twinplex + actually shows both embeddings corresponding to Fischer and Little's + `\Gamma_1`, which is the Cubeplex graph. Followingly, for + ``embedding='NT'``, we present only the embedding that is shown by the + labeling ``Cubeplex`` in the paper of Norine and Thomas [NT2007]_. + + PLOTTING: + + Upon construction, the position dictionary is filled to override + the spring-layout algorithm. For different values of the parameter + ``embedding``, the Cubeplex graph is displayed as it is mentioned in the + respective paper/ book. + + INPUT: + + - ``embedding`` -- string (default: ``'LM'``) + + - ``'LM'`` displays the embedding as shown for `\Gamma_1` by Lucchesi and + Murty [LM2024]_ + + - ``'FL'`` displays the embedding as shown for `\Gamma_1` by Fischer and + Little [FiLi2001]_ + + - ``'NT'`` displays the embedding as shown for the ``Cubeplex`` by Norine + and Thomas [NT2007]_ + + OUTPUT: + + - ``G`` -- the Cubeplex graph; note that a :class:`ValueError` is returned + if ``embedding`` is none of ``'FT'``, ``'NT'`` or ``'LM'`` + + EXAMPLES: + + Construct and show the Cubeplex graph:: + + sage: g = graphs.CubeplexGraph() + sage: g.name() + 'Cubeplex Graph' + sage: g.order() + 12 + sage: g.size() + 18 + sage: g.girth() + 4 + sage: g.diameter() + 3 + sage: g.is_hamiltonian() + True + sage: g.crossing_number() + 1 + sage: g.show() # long time # needs sage.plot + + TESTS: + + Note that all three embeddings refer to the same graph, the Cubeplex graph, + aka `\Gamma_1`:: + + sage: fl = graphs.CubeplexGraph(embedding='FL') + sage: nt = graphs.CubeplexGraph(embedding='NT') + sage: lm = graphs.CubeplexGraph(embedding='LM') + sage: fl.is_isomorphic(nt) and fl.is_isomorphic(lm) + True + + The input parameter must be one of 'FL', 'NT' or 'LM':: + + sage: g = graphs.CubeplexGraph(embedding='embedding') + Traceback (most recent call last): + ... + ValueError: parameter 'embedding' must be 'FL', 'NT' or 'LM' + + .. SEEALSO:: + + :meth:`~sage.graphs.graph_generators.GraphGenerators.TwinplexGraph` + + AUTHORS: + + - Janmenjaya Panda (2024-08-03) + """ + if embedding == 'FL': + from math import pi + + G = Graph(12, name='Cubeplex Graph') + G.add_cycle(list(range(12))) + + G.add_edges([ + (0, 3), (1, 6), (2, 8), + (4, 9), (5, 11), (7, 10) + ]) + + G._circle_embedding(list(range(12)), angle=2*pi/3) + + elif embedding == 'NT': + pos_dict = { + 0: (1, 2), + 1: (3, 2), + 2: (0, 1), + 3: (1, 1), + 4: (2, 1), + 5: (3, 1), + 6: (4, 1), + 7: (0, -1), + 8: (1, 0), + 9: (2, 0), + 10: (3, 0), + 11: (4, -1), + } + + G = Graph(12, pos=pos_dict, name='Cubeplex Graph') + G.add_edges([ + (0, 2), (0, 4), (0, 6), + (1, 3), (1, 5), (1, 6), + (2, 7), (2, 8), (3, 7), + (3, 8), (4, 9), (4, 10), + (5, 9), (5, 10), (6, 11), + (7, 11), (8, 9), (10, 11) + ]) + + elif embedding == 'LM': + from math import pi + + pos_dict = { + 8: (0, 1), + 9: (1, 0), + 10: (-3*cos(pi/16), -3*sin(pi/16)), + 11: (3*cos(pi/16), -3*sin(pi/16)) + } + + for v in range(8): + t = pi * (v+2)/4 + pos_dict[v] = (-2*cos(t), 2*sin(t)) + + G = Graph(12, pos=pos_dict, name='Cubeplex Graph') + + G.add_cycle(list(range(8))) + G.add_edges([ + (0, 8), (1, 11), (2, 9), (3, 11), (4, 8), + (5, 10), (6, 9), (7, 10), (8, 9), (10, 11) + ]) + + else: + raise ValueError("parameter 'embedding' must be 'FL', 'NT' or 'LM'") + + return G + + def DejterGraph(): r""" Return the Dejter graph. @@ -3813,6 +3976,88 @@ def MoserSpindle(): return Graph(edge_dict, pos=pos_dict, name="Moser spindle") +def MurtyGraph(): + r""" + Return the Murty graph. + + Consider the complete bipartite graph `K_{3, 3}`. There is a set of three + black vertices and a set of three white vertices. Now, consider splicing + the complete graph `K_4` with one of the black vertices, this generates the + graph `K_4 \odot K_{3, 3}`. The Murty graph is obtained from + `K_4 \odot K_{3, 3}` with the addition of an edge joining the remaining two + black vertices. The Murty graph is free of conformal bicycles; in + other words, the Murty graph is an example of a graph that is Birkhoff-von + Neumann as well as PM-compact. + + This is the smallest brick that is Birkhoff-von Neumann, aka a solid + brick, but is not odd-intercyclic. It is in this context that + Prof. U.S.R. Murty first stumbled upon this graph, and it also appears in + the work of Carvalho, Lucchesi, and Murty [CLM2006]_. + + PLOTTING: + + Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the Murty graph is + displayed as mentioned in the paper [CKWL2019]_, with the first two + (noncubic) vertices on the top row, the second three vertices (that form a + stable set) in the middle row, and the remaining three vertices (that form + a triangle) at the bottom. + + OUTPUT: + + - ``G`` -- the Murty graph + + EXAMPLES: + + Construct and show the Murty graph:: + + sage: g = graphs.MurtyGraph() + sage: g.name() + 'Murty Graph' + sage: g.order() + 8 + sage: g.size() + 13 + sage: g.girth() + 3 + sage: g.diameter() + 2 + sage: g.is_hamiltonian() + True + sage: g.show() # long time # needs sage.plot + + REFERENCES: + + - [CKWL2019]_ + - [CLM2006]_ + - [LM2024]_ + + AUTHORS: + + - Janmenjaya Panda (2024-08-03) + """ + pos_dict = { + 0: (-0.5, sqrt(3)/2), + 1: (0.5, sqrt(3)/2), + 2: (-1, 0), + 3: (0, 0), + 4: (1, 0), + 5: (-0.5, -1 - sqrt(3)/2), + 6: (0, -1), + 7: (0.5, -1 - sqrt(3)/2) + } + + G = Graph(8, pos=pos_dict, name="Murty Graph") + + G.add_edge(0, 1) + for v in range(2, 5): + G.add_edges([(0, v), (1, v), (v, v+3)]) + + G.add_edges([(5, 6), (5, 7), (6, 7)]) + + return G + + def NauruGraph(embedding=2): """ Return the Nauru Graph. @@ -4372,6 +4617,102 @@ def TietzeGraph(): return g +def TricornGraph(): + r""" + Return the Tricorn graph. + + The Tricorn graph is obtained by splicing a complete graph `K_4` with the + the triangular circular ladder graph `\overline{C_6}`. (Note that this + generates a unqiue graph as both of the graphs `K_4` and `\overline{C_6}` + are vertex-transitive). It is a nonsolid brick. This matching covered graph + is one of the ten extremal cubic bricks. (A matching covered graph `G` is + *extremal* if `\Phi(G) = dim(\mathcal{Lin}(G))`, where `\Phi(G)` denotes + the number of perfect matchings of `G`, and `dim(\mathcal{Lin}(G))` stands + for the dimension of the linear space of `G`). + + The Tricorn graph has no removable doubletons and has precisely three + removable edges. The wheel graph `W_5` and the complete graph `K_4` are + matching minors of the Tricorn graph. + + As per a theorem of Lovász [Lov1983]_, each non bipartite matching covered + graph has a conformal subgraph which is either a bi-subdivision of `K_4` or + of `\overline{C_6}` or both. In their paper, Kothari and Murty [KM2015]_ + characterized those planar bricks that are free of `\overline{C_6}` (that + is, the planar bricks that do not contain a bi-subdivision of + `\overline{C_6}` as a conformal subgraph). Besides two infinite families of + matching covered graphs (odd wheel graphs and staircase graphs of order + *4k*), the Tricorn graph is the only exception brick that is simple, planar + and free of `\overline{C_6}`. + + PLOTTING: + + Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the Tricorn graph is + displayed as mentioned in the book [LM2024]_, with the central vertex being + the `0`-th one. Rest of the nine vertices are shown in groups of three, + one on the top, rest two on the bottom left and on the bottom right + corners respectively. + + OUTPUT: + + - ``G`` -- the Tricorn graph + + EXAMPLES: + + Construct and show the Tricorn graph; note that the edges `(2, 3)`, + `(5, 6)` and `(8, 9)` are the only removable edges of the Tricorn + graph:: + + sage: g = graphs.TricornGraph() + sage: g.name() + 'Tricorn Graph' + sage: g.order() + 10 + sage: g.size() + 15 + sage: g.girth() + 3 + sage: g.diameter() + 3 + sage: g.is_hamiltonian() + True + sage: g.show() # long time # needs sage.plot + + REFERENCES: + + - [KM2015]_ + - [LM2024]_ + - [Lov1983]_ + + AUTHORS: + + - Janmenjaya Panda (2024-08-02) + """ + pos_dict = { + 0: (0, 0), + 1: (0, 1), + 2: (1/2, 1 + sqrt(3)/2), + 3: (-1/2, 1 + sqrt(3)/2), + 4: (-sqrt(3)/2, -1/2), + 5: (-sqrt(3)/2 - 1, -1/2), + 6: (-sqrt(3)/2 - 1/2, -1/2 - sqrt(3)/2), + 7: (sqrt(3)/2, -1/2), + 8: (sqrt(3)/2 + 1/2, -1/2 - sqrt(3)/2), + 9: (sqrt(3)/2 + 1, -1/2) + } + + G = Graph(10, pos=pos_dict, name="Tricorn Graph") + + for v in range(1, 8, 3): + G.add_edges([ + (0, v), (v, v+1), + (v, v+2), (v+1, v+2), + (v+2, int((-v**2 + 7*v + 4)/2)) + ]) + + return G + + def TruncatedIcosidodecahedralGraph(): r""" Return the truncated icosidodecahedron. @@ -4571,6 +4912,204 @@ def TutteGraph(): return g +def TwinplexGraph(embedding='LM'): + r""" + Return the Twinplex graph. + + The Twinplex graph is a cubic hamiltonian graph of order 12 with the graph + crossing number 2 and has a girth 5 (that is the maximal girth among all + cubic graphs on 12 vertices [CHNP2020]_). It corresponds to the graph + labeled as `\Gamma_2` by Fischer and Little [FiLi2001]_. The Twinplex graph + has LCF notation `[-5, -4, 4, -4, 4, 5, -4, 5, -4, 4, -5, 4]`. + + The Fischer-Little Theorem [FiLi2001]_ may be stated as follows [LM2024]_: + + A near-bipartite graph is non-Pfaffian if and only if it contains one of + the graphs `K_{3, 3}`, `\Gamma_1` and `\Gamma_2` as an `S`-minor. + + Norine and Thomas [NT2007]_ use the term ``Twinplex`` to describe one of + the 12-vertex cubic graphs, `\Gamma_1` and `\Gamma_2`, as defined by + Fischer and Little [FiLi2001]_. However, the figure in their paper that + supposedly provides embeddings for the graphs labeled Cubeplex and Twinplex + actually shows both embeddings corresponding to Fischer and Little's + `\Gamma_1`, which is the Cubeplex graph. Followingly, for + ``embedding='NT'``, we present a correct version of the Twinplex graph + with a slight modification of the embedding that is labeled as ``Twinplex`` + in the paper of Norine and Thomas [NT2007]_. + + PLOTTING: + + Upon construction, the position dictionary is filled to override + the spring-layout algorithm. For different values of the parameter + ``embedding``, the Twinplex graph is displayed as it is mentioned in the + respective paper/ book. Note that for ``embedding='NT'``, a correct + embedding of the Twinplex graph is displayed with a minor modification to + the (incorrect) embedding shown in the paper [NT2007]_. + + INPUT: + + - ``embedding`` -- string (default: ``'LM'``) + + - ``'LM'`` displays the embedding as shown for `\Gamma_2` by Lucchesi and + Murty [LM2024]_ + + - ``'FL'`` displays the embedding as shown for `\Gamma_2` by Fischer and + Little [FiLi2001]_ + + - ``'NT'`` displays the correct embedding with a minor modification to + the one shown as the (incorrect) ``Twinplex`` by Norine and Thomas + [NT2007]_ + + - ``'RST'`` displays the embedding as shown for the ``Twinplex`` by + Robertson, Seymour and Thomas [RST2019]_ + + OUTPUT: + + - ``G`` -- the Twinplex graph; note that a :class:`ValueError` is returned + if ``embedding`` is none of ``'FT'``, ``'NT'``, ``'RST'`` or ``'LM'`` + + EXAMPLES: + + Construct and show the Twinplex graph:: + + sage: g = graphs.TwinplexGraph() + sage: g.name() + 'Twinplex Graph' + sage: g.order() + 12 + sage: g.size() + 18 + sage: g.girth() + 5 + sage: g.diameter() + 3 + sage: g.is_hamiltonian() + True + sage: g.crossing_number() + 2 + sage: g.show() # long time # needs sage.plot + + TESTS: + + Note that all four embeddings refer to the same graph, the Twinplex graph, + aka `\Gamma_2`:: + + sage: fl = graphs.TwinplexGraph(embedding='FL') + sage: nt = graphs.TwinplexGraph(embedding='NT') + sage: rst = graphs.TwinplexGraph(embedding='RST') + sage: lm = graphs.TwinplexGraph(embedding='LM') + sage: all(fl.is_isomorphic(g) for g in (nt, rst, lm)) + True + + The input parameter must be one of 'FL', 'NT', 'RST' or 'LM':: + + sage: g = graphs.TwinplexGraph(embedding='embedding') + Traceback (most recent call last): + ... + ValueError: parameter 'embedding' must be 'FL', 'NT', 'LM' or 'RST' + + .. SEEALSO:: + + :meth:`~sage.graphs.graph_generators.GraphGenerators.CubeplexGraph` + + AUTHORS: + + - Janmenjaya Panda (2024-08-03) + """ + if embedding == 'FL': + from math import pi + + G = Graph(12, name='Twinplex Graph') + G.add_cycle(list(range(12))) + + G.add_edges([ + (0, 8), (1, 5), (2, 9), + (3, 7), (4, 11), (6, 10) + ]) + + G._circle_embedding(list(range(12)), angle=5*pi/12) + + elif embedding == 'NT': + pos_dict = { + 0: (1, 2), + 1: (3, 2), + 2: (0, 1), + 3: (1, 1), + 4: (2, 1), + 5: (3, 1), + 6: (4, 1), + 7: (0, -1), + 8: (1, 0), + 9: (2, 0), + 10: (3, 0), + 11: (4, -1), + } + + G = Graph(12, pos=pos_dict, name='Twinplex Graph') + G.add_edges([ + (0, 2), (0, 4), (0, 6), + (1, 3), (1, 5), (1, 6), + (2, 7), (2, 9), (3, 7), + (3, 8), (4, 8), (4, 10), + (5, 9), (5, 10), (6, 11), + (7, 11), (8, 9), (10, 11) + ]) + + elif embedding == 'RST': + pos_dict = { + 0: (-1, 3), + 1: (1, 3), + 2: (3, 1), + 3: (3, -1), + 4: (1, -3), + 5: (-1, -3), + 6: (-3, -1), + 7: (-3, 1), + 8: (-1, 1), + 9: (1, 1), + 10: (1, -1), + 11: (-1, -1) + } + + G = Graph(12, pos=pos_dict, name='Twinplex Graph') + + G.add_cycle(list(range(8))) + G.add_edges([ + (0, 4), (1, 8), (2, 10), + (3, 9), (5, 10), (6, 8), + (7, 11), (8, 9), (9, 11), + (10, 11) + ]) + + elif embedding == 'LM': + from math import pi + + pos_dict = { + 8: (0, 1), + 9: (1, 0), + 10: (-3*cos(pi/16), -3*sin(pi/16)), + 11: (3*cos(pi/16), -3*sin(pi/16)) + } + + for v in range(8): + t = pi * (v+2)/4 + pos_dict[v] = (-2*cos(t), 2*sin(t)) + + G = Graph(12, pos=pos_dict, name='Twinplex Graph') + + G.add_cycle(list(range(8))) + G.add_edges([ + (0, 8), (1, 11), (2, 9), (3, 10), (4, 8), + (5, 11), (6, 9), (7, 10), (8, 9), (10, 11) + ]) + + else: + raise ValueError("parameter 'embedding' must be 'FL', 'NT'," + " 'LM' or 'RST'") + + return G + + def WagnerGraph(): """ Return the Wagner Graph. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 46523fdfda5..44a5e8f6a63 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -117,7 +117,6 @@ :widths: 30, 70 :delim: | - :meth:`~GenericGraph.eulerian_orientation` | Return a DiGraph which is an Eulerian orientation of the current graph. :meth:`~GenericGraph.eulerian_circuit` | Return a list of edges forming an Eulerian circuit if one exists. :meth:`~GenericGraph.minimum_cycle_basis` | Return a minimum weight cycle basis of the graph. :meth:`~GenericGraph.cycle_basis` | Return a list of cycles which form a basis of the cycle space of ``self``. @@ -243,8 +242,10 @@ :meth:`~GenericGraph.connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. - :meth:`~GenericGraph.is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge. - :meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex. + :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. + :meth:`~GenericGraph.`is_edge_cut` | Check whether the input edges form an edge cut. + :meth:`~GenericGraph.is_cut_vertex` | Check whether the input vertex is a cut-vertex. + :meth:`~GenericGraph.is_vertex_cut` | Check whether the input vertices form a vertex cut. :meth:`~GenericGraph.edge_cut` | Return a minimum edge cut between vertices `s` and `t` :meth:`~GenericGraph.vertex_cut` | Return a minimum vertex cut between non-adjacent vertices `s` and `t` :meth:`~GenericGraph.flow` | Return a maximum flow in the graph from ``x`` to ``y`` @@ -295,6 +296,7 @@ :meth:`~GenericGraph.show3d` | Plot the graph using :class:`~sage.plot.plot3d.tachyon.Tachyon`, and shows the resulting plot. :meth:`~GenericGraph.graphviz_string` | Return a representation in the ``dot`` language. :meth:`~GenericGraph.graphviz_to_file_named` | Write a representation in the ``dot`` language in a file. + :meth:`~GenericGraph.tikz` | Return a :class:`~sage.misc.latex_standalone.TikzPicture` object representing the (di)graph. **Algorithmically hard stuff:** @@ -831,8 +833,11 @@ def __str__(self): def _bit_vector(self): """ - Return a string representing the edges of the (simple) graph for - ``graph6`` and ``dig6`` strings. + Return a string representing the edges of the graph for ``graph6`` + and ``dig6`` strings. + + The graph must be simple; loops are allowed only if the graph is + directed, and multiple edges are never allowed. EXAMPLES:: @@ -862,7 +867,7 @@ def _bit_vector(self): sage: P.canonical_label(algorithm='sage')._bit_vector() '001100001111000000011010100110100011' """ - self._scream_if_not_simple() + self._scream_if_not_simple(allow_loops=self._directed) n = self.order() if self._directed: total_length = n * n @@ -934,6 +939,190 @@ def _latex_(self): return self.latex_options().latex() + def tikz(self, format=None, edge_labels=None, + color_by_label=False, prog='dot', rankdir='down', + standalone_config=None, usepackage=None, + usetikzlibrary=None, macros=None, + use_sage_preamble=None, **kwds): + r""" + Return a TikzPicture of the graph. + + If graphviz and dot2tex are available, it uses these packages for + placements of vertices and edges. + + INPUT: + + - ``format`` -- string (default: ``None``), ``'dot2tex'`` or + ``'tkz_graph'``. If ``None``, it is set to ``'dot2tex'`` if + dot2tex is present, otherwise it is set to ``'tkz_graph'``. + - ``edge_labels`` -- bool (default: ``None``), if ``None`` + it is set to ``True`` if and only if format is ``'dot2tex'`` + - ``color_by_label`` -- boolean or dictionary or function (default: + ``False``); whether to color each edge with a different color + according to its label; the colors are chosen along a rainbow, unless + they are specified by a function or dictionary mapping labels to + colors; + + When using format ``'dot2tex'``, the following inputs are considered: + + - ``prog`` -- string (default: ``'dot'``) the program used for the + layout corresponding to one of the software of the graphviz + suite: 'dot', 'neato', 'twopi', 'circo' or 'fdp'. + - ``rankdir`` -- string (default: ``'down'``), direction of graph layout + when prog is ``'dot'``, possible values are ``'down'``, + ``'up'``, ``'right'`` and ``'left'``. + - ``subgraph_clusters`` -- (default: ``[]``) a list of lists of + vertices, if supported by the layout engine, nodes belonging to + the same cluster subgraph are drawn together, with the entire + drawing of the cluster contained within a bounding rectangle. + + Additionnal keywords arguments are forwarded to + :meth:`sage.graphs.graph_latex.GraphLatex.set_option`. + + The following inputs define the preamble of the latex standalone + document class file containing the tikzpicture: + + - ``standalone_config`` -- list of strings (default: ``["border=4mm"]``); + latex document class standalone configuration options + - ``usepackage`` -- list of strings (default: ``[]``); latex + packages + - ``usetikzlibrary`` -- list of strings (default: ``[]``); tikz + libraries to use + - ``macros`` -- list of strings (default: ``[]``); list of + newcommands needed for the picture + - ``use_sage_preamble`` -- bool (default: ``None``), if ``None`` + it is set to ``True`` if and only if format is ``'tkz_graph'`` + + OUTPUT: + + An instance of :mod:`sage.misc.latex_standalone.TikzPicture`. + + .. NOTE:: + + Prerequisite: dot2tex optional Sage package and graphviz must be + installed when using format ``'dot2tex'``. + + EXAMPLES:: + + sage: g = graphs.PetersenGraph() + sage: tikz = g.tikz() # optional - dot2tex graphviz # long time + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time + + :: + + sage: tikz = g.tikz(format='tkz_graph') + sage: _ = tikz.pdf(view=False) # optional - latex + + Using another value for ``prog``:: + + sage: tikz = g.tikz(prog='neato') # optional - dot2tex graphviz # long time + sage: _ = tikz.pdf() # optional - dot2tex graphviz latex # long time + + Using ``color_by_label`` with default rainbow colors:: + + sage: G = DiGraph({0: {1: 333, 2: 444}, 1: {0: 444}, 2: {0: 555}}) + sage: t = G.tikz(color_by_label=True) # optional - dot2tex graphviz # long time + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time + + Using ``color_by_label`` with colors given as a dictionary:: + + sage: G = DiGraph({0: {1: 333, 2: 444}, 1: {0: 444}, 2: {0: 555}}) + sage: cbl = {333:'orange', 444: 'yellow', 555: 'purple'} + sage: t = G.tikz(color_by_label=cbl) # optional - dot2tex graphviz # long time + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time + + Using ``color_by_label`` with colors given as a function:: + + sage: G = DiGraph({0: {1: -333, 2: -444}, 1: {0: 444}, 2: {0: 555}}) + sage: cbl = lambda label:'green' if label >= 0 else 'orange' + sage: t = G.tikz(color_by_label=cbl) # optional - dot2tex graphviz # long time + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time + + Using another value for ``rankdir``:: + + sage: tikz = g.tikz(rankdir='right') # optional - dot2tex graphviz # long time + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time + + Using subgraphs clusters (broken when using labels, see + :issue:`22070`):: + + sage: S = FiniteSetMaps(5) + sage: I = S((0,1,2,3,4)) + sage: a = S((0,1,3,0,0)) + sage: b = S((0,2,4,1,0)) + sage: roots = [I] + sage: succ = lambda v: [v*a,v*b,a*v,b*v] + sage: R = RecursivelyEnumeratedSet(roots, succ) + sage: G = R.to_digraph() + sage: G + Looped multi-digraph on 27 vertices + sage: C = G.strongly_connected_components() + sage: tikz = G.tikz(subgraph_clusters=C)# optional - dot2tex graphviz # long time + sage: tikz.add_usepackage('amstext') # optional - dot2tex graphviz # long time + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time + + An example coming from ``graphviz_string`` documentation in SageMath:: + + sage: # needs sage.symbolic + sage: f(x) = -1 / x + sage: g(x) = 1 / (x + 1) + sage: G = DiGraph() + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) + sage: tikz = G.tikz(format='dot2tex') # optional - dot2tex graphviz # long time + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time + sage: def edge_options(data): + ....: u, v, label = data + ....: options = {"color": {f: "red", g: "blue"}[label]} + ....: if (u,v) == (1/2, -2): options["label"] = "coucou"; options["label_style"] = "string" + ....: if (u,v) == (1/2,2/3): options["dot"] = "x=1,y=2" + ....: if (u,v) == (1, -1): options["label_style"] = "latex" + ....: if (u,v) == (1, 1/2): options["dir"] = "back" + ....: return options + sage: tikz = G.tikz(format='dot2tex', # optional - dot2tex graphviz # long time + ....: edge_options=edge_options) + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time + """ + # use format dot2tex by default + if format is None: + from sage.features import PythonModule + if PythonModule("dot2tex").is_present(): + format = 'dot2tex' + else: + format = 'tkz_graph' + + # by default draw edge_labels for dot2tex but not for tkz_graph + # (because tkz_graph draws None everywhere which is ugly, whereas + # dot2tex ignores the labels when they are ``None``) + if edge_labels is None: + if format == 'tkz_graph': + edge_labels = False + elif format == 'dot2tex': + edge_labels = True + + self.latex_options().set_options(format=format, + edge_labels=edge_labels, color_by_label=color_by_label, + prog=prog, rankdir=rankdir, **kwds) + + # by default use sage preamble only for format tkz_graph + # because content generated by tkz_graph depends on it + if use_sage_preamble is None: + if format == 'tkz_graph': + use_sage_preamble = True + elif format == 'dot2tex': + use_sage_preamble = False + + if standalone_config is None: + standalone_config = ["border=4mm"] + + from sage.misc.latex_standalone import TikzPicture + return TikzPicture(self._latex_(), + standalone_config=standalone_config, + usepackage=usepackage, + usetikzlibrary=usetikzlibrary, + macros=macros, + use_sage_preamble=use_sage_preamble) + def _matrix_(self, R=None, vertices=None): """ Return the adjacency matrix of the graph over the specified ring. @@ -4542,110 +4731,6 @@ def size(self): num_edges = size - # Orientations - - def eulerian_orientation(self): - r""" - Return a DiGraph which is an Eulerian orientation of the current graph. - - An Eulerian graph being a graph such that any vertex has an even degree, - an Eulerian orientation of a graph is an orientation of its edges such - that each vertex `v` verifies `d^+(v)=d^-(v)=d(v)/2`, where `d^+` and - `d^-` respectively represent the out-degree and the in-degree of a - vertex. - - If the graph is not Eulerian, the orientation verifies for any vertex - `v` that `| d^+(v)-d^-(v) | \leq 1`. - - ALGORITHM: - - This algorithm is a random walk through the edges of the graph, which - orients the edges according to the walk. When a vertex is reached which - has no non-oriented edge (this vertex must have odd degree), the walk - resumes at another vertex of odd degree, if any. - - This algorithm has complexity `O(n+m)` for ``SparseGraph`` and `O(n^2)` - for ``DenseGraph``, where `m` is the number of edges in the graph and - `n` is the number of vertices in the graph. - - EXAMPLES: - - The CubeGraph with parameter 4, which is regular of even degree, has an - Eulerian orientation such that `d^+ = d^-`:: - - sage: g = graphs.CubeGraph(4) - sage: g.degree() - [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] - sage: o = g.eulerian_orientation() - sage: o.in_degree() - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - sage: o.out_degree() - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - - Secondly, the Petersen Graph, which is 3 regular has an orientation such - that the difference between `d^+` and `d^-` is at most 1:: - - sage: g = graphs.PetersenGraph() - sage: o = g.eulerian_orientation() - sage: o.in_degree() - [2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - sage: o.out_degree() - [1, 1, 1, 1, 1, 2, 2, 2, 2, 2] - - TESTS:: - - sage: E0 = Graph(); E4 = Graph(4) # See trac #21741 - sage: E0.eulerian_orientation() - Digraph on 0 vertices - sage: E4.eulerian_orientation() - Digraph on 4 vertices - """ - from sage.graphs.digraph import DiGraph - - d = DiGraph() - d.add_vertices(self.vertex_iterator()) - - if not self.size(): - return d - - g = copy(self) - - # list of vertices of odd degree - odd = [x for x in g.vertex_iterator() if g.degree(x) % 2] - - # Picks the first vertex, which is preferably an odd one - if odd: - v = odd.pop() - else: - v = next(g.edge_iterator(labels=None))[0] - odd.append(v) - # Stops when there is no edge left - while True: - - # If there is an edge adjacent to the current one - if g.degree(v): - e = next(g.edge_iterator(v)) - g.delete_edge(e) - if e[0] != v: - e = (e[1], e[0], e[2]) - d.add_edge(e) - v = e[1] - - # The current vertex is isolated - else: - odd.remove(v) - - # jumps to another odd vertex if possible - if odd: - v = odd.pop() - # Else jumps to an even vertex which is not isolated - elif g.size(): - v = next(g.edge_iterator())[0] - odd.append(v) - # If there is none, we are done ! - else: - return d - def eulerian_circuit(self, return_vertices=False, labels=True, path=False): r""" Return a list of edges forming an Eulerian circuit if one exists. @@ -7353,6 +7438,22 @@ def edge_disjoint_spanning_trees(self, k, algorithm=None, root=None, solver=None p.add_constraint(pos[root, c] + BFS[u] <= pos[u, c]) # We now solve this program and extract the solution + + from sage.numerical.backends.glpk_backend import GLPKBackend + if isinstance(p.get_backend(), GLPKBackend): + # The MIP approach with GLPK is prone to compiler and + # optimization-level weirdness on some hardware: + # + # * https://github.com/sagemath/sage/issues/34575 + # * https://github.com/sagemath/sage/issues/38831 + # + # Disabling the presolver manages to perturb reality just + # enough in the one scenario that we doctest explicitly to + # "fix" the problem. It's also limited enough in scope + # that it probably hasn't badly broken some other use + # case. + p.solver_parameter("presolve_intopt", False) + try: p.solve(log=verbose) except MIPSolverException: @@ -7508,7 +7609,7 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, sage: g.edge_cut(1, 2, value_only=True, algorithm='LP') # needs sage.numerical.mip 3 - :issue:`12797`:: + Check that :issue:`12797` and :issue:`38713` are fixed:: sage: G = Graph([(0, 3, 1), (0, 4, 1), (1, 2, 1), (2, 3, 1), (2, 4, 1)]) sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True) @@ -7517,7 +7618,7 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True) [1, [(2, 1, 1)]] sage: G.edge_cut(0, 1, value_only=False, use_edge_labels=True, algorithm='LP') # needs sage.numerical.mip - (1, [(2, 1)]) + (1, [(2, 1, 1)]) """ self._scream_if_not_simple(allow_loops=True) if vertices: @@ -7570,9 +7671,10 @@ def weight(x): # frozensets otherwise if g.is_directed(): def good_edge(e): - return e + return (e[0], e[1]) else: - good_edge = frozenset + def good_edge(e): + return frozenset((e[0], e[1])) # Some vertices belong to part 1, others to part 0 p.add_constraint(v[s], min=0, max=0) @@ -7585,7 +7687,7 @@ def good_edge(e): # Adjacent vertices can belong to different parts only if the # edge that connects them is part of the cut - for x, y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=False): p.add_constraint(v[x] + b[good_edge((x, y))] - v[y], min=0) else: @@ -7593,7 +7695,7 @@ def good_edge(e): p.set_objective(p.sum(weight(w) * b[good_edge((x, y))] for x, y, w in g.edge_iterator())) # Adjacent vertices can belong to different parts only if the # edge that connects them is part of the cut - for x, y in g.edge_iterator(labels=None): + for x, y in g.edge_iterator(labels=False): p.add_constraint(v[x] + b[good_edge((x, y))] - v[y], min=0) p.add_constraint(v[y] + b[good_edge((x, y))] - v[x], min=0) @@ -7608,7 +7710,7 @@ def good_edge(e): return obj answer = [obj] - answer.append([e for e in g.edge_iterator(labels=False) if b[good_edge(e)]]) + answer.append([e for e in g.edge_iterator(labels=True) if b[good_edge(e)]]) if vertices: v = p.get_values(v, convert=bool, tolerance=integrality_tolerance) @@ -18922,7 +19024,7 @@ def breadth_first_search(self, start, ignore_direction=False, # Non-existing start vertex is detected later if distance > 0. if not distance: for v in queue: - if not v[0] in self: + if v[0] not in self: raise LookupError("start vertex ({0}) is not a vertex of the graph".format(v[0])) for v, d in queue: @@ -22672,7 +22774,7 @@ def graphviz_string(self, **options): for f in edge_option_functions: edge_options.update(f((u, v, label))) - if not edge_options['edge_string'] in ['--', '->']: + if edge_options['edge_string'] not in ['--', '->']: raise ValueError("edge_string(='{}') in edge_options dict for " "the edge ({}, {}) should be '--' or '->'" .format(edge_options['edge_string'], u, v)) @@ -22757,7 +22859,7 @@ def graphviz_to_file_named(self, filename, **options): node_2 -- node_3 [label="foo"]; } """ - with open(filename, 'wt') as file: + with open(filename, "w") as file: file.write(self.graphviz_string(**options)) # Spectrum @@ -24997,7 +25099,9 @@ def is_self_complementary(self): from sage.graphs.connectivity import blocks_and_cut_vertices from sage.graphs.connectivity import blocks_and_cuts_tree from sage.graphs.connectivity import is_cut_edge + from sage.graphs.connectivity import is_edge_cut from sage.graphs.connectivity import is_cut_vertex + from sage.graphs.connectivity import is_vertex_cut from sage.graphs.connectivity import edge_connectivity from sage.graphs.connectivity import vertex_connectivity from sage.graphs.distances_all_pairs import szeged_index diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index c9969845710..a350ceeba86 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -1374,8 +1374,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, # static copy of the graph for more efficient operations cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=True) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef short_digraph rev_sd cdef bint reverse = False if directed: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 4b0b43e5adb..4eced79fe9b 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -362,7 +362,7 @@ sage: G = graphs.RandomGNP(15,.3) sage: G.show() # needs sage.plot -And you can view it in three dimensions via jmol with ``show3d()``. :: +And you can view it in three dimensions with ``show3d()``. :: sage: G.show3d() # needs sage.plot @@ -3020,527 +3020,6 @@ def weight(x): g.delete_edges(e for e in g.edge_iterator(labels=False) if not b[frozenset(e)]) return g - # Orientations - - @doc_index("Connectivity, orientations, trees") - def strong_orientation(self): - r""" - Return a strongly connected orientation of the current graph. - - An orientation of an undirected graph is a digraph obtained by giving an - unique direction to each of its edges. An orientation is said to be - strong if there is a directed path between each pair of vertices. See - also the :wikipedia:`Strongly_connected_component`. - - If the graph is 2-edge-connected, a strongly connected orientation - can be found in linear time. If the given graph is not 2-connected, - the orientation returned will ensure that each 2-connected component - has a strongly connected orientation. - - OUTPUT: a digraph representing an orientation of the current graph - - .. NOTE:: - - - This method assumes the graph is connected. - - This time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` - for ``DenseGraph`` . - - .. SEEALSO:: - - - :meth:`~sage.graphs.graph.Graph.orientations` - - :meth:`~sage.graphs.orientations.strong_orientations_iterator` - - :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.nauty_directg` - - :meth:`~sage.graphs.orientations.random_orientation` - - EXAMPLES: - - For a 2-regular graph, a strong orientation gives to each vertex an - out-degree equal to 1:: - - sage: g = graphs.CycleGraph(5) - sage: g.strong_orientation().out_degree() - [1, 1, 1, 1, 1] - - The Petersen Graph is 2-edge connected. It then has a strongly connected - orientation:: - - sage: g = graphs.PetersenGraph() - sage: o = g.strong_orientation() - sage: len(o.strongly_connected_components()) - 1 - - The same goes for the CubeGraph in any dimension :: - - sage: all(len(graphs.CubeGraph(i).strong_orientation().strongly_connected_components()) == 1 for i in range(2,6)) - True - - A multigraph also has a strong orientation :: - - sage: g = Graph([(1,2),(1,2)], multiedges=True) - sage: g.strong_orientation() - Multi-digraph on 2 vertices - """ - from sage.graphs.digraph import DiGraph - d = DiGraph(multiedges=self.allows_multiple_edges()) - i = 0 - - # The algorithm works through a depth-first search. Any edge - # used in the depth-first search is oriented in the direction - # in which it has been used. All the other edges are oriented - # backward - - v = next(self.vertex_iterator()) - seen = {} - i = 1 - - # Time at which the vertices have been discovered - seen[v] = i - - # indicates the stack of edges to explore - next_ = self.edges_incident(v) - - while next_: - e = next_.pop() - - # Ignore loops - if e[0] == e[1]: - continue - - # We assume e[0] to be a `seen` vertex - e = e if seen.get(e[0], False) is not False else (e[1], e[0], e[2]) - - # If we discovered a new vertex - if seen.get(e[1], False) is False: - d.add_edge(e) - next_.extend(ee for ee in self.edges_incident(e[1]) - if ((e[0], e[1]) != (ee[0], ee[1])) and ((e[0], e[1]) != (ee[1], ee[0]))) - i += 1 - seen[e[1]] = i - - # Else, we orient the edges backward - else: - if seen[e[0]] < seen[e[1]]: - d.add_edge(e[1], e[0], e[2]) - else: - d.add_edge(e) - - # Case of multiple edges. If another edge has already been inserted, we - # add the new one in the opposite direction. - tmp = None - for e in self.multiple_edges(): - if tmp == (e[0], e[1]): - if d.has_edge(e[0], e[1]): - d.add_edge(e[1], e[0], e[2]) - else: - d.add_edge(e) - tmp = (e[0], e[1]) - - return d - - @doc_index("Connectivity, orientations, trees") - def minimum_outdegree_orientation(self, use_edge_labels=False, solver=None, verbose=0, - *, integrality_tolerance=1e-3): - r""" - Return an orientation of ``self`` with the smallest possible maximum - outdegree. - - Given a Graph `G`, it is polynomial to compute an orientation `D` of the - edges of `G` such that the maximum out-degree in `D` is minimized. This - problem, though, is NP-complete in the weighted case [AMOZ2006]_. - - INPUT: - - - ``use_edge_labels`` -- boolean (default: ``False``) - - - When set to ``True``, uses edge labels as weights to compute the - orientation and assumes a weight of `1` when there is no value - available for a given edge. - - - When set to ``False`` (default), gives a weight of 1 to all the - edges. - - - ``solver`` -- string (default: ``None``); specifies a Mixed Integer - Linear Programming (MILP) solver to be used. If set to ``None``, the - default one is used. For more information on MILP solvers and which - default solver is used, see the method :meth:`solve - ` of the class - :class:`MixedIntegerLinearProgram - `. - - - ``verbose`` -- integer (default: 0); sets the level of - verbosity. Set to 0 by default, which means quiet. - - - ``integrality_tolerance`` -- float; parameter for use with MILP - solvers over an inexact base ring; see - :meth:`MixedIntegerLinearProgram.get_values`. - - EXAMPLES: - - Given a complete bipartite graph `K_{n,m}`, the maximum out-degree of an - optimal orientation is `\left\lceil \frac {nm} {n+m}\right\rceil`:: - - sage: g = graphs.CompleteBipartiteGraph(3,4) - sage: o = g.minimum_outdegree_orientation() # needs sage.numerical.mip - sage: max(o.out_degree()) == integer_ceil((4*3)/(3+4)) # needs sage.numerical.mip - True - """ - self._scream_if_not_simple() - if self.is_directed(): - raise ValueError("Cannot compute an orientation of a DiGraph. " - "Please convert it to a Graph if you really mean it.") - - if use_edge_labels: - from sage.rings.real_mpfr import RR - - def weight(e): - l = self.edge_label(e) - return l if l in RR else 1 - else: - def weight(e): - return 1 - - from sage.numerical.mip import MixedIntegerLinearProgram - - p = MixedIntegerLinearProgram(maximization=False, solver=solver) - degree = p.new_variable(nonnegative=True) - - # The orientation of an edge is boolean and indicates whether the edge - # uv goes from u to v ( equal to 0 ) or from v to u ( equal to 1) - orientation = p.new_variable(binary=True) - - # Whether an edge adjacent to a vertex u counts positively or - # negatively. To do so, we first fix an arbitrary extremity per edge uv. - ext = {frozenset(e): e[0] for e in self.edge_iterator(labels=False)} - - def outgoing(u, e, variable): - if u == ext[frozenset(e)]: - return variable - else: - return 1 - variable - - for u in self: - p.add_constraint(p.sum(weight(e) * outgoing(u, e, orientation[frozenset(e)]) - for e in self.edge_iterator(vertices=[u], labels=False)) - - degree['max'], max=0) - - p.set_objective(degree['max']) - - p.solve(log=verbose) - - orientation = p.get_values(orientation, convert=bool, tolerance=integrality_tolerance) - - # All the edges from self are doubled in O - # ( one in each direction ) - from sage.graphs.digraph import DiGraph - O = DiGraph(self) - - # Builds the list of edges that should be removed - edges = [] - - for e in self.edge_iterator(labels=None): - if orientation[frozenset(e)]: - edges.append(e[::-1]) - else: - edges.append(e) - - O.delete_edges(edges) - - return O - - @doc_index("Connectivity, orientations, trees") - def bounded_outdegree_orientation(self, bound, solver=None, verbose=False, - *, integrality_tolerance=1e-3): - r""" - Compute an orientation of ``self`` such that every vertex `v` has - out-degree less than `b(v)` - - INPUT: - - - ``bound`` -- maximum bound on the out-degree. Can be of three - different types : - - * An integer `k`. In this case, computes an orientation whose maximum - out-degree is less than `k`. - - * A dictionary associating to each vertex its associated maximum - out-degree. - - * A function associating to each vertex its associated maximum - out-degree. - - - ``solver`` -- string (default: ``None``); specifies a Mixed Integer - Linear Programming (MILP) solver to be used. If set to ``None``, the - default one is used. For more information on MILP solvers and which - default solver is used, see the method :meth:`solve - ` of the class - :class:`MixedIntegerLinearProgram - `. - - - ``verbose`` -- integer (default: 0); sets the level of - verbosity. Set to 0 by default, which means quiet. - - - ``integrality_tolerance`` -- float; parameter for use with MILP - solvers over an inexact base ring; see - :meth:`MixedIntegerLinearProgram.get_values`. - - OUTPUT: - - A DiGraph representing the orientation if it exists. - A :exc:`ValueError` exception is raised otherwise. - - ALGORITHM: - - The problem is solved through a maximum flow : - - Given a graph `G`, we create a ``DiGraph`` `D` defined on `E(G)\cup - V(G)\cup \{s,t\}`. We then link `s` to all of `V(G)` (these edges having - a capacity equal to the bound associated to each element of `V(G)`), and - all the elements of `E(G)` to `t` . We then link each `v \in V(G)` to - each of its incident edges in `G`. A maximum integer flow of value - `|E(G)|` corresponds to an admissible orientation of `G`. Otherwise, - none exists. - - EXAMPLES: - - There is always an orientation of a graph `G` such that a vertex `v` has - out-degree at most `\lceil \frac {d(v)} 2 \rceil`:: - - sage: g = graphs.RandomGNP(40, .4) - sage: b = lambda v: integer_ceil(g.degree(v)/2) - sage: D = g.bounded_outdegree_orientation(b) - sage: all( D.out_degree(v) <= b(v) for v in g ) - True - - - Chvatal's graph, being 4-regular, can be oriented in such a way that its - maximum out-degree is 2:: - - sage: g = graphs.ChvatalGraph() - sage: D = g.bounded_outdegree_orientation(2) - sage: max(D.out_degree()) - 2 - - For any graph `G`, it is possible to compute an orientation such that - the maximum out-degree is at most the maximum average degree of `G` - divided by 2. Anything less, though, is impossible. - - sage: g = graphs.RandomGNP(40, .4) - sage: mad = g.maximum_average_degree() # needs sage.numerical.mip - - Hence this is possible :: - - sage: d = g.bounded_outdegree_orientation(integer_ceil(mad/2)) # needs sage.numerical.mip - - While this is not:: - - sage: try: # needs sage.numerical.mip - ....: g.bounded_outdegree_orientation(integer_ceil(mad/2-1)) - ....: print("Error") - ....: except ValueError: - ....: pass - - TESTS: - - As previously for random graphs, but more intensively:: - - sage: for i in range(30): # long time (up to 6s on sage.math, 2012) - ....: g = graphs.RandomGNP(40, .4) - ....: b = lambda v: integer_ceil(g.degree(v)/2) - ....: D = g.bounded_outdegree_orientation(b) - ....: if not ( - ....: all( D.out_degree(v) <= b(v) for v in g ) or - ....: D.size() != g.size()): - ....: print("Something wrong happened") - """ - self._scream_if_not_simple() - from sage.graphs.digraph import DiGraph - n = self.order() - - if not n: - return DiGraph() - - vertices = list(self) - vertices_id = {y: x for x, y in enumerate(vertices)} - - b = {} - - # Checking the input type. We make a dictionary out of it - if isinstance(bound, dict): - b = bound - else: - try: - b = dict(zip(vertices, map(bound, vertices))) - - except TypeError: - b = dict(zip(vertices, [bound]*n)) - - d = DiGraph() - - # Adding the edges (s,v) and ((u,v),t) - d.add_edges(('s', vertices_id[v], b[v]) for v in vertices) - - d.add_edges(((vertices_id[u], vertices_id[v]), 't', 1) - for u, v in self.edges(sort=False, labels=None)) - - # each v is linked to its incident edges - - for u, v in self.edge_iterator(labels=None): - u, v = vertices_id[u], vertices_id[v] - d.add_edge(u, (u, v), 1) - d.add_edge(v, (u, v), 1) - - # Solving the maximum flow - value, flow = d.flow('s', 't', value_only=False, integer=True, - use_edge_labels=True, solver=solver, verbose=verbose, - integrality_tolerance=integrality_tolerance) - - if value != self.size(): - raise ValueError("No orientation exists for the given bound") - - D = DiGraph() - D.add_vertices(vertices) - - # The flow graph may not contain all the vertices, if they are - # not part of the flow... - - for u in [x for x in range(n) if x in flow]: - - for uu, vv in flow.neighbors_out(u): - v = vv if vv != u else uu - D.add_edge(vertices[u], vertices[v]) - - # I do not like when a method destroys the embedding ;-) - D.set_pos(self.get_pos()) - - return D - - @doc_index("Connectivity, orientations, trees") - def orientations(self, data_structure=None, sparse=None): - r""" - Return an iterator over orientations of ``self``. - - An *orientation* of an undirected graph is a directed graph such that - every edge is assigned a direction. Hence there are `2^s` oriented - digraphs for a simple graph with `s` edges. - - INPUT: - - - ``data_structure`` -- one of ``'sparse'``, ``'static_sparse'``, or - ``'dense'``; see the documentation of :class:`Graph` or - :class:`DiGraph`; default is the data structure of ``self`` - - - ``sparse`` -- boolean (default: ``None``); ``sparse=True`` is an alias - for ``data_structure="sparse"``, and ``sparse=False`` is an alias for - ``data_structure="dense"``. By default (``None``), guess the most - suitable data structure. - - .. WARNING:: - - This always considers multiple edges of graphs as distinguishable, - and hence, may have repeated digraphs. - - .. SEEALSO:: - - - :meth:`~sage.graphs.graph.Graph.strong_orientation` - - :meth:`~sage.graphs.orientations.strong_orientations_iterator` - - :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.nauty_directg` - - :meth:`~sage.graphs.orientations.random_orientation` - - EXAMPLES:: - - sage: G = Graph([[1,2,3], [(1, 2, 'a'), (1, 3, 'b')]], format='vertices_and_edges') - sage: it = G.orientations() - sage: D = next(it) - sage: D.edges(sort=True) - [(1, 2, 'a'), (1, 3, 'b')] - sage: D = next(it) - sage: D.edges(sort=True) - [(1, 2, 'a'), (3, 1, 'b')] - - TESTS:: - - sage: G = Graph() - sage: D = [g for g in G.orientations()] - sage: len(D) - 1 - sage: D[0] - Digraph on 0 vertices - - sage: G = Graph(5) - sage: it = G.orientations() - sage: D = next(it) - sage: D.size() - 0 - - sage: G = Graph([[1,2,'a'], [1,2,'b']], multiedges=True) - sage: len(list(G.orientations())) - 4 - - sage: G = Graph([[1,2], [1,1]], loops=True) - sage: len(list(G.orientations())) - 2 - - sage: G = Graph([[1,2],[2,3]]) - sage: next(G.orientations()) - Digraph on 3 vertices - sage: G = graphs.PetersenGraph() - sage: next(G.orientations()) - An orientation of Petersen graph: Digraph on 10 vertices - - An orientation must have the same ground set of vertices as the original - graph (:issue:`24366`):: - - sage: G = Graph(1) - sage: next(G.orientations()) - Digraph on 1 vertex - """ - if sparse is not None: - if data_structure is not None: - raise ValueError("cannot specify both 'sparse' and 'data_structure'") - data_structure = "sparse" if sparse else "dense" - if data_structure is None: - from sage.graphs.base.dense_graph import DenseGraphBackend - from sage.graphs.base.sparse_graph import SparseGraphBackend - if isinstance(self._backend, DenseGraphBackend): - data_structure = "dense" - elif isinstance(self._backend, SparseGraphBackend): - data_structure = "sparse" - else: - data_structure = "static_sparse" - - name = self.name() - if name: - name = 'An orientation of ' + name - - from sage.graphs.digraph import DiGraph - if not self.size(): - D = DiGraph(data=[self.vertices(sort=False), []], - format='vertices_and_edges', - name=name, - pos=self._pos, - multiedges=self.allows_multiple_edges(), - loops=self.allows_loops(), - data_structure=data_structure) - if hasattr(self, '_embedding'): - D._embedding = copy(self._embedding) - yield D - return - - E = [[(u, v, label), (v, u, label)] if u != v else [(u, v, label)] - for u, v, label in self.edge_iterator()] - verts = self.vertices(sort=False) - for edges in itertools.product(*E): - D = DiGraph(data=[verts, edges], - format='vertices_and_edges', - name=name, - pos=self._pos, - multiedges=self.allows_multiple_edges(), - loops=self.allows_loops(), - data_structure=data_structure) - if hasattr(self, '_embedding'): - D._embedding = copy(self._embedding) - yield D - # Coloring @doc_index("Basic methods") @@ -4122,407 +3601,99 @@ def asc(sigma): ret += M.term(sigma.to_composition(), t**asc(sigma)) return ret - @doc_index("Leftovers") - def matching(self, value_only=False, algorithm='Edmonds', - use_edge_labels=False, solver=None, verbose=0, - *, integrality_tolerance=1e-3): + @doc_index("Coloring") + def tutte_symmetric_function(self, R=None, t=None): r""" - Return a maximum weighted matching of the graph represented by the list - of its edges. - - For more information, see the :wikipedia:`Matching_(graph_theory)`. + Return the Tutte symmetric function of ``self``. - Given a graph `G` such that each edge `e` has a weight `w_e`, a maximum - matching is a subset `S` of the edges of `G` of maximum weight such that - no two edges of `S` are incident with each other. - - As an optimization problem, it can be expressed as: + Let `G` be a graph. The Tutte symmetric function `XB_G` of the graph + `G` was introduced in [Sta1998]_. We present the equivalent definition + given in [CS2022]_. .. MATH:: - \mbox{Maximize : }&\sum_{e\in G.edges()} w_e b_e\\ - \mbox{Such that : }&\forall v \in G, - \sum_{(u,v)\in G.edges()} b_{(u,v)}\leq 1\\ - &\forall x\in G, b_x\mbox{ is a binary variable} - - INPUT: - - - ``value_only`` -- boolean (default: ``False``); when set to ``True``, - only the cardinal (or the weight) of the matching is returned - - - ``algorithm`` -- string (default: ``'Edmonds'``) - - - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX - - - ``'LP'`` uses a Linear Program formulation of the matching problem - - - ``use_edge_labels`` -- boolean (default: ``False``) - - - when set to ``True``, computes a weighted matching where each edge - is weighted by its label (if an edge has no label, `1` is assumed) - - - when set to ``False``, each edge has weight `1` - - - ``solver`` -- string (default: ``None``); specifies a Mixed Integer - Linear Programming (MILP) solver to be used. If set to ``None``, the - default one is used. For more information on MILP solvers and which - default solver is used, see the method :meth:`solve - ` of the class - :class:`MixedIntegerLinearProgram - `. - - - ``verbose`` -- integer (default: 0); sets the level of verbosity: - set to 0 by default, which means quiet (only useful when ``algorithm - == "LP"``) - - - ``integrality_tolerance`` -- float; parameter for use with MILP - solvers over an inexact base ring; see - :meth:`MixedIntegerLinearProgram.get_values`. - - OUTPUT: - - - When ``value_only=False`` (default), this method returns an - :class:`EdgesView` containing the edges of a maximum matching of `G`. - - - When ``value_only=True``, this method returns the sum of the - weights (default: ``1``) of the edges of a maximum matching of `G`. - The type of the output may vary according to the type of the edge - labels and the algorithm used. - - ALGORITHM: - - The problem is solved using Edmond's algorithm implemented in NetworkX, - or using Linear Programming depending on the value of ``algorithm``. - - EXAMPLES: - - Maximum matching in a Pappus Graph:: - - sage: g = graphs.PappusGraph() - sage: g.matching(value_only=True) # needs sage.networkx - 9 - - Same test with the Linear Program formulation:: - - sage: g = graphs.PappusGraph() - sage: g.matching(algorithm='LP', value_only=True) # needs sage.numerical.mip - 9 - - .. PLOT:: - - g = graphs.PappusGraph() - sphinx_plot(g.plot(edge_colors={"red":g.matching()})) - - TESTS: - - When ``use_edge_labels`` is set to ``False``, with Edmonds' algorithm - and LP formulation:: - - sage: g = Graph([(0,1,0), (1,2,999), (2,3,-5)]) - sage: sorted(g.matching()) # needs sage.networkx - [(0, 1, 0), (2, 3, -5)] - sage: sorted(g.matching(algorithm='LP')) # needs sage.numerical.mip - [(0, 1, 0), (2, 3, -5)] - - When ``use_edge_labels`` is set to ``True``, with Edmonds' algorithm and - LP formulation:: - - sage: g = Graph([(0,1,0), (1,2,999), (2,3,-5)]) - sage: g.matching(use_edge_labels=True) # needs sage.networkx - [(1, 2, 999)] - sage: g.matching(algorithm='LP', use_edge_labels=True) # needs sage.numerical.mip - [(1, 2, 999)] - - With loops and multiedges:: - - sage: edge_list = [(0,0,5), (0,1,1), (0,2,2), (0,3,3), (1,2,6) - ....: , (1,2,3), (1,3,3), (2,3,3)] - sage: g = Graph(edge_list, loops=True, multiedges=True) - sage: m = g.matching(use_edge_labels=True) # needs sage.networkx - sage: type(m) # needs sage.networkx - - sage: sorted(m) # needs sage.networkx - [(0, 3, 3), (1, 2, 6)] - - TESTS: - - If ``algorithm`` is set to anything different from ``'Edmonds'`` or - ``'LP'``, an exception is raised:: - - sage: g = graphs.PappusGraph() - sage: g.matching(algorithm='somethingdifferent') - Traceback (most recent call last): - ... - ValueError: algorithm must be set to either "Edmonds" or "LP" - """ - from sage.rings.real_mpfr import RR - - def weight(x): - if x in RR: - return x - else: - return 1 - - W = {} - L = {} - for u, v, l in self.edge_iterator(): - if u is v: - continue - fuv = frozenset((u, v)) - if fuv not in L or (use_edge_labels and W[fuv] < weight(l)): - L[fuv] = l - if use_edge_labels: - W[fuv] = weight(l) - - if algorithm == "Edmonds": - import networkx - g = networkx.Graph() - if use_edge_labels: - for (u, v), w in W.items(): - g.add_edge(u, v, weight=w) - else: - for u, v in L: - g.add_edge(u, v) - d = networkx.max_weight_matching(g) - if value_only: - if use_edge_labels: - return sum(W[frozenset(e)] for e in d) - return Integer(len(d)) - - return EdgesView(Graph([(u, v, L[frozenset((u, v))]) for u, v in d], - format='list_of_edges')) - - elif algorithm == "LP": - g = self - from sage.numerical.mip import MixedIntegerLinearProgram - # returns the weight of an edge considering it may not be - # weighted ... - p = MixedIntegerLinearProgram(maximization=True, solver=solver) - b = p.new_variable(binary=True) - if use_edge_labels: - p.set_objective(p.sum(w * b[fe] for fe, w in W.items())) - else: - p.set_objective(p.sum(b[fe] for fe in L)) - # for any vertex v, there is at most one edge incident to v in - # the maximum matching - for v in g: - p.add_constraint(p.sum(b[frozenset(e)] for e in self.edge_iterator(vertices=[v], labels=False) - if e[0] != e[1]), max=1) - - p.solve(log=verbose) - b = p.get_values(b, convert=bool, tolerance=integrality_tolerance) - if value_only: - if use_edge_labels: - return sum(w for fe, w in W.items() if b[fe]) - return Integer(sum(1 for fe in L if b[fe])) - - return EdgesView(Graph([(u, v, L[frozenset((u, v))]) - for u, v in L if b[frozenset((u, v))]], - format='list_of_edges')) + XB_G = \sum_{\pi \vdash V} (1+t)^{e(\pi)} \tilde{m}_{\lambda(\pi)}, - raise ValueError('algorithm must be set to either "Edmonds" or "LP"') + where the sum ranges over all set-partitions `\pi` of the vertex set + `V`, `\lambda(\pi)` is the partition determined by the sizes of the + blocks of `\pi`, and `e(\pi)` is the number of edges whose endpoints + lie in the same block of `\pi`. In particular, the coefficients of + `XB_G` when expanded in terms of augmented monomial symmetric functions + are polynomials in `t` with non-negative integer coefficients. - @doc_index("Leftovers") - def is_factor_critical(self, matching=None, algorithm='Edmonds', solver=None, verbose=0, - *, integrality_tolerance=0.001): - r""" - Check whether this graph is factor-critical. + For an integer partition `\lambda = 1^{r_1}2^{r_2}\cdots` expressed in + the exponential notation, the augmented monomial symmetric function + is defined as - A graph of order `n` is factor-critical if every subgraph of `n-1` - vertices have a perfect matching, hence `n` must be odd. See - :wikipedia:`Factor-critical_graph` for more details. + .. MATH:: - This method implements the algorithm proposed in [LR2004]_ and we assume - that a graph of order one is factor-critical. The time complexity of the - algorithm is linear if a near perfect matching is given as input (i.e., - a matching such that all vertices but one are incident to an edge of the - matching). Otherwise, the time complexity is dominated by the time - needed to compute a maximum matching of the graph. + \tilde{m}_{\lambda} = \left(\prod_{i} r_i! \right) m_{\lambda}. INPUT: - - ``matching`` -- (default: ``None``) a near perfect matching of the - graph, that is a matching such that all vertices of the graph but one - are incident to an edge of the matching. It can be given using any - valid input format of :class:`~sage.graphs.graph.Graph`. - - If set to ``None``, a matching is computed using the other parameters. - - - ``algorithm`` -- string (default: ``'Edmonds'``); the algorithm to use - to compute a maximum matching of the graph among - - - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX - - - ``'LP'`` uses a Linear Program formulation of the matching problem - - - ``solver`` -- string (default: ``None``); specifies a Mixed Integer - Linear Programming (MILP) solver to be used. If set to ``None``, the - default one is used. For more information on MILP solvers and which - default solver is used, see the method :meth:`solve - ` of the class - :class:`MixedIntegerLinearProgram - `. - - - ``verbose`` -- integer (default: 0); sets the level of verbosity: - set to 0 by default, which means quiet (only useful when ``algorithm - == "LP"``) - - - ``integrality_tolerance`` -- float; parameter for use with MILP - solvers over an inexact base ring; see - :meth:`MixedIntegerLinearProgram.get_values`. - - EXAMPLES: - - Odd length cycles and odd cliques of order at least 3 are - factor-critical graphs:: - - sage: [graphs.CycleGraph(2*i + 1).is_factor_critical() for i in range(5)] # needs networkx - [True, True, True, True, True] - sage: [graphs.CompleteGraph(2*i + 1).is_factor_critical() for i in range(5)] # needs networkx - [True, True, True, True, True] - - More generally, every Hamiltonian graph with an odd number of vertices - is factor-critical:: - - sage: G = graphs.RandomGNP(15, .2) - sage: G.add_path([0..14]) - sage: G.add_edge(14, 0) - sage: G.is_hamiltonian() - True - sage: G.is_factor_critical() # needs networkx - True - - Friendship graphs are non-Hamiltonian factor-critical graphs:: + - ``R`` -- (default: the parent of ``t``) the base ring for the symmetric + functions - sage: [graphs.FriendshipGraph(i).is_factor_critical() for i in range(1, 5)] # needs networkx - [True, True, True, True] + - ``t`` -- (default: `t` in `\ZZ[t]`) the parameter `t` - Bipartite graphs are not factor-critical:: + EXAMPLES:: - sage: G = graphs.RandomBipartite(randint(1, 10), randint(1, 10), .5) # needs numpy - sage: G.is_factor_critical() # needs numpy - False + sage: p = SymmetricFunctions(ZZ).p() # needs sage.combinat sage.modules + sage: G = Graph([[1,2],[2,3],[3,4],[4,1],[1,3]]) + sage: XB_G = G.tutte_symmetric_function(); XB_G # needs sage.combinat sage.modules + 24*m[1, 1, 1, 1] + (10*t+12)*m[2, 1, 1] + (4*t^2+10*t+6)*m[2, 2] + + (2*t^3+8*t^2+10*t+4)*m[3, 1] + + (t^5+5*t^4+10*t^3+10*t^2+5*t+1)*m[4] + sage: p(XB_G) # needs sage.combinat sage.modules + p[1, 1, 1, 1] + 5*t*p[2, 1, 1] + 2*t^2*p[2, 2] + + (2*t^3+8*t^2)*p[3, 1] + (t^5+5*t^4+8*t^3)*p[4] - Graphs with even order are not factor critical:: + Graphs are allowed to have multiedges and loops:: - sage: G = graphs.RandomGNP(10, .5) - sage: G.is_factor_critical() - False + sage: G = Graph([[1,2],[2,3],[2,3]], multiedges = True) + sage: XB_G = G.tutte_symmetric_function(); XB_G # needs sage.combinat sage.modules + 6*m[1, 1, 1] + (t^2+3*t+3)*m[2, 1] + (t^3+3*t^2+3*t+1)*m[3] - One can specify a matching:: + We check that at `t = -1`, we recover the usual chromatic symmetric + function:: - sage: F = graphs.FriendshipGraph(4) - sage: M = F.matching() # needs networkx - sage: F.is_factor_critical(matching=M) # needs networkx - True - sage: F.is_factor_critical(matching=Graph(M)) # needs networkx + sage: G = Graph([[1,2],[1,2],[2,3],[3,4],[4,5]], multiedges=True) + sage: XB_G = G.tutte_symmetric_function(t=-1); XB_G # needs sage.combinat sage.modules + 120*m[1, 1, 1, 1, 1] + 36*m[2, 1, 1, 1] + 12*m[2, 2, 1] + + 2*m[3, 1, 1] + m[3, 2] + sage: X_G = G.chromatic_symmetric_function(); X_G # needs sage.combinat sage.modules + p[1, 1, 1, 1, 1] - 4*p[2, 1, 1, 1] + 3*p[2, 2, 1] + 3*p[3, 1, 1] + - 2*p[3, 2] - 2*p[4, 1] + p[5] + sage: XB_G == X_G # needs sage.combinat sage.modules True - - TESTS: - - Giving a wrong matching:: - - sage: G = graphs.RandomGNP(15, .3) - sage: while not G.is_biconnected(): - ....: G = graphs.RandomGNP(15, .3) - sage: M = G.matching() # needs networkx - sage: G.is_factor_critical(matching=M[:-1]) # needs networkx - Traceback (most recent call last): - ... - ValueError: the input is not a near perfect matching of the graph - sage: G.is_factor_critical(matching=G.edges(sort=True)) - Traceback (most recent call last): - ... - ValueError: the input is not a matching - sage: M = [(2*i, 2*i + 1) for i in range(9)] - sage: G.is_factor_critical(matching=M) - Traceback (most recent call last): - ... - ValueError: the input is not a matching of the graph """ - if self.order() == 1: - return True - - # The graph must have an odd number of vertices, be 2-edge connected, so - # without bridges, and not bipartite - if (not self.order() % 2 or not self.is_connected() or - list(self.bridges()) or self.is_bipartite()): - return False - - if matching: - # We check that the input matching is a valid near perfect matching - # of the graph. - M = Graph(matching) - if any(d != 1 for d in M.degree()): - raise ValueError("the input is not a matching") - if not M.is_subgraph(self, induced=False): - raise ValueError("the input is not a matching of the graph") - if (self.order() != M.order() + 1) or (self.order() != 2*M.size() + 1): - raise ValueError("the input is not a near perfect matching of the graph") - else: - # We compute a maximum matching of the graph - M = Graph(self.matching(algorithm=algorithm, solver=solver, verbose=verbose, - integrality_tolerance=integrality_tolerance)) - - # It must be a near-perfect matching - if self.order() != M.order() + 1: - return False + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.set_partition import SetPartitions + from sage.misc.misc_c import prod + from collections import Counter - # We find the unsaturated vertex u, i.e., the only vertex of the graph - # not in M - for u in self: - if u not in M: - break + if t is None: + t = ZZ['t'].gen() + if R is None: + R = t.parent() + m = SymmetricFunctions(R).m() + ret = m.zero() + V = self.vertices() + M = Counter(self.edge_iterator(labels=False)) + fact = [1] + fact.extend(fact[-1] * i for i in range(1, len(V)+1)) + + def mono(pi): + arcs = 0 + for s in pi: + for u in s: + arcs += sum(M[(u, v)] for v in s if self.has_edge(u, v)) + return arcs - # We virtually build an M-alternating tree T - from queue import Queue - Q = Queue() - Q.put(u) - even = set([u]) - odd = set() - pred = {u: u} - rank = {u: 0} - - while not Q.empty(): - x = Q.get() - for y in self.neighbor_iterator(x): - if y in odd: - continue - elif y in even: - # Search for the nearest common ancestor t of x and y - P = [x] - R = [y] - while P[-1] != R[-1]: - if rank[P[-1]] > rank[R[-1]]: - P.append(pred[P[-1]]) - elif rank[P[-1]] < rank[R[-1]]: - R.append(pred[R[-1]]) - else: - P.append(pred[P[-1]]) - R.append(pred[R[-1]]) - t = P.pop() - R.pop() - # Set t as pred of all vertices of the chains and add - # vertices marked odd to the queue - for a in itertools.chain(P, R): - pred[a] = t - rank[a] = rank[t] + 1 - if a in odd: - even.add(a) - odd.discard(a) - Q.put(a) - else: # y has not been visited yet - z = next(M.neighbor_iterator(y)) - odd.add(y) - even.add(z) - Q.put(z) - pred[y] = x - pred[z] = y - rank[y] = rank[x] + 1 - rank[z] = rank[y] + 1 - - # The graph is factor critical if all vertices are marked even - return len(even) == self.order() + for pi in SetPartitions(V): + pa = pi.to_partition() + ret += prod(fact[i] for i in pa.to_exp()) * m[pa] * (1+t)**mono(pi) + return ret @doc_index("Algorithmically hard stuff") def has_homomorphism_to(self, H, core=False, solver=None, verbose=0, @@ -6367,7 +5538,7 @@ def twograph(self): T.append([x, y, z]) T = TwoGraph(T) - T.relabel({i: v for i, v in enumerate(self)}) + T.relabel(dict(enumerate(self))) return T @@ -9016,221 +8187,6 @@ def ihara_zeta_function_inverse(self): T[2 * j + 1, 2 * i] = 1 return T.charpoly('t').reverse() - @doc_index("Leftovers") - def perfect_matchings(self, labels=False): - r""" - Return an iterator over all perfect matchings of the graph. - - ALGORITHM: - - Choose a vertex `v`, then recurse through all edges incident to `v`, - removing one edge at a time whenever an edge is added to a matching. - - INPUT: - - - ``labels`` -- boolean (default: ``False``); when ``True``, the edges - in each perfect matching are triples (containing the label as the - third element), otherwise the edges are pairs. - - .. SEEALSO:: - - :meth:`matching` - - EXAMPLES:: - - sage: G=graphs.GridGraph([2,3]) - sage: for m in G.perfect_matchings(): - ....: print(sorted(m)) - [((0, 0), (0, 1)), ((0, 2), (1, 2)), ((1, 0), (1, 1))] - [((0, 0), (1, 0)), ((0, 1), (0, 2)), ((1, 1), (1, 2))] - [((0, 0), (1, 0)), ((0, 1), (1, 1)), ((0, 2), (1, 2))] - - sage: G = graphs.CompleteGraph(4) - sage: for m in G.perfect_matchings(labels=True): - ....: print(sorted(m)) - [(0, 1, None), (2, 3, None)] - [(0, 2, None), (1, 3, None)] - [(0, 3, None), (1, 2, None)] - - sage: G = Graph([[1,-1,'a'], [2,-2, 'b'], [1,-2,'x'], [2,-1,'y']]) - sage: sorted(sorted(m) for m in G.perfect_matchings(labels=True)) - [[(-2, 1, 'x'), (-1, 2, 'y')], [(-2, 2, 'b'), (-1, 1, 'a')]] - - sage: G = graphs.CompleteGraph(8) - sage: mpc = G.matching_polynomial().coefficients(sparse=False)[0] # needs sage.libs.flint - sage: len(list(G.perfect_matchings())) == mpc # needs sage.libs.flint - True - - sage: G = graphs.PetersenGraph().copy(immutable=True) - sage: [sorted(m) for m in G.perfect_matchings()] - [[(0, 1), (2, 3), (4, 9), (5, 7), (6, 8)], - [(0, 1), (2, 7), (3, 4), (5, 8), (6, 9)], - [(0, 4), (1, 2), (3, 8), (5, 7), (6, 9)], - [(0, 4), (1, 6), (2, 3), (5, 8), (7, 9)], - [(0, 5), (1, 2), (3, 4), (6, 8), (7, 9)], - [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]] - - sage: list(Graph().perfect_matchings()) - [[]] - - sage: G = graphs.CompleteGraph(5) - sage: list(G.perfect_matchings()) - [] - """ - if not self: - yield [] - return - if self.order() % 2 or any(len(cc) % 2 for cc in self.connected_components(sort=False)): - return - - def rec(G): - """ - Iterator over all perfect matchings of a simple graph `G`. - """ - if not G: - yield [] - return - if G.order() % 2 == 0: - v = next(G.vertex_iterator()) - Nv = list(G.neighbor_iterator(v)) - G.delete_vertex(v) - for u in Nv: - Nu = list(G.neighbor_iterator(u)) - G.delete_vertex(u) - for partial_matching in rec(G): - partial_matching.append((u, v)) - yield partial_matching - G.add_vertex(u) - G.add_edges((u, nu) for nu in Nu) - G.add_vertex(v) - G.add_edges((v, nv) for nv in Nv) - - # We create a mutable copy of the graph and remove its loops, if any - G = self.copy(immutable=False) - G.allow_loops(False) - - # We create a mapping from frozen unlabeled edges to (labeled) edges. - # This ease for instance the manipulation of multiedges (if any) - edges = {} - for e in G.edges(sort=False, labels=labels): - f = frozenset(e[:2]) - if f in edges: - edges[f].append(e) - else: - edges[f] = [e] - - # We now get rid of multiple edges, if any - G.allow_multiple_edges(False) - - # For each unlabeled matching, we yield all its possible labelings - for m in rec(G): - yield from itertools.product(*[edges[frozenset(e)] for e in m]) - - @doc_index("Leftovers") - def has_perfect_matching(self, algorithm='Edmonds', solver=None, verbose=0, - *, integrality_tolerance=1e-3): - r""" - Return whether this graph has a perfect matching. - INPUT: - - - ``algorithm`` -- string (default: ``'Edmonds'``) - - - ``'Edmonds'`` uses Edmonds' algorithm as implemented in NetworkX to - find a matching of maximal cardinality, then check whether this - cardinality is half the number of vertices of the graph. - - - ``'LP_matching'`` uses a Linear Program to find a matching of - maximal cardinality, then check whether this cardinality is half the - number of vertices of the graph. - - - ``'LP'`` uses a Linear Program formulation of the perfect matching - problem: put a binary variable ``b[e]`` on each edge `e`, and for - each vertex `v`, require that the sum of the values of the edges - incident to `v` is 1. - - - ``solver`` -- string (default: ``None``); specifies a Mixed Integer - Linear Programming (MILP) solver to be used. If set to ``None``, the - default one is used. For more information on MILP solvers and which - default solver is used, see the method :meth:`solve - ` of the class - :class:`MixedIntegerLinearProgram - `. - - - ``verbose`` -- integer (default: 0); sets the level of verbosity: - set to 0 by default, which means quiet (only useful when - ``algorithm == "LP_matching"`` or ``algorithm == "LP"``) - - - ``integrality_tolerance`` -- float; parameter for use with MILP - solvers over an inexact base ring; see - :meth:`MixedIntegerLinearProgram.get_values`. - - OUTPUT: boolean - - EXAMPLES:: - - sage: graphs.PetersenGraph().has_perfect_matching() # needs networkx - True - sage: graphs.WheelGraph(6).has_perfect_matching() # needs networkx - True - sage: graphs.WheelGraph(5).has_perfect_matching() # needs networkx - False - sage: graphs.PetersenGraph().has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip - True - sage: graphs.WheelGraph(6).has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip - True - sage: graphs.WheelGraph(5).has_perfect_matching(algorithm='LP_matching') - False - sage: graphs.PetersenGraph().has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip - True - sage: graphs.WheelGraph(6).has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip - True - sage: graphs.WheelGraph(5).has_perfect_matching(algorithm='LP_matching') - False - - TESTS:: - - sage: G = graphs.EmptyGraph() - sage: all(G.has_perfect_matching(algorithm=algo) # needs networkx - ....: for algo in ['Edmonds', 'LP_matching', 'LP']) - True - - Be careful with isolated vertices:: - - sage: G = graphs.PetersenGraph() - sage: G.add_vertex(11) - sage: any(G.has_perfect_matching(algorithm=algo) # needs networkx - ....: for algo in ['Edmonds', 'LP_matching', 'LP']) - False - """ - if self.order() % 2: - return False - if algorithm == "Edmonds": - return len(self) == 2*self.matching(value_only=True, - use_edge_labels=False, - algorithm='Edmonds') - elif algorithm == "LP_matching": - return len(self) == 2*self.matching(value_only=True, - use_edge_labels=False, - algorithm='LP', - solver=solver, - verbose=verbose, - integrality_tolerance=integrality_tolerance) - elif algorithm == "LP": - from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException - p = MixedIntegerLinearProgram(solver=solver) - b = p.new_variable(binary=True) - for v in self: - edges = self.edges_incident(v, labels=False) - if not edges: - return False - p.add_constraint(p.sum(b[frozenset(e)] for e in edges) == 1) - try: - p.solve(log=verbose) - return True - except MIPSolverException: - return False - raise ValueError('algorithm must be set to "Edmonds", "LP_matching" or "LP"') - @doc_index("Leftovers") def effective_resistance(self, i, j, *, base_ring=None): r""" @@ -9571,7 +8527,7 @@ def least_effective_resistance(self, nonedgesonly=True): resistances * :meth:`effective_resistance` -- - compuetes effective resistance for a single node pair + computes effective resistance for a single node pair * See :wikipedia:`Resistance_distance` for more details. @@ -10200,6 +9156,7 @@ def bipartite_double(self, extended=False): from sage.graphs.graph_decompositions.clique_separators import atoms_and_clique_separators from sage.graphs.graph_decompositions.bandwidth import bandwidth from sage.graphs.graph_decompositions.cutwidth import cutwidth + from sage.graphs.graph_decompositions.slice_decomposition import slice_decomposition matching_polynomial = LazyImport('sage.graphs.matchpoly', 'matching_polynomial', at_startup=True) from sage.graphs.cliquer import all_max_clique as cliques_maximum from sage.graphs.cliquer import all_cliques @@ -10212,7 +9169,15 @@ def bipartite_double(self, extended=False): from sage.graphs.tutte_polynomial import tutte_polynomial from sage.graphs.lovasz_theta import lovasz_theta from sage.graphs.partial_cube import is_partial_cube - from sage.graphs.orientations import strong_orientations_iterator, random_orientation, acyclic_orientations + from sage.graphs.orientations import orient + from sage.graphs.orientations import orientations + from sage.graphs.orientations import strong_orientation + from sage.graphs.orientations import strong_orientations_iterator + from sage.graphs.orientations import random_orientation + from sage.graphs.orientations import acyclic_orientations + from sage.graphs.orientations import minimum_outdegree_orientation + from sage.graphs.orientations import bounded_outdegree_orientation + from sage.graphs.orientations import eulerian_orientation from sage.graphs.connectivity import bridges, cleave, spqr_tree from sage.graphs.connectivity import is_triconnected from sage.graphs.comparability import is_comparability @@ -10228,6 +9193,12 @@ def bipartite_double(self, extended=False): from sage.graphs.graph_coloring import fractional_chromatic_number from sage.graphs.graph_coloring import fractional_chromatic_index from sage.graphs.hyperbolicity import hyperbolicity + from sage.graphs.matching import has_perfect_matching + from sage.graphs.matching import is_bicritical + from sage.graphs.matching import is_factor_critical + from sage.graphs.matching import is_matching_covered + from sage.graphs.matching import matching + from sage.graphs.matching import perfect_matchings _additional_categories = { @@ -10256,9 +9227,15 @@ def bipartite_double(self, extended=False): "is_permutation" : "Graph properties", "tutte_polynomial" : "Algorithmically hard stuff", "lovasz_theta" : "Leftovers", + "orient": "Connectivity, orientations, trees", + "orientations": "Connectivity, orientations, trees", + "strong_orientation" : "Connectivity, orientations, trees", "strong_orientations_iterator" : "Connectivity, orientations, trees", "random_orientation" : "Connectivity, orientations, trees", "acyclic_orientations" : "Connectivity, orientations, trees", + "minimum_outdegree_orientation": "Connectivity, orientations, trees", + "bounded_outdegree_orientation": "Connectivity, orientations, trees", + "eulerian_orientation": "Connectivity, orientations, trees", "bridges" : "Connectivity, orientations, trees", "cleave" : "Connectivity, orientations, trees", "spqr_tree" : "Connectivity, orientations, trees", @@ -10276,7 +9253,13 @@ def bipartite_double(self, extended=False): "fractional_chromatic_number" : "Coloring", "fractional_chromatic_index" : "Coloring", "geodetic_closure" : "Leftovers", - "hyperbolicity" : "Distances", + "hyperbolicity" : "Distances", + "has_perfect_matching" : "Matching", + "is_bicritical" : "Matching", + "is_factor_critical" : "Matching", + "is_matching_covered" : "Matching", + "matching" : "Matching", + "perfect_matchings" : "Matching" } __doc__ = __doc__.replace("{INDEX_OF_METHODS}", gen_thematic_rest_table_index(Graph, _additional_categories)) diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 0bd02e3b5db..3cb10805b8a 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -172,12 +172,6 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal :meth:`~sage.graphs.traversals.maximum_cardinality_search_M` graph traversal and has time complexity in `O(|V|\cdot|E|)`. - .. NOTE:: - As the graph is converted to a short_digraph (with - ``sort_neighbors=True``), the complexity has an extra - `O(|V|+|E|\log{|E|})` for ``SparseGraph`` and `O(|V|^2\log{|E|})` for - ``DenseGraph``. - If the graph is not connected, we insert empty separators between the lists of separators of each connected components. See the examples below for more details. @@ -464,8 +458,7 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=True) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) # variables for the manipulation of the short digraph cdef uint32_t** p_vertices = sd.neighbors diff --git a/src/sage/graphs/graph_decompositions/meson.build b/src/sage/graphs/graph_decompositions/meson.build new file mode 100644 index 00000000000..0d9778ae2ba --- /dev/null +++ b/src/sage/graphs/graph_decompositions/meson.build @@ -0,0 +1,64 @@ +tdlib = cc.find_library('tdlib', required: false, disabler: true) +# Cannot be found via pkg-config +rw = cc.find_library('rw') + +py.install_sources( + 'all.py', + 'all__sagemath_tdlib.py', + 'fast_digraph.pxd', + 'modular_decomposition.py', + 'rankwidth.pxd', + 'slice_decomposition.pxd', + 'tree_decomposition.pxd', + 'vertex_separation.pxd', + subdir: 'sage/graphs/graph_decompositions', +) + +extension_data = { + 'bandwidth' : files('bandwidth.pyx'), + 'cutwidth' : files('cutwidth.pyx'), + 'fast_digraph' : files('fast_digraph.pyx'), + 'graph_products' : files('graph_products.pyx'), + 'rankwidth' : files('rankwidth.pyx'), + 'tree_decomposition' : files('tree_decomposition.pyx'), + 'vertex_separation' : files('vertex_separation.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs/graph_decompositions', + install: true, + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, cysignals, gmp, rw], + ) +endforeach + +extension_data_cpp = { + 'clique_separators': files('clique_separators.pyx'), + 'slice_decomposition' : files('slice_decomposition.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs/graph_decompositions', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, cysignals, gmp, rw], + ) +endforeach + +py.extension_module( + 'tdlib', + sources: 'tdlib.pyx', + subdir: 'sage/graphs/graph_decompositions', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, cysignals, tdlib], +) + diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pxd b/src/sage/graphs/graph_decompositions/slice_decomposition.pxd new file mode 100644 index 00000000000..fcd14a25300 --- /dev/null +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pxd @@ -0,0 +1,17 @@ +from libcpp.vector cimport vector + +from sage.structure.sage_object cimport SageObject +from sage.graphs.base.c_graph cimport CGraph + +cdef void extended_lex_BFS( + CGraph cg, vector[int] &sigma, vector[int] *sigma_inv, + int initial_v_int, vector[int] *pred, vector[size_t] *xslice_len, + vector[vector[int]] *lex_label) except * + +cdef class SliceDecomposition(SageObject): + cdef tuple sigma + cdef dict sigma_inv + cdef vector[size_t] xslice_len + cdef dict lex_label + cdef object _graph_class + cdef object _underlying_graph diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx new file mode 100644 index 00000000000..39a85485dfa --- /dev/null +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -0,0 +1,1079 @@ +# cython: binding=True +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 +r""" +Slice decomposition + +This module implements an extended lexBFS algorithm for computing the slice +decomposition of undirected graphs and the class :class:`~SliceDecomposition` to +represent such decompositions. + +A formal definition of slice decompositions can be found in Section 3.2 of +[TCHP2008]_ and a description of the extended lexBFS algorithm is given in +Section 3.3 of [TCHP2008]_. + +AUTHORS: + +- Cyril Bouvier (2024-06-25): initial version +""" +# **************************************************************************** +# Copyright (C) 2024 Cyril Bouvier +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from libcpp.algorithm cimport swap +from cython.operator cimport dereference as deref + +from sage.graphs.base.c_graph cimport CGraphBackend +from sage.data_structures.bitset_base cimport bitset_in + +cdef void extended_lex_BFS( + CGraph cg, vector[int] &sigma, vector[int] *sigma_inv, + int initial_v_int, vector[int] *pred, vector[size_t] *xslice_len, + vector[vector[int]] *lex_label) except *: + r""" + Perform a extended lexicographic breadth first search (LexBFS) on the + undirected graph `G`. + + In addition to computing a LexBFS ordering, the extended LexBFS algorithm + can be used to compute the slice decomposition of the graph. + + This function implements the `O(n+m)` time algorithm proposed in [HMPV2000]_ + and [TCHP2008]_. + + INPUT: + + - ``cg`` -- a ``CGraph``. This function ignores loops and multiple edges and + assumes that the graph is undirected. + + - ``sigma`` -- vector of ``int`` to store the ordering of the vertices + resulting from the LexBFS traversal. At the end, the vector will have size + `n` (the number of vertices of the graph). + + - ``sigma_inv`` -- a pointer to a vector to store the inverse of the + permutation ``sigma``. ``sigma_inv`` can be ``NULL`` if the caller does + not need it (but, note that, the inverse of ``sigma`` is still needed by + the algorithm, so it does not save time nor memory to have ``sigma_inv`` + equal to ``NULL``). At the end, if ``sigma_inv`` is not NULL, the vector + pointer by it will have size `n` (the number of vertices of the graph) + and will satisfy: + * sigma[deref(sigma_inv)[v_int]] = v_int + * deref(sigma_inv)[sigma[i]] = i + + - ``initial_v_int`` -- the first vertex to consider. It can be `-1`; in this + case the first active vertex (corresponding to the first bit set in + ``cg.active_vertices``) will be taken as first vertex. + + - ``pred`` -- a pointer to a vector of int to store the predecessor of a + vertex in the LexBFS traversal. ``pred`` can be ``NULL`` if the caller + does not need it (and the information will not be computed by the + algorithm). At the end, if ``pred`` is not NULL, the vector pointer by it + will have size `n` (the number of vertices of the graph) and pred[i] will + be either -1 (if sigma[i] as no predecessor) or a positive value less than + n such that the predecessor of sigma[i] is sigma[pred[i]]. + + - ``xslice_len`` -- a pointer to a vector of size_t to store the length of + the x-slices associated with the lexBFS traversal. ``xslice_len`` can be + ``NULL`` if the caller does not need it (and the information will not be + computed by the algorithm). At the end, if ``xslice_len`` is not NULL, the + vector pointer by it will have size `n` (the number of vertices of the + graph) and the length of the x-slice starting at sigma[i] will be + xslice_len[i]. + + - ``lex_label`` -- a pointer to a vector of vector[int] to store the + lexicographic labels associated with the lexBFS traversal. ``lex_label`` + can be ``NULL`` if the caller does not need it (and the information will + not be computed by the algorithm). At the end, if ``lex_label`` is not + NULL, the vector pointer by it will have size `n` (the number of + vertices of the graph) and the lexicographic label of sigma[i] + will given by lex_label[i]. + + ALGORITHM: + + This algorithm uses the notion of *partition refinement* to determine the + exact position of the vertices in the ordering. + + Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define + `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset + of neighbors of `v` appearing before the `i`-th vertex in the ordering + `\sigma`. Now, a part of an ordering `\sigma` is a set of consecutive + vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in + S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < + \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a part is the + first position of its vertices. + + The algorithm starts with a single part containing all vertices. Then, when + the position of the `i`-th vertex `v` is fixed, it explores the neighbors of + `v` that have not yet been ordered. Consider a part `S` such that `N(x)\cap + S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices + in `S` so that the first vertices are the neighbors of `v`. The subpart + containing the neighbors of `v` is assigned a new name, and the head of `S` + is set to the position of the first vertex of `S \setminus N(v)` in the + ordering `\sigma`. + + Observe that each arc of the graph can induce the subdivision of a part. + Hence, the algorithm can use up to `m + 1` different parts. + + The time complexity of this algorithm is in `O(n + m)`, and our + implementation follows that complexity ``SparseGraph``. For ``DenseGraph``, + the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more + details. + + This implementation of extended LexBFS offers some guarantee on the order in + which the vertices appear in the computed ordering: in case of a tie between + lexicographic labels during the computation, this function will "choose" the + vertices in the order in which they appear during the enumeration of the + neighbors of their last common neighbor. + For example, if `(u_0, ..., u_k)` is the beginning of the ordering being + computed and the vertices `v` and `w` currently have the same lexicographic + label (it means that they have the same neighbors in `(u_0, ..., u_k)`). + Let call `u_j` their last neighbor in the current ordering (*i.e.*, for all + `i > j`, `u_i` is not a neighbor of `v` and `w`). This implementation + will choose `v` for the next vertex of the ordering if and only if `v` + appeared before `w` when the neighbors of `u_j` where enumerated. + + One possible use of this guarantee is that the caller can reorder the + adjacency list of vertices (by using, for example, a static sparse graph) + to force the computed LexBFS order to respect a previous one. + + EXAMPLES:: + + To see how it can be used, see the code of the lex_BFS method (in + traversals.pyx) or of the class SliceDecomposition in this module. + + TESTS: + + Indirect doctests:: + + sage: G = graphs.HouseGraph() + sage: G.slice_decomposition() + [0[1[2]] [3] [4]] + sage: G.lex_BFS(algorithm="fast") + [0, 1, 2, 3, 4] + """ + cdef int n = cg.num_verts + # Variables for the partition refinement algorithm + cdef size_t max_nparts = cg.num_arcs // 2 + 1 + cdef bint need_to_delete_sigma_inv = sigma_inv == NULL + if sigma_inv == NULL: + sigma_inv = new vector[int]() + cdef vector[size_t] part_of = vector[size_t](n, 0) + cdef vector[size_t] part_len # only used if xslice_len != NULL (see below) + cdef vector[size_t] part_head = vector[size_t](max_nparts) + cdef vector[size_t] subpart = vector[size_t](max_nparts) + cdef size_t p, part_of_i, nparts, old_nparts + # Temporary variables + cdef int max_degree = 0 + cdef int i, j, k, l, u_int, v_int, t_int + + # Resize vectors + sigma.resize(n) + deref(sigma_inv).resize(cg.active_vertices.size) + if pred != NULL: + deref(pred).clear() + deref(pred).resize(n, -1) # initialize pred[i] to -1 for 0 <= i < n + if xslice_len != NULL: + deref(xslice_len).resize(n) + part_len.resize(max_nparts) + if lex_label != NULL: + deref(lex_label).resize(n) + + # Initialize the position of vertices in sigma (and compute max_degree) + if initial_v_int >= 0: + sigma[0] = initial_v_int + deref(sigma_inv)[initial_v_int] = 0 + i = 1 + else: + i = 0 + for v_int in range( cg.active_vertices.size): + if bitset_in(cg.active_vertices, v_int): + if v_int != initial_v_int: + sigma[i] = v_int + deref(sigma_inv)[v_int] = i + i = i + 1 + max_degree = max(max_degree, cg.out_degrees[v_int]) + + # Variables needed to iterate over neighbors of a vertex + cdef int nneighbors + cdef vector[int] neighbors = vector[int](max_degree) + + # Initialize partition: one part containing all the vertices + nparts = 1 + # all element of part_of are already initialized to 0 + part_head[0] = 0 + subpart[0] = 0 + if xslice_len != NULL: + part_len[0] = n + + # Main loop + for i in range(n): + old_nparts = nparts + + part_of_i = part_of[i] + + # put i out of its part (updating part_len if needed) + part_head[part_of_i] += 1 + if xslice_len != NULL: + deref(xslice_len)[i] = part_len[part_of_i] + part_len[part_of_i] -= 1 + + v_int = sigma[i] + + # Iterate over the neighbors of v + nneighbors = cg.out_neighbors_unsafe (v_int, neighbors.data(), max_degree) + for k in range(nneighbors): + u_int = neighbors[k] + j = deref(sigma_inv)[u_int] # get the position of u + if j <= i: + continue # already taken care of + + if lex_label != NULL: + deref(lex_label)[j].push_back (v_int) + + p = part_of[j] # get the part of u + l = part_head[p] # get the beginning of the part containing u + + # if not last and next elem belongs in the same part (ie #part >= 2) + if l < n - 1 and part_of[l + 1] == p: + if l != j: # not already first elem of the part + # Place u at the position of the head of the part + t_int = sigma[l] + deref(sigma_inv)[t_int], deref(sigma_inv)[u_int] = j, l + sigma[j], sigma[l] = t_int, u_int + if lex_label != NULL: + swap[vector[int]](deref(lex_label)[j], + deref(lex_label)[l]) + j = l + part_head[p] += 1 # move the head of the part to next elem + + # if part p was not already cut in two during this iteration, we + # create a new part using subpart + if subpart[p] < old_nparts: + subpart[p] = nparts + part_head[nparts] = j + if xslice_len != NULL: + part_len[nparts] = 0 + subpart[nparts] = 0 + nparts += 1 + + # Finally, we update the name of the part for position j and set v + # as predecessor of u + part_of[j] = subpart[p] + if xslice_len != NULL: + part_len[p] -= 1 + part_len[subpart[p]] += 1 + if pred != NULL: + deref(pred)[j] = i + + if need_to_delete_sigma_inv: + del sigma_inv + + +def slice_decomposition(G, initial_vertex=None): + r""" + Compute a slice decomposition of the simple undirected graph + + INPUT: + + - ``G`` -- a Sage graph. + + - ``initial_vertex`` -- (default: ``None``); the first vertex to consider. + + OUTPUT: + + An object of type :class:`~sage.graphs.graph_decompositions.slice_decomposition.SliceDecomposition` + that represents a slice decomposition of ``G`` + + .. NOTE:: + + Loops and multiple edges are ignored during the computation of the slice + decomposition. + + ALGORITHM: + + The method use the algorithm based on "partition refinement" described in + [HMPV2000]_ and [TCHP2008]_. + The time complexity of this algorithm is in `O(n + m)`, and our + implementation follows that complexity for ``SparseGraph``. For + ``DenseGraph``, the complexity is `O(n^2)`. + + EXAMPLES: + + Slice decomposition of the Petersen Graph:: + + sage: G = graphs.PetersenGraph() + sage: SD = G.slice_decomposition(); SD + [0[1[4[5]]] [2[6]] [3] [9] [7] [8]] + + The graph can have loops or multiple edges but they are ignored:: + + sage: H = Graph(G,loops=True,multiedges=True) + sage: H.add_edges([(4, 4), (2, 2), (1, 6)]) + sage: SD2 = H.slice_decomposition() + sage: SD2 == SD + True + sage: SD2.underlying_graph() == G.to_simple(immutable=True) + True + + The tree corresponding to the slice decomposition can be displayed using + ``view``:: + + sage: from sage.graphs.graph_latex import check_tkz_graph + sage: check_tkz_graph() # random - depends on Tex installation + sage: view(G) # not tested + sage: latex(G) # to obtain the corresponding LaTeX code + \begin{tikzpicture} + ... + \end{tikzpicture} + + Slice decompositions are only defined for undirected graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import slice_decomposition + sage: slice_decomposition(DiGraph()) + Traceback (most recent call last): + ... + ValueError: parameter G must be an undirected graph + """ + return SliceDecomposition(G, initial_vertex=initial_vertex) + + +cdef class SliceDecomposition(SageObject): + + def __init__(self, G, initial_vertex=None): + r""" + Represents a slice decomposition of a simple directed graph. + + INPUT: + + - ``G`` -- a Sage graph. + + - ``initial_vertex`` -- (default: ``None``); the first vertex to + consider. + + .. SEEALSO:: + + * :meth:`~slice_decomposition` -- compute a slice decomposition of + the simple undirected graph + * Section 3.2 of [TCHP2008]_ for a formal definition. + + EXAMPLES: + + The constructor of the :class:`~SliceDecomposition` class is called by + the :meth:`~slice_decomposition` method of undirected graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: G = graphs.PetersenGraph() + sage: SliceDecomposition(G) == G.slice_decomposition() + True + + The vertex appearing first in the slice decomposition can be specified:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: SliceDecomposition(graphs.PetersenGraph(), initial_vertex=3) + [3[2[4[8]]] [1[7]] [0] [9] [6] [5]] + + Slice decompositions are not defined for directed graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: SliceDecomposition(DiGraph()) + Traceback (most recent call last): + ... + ValueError: parameter G must be an undirected graph + + .. automethod:: __getitem__ + """ + if G.is_directed(): + raise ValueError("parameter G must be an undirected graph") + + if initial_vertex is not None and initial_vertex not in G: + raise LookupError(f"vertex ({initial_vertex}) is not a vertex of the graph") + + cdef CGraphBackend Gbackend = G._backend + cdef CGraph cg = Gbackend.cg() + + self._graph_class = type(G) + + cdef int initial_v_int + if initial_vertex is not None: + # we already checked that initial_vertex is in G + initial_v_int = Gbackend.get_vertex(initial_vertex) + else: + initial_v_int = -1 + + cdef vector[int] sigma + cdef vector[vector[int]] lex_label + + # Compute the slice decomposition using the extended lexBFS algorithm + extended_lex_BFS(cg, sigma, NULL, initial_v_int, NULL, + &(self.xslice_len), &lex_label) + + # Translate the results with the actual vertices of the graph + self.sigma = tuple(Gbackend.vertex_label(v_int) for v_int in sigma) + self.sigma_inv = {v: i for i, v in enumerate(self.sigma)} + self.lex_label = {i: tuple(Gbackend.vertex_label(v_int) for v_int in lli) + for i, lli in enumerate(lex_label)} + + def __eq__(self, other): + """ + Return whether ``self`` and ``other`` are equal. + + TESTS:: + + sage: G = graphs.PetersenGraph() + sage: SD = G.slice_decomposition() + sage: SD == SD + True + sage: SD == G.slice_decomposition() + True + + sage: P3 = graphs.PathGraph(3) + sage: SD1 = P3.slice_decomposition(initial_vertex=0) + sage: SD2 = P3.slice_decomposition(initial_vertex=2) + sage: SD1 == SD2 + False + sage: SD3 = graphs.CompleteGraph(3).slice_decomposition() + sage: SD1 == SD3 # same lexBFS but different slice for 1 + False + sage: SD4 = Graph([(0,1), (0,2)]).slice_decomposition() + sage: SD3 == SD4 # same lexBFS and slices but different active edges + False + """ + if not isinstance(other, type(self)): + return False + + cdef SliceDecomposition sd = other + + return self.sigma_inv == sd.sigma_inv \ + and self.lex_label == sd.lex_label \ + and self.xslice_len == sd.xslice_len + + def __hash__(self): + r""" + Compute a hash of a ``SliceDecomposition`` object. + + TESTS:: + + sage: P3 = graphs.PathGraph(3) + sage: SD1 = P3.slice_decomposition(initial_vertex=0) + sage: SD2 = P3.slice_decomposition(initial_vertex=2) + sage: len({SD1: 1, SD2: 2}) # indirect doctest + 2 + """ + return hash((tuple(self.sigma_inv.items()), + tuple(self.lex_label.items()), + tuple(self.xslice_len))) + + def __getitem__(self, v): + r""" + Return the data about the x-slice of the vertex `v`. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A dictionnary with the keys: + + * ``"pivot"`` -- the vertex `v` given as parameter + + * ``"slice"`` -- the slice of `v` (see :meth:`~slice`) + + * ``"active_edges"`` -- the actives edges of `v` (see + :meth:`~active_edges`) + + * ``"lexicographic_label"`` -- the lexicographic label of `v` (see + :meth:`~lexicographic_label`) + + * ``"sequence"`` -- the x-slice sequence of `v` (see + :meth:`~xslice_sequence`) + + This method can also be called via :meth:`xslice_data`. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_data('a') + {'active_edges': [('a', 'b'), + ('a', 'c'), + ('a', 'd'), + ('a', 'e'), + ('a', 'f'), + ('c', 'g'), + ('d', 'g'), + ('f', 'g')], + 'lexicographic_label': ['x'], + 'pivot': 'a', + 'sequence': [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']], + 'slice': ['a', 'b', 'c', 'd', 'e', 'f', 'g']} + sage: SD.xslice_data('u') + {'active_edges': [], + 'lexicographic_label': ['a', 'b', 'c', 'd', 'e', 'f', 'g'], + 'pivot': 'u', + 'sequence': [['u'], ['y', 'z']], + 'slice': ['u', 'y', 'z']} + + Some values of the returned dictionnary can be obtained via other + methods (:meth:`~slice`, :meth:`~xslice_sequence`, + :meth:`~active_edges`, :meth:`~lexicographic_label`):: + + sage: SD.slice('a') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + sage: SD.xslice_data('a')['slice'] + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + sage: SD.xslice_data('a')['sequence'] + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + + sage: SD.active_edges('b') == SD.xslice_data('b')['active_edges'] + True + + sage: SD.lexicographic_label('u') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + sage: SD.xslice_data('u')['lexicographic_label'] + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + TESTS:: + + sage: G = graphs.RandomGNP(15, 0.3) + sage: SD = G.slice_decomposition() + sage: all(SD[v]['slice'] == SD.slice(v) for v in G) + True + sage: all(SD[v]['sequence'] == SD.xslice_sequence(v) for v in G) + True + sage: all(SD[v]['active_edges'] == SD.active_edges(v) for v in G) + True + sage: all(SD[v]['lexicographic_label'] == SD.lexicographic_label(v) for v in G) + True + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD['John'] + Traceback (most recent call last): + ... + LookupError: vertex (John) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice " + "decomposition") + cdef size_t i = self.sigma_inv[v] + return {'pivot': v, + 'slice': self._slice(i), + 'sequence': self._xslice_sequence(i), + 'lexicographic_label': self._xslice_lex_label(i), + 'active_edges': self._xslice_active_edges(i), + } + + def lexBFS_order(self): + r""" + Return the lexBFS order corresponding to the slice decomposition. + + EXAMPLES:: + + sage: from sage.graphs.traversals import _is_valid_lex_BFS_order + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: SD.lexBFS_order() + [0, 1, 4, 5, 2, 6, 3, 9, 7, 8] + sage: _is_valid_lex_BFS_order(G, SD.lexBFS_order()) + True + + TESTS:: + + sage: from sage.graphs.traversals import _is_valid_lex_BFS_order + sage: for _ in range(5): + ....: G = graphs.RandomGNP(15, 0.3) + ....: SD = G.slice_decomposition() + ....: _is_valid_lex_BFS_order(G, SD.lexBFS_order()) + True + True + True + True + True + """ + return list(self.sigma) + + def xslice_data(self, v): + r""" + Return the data about the x-slice of the vertex `v`. + + This method is a wrapper around :meth:`SliceDecomposition.__getitem__` + + TESTS:: + + sage: G = graphs.RandomGNP(15, 0.3) + sage: SD = G.slice_decomposition() + sage: all(SD[v] == SD.xslice_data(v) for v in G) + True + """ + return self[v] + + def slice(self, v): + r""" + Return the slice of the vertex `v`. + + The slice of `v` is the list of vertices `u` such that the neighbors of + `u` that are before `v` in the lexBFS order are that same that the + neighbors of `v` that are before `v` in the lexBFS order (*i.e.*, the + lexicographic label of `v`). It can be shown that it is a factor of the + lexBFS order. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of vertices + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.slice('a') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + The vertices of the slice have the same neighborhood "on the left":: + + sage: pos = lambda v: SD.lexBFS_order().index(v) + sage: lla = set(SD.lexicographic_label('a')) + sage: all(lla == {u for u in G.neighbors(v) if pos(u) < pos('a')} \ + ....: for v in SD.slice('a')) + True + + The slice is a factor of the lexBFS order:: + + sage: ''.join(SD.slice('a')) in ''.join(SD.lexBFS_order()) + True + + The slice of the initial vertex is the whole graph:: + + sage: SD.slice('x') == SD.lexBFS_order() + True + + TESTS:: + + sage: SD.slice('Michael') + Traceback (most recent call last): + ... + LookupError: vertex (Michael) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice " + "decomposition") + cdef size_t i = self.sigma_inv[v] + return self._slice(i) + + def xslice_sequence(self, v): + r""" + Return the x-slice sequence of the vertex `v`. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of list corresponding to the x-slice sequence of ``v``. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_sequence('x') + [['x'], ['a', 'b', 'c', 'd', 'e', 'f', 'g'], ['u', 'y', 'z'], ['v', 'w']] + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + + The flatten x-slice sequence of a vertex corresponds to the slice of the + same vertex:: + + sage: from itertools import chain + sage: all(list(chain(*SD.xslice_sequence(v))) == SD.slice(v) \ + ....: for v in G) + True + + The first list of the sequence is always a singleton containing the + input vertex:: + + sage: all(SD.xslice_sequence(v)[0] == [v] for v in G) + True + + If the length of the slice if more than 1, the second list of the + sequence is either, all the remaining vertices of the slice of `v`, if + `v` is isolated in the subgraph induced by the slice of `v`, or the + neighbors of `v` in the subgraph induced by the slice of `v`:: + + sage: all(SD.xslice_sequence(v)[1] == SD.slice(v)[1:] for v in G \ + ....: if G.subgraph(SD.slice(v)).degree(v) == 0 \ + ....: and len(SD.slice(v)) > 1) + True + sage: for v in G: + ....: if len(SD.slice(v)) > 1: + ....: xslice_seq = SD.xslice_sequence(v) + ....: S = G.subgraph(SD.slice(v)) + ....: if S.degree(v) > 0: + ....: set(xslice_seq[1]) == set(S.neighbor_iterator(v)) + True + True + True + True + + TESTS:: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.xslice_sequence('Terry') + Traceback (most recent call last): + ... + LookupError: vertex (Terry) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice " + "decomposition") + cdef size_t i = self.sigma_inv[v] + return self._xslice_sequence(i) + + def lexicographic_label(self, v): + r""" + Return the lexicographic label of the vertex `v`. + + The lexicographic label of a vertex `v` is the list of all the + neighbors of `v` that appear before `v` in the lexBFS ordering + corresponding to the slice decomposition. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of vertices. + + EXAMPLES:: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.lexicographic_label('f') + ['x', 'a', 'c', 'd'] + sage: pos = lambda v: SD.lexBFS_order().index(v) + sage: set(SD.lexicographic_label('f')) \ + ....: == {v for v in G.neighbors('f') if pos(v) < pos('f')} + True + + TESTS:: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.lexicographic_label('Eric') + Traceback (most recent call last): + ... + LookupError: vertex (Eric) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice " + "decomposition") + cdef size_t i = self.sigma_inv[v] + return self._xslice_lex_label(i) + + def active_edges(self, v): + r""" + Return the active edges of the vertex `v`. + + An edge `(u, w)` is said to be active for `v` if `u` and `w` belongs + to two differents slices of the x-slice sequence of `v`. Note that it + defines a partition of the edges of the underlying graph. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of edges + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + sage: ('c', 'g') in SD.active_edges('a') + True + sage: ('a', 'c') in SD.active_edges('a') + True + sage: ('c', 'd') in SD.active_edges('a') # c and d in same slice + False + sage: ('a', 'u') in SD.active_edges('a') # u not in x-slice of a + False + + The set of active edges of every vertex is a partition of the edges:: + + sage: from itertools import chain + sage: E = list(chain(*(SD.active_edges(v) for v in G))) + sage: G.size() == len(E) == len(set(E)) \ + ....: and all(G.has_edge(u, w) for v in G for u, w in SD.active_edges(v)) + True + + TESTS:: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.active_edges('Graham') + Traceback (most recent call last): + ... + LookupError: vertex (Graham) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice " + "decomposition") + cdef size_t i = self.sigma_inv[v] + return self._xslice_active_edges(i) + + def _slice(self, size_t idx): + r""" + This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.slice(1) + [1, 2] + """ + return list(self.sigma[idx:idx+self.xslice_len[idx]]) + + def _xslice_sequence(self, size_t idx): + r""" + This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.xslice_sequence(0) + [[0], [1, 2], [3], [4]] + """ + cdef size_t l = self.xslice_len[idx] + cdef size_t j = idx + 1 + cdef size_t lj + + S = [ [self.sigma[idx]] ] + while j < idx + l: + lj = self.xslice_len[j] + S.append(list(self.sigma[j:j+lj])) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return S + + def _xslice_lex_label(self, size_t idx): + r""" + This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.lexicographic_label(3) + [1, 2] + """ + return list(self.lex_label[idx]) + + def _xslice_active_edges(self, size_t idx): + r""" + This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.active_edges(0) + [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)] + """ + cdef size_t l = self.xslice_len[idx] + cdef size_t llv_prefix = len(self.lex_label[idx]) + cdef size_t j = idx + 1 + cdef size_t lj + + A = [] + while j < idx + l: + lj = self.xslice_len[j] + llj = self.lex_label[j] + for u in self.sigma[j:j+lj]: + for w in llj[llv_prefix:]: + A.append((w, u)) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return A + + def underlying_graph(self): + r""" + Return the underlying graph corresponding to the slice decomposition. + + If `G` was the graph given as parameter to compute the slice + decomposition, the underlying graph corresponds to ``G.to_simple()`` + where labels are ignored, *i.e.*, it is the input graph without loops, + multiple edges and labels. + + .. NOTE:: + + This method is mostly defined to test the computation of + lexicographic labels and actives edges. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.underlying_graph() == G + True + + The graph can have loops or multiple edges but they are ignored:: + + sage: G = graphs.CubeConnectedCycle(2) # multiple edges + sage: SD = G.slice_decomposition() + sage: SD.underlying_graph() == G.to_simple(immutable=True) + True + + sage: G = graphs.CubeConnectedCycle(1) # loops + sage: SD = G.slice_decomposition() + sage: SD.underlying_graph() == G.to_simple(immutable=True) + True + + TESTS:: + + sage: for _ in range(5): + ....: G = graphs.RandomGNP(15, 0.3) + ....: SD = G.slice_decomposition() + ....: SD.underlying_graph() == G + True + True + True + True + True + """ + if not hasattr(self, '_underlying_graph'): + vertices = self.sigma + edges = [(u, v) for i, v in enumerate(self.sigma) + for u in self.lex_label[i]] + data = [vertices, edges] + Gclass = self._graph_class + self._underlying_graph = Gclass(data, format='vertices_and_edges', + immutable=True) + return self._underlying_graph + + def _repr_(self): + r""" + Return a string representation of a ``SliceDecomposition`` object. + + TESTS:: + + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: repr(SD) + '[0[1[4[5]]] [2[6]] [3] [9] [7] [8]]' + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x'); repr(SD) + '[x[a[b[c[d]] [e[f]]] [g]] [u[y[z]]] [v[w]]]' + """ + def inner_repr(idx): + l = self.xslice_len[idx] + S = [] + if l > 1: + j = idx + 1 + while j < idx + l: + lj = self.xslice_len[j] + S.append(inner_repr(j)) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return f'{self.sigma[idx]}' + ' '.join(f'[{s}]' for s in S) + return f'[{inner_repr(0)}]' + + def _latex_(self): + r""" + Return a string to render, using `\LaTeX`, the slice decomposition as a + tree. + + TESTS:: + + sage: from sage.graphs.graph_latex import check_tkz_graph + sage: check_tkz_graph() # random - depends on Tex installation + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: latex(SD) + \begin{tikzpicture} + ... + v0 -- {l0, v1, v4, v6, v7, v8, v9}; + v1 -- {l1, v2}; + v2 -- {l2, v3}; + v3 -- {l3}; + v4 -- {l4, v5}; + v5 -- {l5}; + v6 -- {l6}; + v7 -- {l7}; + v8 -- {l8}; + v9 -- {l9}; + ... + \end{tikzpicture} + + """ + from sage.misc.latex import latex + + latex.add_package_to_preamble_if_available("tikz") + latex.add_to_preamble(r"\usetikzlibrary{arrows,shapes,fit}") + latex.add_to_preamble(r"\usetikzlibrary{graphs,graphdrawing}") + latex.add_to_preamble(r"\usegdlibrary{trees}") + + # Call latex() on all vertices + sigma_latex = [ latex(v) for v in self.sigma ] + slices = [[] for _ in self.sigma] + + lines = [ r"\begin{tikzpicture}" ] + lines.append(r"\graph [tree layout,level distance=0,level sep=1em," + r"sibling distance=0,sibling sep=0.6em," + r"tail anchor=center,head anchor=north," + r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]") + lines.append("{") + bo, bc = "{", "}" # to write { and } in f-strings + # Create the nodes and leaves of the slice decomposition tree + for i in range(len(self.sigma)): + l = self.xslice_len[i] + label = r"\ ".join(sigma_latex[i:i+l]) + lines.append(f" v{i}[as={bo}${label}${bc}];") + lines.append(f" l{i}[draw=none,as={bo}${sigma_latex[i]}${bc}];") + j = i + 1 + slices[i].append(f"l{i}") + while j < i + l: + slices[i].append(f"v{j}") + j += self.xslice_len[j] + # Create the edges of the slice decomposition tree + for i, S in enumerate(slices): + lines.append(f" v{i} -- " + "{" + ", ".join(S) + "};") + lines.append("};") + # Add dahsed red boxes around xslices + for i, S in enumerate(slices): + fit=" ".join(f"({s})" for s in S) + lines.append(rf"\node (s{i}) [rectangle,inner xsep=0.2em,draw=red," + f"densely dashed,fit={fit}]{bo}{bc};") + + lines.append(r"\end{tikzpicture}") + return "\n".join(lines) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index e8049664aae..812a16e8b4d 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -114,6 +114,7 @@ def wrap_name(x): "cocliques_HoffmannSingleton", "ConwaySmith_for_3S7", "CoxeterGraph", + "CubeplexGraph", "DesarguesGraph", "DejterGraph", "distance_3_doubly_truncated_Golay_code_graph", @@ -173,6 +174,7 @@ def wrap_name(x): "MeredithGraph", "MoebiusKantorGraph", "MoserSpindle", + "MurtyGraph", "NauruGraph", "PappusGraph", "PoussinGraph", @@ -189,12 +191,14 @@ def wrap_name(x): "SzekeresSnarkGraph", "ThomsenGraph", "TietzeGraph", + "TricornGraph", "TruncatedIcosidodecahedralGraph", "TruncatedTetrahedralGraph", "TruncatedWittGraph", "Tutte12Cage", "TutteCoxeterGraph", "TutteGraph", + "TwinplexGraph", "U42Graph216", "U42Graph540", "WagnerGraph", @@ -1007,12 +1011,12 @@ def nauty_geng(self, options='', debug=False): def nauty_genbg(self, options='', debug=False): r""" - Return a generator which creates bipartite graphs from nauty's ``genbg`` + Return a generator which creates bipartite graphs from nauty's ``genbgL`` program. INPUT: - - ``options`` -- string (default: ``""``); a string passed to ``genbg`` + - ``options`` -- string (default: ``""``); a string passed to ``genbgL`` as if it was run at a system command line. At a minimum, you *must* pass the number of vertices you desire in each side. Sage expects the bipartite graphs to be in nauty's "graph6" format, do not set an @@ -1025,12 +1029,12 @@ def nauty_genbg(self, options='', debug=False): the program with some information on the arguments, while a line beginning with ">E" indicates an error with the input. - The possible options, obtained as output of ``genbg --help``:: + The possible options, obtained as output of ``genbgL --help``:: n1 : the number of vertices in the first class. - We must have n1=1..24. + We must have n1=1..30. n2 : the number of vertices in the second class. - We must have n2=0..32 and n1+n2=1..32. + We must have n2=0..64 and n1+n2=1..64. mine:maxe : : a range for the number of edges :0 means ' or more' except in the case 0:0 res/mod : only generate subset res out of subsets 0..mod-1 @@ -1058,8 +1062,8 @@ def nauty_genbg(self, options='', debug=False): -v : display counts by number of edges to stderr -l : canonically label output graphs - Options which cause ``genbg`` to use an output format different than the - ``graph6`` format are not listed above (``-s``, ``-a``) as they will + Options which cause ``genbgL`` to use an output format different than + the ``graph6`` format are not listed above (``-s``, ``-a``) as they will confuse the creation of a Sage graph. Option ``-q`` which suppress auxiliary output (except from ``-v``) should never be used as we are unable to recover the partition of the vertices of the bipartite graph @@ -1113,16 +1117,16 @@ def nauty_genbg(self, options='', debug=False): sage: len(list(gen)) 17 - The ``debug`` switch can be used to examine ``genbg``'s reaction to the + The ``debug`` switch can be used to examine ``genbgL``'s reaction to the input in the ``options`` string. A message starting with ">A" indicates success and a message starting with ">E" indicates a failure:: sage: gen = graphs.nauty_genbg("2 3", debug=True) sage: print(next(gen)) - >A ...genbg n=2+3 e=0:6 d=0:0 D=3:2 + >A ...genbg... n=2+3 e=0:6 d=0:0 D=3:2 sage: gen = graphs.nauty_genbg("-c2 3", debug=True) sage: next(gen) - '>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + '>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... Check that the partition of the bipartite graph is consistent:: @@ -1141,34 +1145,35 @@ def nauty_genbg(self, options='', debug=False): ... ValueError: wrong format of parameter options sage: list(graphs.nauty_genbg("-c1 2", debug=True)) - ['>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + ['>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... sage: list(graphs.nauty_genbg("-c 1 2", debug=True)) - ['>A ...genbg n=1+2 e=2:2 d=1:1 D=2:1 c...\n', Bipartite graph on 3 vertices] + ['>A ...genbg... n=1+2 e=2:2 d=1:1 D=2:1 c...\n', Bipartite graph on 3 vertices] - We must have n1=1..24, n2=0..32 and n1+n2=1..32 (:issue:`34179`):: + We must have n1=1..30, n2=0..64 and n1+n2=1..64 (:issue:`34179`, + :issue:`38618`):: - sage: next(graphs.nauty_genbg("25 1", debug=False)) + sage: next(graphs.nauty_genbg("31 1", debug=False)) Traceback (most recent call last): ... ValueError: wrong format of parameter options - sage: next(graphs.nauty_genbg("25 1", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("24 9", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("1 31", debug=False)) - Bipartite graph on 32 vertices - sage: next(graphs.nauty_genbg("1 32", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("0 32", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... + sage: next(graphs.nauty_genbg("31 1", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("30 40", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("1 63", debug=False)) + Bipartite graph on 64 vertices + sage: next(graphs.nauty_genbg("1 64", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("0 2", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... sage: next(graphs.nauty_genbg("2 0", debug=False)) Bipartite graph on 2 vertices sage: next(graphs.nauty_genbg("2 -1", debug=True)) - '>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + '>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... """ import shlex from sage.features.nauty import NautyExecutable - genbg_path = NautyExecutable("genbg").absolute_filename() + genbg_path = NautyExecutable("genbgL").absolute_filename() sp = subprocess.Popen(shlex.quote(genbg_path) + " {0}".format(options), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, @@ -1200,7 +1205,7 @@ def nauty_genbg(self, options='', debug=False): try: s = next(gen) except StopIteration: - # Exhausted list of bipartite graphs from nauty genbg + # Exhausted list of bipartite graphs from nauty genbgL return G = BipartiteGraph(s[:-1], format='graph6', partition=partition) yield G @@ -2542,6 +2547,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None cocliques_HoffmannSingleton = staticmethod(distance_regular.cocliques_HoffmannSingleton) ConwaySmith_for_3S7 = staticmethod(distance_regular.ConwaySmith_for_3S7) CoxeterGraph = staticmethod(smallgraphs.CoxeterGraph) + CubeplexGraph = staticmethod(smallgraphs.CubeplexGraph) DejterGraph = staticmethod(smallgraphs.DejterGraph) DesarguesGraph = staticmethod(smallgraphs.DesarguesGraph) distance_3_doubly_truncated_Golay_code_graph = staticmethod(distance_regular.distance_3_doubly_truncated_Golay_code_graph) @@ -2602,6 +2608,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None MeredithGraph = staticmethod(smallgraphs.MeredithGraph) MoebiusKantorGraph = staticmethod(smallgraphs.MoebiusKantorGraph) MoserSpindle = staticmethod(smallgraphs.MoserSpindle) + MurtyGraph = staticmethod(smallgraphs.MurtyGraph) NauruGraph = staticmethod(smallgraphs.NauruGraph) PappusGraph = staticmethod(smallgraphs.PappusGraph) PoussinGraph = staticmethod(smallgraphs.PoussinGraph) @@ -2618,12 +2625,14 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None SzekeresSnarkGraph = staticmethod(smallgraphs.SzekeresSnarkGraph) ThomsenGraph = staticmethod(smallgraphs.ThomsenGraph) TietzeGraph = staticmethod(smallgraphs.TietzeGraph) + TricornGraph = staticmethod(smallgraphs.TricornGraph) Tutte12Cage = staticmethod(smallgraphs.Tutte12Cage) TruncatedIcosidodecahedralGraph = staticmethod(smallgraphs.TruncatedIcosidodecahedralGraph) TruncatedTetrahedralGraph = staticmethod(smallgraphs.TruncatedTetrahedralGraph) TruncatedWittGraph = staticmethod(distance_regular.TruncatedWittGraph) TutteCoxeterGraph = staticmethod(smallgraphs.TutteCoxeterGraph) TutteGraph = staticmethod(smallgraphs.TutteGraph) + TwinplexGraph = staticmethod(smallgraphs.TwinplexGraph) U42Graph216 = staticmethod(smallgraphs.U42Graph216) U42Graph540 = staticmethod(smallgraphs.U42Graph540) WagnerGraph = staticmethod(smallgraphs.WagnerGraph) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 432cce4ec99..678fdbcf32e 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -144,6 +144,13 @@ def from_dig6(G, dig6_string): sage: from_dig6(g, digraphs.Circuit(10).dig6_string()) sage: g.is_isomorphic(digraphs.Circuit(10)) True + + The string may represent a directed graph with loops:: + + sage: L = DiGraph(loops=True) + sage: from_dig6(L, 'CW`C') + sage: L.edges(labels=False, sort=True) + [(0, 1), (0, 2), (1, 2), (2, 3), (3, 3)] """ from .generic_graph_pyx import length_and_string_from_graph6, binary_string_from_dig6 if isinstance(dig6_string, bytes): diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py index 4606c56b4f9..f0fb9329002 100644 --- a/src/sage/graphs/graph_latex.py +++ b/src/sage/graphs/graph_latex.py @@ -1170,7 +1170,7 @@ def set_option(self, option_name, option_value=None): raise TypeError('%s option must be a dictionary, not %s' % (name, value)) else: for key, x in value.items(): - if not type(x) in [int, Integer, float, RealLiteral] or not x >= 0.0: + if type(x) not in [int, Integer, float, RealLiteral] or not x >= 0.0: raise ValueError('%s option for %s needs to be a positive number, not %s' % (name, key, x)) elif name in boolean_dicts: if not isinstance(value, dict): @@ -1562,6 +1562,21 @@ def tkz_picture(self): % % \end{tikzpicture} + + For a complicated vertex, a TeX box is used. :: + + sage: B = crystals.Tableaux(['B', 2], shape=[1]) + sage: latex(B) + \begin{tikzpicture} + ... + \newsavebox{\vertex} + \sbox{\vertex}{${\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{1}c}\cline{1-1} + \lr{1}\\\cline{1-1} + \end{array}$} + }$}\Vertex[style={minimum size=1.0cm,draw=cv0,fill=cfv0,text=clv0,shape=circle},LabelOut=false,L=\usebox{\vertex},x=...,y=...]{v0} + ... + \end{tikzpicture} """ # This routine does not handle multiple edges # It will properly handle digraphs where a pair of vertices has an edge @@ -1938,48 +1953,66 @@ def translate(p): s += [str(round(el_color[edge][2], 4)), '}\n'] s += ['%\n'] - # Create each vertex + # Create vertices + v = [] + box = '' + used = False for u in vertex_list: - s += ['\\Vertex['] + t = [r'\Vertex['] # colors, shapes, sizes, labels/placement for 'Custom' style if customized: - s += ['style={'] # begin style list - s += ['minimum size=', str(round(float(scale * v_size[u]), 4)), + t += ['style={'] # begin style list + t += ['minimum size=', str(round(float(scale * v_size[u]), 4)), units, ','] - s += ['draw=', vertex_color_names[u], ','] - s += ['fill=', vertex_fill_color_names[u], ','] + t += ['draw=', vertex_color_names[u], ','] + t += ['fill=', vertex_fill_color_names[u], ','] if vertex_labels: - s += ['text=', vertex_label_color_names[u], ','] + t += ['text=', vertex_label_color_names[u], ','] if v_shape[u] == 'sphere': - s += ['shape=circle,shading=ball,line width=0pt,ball color=', vertex_color_names[u], ','] + t += ['shape=circle,shading=ball,line width=0pt,ball color=', vertex_color_names[u], ','] else: - s += ['shape=', v_shape[u]] - s += ['},'] # end style list + t += ['shape=', v_shape[u]] + t += ['},'] # end style list if vertex_labels: if vl_placement[u] == 'center': - s += ['LabelOut=false,'] + t += ['LabelOut=false,'] else: - s += ['LabelOut=true,'] - s += ['Ldist=', str(round(float(scale * vl_placement[u][0]), 4)), units, ','] - s += ['Lpos=', str(round(float(vl_placement[u][1]), 4)), ','] # degrees, no units + t += ['LabelOut=true,'] + t += ['Ldist=', str(round(float(scale * vl_placement[u][0]), 4)), units, ','] + t += ['Lpos=', str(round(float(vl_placement[u][1]), 4)), ','] # degrees, no units else: - s += ['NoLabel,'] + t += ['NoLabel,'] # vertex label information is available to all pre-built styles # but may be ignored by the style, so not apparent if vertex_labels or not customized: if vertex_labels_math and not (isinstance(u, str) and u[0] == '$' and u[-1] == '$'): - lab = r'\hbox{$%s$}' % latex(u) + ltx = str(latex(u)) + if '\\' in ltx: # complicated case; use \sbox + box = r'\sbox{\vertex}{$' + ltx + '$}' + lab = r'\usebox{\vertex}' + else: + lab = r'\hbox{$%s$}' % ltx else: lab = r'\hbox{%s}' % u - s += ['L=', lab, ','] + t += ['L=', lab, ','] scaled_pos = translate(pos[u]) - s += ['x=', str(round(float(scale * scaled_pos[0]), 4)), units, ','] - s += ['y=', str(round(float(scale * scaled_pos[1]), 4)), units] - s += [']'] - s += ['{', prefix, str(index_of_vertex[u]), '}\n'] + t += ['x=', str(round(float(scale * scaled_pos[0]), 4)), units, ','] + t += ['y=', str(round(float(scale * scaled_pos[1]), 4)), units] + t += [']'] + t += ['{', prefix, str(index_of_vertex[u]), '}\n'] + if box: + v += [box] + t + box = '' + used = True + else: + v += t + if used: + s += [r'\newsavebox{\vertex}' + '\n'] + v + else: + s += v s += ['%\n'] - # Create each edge or loop + # Create edges and loops for e in self._graph.edges(sort=False): edge = (e[0], e[1]) loop = e[0] == e[1] diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index c97a5892efe..40b6ecc764d 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -1491,7 +1491,7 @@ def layout_tree(self, root, orientation): children = {root: T.neighbors(root)} # Always make a copy of the children because they get eaten - stack = [[u for u in children[root]]] + stack = [list(children[root])] stick = [root] parent = {u: root for u in children[root]} pos = {} diff --git a/src/sage/graphs/graph_plot_js.py b/src/sage/graphs/graph_plot_js.py index 2c439a56dec..7ef9057d3b2 100644 --- a/src/sage/graphs/graph_plot_js.py +++ b/src/sage/graphs/graph_plot_js.py @@ -73,9 +73,9 @@ Functions --------- """ +from pathlib import Path from sage.misc.temporary_file import tmp_filename from sage.misc.lazy_import import lazy_import -import os lazy_import("sage.plot.colors", "rainbow") # **************************************************************************** @@ -317,14 +317,13 @@ def gen_html_code(G, "edge_thickness": int(edge_thickness)}) from sage.env import SAGE_EXTCODE, SAGE_SHARE - js_code_file = open(SAGE_EXTCODE + "/graphs/graph_plot_js.html", 'r') - js_code = js_code_file.read().replace("// GRAPH_DATA_HEREEEEEEEEEEE", string) - js_code_file.close() + with open(Path(SAGE_EXTCODE) / "graphs" / "graph_plot_js.html") as f: + js_code = f.read().replace("// GRAPH_DATA_HEREEEEEEEEEEE", string) # Add d3.js script depending on whether d3js package is installed. - d3js_filepath = os.path.join(SAGE_SHARE, 'd3js', 'd3.min.js') - if os.path.exists(d3js_filepath): - with open(d3js_filepath, 'r') as d3js_code_file: + d3js_filepath = Path(SAGE_SHARE) / 'd3js' / 'd3.min.js' + if d3js_filepath.exists(): + with open(d3js_filepath) as d3js_code_file: d3js_script = '' else: d3js_script = '' diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index c24c8d3334f..bf24e7d3d97 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -418,8 +418,7 @@ cdef inline distances_and_far_apart_pairs(gg, # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t** p_vertices = sd.neighbors cdef uint32_t* p_tmp cdef uint32_t* end diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index abbab8fac9b..f8bcf801d7e 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -832,7 +832,7 @@ def _parse_db(self): inclusions = DB['Inclusions']['incl'] # Parses the list of ISGCI small graphs - smallgraph_file = open(os.path.join(data_dir, _SMALLGRAPHS_FILE), 'r') + smallgraph_file = open(os.path.join(data_dir, _SMALLGRAPHS_FILE)) smallgraphs = {} for line in smallgraph_file.readlines(): diff --git a/src/sage/graphs/isoperimetric_inequalities.pyx b/src/sage/graphs/isoperimetric_inequalities.pyx index 5fd13cade15..731cfc00040 100644 --- a/src/sage/graphs/isoperimetric_inequalities.pyx +++ b/src/sage/graphs/isoperimetric_inequalities.pyx @@ -110,8 +110,7 @@ def cheeger_constant(g): cdef unsigned long vmin = 1 # value of the volume for the min cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g), - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) subgraph = check_malloc(sd.n * sizeof(int)) bitsubgraph = check_malloc(sd.n * sizeof(int)) @@ -245,8 +244,7 @@ def edge_isoperimetric_number(g): cdef int u = 0 # current vertex cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g), - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) cdef unsigned long bmin = sd.neighbors[1] - sd.neighbors[0] # value of boundary for the min cdef unsigned long vmin = 1 # value of the volume for the min diff --git a/src/sage/graphs/matching.py b/src/sage/graphs/matching.py new file mode 100644 index 00000000000..457ccc16a75 --- /dev/null +++ b/src/sage/graphs/matching.py @@ -0,0 +1,1641 @@ +r""" +Matching + +This module implements the functions pertaining to matching of undirected +graphs. A *matching* in a graph is a set of pairwise nonadjacent links +(nonloop edges). In other words, a matching in a graph is the edge set of an +1-regular subgraph. A matching is called a *perfect* *matching* if it the +subgraph generated by a set of matching edges spans the graph, i.e. it's the +edge set of an 1-regular spanning subgraph. + +The implemented methods are listed below: + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~has_perfect_matching` | Return whether the graph has a perfect matching + :meth:`~is_bicritical` | Check if the graph is bicritical + :meth:`~is_factor_critical` | Check whether the graph is factor-critical + :meth:`~is_matching_covered` | Check if the graph is matching covered + :meth:`~matching` | Return a maximum weighted matching of the graph represented by the list of its edges + :meth:`~perfect_matchings` | Return an iterator over all perfect matchings of the graph + :meth:`~M_alternating_even_mark` | Return the vertices reachable from the provided vertex via an even alternating path starting with a non-matching edge + +AUTHORS: + +- Robert L. Miller (2006-10-22): initial implementations + +- Janmenjaya Panda (2024-06-17): added + :meth:`~M_alternating_even_mark`, + :meth:`~is_bicritical` and + :meth:`~is_matching_covered` + + +Methods +------- +""" + +# **************************************************************************** +# Copyright (C) 2006 Robert L. Miller +# 2024 Janmenjaya Panda +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.rings.integer import Integer +from sage.graphs.views import EdgesView + + +def has_perfect_matching(G, algorithm='Edmonds', solver=None, verbose=0, + *, integrality_tolerance=1e-3): + r""" + Return whether the graph has a perfect matching + + INPUT: + + - ``algorithm`` -- string (default: ``'Edmonds'``) + + - ``'Edmonds'`` uses Edmonds' algorithm as implemented in NetworkX to + find a matching of maximal cardinality, then check whether this + cardinality is half the number of vertices of the graph. + + - ``'LP_matching'`` uses a Linear Program to find a matching of + maximal cardinality, then check whether this cardinality is half the + number of vertices of the graph. + + - ``'LP'`` uses a Linear Program formulation of the perfect matching + problem: put a binary variable ``b[e]`` on each edge `e`, and for + each vertex `v`, require that the sum of the values of the edges + incident to `v` is 1. + + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer + Linear Programming (MILP) solver to be used. If set to ``None``, the + default one is used. For more information on MILP solvers and which + default solver is used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: 0); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when + ``algorithm == "LP_matching"`` or ``algorithm == "LP"``) + + - ``integrality_tolerance`` -- float; parameter for use with MILP + solvers over an inexact base ring; see + :meth:`MixedIntegerLinearProgram.get_values`. + + OUTPUT: boolean + + EXAMPLES:: + + sage: graphs.PetersenGraph().has_perfect_matching() # needs networkx + True + sage: graphs.WheelGraph(6).has_perfect_matching() # needs networkx + True + sage: graphs.WheelGraph(5).has_perfect_matching() # needs networkx + False + sage: graphs.PetersenGraph().has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip + True + sage: graphs.WheelGraph(6).has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip + True + sage: graphs.WheelGraph(5).has_perfect_matching(algorithm='LP_matching') + False + sage: graphs.PetersenGraph().has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip + True + sage: graphs.WheelGraph(6).has_perfect_matching(algorithm='LP_matching') # needs sage.numerical.mip + True + sage: graphs.WheelGraph(5).has_perfect_matching(algorithm='LP_matching') + False + + TESTS:: + + sage: G = graphs.EmptyGraph() + sage: all(G.has_perfect_matching(algorithm=algo) # needs networkx + ....: for algo in ['Edmonds', 'LP_matching', 'LP']) + True + + Be careful with isolated vertices:: + + sage: G = graphs.PetersenGraph() + sage: G.add_vertex(11) + sage: any(G.has_perfect_matching(algorithm=algo) # needs networkx + ....: for algo in ['Edmonds', 'LP_matching', 'LP']) + False + """ + if G.order() % 2: + return False + + if algorithm == "Edmonds": + return len(G) == 2*G.matching(value_only=True, + use_edge_labels=False, + algorithm='Edmonds') + elif algorithm == "LP_matching": + return len(G) == 2*G.matching(value_only=True, + use_edge_labels=False, + algorithm='LP', + solver=solver, + verbose=verbose, + integrality_tolerance=integrality_tolerance) + elif algorithm == "LP": + from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException + p = MixedIntegerLinearProgram(solver=solver) + b = p.new_variable(binary=True) + for v in G: + edges = G.edges_incident(v, labels=False) + if not edges: + return False + p.add_constraint(p.sum(b[frozenset(e)] for e in edges) == 1) + try: + p.solve(log=verbose) + return True + except MIPSolverException: + return False + raise ValueError('algorithm must be set to "Edmonds", "LP_matching" or "LP"') + + +def is_bicritical(G, matching=None, algorithm='Edmonds', coNP_certificate=False, + solver=None, verbose=0, *, integrality_tolerance=0.001): + r""" + Check if the graph is bicritical + + A nontrivial graph `G` is *bicritical* if `G - u - v` has a perfect + matching for any two distinct vertices `u` and `v` of `G`. Bicritical + graphs are special kind of matching covered graphs. Each maximal barrier of + a bicritical graph is a singleton. Thus, for a bicritical graph, the + canonical partition of the vertex set is the set of sets where each set is + an indiviudal vertex. Three-connected bicritical graphs, aka *bricks*, play + an important role in the theory of matching covered graphs. + + This method implements the algorithm proposed in [LZ2001]_ and we + assume that a connected graph of order two is bicritical, whereas a + disconnected graph of the same order is not. The time complexity of + the algorithm is `\mathcal{O}(|V| \cdot |E|)`, if a perfect matching of + the graph is given, where `|V|` and `|E|` are the order and the size of + the graph respectively. Otherwise, time complexity may be dominated by + the time needed to compute a maximum matching of the graph. + + Note that a :class:`ValueError` is returned if the graph has loops or if + the graph is trivial, i.e., it has at most one vertex. + + INPUT: + + - ``matching`` -- (default: ``None``); a perfect matching of the + graph, that can be given using any valid input format of + :class:`~sage.graphs.graph.Graph`. + + If set to ``None``, a matching is computed using the other parameters. + + - ``algorithm`` -- string (default: ``'Edmonds'``); the algorithm to be + used to compute a maximum matching of the graph among + + - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX, + + - ``'LP'`` uses a Linear Program formulation of the matching problem. + + - ``coNP_certificate`` -- boolean (default: ``False``); if set to + ``True`` a set of pair of vertices (say `u` and `v`) is returned such + that `G - u - v` does not have a perfect matching if `G` is not + bicritical or otherwise ``None`` is returned. + + - ``solver`` -- string (default: ``None``); specify a Mixed Integer + Linear Programming (MILP) solver to be used. If set to ``None``, the + default one is used. For more information on MILP solvers and which + default solver is used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: ``0``); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when ``algorithm + == 'LP'``). + + - ``integrality_tolerance`` -- float; parameter for use with MILP + solvers over an inexact base ring; see + :meth:`MixedIntegerLinearProgram.get_values`. + + OUTPUT: + + - A boolean indicating whether the graph is bicritical or not. + + - If ``coNP_certificate`` is set to ``True``, a set of pair of vertices + is returned in case the graph is not bicritical otherwise ``None`` is + returned. + + EXAMPLES: + + The Petersen graph is bicritical:: + + sage: G = graphs.PetersenGraph() + sage: G.is_bicritical() + True + + A graph (without a self-loop) is bicritical if and only if the underlying + simple graph is bicritical:: + + sage: G = graphs.PetersenGraph() + sage: G.allow_multiple_edges(True) + sage: G.add_edge(0, 5) + sage: G.is_bicritical() + True + + A nontrivial circular ladder graph whose order is not divisible by 4 is bicritical:: + + sage: G = graphs.CircularLadderGraph(5) + sage: G.is_bicritical() + True + + The graph obtained by splicing two bicritical graph is also bicritical. + For instance, `K_4` with one extra (multiple) edge (say `G := K_4^+`) is + bicritical. Let `H := K_4^+ \odot K_4^+` such that `H` is free of multiple + edge. The graph `H` is also bicritical:: + + sage: G = graphs.CompleteGraph(4) + sage: G.allow_multiple_edges(True) + sage: G.add_edge(0, 1) + sage: G.is_bicritical() + True + sage: H = Graph() + sage: H.add_edges([ + ....: (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), + ....: (1, 5), (2, 5), (3, 4), (3, 5), (4, 5) + ....: ]) + sage: H.is_bicritical() + True + + A graph (of order more than two) with more that one component is not bicritical:: + + sage: cycle1 = graphs.CycleGraph(4) + sage: cycle2 = graphs.CycleGraph(6) + sage: cycle2.relabel(lambda v: v + 4) + sage: G = Graph() + sage: G.add_edges(cycle1.edges() + cycle2.edges()) + sage: len(G.connected_components(sort=False)) + 2 + sage: G.is_bicritical() + False + + A graph (of order more than two) with a cut-vertex is not bicritical:: + + sage: G = graphs.CycleGraph(6) + sage: G.add_edges([(5, 6), (5, 7), (6, 7)]) + sage: G.is_cut_vertex(5) + True + sage: G.has_perfect_matching() + True + sage: G.is_bicritical() + False + + A connected graph of order two is assumed to be bicritical, whereas the + disconnected graph of the same order is not:: + + sage: G = graphs.CompleteBipartiteGraph(1, 1) + sage: G.is_bicritical() + True + sage: G = graphs.CompleteBipartiteGraph(2, 0) + sage: G.is_bicritical() + False + + A bipartite graph of order three or more is not bicritical:: + + sage: G = graphs.CompleteBipartiteGraph(3, 3) + sage: G.has_perfect_matching() + True + sage: G.is_bicritical() + False + + One may specify a matching:: + + sage: G = graphs.WheelGraph(10) + sage: M = G.matching() + sage: G.is_bicritical(matching=M) + True + sage: H = graphs.HexahedralGraph() + sage: N = H.matching() + sage: H.is_bicritical(matching=N) + False + + One may ask for a co-`\mathcal{NP}` certificate:: + + sage: G = graphs.CompleteGraph(14) + sage: G.is_bicritical(coNP_certificate=True) + (True, None) + sage: H = graphs.CircularLadderGraph(20) + sage: M = H.matching() + sage: H.is_bicritical(matching=M, coNP_certificate=True) + (False, {0, 2}) + + TESTS: + + If the graph is trivial:: + + sage: G = Graph() + sage: G.is_bicritical() + Traceback (most recent call last): + ... + ValueError: the graph is trivial + sage: H = graphs.CycleGraph(1) + sage: H.is_bicritical() + Traceback (most recent call last): + ... + ValueError: the graph is trivial + + Providing with a wrong matching:: + + sage: G = graphs.CompleteGraph(6) + sage: M = Graph(G.matching()) + sage: M.add_edges([(0, 1), (0, 2)]) + sage: G.is_bicritical(matching=M) + Traceback (most recent call last): + ... + ValueError: the input is not a matching + sage: N = Graph(G.matching()) + sage: N.add_edge(6, 7) + sage: G.is_bicritical(matching=N) + Traceback (most recent call last): + ... + ValueError: the input is not a matching of the graph + sage: J = Graph() + sage: J.add_edges([(0, 1), (2, 3)]) + sage: G.is_bicritical(matching=J) + Traceback (most recent call last): + ... + ValueError: the input is not a perfect matching of the graph + + Providing with a graph with a self-loop:: + + sage: G = graphs.CompleteGraph(4) + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.is_bicritical() + Traceback (most recent call last): + ... + ValueError: This method is not known to work on graphs with loops. + Perhaps this method can be updated to handle them, but in the meantime + if you want to use it please disallow loops using allow_loops(). + + REFERENCES: + + - [LM2024]_ + + - [LZ2001]_ + + .. SEEALSO:: + :meth:`~sage.graphs.graph.Graph.is_factor_critical`, + :meth:`~sage.graphs.graph.Graph.is_matching_covered` + + AUTHORS: + + - Janmenjaya Panda (2024-06-17) + """ + # The graph must be simple + G._scream_if_not_simple(allow_multiple_edges=True) + + # The graph must be nontrivial + if G.order() < 2: + raise ValueError("the graph is trivial") + + # A graph of order two is assumed to be bicritical + if G.order() == 2: + if G.is_connected(): + return (True, None) if coNP_certificate else True + else: + return (False, None) if coNP_certificate else False + + # The graph must have an even number of vertices + if G.order() % 2: + return (False, set(list(G)[:2])) if coNP_certificate else False + + # The graph must be connected + if not G.is_connected(): + if not coNP_certificate: + return False + + components = G.connected_components(sort=False) + + # Check if there is an odd component with at least three vertices + for component in components: + if len(component) % 2 and len(component) > 2: + return (False, set(component[:2])) + + # Check if there are at least two even components + components_of_even_order = [component for component in components if len(component) % 2 == 0] + if len(components_of_even_order) > 1: + return (False, set([components_of_even_order[0][0], components_of_even_order[1][0]])) + + # Or otherwise there is at most one even component with at least two trivial odd components + u, v = None, None + + for component in components: + if u is not None and not len(component) % 2: + v = component[0] + return (False, set([u, v])) + elif len(component) == 1: + u = component[0] + + # Bipartite graphs of order at least three are not bicritical + if G.is_bipartite(): + if not coNP_certificate: + return False + + A, B = G.bipartite_sets() + + if len(A) > 1: + return (False, set(list(A)[:2])) + return (False, set(list(B)[:2])) + + # A graph (without a self-loop) is bicritical if and only if the underlying + # simple graph is bicritical + G_simple = G.to_simple() + + from sage.graphs.graph import Graph + if matching: + # The input matching must be a valid perfect matching of the graph + M = Graph(matching) + if any(d != 1 for d in M.degree()): + raise ValueError("the input is not a matching") + if any(not G_simple.has_edge(edge) for edge in M.edge_iterator()): + raise ValueError("the input is not a matching of the graph") + if (G_simple.order() != M.order()) or (G_simple.order() != 2*M.size()): + raise ValueError("the input is not a perfect matching of the graph") + else: + # A maximum matching of the graph is computed + M = Graph(G_simple.matching(algorithm=algorithm, solver=solver, verbose=verbose, + integrality_tolerance=integrality_tolerance)) + + # It must be a perfect matching + if G_simple.order() != M.order(): + u, v = next(M.edge_iterator(labels=False)) + return (False, set([u, v])) if coNP_certificate else False + + # G is bicritical if and only if for each vertex u with its M-matched neighbor being v, + # every vertex of the graph distinct from v must be reachable from u through an even length + # M-alternating uv-path starting with an edge not in M and ending with an edge in M + + for u in G_simple: + v = next(M.neighbor_iterator(u)) + + even = M_alternating_even_mark(G_simple, u, M) + + for w in G_simple: + if w != v and w not in even: + return (False, set([v, w])) if coNP_certificate else False + + return (True, None) if coNP_certificate else True + + +def is_factor_critical(G, matching=None, algorithm='Edmonds', solver=None, verbose=0, + *, integrality_tolerance=0.001): + r""" + Check whether the graph is factor-critical. + + A graph of order `n` is *factor-critical* if every subgraph of `n-1` + vertices have a perfect matching, hence `n` must be odd. See + :wikipedia:`Factor-critical_graph` for more details. + + This method implements the algorithm proposed in [LR2004]_ and we assume + that a graph of order one is factor-critical. The time complexity of the + algorithm is linear if a near perfect matching is given as input (i.e., + a matching such that all vertices but one are incident to an edge of the + matching). Otherwise, the time complexity is dominated by the time + needed to compute a maximum matching of the graph. + + INPUT: + + - ``matching`` -- (default: ``None``); a near perfect matching of the + graph, that is a matching such that all vertices of the graph but one + are incident to an edge of the matching. It can be given using any + valid input format of :class:`~sage.graphs.graph.Graph`. + + If set to ``None``, a matching is computed using the other parameters. + + - ``algorithm`` -- string (default: ``'Edmonds'``); the algorithm to use + to compute a maximum matching of the graph among + + - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX + + - ``'LP'`` uses a Linear Program formulation of the matching problem + + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer + Linear Programming (MILP) solver to be used. If set to ``None``, the + default one is used. For more information on MILP solvers and which + default solver is used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: 0); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when ``algorithm + == "LP"``) + + - ``integrality_tolerance`` -- float; parameter for use with MILP + solvers over an inexact base ring; see + :meth:`MixedIntegerLinearProgram.get_values`. + + EXAMPLES: + + Odd length cycles and odd cliques of order at least 3 are + factor-critical graphs:: + + sage: [graphs.CycleGraph(2*i + 1).is_factor_critical() for i in range(5)] # needs networkx + [True, True, True, True, True] + sage: [graphs.CompleteGraph(2*i + 1).is_factor_critical() for i in range(5)] # needs networkx + [True, True, True, True, True] + + More generally, every Hamiltonian graph with an odd number of vertices + is factor-critical:: + + sage: G = graphs.RandomGNP(15, .2) + sage: G.add_path([0..14]) + sage: G.add_edge(14, 0) + sage: G.is_hamiltonian() + True + sage: G.is_factor_critical() # needs networkx + True + + Friendship graphs are non-Hamiltonian factor-critical graphs:: + + sage: [graphs.FriendshipGraph(i).is_factor_critical() for i in range(1, 5)] # needs networkx + [True, True, True, True] + + Bipartite graphs are not factor-critical:: + + sage: G = graphs.RandomBipartite(randint(1, 10), randint(1, 10), .5) # needs numpy + sage: G.is_factor_critical() # needs numpy + False + + Graphs with even order are not factor critical:: + + sage: G = graphs.RandomGNP(10, .5) + sage: G.is_factor_critical() + False + + One can specify a matching:: + + sage: F = graphs.FriendshipGraph(4) + sage: M = F.matching() # needs networkx + sage: F.is_factor_critical(matching=M) # needs networkx + True + sage: F.is_factor_critical(matching=Graph(M)) # needs networkx + True + + TESTS: + + Giving a wrong matching:: + + sage: G = graphs.RandomGNP(15, .3) + sage: while not G.is_biconnected(): + ....: G = graphs.RandomGNP(15, .3) + sage: M = G.matching() # needs networkx + sage: G.is_factor_critical(matching=M[:-1]) # needs networkx + Traceback (most recent call last): + ... + ValueError: the input is not a near perfect matching of the graph + sage: G.is_factor_critical(matching=G.edges(sort=True)) + Traceback (most recent call last): + ... + ValueError: the input is not a matching + sage: M = [(2*i, 2*i + 1) for i in range(9)] + sage: G.is_factor_critical(matching=M) + Traceback (most recent call last): + ... + ValueError: the input is not a matching of the graph + """ + if G.order() == 1: + return True + + # The graph must have an odd number of vertices, be 2-edge connected, so + # without bridges, and not bipartite + if (not G.order() % 2 or not G.is_connected() or + list(G.bridges()) or G.is_bipartite()): + return False + + from sage.graphs.graph import Graph + if matching: + # We check that the input matching is a valid near perfect matching + # of the graph. + M = Graph(matching) + if any(d != 1 for d in M.degree()): + raise ValueError("the input is not a matching") + if not M.is_subgraph(G, induced=False): + raise ValueError("the input is not a matching of the graph") + if (G.order() != M.order() + 1) or (G.order() != 2*M.size() + 1): + raise ValueError("the input is not a near perfect matching of the graph") + else: + # We compute a maximum matching of the graph + M = Graph(G.matching(algorithm=algorithm, solver=solver, verbose=verbose, + integrality_tolerance=integrality_tolerance)) + + # It must be a near-perfect matching + if G.order() != M.order() + 1: + return False + + # We find the unsaturated vertex u, i.e., the only vertex of the graph + # not in M + for u in G: + if u not in M: + break + + # We virtually build an M-alternating tree T + from queue import Queue + Q = Queue() + Q.put(u) + even = set([u]) + odd = set() + pred = {u: u} + rank = {u: 0} + + while not Q.empty(): + x = Q.get() + for y in G.neighbor_iterator(x): + if y in odd: + continue + elif y in even: + # Search for the nearest common ancestor t of x and y + P = [x] + R = [y] + while P[-1] != R[-1]: + if rank[P[-1]] > rank[R[-1]]: + P.append(pred[P[-1]]) + elif rank[P[-1]] < rank[R[-1]]: + R.append(pred[R[-1]]) + else: + P.append(pred[P[-1]]) + R.append(pred[R[-1]]) + t = P.pop() + R.pop() + # Set t as pred of all vertices of the chains and add + # vertices marked odd to the queue + import itertools + + for a in itertools.chain(P, R): + pred[a] = t + rank[a] = rank[t] + 1 + if a in odd: + even.add(a) + odd.discard(a) + Q.put(a) + else: # y has not been visited yet + z = next(M.neighbor_iterator(y)) + odd.add(y) + even.add(z) + Q.put(z) + pred[y] = x + pred[z] = y + rank[y] = rank[x] + 1 + rank[z] = rank[y] + 1 + + # The graph is factor critical if all vertices are marked even + return len(even) == G.order() + + +def is_matching_covered(G, matching=None, algorithm='Edmonds', coNP_certificate=False, + solver=None, verbose=0, *, integrality_tolerance=0.001): + r""" + Check if the graph is matching covered. + + A connected nontrivial graph wherein each edge participates in some + perfect matching is called a *matching* *covered* *graph*. + + If a perfect matching of the graph is provided, for bipartite graph, + this method implements a linear time algorithm as proposed in [LM2024]_ + that is based on the following theorem: + + Given a connected bipartite graph `G[A, B]` with a perfect matching + `M`. Construct a directed graph `D` from `G` such that `V(D) := V(G)` + and for each edge in `G` direct the corresponding edge from `A` to `B` + in `D`, if it is in `M` or otherwise direct it from `B` to `A`. The + graph `G` is matching covered if and only if `D` is strongly connected. + + For nonbipartite graph, if a perfect matching of the graph is provided, + this method implements an `\mathcal{O}(|V| \cdot |E|)` algorithm, where + `|V|` and `|E|` are the order and the size of the graph respectively. + This implementation is inspired by the `M`-`alternating` `tree` `search` + method explained in [LZ2001]_. For nonbipartite graph, the + implementation is based on the following theorem: + + Given a nonbipartite graph `G` with a perfect matching `M`. The + graph `G` is matching covered if and only if for each edge `uv` + not in `M`, there exists an `M`-`alternating` odd length `uv`-path + starting and ending with edges not in `M`. + + The time complexity may be dominated by the time needed to compute a + maximum matching of the graph, in case a perfect matching is not + provided. Also, note that for a disconnected or a trivial or a + graph with a loop, a :class:`ValueError` is returned. + + INPUT: + + - ``matching`` -- (default: ``None``); a perfect matching of the + graph, that can be given using any valid input format of + :class:`~sage.graphs.graph.Graph`. + + If set to ``None``, a matching is computed using the other parameters. + + - ``algorithm`` -- string (default: ``'Edmonds'``); the algorithm to be + used to compute a maximum matching of the graph among + + - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX, + + - ``'LP'`` uses a Linear Program formulation of the matching problem. + + - ``coNP_certificate`` -- boolean (default: ``False``); if set to + ``True`` an edge of the graph, that does not participate in any + perfect matching, is returned if `G` is not matching covered or + otherwise ``None`` is returned. + + - ``solver`` -- string (default: ``None``); specify a Mixed Integer + Linear Programming (MILP) solver to be used. If set to ``None``, the + default one is used. For more information on MILP solvers and which + default solver is used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: ``0``); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when ``algorithm + == 'LP'``). + + - ``integrality_tolerance`` -- float; parameter for use with MILP + solvers over an inexact base ring; see + :meth:`MixedIntegerLinearProgram.get_values`. + + OUTPUT: + + - A boolean indicating whether the graph is matching covered or not. + + - If ``coNP_certificate`` is set to ``True``, an edge is returned in + case the graph is not matching covered otherwise ``None`` is + returned. + + EXAMPLES: + + The Petersen graph is matching covered:: + + sage: G = graphs.PetersenGraph() + sage: G.is_matching_covered() + True + + A graph (without a self-loop) is matching covered if and only if the + underlying simple graph is matching covered:: + + sage: G = graphs.PetersenGraph() + sage: G.allow_multiple_edges(True) + sage: G.add_edge(0, 5) + sage: G.is_matching_covered() + True + + A corollary to Tutte's fundamental result [Tut1947]_, as a + strengthening of Petersen's Theorem, states that every 2-connected + cubic graph is matching covered:: + + sage: G = Graph() + sage: G.add_edges([ + ....: (0, 1), (0, 2), (0, 3), + ....: (1, 2), (1, 4), (2, 4), + ....: (3, 5), (3, 6), (4, 7), + ....: (5, 6), (5, 7), (6, 7) + ....: ]) + sage: G.vertex_connectivity() + 2 + sage: degree_sequence = G.degree_sequence() + sage: min(degree_sequence) == max(degree_sequence) == 3 + True + sage: G.is_matching_covered() + True + + A connected bipartite graph `G[A, B]`, with `|A| = |B| \geq 2`, is + matching covered if and only if `|N(X)| \geq |X| + 1`, for all + `X \subset A` such that `1 \leq |X| \leq |A| - 1`. For instance, + the Hexahedral graph is matching covered, but not the path graphs on + even number of vertices, even though they have a perfect matching:: + + sage: G = graphs.HexahedralGraph() + sage: G.is_bipartite() + True + sage: G.is_matching_covered() + True + sage: P = graphs.PathGraph(10) + sage: P.is_bipartite() + True + sage: M = Graph(P.matching()) + sage: set(P) == set(M) + True + sage: P.is_matching_covered() + False + + A connected bipartite graph `G[A, B]` of order six or more is matching + covered if and only if `G - a - b` has a perfect matching for some + vertex `a` in `A` and some vertex `b` in `B`:: + + sage: G = graphs.CircularLadderGraph(8) + sage: G.is_bipartite() + True + sage: G.is_matching_covered() + True + sage: A, B = G.bipartite_sets() + sage: # needs random + sage: import random + sage: a = random.choice(list(A)) + sage: b = random.choice(list(B)) + sage: G.delete_vertices([a, b]) + sage: M = Graph(G.matching()) + sage: set(M) == set(G) + True + sage: cycle1 = graphs.CycleGraph(4) + sage: cycle2 = graphs.CycleGraph(6) + sage: cycle2.relabel(lambda v: v + 4) + sage: H = Graph() + sage: H.add_edges(cycle1.edges() + cycle2.edges()) + sage: H.add_edge(3, 4) + sage: H.is_bipartite() + True + sage: H.is_matching_covered() + False + sage: H.delete_vertices([3, 4]) + sage: N = Graph(H.matching()) + sage: set(N) == set(H) + False + + One may specify a matching:: + + sage: G = graphs.WheelGraph(20) + sage: M = Graph(G.matching()) + sage: G.is_matching_covered(matching=M) + True + sage: J = graphs.CycleGraph(4) + sage: J.add_edge(0, 2) + sage: N = J.matching() + sage: J.is_matching_covered(matching=N) + False + + One may ask for a co-`\mathcal{NP}` certificate:: + + sage: G = graphs.CompleteGraph(14) + sage: G.is_matching_covered(coNP_certificate=True) + (True, None) + sage: H = graphs.PathGraph(20) + sage: M = H.matching() + sage: H.is_matching_covered(matching=M, coNP_certificate=True) + (False, (1, 2, None)) + + TESTS: + + If the graph is not connected:: + + sage: cycle1 = graphs.CycleGraph(4) + sage: cycle2 = graphs.CycleGraph(6) + sage: cycle2.relabel(lambda v: v + 4) + sage: G = Graph() + sage: G.add_edges(cycle1.edges() + cycle2.edges()) + sage: len(G.connected_components(sort=False)) + 2 + sage: G.is_matching_covered() + Traceback (most recent call last): + ... + ValueError: the graph is not connected + + If the graph is trivial:: + + sage: G = Graph() + sage: G.is_matching_covered() + Traceback (most recent call last): + ... + ValueError: the graph is trivial + sage: H = graphs.CycleGraph(1) + sage: H.is_matching_covered() + Traceback (most recent call last): + ... + ValueError: the graph is trivial + + Providing with a wrong matching:: + + sage: G = graphs.CompleteGraph(6) + sage: M = Graph(G.matching()) + sage: M.add_edges([(0, 1), (0, 2)]) + sage: G.is_matching_covered(matching=M) + Traceback (most recent call last): + ... + ValueError: the input is not a matching + sage: N = Graph(G.matching()) + sage: N.add_edge(6, 7) + sage: G.is_matching_covered(matching=N) + Traceback (most recent call last): + ... + ValueError: the input is not a matching of the graph + sage: J = Graph() + sage: J.add_edges([(0, 1), (2, 3)]) + sage: G.is_matching_covered(matching=J) + Traceback (most recent call last): + ... + ValueError: the input is not a perfect matching of the graph + + Providing with a graph with a self-loop:: + + sage: G = graphs.PetersenGraph() + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.is_matching_covered() + Traceback (most recent call last): + ... + ValueError: This method is not known to work on graphs with loops. Perhaps this method can be updated to handle them, but in the meantime if you want to use it please disallow loops using allow_loops(). + + REFERENCES: + + - [LM2024]_ + + - [LZ2001]_ + + - [Tut1947]_ + + .. SEEALSO:: + :meth:`~sage.graphs.graph.Graph.is_factor_critical`, + :meth:`~sage.graphs.graph.Graph.is_bicritical` + + AUTHORS: + + - Janmenjaya Panda (2024-06-23) + """ + G._scream_if_not_simple(allow_multiple_edges=True) + + # The graph must be nontrivial + if G.order() < 2: + raise ValueError("the graph is trivial") + + # The graph must be connected + if not G.is_connected(): + raise ValueError("the graph is not connected") + + # The graph must have an even order + if G.order() % 2: + return (False, next(G.edge_iterator())) if coNP_certificate else False + + # If the underlying simple graph is a complete graph of order two, + # the graph is matching covered + if G.order() == 2: + return (True, None) if coNP_certificate else True + + # A graph (without a self-loop) is matching covered if and only if the + # underlying simple graph is matching covered + G_simple = G.to_simple() + + from sage.graphs.graph import Graph + if matching: + # The input matching must be a valid perfect matching of the graph + M = Graph(matching) + if any(d != 1 for d in M.degree()): + raise ValueError("the input is not a matching") + if any(not G_simple.has_edge(edge) for edge in M.edge_iterator()): + raise ValueError("the input is not a matching of the graph") + if (G_simple.order() != M.order()) or (G_simple.order() != 2*M.size()): + raise ValueError("the input is not a perfect matching of the graph") + else: + # A maximum matching of the graph is computed + M = Graph(G_simple.matching(algorithm=algorithm, solver=solver, verbose=verbose, + integrality_tolerance=integrality_tolerance)) + + # It must be a perfect matching + if G_simple.order() != M.order(): + return (False, next(M.edge_iterator())) if coNP_certificate else False + + # Biparite graph: + # + # Given a connected bipartite graph G[A, B] with a perfect matching M. + # Construct a directed graph D from G such that V(D) := V(G) and + # for each edge in G direct the corresponding edge from A to B in D, + # if it is in M or otherwise direct it from B to A. The graph G is + # matching covered if and only if D is strongly connected. + + if G_simple.is_bipartite(): + A, _ = G_simple.bipartite_sets() + color = dict() + + for u in G_simple: + color[u] = 0 if u in A else 1 + + from sage.graphs.digraph import DiGraph + H = DiGraph() + + for u, v in G_simple.edge_iterator(labels=False): + if color[u]: + u, v = v, u + + if M.has_edge(u, v): + H.add_edge(u, v) + else: + H.add_edge(v, u) + + # Check if H is strongly connected using Kosaraju's algorithm + def dfs(J, v, visited, orientation): + stack = [v] # a stack of vertices + + while stack: + v = stack.pop() + + if v not in visited: + visited[v] = True + + if orientation == 'in': + for u in J.neighbors_out(v): + if u not in visited: + stack.append(u) + + elif orientation == 'out': + for u in J.neighbors_in(v): + if u not in visited: + stack.append(u) + else: + raise ValueError('Unknown orientation') + + root = next(H.vertex_iterator()) + + visited_in = {} + dfs(H, root, visited_in, 'in') + + visited_out = {} + dfs(H, root, visited_out, 'out') + + for edge in H.edge_iterator(): + u, v, _ = edge + if (u not in visited_in) or (v not in visited_out): + if not M.has_edge(edge): + return (False, edge) if coNP_certificate else False + + return (True, None) if coNP_certificate else True + + # Nonbipartite graph: + # + # Given a nonbipartite graph G with a perfect matching M. The graph G is + # matching covered if and only if for each edge uv not in M, there exists + # an M-alternating odd length uv-path starting and ending with edges not + # in M. + + for u in G_simple: + v = next(M.neighbor_iterator(u)) + + even = M_alternating_even_mark(G_simple, u, M) + + for w in G_simple.neighbor_iterator(v): + if w != u and w not in even: + return (False, (v, w)) if coNP_certificate else False + + return (True, None) if coNP_certificate else True + + +def matching(G, value_only=False, algorithm='Edmonds', + use_edge_labels=False, solver=None, verbose=0, + *, integrality_tolerance=1e-3): + r""" + Return a maximum weighted matching of the graph represented by the list + of its edges + + For more information, see the :wikipedia:`Matching_(graph_theory)`. + + Given a graph `G` such that each edge `e` has a weight `w_e`, a maximum + matching is a subset `S` of the edges of `G` of maximum weight such that + no two edges of `S` are incident with each other. + + As an optimization problem, it can be expressed as: + + .. MATH:: + + \mbox{Maximize : }&\sum_{e\in G.edges()} w_e b_e\\ + \mbox{Such that : }&\forall v \in G, + \sum_{(u,v)\in G.edges()} b_{(u,v)}\leq 1\\ + &\forall x\in G, b_x\mbox{ is a binary variable} + + INPUT: + + - ``value_only`` -- boolean (default: ``False``); when set to ``True``, + only the cardinal (or the weight) of the matching is returned + + - ``algorithm`` -- string (default: ``'Edmonds'``) + + - ``'Edmonds'`` selects Edmonds' algorithm as implemented in NetworkX + + - ``'LP'`` uses a Linear Program formulation of the matching problem + + - ``use_edge_labels`` -- boolean (default: ``False``) + + - when set to ``True``, computes a weighted matching where each edge + is weighted by its label (if an edge has no label, `1` is assumed) + + - when set to ``False``, each edge has weight `1` + + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer + Linear Programming (MILP) solver to be used. If set to ``None``, the + default one is used. For more information on MILP solvers and which + default solver is used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: 0); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when ``algorithm + == "LP"``) + + - ``integrality_tolerance`` -- float; parameter for use with MILP + solvers over an inexact base ring; see + :meth:`MixedIntegerLinearProgram.get_values`. + + OUTPUT: + + - When ``value_only=False`` (default), this method returns an + :class:`EdgesView` containing the edges of a maximum matching of `G`. + + - When ``value_only=True``, this method returns the sum of the + weights (default: ``1``) of the edges of a maximum matching of `G`. + The type of the output may vary according to the type of the edge + labels and the algorithm used. + + ALGORITHM: + + The problem is solved using Edmond's algorithm implemented in NetworkX, + or using Linear Programming depending on the value of ``algorithm``. + + EXAMPLES: + + Maximum matching in a Pappus Graph:: + + sage: g = graphs.PappusGraph() + sage: g.matching(value_only=True) # needs sage.networkx + 9 + + Same test with the Linear Program formulation:: + + sage: g = graphs.PappusGraph() + sage: g.matching(algorithm='LP', value_only=True) # needs sage.numerical.mip + 9 + + .. PLOT:: + + g = graphs.PappusGraph() + sphinx_plot(g.plot(edge_colors={"red":g.matching()})) + + TESTS: + + When ``use_edge_labels`` is set to ``False``, with Edmonds' algorithm + and LP formulation:: + + sage: g = Graph([(0,1,0), (1,2,999), (2,3,-5)]) + sage: sorted(g.matching()) # needs sage.networkx + [(0, 1, 0), (2, 3, -5)] + sage: sorted(g.matching(algorithm='LP')) # needs sage.numerical.mip + [(0, 1, 0), (2, 3, -5)] + + When ``use_edge_labels`` is set to ``True``, with Edmonds' algorithm and + LP formulation:: + + sage: g = Graph([(0,1,0), (1,2,999), (2,3,-5)]) + sage: g.matching(use_edge_labels=True) # needs sage.networkx + [(1, 2, 999)] + sage: g.matching(algorithm='LP', use_edge_labels=True) # needs sage.numerical.mip + [(1, 2, 999)] + + With loops and multiedges:: + + sage: edge_list = [(0,0,5), (0,1,1), (0,2,2), (0,3,3), (1,2,6) + ....: , (1,2,3), (1,3,3), (2,3,3)] + sage: g = Graph(edge_list, loops=True, multiedges=True) + sage: m = g.matching(use_edge_labels=True) # needs sage.networkx + sage: type(m) # needs sage.networkx + + sage: sorted(m) # needs sage.networkx + [(0, 3, 3), (1, 2, 6)] + + TESTS: + + If ``algorithm`` is set to anything different from ``'Edmonds'`` or + ``'LP'``, an exception is raised:: + + sage: g = graphs.PappusGraph() + sage: g.matching(algorithm='somethingdifferent') + Traceback (most recent call last): + ... + ValueError: algorithm must be set to either "Edmonds" or "LP" + """ + from sage.rings.real_mpfr import RR + + def weight(x): + if x in RR: + return x + else: + return 1 + + W = {} + L = {} + for u, v, l in G.edge_iterator(): + if u is v: + continue + fuv = frozenset((u, v)) + if fuv not in L or (use_edge_labels and W[fuv] < weight(l)): + L[fuv] = l + if use_edge_labels: + W[fuv] = weight(l) + + if algorithm == "Edmonds": + import networkx + g = networkx.Graph() + if use_edge_labels: + for (u, v), w in W.items(): + g.add_edge(u, v, weight=w) + else: + for u, v in L: + g.add_edge(u, v) + d = networkx.max_weight_matching(g) + if value_only: + if use_edge_labels: + return sum(W[frozenset(e)] for e in d) + return Integer(len(d)) + + from sage.graphs.graph import Graph + return EdgesView(Graph([(u, v, L[frozenset((u, v))]) for u, v in d], + format='list_of_edges')) + + elif algorithm == "LP": + g = G + from sage.numerical.mip import MixedIntegerLinearProgram + # returns the weight of an edge considering it may not be + # weighted ... + p = MixedIntegerLinearProgram(maximization=True, solver=solver) + b = p.new_variable(binary=True) + if use_edge_labels: + p.set_objective(p.sum(w * b[fe] for fe, w in W.items())) + else: + p.set_objective(p.sum(b[fe] for fe in L)) + # for any vertex v, there is at most one edge incident to v in + # the maximum matching + for v in g: + p.add_constraint(p.sum(b[frozenset(e)] for e in G.edge_iterator(vertices=[v], labels=False) + if e[0] != e[1]), max=1) + + p.solve(log=verbose) + b = p.get_values(b, convert=bool, tolerance=integrality_tolerance) + if value_only: + if use_edge_labels: + return sum(w for fe, w in W.items() if b[fe]) + return Integer(sum(1 for fe in L if b[fe])) + + from sage.graphs.graph import Graph + return EdgesView(Graph([(u, v, L[frozenset((u, v))]) + for u, v in L if b[frozenset((u, v))]], + format='list_of_edges')) + + raise ValueError('algorithm must be set to either "Edmonds" or "LP"') + + +def perfect_matchings(G, labels=False): + r""" + Return an iterator over all perfect matchings of the graph + + ALGORITHM: + + Choose a vertex `v`, then recurse through all edges incident to `v`, + removing one edge at a time whenever an edge is added to a matching. + + INPUT: + + - ``labels`` -- boolean (default: ``False``); when ``True``, the edges + in each perfect matching are triples (containing the label as the + third element), otherwise the edges are pairs. + + .. SEEALSO:: + + :meth:`matching` + + EXAMPLES:: + + sage: G=graphs.GridGraph([2,3]) + sage: for m in G.perfect_matchings(): + ....: print(sorted(m)) + [((0, 0), (0, 1)), ((0, 2), (1, 2)), ((1, 0), (1, 1))] + [((0, 0), (1, 0)), ((0, 1), (0, 2)), ((1, 1), (1, 2))] + [((0, 0), (1, 0)), ((0, 1), (1, 1)), ((0, 2), (1, 2))] + + sage: G = graphs.CompleteGraph(4) + sage: for m in G.perfect_matchings(labels=True): + ....: print(sorted(m)) + [(0, 1, None), (2, 3, None)] + [(0, 2, None), (1, 3, None)] + [(0, 3, None), (1, 2, None)] + + sage: G = Graph([[1,-1,'a'], [2,-2, 'b'], [1,-2,'x'], [2,-1,'y']]) + sage: sorted(sorted(m) for m in G.perfect_matchings(labels=True)) + [[(-2, 1, 'x'), (-1, 2, 'y')], [(-2, 2, 'b'), (-1, 1, 'a')]] + + sage: G = graphs.CompleteGraph(8) + sage: mpc = G.matching_polynomial().coefficients(sparse=False)[0] # needs sage.libs.flint + sage: len(list(G.perfect_matchings())) == mpc # needs sage.libs.flint + True + + sage: G = graphs.PetersenGraph().copy(immutable=True) + sage: [sorted(m) for m in G.perfect_matchings()] + [[(0, 1), (2, 3), (4, 9), (5, 7), (6, 8)], + [(0, 1), (2, 7), (3, 4), (5, 8), (6, 9)], + [(0, 4), (1, 2), (3, 8), (5, 7), (6, 9)], + [(0, 4), (1, 6), (2, 3), (5, 8), (7, 9)], + [(0, 5), (1, 2), (3, 4), (6, 8), (7, 9)], + [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]] + + sage: list(Graph().perfect_matchings()) + [[]] + + sage: G = graphs.CompleteGraph(5) + sage: list(G.perfect_matchings()) + [] + """ + if not G: + yield [] + return + if G.order() % 2 or any(len(cc) % 2 for cc in G.connected_components(sort=False)): + return + + def rec(G): + """ + Iterator over all perfect matchings of a simple graph `G`. + """ + if not G: + yield [] + return + if G.order() % 2 == 0: + v = next(G.vertex_iterator()) + Nv = list(G.neighbor_iterator(v)) + G.delete_vertex(v) + for u in Nv: + Nu = list(G.neighbor_iterator(u)) + G.delete_vertex(u) + for partial_matching in rec(G): + partial_matching.append((u, v)) + yield partial_matching + G.add_vertex(u) + G.add_edges((u, nu) for nu in Nu) + G.add_vertex(v) + G.add_edges((v, nv) for nv in Nv) + + # We create a mutable copy of the graph and remove its loops, if any + G_copy = G.copy(immutable=False) + G_copy.allow_loops(False) + + # We create a mapping from frozen unlabeled edges to (labeled) edges. + # This ease for instance the manipulation of multiedges (if any) + edges = {} + for e in G_copy.edges(sort=False, labels=labels): + f = frozenset(e[:2]) + if f in edges: + edges[f].append(e) + else: + edges[f] = [e] + + # We now get rid of multiple edges, if any + G_copy.allow_multiple_edges(False) + + # For each unlabeled matching, we yield all its possible labelings + import itertools + + for m in rec(G_copy): + yield from itertools.product(*[edges[frozenset(e)] for e in m]) + + +def M_alternating_even_mark(G, vertex, matching): + r""" + Return the vertices reachable from ``vertex`` via an even alternating path + starting with a non-matching edge + + This method implements the algorithm proposed in [LR2004]_. Note that + the complexity of the algorithm is linear in number of edges. + + INPUT: + + - ``vertex`` -- a vertex of the graph + + - ``matching`` -- a matching of the graph; it can be given using any + valid input format of :class:`~sage.graphs.graph.Graph` + + OUTPUT: + + - ``even`` -- the set of vertices each of which is reachable from the + provided vertex through a path starting with an edge not in the + matching and ending with an edge in the matching; note that a note that a + :class:`ValueError` is returned if the graph is not simple + + EXAMPLES: + + Show the list of required vertices for a graph `G` with a matching `M` + for a vertex `u`:: + + sage: G = graphs.CycleGraph(3) + sage: M = G.matching() + sage: M + [(0, 2, None)] + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S0 = M_alternating_even_mark(G, 0, M) + sage: S0 + {0} + sage: S1 = M_alternating_even_mark(G, 1, M) + sage: S1 + {0, 1, 2} + + The result is equivalent for the underlying simple graph of the provided + graph, if the other parameters provided are the same:: + + sage: G = graphs.CompleteBipartiteGraph(3, 3) + sage: G.allow_multiple_edges(True) + sage: G.add_edge(0, 3) + sage: M = G.matching() + sage: u = 0 + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S = M_alternating_even_mark(G, u, M) + sage: S + {0, 1, 2} + sage: T = M_alternating_even_mark(G.to_simple(), u, M) + sage: T + {0, 1, 2} + + For a factor critical graph `G` (for instance, a wheel graph of an odd + order) with a near perfect matching `M` and `u` being the (unique) + `M`-exposed vertex, each vertex in `G` is reachable from `u` through an + even length `M`-alternating path as described above:: + + sage: G = graphs.WheelGraph(11) + sage: M = Graph(G.matching()) + sage: G.is_factor_critical(M) + True + sage: for v in G: + ....: if v not in M: + ....: break + ....: + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S = M_alternating_even_mark(G, v, M) + sage: S == set(G) + True + + For a matching covered graph `G` (for instance, `K_4 \odot K_{3,3}`) with a + perfect matching `M` and for some vertex `u` with `v` being its `M`-matched + neighbor, each neighbor of `v` is reachable from `u` through an even length + `M`-alternating path as described above:: + + sage: G = Graph() + sage: G.add_edges([ + ....: (0, 2), (0, 3), (0, 4), (1, 2), + ....: (1, 3), (1, 4), (2, 5), (3, 6), + ....: (4, 7), (5, 6), (5, 7), (6, 7) + ....: ]) + sage: M = Graph(G.matching()) + sage: G.is_matching_covered(M) + True + sage: u = 0 + sage: v = next(M.neighbor_iterator(u)) + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S = M_alternating_even_mark(G, u, M) + sage: (set(G.neighbor_iterator(v))).issubset(S) + True + + For a bicritical graph `G` (for instance, the Petersen graph) with a + perfect matching `M` and for some vertex `u` with its `M`-matched neighbor + being `v`, each vertex of the graph distinct from `v` is reachable from `u` + through an even length `M`-alternating path as described above:: + + sage: G = graphs.PetersenGraph() + sage: M = Graph(G.matching()) + sage: G.is_bicritical(M) + True + sage: import random + sage: u = random.choice(list(G)) # needs random + sage: v = next(M.neighbor_iterator(u)) + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S = M_alternating_even_mark(G, u, M) + sage: S == (set(G) - {v}) + True + + TESTS: + + Giving a wrong vertex:: + + sage: G = graphs.HexahedralGraph() + sage: M = G.matching() + sage: u = G.order() + sage: from sage.graphs.matching import M_alternating_even_mark + sage: S = M_alternating_even_mark(G, u, M) + Traceback (most recent call last): + ... + ValueError: '8' is not a vertex of the graph + + Giving a wrong matching:: + + sage: from sage.graphs.matching import M_alternating_even_mark + sage: G = graphs.CompleteGraph(6) + sage: M = [(0, 1), (0, 2)] + sage: u = 0 + sage: S = M_alternating_even_mark(G, u, M) + Traceback (most recent call last): + ... + ValueError: the input is not a matching + sage: G = graphs.CompleteBipartiteGraph(3, 3) + sage: M = [(2*i, 2*i + 1) for i in range(4)] + sage: u = 0 + sage: S = M_alternating_even_mark(G, u, M) + Traceback (most recent call last): + ... + ValueError: the input is not a matching of the graph + + REFERENCES: + + - [LR2004]_ + + .. SEEALSO:: + :meth:`~sage.graphs.graph.Graph.is_factor_critical`, + :meth:`~sage.graphs.graph.Graph.is_matching_covered`, + :meth:`~sage.graphs.graph.Graph.is_bicritical` + + AUTHORS: + + - Janmenjaya Panda (2024-06-17) + """ + # The input vertex must be a valid vertex of the graph + if vertex not in G: + raise ValueError("'{}' is not a vertex of the graph".format(vertex)) + + # The result is equivalent for the underlying simple graph of the provided + # graph. So, the underlying simple graph is considered for implementational + # simplicity. + G_simple = G.to_simple() + + # The input matching must be a valid matching of the graph + from sage.graphs.graph import Graph + M = Graph(matching) + if any(d != 1 for d in M.degree()): + raise ValueError("the input is not a matching") + if any(not G_simple.has_edge(edge) for edge in M.edge_iterator()): + raise ValueError("the input is not a matching of the graph") + + # Build an M-alternating tree T rooted at vertex + import itertools + from queue import Queue + + q = Queue() + q.put(vertex) + + even = set([vertex]) + odd = set() + predecessor = {vertex: vertex} + rank = {vertex: 0} + + if vertex in M: + u = next(M.neighbor_iterator(vertex)) + predecessor[u] = None + rank[u] = -1 + odd.add(u) + + while not q.empty(): + x = q.get() + for y in G_simple.neighbor_iterator(x): + if y in odd: + continue + elif y in even: + # Search t := LCA(x, y) + ancestor_x = [x] + ancestor_y = [y] + + # Loop over until the nearest common ancestor of x and y is reached + while ancestor_x[-1] != ancestor_y[-1]: + if rank[ancestor_x[-1]] > rank[ancestor_y[-1]]: + ancestor_x.append(predecessor[ancestor_x[-1]]) + elif rank[ancestor_x[-1]] < rank[ancestor_y[-1]]: + ancestor_y.append(predecessor[ancestor_y[-1]]) + else: + ancestor_x.append(predecessor[ancestor_x[-1]]) + ancestor_y.append(predecessor[ancestor_y[-1]]) + + lcs = ancestor_x.pop() + ancestor_y.pop() + # Set t as pred of all vertices of the chains and add + # vertices marked odd to the queue + next_rank_to_lcs_rank = rank[lcs] + 1 + for a in itertools.chain(ancestor_x, ancestor_y): + predecessor[a] = lcs + rank[a] = next_rank_to_lcs_rank + + if a in odd: + even.add(a) + odd.discard(a) + q.put(a) + + elif y in M: + # y has not been visited yet + z = next(M.neighbor_iterator(y)) + odd.add(y) + even.add(z) + q.put(z) + + predecessor[y] = x + predecessor[z] = y + + rank[y] = rank[x] + 1 + rank[z] = rank[y] + 1 + + return even \ No newline at end of file diff --git a/src/sage/graphs/meson.build b/src/sage/graphs/meson.build new file mode 100644 index 00000000000..dbc1808f107 --- /dev/null +++ b/src/sage/graphs/meson.build @@ -0,0 +1,138 @@ +bliss = cc.find_library('bliss', required: false, disabler: true) +mcqd = cc.find_library('mcqd', required: false, disabler: true) +cliquer = cc.find_library('cliquer') + +# Cannot be found via pkg-config +planarity = cc.find_library('planarity') + +py.install_sources( + 'all.py', + 'all__sagemath_bliss.py', + 'all__sagemath_mcqd.py', + 'all__sagemath_tdlib.py', + 'bipartite_graph.py', + 'cliquer.pxd', + 'cographs.py', + 'connectivity.pxd', + 'convexity_properties.pxd', + 'digraph.py', + 'digraph_generators.py', + 'distances_all_pairs.pxd', + 'domination.py', + 'dot2tex_utils.py', + 'generic_graph.py', + 'generic_graph_pyx.pxd', + 'graph.py', + 'graph_database.py', + 'graph_editor.py', + 'graph_generators.py', + 'graph_input.py', + 'graph_latex.py', + 'graph_list.py', + 'graph_plot.py', + 'graph_plot_js.py', + 'hypergraph_generators.py', + 'independent_sets.pxd', + 'isgci.py', + 'lovasz_theta.py', + 'matching.py', + 'mcqd.pxd', + 'orientations.py', + 'partial_cube.py', + 'pq_trees.py', + 'print_graphs.py', + 'schnyder.py', + 'traversals.pxd', + 'trees.pxd', + 'tutte_polynomial.py', + subdir: 'sage/graphs', +) + +extension_data = { + 'asteroidal_triples' : files('asteroidal_triples.pyx'), + 'centrality' : files('centrality.pyx'), + 'chrompoly' : files('chrompoly.pyx'), + 'cliquer' : files('cliquer.pyx'), + 'comparability' : files('comparability.pyx'), + 'connectivity' : files('connectivity.pyx'), + 'convexity_properties' : files('convexity_properties.pyx'), + 'distances_all_pairs' : files('distances_all_pairs.pyx'), + 'generic_graph_pyx' : files('generic_graph_pyx.pyx'), + 'genus' : files('genus.pyx'), + 'graph_generators_pyx' : files('graph_generators_pyx.pyx'), + 'hyperbolicity' : files('hyperbolicity.pyx'), + 'independent_sets' : files('independent_sets.pyx'), + 'isoperimetric_inequalities' : files('isoperimetric_inequalities.pyx'), + 'line_graph' : files('line_graph.pyx'), + 'matchpoly' : files('matchpoly.pyx'), + 'planarity' : files('planarity.pyx'), + 'spanning_tree' : files('spanning_tree.pyx'), + 'strongly_regular_db' : files('strongly_regular_db.pyx'), + 'trees' : files('trees.pyx'), + 'views' : files('views.pyx'), + 'weakly_chordal' : files('weakly_chordal.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs', + install: true, + include_directories: [ + inc_cpython, + inc_data_structures, + inc_flint, + inc_rings, + ], + dependencies: [py_dep, cysignals, cliquer, flint, gmp, planarity], + ) +endforeach + +extension_data_cpp = { + 'edge_connectivity': files('edge_connectivity.pyx'), + 'graph_coloring': files('graph_coloring.pyx'), + 'path_enumeration': files('path_enumeration.pyx'), + 'traversals': files('traversals.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/graphs', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_data_structures, + inc_flint, + inc_rings, + ], + dependencies: [py_dep, cysignals, flint, gmp, planarity], + ) +endforeach + +py.extension_module( + 'bliss', + sources: files('bliss.pyx'), + subdir: 'sage/graphs', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_data_structures, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, bliss], +) + +py.extension_module( + 'mcqd', + sources: files('mcqd.pyx'), + subdir: 'sage/graphs', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_data_structures, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, mcqd], +) + +subdir('base') +subdir('generators') +subdir('graph_decompositions') diff --git a/src/sage/graphs/orientations.py b/src/sage/graphs/orientations.py index 2dac79b7146..af9c795f535 100644 --- a/src/sage/graphs/orientations.py +++ b/src/sage/graphs/orientations.py @@ -12,9 +12,15 @@ :widths: 30, 70 :delim: | + :meth:`orient` | Return an oriented version of `G` according the input function `f`. + :meth:`orientations` | Return an iterator over orientations of `G`. + :meth:`acyclic_orientations` | Return an iterator over all acyclic orientations of an undirected graph `G`. + :meth:`strong_orientation` | Return a strongly connected orientation of the graph `G`. :meth:`strong_orientations_iterator` | Return an iterator over all strong orientations of a graph `G` :meth:`random_orientation` | Return a random orientation of a graph `G` - + :meth:`minimum_outdegree_orientation` | Return an orientation of `G` with the smallest possible maximum outdegree. + :meth:`bounded_outdegree_orientation` | Return an orientation of `G` such that every vertex `v` has out-degree less than `b(v)`. + :meth:`eulerian_orientation(G)` | Return a DiGraph which is an Eulerian orientation of the graph `G`. Authors ------- @@ -28,7 +34,7 @@ # **************************************************************************** # Copyright (C) 2017 Kolja Knauer # 2017 Petru Valicov -# 2017-2023 David Coudert +# 2017-2024 David Coudert # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -41,6 +47,342 @@ from sage.graphs.digraph import DiGraph +def orient(G, f, weighted=None, data_structure=None, sparse=None, + immutable=None, hash_labels=None): + r""" + Return an oriented version of `G` according the input function `f`. + + INPUT: + + - ``G`` -- an undirected graph + + - ``f`` -- a function that inputs an edge and outputs an orientation of this + edge + + - ``weighted`` -- boolean (default: ``None``); weightedness for the oriented + digraph. By default (``None``), the graph and its orientation will behave + the same. + + - ``sparse`` -- boolean (default: ``None``); ``sparse=True`` is an alias for + ``data_structure="sparse"``, and ``sparse=False`` is an alias for + ``data_structure="dense"``. Only used when ``data_structure=None``. + + - ``data_structure`` -- string (default: ``None``); one of ``'sparse'``, + ``'static_sparse'``, or ``'dense'``. See the documentation of + :class:`DiGraph`. + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable digraph. Only used when ``data_structure=None``. + + * ``immutable=None`` (default) means that the graph and its orientation + will behave the same way. + + * ``immutable=True`` is a shortcut for ``data_structure='static_sparse'`` + + * ``immutable=False`` means that the created digraph is mutable. When used + to orient an immutable graph, the data structure used is ``'sparse'`` + unless anything else is specified. + + - ``hash_labels`` -- boolean (default: ``None``); whether to include edge + labels during hashing of the oriented digraph. This parameter defaults to + ``True`` if the graph is weighted. This parameter is ignored when + parameter ``immutable`` is not ``True``. Beware that trying to hash + unhashable labels will raise an error. + + OUTPUT: a :class:`DiGraph` object + + .. NOTE:: + + This method behaves similarly to method + :meth:`~sage.graphs.generic_graph.GenericGraph.copy`. That is, the + returned digraph uses the same data structure by default, unless the + user asks to use another data structure, and the attributes of the input + graph are copied. + + EXAMPLES:: + + sage: G = graphs.CycleGraph(4); G + Cycle graph: Graph on 4 vertices + sage: D = G.orient(lambda e:e if e[0] < e[1] else (e[1], e[0], e[2])); D + Orientation of Cycle graph: Digraph on 4 vertices + sage: sorted(D.edges(labels=False)) + [(0, 1), (0, 3), (1, 2), (2, 3)] + + TESTS: + + We make sure that one can get an immutable orientation by providing the + ``data_structure`` optional argument:: + + sage: def foo(e): + ....: return e if e[0] < e[1] else (e[1], e[0], e[2]) + sage: G = graphs.CycleGraph(4) + sage: D = G.orient(foo, data_structure='static_sparse') + sage: D.is_immutable() + True + sage: D = G.orient(foo, immutable=True) + sage: D.is_immutable() + True + + Bad input:: + + sage: G.orient(foo, data_structure='sparse', sparse=False) + Traceback (most recent call last): + ... + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value + sage: G.orient(foo, data_structure='sparse', immutable=True) + Traceback (most recent call last): + ... + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value + sage: G.orient(foo, immutable=True, sparse=False) + Traceback (most recent call last): + ... + ValueError: there is no dense immutable backend at the moment + + Which backend? :: + + sage: G.orient(foo, data_structure='sparse')._backend + + sage: G.orient(foo, data_structure='dense')._backend + + sage: G.orient(foo, data_structure='static_sparse')._backend + + sage: G.orient(foo, immutable=True)._backend + + sage: G.orient(foo, immutable=True, sparse=True)._backend + + sage: G.orient(foo, immutable=False, sparse=True)._backend + + sage: G.orient(foo, immutable=False, sparse=False)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=True)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=False)._backend + + sage: G.orient(foo, data_structure=None, immutable=None, sparse=None)._backend + + sage: H = Graph(data_structure='dense') + sage: H.orient(foo, data_structure=None, immutable=None, sparse=None)._backend + + """ + # Which data structure should be used ? + if data_structure is not None: + # data_structure is already defined so there is nothing left to do + # here. Did the user try to define too much ? + if immutable is not None or sparse is not None: + raise ValueError("you cannot define 'immutable' or 'sparse' " + "when 'data_structure' has a value") + # At this point, data_structure is None. + elif immutable is True: + data_structure = 'static_sparse' + if sparse is False: + raise ValueError("there is no dense immutable backend at the moment") + elif immutable is False: + # If the user requests a mutable digraph and input is immutable, we + # choose the 'sparse' cgraph backend. Unless the user explicitly + # asked for something different. + if G.is_immutable(): + data_structure = 'dense' if sparse is False else 'sparse' + elif sparse is True: + data_structure = "sparse" + elif sparse is False: + data_structure = "dense" + + if data_structure is None: + from sage.graphs.base.dense_graph import DenseGraphBackend + if isinstance(G._backend, DenseGraphBackend): + data_structure = "dense" + else: + data_structure = "sparse" + + if weighted is None: + weighted = G.weighted() + + edges = (f(e) for e in G.edge_iterator()) + D = DiGraph([G, edges], format='vertices_and_edges', + data_structure=data_structure, + loops=G.allows_loops(), + multiedges=G.allows_multiple_edges(), + name=f"Orientation of {G.name()}", + pos=copy(G._pos), weighted=weighted, + hash_labels=hash_labels) + + attributes_to_copy = ('_assoc', '_embedding') + for attr in attributes_to_copy: + if hasattr(G, attr): + copy_attr = {} + old_attr = getattr(G, attr) + if isinstance(old_attr, dict): + for v, value in old_attr.items(): + try: + copy_attr[v] = value.copy() + except AttributeError: + copy_attr[v] = copy(value) + setattr(D, attr, copy_attr) + else: + setattr(D, attr, copy(old_attr)) + + return D + + +def orientations(G, data_structure=None, sparse=None): + r""" + Return an iterator over orientations of `G`. + + An *orientation* of an undirected graph is a directed graph such that + every edge is assigned a direction. Hence there are `2^s` oriented + digraphs for a simple graph with `s` edges. + + INPUT: + + - ``G`` -- an undirected graph + + - ``data_structure`` -- one of ``'sparse'``, ``'static_sparse'``, or + ``'dense'``; see the documentation of :class:`Graph` or :class:`DiGraph`; + default is the data structure of `G` + + - ``sparse`` -- boolean (default: ``None``); ``sparse=True`` is an alias for + ``data_structure="sparse"``, and ``sparse=False`` is an alias for + ``data_structure="dense"``. By default (``None``), guess the most suitable + data structure. + + .. WARNING:: + + This always considers multiple edges of graphs as distinguishable, and + hence, may have repeated digraphs. + + .. SEEALSO:: + + - :meth:`~sage.graphs.graph.Graph.strong_orientation` + - :meth:`~sage.graphs.orientations.strong_orientations_iterator` + - :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.nauty_directg` + - :meth:`~sage.graphs.orientations.random_orientation` + + EXAMPLES:: + + sage: G = Graph([[1,2,3], [(1, 2, 'a'), (1, 3, 'b')]], format='vertices_and_edges') + sage: it = G.orientations() + sage: D = next(it) + sage: D.edges(sort=True) + [(1, 2, 'a'), (1, 3, 'b')] + sage: D = next(it) + sage: D.edges(sort=True) + [(1, 2, 'a'), (3, 1, 'b')] + + TESTS:: + + sage: G = Graph() + sage: D = [g for g in G.orientations()] + sage: len(D) + 1 + sage: D[0] + Digraph on 0 vertices + + sage: G = Graph(5) + sage: it = G.orientations() + sage: D = next(it) + sage: D.size() + 0 + + sage: G = Graph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: len(list(G.orientations())) + 4 + + sage: G = Graph([[1,2], [1,1]], loops=True) + sage: len(list(G.orientations())) + 2 + + sage: G = Graph([[1,2],[2,3]]) + sage: next(G.orientations()) + Digraph on 3 vertices + sage: G = graphs.PetersenGraph() + sage: next(G.orientations()) + An orientation of Petersen graph: Digraph on 10 vertices + + An orientation must have the same ground set of vertices as the original + graph (:issue:`24366`):: + + sage: G = Graph(1) + sage: next(G.orientations()) + Digraph on 1 vertex + + Which backend? :: + + sage: next(G.orientations(data_structure='sparse', sparse=True))._backend + Traceback (most recent call last): + ... + ValueError: cannot specify both 'sparse' and 'data_structure' + sage: next(G.orientations(sparse=True))._backend + + sage: next(G.orientations(sparse=False))._backend + + sage: next(G.orientations())._backend + + sage: G = Graph(1, data_structure='dense') + sage: next(G.orientations())._backend + + sage: G = Graph(1, data_structure='static_sparse') + sage: next(G.orientations())._backend + + + Check that the embedding is copied:: + + sage: G = Graph([(0, 1), (0, 2), (1, 2)]) + sage: embedding = {0: [1, 2], 1: [2, 0], 2: [0, 1]} + sage: G.set_embedding(embedding) + sage: next(G.orientations()).get_embedding() == embedding + True + sage: G = Graph() + sage: G.set_embedding({}) + sage: next(G.orientations()).get_embedding() == {} + True + """ + if sparse is not None: + if data_structure is not None: + raise ValueError("cannot specify both 'sparse' and 'data_structure'") + data_structure = "sparse" if sparse else "dense" + if data_structure is None: + from sage.graphs.base.dense_graph import DenseGraphBackend + from sage.graphs.base.sparse_graph import SparseGraphBackend + if isinstance(G._backend, DenseGraphBackend): + data_structure = "dense" + elif isinstance(G._backend, SparseGraphBackend): + data_structure = "sparse" + else: + data_structure = "static_sparse" + + name = G.name() + if name: + name = 'An orientation of ' + name + + if not G.size(): + D = DiGraph(data=[G, []], + format='vertices_and_edges', + name=name, + pos=G._pos, + multiedges=G.allows_multiple_edges(), + loops=G.allows_loops(), + data_structure=data_structure) + if hasattr(G, '_embedding'): + D._embedding = copy(G._embedding) + yield D + return + + E = [[(u, v, label), (v, u, label)] if u != v else [(u, v, label)] + for u, v, label in G.edge_iterator()] + from itertools import product + for edges in product(*E): + D = DiGraph(data=[G, edges], + format='vertices_and_edges', + name=name, + pos=G._pos, + multiedges=G.allows_multiple_edges(), + loops=G.allows_loops(), + data_structure=data_structure) + if hasattr(G, '_embedding'): + D._embedding = copy(G._embedding) + yield D + + def acyclic_orientations(G): r""" Return an iterator over all acyclic orientations of an undirected graph `G`. @@ -67,11 +409,12 @@ def acyclic_orientations(G): .. NOTE:: - The function assumes that the input graph is undirected and the edges are unlabelled. + The function assumes that the input graph is undirected and the edges + are unlabelled. EXAMPLES: - To count number acyclic orientations for a graph:: + To count the number of acyclic orientations for a graph:: sage: g = Graph([(0, 3), (0, 4), (3, 4), (1, 3), (1, 2), (2, 3), (2, 4)]) sage: it = g.acyclic_orientations() @@ -80,11 +423,21 @@ def acyclic_orientations(G): Test for arbitrary vertex labels:: - sage: g_str = Graph([('abc', 'def'), ('ghi', 'def'), ('xyz', 'abc'), ('xyz', 'uvw'), ('uvw', 'abc'), ('uvw', 'ghi')]) + sage: g_str = Graph([('abc', 'def'), ('ghi', 'def'), ('xyz', 'abc'), + ....: ('xyz', 'uvw'), ('uvw', 'abc'), ('uvw', 'ghi')]) sage: it = g_str.acyclic_orientations() sage: len(list(it)) 42 + Check that the method returns properly relabeled acyclic digraphs:: + + sage: g = Graph([(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)]) + sage: orientations = set([frozenset(d.edges(labels=false)) for d in g.acyclic_orientations()]) + sage: len(orientations) + 18 + sage: all(d.is_directed_acyclic() for d in g.acyclic_orientations()) + True + TESTS: To count the number of acyclic orientations for a graph with 0 vertices:: @@ -291,8 +644,127 @@ def helper(G, globO, m, k): # Iterate over acyclic orientations and create relabeled graphs for orientation in orientations: - relabeled_graph = DiGraph([(reverse_vertex_labels[u], reverse_vertex_labels[v], label) for (u, v), label in orientation.items()]) - yield relabeled_graph + D = DiGraph([(u, v) if label else (v, u) for (u, v), label in orientation.items()]) + D.relabel(perm=reverse_vertex_labels, inplace=True) + yield D + + +def strong_orientation(G): + r""" + Return a strongly connected orientation of the graph `G`. + + An orientation of an undirected graph is a digraph obtained by giving an + unique direction to each of its edges. An orientation is said to be strong + if there is a directed path between each pair of vertices. See also the + :wikipedia:`Strongly_connected_component`. + + If the graph is 2-edge-connected, a strongly connected orientation can be + found in linear time. If the given graph is not 2-connected, the orientation + returned will ensure that each 2-connected component has a strongly + connected orientation. + + INPUT: + + - ``G`` -- an undirected graph + + OUTPUT: a digraph representing an orientation of the current graph + + .. NOTE:: + + - This method assumes that the input the graph is connected. + - The time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for + ``DenseGraph`` . + + .. SEEALSO:: + + - :meth:`~sage.graphs.graph.Graph.orientations` + - :meth:`~sage.graphs.orientations.strong_orientations_iterator` + - :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.nauty_directg` + - :meth:`~sage.graphs.orientations.random_orientation` + + EXAMPLES: + + For a 2-regular graph, a strong orientation gives to each vertex an + out-degree equal to 1:: + + sage: g = graphs.CycleGraph(5) + sage: g.strong_orientation().out_degree() + [1, 1, 1, 1, 1] + + The Petersen Graph is 2-edge connected. It then has a strongly connected + orientation:: + + sage: g = graphs.PetersenGraph() + sage: o = g.strong_orientation() + sage: len(o.strongly_connected_components()) + 1 + + The same goes for the CubeGraph in any dimension:: + + sage: all(len(graphs.CubeGraph(i).strong_orientation().strongly_connected_components()) == 1 + ....: for i in range(2,6)) + True + + A multigraph also has a strong orientation:: + + sage: g = Graph([(0, 1), (0, 2), (1, 2)] * 2, multiedges=True) + sage: g.strong_orientation() + Multi-digraph on 3 vertices + """ + d = DiGraph(multiedges=G.allows_multiple_edges()) + i = 0 + + # The algorithm works through a depth-first search. Any edge used in the + # depth-first search is oriented in the direction in which it has been + # used. All the other edges are oriented backward + + v = next(G.vertex_iterator()) + seen = {} + i = 1 + + # Time at which the vertices have been discovered + seen[v] = i + + # indicates the stack of edges to explore + next_ = G.edges_incident(v) + + while next_: + e = next_.pop() + + # Ignore loops + if e[0] == e[1]: + continue + + # We assume e[0] to be a `seen` vertex + e = e if seen.get(e[0], False) is not False else (e[1], e[0], e[2]) + + # If we discovered a new vertex + if seen.get(e[1], False) is False: + d.add_edge(e) + next_.extend(ee for ee in G.edges_incident(e[1]) + if ((e[0], e[1]) != (ee[0], ee[1])) and ((e[0], e[1]) != (ee[1], ee[0]))) + i += 1 + seen[e[1]] = i + + # Else, we orient the edges backward + else: + if seen[e[0]] < seen[e[1]]: + d.add_edge(e[1], e[0], e[2]) + else: + d.add_edge(e) + + # Case of multiple edges. If another edge has already been inserted, we add + # the new one in the opposite direction. + tmp = None + for e in G.multiple_edges(): + if tmp == (e[0], e[1]): + if d.has_edge(e[0], e[1]): + d.add_edge(e[1], e[0], e[2]) + else: + d.add_edge(e) + tmp = (e[0], e[1]) + + return d def strong_orientations_iterator(G): @@ -467,7 +939,7 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E): while i < length: u, v = E[i] Dg.delete_edge(u, v) - if not (v in Dg.depth_first_search(u)): + if v not in Dg.depth_first_search(u): # del E[i] in constant time E[i] = E[-1] E.pop() @@ -478,7 +950,7 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E): else: Dg.add_edge(u, v) Dg.delete_edge(v, u) - if not (u in Dg.depth_first_search(v)): + if u not in Dg.depth_first_search(v): # del E[i] in constant time E[i] = E[-1] E.pop() @@ -566,3 +1038,412 @@ def random_orientation(G): D.add_edge(v, u, l) rbits >>= 1 return D + + +def minimum_outdegree_orientation(G, use_edge_labels=False, solver=None, verbose=0, + *, integrality_tolerance=1e-3): + r""" + Return an orientation of `G` with the smallest possible maximum outdegree. + + Given a Graph `G`, it is polynomial to compute an orientation `D` of the + edges of `G` such that the maximum out-degree in `D` is minimized. This + problem, though, is NP-complete in the weighted case [AMOZ2006]_. + + INPUT: + + - ``G`` -- an undirected graph + + - ``use_edge_labels`` -- boolean (default: ``False``) + + - When set to ``True``, uses edge labels as weights to compute the + orientation and assumes a weight of `1` when there is no value available + for a given edge. + + - When set to ``False`` (default), gives a weight of 1 to all the edges. + + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear + Programming (MILP) solver to be used. If set to ``None``, the default one + is used. For more information on MILP solvers and which default solver is + used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: 0); sets the level of verbosity. Set to 0 + by default, which means quiet. + + - ``integrality_tolerance`` -- float; parameter for use with MILP solvers + over an inexact base ring; + see :meth:`MixedIntegerLinearProgram.get_values`. + + EXAMPLES: + + Given a complete bipartite graph `K_{n,m}`, the maximum out-degree of an + optimal orientation is `\left\lceil \frac {nm} {n+m}\right\rceil`:: + + sage: g = graphs.CompleteBipartiteGraph(3,4) + sage: o = g.minimum_outdegree_orientation() # needs sage.numerical.mip + sage: max(o.out_degree()) == integer_ceil((4*3)/(3+4)) # needs sage.numerical.mip + True + + Show the influence of edge labels on the solution:: + + sage: # needs sage.numerical.mip + sage: g = graphs.PetersenGraph() + sage: o = g.minimum_outdegree_orientation(use_edge_labels=False) + sage: max(o.out_degree()) + 2 + sage: _ = [g.set_edge_label(u, v, 1) for u, v in g.edge_iterator(labels=False)] + sage: o = g.minimum_outdegree_orientation(use_edge_labels=True) + sage: max(o.out_degree()) + 2 + sage: g.set_edge_label(0, 1, 100) + sage: o = g.minimum_outdegree_orientation(use_edge_labels=True) + sage: max(o.out_degree()) + 3 + + TESTS:: + + sage: from sage.graphs.orientations import minimum_outdegree_orientation + sage: minimum_outdegree_orientation(DiGraph()) # needs sage.numerical.mip + Traceback (most recent call last): + ... + ValueError: Cannot compute an orientation of a DiGraph. + Please convert it to a Graph if you really mean it. + """ + G._scream_if_not_simple() + if G.is_directed(): + raise ValueError("Cannot compute an orientation of a DiGraph. " + "Please convert it to a Graph if you really mean it.") + + if use_edge_labels: + from sage.rings.real_mpfr import RR + + def weight(e): + label = G.edge_label(e[0], e[1]) + return label if label in RR else 1 + else: + def weight(e): + return 1 + + from sage.numerical.mip import MixedIntegerLinearProgram + + p = MixedIntegerLinearProgram(maximization=False, solver=solver) + degree = p.new_variable(nonnegative=True) + + # The orientation of an edge is boolean and indicates whether the edge uv + # goes from u to v ( equal to 0 ) or from v to u ( equal to 1) + orientation = p.new_variable(binary=True) + + # Whether an edge adjacent to a vertex u counts positively or negatively. To + # do so, we first fix an arbitrary extremity per edge uv. + ext = {frozenset(e): e[0] for e in G.edge_iterator(labels=False)} + + def outgoing(u, e, variable): + if u == ext[frozenset(e)]: + return variable + return 1 - variable + + for u in G: + p.add_constraint(p.sum(weight(e) * outgoing(u, e, orientation[frozenset(e)]) + for e in G.edge_iterator(vertices=[u], labels=False)) + - degree['max'], max=0) + + p.set_objective(degree['max']) + + p.solve(log=verbose) + + orientation = p.get_values(orientation, convert=bool, tolerance=integrality_tolerance) + + # All the edges from G are doubled in O ( one in each direction ) + O = DiGraph(G) + + # Builds the list of edges that should be removed + edges = (e[::-1] if orientation[frozenset(e)] else e + for e in G.edge_iterator(labels=False)) + O.delete_edges(edges) + + return O + + +def bounded_outdegree_orientation(G, bound, solver=None, verbose=False, + *, integrality_tolerance=1e-3): + r""" + Return an orientation of `G` such that every vertex `v` has out-degree less + than `b(v)`. + + INPUT: + + - ``G`` -- an undirected graph + + - ``bound`` -- maximum bound on the out-degree. Can be of three + different types : + + * An integer `k`. In this case, computes an orientation whose maximum + out-degree is less than `k`. + + * A dictionary associating to each vertex its associated maximum + out-degree. + + * A function associating to each vertex its associated maximum + out-degree. + + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear + Programming (MILP) solver to be used. If set to ``None``, the default one + is used. For more information on MILP solvers and which default solver is + used, see the method :meth:`solve + ` of the class + :class:`MixedIntegerLinearProgram + `. + + - ``verbose`` -- integer (default: 0); sets the level of verbosity. Set to 0 + by default, which means quiet. + + - ``integrality_tolerance`` -- float; parameter for use with MILP solvers + over an inexact base ring; + see :meth:`MixedIntegerLinearProgram.get_values`. + + OUTPUT: + + A DiGraph representing the orientation if it exists. + A :exc:`ValueError` exception is raised otherwise. + + ALGORITHM: + + The problem is solved through a maximum flow : + + Given a graph `G`, we create a ``DiGraph`` `D` defined on `E(G)\cup V(G)\cup + \{s,t\}`. We then link `s` to all of `V(G)` (these edges having a capacity + equal to the bound associated to each element of `V(G)`), and all the + elements of `E(G)` to `t` . We then link each `v \in V(G)` to each of its + incident edges in `G`. A maximum integer flow of value `|E(G)|` corresponds + to an admissible orientation of `G`. Otherwise, none exists. + + EXAMPLES: + + There is always an orientation of a graph `G` such that a vertex `v` has + out-degree at most `\lceil \frac {d(v)} 2 \rceil`:: + + sage: g = graphs.RandomGNP(40, .4) + sage: b = lambda v: integer_ceil(g.degree(v)/2) + sage: D = g.bounded_outdegree_orientation(b) + sage: all(D.out_degree(v) <= b(v) for v in g) + True + + Chvatal's graph, being 4-regular, can be oriented in such a way that its + maximum out-degree is 2:: + + sage: g = graphs.ChvatalGraph() + sage: D = g.bounded_outdegree_orientation(2) + sage: max(D.out_degree()) + 2 + + For any graph `G`, it is possible to compute an orientation such that + the maximum out-degree is at most the maximum average degree of `G` + divided by 2. Anything less, though, is impossible. + + sage: g = graphs.RandomGNP(40, .4) + sage: mad = g.maximum_average_degree() # needs sage.numerical.mip + + Hence this is possible :: + + sage: d = g.bounded_outdegree_orientation(integer_ceil(mad/2)) # needs sage.numerical.mip + + While this is not:: + + sage: try: # needs sage.numerical.mip + ....: g.bounded_outdegree_orientation(integer_ceil(mad/2-1)) + ....: print("Error") + ....: except ValueError: + ....: pass + + The bounds can be specified in different ways:: + + sage: g = graphs.PetersenGraph() + sage: b = lambda v: integer_ceil(g.degree(v)/2) + sage: D = g.bounded_outdegree_orientation(b) + sage: b_dict = {u: b(u) for u in g} + sage: D = g.bounded_outdegree_orientation(b_dict) + sage: unique_bound = 2 + sage: D = g.bounded_outdegree_orientation(unique_bound) + + TESTS: + + As previously for random graphs, but more intensively:: + + sage: for i in range(30): # long time (up to 6s on sage.math, 2012) + ....: g = graphs.RandomGNP(40, .4) + ....: b = lambda v: integer_ceil(g.degree(v)/2) + ....: D = g.bounded_outdegree_orientation(b) + ....: if not ( + ....: all( D.out_degree(v) <= b(v) for v in g ) or + ....: D.size() != g.size()): + ....: print("Something wrong happened") + + Empty graph:: + + sage: Graph().bounded_outdegree_orientation(b) + Digraph on 0 vertices + """ + G._scream_if_not_simple() + n = G.order() + + if not n: + return DiGraph() + + vertices = list(G) + vertices_id = {y: x for x, y in enumerate(vertices)} + + b = {} + + # Checking the input type. We make a dictionary out of it + if isinstance(bound, dict): + b = bound + else: + try: + b = dict(zip(vertices, map(bound, vertices))) + except TypeError: + b = dict(zip(vertices, [bound]*n)) + + d = DiGraph() + + # Adding the edges (s,v) and ((u,v),t) + d.add_edges(('s', vertices_id[v], b[v]) for v in vertices) + + d.add_edges(((vertices_id[u], vertices_id[v]), 't', 1) + for u, v in G.edges(sort=False, labels=None)) + + # each v is linked to its incident edges + + for u, v in G.edge_iterator(labels=None): + u, v = vertices_id[u], vertices_id[v] + d.add_edge(u, (u, v), 1) + d.add_edge(v, (u, v), 1) + + # Solving the maximum flow + value, flow = d.flow('s', 't', value_only=False, integer=True, + use_edge_labels=True, solver=solver, verbose=verbose, + integrality_tolerance=integrality_tolerance) + + if value != G.size(): + raise ValueError("No orientation exists for the given bound") + + D = DiGraph() + D.add_vertices(vertices) + + # The flow graph may not contain all the vertices, if they are + # not part of the flow... + + for u in [x for x in range(n) if x in flow]: + + for uu, vv in flow.neighbors_out(u): + v = vv if vv != u else uu + D.add_edge(vertices[u], vertices[v]) + + # I do not like when a method destroys the embedding ;-) + D.set_pos(G.get_pos()) + + return D + + +def eulerian_orientation(G): + r""" + Return a DiGraph which is an Eulerian orientation of the graph `G`. + + An Eulerian graph being a graph such that any vertex has an even degree, an + Eulerian orientation of a graph is an orientation of its edges such that + each vertex `v` verifies `d^+(v)=d^-(v)=d(v)/2`, where `d^+` and `d^-` + respectively represent the out-degree and the in-degree of a vertex. + + If the graph is not Eulerian, the orientation verifies for any vertex `v` + that `| d^+(v)-d^-(v) | \leq 1`. + + INPUT: + + - ``G`` -- an undirected graph + + ALGORITHM: + + This algorithm is a random walk through the edges of the graph, which + orients the edges according to the walk. When a vertex is reached which has + no non-oriented edge (this vertex must have odd degree), the walk resumes at + another vertex of odd degree, if any. + + This algorithm has time complexity in `O(n+m)` for ``SparseGraph`` and + `O(n^2)` for ``DenseGraph``, where `m` is the number of edges in the graph + and `n` is the number of vertices in the graph. + + EXAMPLES: + + The CubeGraph with parameter 4, which is regular of even degree, has an + Eulerian orientation such that `d^+ = d^-`:: + + sage: g = graphs.CubeGraph(4) + sage: g.degree() + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] + sage: o = g.eulerian_orientation() + sage: o.in_degree() + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + sage: o.out_degree() + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + + Secondly, the Petersen Graph, which is 3 regular has an orientation such + that the difference between `d^+` and `d^-` is at most 1:: + + sage: g = graphs.PetersenGraph() + sage: o = g.eulerian_orientation() + sage: o.in_degree() + [2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + sage: o.out_degree() + [1, 1, 1, 1, 1, 2, 2, 2, 2, 2] + + TESTS:: + + sage: E0 = Graph(); E4 = Graph(4) # See trac #21741 + sage: E0.eulerian_orientation() + Digraph on 0 vertices + sage: E4.eulerian_orientation() + Digraph on 4 vertices + """ + d = DiGraph([G, []], format='vertices_and_edges') + + if not G.size(): + return d + + g = copy(G) + + # list of vertices of odd degree + odd = [x for x in g.vertex_iterator() if g.degree(x) % 2] + + # Picks the first vertex, which is preferably an odd one + if odd: + v = odd.pop() + else: + v = next(g.edge_iterator(labels=None))[0] + odd.append(v) + # Stops when there is no edge left + while True: + + # If there is an edge adjacent to the current one + if g.degree(v): + e = next(g.edge_iterator(v)) + g.delete_edge(e) + if e[0] != v: + e = (e[1], e[0], e[2]) + d.add_edge(e) + v = e[1] + + # The current vertex is isolated + else: + odd.remove(v) + + # jumps to another odd vertex if possible + if odd: + v = odd.pop() + # Else jumps to an even vertex which is not isolated + elif g.size(): + v = next(g.edge_iterator())[0] + odd.append(v) + # If there is none, we are done ! + else: + return d diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index fe9095001dd..0ce87c3ad11 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -20,12 +20,37 @@ Graph traversals :meth:`~maximum_cardinality_search` | Return an ordering of the vertices according a maximum cardinality search. :meth:`~maximum_cardinality_search_M` | Return the ordering and the edges of the triangulation produced by MCS-M. + +ALGORITHM: + +For :meth:`~lex_BFS` with ``algorithm="slow"``, :meth:`~lex_DFS`, +:meth:`~lex_UP` and :meth:`~lex_DOWN` the same generic implementation is used. +It corresponds to an implementation the generic algorithm described in +"Algorithm 1" of [Mil2017]_. + +This algorithm maintains for each vertex left in the graph a lexicographic label +corresponding to the vertices already removed. The vertex of maximal +lexicographic label is then removed, and the lexicographic labels of its +neighbors are updated. Depending on how the update is done, it corresponds to +LexBFS, LexUP, LexDFS or LexDOWN: during the `i`-th iteration of the algorithm +`n-i` (for LexBFS and LexDOWN) or `i` (for LexDFS and LexUP) is appended (for +LexBFS and LexUP) or prepended (for LexDFS and LexDOWN) to the lexicographic +labels of all neighbors of the selected vertex that are left in the graph. + +The time complexity of the algorithm is `O(mn)` for ``SparseGraph`` and +`O(\max\{mn, n^2\})` for ``DenseGraph``, where `n` is the number of vertices +and `m` is the number of edges. + +See [CK2008]_ and [Mil2017]_ for more details on the algorithm and graphs +searching. + Methods ------- """ # **************************************************************************** # Copyright (C) 2019 Georgios Giapitzakis Tzintanos # David Coudert +# 2024 Cyril Bouvier # # 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 @@ -47,15 +72,166 @@ from memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from sage.graphs.base.static_sparse_graph cimport out_degree +from sage.graphs.base.c_graph cimport CGraph, CGraphBackend +from sage.graphs.graph_decompositions.slice_decomposition cimport \ + extended_lex_BFS + + +def _lex_order_common(G, algo, reverse, tree, initial_vertex): + r""" + Perform a lexicographic search (LexBFS, LexUP, LexDFS or LexDOWN) on the + graph. + + INPUT: + + - ``G`` -- a sage graph + + - ``algo`` -- string; the name of the actual algorithm among: + + - ``"lex_BFS"`` + + - ``"lex_UP"`` + + - ``"lex_DFS"`` + + - ``"lex_DOWN"`` + + - ``reverse`` -- whether to return the vertices in discovery order, or the + reverse + + - ``tree`` -- whether to return the discovery directed tree (each vertex + being linked to the one that saw it last) + + - ``initial_vertex`` -- the first vertex to consider, can be None + + .. NOTE:: + + Loops and multiple edges are ignored and directed graphs are considered + as undirected graphs. + + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + + TESTS: + + Lex ordering of a graph on one vertex:: + + sage: Graph(1).lex_BFS(tree=True, algorithm="slow") + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_UP(tree=True) + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_DFS(tree=True) + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_DOWN(tree=True) + ([0], Digraph on 1 vertex) + + Lex ordering of an empty (di)graph is an empty sequence:: + + sage: g = Graph() + sage: g.lex_BFS(algorithm="slow") + [] + sage: g.lex_BFS(algorithm="slow", tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_UP() + [] + sage: g.lex_UP(tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_DFS() + [] + sage: g.lex_DFS(tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_DOWN() + [] + sage: g.lex_DFS(tree=True) + ([], Digraph on 0 vertices) + + Lex UP ordering of a symmetric digraph should be the same as the Lex UP + ordering of the corresponding undirected graph:: + + sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) + sage: H = DiGraph(G) + sage: G.lex_BFS(algorithm="slow") == H.lex_BFS(algorithm="slow") + True + sage: G.lex_UP() == H.lex_UP() + True + sage: G.lex_DFS() == H.lex_DFS() + True + sage: G.lex_DOWN() == H.lex_DOWN() + True + + ``initial_vertex`` should be a valid graph vertex:: + + sage: G = graphs.CompleteGraph(6) + sage: G.lex_BFS(initial_vertex='foo', algorithm="slow") + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_UP(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_DFS(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_DOWN(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + """ + if initial_vertex is not None and initial_vertex not in G: + raise ValueError(f"'{initial_vertex}' is not a graph vertex") + + if algo not in ("lex_BFS", "lex_UP", "lex_DFS", "lex_DOWN"): + raise ValueError(f"unknown algorithm '{algo}'") + + cdef size_t n = G.order() + cdef list sigma = [] + cdef dict predecessor = {} + + cdef bint right = algo in ("lex_BFS", "lex_UP") + cdef bint decr = algo in ("lex_BFS", "lex_DOWN") + + cdef size_t cur_label = n if decr else -1 + cdef int label_incr = -1 if decr else 1 + + # Perform the search + lexicographic_label = {u: deque() for u in G} + if initial_vertex is not None: + # append or appendleft does not matter here, as the deque is empty + lexicographic_label[initial_vertex].append(cur_label) + while lexicographic_label: + u = max(lexicographic_label, key=lexicographic_label.get) + lexicographic_label.pop(u) + sigma.append(u) + cur_label += label_incr + for v in G.neighbor_iterator(u): # graphs are considered undirected + if v in lexicographic_label: + if right: + lexicographic_label[v].append(cur_label) + else: + lexicographic_label[v].appendleft(cur_label) + predecessor[v] = u + + if reverse: + sigma.reverse() + + if tree: + from sage.graphs.digraph import DiGraph + edges = predecessor.items() + g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) + return sigma, g + return sigma def _is_valid_lex_BFS_order(G, L): r""" - Check whether `L` is a valid lex BFS ordering of the vertices of `G`. + Check whether ``L`` is a valid LexBFS ordering of the vertices of ``G``. Given two vertices `a` and `b` of `G = (V, E)`, we write `a < b` if `a` has a smaller label than `b`, and so if `a` is after `b` in the ordering `L`. - It is proved in [DNB1996]_ that any lex BFS ordering satisfies that, + It is proved in [DNB1996]_ that any LexBFS ordering satisfies that, if `a < b < c` and `ac \in E` and `bc \not\in E`, then there exists `d\in V` such that `c < d`, `db \in E` and `da \not\in E`. @@ -65,6 +241,21 @@ def _is_valid_lex_BFS_order(G, L): - ``L`` -- list; an ordering of the vertices of `G` + OUTPUT: + + - ``True`` if ``L`` is a LexBFS ordering of ``G``; ``False`` otherwise + + .. NOTE:: + + Loops and multiple edges are ignored for LexBFS ordering and directed + graphs are considered as undirected graphs. + + .. SEEALSO:: + + * :wikipedia:`Lexicographic_breadth-first_search` + * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a + lexicographic depth first search (LexBFS) on the graph + TESTS:: sage: from sage.graphs.traversals import _is_valid_lex_BFS_order @@ -87,7 +278,7 @@ def _is_valid_lex_BFS_order(G, L): """ # Convert G to a simple undirected graph if G.has_loops() or G.has_multiple_edges() or G.is_directed(): - G = G.to_simple(immutable=True, to_undirected=True) + G = G.to_simple(immutable=False, to_undirected=True) cdef int n = G.order() @@ -115,126 +306,7 @@ def _is_valid_lex_BFS_order(G, L): return True -cdef lex_BFS_fast_short_digraph(short_digraph sd, uint32_t *sigma, uint32_t *pred): - r""" - Perform a lexicographic breadth first search (LexBFS) on the graph. - - This method implements the `O(n+m)` time algorithm proposed in [HMPV2000]_. - - The method assumes that the initial vertex is vertex `0` and feeds input - arrays ``sigma`` and ``pred`` with respectively the ordering of the vertices - and the predecessor in the traversal. - - This algorithm uses the notion of *slices*, i.e., subsets of consecutive - vertices in the ordering, and iteratively refines the slices by subdividing - them into sub-slices to determine the exact position of the vertices in the - ordering. - - Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define - `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset - of neighbors of `v` appearing before the `i`-th vertex in the ordering - `\sigma`. Now, a slice of an ordering `\sigma` is a set of consecutive - vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in - S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < - \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a slice is the - first position of its vertices. - - The algorithm starts with a single slice containing all vertices. Then, when - the position of the `i`-th vertex `v` is fixed, it explores the neighbors of - `v` that have not yet been ordered. Consider a slice `S` such that `N(x)\cap - S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices - in `S` so that the first vertices are the neighbors of `v`. The sub-slice - containing the neighbors of `v` is assigned a new slice name, and the head - of slice `S` is set to the position of the first vertex of `S \setminus - N(v)` in the ordering `\sigma`. - - Observe that each arc of the graph can induce the subdivision of a - slice. Hence, the algorithm can use up to `m + 1` different slices. - - INPUT: - - - ``sd`` -- a ``short_digraph`` - - - ``sigma`` -- array of size ``n`` to store the ordering of the vertices - resulting from the LexBFS traversal from vertex 0. This method assumes - that this array has already been allocated. However, there is no need to - initialize it. - - - ``pred`` -- array of size ``n`` to store the predecessor of a vertex in - the LexBFS traversal from vertex 0. This method assumes that this array - has already been allocated and initialized (e.g., ``pred[i] = i``). - - EXAMPLES: - - Lex BFS ordering of the 3-sun graph:: - - sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: g.lex_BFS(algorithm='fast') - [1, 2, 3, 5, 4, 6] - """ - cdef uint32_t n = sd.n - cdef uint32_t n_slice = sd.m + 1 - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint32_t *sigma_inv = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *slice_of = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *slice_head = mem.allocarray(n_slice, sizeof(uint32_t)) - cdef uint32_t *subslice = mem.allocarray(n_slice, sizeof(uint32_t)) - cdef uint32_t i, j, k, l, a, old_k, v - cdef int wi - - # Initialize slices (slice_of, slice_head, subslice) to 0 - memset(slice_of, 0, n * sizeof(uint32_t)) - slice_head[0] = 0 - subslice[0] = 0 - - # Initialize the position of vertices in sigma - for i in range(n): - sigma[i] = i - sigma_inv[i] = i - - k = 1 - for i in range(n): - old_k = k - v = sigma[i] - - # We update the labeling of all unordered neighbors of v - for wi in range(out_degree(sd, v)): - w = sd.neighbors[v][wi] - j = sigma_inv[w] - if j <= i: - # w has already been ordered - continue - - # Get the name of the slice for position j - a = slice_of[j] - if slice_head[a] <= i: - # This slice cannot start at a position less than i - slice_head[a] = i + 1 - - # Get the position of the head of the slice - l = slice_head[a] - if l < n - 1 and slice_of[l + 1] == a: - if l != j: - # Place w at the position of the head of the slice - u = sigma[l] - sigma_inv[u], sigma_inv[w] = j, l - sigma[j], sigma[l] = u, w - j = l - slice_head[a] += 1 - if subslice[a] < old_k: - # Form a new slice - subslice[a] = k - slice_head[k] = j - subslice[k] = 0 - k += 1 - - # Finally, we update the name of the slice for position j and set v - # as predecessor of w - slice_of[j] = subslice[a] - pred[w] = v - - -def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm='fast'): +def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"): r""" Perform a lexicographic breadth first search (LexBFS) on the graph. @@ -247,62 +319,38 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm='fast') - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``) the first vertex to consider - ``algorithm`` -- string (default: ``'fast'``); algorithm to use among: - - ``'slow'`` -- this algorithm maintains for each vertex left in the graph - a code corresponding to the vertices already removed. The vertex of - maximal code (according to the lexicographic order) is then removed, and - the codes are updated. See for instance [CK2008]_ for more details. The - time complexity of this algorithm as described in [CK2008]_ is in - `O(n + m)`, where `n` is the number of vertices and `m` is the number of - edges, but our implementation is in `O(n^2)`. - - - ``'fast'`` -- this algorithm uses the notion of *slices* to refine the - position of the vertices in the ordering. The time complexity of this - algorithm is in `O(n + m)`, and our implementation follows that - complexity for ``SparseGraph``. For ``DenseGraph``, the complexity is - `O(n^2)`. See [HMPV2000]_ and next section for more details. + - ``'slow'`` -- it use the generic algorithm for all the lexicographic + searchs. See the documentation of the :mod:`~sage.graphs.traversals` + module for more details. - Loops and multiple edges are ignored during the computation of ``lex_BFS`` - and directed graphs are converted to undirected graphs. + - ``'fast'`` -- this algorithm uses the notion of *partition refinement* + to determine the position of the vertices in the ordering. The time + complexity of this algorithm is in `O(n + m)`, and our implementation + follows that complexity for ``SparseGraph``. For ``DenseGraph``, + the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more + details. This algorithm is also used to compute slice decompositions of + undirected graphs, a more thorough description can be found in the + documentation of the + :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module. - ALGORITHM: + .. NOTE:: - The ``'fast'`` algorithm is the `O(n + m)` time algorithm proposed in - [HMPV2000]_, where `n` is the number of vertices and `m` is the number of - edges. It uses the notion of *slices*, i.e., subsets of consecutive vertices - in the ordering, and iteratively refines the slices by subdividing them into - sub-slices to determine the exact position of the vertices in the ordering. - - Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define - `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset - of neighbors of `v` appearing before the `i`-th vertex in the ordering - `\sigma`. Now, a slice of an ordering `\sigma` is a set of consecutive - vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in - S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < - \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a slice is the - first position of its vertices. - - The algorithm starts with a single slice containing all vertices. Then, when - the position of the `i`-th vertex `v` is fixed, it explores the neighbors of - `v` that have not yet been ordered. Consider a slice `S` such that `N(x)\cap - S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices - in `S` so that the first vertices are the neighbors of `v`. The sub-slice - containing the neighbors of `v` is assigned a new slice name, and the head - of slice `S` is set to the position of the first vertex of `S \setminus - N(v)` in the ordering `\sigma`. - - Observe that each arc of the graph can induce the subdivision of a - slice. Hence, the algorithm can use up to `m + 1` different slices. + Loops and multiple edges are ignored during the computation of + ``lex_BFS`` and directed graphs are converted to undirected graphs. .. SEEALSO:: * :wikipedia:`Lexicographic_breadth-first_search` + * :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module + and :meth:`~sage.graphs.graph.Graph.slice_decomposition` -- compute a + slice decomposition of the graph using an extended lex BFS algorithm * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DFS` -- perform a lexicographic depth first search (LexDFS) on the graph * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a @@ -315,7 +363,7 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm='fast') A Lex BFS is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_BFS()) == g.order() + sage: set(g.lex_BFS()) == set(g) True Lex BFS ordering of the 3-sun graph:: @@ -388,104 +436,77 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm='fast') Lex BFS ordering of a graph on one vertex:: - sage: Graph(1).lex_BFS(tree=True) + sage: Graph(1).lex_BFS(algorithm="fast", tree=True) ([0], Digraph on 1 vertex) Lex BFS ordering of an empty (di)graph is an empty sequence:: sage: g = Graph() - sage: g.lex_BFS() + sage: g.lex_BFS(algorithm="fast") [] + sage: g.lex_BFS(algorithm="fast", tree=True) + ([], Digraph on 0 vertices) Lex BFS ordering of a symmetric digraph should be the same as the Lex BFS ordering of the corresponding undirected graph:: sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) sage: H = DiGraph(G) - sage: G.lex_BFS() == H.lex_BFS() + sage: G.lex_BFS(algorithm="fast") == H.lex_BFS(algorithm="fast") True ``initial_vertex`` should be a valid graph vertex:: sage: G = graphs.CompleteGraph(6) - sage: G.lex_BFS(initial_vertex='foo') + sage: G.lex_BFS(algorithm="fast", initial_vertex='foo') Traceback (most recent call last): ... ValueError: 'foo' is not a graph vertex """ if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - if algorithm not in ['slow', 'fast']: - raise ValueError("unknown algorithm '{}'".format(algorithm)) - if tree: - from sage.graphs.digraph import DiGraph - - # Convert G to a simple undirected graph - if G.has_loops() or G.has_multiple_edges() or G.is_directed(): - G = G.to_simple(immutable=True, to_undirected=True) + raise ValueError(f"'{initial_vertex}' is not a graph vertex") - cdef size_t n = G.order() - if not n: - return ([], DiGraph(sparse=True)) if tree else [] + if algorithm == "slow": + return _lex_order_common(G, "lex_BFS", reverse, tree, initial_vertex) - # Build adjacency list of G - cdef list int_to_v = list(G) + if algorithm != "fast": + raise ValueError(f"unknown algorithm '{algorithm}'") - # If an initial vertex is given, we place it at first position - cdef size_t i - if initial_vertex is not None: - i = int_to_v.index(initial_vertex) - int_to_v[0], int_to_v[i] = int_to_v[i], int_to_v[0] - - # Copying the whole graph to obtain the list of neighbors quicker than by - # calling out_neighbors. This data structure is well documented in the - # module sage.graphs.base.static_sparse_graph - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) + cdef size_t n = G.order() - # Initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint32_t *sigma_int = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *pred = mem.allocarray(n, sizeof(uint32_t)) - for i in range(n): - pred[i] = i + # For algorithm "fast" we need to convert G to an undirected graph + if G.is_directed(): + G = G.to_undirected() + # Initialize variables needed by the fast and slow algorithms + cdef CGraphBackend Gbackend = G._backend + cdef CGraph cg = Gbackend.cg() cdef list sigma = [] - cdef set vertices - cdef list code - cdef int now, v, vi, int_neighbor + cdef dict predecessor = {} + # Initialize variables needed by the fast algorithm + cdef vector[int] sigma_int + cdef vector[int] pred + # Initialize variables needed by the slow algorithm + cdef dict lexicographic_label + # Temporary variables + cdef int vi, i, initial_v_int # Perform Lex BFS - if algorithm == "fast": - lex_BFS_fast_short_digraph(sd, sigma_int, pred) - sigma = [int_to_v[sigma_int[i]] for i in range(n)] - - else: # "slow" algorithm - vertices = set(range(n)) - code = [[] for i in range(n)] - - # The initial_vertex is at position 0 and so named 0 in sd - code[0].append(n + 1) - now = 1 - while vertices: - v = max(vertices, key=code.__getitem__) - vertices.remove(v) - sigma.append(int_to_v[v]) - for vi in range(out_degree(sd, v)): - int_neighbor = sd.neighbors[v][vi] - if int_neighbor in vertices: - code[int_neighbor].append(n - now) - pred[int_neighbor] = v - now += 1 - - free_short_digraph(sd) + if initial_vertex is not None: + # we already checked that initial_vertex is in G + initial_v_int = Gbackend.get_vertex(initial_vertex) + else: + initial_v_int = -1 + extended_lex_BFS(cg, sigma_int, NULL, initial_v_int, &pred, NULL, NULL) + sigma = [ Gbackend.vertex_label(vi) for vi in sigma_int ] + predecessor = { u: sigma[i] for u, i in zip(sigma, pred) if i != -1 } if reverse: sigma.reverse() if tree: - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(n) if pred[i] != i] + from sage.graphs.digraph import DiGraph + edges = predecessor.items() g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) return sigma, g return sigma @@ -504,28 +525,15 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``) the first vertex to consider - Loops and multiple edges are ignored during the computation of ``lex_UP`` - and directed graphs are converted to undirected graphs. - - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. During the `i`-th iteration of the algorithm `i` is - appended to the codes of all neighbors of the selected vertex that are left - in the graph. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [Mil2017]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of + ``lex_UP`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -536,12 +544,16 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a lexicographic DOWN search (LexDOWN) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex UP is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_UP()) == g.order() + sage: set(g.lex_UP()) == set(g) True Lex UP ordering of the 3-sun graph:: @@ -569,103 +581,8 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): ['000', '001', '010', '101', '110', '111', '011', '100'] sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - - TESTS: - - Lex UP ordering of a graph on one vertex:: - - sage: Graph(1).lex_UP(tree=True) - ([0], Digraph on 1 vertex) - - Lex UP ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_UP() - [] - - Lex UP ordering of a symmetric digraph should be the same as the Lex UP - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_UP() == H.lex_UP() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_UP(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Convert G to a simple undirected graph - if G.has_loops() or G.has_multiple_edges() or G.is_directed(): - G = G.to_simple(immutable=True, to_undirected=True) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex UP - - cdef list code = [[] for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # Initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].append(nV + 1) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].append(now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_UP", reverse, tree, initial_vertex) def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): @@ -681,27 +598,15 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``) the first vertex to consider - Loops and multiple edges are ignored during the computation of ``lex_DFS`` - and directed graphs are converted to undirected graphs. - - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. Lex DFS differs from Lex BFS only in the way codes are - updated after each iteration. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [CK2008]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of + ``lex_DFS`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -712,12 +617,16 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a lexicographic DOWN search (LexDOWN) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex DFS is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_DFS()) == g.order() + sage: set(g.lex_DFS()) == set(g) True Lex DFS ordering of the 3-sun graph:: @@ -745,104 +654,8 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): ['000', '001', '010', '101', '110', '111', '011', '100'] sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - - TESTS: - - Lex DFS ordering of a graph on one vertex:: - - sage: Graph(1).lex_DFS(tree=True) - ([0], Digraph on 1 vertex) - - Lex DFS ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_DFS() - [] - - Lex DFS ordering of a symmetric digraph should be the same as the Lex DFS - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_DFS() == H.lex_DFS() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_DFS(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Convert G to a simple undirected graph - if G.has_loops() or G.has_multiple_edges() or G.is_directed(): - G = G.to_simple(immutable=True, to_undirected=True) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex DFS - - # We are using deque in order to prepend items in list efficiently - cdef list code = [deque([]) for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].appendleft(0) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].appendleft(now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_DFS", reverse, tree, initial_vertex) def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): @@ -858,28 +671,15 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it) - ``initial_vertex`` -- (default: ``None``) the first vertex to consider - Loops and multiple edges are ignored during the computation of ``lex_DOWN`` - and directed graphs are converted to undirected graphs. - - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. During the `i`-th iteration of the algorithm `n-i` is - prepended to the codes of all neighbors of the selected vertex that are left - in the graph. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [Mil2017]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of + ``lex_DOWN`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -890,12 +690,16 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a lexicographic UP search (LexUP) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex DOWN is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_DOWN()) == g.order() + sage: set(g.lex_DOWN()) == set(g) True Lex DOWN ordering of the 3-sun graph:: @@ -923,104 +727,8 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): ['000', '001', '010', '101', '110', '111', '011', '100'] sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - - TESTS: - - Lex DOWN ordering of a graph on one vertex:: - - sage: Graph(1).lex_DOWN(tree=True) - ([0], Digraph on 1 vertex) - - Lex DOWN ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_DOWN() - [] - - Lex DOWN ordering of a symmetric digraph should be the same as the Lex DOWN - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_DOWN() == H.lex_DOWN() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_DOWN(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Convert G to a simple undirected graph - if G.has_loops() or G.has_multiple_edges() or G.is_directed(): - G = G.to_simple(immutable=True, to_undirected=True) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex DOWN - - # We are using deque in order to prepend items in list efficiently - cdef list code = [deque([]) for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].appendleft(0) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].appendleft(nV - now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_DOWN", reverse, tree, initial_vertex) def lex_M(self, triangulation=False, labels=False, initial_vertex=None, algorithm=None): @@ -1437,8 +1145,7 @@ def lex_M_fast(G, triangulation=False, initial_vertex=None): int_to_v[0], int_to_v[i] = int_to_v[i], int_to_v[0] cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v) cdef uint32_t* p_tmp cdef uint32_t* p_end @@ -1701,8 +1408,7 @@ def maximum_cardinality_search(G, reverse=False, tree=False, initial_vertex=None raise ValueError("vertex ({0}) is not a vertex of the graph".format(initial_vertex)) cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t** p_vertices = sd.neighbors cdef uint32_t* p_tmp cdef uint32_t* p_end @@ -2076,8 +1782,7 @@ def maximum_cardinality_search_M(G, initial_vertex=None): # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex, - sort_neighbors=False) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef MemoryAllocator mem = MemoryAllocator() cdef int* alpha = mem.calloc(N, sizeof(int)) diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index c60fae5d6a6..37961e273e5 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -213,8 +213,7 @@ def is_long_hole_free(g, certificate=False): cdef int n = g.order() cdef list id_label = list(g) cdef short_digraph sd - init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label, - sort_neighbors=False) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) # Make a dense copy of the graph for quick adjacency tests cdef bitset_t dense_graph @@ -528,9 +527,9 @@ def is_weakly_chordal(g, certificate=False): contain an induced cycle of length at least 5. Using is_long_hole_free() and is_long_antihole_free() yields a run time - of `O(n+m^2)` for ``SparseGraph`` and `O(n^2\log{m} + m^2)` for - ``DenseGraph`` (where `n` is the number of vertices and `m` is the number of - edges of the graph). + of `O(n+m^2)` for ``SparseGraph`` and `O(n^2 + m^2)` for ``DenseGraph`` + (where `n` is the number of vertices and `m` is the number of edges of the + graph). EXAMPLES: diff --git a/src/sage/groups/abelian_gps/abelian_aut.py b/src/sage/groups/abelian_gps/abelian_aut.py index 7108f311a48..89551916589 100644 --- a/src/sage/groups/abelian_gps/abelian_aut.py +++ b/src/sage/groups/abelian_gps/abelian_aut.py @@ -205,6 +205,7 @@ def matrix(self): m.set_immutable() return m + class AbelianGroupAutomorphismGroup_gap(CachedRepresentation, GroupMixinLibGAP, Group, @@ -407,6 +408,7 @@ def is_subgroup_of(self, G): return False return G.gap().IsSubsemigroup(self).sage() + class AbelianGroupAutomorphismGroup(AbelianGroupAutomorphismGroup_gap): r""" The full automorphism group of a finite abelian group. @@ -467,6 +469,7 @@ def _repr_(self): """ return "Full group of automorphisms of %s" % self.domain() + class AbelianGroupAutomorphismGroup_subgroup(AbelianGroupAutomorphismGroup_gap): r""" Groups of automorphisms of abelian groups. diff --git a/src/sage/groups/abelian_gps/abelian_group_gap.py b/src/sage/groups/abelian_gps/abelian_group_gap.py index 199aa935f56..0c0d6a5f49b 100644 --- a/src/sage/groups/abelian_gps/abelian_group_gap.py +++ b/src/sage/groups/abelian_gps/abelian_group_gap.py @@ -39,6 +39,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.categories.groups import Groups + class AbelianGroupElement_gap(ElementLibGAP): r""" An element of an abelian group via libgap. @@ -184,6 +185,7 @@ def order(self): """ return self.gap().Order().sage() + class AbelianGroupElement_polycyclic(AbelianGroupElement_gap): r""" An element of an abelian group using the GAP package ``Polycyclic``. @@ -220,6 +222,7 @@ def exponents(self): """ return tuple(self.gap().Exponents().sage()) + class AbelianGroup_gap(UniqueRepresentation, GroupMixinLibGAP, ParentLibGAP, AbelianGroupBase): r""" Finitely generated abelian groups implemented in GAP. @@ -620,6 +623,7 @@ def subgroup(self, gens): gens = tuple([self(g) for g in gens]) return AbelianGroupSubgroup_gap(self.ambient(), gens) + class AbelianGroupGap(AbelianGroup_gap): r""" Abelian groups implemented using GAP. @@ -730,6 +734,7 @@ def __reduce__(self): """ return AbelianGroupGap, (self.gens_orders(),) + class AbelianGroupSubgroup_gap(AbelianGroup_gap): r""" Subgroups of abelian groups with GAP. @@ -879,6 +884,7 @@ def retract(self, x): """ return self(x) + class AbelianGroupQuotient_gap(AbelianGroup_gap): r""" Quotients of abelian groups by a subgroup. diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 645b5cc8448..75488f0772d 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -708,8 +708,7 @@ def _expand_basis_pgroup(p, alphas, vals, beta, h, rel): q = rel[i].p_primary_part(p) alphas[i] *= rel[i] // q rel[i] = q - if q < min_r: - min_r = q + min_r = min(q, min_r) if min_r == float('inf'): raise ValueError('rel must have at least one nonzero entry') val_rlast = rel[-1].valuation(p) diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index c6628c587e2..2339394224e 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -21,13 +21,14 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** -from sage.misc.cachefunc import cached_method -from sage.groups.free_group import FreeGroup -from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix from sage.combinat.root_system.coxeter_group import CoxeterGroup +from sage.groups.free_group import FreeGroup +from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement +from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity from sage.structure.richcmp import richcmp, rich_to_bool +from sage.structure.unique_representation import UniqueRepresentation class ArtinGroupElement(FinitelyPresentedGroupElement): @@ -329,7 +330,7 @@ def _left_normal_form_coxeter(self): return tuple([-delta] + form) -class ArtinGroup(FinitelyPresentedGroup): +class ArtinGroup(UniqueRepresentation, FinitelyPresentedGroup): r""" An Artin group. diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 88aa11802de..8945175cf7a 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -888,7 +888,7 @@ def links_gould_polynomial(self, varnames=None, use_symbolics=False): # Since the result of the calculation is known to be a Laurent polynomial # in t0 and t1 all exponents of ltemp must be divisable by 2 L = ltemp.parent() - lred = L({(k[0]/2, k[1]/2): v for k, v in ltemp.dict().items()}) + lred = L({(k[0]/2, k[1]/2): v for k, v in ltemp.monomial_coefficients().items()}) t0, t1 = R.gens() return lred(t0, t1) diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index 61db70925e1..e407b9d1326 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -91,6 +91,8 @@ from sage.groups.braid import BraidGroup from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer +from sage.structure.unique_representation import UniqueRepresentation + try: from sage.libs.gap.element import GapElement @@ -282,7 +284,7 @@ def _richcmp_(self, other, op): EXAMPLES:: sage: CBG3 = CubicBraidGroup(3) - sage: sorted(CBG3) # indirect doctest + sage: sorted(CBG3) # indirect doctest, random output [(c0*c1^-1)^2, c0*c1^-1*c0, c0^-1*c1*c0^-1, c0^-1*c1^-1*c0, c1*c0^-1*c1, c0^-1*c1^-1*c0^-1, c0^-1*c1^-1, c1^-1*c0*c1^-1, c0*c1^-1*c0^-1, c0^-1*c1, c0^-1*c1*c0, c0*c1^-1, c1*c0^-1, @@ -565,7 +567,7 @@ def conv2domain(laur_pol): # Class CubicBraidGroup # ############################################################################## -class CubicBraidGroup(FinitelyPresentedGroup): +class CubicBraidGroup(UniqueRepresentation, FinitelyPresentedGroup): r""" Factor groups of the Artin braid group mapping their generators to elements of order 3. @@ -750,7 +752,6 @@ def __init__(self, names, cbg_type=None): n = Integer(len(names)) if n < 1: raise ValueError("the number of strands must be an integer larger than one") - if cbg_type is None: cbg_type = CubicBraidGroup.type.Coxeter if not isinstance(cbg_type, CubicBraidGroup.type): @@ -1200,7 +1201,7 @@ def create_sympl_realization(self, m): def transvec2mat(v, bas=bas, bform=bform, fact=1): t = [x + fact*(x * bform * v) * v for x in bas] - return matrix(bform.base_ring(), t) + return matrix(bform.base_ring(), t) # ------------------------------------------------------------------------------ # setting the centralizing matrix for the case of projective group realization diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 4ac2faa783b..9b5f7e422d6 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -130,7 +130,6 @@ from sage.arith.misc import GCD as gcd from sage.categories.morphism import SetMorphism -from sage.functions.generalized import sign from sage.groups.free_group import FreeGroup from sage.groups.free_group import FreeGroupElement from sage.groups.group import Group @@ -143,7 +142,8 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.rational_field import QQ from sage.sets.set import Set -from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.richcmp import richcmp, richcmp_method +from sage.structure.unique_representation import CachedRepresentation class GroupMorphismWithGensImages(SetMorphism): @@ -362,50 +362,6 @@ def __call__(self, *values, **kwds): return super().__call__(values) -def wrap_FpGroup(libgap_fpgroup): - """ - Wrap a GAP finitely presented group. - - This function changes the comparison method of - ``libgap_free_group`` to comparison by Python ``id``. If you want - to put the LibGAP free group into a container ``(set, dict)`` then you - should understand the implications of - :meth:`~sage.libs.gap.element.GapElement._set_compare_by_id`. To - be safe, it is recommended that you just work with the resulting - Sage :class:`FinitelyPresentedGroup`. - - INPUT: - - - ``libgap_fpgroup`` -- a LibGAP finitely presented group - - OUTPUT: a Sage :class:`FinitelyPresentedGroup` - - EXAMPLES: - - First construct a LibGAP finitely presented group:: - - sage: F = libgap.FreeGroup(['a', 'b']) - sage: a_cubed = F.GeneratorsOfGroup()[0] ^ 3 - sage: P = F / libgap([ a_cubed ]); P - - sage: type(P) - - - Now wrap it:: - - sage: from sage.groups.finitely_presented import wrap_FpGroup - sage: wrap_FpGroup(P) - Finitely presented group < a, b | a^3 > - """ - assert libgap_fpgroup.IsFpGroup() - libgap_fpgroup._set_compare_by_id() - from sage.groups.free_group import wrap_FreeGroup - free_group = wrap_FreeGroup(libgap_fpgroup.FreeGroupOfFpGroup()) - relations = tuple(free_group(rel.UnderlyingElement()) - for rel in libgap_fpgroup.RelatorsOfFpGroup()) - return FinitelyPresentedGroup(free_group, relations) - - class RewritingSystem: """ A class that wraps GAP's rewriting systems. @@ -730,7 +686,8 @@ def make_confluent(self): raise ValueError('could not make the system confluent') -class FinitelyPresentedGroup(GroupMixinLibGAP, UniqueRepresentation, Group, ParentLibGAP): +@richcmp_method +class FinitelyPresentedGroup(GroupMixinLibGAP, CachedRepresentation, Group, ParentLibGAP): """ A class that wraps GAP's Finitely Presented Groups. @@ -739,7 +696,9 @@ class FinitelyPresentedGroup(GroupMixinLibGAP, UniqueRepresentation, Group, Pare You should use :meth:`~sage.groups.free_group.FreeGroup_class.quotient` to construct finitely presented groups as quotients of free - groups. + groups. Any class inheriting this one should define + ``__reduce__ = CachedRepresentation.__reduce__`` + after importing ``CachedRepresentation``. EXAMPLES:: @@ -770,7 +729,7 @@ class FinitelyPresentedGroup(GroupMixinLibGAP, UniqueRepresentation, Group, Pare """ Element = FinitelyPresentedGroupElement - def __init__(self, free_group, relations, category=None): + def __init__(self, free_group, relations, category=None, libgap_fpgroup=None): """ The Python constructor. @@ -786,6 +745,25 @@ def __init__(self, free_group, relations, category=None): sage: J is H True + sage: A5 = libgap(AlternatingGroup(5)) + sage: A5gapfp = A5.IsomorphismFpGroup().Range() + sage: A5gapfp + + sage: A5sage = A5gapfp.sage(); A5sage; + Finitely presented group < A_5.1, A_5.2 | A_5.1^5*A_5.2^-5, A_5.1^5*(A_5.2^-1*A_5.1^-1)^2, (A_5.1^-2*A_5.2^2)^2 > + sage: A5sage.inject_variables() + Traceback (most recent call last): + ... + ValueError: variable names have not yet been set using self._assign_names(...) + + Check that pickling works:: + + sage: G = FreeGroup(2) / [2 * (1, 2, -1, -2)] + sage: loads(dumps(G)) + Finitely presented group < x0, x1 | (x0*x1*x0^-1*x1^-1)^2 > + sage: G.__reduce__()[1][1] + (Free Group on generators {x0, x1}, ((x0*x1*x0^-1*x1^-1)^2,)) + sage: TestSuite(H).run() sage: TestSuite(J).run() """ @@ -794,11 +772,47 @@ def __init__(self, free_group, relations, category=None): assert isinstance(relations, tuple) self._free_group = free_group self._relations = relations - self._assign_names(free_group.variable_names()) - parent_gap = free_group.gap() / libgap([rel.gap() for rel in relations]) - ParentLibGAP.__init__(self, parent_gap) + try: + self._assign_names(free_group.variable_names()) + except ValueError: + pass + if libgap_fpgroup is None: + libgap_fpgroup = free_group.gap() / libgap([rel.gap() for rel in relations]) + ParentLibGAP.__init__(self, libgap_fpgroup) Group.__init__(self, category=category) + def __hash__(self): + """ + Make hashable. + + EXAMPLES:: + + sage: G = FreeGroup(2) / [(1, 2, 2, 1)] + sage: G.__hash__() == hash((G.free_group(), G.relations())) + True + """ + return hash((self._free_group, self._relations)) + + def __richcmp__(self, other, op): + """ + Rich comparison of ``self`` and ``other``. + + EXAMPLES:: + + sage: G1 = FreeGroup(2) / [(1, 2, 2, 1, 2, 1)] + sage: G2 = libgap(G1).sage() + sage: G1 == G2 + True + sage: G1 is G2 + False + """ + if not isinstance(other, self.__class__): + from sage.structure.richcmp import op_NE + return (op == op_NE) + self_data = (self._free_group, self._relations) + other_data = (other._free_group, other._relations) + return richcmp(self_data, other_data, op) + def _repr_(self) -> str: """ Return a string representation. @@ -812,7 +826,7 @@ def _repr_(self) -> str: sage: H._repr_() 'Finitely presented group < a, b | a, b^3 >' """ - gens = ', '.join(self.variable_names()) + gens = ', '.join(self._free_group._gen_names) rels = ', '.join(str(r) for r in self.relations()) return 'Finitely presented group ' + '< ' + gens + ' | ' + rels + ' >' @@ -1344,8 +1358,8 @@ def abelianization_map(self): sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4]) sage: H.abelianization_map() Group morphism: - From: Finitely presented group < g0, g1, g2, g3 | g1^2, g2*g1*g2^-1*g1^-1, g1*g3^-2, g0^4 > - To: Finitely presented group < f2, f3, f4 | f2^-1*f3^-1*f2*f3, f2^-1*f4^-1*f2*f4, f3^-1*f4^-1*f3*f4, f2^4, f3^4 > + From: Finitely presented group < g0, g1, g2, g3 | g1^2, g2*g1*g2^-1*g1^-1, g1*g3^-2, g0^4 > + To: Finitely presented group < f1, f2, f3 | f1^4, f2^-1*f1^-1*f2*f1, f2^4, f3^-1*f1^-1*f3*f1, f3^-1*f2^-1*f3*f2 > sage: g = FreeGroup(0) / [] sage: g.abelianization_map() Group endomorphism of Finitely presented group < | > @@ -1357,7 +1371,7 @@ def abelianization_map(self): hom_ab_fp = ab_libgap.IsomorphismFpGroup() ab_libgap_fp = hom_ab_fp.Range() hom_simply = ab_libgap_fp.IsomorphismSimplifiedFpGroup() - ab = wrap_FpGroup(hom_simply.Range()) + ab = hom_simply.Range().sage() images = [] for f in self.gens(): f0 = hom_ab_libgap.Image(f) @@ -1365,7 +1379,7 @@ def abelianization_map(self): f2 = hom_simply.Image(f1) L = f2.UnderlyingElement().LetterRepAssocWord() images.append(ab([int(j) for j in L])) - return self.hom(codomain=ab, im_gens=images) + return self.hom(codomain=ab, im_gens=images, check=False) @cached_method def abelianization_to_algebra(self, ring=QQ): @@ -1394,10 +1408,10 @@ def abelianization_to_algebra(self, ring=QQ): Defining g0, g1, g2, g3 sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4]) sage: H.abelianization_to_algebra() - (Finitely presented group < f2, f3, f4 | f2^-1*f3^-1*f2*f3, f2^-1*f4^-1*f2*f4, - f3^-1*f4^-1*f3*f4, f2^4, f3^4 >, - Multivariate Laurent Polynomial Ring in f2, f3, f4 over Rational Field, - [f2^4 - 1, f3^4 - 1], [f2^-1*f3^-2, f3^-2, f4, f3]) + (Finitely presented group < f1, f2, f3 | f1^4, f2^-1*f1^-1*f2*f1, f2^4, f3^-1*f1^-1*f3*f1, f3^-1*f2^-1*f3*f2 >, + Multivariate Laurent Polynomial Ring in f1, f2, f3 over Rational Field, + [f1^4 - 1, f2^4 - 1], + [f1^3*f2^2, f2^2, f3, f2]) sage: g=FreeGroup(0) / [] sage: g.abelianization_to_algebra() (Finitely presented group < | >, Rational Field, [], []) @@ -1438,12 +1452,9 @@ def simplification_isomorphism(self): sage: H = G / [a*b*c, a*b^2, c*b/c^2] sage: I = H.simplification_isomorphism() sage: I - Generic morphism: + Group morphism: From: Finitely presented group < a, b, c | a*b*c, a*b^2, c*b*c^-2 > To: Finitely presented group < b | > - Defn: a |--> b^-2 - b |--> b - c |--> b sage: I(a) b^-2 sage: I(b) @@ -1455,21 +1466,26 @@ def simplification_isomorphism(self): sage: F = FreeGroup(1) sage: G = F.quotient([F.0]) - sage: G.simplification_isomorphism() - Generic morphism: + sage: h = G.simplification_isomorphism(); h + Group morphism: From: Finitely presented group < x | x > To: Finitely presented group < | > - Defn: x |--> 1 + sage: h(G.gen(0)) + 1 ALGORITHM: Uses GAP. """ II = self.gap().IsomorphismSimplifiedFpGroup() - codomain = wrap_FpGroup(II.Range()) - phi = lambda x: codomain(II.ImageElm(x.gap())) - HS = self.Hom(codomain) - return GroupMorphismWithGensImages(HS, phi) + cod = II.Range().sage() + phi = [cod(II.ImageElm(x)) for x in self.gap().GeneratorsOfGroup()] + return self.hom(codomain=cod, im_gens=phi, check=False) + # II = self.gap().IsomorphismSimplifiedFpGroup() + # codomain = II.Range().sage() + # phi = lambda x: codomain(II.ImageElm(x.gap())) + # HS = self.Hom(codomain) + # return GroupMorphismWithGensImages(HS, phi) def simplified(self): """ @@ -1543,48 +1559,44 @@ def epimorphisms(self, H): sage: F = FreeGroup(3) sage: G = F / [F([1, 2, 3, 1, 2, 3]), F([1, 1, 1])] sage: H = AlternatingGroup(3) - sage: G.epimorphisms(H) - [Generic morphism: - From: Finitely presented group < x0, x1, x2 | x0*x1*x2*x0*x1*x2, x0^3 > - To: Alternating group of order 3!/2 as a permutation group - Defn: x0 |--> () - x1 |--> (1,3,2) - x2 |--> (1,2,3), - Generic morphism: - From: Finitely presented group < x0, x1, x2 | x0*x1*x2*x0*x1*x2, x0^3 > - To: Alternating group of order 3!/2 as a permutation group - Defn: x0 |--> (1,3,2) - x1 |--> () - x2 |--> (1,2,3), - Generic morphism: - From: Finitely presented group < x0, x1, x2 | x0*x1*x2*x0*x1*x2, x0^3 > - To: Alternating group of order 3!/2 as a permutation group - Defn: x0 |--> (1,3,2) - x1 |--> (1,2,3) - x2 |--> (), - Generic morphism: - From: Finitely presented group < x0, x1, x2 | x0*x1*x2*x0*x1*x2, x0^3 > - To: Alternating group of order 3!/2 as a permutation group - Defn: x0 |--> (1,2,3) - x1 |--> (1,2,3) - x2 |--> (1,2,3)] + sage: for quo in G.epimorphisms(H): + ....: for a in G.gens(): + ....: print(a, "|-->", quo(a)) + ....: print("-----") + x0 |--> () + x1 |--> (1,3,2) + x2 |--> (1,2,3) + ----- + x0 |--> (1,3,2) + x1 |--> () + x2 |--> (1,2,3) + ----- + x0 |--> (1,3,2) + x1 |--> (1,2,3) + x2 |--> () + ----- + x0 |--> (1,2,3) + x1 |--> (1,2,3) + x2 |--> (1,2,3) + ----- ALGORITHM: Uses libgap's GQuotients function. """ - from sage.misc.misc_c import prod - HomSpace = self.Hom(H) + # from sage.misc.misc_c import prod + # HomSpace = self.Hom(H) Gg = libgap(self) Hg = libgap(H) gquotients = Gg.GQuotients(Hg) res = [] # the following closure is needed to attach a specific value of quo to # each function in the different morphisms - fmap = lambda tup: (lambda a: H(prod(tup[abs(i)-1]**sign(i) for i in a.Tietze()))) + # fmap = lambda tup: (lambda a: H(prod(tup[abs(i)-1]**sign(i) for i in a.Tietze()))) for quo in gquotients: - tup = tuple(H(quo.ImageElm(i.gap()).sage()) for i in self.gens()) - fhom = GroupMorphismWithGensImages(HomSpace, fmap(tup)) + # tup = tuple(H(quo.ImageElm(i.gap()).sage()) for i in self.gens()) + # fhom = GroupMorphismWithGensImages(HomSpace, fmap(tup)) + fhom = self.hom(codomain=H, im_gens=[H(quo.ImageElm(a.gap())) for a in self.gens()]) res.append(fhom) return res @@ -1673,7 +1685,7 @@ def abelian_alexander_matrix(self, ring=QQ, simplified=True): [] sage: G = FreeGroup(3)/[(2, 1, 1), (1, 2, 2, 3, 3)] sage: A, ideal = G.abelian_alexander_matrix(simplified=True); A - [-f3^2 - f3^4 - f3^6 f3^3 + f3^6] + [-f1^2 - f1^4 - f1^6 f1^3 + f1^6] sage: g = FreeGroup(1) / [] sage: g.abelian_alexander_matrix() ([], []) @@ -1773,11 +1785,11 @@ def characteristic_varieties(self, ring=QQ, matrix_ideal=None, groebner=False): 3: Ideal (1) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring} sage: G = FreeGroup(2)/[(1,2,1,-2,-1,-2)] sage: G.characteristic_varieties() - {0: Ideal (0) of Univariate Laurent Polynomial Ring in f2 over Rational Field, - 1: Ideal (-1 + 2*f2 - 2*f2^2 + f2^3) of Univariate Laurent Polynomial Ring in f2 over Rational Field, - 2: Ideal (1) of Univariate Laurent Polynomial Ring in f2 over Rational Field} + {0: Ideal (0) of Univariate Laurent Polynomial Ring in f1 over Rational Field, + 1: Ideal (-1 + 2*f1 - 2*f1^2 + f1^3) of Univariate Laurent Polynomial Ring in f1 over Rational Field, + 2: Ideal (1) of Univariate Laurent Polynomial Ring in f1 over Rational Field} sage: G.characteristic_varieties(groebner=True) - {0: [0], 1: [-1 + f2, 1 - f2 + f2^2], 2: []} + {0: [0], 1: [-1 + f1, 1 - f1 + f1^2], 2: []} sage: G = FreeGroup(2)/[3 * (1, ), 2 * (2, )] sage: G.characteristic_varieties(groebner=True) {0: [-1 + F1, 1 + F1, 1 - F1 + F1^2, 1 + F1 + F1^2], 1: [1 - F1 + F1^2], 2: []} diff --git a/src/sage/groups/finitely_presented_named.py b/src/sage/groups/finitely_presented_named.py index 8b07c5af7df..940d761ef49 100644 --- a/src/sage/groups/finitely_presented_named.py +++ b/src/sage/groups/finitely_presented_named.py @@ -202,6 +202,7 @@ def FinitelyGeneratedAbelianPresentation(int_list): ret_rls = ret_rls + [x[0]**(-1)*x[1]**(-1)*x[0]*x[1] for x in gen_pairs] return FinitelyPresentedGroup(F, tuple(ret_rls)) + def FinitelyGeneratedHeisenbergPresentation(n=1, p=0): r""" Return a finite presentation of the Heisenberg group. @@ -292,6 +293,7 @@ def commutator(a, b): rls += [w**p for w in F.gens()] return FinitelyPresentedGroup(F, tuple(rls)) + def DihedralPresentation(n): r""" Build the Dihedral group of order `2n` as a finitely presented group. @@ -327,6 +329,7 @@ def DihedralPresentation(n): rls = F([1])**n, F([2])**2, (F([1])*F([2]))**2 return FinitelyPresentedGroup( F, rls ) + def DiCyclicPresentation(n): r""" Build the dicyclic group of order `4n`, for `n \geq 2`, as a finitely @@ -455,6 +458,7 @@ def QuaternionPresentation(): rls = F([1])**4, F([2,2,-1,-1]), F([1,2,1,-2]) return FinitelyPresentedGroup(F, rls) + def AlternatingPresentation(n): r""" Build the Alternating group of order `n!/2` as a finitely presented group. @@ -521,6 +525,7 @@ def KleinFourPresentation(): rls = F([1])**2, F([2])**2, F([-1])*F([-2])*F([1])*F([2]) return FinitelyPresentedGroup(F, rls) + def BinaryDihedralPresentation(n): r""" Build a binary dihedral group of order `4n` as a finitely presented group. @@ -561,6 +566,7 @@ def BinaryDihedralPresentation(n): rls = (x**-2 * y**2, x**-2 * z**n, x**-2 * x*y*z) return FinitelyPresentedGroup(F, rls) + def CactusPresentation(n): r""" Build the `n`-fruit cactus group as a finitely presented group. diff --git a/src/sage/groups/fqf_orthogonal.py b/src/sage/groups/fqf_orthogonal.py index 5da64f03212..e5ebd06ccdb 100644 --- a/src/sage/groups/fqf_orthogonal.py +++ b/src/sage/groups/fqf_orthogonal.py @@ -381,6 +381,7 @@ def _repr_(self): """ return "Group of isometries of \n%s\ngenerated by %s elements" % (self.invariant_form(), len(self.gens())) + class ActionOnFqf(Action): r""" Action on a finite quadratic module. diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 207c80ea348..bc952896e67 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -63,7 +63,7 @@ from sage.categories.groups import Groups from sage.groups.group import Group from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP -from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.unique_representation import CachedRepresentation from sage.libs.gap.libgap import libgap from sage.libs.gap.element import GapElement from sage.rings.integer import Integer @@ -72,6 +72,7 @@ from sage.misc.misc_c import prod from sage.structure.sequence import Sequence from sage.structure.element import coercion_model, parent +from sage.structure.richcmp import richcmp, richcmp_method def is_FreeGroup(x): @@ -672,56 +673,11 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): from sage.groups.indexed_free_group import IndexedFreeGroup return IndexedFreeGroup(index_set, names=names, **kwds) - return FreeGroup_class(names) + return FreeGroup_class(names, **kwds) -def wrap_FreeGroup(libgap_free_group): - """ - Wrap a LibGAP free group. - - This function changes the comparison method of - ``libgap_free_group`` to comparison by Python ``id``. If you want - to put the LibGAP free group into a container (set, dict) then you - should understand the implications of - :meth:`~sage.libs.gap.element.GapElement._set_compare_by_id`. To - be safe, it is recommended that you just work with the resulting - Sage :class:`FreeGroup_class`. - - INPUT: - - - ``libgap_free_group`` -- a LibGAP free group - - OUTPUT: a Sage :class:`FreeGroup_class` - - EXAMPLES: - - First construct a LibGAP free group:: - - sage: F = libgap.FreeGroup(['a', 'b']) - sage: type(F) - - - Now wrap it:: - - sage: from sage.groups.free_group import wrap_FreeGroup - sage: wrap_FreeGroup(F) - Free Group on generators {a, b} - - TESTS: - - Check that we can do it twice (see :issue:`12339`) :: - - sage: G = libgap.FreeGroup(['a', 'b']) - sage: wrap_FreeGroup(G) - Free Group on generators {a, b} - """ - assert libgap_free_group.IsFreeGroup() - libgap_free_group._set_compare_by_id() - names = tuple( str(g) for g in libgap_free_group.GeneratorsOfGroup() ) - return FreeGroup_class(names, libgap_free_group) - - -class FreeGroup_class(UniqueRepresentation, Group, ParentLibGAP): +@richcmp_method +class FreeGroup_class(CachedRepresentation, Group, ParentLibGAP): """ A class that wraps GAP's FreeGroup. @@ -736,7 +692,7 @@ class FreeGroup_class(UniqueRepresentation, Group, ParentLibGAP): """ Element = FreeGroupElement - def __init__(self, generator_names, libgap_free_group=None): + def __init__(self, generator_names, gap_group=None): """ Python constructor. @@ -757,15 +713,55 @@ def __init__(self, generator_names, libgap_free_group=None): sage: G.variable_names() ('a', 'b') """ - self._assign_names(generator_names) - if libgap_free_group is None: - libgap_free_group = libgap.FreeGroup(generator_names) - ParentLibGAP.__init__(self, libgap_free_group) + if gap_group is None: + gap_group = libgap.FreeGroup(generator_names) + ParentLibGAP.__init__(self, gap_group) if not generator_names: cat = Groups().Finite() else: cat = Groups().Infinite() Group.__init__(self, category=cat) + self._gen_names = generator_names + try: + self._assign_names(generator_names) + except ValueError: + pass + + def __hash__(self): + """ + Make hashable. + + EXAMPLES:: + + sage: F = FreeGroup(3) + sage: F.__hash__() == hash(F._gen_names) + True + """ + return hash(self._gen_names) + + def __richcmp__(self, other, op): + """ + Rich comparison of ``self`` and ``other``. + + EXAMPLES:: + + sage: G1 = FreeGroup('a, b') + sage: gg = libgap.FreeGroup('x', 'y') + sage: G2 = FreeGroup('a, b', gap_group=gg) + sage: G1 == G2 + True + sage: G1 is G2 + False + sage: G3 = FreeGroup('x, y') + sage: G1 == G3 + False + sage: G2 == G3 + False + """ + if not isinstance(other, self.__class__): + from sage.structure.richcmp import op_NE + return (op == op_NE) + return richcmp(self._gen_names, other._gen_names, op) def _repr_(self): """ @@ -777,7 +773,7 @@ def _repr_(self): sage: G._repr_() 'Free Group on generators {a, b}' """ - return 'Free Group on generators {' + ', '.join(self.variable_names()) + '}' + return 'Free Group on generators {' + ', '.join(self._gen_names) + '}' def rank(self): """ @@ -811,7 +807,7 @@ def _gap_init_(self): sage: G._gap_init_() 'FreeGroup(["x0", "x1", "x2"])' """ - gap_names = [ '"' + s + '"' for s in self.variable_names() ] + gap_names = ['"' + s + '"' for s in self._gen_names] gen_str = ', '.join(gap_names) return 'FreeGroup(['+gen_str+'])' @@ -867,9 +863,9 @@ def _element_constructor_(self, *args, **kwds): except AttributeError: return self.element_class(self, x, **kwds) if isinstance(P, FreeGroup_class): - names = {P._names[abs(i)-1] for i in x.Tietze()} - if names.issubset(self._names): - return self([i.sign()*(self._names.index(P._names[abs(i)-1])+1) + names = {P._gen_names[abs(i)-1] for i in x.Tietze()} + if names.issubset(self._gen_names): + return self([i.sign()*(self._gen_names.index(P._gen_names[abs(i)-1])+1) for i in x.Tietze()]) else: raise ValueError('generators of %s not in the group' % x) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index c3831eb2bc5..69acc7c654d 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -1257,6 +1257,17 @@ def order_from_multiple(P, m, plist=None, factorization=None, check=True, sage: K. = GF(3^60) sage: order_from_multiple(a, 3^60 - 1, operation='*', check=False) 42391158275216203514294433200 + + TESTS: + + Check that :issue:`38489` is fixed:: + + sage: from sage.groups.generic import order_from_multiple + sage: plist = [43, 257, 547, 881] + sage: m = prod(plist[:-1]) + sage: elt = Zmod(m)(plist[-1]) + sage: order_from_multiple(elt, m, plist=plist) + 6044897 """ Z = integer_ring.ZZ @@ -1325,6 +1336,8 @@ def _order_from_multiple_helper(Q, L, S): if abs(sum_left + v - (S / 2)) > abs(sum_left - (S / 2)): break sum_left += v + if not 0 < k < l: + k = l // 2 L1 = L[:k] L2 = L[k:] # recursive calls diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index b5047594a9c..d004cdf98e2 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -110,7 +110,7 @@ cdef class Group(Parent): if not isinstance(category, tuple): category = (category,) if not any(cat.is_subcategory(Groups()) for cat in category): - raise ValueError("%s is not a subcategory of %s"%(category, Groups())) + raise ValueError("%s is not a subcategory of %s" % (category, Groups())) Parent.__init__(self, base=base, category=category) def is_abelian(self): diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index ad484532f13..67a3308d787 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -137,6 +137,7 @@ def group_generators(self): gens = group_generators + class IndexedFreeGroup(IndexedGroup, Group): """ An indexed free group. @@ -297,6 +298,7 @@ def to_word_list(self): return [ (k, sign(e)) for k,e in self._sorted_items() for dummy in range(abs(e))] + class IndexedFreeAbelianGroup(IndexedGroup, AbelianGroup): """ An indexed free abelian group. diff --git a/src/sage/groups/libgap_group.py b/src/sage/groups/libgap_group.py index 09889de4af5..81c7b9dd1df 100644 --- a/src/sage/groups/libgap_group.py +++ b/src/sage/groups/libgap_group.py @@ -36,6 +36,7 @@ from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP from sage.groups.libgap_mixin import GroupMixinLibGAP + class GroupLibGAP(GroupMixinLibGAP, Group, ParentLibGAP): Element = ElementLibGAP diff --git a/src/sage/groups/matrix_gps/finitely_generated_gap.py b/src/sage/groups/matrix_gps/finitely_generated_gap.py index d3403f22f40..70f409b2b54 100644 --- a/src/sage/groups/matrix_gps/finitely_generated_gap.py +++ b/src/sage/groups/matrix_gps/finitely_generated_gap.py @@ -924,6 +924,7 @@ def invariants_of_degree(self, deg, chi=None, R=None): break return list(inv) + def _new_invariant_is_linearly_independent(F, invariants): """ EXAMPLES:: diff --git a/src/sage/groups/matrix_gps/heisenberg.py b/src/sage/groups/matrix_gps/heisenberg.py index f101c27153b..ed71925c5ed 100644 --- a/src/sage/groups/matrix_gps/heisenberg.py +++ b/src/sage/groups/matrix_gps/heisenberg.py @@ -26,6 +26,7 @@ from sage.rings.integer_ring import ZZ from copy import copy + class HeisenbergGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gap): r""" The Heisenberg group of degree `n`. diff --git a/src/sage/groups/matrix_gps/homset.py b/src/sage/groups/matrix_gps/homset.py index b9d25ae0727..02818275424 100644 --- a/src/sage/groups/matrix_gps/homset.py +++ b/src/sage/groups/matrix_gps/homset.py @@ -21,6 +21,7 @@ from sage.misc.lazy_import import lazy_import from sage.misc.superseded import deprecation + def is_MatrixGroupHomset(x): r""" Test whether ``x`` is a matrix group homset. diff --git a/src/sage/groups/matrix_gps/meson.build b/src/sage/groups/matrix_gps/meson.build new file mode 100644 index 00000000000..43968d8ed59 --- /dev/null +++ b/src/sage/groups/matrix_gps/meson.build @@ -0,0 +1,45 @@ +py.install_sources( + 'all.py', + 'binary_dihedral.py', + 'catalog.py', + 'coxeter_group.py', + 'finitely_generated.py', + 'finitely_generated_gap.py', + 'group_element.pxd', + 'group_element_gap.pxd', + 'heisenberg.py', + 'homset.py', + 'isometries.py', + 'linear.py', + 'linear_gap.py', + 'matrix_group.py', + 'matrix_group_gap.py', + 'morphism.py', + 'named_group.py', + 'named_group_gap.py', + 'orthogonal.py', + 'orthogonal_gap.py', + 'pickling_overrides.py', + 'symplectic.py', + 'symplectic_gap.py', + 'unitary.py', + 'unitary_gap.py', + subdir: 'sage/groups/matrix_gps', +) + +extension_data = { + 'group_element' : files('group_element.pyx'), + 'group_element_gap' : files('group_element_gap.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups/matrix_gps', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/groups/matrix_gps/morphism.py b/src/sage/groups/matrix_gps/morphism.py index e2fc2e0fd59..e73ba7e945a 100644 --- a/src/sage/groups/matrix_gps/morphism.py +++ b/src/sage/groups/matrix_gps/morphism.py @@ -15,6 +15,7 @@ from sage.misc.lazy_import import lazy_import + def to_libgap(x): """ Helper to convert ``x`` to a LibGAP matrix or matrix group diff --git a/src/sage/groups/matrix_gps/named_group_gap.py b/src/sage/groups/matrix_gps/named_group_gap.py index 699e351f06d..926ae0649a0 100644 --- a/src/sage/groups/matrix_gps/named_group_gap.py +++ b/src/sage/groups/matrix_gps/named_group_gap.py @@ -18,6 +18,7 @@ from sage.groups.matrix_gps.matrix_group_gap import MatrixGroup_gap from sage.groups.matrix_gps.named_group import NamedMatrixGroup_generic + class NamedMatrixGroup_gap(NamedMatrixGroup_generic, MatrixGroup_gap): def __init__(self, degree, base_ring, special, sage_name, latex_string, diff --git a/src/sage/groups/meson.build b/src/sage/groups/meson.build new file mode 100644 index 00000000000..3e2ffbb471c --- /dev/null +++ b/src/sage/groups/meson.build @@ -0,0 +1,57 @@ +py.install_sources( + 'all.py', + 'artin.py', + 'braid.py', + 'cactus_group.py', + 'class_function.py', + 'conjugacy_classes.py', + 'cubic_braid.py', + 'finitely_presented.py', + 'finitely_presented_catalog.py', + 'finitely_presented_named.py', + 'fqf_orthogonal.py', + 'free_group.py', + 'galois_group.py', + 'galois_group_perm.py', + 'generic.py', + 'group.pxd', + 'group_exp.py', + 'group_semidirect_product.py', + 'groups_catalog.py', + 'indexed_free_group.py', + 'kernel_subgroup.py', + 'libgap_group.py', + 'libgap_mixin.py', + 'libgap_morphism.py', + 'libgap_wrapper.pxd', + 'old.pxd', + 'pari_group.py', + 'raag.py', + subdir: 'sage/groups', +) + +extension_data = { + 'group' : files('group.pyx'), + 'libgap_wrapper' : files('libgap_wrapper.pyx'), + 'old' : files('old.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + +install_subdir('abelian_gps', install_dir: sage_install_dir / 'groups') +install_subdir('additive_abelian', install_dir: sage_install_dir / 'groups') +install_subdir('affine_gps', install_dir: sage_install_dir / 'groups') +install_subdir('lie_gps', install_dir: sage_install_dir / 'groups') +subdir('matrix_gps') +install_subdir('misc_gps', install_dir: sage_install_dir / 'groups') +subdir('perm_gps') +subdir('semimonomial_transformations') diff --git a/src/sage/groups/perm_gps/cubegroup.py b/src/sage/groups/perm_gps/cubegroup.py index 8eadbac39f1..7603da471f9 100644 --- a/src/sage/groups/perm_gps/cubegroup.py +++ b/src/sage/groups/perm_gps/cubegroup.py @@ -128,6 +128,7 @@ ######################################################### # written by Tom Boothby, placed in the public domain + def xproj(x, y, z, r): r""" Return the `x`-projection of `(x,y,z)` rotated by `r`. @@ -347,6 +348,7 @@ def create_poly(face, color): 32: "rbd", } + def index2singmaster(facet): """ Translate index used (eg, 43) to Singmaster facet notation (eg, diff --git a/src/sage/groups/perm_gps/meson.build b/src/sage/groups/perm_gps/meson.build new file mode 100644 index 00000000000..e986fcd964a --- /dev/null +++ b/src/sage/groups/perm_gps/meson.build @@ -0,0 +1,28 @@ +py.install_sources( + 'all.py', + 'constructor.py', + 'cubegroup.py', + 'permgroup.py', + 'permgroup_element.pxd', + 'permgroup_morphism.py', + 'permgroup_named.py', + 'permutation_groups_catalog.py', + 'symgp_conjugacy_class.py', + subdir: 'sage/groups/perm_gps', +) + +extension_data = {'permgroup_element' : files('permgroup_element.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups/perm_gps', + install: true, + include_directories: [inc_cpython, inc_ext, inc_rings], + dependencies: [py_dep, cysignals, gap, gmp], + ) +endforeach + +subdir('partn_ref') +subdir('partn_ref2') 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 a6841f43f9f..1cbb95231d5 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd @@ -14,6 +14,8 @@ from libc.string cimport memcpy from libc.stdlib cimport rand from sage.libs.gmp.mpz cimport * +from cysignals.memory cimport sig_free + cdef enum: # The following is for the automorphism group computation, says what the @@ -136,6 +138,39 @@ cdef inline void OP_join(OrbitPartition *OP, int m, int n) noexcept: if m_root != n_root: OP.num_cells -= 1 + +cdef inline void OP_make_set(OrbitPartition *OP) noexcept: + cdef int i, n = OP.degree + cdef int *new_parent, *new_rank, *new_mcr, *new_size + + cdef int *int_array = sig_malloc(4*(n+1) * sizeof(int)) + if int_array is NULL: + raise MemoryError("MemoryError allocating int_array in make_set method") + + OP.degree = n + 1 + OP.num_cells = OP.num_cells + 1 + new_parent = int_array + new_rank = int_array + (n + 1) + new_mcr = int_array + (2*n + 2) + new_size = int_array + (3 * n + 3) + + memcpy(new_parent, OP.parent, n * sizeof(int)) + memcpy(new_rank, OP.rank, n * sizeof(int)) + memcpy(new_mcr, OP.mcr, n * sizeof(int)) + memcpy(new_size, OP.size, n * sizeof(int)) + + new_parent[n] = n + new_rank[n] = 0 + new_mcr[n] = n + new_size[n] = 1 + + sig_free(OP.parent) + + OP.parent = new_parent + OP.rank = new_rank + OP.mcr = new_mcr + OP.size = new_size + cdef inline int OP_merge_list_perm(OrbitPartition *OP, int *gamma) noexcept: """ Joins the cells of OP which intersect the same orbit of gamma. diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx index 28672d00038..919deebd0de 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx @@ -145,9 +145,9 @@ def OP_represent(int n, merges, perm): print("Finding:") for i in range(n): j = OP_find(OP, i) - s = "%d -> %d"%(i, j) + s = "%d -> %d" % (i, j) if i == j: - s += ", root: size=%d, mcr=%d, rank=%d"%\ + s += ", root: size=%d, mcr=%d, rank=%d" % \ (OP.size[i], OP.mcr[i], OP.rank[i]) print(s) print("Allocating array to test merge_perm.") @@ -165,9 +165,9 @@ def OP_represent(int n, merges, perm): print("Finding:") for i in range(n): j = OP_find(OP, i) - s = "%d -> %d"%(i, j) + s = "%d -> %d" % (i, j) if i == j: - s += ", root: size=%d, mcr=%d, rank=%d"%\ + s += ", root: size=%d, mcr=%d, rank=%d" % \ (OP.size[i], OP.mcr[i], OP.rank[i]) print(s) print("Deallocating OrbitPartition.") @@ -474,7 +474,7 @@ def PS_represent(partition, splits): print(PS.entries[i], PS.levels[i], i, n) good = False if not (PS.entries[n-1] == n-1 and PS.levels[n-1] == -1): - print("Failed at i = %d!"%(n-1)) + print("Failed at i = %d!" % (n-1)) good = False if not PS.degree == n or not PS.depth == 0: print("Incorrect degree or depth!") @@ -486,8 +486,8 @@ def PS_represent(partition, splits): print("Creating PartitionStack from partition %s." % partition) PS = PS_from_list(partition) print("PartitionStack's data:") - print("entries -> %s"%[PS.entries[i] for i in range(n)]) - print("levels -> %s"%[PS.levels[i] for i in range(n)]) + print("entries -> %s" % [PS.entries[i] for i in range(n)]) + print("levels -> %s" % [PS.levels[i] for i in range(n)]) print("depth = %d, degree = %d" % (PS.depth,PS.degree)) PS_print(PS) print("Checking PS_is_discrete:") @@ -506,7 +506,7 @@ def PS_represent(partition, splits): good = True for i in range(n): if PS.entries[i] != PS2.entries[i] or PS.levels[i] != PS2.levels[i]: - print("Failed at i = %d!"%i) + print("Failed at i = %d!" % i) good = False if PS.degree != PS2.degree or PS.depth != PS2.depth: print("Failure with degree or depth!") @@ -517,7 +517,7 @@ def PS_represent(partition, splits): PS_clear(PS2) PS_print(PS2) for s in splits: - print("Splitting point %d from original:"%s) + print("Splitting point %d from original:" % s) print(PS_split_point(PS, s)) PS_print(PS) print("Getting permutation from PS2->PS:") @@ -528,7 +528,7 @@ def PS_represent(partition, splits): print("Finding first smallest:") bitset_init(b, n) i = PS_first_smallest(PS, b) - print("Minimal element is %d, bitset is:"%i) + print("Minimal element is %d, bitset is:" % i) print(bitset_string(b)) bitset_free(b) print("Finding element 1:") @@ -1187,7 +1187,7 @@ cdef bint SC_is_giant(int n, int num_perms, int *perms, float p, bitset_t suppor # get a bit lost in the group, so our random elements are more random: SC_identify(perm, n) for i from 0 <= i < 10: - SC_mult_perms(perm, perm, perms + n*(rand()%num_perms), n) + SC_mult_perms(perm, perm, perms + n*(rand() % num_perms), n) # look for elements with cycles of prime length q, m/2 < q < m-2 num_steps = ceil(-log(1-p)*log(m)/log(2)) @@ -1203,7 +1203,7 @@ cdef bint SC_is_giant(int n, int num_perms, int *perms, float p, bitset_t suppor sig_free(perm) OP_dealloc(OP) return True - SC_mult_perms(perm, perm, perms + n*(rand()%num_perms), n) + SC_mult_perms(perm, perm, perms + n*(rand() % num_perms), n) OP_dealloc(OP) sig_free(perm) return False @@ -1421,21 +1421,21 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, m = bitset_len(giant_support) from sage.arith.misc import factorial if not (order == factorial(m) or order == factorial(m)/2): - print("SC_is_giant failed: %s %s"%(str(L), order)) + print("SC_is_giant failed: %s %s" % (str(L), order)) raise AssertionError if order == factorial(n): SC_dealloc(SC) SC = SC_symmetric_group(n) SC_order(SC,0,order.value) if not order == factorial(n): - print("SC_symmetric_group failed: %s %s"%(str(L), order)) + print("SC_symmetric_group failed: %s %s" % (str(L), order)) raise AssertionError elif order == factorial(n)/2: SC_dealloc(SC) SC = SC_alternating_group(n) SC_order(SC,0,order.value) if not order == factorial(n)/2: - print("SC_alternating_group failed: %s %s"%(str(L), order)) + print("SC_alternating_group failed: %s %s" % (str(L), order)) raise AssertionError order2 = Integer(0) SC_order(SCC,0,order2.value) @@ -1540,8 +1540,8 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, if SC_is_giant(n, len(L), perm, 0.9, giant_support): from sage.arith.misc import factorial m = bitset_len(giant_support) - if order != factorial(m) and order != factorial(m)/2: - print("SC_is_giant failed: %s %s"%(str(L), order)) + if order != factorial(m) and order != factorial(m)//2: + print("SC_is_giant failed: %s %s" % (str(L), order)) raise AssertionError if order != G.order(): print("FAIL {}".format(L)) @@ -1572,13 +1572,13 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, if bool(SC_says) != bool(gap_says): print("FAIL {}".format(L)) print('element {}'.format(permy)) - print('GAP says %d, SC_contains(modify=0) says %d'%(gap_says, SC_says)) + print('GAP says %d, SC_contains(modify=0) says %d' % (gap_says, SC_says)) raise AssertionError SC_says = SC_contains(SC, 0, perm, 1) if bool(SC_says) != bool(gap_says): print("FAIL {}".format(L)) print('element {}'.format(permy)) - print('GAP says %d, SC_contains(modify=0) says %d'%(gap_says, SC_says)) + print('GAP says %d, SC_contains(modify=0) says %d' % (gap_says, SC_says)) raise AssertionError SC_random_element(SC, 0, perm) for j from 0 <= j < n: diff --git a/src/sage/groups/perm_gps/partn_ref/meson.build b/src/sage/groups/perm_gps/partn_ref/meson.build new file mode 100644 index 00000000000..092b0d0b2c8 --- /dev/null +++ b/src/sage/groups/perm_gps/partn_ref/meson.build @@ -0,0 +1,41 @@ +py.install_sources( + 'all.py', + 'automorphism_group_canonical_label.pxd', + 'canonical_augmentation.pxd', + 'data_structures.pxd', + 'double_coset.pxd', + 'refinement_binary.pxd', + 'refinement_graphs.pxd', + 'refinement_lists.pxd', + 'refinement_matrices.pxd', + 'refinement_python.pxd', + 'refinement_sets.pxd', + subdir: 'sage/groups/perm_gps/partn_ref', +) + +extension_data = { + 'automorphism_group_canonical_label' : files( + 'automorphism_group_canonical_label.pyx', + ), + 'canonical_augmentation' : files('canonical_augmentation.pyx'), + 'data_structures' : files('data_structures.pyx'), + 'double_coset' : files('double_coset.pyx'), + 'refinement_binary' : files('refinement_binary.pyx'), + 'refinement_graphs' : files('refinement_graphs.pyx'), + 'refinement_lists' : files('refinement_lists.pyx'), + 'refinement_matrices' : files('refinement_matrices.pyx'), + 'refinement_python' : files('refinement_python.pyx'), + 'refinement_sets' : files('refinement_sets.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups/perm_gps/partn_ref', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx index 0623c237b76..3a58fc0b9f6 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx @@ -78,7 +78,7 @@ cdef class PythonPartitionStack: sage: P # implicit doctest PythonPartitionStack of degree 7 and depth 0. """ - return "PythonPartitionStack of degree %d and depth %d."%(self.c_ps.degree, self.c_ps.depth) + return "PythonPartitionStack of degree %d and depth %d." % (self.c_ps.degree, self.c_ps.depth) def display(self): """ diff --git a/src/sage/groups/perm_gps/partn_ref2/meson.build b/src/sage/groups/perm_gps/partn_ref2/meson.build new file mode 100644 index 00000000000..ca6fadbfda9 --- /dev/null +++ b/src/sage/groups/perm_gps/partn_ref2/meson.build @@ -0,0 +1,19 @@ +py.install_sources( + 'all.py', + 'refinement_generic.pxd', + subdir: 'sage/groups/perm_gps/partn_ref2', +) + +extension_data = {'refinement_generic' : files('refinement_generic.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups/perm_gps/partn_ref2', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_partn_ref2], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index a4e60e771f9..1d3b0bab92c 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -2974,20 +2974,21 @@ def semidirect_product(self, N, mapping, check=True): raise ValueError(msg) # create a parallel list of the automorphisms of N in GAP - libgap.eval('N := Group({})'.format(list(N.gens()))) - gens_string = ",".join(str(x) for x in N.gens()) - homomorphism_cmd = 'alpha := GroupHomomorphismByImages(N, N, [{0}],[{1}])' - libgap.eval('morphisms := []') + N_gap = libgap.eval(f'Group({list(N.gens())})') + morphisms = libgap.eval('[]') + libgap_gens = N_gap.GeneratorsOfGroup() for alpha in mapping[1]: - images_string = ",".join(str(alpha(n)) for n in N.gens()) - libgap.eval(homomorphism_cmd.format(gens_string, images_string)) - libgap.eval('Add(morphisms, alpha)') + images = [alpha(g) for g in N.gens()] + alpha_gap = N_gap.GroupHomomorphismByImages(N_gap, + libgap_gens, images) + morphisms.Add(alpha_gap) # create the necessary homomorphism from self into the # automorphism group of N in GAP - libgap.eval('H := Group({0})'.format(mapping[0])) - libgap.eval('phi := GroupHomomorphismByImages(H, AutomorphismGroup(N),{},morphisms)'.format(mapping[0])) - libgap.eval('sdp := SemidirectProduct(H, phi, N)') - return PermutationGroup(gap_group='sdp') + H = libgap.eval(f'Group({mapping[0]})') + phi = H.GroupHomomorphismByImages(N_gap.AutomorphismGroup(), + H.GeneratorsOfGroup(), morphisms) + sdp = H.SemidirectProduct(phi, N_gap) + return PermutationGroup(gap_group=sdp) def holomorph(self): r""" @@ -3047,11 +3048,11 @@ def holomorph(self): - Kevin Halasz (2012-08-14) """ - libgap.eval('G := Group({})'.format(list(self.gens()))) - libgap.eval('aut := AutomorphismGroup(G)') - libgap.eval('alpha := InverseGeneralMapping(NiceMonomorphism(aut))') - libgap.eval('product := SemidirectProduct(NiceObject(aut),alpha,G)') - return PermutationGroup(gap_group='product') + G = libgap.eval(f'Group({list(self.gens())})') + aut = G.AutomorphismGroup() + alpha = aut.NiceMonomorphism().InverseGeneralMapping() + product = aut.NiceObject().SemidirectProduct(alpha, G) + return PermutationGroup(gap_group=product) def subgroup(self, gens=None, gap_group=None, domain=None, category=None, canonicalize=True, check=True): """ diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 2addd364812..303a2dc6214 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -596,8 +596,8 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): ... ValueError: invalid data to initialize a permutation """ - cdef UInt2* p2 - cdef UInt4* p4 + cdef const UInt2* p2 + cdef const UInt4* p4 cdef int i cdef UInt d @@ -1124,7 +1124,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): ... AssertionError: (1,3,5)(2,4,6) and [1, 2, 3, 4, 5, 6, 7] should have the same length """ - assert len(x) == self.n, '%s and %s should have the same length'%(self, x) + assert len(x) == self.n, '%s and %s should have the same length' % (self, x) return [ x[self.perm[i]] for i in range(self.n) ] cpdef ClonableIntArray _act_on_array_on_position(self, ClonableIntArray x): @@ -1144,7 +1144,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): cdef int i cdef ClonableIntArray y cdef int l = self.n - assert x._len == l, '%s and %s should have the same length'%(self, x) + assert x._len == l, '%s and %s should have the same length' % (self, x) y = x.clone() for i in range(l): y._list[i] = x._list[self.perm[i]] diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 071e833db31..7e9d3a0f358 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -187,10 +187,10 @@ def __classcall__(cls, domain): try: domain = Integer(domain) except TypeError: - raise TypeError("domain (={}) must be an integer >= 0 or a finite set (but domain has type {})".format(domain, type(domain))) + raise TypeError(f"domain (={domain}) must be an integer >= 0 or a finite set (but domain has type {type(domain)})") if domain < 0: - raise ValueError("domain (={}) must be an integer >= 0 or a list".format(domain)) + raise ValueError(f"domain (={domain}) must be an integer >= 0 or a list") domain = list(range(1, domain + 1)) v = FiniteEnumeratedSet(domain) else: @@ -273,8 +273,8 @@ def __init__(self, domain=None): self._domain = domain self._deg = len(self._domain) - self._domain_to_gap = {key: i+1 for i, key in enumerate(self._domain)} - self._domain_from_gap = {i+1: key for i, key in enumerate(self._domain)} + self._domain_to_gap = {key: i + 1 for i, key in enumerate(self._domain)} + self._domain_from_gap = {i + 1: key for i, key in enumerate(self._domain)} # Create the generators for the symmetric group if self._deg <= 1: @@ -299,7 +299,7 @@ def _gap_init_(self, gap=None): sage: S._gap_init_() 'SymmetricGroup(3)' """ - return 'SymmetricGroup({})'.format(self.degree()) + return f'SymmetricGroup({self.degree()})' @cached_method def index_set(self): @@ -340,7 +340,7 @@ def _repr_(self): sage: A = SymmetricGroup([2,3,7]); A Symmetric group of order 3! as a permutation group """ - return "Symmetric group of order {}! as a permutation group".format(self.degree()) + return f"Symmetric group of order {self.degree()}! as a permutation group" def _coerce_map_from_(self, G): """ @@ -426,6 +426,50 @@ def simple_reflection(self, i): """ return self([(i, self._domain[self._domain.index(i)+1])], check=False) + @cached_method + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections` + + EXAMPLES:: + + sage: S5 = SymmetricGroup(5) + sage: S5.reflection_index_set() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + """ + return tuple(range(len(self.reflections()))) + + def reflection(self, i): + r""" + Return the `i`-th reflection of ``self``. + + For `i` in `1,\dots,N`, this gives the `i`-th reflection of + ``self``. + + .. SEEALSO:: + + - :meth:`reflections_index_set` + - :meth:`reflections` + + EXAMPLES:: + + sage: S4 = SymmetricGroup(4) + sage: for i in S4.reflection_index_set(): + ....: print('%s %s'%(i, S4.reflection(i))) + 0 (1,2) + 1 (1,3) + 2 (1,4) + 3 (2,3) + 4 (2,4) + 5 (3,4) + """ + return self.reflections()[i] + def reflections(self): """ Return the list of all reflections in ``self``. @@ -658,11 +702,9 @@ def algebra(self, base_ring, category=None): (3,5) """ from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra - domain = self.domain() - if list(domain) == list(range(1, len(domain) + 1)): + if all(i == j for i, j in enumerate(self.domain(), start=1)): return SymmetricGroupAlgebra(base_ring, self, category=category) - else: - return super().algebra(base_ring) + return super().algebra(base_ring) Element = SymmetricGroupElement @@ -2146,7 +2188,7 @@ def __call__(self, G): for H in self: if H.is_isomorphic(G): return H - raise ValueError("{} is not a transitive group of degree {}".format(G, self._degree)) + raise ValueError(f"{G} is not a transitive group of degree {self._degree}") @cached_method def cardinality(self): @@ -2652,7 +2694,7 @@ def __init__(self, n, q, name='a'): self._base_ring = GF(q, name=name) self._n = n - def __str__(self): + def __str__(self) -> str: """ EXAMPLES:: @@ -2741,16 +2783,13 @@ def __init__(self, n, q, name='a'): q = q.cardinality() if q not in NonNegativeIntegers(): raise ValueError('q must be a prime power or a finite field') - if n == 1: - id = 'Group([()])' - else: - id = 'PSL(%s,%s)' % (n, q) + id = 'Group([()])' if n == 1 else f'PSL({n},{q})' PermutationGroup_generic.__init__(self, gap_group=id) self._q = q self._base_ring = GF(q, name=name) self._n = n - def __str__(self): + def __str__(self) -> str: """ EXAMPLES:: @@ -2806,8 +2845,8 @@ def ramification_module_decomposition_hurwitz_curve(self): F = self.base_ring() q = F.order() - libgap.Read(str(Path(SAGE_EXTCODE) / 'gap' / 'joyner' / - 'hurwitz_crv_rr_sp.gap')) + libgap.Read(Path(SAGE_EXTCODE) / 'gap' / 'joyner' / + 'hurwitz_crv_rr_sp.gap') mults = libgap.eval(f"ram_module_hurwitz({q})") return mults.sage() @@ -2850,8 +2889,8 @@ def ramification_module_decomposition_modular_curve(self): raise ValueError("degree must be 2") F = self.base_ring() q = F.order() - libgap.Read(str(Path(SAGE_EXTCODE) / 'gap' / 'joyner' / - 'modular_crv_rr_sp.gap')) + libgap.Read(Path(SAGE_EXTCODE) / 'gap' / 'joyner' / + 'modular_crv_rr_sp.gap') mults = libgap.eval(f"ram_module_X({q})") return mults.sage() @@ -2907,7 +2946,7 @@ def __init__(self, n, q, name='a'): self._base_ring = GF(q, name=name) self._n = n - def __str__(self): + def __str__(self) -> str: """ EXAMPLES:: @@ -3100,7 +3139,7 @@ def base_ring(self): """ return self._base_ring - def __str__(self): + def __str__(self) -> str: """ EXAMPLES:: @@ -3477,16 +3516,14 @@ class SmallPermutationGroup(PermutationGroup_generic): sage: G = SmallPermutationGroup(12,4); G Group of order 12 and GAP Id 4 as a permutation group sage: G.gens() - ((1,2)(3,5)(4,10)(6,8)(7,12)(9,11), - (1,3)(2,5)(4,7)(6,9)(8,11)(10,12), - (1,4,8)(2,6,10)(3,7,11)(5,9,12)) + ((4,5), (1,2), (3,4,5)) sage: G.character_table() # needs sage.rings.number_field [ 1 1 1 1 1 1] - [ 1 -1 -1 1 1 -1] + [ 1 -1 1 -1 1 -1] [ 1 -1 1 1 -1 1] - [ 1 1 -1 1 -1 -1] - [ 2 0 -2 -1 0 1] - [ 2 0 2 -1 0 -1] + [ 1 1 1 -1 -1 -1] + [ 2 0 -1 -2 0 1] + [ 2 0 -1 2 0 -1] sage: def numgps(n): return ZZ(libgap.NumberSmallGroups(n)) sage: all(SmallPermutationGroup(n,k).id() == [n,k] ....: for n in [1..64] for k in [1..numgps(n)]) @@ -3495,11 +3532,11 @@ class SmallPermutationGroup(PermutationGroup_generic): sage: H.is_abelian() False sage: [H.centralizer(g) for g in H.conjugacy_classes_representatives()] - [Subgroup generated by [(1,2)(3,6)(4,5), (1,3,5)(2,4,6)] of + [Subgroup generated by [(1,3), (2,3)] of (Group of order 6 and GAP Id 1 as a permutation group), - Subgroup generated by [(1,2)(3,6)(4,5)] of + Subgroup generated by [(2,3)] of (Group of order 6 and GAP Id 1 as a permutation group), - Subgroup generated by [(1,3,5)(2,4,6), (1,5,3)(2,6,4)] of + Subgroup generated by [(1,2,3)] of (Group of order 6 and GAP Id 1 as a permutation group)] """ diff --git a/src/sage/groups/semimonomial_transformations/meson.build b/src/sage/groups/semimonomial_transformations/meson.build new file mode 100644 index 00000000000..402cb5244e5 --- /dev/null +++ b/src/sage/groups/semimonomial_transformations/meson.build @@ -0,0 +1,22 @@ +py.install_sources( + 'all.py', + 'semimonomial_transformation.pxd', + 'semimonomial_transformation_group.py', + subdir: 'sage/groups/semimonomial_transformations', +) + +extension_data = { + 'semimonomial_transformation' : files('semimonomial_transformation.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/groups/semimonomial_transformations', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/groups/semimonomial_transformations/semimonomial_transformation.pyx b/src/sage/groups/semimonomial_transformations/semimonomial_transformation.pyx index 72042533883..f3abbd13129 100644 --- a/src/sage/groups/semimonomial_transformations/semimonomial_transformation.pyx +++ b/src/sage/groups/semimonomial_transformations/semimonomial_transformation.pyx @@ -243,8 +243,8 @@ cdef class SemimonomialTransformation(MultiplicativeGroupElement): sage: SemimonomialTransformationGroup(F, 4).an_element() # indirect doctest ((a, 1, 1, 1); (1,4,3,2), Ring endomorphism of Finite Field in a of size 3^2 Defn: a |--> 2*a + 1) """ - return "(%s; %s, %s)"%(self.v, self.perm.cycle_string(), - self.get_autom()) + return "(%s; %s, %s)" % (self.v, self.perm.cycle_string(), + self.get_autom()) cpdef _richcmp_(left, _right, int op): """ diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 97111a0cbfa..2f050801f25 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -482,15 +482,15 @@ def _unicode_art_(self): from sage.typeset.unicode_art import UnicodeArt def arrow_art(d): - d_str = [u' d_{0} '.format(d)] - arrow = u' <' + u'─' * (len(d_str[0]) - 3) + u' ' + d_str = [' d_{0} '.format(d)] + arrow = ' <' + '─' * (len(d_str[0]) - 3) + ' ' d_str.append(arrow) return UnicodeArt(d_str, baseline=0) def vector_art(d): v = self.vector(d) if not v.degree(): - return UnicodeArt([u'0']) + return UnicodeArt(['0']) w = matrix(v).transpose() return w._unicode_art_() @@ -499,17 +499,17 @@ def vector_art(d): for ordered in chain_complex.ordered_degrees(): ordered = list(reversed(ordered)) if not ordered: - return UnicodeArt([u'0']) + return UnicodeArt(['0']) result_ordered = vector_art(ordered[0] + chain_complex.degree_of_differential()) for n in ordered: result_ordered += arrow_art(n) + vector_art(n) result = [result_ordered] + result if len(result) == 0: - return UnicodeArt([u'0']) + return UnicodeArt(['0']) concatenated = result[0] for r in result[1:]: - concatenated += UnicodeArt([u' ... ']) + r + concatenated += UnicodeArt([' ... ']) + r return concatenated def is_cycle(self): @@ -1721,33 +1721,33 @@ def _unicode_art_(self): def arrow_art(n): d_n = self.differential(n) if not d_n.nrows() or not d_n.ncols(): - return UnicodeArt([u'<──']) + return UnicodeArt(['<──']) d_str = list(d_n._unicode_art_()) - arrow = u'<' + u'─' * (len(d_str[0]) - 1) + arrow = '<' + '─' * (len(d_str[0]) - 1) d_str.append(arrow) return UnicodeArt(d_str) def module_art(n): C_n = self.free_module(n) if not C_n.rank(): - return UnicodeArt([u' 0 ']) + return UnicodeArt([' 0 ']) else: - return UnicodeArt([u' C_{0} '.format(n)]) + return UnicodeArt([' C_{0} '.format(n)]) result = [] for ordered in self.ordered_degrees(): ordered = list(reversed(ordered)) if not ordered: - return UnicodeArt([u'0']) + return UnicodeArt(['0']) result_ordered = module_art(ordered[0] + self.degree_of_differential()) for n in ordered: result_ordered += arrow_art(n) + module_art(n) result = [result_ordered] + result if len(result) == 0: - return UnicodeArt([u'0']) + return UnicodeArt(['0']) concatenated = result[0] for r in result[1:]: - concatenated += UnicodeArt([u' ... ']) + r + concatenated += UnicodeArt([' ... ']) + r return concatenated def _latex_(self): diff --git a/src/sage/homology/chains.py b/src/sage/homology/chains.py index 3489d69286c..41bdf06776c 100644 --- a/src/sage/homology/chains.py +++ b/src/sage/homology/chains.py @@ -25,7 +25,7 @@ from sage.structure.element import coercion_model -class CellComplexReference(): +class CellComplexReference: def __init__(self, cell_complex, degree, cells=None): """ diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index bb576a56462..60f2ccc77f2 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -444,7 +444,7 @@ def _test_duality(self, **options): tester = self._tester(**options) dual = self.dual() dims = [a[0] for a in self._indices] - for dim in range(max(max(dims), tester._max_runs) + 1): + for dim in range(max(max(dims), tester._max_runs) + 1): n = len(self.basis(dim)) m = matrix(n, n, [a.eval(b) for a in self.basis(dim) for b in dual.basis(dim)]) tester.assertEqual(m, 1, f"error in dimension {dim}") @@ -633,7 +633,7 @@ def _acted_upon_(self, a, self_on_left): = \langle f, \phi_L (a \otimes x) \rangle, for `f \in H_m`, `a \in A^n`, and `x \in - H^{m-n}`. Somewhat more succintly, we define the action `f + H^{m-n}`. Somewhat more succinctly, we define the action `f \cdot a` by .. MATH:: @@ -690,10 +690,9 @@ def _acted_upon_(self, a, self_on_left): if m <= n: return self.parent().zero() - if not self_on_left: # i.e., module element on left + if not self_on_left: # i.e., module element on left a = a.antipode() P = self.parent() - B = list(P.basis(m-n)) return P._from_dict({x.support()[0]: self.eval(a * x) for x in sorted(self.parent().dual().basis(m-n))}) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index a3da2cdc403..7aa5ac779d9 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -1464,12 +1464,13 @@ def riemann_sum( b = interval_g[0][1] func = symbolic_expression(f).function(x) division = [a]+[a+random()*(b-a) for i in range(n-1)]+[b] - division = sorted([i for i in division]) + division = sorted(division) xs = [division[i]+random()*(division[i+1]-division[i]) for i in range(n)] ys = [func(x_val) for x_val in xs] rects = Graphics() for i in range(n): - body = [[division[i],0],[division[i],ys[i]],[division[i+1],ys[i]],[division[i+1],0]] + body = [[division[i], 0], [division[i], ys[i]], + [division[i+1], ys[i]], [division[i+1], 0]] if ys[i].n() > 0: color_rect = 'green' else: @@ -1486,7 +1487,7 @@ def riemann_sum( ["$i$", "$[x_{i-1},x_i]$", r"$\eta_i$", r"$f(\eta_i)$", "$x_{i}-x_{i-1}$"] ] + [ [i+1,[division[i],division[i+1]],xs[i],ys[i],delka_intervalu[i]] for i in range(n) - ], header_row=True)) + ], header_row=True)) html(r'Riemann sum: $\displaystyle\sum_{i=1}^{%s} f(\eta_i)(x_i-x_{i-1})=%s$ ' % (latex(n),latex(sum([ys[i]*delka_intervalu[i] for i in range(n)])))) diff --git a/src/sage/interacts/meson.build b/src/sage/interacts/meson.build new file mode 100644 index 00000000000..4889c06f9fa --- /dev/null +++ b/src/sage/interacts/meson.build @@ -0,0 +1,24 @@ +py.install_sources( + 'algebra.py', + 'all.py', + 'calculus.py', + 'fractals.py', + 'geometry.py', + 'library.py', + 'statistics.py', + subdir: 'sage/interacts', +) + +extension_data = {'library_cython' : files('library_cython.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/interacts', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/interfaces/cleaner.py b/src/sage/interfaces/cleaner.py index 54bfac779ac..f731205b652 100644 --- a/src/sage/interfaces/cleaner.py +++ b/src/sage/interfaces/cleaner.py @@ -1,3 +1,4 @@ +# sage_setup: distribution = sagemath-repl """ Interface to the Sage cleaner diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index 5454cb868c7..7d1ac74b7c1 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -273,7 +273,7 @@ def interact(self): 65: 850000000, 70: 2900000000} - def _B1_table_value(self, factor_digits, min=15, max=70): + def _B1_table_value(self, factor_digits, min_val=15, max_val=70): """ Return key in ``_recommended_B1_list``. @@ -290,9 +290,8 @@ def _B1_table_value(self, factor_digits, min=15, max=70): sage: ecm._B1_table_value(33) 35 """ - if factor_digits < min: - factor_digits = min - if factor_digits > max: + factor_digits = max(factor_digits, min_val) + if factor_digits > max_val: raise ValueError('too many digits') step = 5 return ((factor_digits + step - 1) // step) * step diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index dddebf0d1df..91f8efcf651 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -85,7 +85,7 @@ # "return", "break", or "continue", raising an exception, ...) -class gc_disabled(): +class gc_disabled: """ This is a "with" statement context manager. Garbage collection is disabled within its scope. Nested usage is properly handled. diff --git a/src/sage/interfaces/four_ti_2.py b/src/sage/interfaces/four_ti_2.py index 7a1034243ad..8d93ce9c5e3 100644 --- a/src/sage/interfaces/four_ti_2.py +++ b/src/sage/interfaces/four_ti_2.py @@ -41,7 +41,7 @@ import os -class FourTi2(): +class FourTi2: r""" An interface to the program 4ti2. @@ -516,13 +516,13 @@ def _magic3x3(self): """ from sage.matrix.constructor import matrix return matrix(ZZ, 7, 9, - [[1, 1, 1, -1, -1, -1, 0, 0, 0], - [1, 1, 1, 0, 0, 0, -1, -1, -1], - [0, 1, 1, -1, 0, 0, -1, 0, 0], - [1, 0, 1, 0, -1, 0, 0, -1, 0], - [1, 1, 0, 0, 0, -1, 0, 0, -1], - [0, 1, 1, 0, -1, 0, 0, 0, -1], - [1, 1, 0, 0, -1, 0, -1, 0, 0]]) + [[1, 1, 1, -1, -1, -1, 0, 0, 0], + [1, 1, 1, 0, 0, 0, -1, -1, -1], + [0, 1, 1, -1, 0, 0, -1, 0, 0], + [1, 0, 1, 0, -1, 0, 0, -1, 0], + [1, 1, 0, 0, 0, -1, 0, 0, -1], + [0, 1, 1, 0, -1, 0, 0, 0, -1], + [1, 1, 0, 0, -1, 0, -1, 0, 0]]) # The instance that should be used outside this file. diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 39f41ec2045..47cd5570355 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -227,8 +227,26 @@ # between ' and ". FRICAS_INIT_CODE = ( ")set functions compile on", - ")set message autoload off", - ")set message type off", + ")set message any on", + ")set message autoload off", + ")set message bottomup off", + ")set message dropmap off", + ")set message expose off", + ")set message file off", + ")set message frame off", + ")set message highlighting off", + ")set message instant off", + ")set message insteach off", + ")set message interponly off", + ")set message prompt step", + ")set message selection off", + ")set message set off", + ")set message startup on", + ")set message storage off", + ")set message testing off", + ")set message time off", + ")set message type off", + ")set message void off", ")set output length " + str(FRICAS_LINE_LENGTH), ")lisp (setf |$ioHook|" " (lambda (x &optional args)" @@ -1963,7 +1981,7 @@ def _sage_(self): if head == "Factored": l = P.new('[[f.factor, f.exponent] for f in factors(%s)]' % self._name).sage() - return Factorization([(p, e) for p, e in l]) + return Factorization(list(l)) if head == "UnivariatePolynomial": base_ring = self._get_sage_type(domain[2]) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index ffd4d48723c..139f8b3acf5 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1314,7 +1314,7 @@ def help(self, s, pager=True): sline = int(sline) - 1 if self.is_remote(): self._get_tmpfile() - with open(self._local_tmpfile(), "r", + with open(self._local_tmpfile(), encoding=gap_encoding) as fobj: help = fobj.read() if pager: diff --git a/src/sage/interfaces/gfan.py b/src/sage/interfaces/gfan.py index a169848c678..a37e8825b5b 100644 --- a/src/sage/interfaces/gfan.py +++ b/src/sage/interfaces/gfan.py @@ -47,7 +47,7 @@ from sage.misc.decorators import rename_keyword -class Gfan(): +class Gfan: """ Interface to Anders Jensen's Groebner Fan program. """ diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 00c9ff429c3..5894980b332 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -1,3 +1,4 @@ +# sage.doctest: needs giac r""" Pexpect Interface to Giac diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index c15f0342de4..6f2fb118968 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -275,7 +275,7 @@ def __call__(self, x, name=None): Check conversion of Booleans (:issue:`28705`):: - sage: giac(True) + sage: giac(True) # needs giac true sage: maxima(True) true @@ -343,7 +343,7 @@ def _coerce_impl(self, x, use_special=True): Check that python type ``complex`` can be converted (:issue:`31775`):: - sage: giac(complex(I))**2 # should not return `j^2` + sage: giac(complex(I))**2 # should not return `j^2` # needs giac -1 """ if isinstance(x, bool): @@ -444,9 +444,12 @@ def _relation_symbols(self): sage: symbols[operator.eq] '==' """ - return dict([(operator.eq, self._equality_symbol()), (operator.ne, self._inequality_symbol()), - (operator.lt, self._lessthan_symbol()), (operator.le, "<="), - (operator.gt, self._greaterthan_symbol()), (operator.ge, ">=")]) + return {operator.eq: self._equality_symbol(), + operator.ne: self._inequality_symbol(), + operator.lt: self._lessthan_symbol(), + operator.le: "<=", + operator.gt: self._greaterthan_symbol(), + operator.ge: ">="} def _exponent_symbol(self): """ @@ -1193,12 +1196,12 @@ def _repr_(self): sage: gap(2) 2 sage: x = var('x') - sage: giac(x) + sage: giac(x) # needs giac sageVARx - sage: giac(5) + sage: giac(5) # needs giac 5 sage: M = matrix(QQ,2,range(4)) - sage: giac(M) + sage: giac(M) # needs giac [[0,1],[2,3]] sage: x = var('x') # optional - maple sage: maple(x) # optional - maple @@ -1338,7 +1341,7 @@ def __bool__(self): By default this returns ``True`` for elements that are considered to be not ``False`` by the interface (:issue:`28705`):: - sage: bool(giac('"a"')) + sage: bool(giac('"a"')) # needs giac True """ P = self._check_valid() diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index 85d4e82772d..00de1d4b455 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -437,10 +437,10 @@ def homology(self, n): m1 = __chcm_mat__(echcm1, n) m2 = __chcm_mat__(echcm1, n + 1) homology = __homologie__(m1, m2) - lhomomology = [i for i in EclListIterator(homology)] + lhomomology = list(EclListIterator(homology)) res = [] for component in lhomomology: - pair = [i for i in EclListIterator(component)] + pair = list(EclListIterator(component)) res.append(pair[0].python()) return HomologyGroup(len(res), ZZ, res) @@ -1309,7 +1309,7 @@ def SFiniteSimplicialSet(ksimpset, limit): bases.append(lbasis_k) names.append(names_k) all_simplices = __sfinitesimplicialset_aux1__(ksimpset._kenzo, limit) - lall_simplices = [i for i in EclListIterator(all_simplices)] + lall_simplices = list(EclListIterator(all_simplices)) dim = 1 for Kdim in lall_simplices: for simp in Kdim: diff --git a/src/sage/interfaces/latte.py b/src/sage/interfaces/latte.py index 3d78543257c..775b8cff908 100644 --- a/src/sage/interfaces/latte.py +++ b/src/sage/interfaces/latte.py @@ -205,7 +205,7 @@ def count(arg, ehrhart_polynomial=False, multivariate_generating_function=False, if not ans: # opening a file is slow (30e-6s), so we read the file # numOfLatticePoints only in case of a IndexError above - with open(tempd.name + '/numOfLatticePoints', 'r') as f: + with open(tempd.name + '/numOfLatticePoints') as f: ans = f.read() if raw_output: diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index d20340717a5..3ec584931cc 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2906,8 +2906,7 @@ def write(self, s): pol_curr, col_curr = map(int, match.groups()) if pol_curr != 0: - if self.max_deg < self.curr_deg: - self.max_deg = self.curr_deg + self.max_deg = max(self.max_deg, self.curr_deg) if style == "sage" and verbosity >= 1: print("Leading term degree: %2d. Critical pairs: %d." % diff --git a/src/sage/interfaces/magma_free.py b/src/sage/interfaces/magma_free.py index 34d5b70f29a..5938e9fa4af 100644 --- a/src/sage/interfaces/magma_free.py +++ b/src/sage/interfaces/magma_free.py @@ -26,8 +26,10 @@ def magma_free_eval(code, strip=True, columns=0): Use the free online MAGMA calculator to evaluate the given input code and return the answer as a string. - LIMITATIONS: The code must evaluate in at most 20 seconds - and there is a limitation on the amount of RAM. + .. WARNING:: + + The code must evaluate in at most 120 seconds + and there is a limitation on the amount of RAM. EXAMPLES:: diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 1e6f85cfae0..1b1e97f3578 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1244,8 +1244,8 @@ def reduce_load_MaximaLib(): sage.functions.other.conjugate: "$CONJUGATE", } # we compile the dictionary -sage_op_dict = dict([(k, EclObject(sage_op_dict[k])) for k in sage_op_dict]) -max_op_dict = dict([(sage_op_dict[k], k) for k in sage_op_dict]) +sage_op_dict = {k: EclObject(sage_op_dict[k]) for k in sage_op_dict} +max_op_dict = {sage_op_dict[k]: k for k in sage_op_dict} # Here we correct the dictionaries for some simple operators @@ -1437,7 +1437,7 @@ def max_at_to_sage(expr): subsvalues = {v.lhs(): v.rhs() for v in max_to_sr(subsarg)} else: v = max_to_sr(subsarg) - subsvalues = dict([(v.lhs(), v.rhs())]) + subsvalues = {v.lhs(): v.rhs()} return SR(arg).subs(subsvalues) @@ -1657,7 +1657,7 @@ def sr_to_max(expr): return EclObject(special_sage_to_max[op](*[sr_to_max(o) for o in expr.operands()])) elif op == tuple: return EclObject(([mlist], list(sr_to_max(op) for op in expr.operands()))) - elif not (op in sage_op_dict): + elif op not in sage_op_dict: # Maxima does some simplifications automatically by default # so calling maxima(expr) can change the structure of expr # op_max=caar(maxima(expr).ecl()) diff --git a/src/sage/interfaces/meson.build b/src/sage/interfaces/meson.build new file mode 100644 index 00000000000..4a12245d532 --- /dev/null +++ b/src/sage/interfaces/meson.build @@ -0,0 +1,79 @@ +py.install_sources( + 'abc.py', + 'all.py', + 'all__sagemath_polyhedra.py', + 'axiom.py', + 'cleaner.py', + 'ecm.py', + 'expect.py', + 'four_ti_2.py', + 'fricas.py', + 'frobby.py', + 'gap.py', + 'gap3.py', + 'gap_workspace.py', + 'genus2reduction.py', + 'gfan.py', + 'giac.py', + 'gnuplot.py', + 'gp.py', + 'interface.py', + 'jmoldata.py', + 'kash.py', + 'kenzo.py', + 'latte.py', + 'lie.py', + 'lisp.py', + 'macaulay2.py', + 'magma.py', + 'magma_free.py', + 'maple.py', + 'mathematica.py', + 'mathics.py', + 'matlab.py', + 'maxima.py', + 'maxima_abstract.py', + 'maxima_lib.py', + 'mupad.py', + 'mwrank.py', + 'octave.py', + 'phc.py', + 'polymake.py', + 'povray.py', + 'process.pxd', + 'psage.py', + 'qepcad.py', + 'qsieve.py', + 'quit.py', + 'r.py', + 'read_data.py', + 'rubik.py', + 'sage-maxima.lisp', + 'sage0.py', + 'scilab.py', + 'singular.py', + 'sympy.py', + 'sympy_wrapper.py', + 'tab_completion.py', + 'tachyon.py', + 'tests.py', + 'tides.py', + subdir: 'sage/interfaces', +) + +extension_data = { + 'process' : files('process.pyx'), + 'sagespawn' : files('sagespawn.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/interfaces', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/interfaces/quit.py b/src/sage/interfaces/quit.py index 9392ffe094f..c7c1643be83 100644 --- a/src/sage/interfaces/quit.py +++ b/src/sage/interfaces/quit.py @@ -1,3 +1,4 @@ +# sage_setup: distribution = sagemath-repl """ Quitting interfaces """ diff --git a/src/sage/interfaces/read_data.py b/src/sage/interfaces/read_data.py index cc6f0b82fab..325c8ce5cb1 100644 --- a/src/sage/interfaces/read_data.py +++ b/src/sage/interfaces/read_data.py @@ -36,7 +36,7 @@ def read_data(f, t): sage: l = read_data(indata, RealField(17)); l [1.234, 5.678] """ - with open(f, "r") as fp: + with open(f) as fp: l = [] while True: s = fp.readline().strip() diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 3bb7d49aaf5..ed883b07105 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -2614,8 +2614,7 @@ def write(self, s): if verbosity >= 1: print("Leading term degree: %2d." % int(token)) self.curr_deg = int(token) - if self.max_deg < self.curr_deg: - self.max_deg = self.curr_deg + self.max_deg = max(self.max_deg, self.curr_deg) elif re.match(SingularGBLogPrettyPrinter.red_para, token) and verbosity >= 3: m, n = re.match(SingularGBLogPrettyPrinter.red_para, token).groups() diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 2ccaf13035d..5a380a86271 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -418,8 +418,8 @@ def _sympysage_Subs(self): sage: from sympy.core.singleton import S """ args = self.args - substi = dict([(args[1][i]._sage_(), args[2][i]._sage_()) - for i in range(len(args[1]))]) + substi = {args[1][i]._sage_(): args[2][i]._sage_() + for i in range(len(args[1]))} return args[0]._sage_().subs(substi) diff --git a/src/sage/interfaces/tab_completion.py b/src/sage/interfaces/tab_completion.py index fdbe51ffd8e..29865b54d9e 100644 --- a/src/sage/interfaces/tab_completion.py +++ b/src/sage/interfaces/tab_completion.py @@ -27,7 +27,7 @@ import builtins -class ExtraTabCompletion(): +class ExtraTabCompletion: def __dir__(self): """ @@ -71,7 +71,7 @@ def completions(s, globs): sage: import sage.interfaces.tab_completion as s sage: p = x**2 + 1 sage: s.completions('p.co',globals()) # indirect doctest - ['p.coefficients',...] + ['p.coefficient',...] sage: s.completions('dic',globals()) # indirect doctest ['dickman_rho', 'dict'] diff --git a/src/sage/knots/all.py b/src/sage/knots/all.py index d25acbda1a3..5e456b752b3 100644 --- a/src/sage/knots/all.py +++ b/src/sage/knots/all.py @@ -1,5 +1,4 @@ from sage.misc.lazy_import import lazy_import -from sage.features.databases import DatabaseKnotInfo lazy_import('sage.knots.knot', ['Knot', 'Knots']) lazy_import('sage.knots.link', 'Link') diff --git a/src/sage/knots/free_knotinfo_monoid.py b/src/sage/knots/free_knotinfo_monoid.py new file mode 100644 index 00000000000..d62e6743c79 --- /dev/null +++ b/src/sage/knots/free_knotinfo_monoid.py @@ -0,0 +1,507 @@ +# sage_setup: distribution = sagemath-graphs +r""" +Free monoid generated by prime knots available via the +:class:`~sage.knots.knotinfo.KnotInfoBase` class. + +A generator of this free abelian monoid is a prime knot according to +the list at `KnotInfo `__. A fully +amphicheiral prime knot is represented by exactly one generator with +the corresponding name. For non-chiral prime knots, there are +additionally one or three generators with the suffixes ``m``, ``r`` +and ``c`` which specify the mirror and reverse images according to +their symmetry type. + +AUTHORS: + +- Sebastian Oehms June 2024: initial version +""" + +############################################################################## +# Copyright (C) 2024 Sebastian Oehms +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +############################################################################## + +from sage.knots.knotinfo import SymmetryMutant +from sage.monoids.indexed_free_monoid import (IndexedFreeAbelianMonoid, + IndexedFreeAbelianMonoidElement) +from sage.misc.cachefunc import cached_method +from sage.structure.unique_representation import UniqueRepresentation + + +class FreeKnotInfoMonoidElement(IndexedFreeAbelianMonoidElement): + """ + An element of an indexed free abelian monoid. + """ + def as_knot(self): + r""" + Return the knot represented by ``self``. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: FKIM.inject_variables(select=3) + Defining K3_1 + Defining K3_1m + sage: K = K3_1^2 * K3_1m + sage: K.as_knot() + Knot represented by 9 crossings + """ + wl = self.to_word_list() + P = self.parent() + if len(wl) == 1: + name = wl[0] + L = P._index_dict[name][0].link() + if name.endswith(SymmetryMutant.mirror_image.value): + return L.mirror_image() + if name.endswith(SymmetryMutant.reverse.value): + return L.reverse() + if name.endswith(SymmetryMutant.concordance_inverse.value): + return L.mirror_image().reverse() + return L + else: + from sage.misc.misc_c import prod + return prod(P.gen(wl[i]).as_knot() for i in range(len(wl))) + + def to_knotinfo(self): + r""" + Return a word representing ``self`` as a list of pairs. + + Each pair ``(ki, sym)`` consists of a + :class:`~sage.knots.knotinfo.KnotInfoBase` instance ``ki`` and + :class:`~sage.knots.knotinfo.SymmetryMutant` instance ``sym``. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: FKIM.inject_variables(select=3) + Defining K3_1 + Defining K3_1m + sage: K = K3_1^2 * K3_1m + sage: K.to_knotinfo() + [(, ), + (, ), + (, )] + """ + wl = self.to_word_list() + P = self.parent() + return [P._index_dict[w] for w in wl] + + +class FreeKnotInfoMonoid(IndexedFreeAbelianMonoid): + + Element = FreeKnotInfoMonoidElement + + @staticmethod + def __classcall_private__(cls, max_crossing_number=6, prefix=None, **kwds): + r""" + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FreeKnotInfoMonoid() + Free abelian monoid of knots with at most 6 crossings + sage: FreeKnotInfoMonoid(5) + Free abelian monoid of knots with at most 5 crossings + """ + if not prefix: + prefix = 'KnotInfo' + # We skip the IndexedMonoid__classcall__ + return UniqueRepresentation.__classcall__(cls, max_crossing_number, + prefix=prefix, **kwds) + + def __init__(self, max_crossing_number, category=None, prefix=None, **kwds): + r""" + Initialize ``self`` with generators belonging to prime knots with + at most ``max_crossing_number`` crossings. + + TESTS: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: FKIM4 = FreeKnotInfoMonoid(4) + sage: TestSuite(FKIM).run() + sage: TestSuite(FKIM4).run() + """ + self._max_crossing_number = None + self._set_index_dictionary(max_crossing_number=max_crossing_number) + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + indices = FiniteEnumeratedSet(self._index_dict) + super().__init__(indices, prefix) + + def _from_knotinfo(self, knotinfo, symmetry_mutant): + r""" + Return the name on the generator for the given ``symmetry_mutant`` + of the given entry ``knotinfo`` if the KnotInfo database. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: from sage.knots.knotinfo import SymmetryMutant + sage: FKIM = FreeKnotInfoMonoid() + sage: ki = KnotInfo.K5_2 + sage: FKIM._from_knotinfo(ki, SymmetryMutant.itself) + 'K5_2' + sage: FKIM._from_knotinfo(ki, SymmetryMutant.concordance_inverse) + 'K5_2c' + """ + if symmetry_mutant == SymmetryMutant.itself: + return knotinfo.name + else: + return '%s%s' % (knotinfo.name, symmetry_mutant.value) + + def _set_index_dictionary(self, max_crossing_number=6): + r""" + Set or expand the set of generators. + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FreeKnotInfoMonoid() + Free abelian monoid of knots with at most 6 crossings + + TESTS:: + + sage: from sage.features.databases import DatabaseKnotInfo + sage: F = DatabaseKnotInfo() + sage: F.hide() + sage: FreeKnotInfoMonoid(7) # indirect doctest + Traceback (most recent call last): + ... + sage.features.FeatureNotPresentError: database_knotinfo is not available. + Feature `database_knotinfo` is hidden. + Use method `unhide` to make it available again. + sage: F.unhide() + """ + if max_crossing_number > 6: + from sage.features.databases import DatabaseKnotInfo + DatabaseKnotInfo().require() + + current_max_crossing_number = self._max_crossing_number + if not current_max_crossing_number: + current_max_crossing_number = - 1 + self._index_dict = {} + self._max_crossing_number = max_crossing_number + + def add_index(ki, sym): + self._index_dict[self._from_knotinfo(ki, sym)] = (ki, sym) + + from sage.knots.knotinfo import KnotInfo + for K in KnotInfo: + ncr = K.crossing_number() + if ncr <= current_max_crossing_number: + continue + if ncr > self._max_crossing_number: + break + for sym in SymmetryMutant: + if sym.is_minimal(K): + add_index(K, sym) + if current_max_crossing_number > 0: + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + self._indices = FiniteEnumeratedSet(self._index_dict) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FreeKnotInfoMonoid(4) + Free abelian monoid of knots with at most 4 crossings + """ + return "Free abelian monoid of knots with at most %s crossings" % self._max_crossing_number + + def _element_constructor_(self, x=None): + """ + Create an element of this abelian monoid from ``x``. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: K = KnotInfo.K5_1.link().mirror_image() + sage: FKIM(K) + KnotInfo['K5_1m'] + """ + if isinstance(x, tuple): + if len(x) == 2: + ki, sym = x + from sage.knots.knotinfo import KnotInfoBase + if isinstance(ki, KnotInfoBase) and isinstance(sym, SymmetryMutant): + mcr = ki.crossing_number() + if mcr > self._max_crossing_number: + self._set_index_dictionary(max_crossing_number=mcr) + + sym_min = min([sym] + sym.matches(ki)) + return self.gen(self._from_knotinfo(ki, sym_min)) + + from sage.knots.knot import Knot + from sage.knots.link import Link + if not isinstance(x, Knot): + if isinstance(x, Link): + x = Knot(x.pd_code()) + if isinstance(x, Knot): + return self.from_knot(x) + return self.element_class(self, x) + + @cached_method + def _check_elements(self, knot, elems): + r""" + Return a matching item from the list in ``elems`` if it exists. + Elsewise return ``None``. This is a helper method for .meth:`from_knot`. + + INPUT: + + - ``knot`` -- an instance of :class:`~sage.knots.knot.Knot` + - ``elems`` -- a tuple of elements of ``self`` + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: FKIM.inject_variables(select=3) + Defining K3_1 + Defining K3_1m + sage: elems = (K3_1, K3_1m) + sage: K = Knots().from_table(3, 1) + sage: FKIM._check_elements(K, elems) + KnotInfo['K3_1m'] + sage: K = Knots().from_table(4, 1) + sage: FKIM._check_elements(K, elems) is None + True + """ + for e in elems: + k = e.as_knot() + if knot.pd_code() == k.pd_code(): + return e + if knot._markov_move_cmp(k.braid()): + return e + return None + + @cached_method + def _search_composition(self, max_cr, knot, hpoly): + r""" + Add KnotInfo items to the list of candidates that have + matching Homfly polynomial. + + INPUT: + + - ``max_cr`` -- max number of crossing to stop searching + - ``knot`` -- instance of :class:`~sage.knots.knot.Knot` + - ``hpoly`` -- Homfly polynomial to search for a component + + OUTPUT: + + A tuple of elements of ``self`` that match a (not necessarily prime or + proper) component of the given knot having the given Homfly polynomial. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: FKIM.inject_variables(select=3) + Defining K3_1 + Defining K3_1m + sage: KI = K3_1 * K3_1m + sage: K = KI.as_knot() + sage: h = K3_1.to_knotinfo()[0][0].homfly_polynomial() + sage: FKIM._search_composition(3, K, h) + (KnotInfo['K3_1'],) + """ + from sage.knots.knotinfo import KnotInfo + + def hp_mirr(hp): + v, z = hp.parent().gens() + return hp.subs({v: ~v, z: z}) + + former_cr = 3 + res = [] + for K in KnotInfo: + if not K.is_knot(): + break + c = K.crossing_number() + if c < 3: + continue + if c > max_cr: + break + hp = K.homfly_polynomial() + hp_sym = {s: hp for s in SymmetryMutant if s.is_minimal(K)} + hpm = hp_mirr(hp) + if hp != hpm: + hp_sym[SymmetryMutant.mirror_image] = hpm + if SymmetryMutant.concordance_inverse in hp_sym.keys(): + hp_sym[SymmetryMutant.concordance_inverse] = hpm + + for sym_mut in hp_sym.keys(): + hps = hp_sym[sym_mut] + if hps.divides(hpoly): + Kgen = self((K, sym_mut)) + h = hpoly // hps + if h.is_unit(): + res += [Kgen] + else: + res_rec = self._search_composition(max_cr - c, knot, h) + if res_rec: + res += [Kgen * k for k in res_rec] + if c > former_cr and res: + k = self._check_elements(knot, tuple(res)) + if k: + # matching item found + return tuple([k]) + former_cr = c + + return tuple(sorted(set(res))) + + @cached_method + def _from_knot(self, knot): + """ + Create a tuple of element of this abelian monoid which possibly + represent ``knot``. This method caches the performance relevant + part of :meth:`from_knot`. + + INPUT: + + - ``knot`` -- an instance of :class:`~sage.knots.knot.Knot` + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: K = KnotInfo.K5_1.link().mirror_image() + sage: FKIM._from_knot(K) + (KnotInfo['K5_1m'],) + """ + hp = knot.homfly_polynomial(normalization='vz') + return self._search_composition(13, knot, hp) + + def from_knot(self, knot, unique=True): + """ + Create an element of this abelian monoid from ``knot``. + + INPUT: + + - ``knot`` -- an instance of :class:`~sage.knots.knot.Knot` + + - ``unique`` -- boolean (default is ``True``). This only affects the case + where a unique identification is not possible. If set to ``False`` you + can obtain a matching list (see explanation of the output below) + + OUTPUT: + + An instance of the element class of ``self`` per default. If the keyword + argument ``unique`` then a list of such instances is returned. + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid() + sage: K = KnotInfo.K5_1.link().mirror_image() + sage: FKIM.from_knot(K) + KnotInfo['K5_1m'] + + sage: # optional - database_knotinfo + sage: K = Knot(KnotInfo.K9_12.braid()) + sage: FKIM.from_knot(K) # long time + Traceback (most recent call last): + ... + NotImplementedError: this (possibly non prime) knot cannot be + identified uniquely by KnotInfo + use keyword argument `unique` to obtain more details + sage: FKIM.from_knot(K, unique=False) # long time + [KnotInfo['K4_1']*KnotInfo['K5_2'], KnotInfo['K9_12']] + """ + hp = knot.homfly_polynomial(normalization='vz') + num_summands = sum(e for _, e in hp.factor()) + if num_summands == 1: + return knot.get_knotinfo() + + res = self._from_knot(knot) + if res: + if len(res) == 1: + if unique: + return res[0] + return [res[0]] # to be consistent with get_knotinfo + k = self._check_elements(knot, res) + if k: + if unique: + return k + return [k] # to be consistent with get_knotinfo + + if res and not unique: + return sorted(set(res)) + if unique and len(res) > 1: + non_unique_hint = '\nuse keyword argument `unique` to obtain more details' + raise NotImplementedError('this (possibly non prime) knot cannot be identified uniquely by KnotInfo%s' % non_unique_hint) + raise NotImplementedError('this (possibly non prime) knot cannot be identified by KnotInfo') + + def inject_variables(self, select=None, verbose=True): + """ + Inject ``self`` with its name into the namespace of the + Python code from which this function is called. + + INPUT: + + - ``select`` -- instance of :class:`~sage.knots.knotinfo.KnotInfoBase`, + :class:`~sage.knots.knotinfo.KnotInfoSeries` or an integer. In all + cases the input is used to restrict the injected generators to the + according subset (number of crossings in the case of integer) + - ``verbose`` -- boolean (optional, default ``True``) to suppress + the message printed on the invocation + + EXAMPLES:: + + sage: from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + sage: FKIM = FreeKnotInfoMonoid(5) + sage: FKIM.inject_variables(select=3) + Defining K3_1 + Defining K3_1m + sage: FKIM.inject_variables(select=KnotInfo.K5_2) + Defining K5_2 + Defining K5_2m + sage: FKIM.inject_variables(select=KnotInfo.K5_2.series()) + Defining K5_1 + Defining K5_1m + sage: FKIM.inject_variables() + Defining K0_1 + Defining K4_1 + """ + from sage.knots.knotinfo import KnotInfoBase, KnotInfoSeries + from sage.rings.integer import Integer + gen_list = [] + idx_dict = self._index_dict + max_crn = self._max_crossing_number + gens = self.gens() + if select: + if isinstance(select, KnotInfoBase): + crn = select.crossing_number() + if crn > max_crn: + self._set_index_dictionary(max_crossing_number=crn) + gen_list += [k for k, v in idx_dict.items() if v[0] == select] + elif isinstance(select, KnotInfoSeries): + for v in select: + self.inject_variables(select=v) + return + elif type(select) is int or isinstance(select, Integer): + crn = select + if crn > max_crn: + self._set_index_dictionary(max_crossing_number=crn) + gen_list += [k for k, v in idx_dict.items() + if v[0].crossing_number() == crn] + else: + raise TypeError('cannot select generators by %s' % select) + else: + gen_list = list(idx_dict.keys()) + + from sage.repl.user_globals import set_global, get_globals + for name in gen_list: + if name not in get_globals().keys(): + set_global(name, gens[name]) + if verbose: + print("Defining %s" % (name)) diff --git a/src/sage/knots/knot.py b/src/sage/knots/knot.py index 21dd0b59f12..aaa01cdade9 100644 --- a/src/sage/knots/knot.py +++ b/src/sage/knots/knot.py @@ -200,14 +200,14 @@ def _unicode_art_(self): x, y, xx, yy = xx, yy, x, y if y < b: if xx < a: - M[a][b] = u"╯" + M[a][b] = "╯" else: - M[a][b] = u"╮" + M[a][b] = "╮" else: if xx < a: - M[a][b] = u"╰" + M[a][b] = "╰" else: - M[a][b] = u"╭" + M[a][b] = "╭" for ab, cd in graphe.edge_iterator(labels=False): a, b = ab @@ -215,21 +215,21 @@ def _unicode_art_(self): if a == c: b, d = sorted((b, d)) for i in range(b + 1, d): - M[a][i] = u"─" + M[a][i] = "─" else: a, c = sorted((a, c)) for i in range(a + 1, c): - M[i][b] = u"│" + M[i][b] = "│" if style == 0: - H = u"┿" - V = u"╂" + H = "┿" + V = "╂" elif style == 1: - H = u"━" - V = u"┃" + H = "━" + V = "┃" elif style == 2: - H = u"─" - V = u"│" + H = "─" + V = "│" for x, y in hori: M[x][y] = H diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index bcd43194ee7..8c246ba8097 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -204,7 +204,7 @@ ....: [17,19,8,18], [9,10,11,14], [10,12,13,11], ....: [12,19,15,13], [20,16,14,15], [16,20,17,2]]) sage: L.get_knotinfo() - (, ) + KnotInfo['K0_1'] REFERENCES: @@ -281,6 +281,7 @@ def eval_knotinfo(string, locals={}, to_tuple=True): new_string = new_string.replace(';', ',') return sage_eval(new_string, locals=locals) + def knotinfo_int(string): r""" Preparse a string from the KnotInfo database representing an integer. @@ -306,6 +307,7 @@ def knotinfo_int(string): else: return int(string) + def knotinfo_bool(string): r""" Preparse a string from the KnotInfo database representing a boolean. @@ -350,9 +352,9 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` """ itself = 's' reverse = 'r' - concordance_inverse = 'mr' + concordance_inverse = 'c' mirror_image = 'm' - mixed = 'x' # to be used in connection with KnotInfoSeries + mixed = 'x' # to be used in connection with KnotInfoSeries unknown = '?' def __gt__(self, other): @@ -366,14 +368,103 @@ def __gt__(self, other): [, , , - , , + , ] """ # We use the reversal of the alphabetical order of the values so that # `itself` occurs before the mirrored cases return self.value < other.value + def rev(self): + r""" + Return the reverse of ``self``. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import SymmetryMutant + sage: all( sym.rev().rev() == sym for sym in SymmetryMutant) + True + """ + if self is SymmetryMutant.itself: + return SymmetryMutant.reverse + elif self is SymmetryMutant.reverse: + return SymmetryMutant.itself + elif self is SymmetryMutant.mirror_image: + return SymmetryMutant.concordance_inverse + elif self is SymmetryMutant.concordance_inverse: + return SymmetryMutant.mirror_image + return self + + def mir(self): + r""" + Return the mirror image of ``self``. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import SymmetryMutant + sage: all( sym.mir().mir() == sym for sym in SymmetryMutant) + True + """ + if self is SymmetryMutant.itself: + return SymmetryMutant.mirror_image + elif self is SymmetryMutant.reverse: + return SymmetryMutant.concordance_inverse + elif self is SymmetryMutant.mirror_image: + return SymmetryMutant.itself + elif self is SymmetryMutant.concordance_inverse: + return SymmetryMutant.reverse + return self + + def matches(self, link): + r""" + Return the list of other symmetry mutants that give isotopic links + with respect to ``link`` and ``self``. For ``self`` is + ``SymmetryMutant.unknown`` a boolean is returned which is ``True`` + if the chirality of ``link`` is unknown. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import SymmetryMutant + sage: SymmetryMutant.itself.matches(KnotInfo.K6_1) + [] + sage: SymmetryMutant.mirror_image.matches(KnotInfo.K6_1) + [] + """ + rev = link.is_reversible() + achp = link.is_amphicheiral(positive=True) + ach = link.is_amphicheiral() + if self is SymmetryMutant.unknown: + if rev is None or ach is None or achp is None: + return True + else: + return False + res = [] + if rev: + res.append(self.rev()) + if achp: + res.append(self.mir()) + if ach: + res.append(self.rev().mir()) + return res + + def is_minimal(self, link): + r""" + Return whether ``self`` is minimal among its matching mutants. + + EXAMPLES:: + + sage: from sage.knots.knotinfo import SymmetryMutant + sage: SymmetryMutant.itself.is_minimal(KnotInfo.K6_1) + True + sage: SymmetryMutant.concordance_inverse.is_minimal(KnotInfo.K6_1) + False + """ + if self in [SymmetryMutant.unknown, SymmetryMutant.mixed]: + return False + matches = self.matches(link) + return all(self < other for other in matches) + # --------------------------------------------------------------------------------- # KnotInfoBase @@ -415,7 +506,7 @@ def __gt__(self, other): True """ if self.__class__ is other.__class__: - tups = (not self.is_knot(), self.crossing_number(), self.value) + tups = (not self.is_knot(), self.crossing_number(), self.value) tupo = (not other.is_knot(), other.crossing_number(), other.value) return tups > tupo return NotImplemented @@ -531,7 +622,7 @@ def _homfly_pol_ring(self, var1, var2): sage: L._homfly_pol_ring('u', 'v') Multivariate Laurent Polynomial Ring in u, v over Integer Ring """ - K3_1 = Knots().from_table(3,1) + K3_1 = Knots().from_table(3, 1) return K3_1.homfly_polynomial(var1=var1, var2=var2).parent() @cached_method @@ -699,7 +790,7 @@ def braid_notation(self, original=False): return (1, ) braid_notation = eval_knotinfo(braid_notation) - if type(braid_notation) is list: + if type(braid_notation) in (list, tuple): # in some cases there are a pair of braid representations # in the database. If this is the case we select the # corresponding to the braid index. @@ -896,7 +987,7 @@ def signature(self): EXAMPLES:: sage: KnotInfo.K5_2.signature() # optional - database_knotinfo - 1 + -2 """ return knotinfo_int(self[self.items.signature]) @@ -966,7 +1057,7 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` if not self.is_knot(): raise NotImplementedError('this is only available for knots') - symmetry_type = self[self.items.symmetry_type].strip() # for example K10_88 is a case with trailing whitespaces + symmetry_type = self[self.items.symmetry_type].strip() # for example K10_88 is a case with trailing whitespaces if not symmetry_type and self.crossing_number() == 0: return 'fully amphicheiral' return symmetry_type @@ -1092,6 +1183,23 @@ def is_amphicheiral(self, positive=False): return None + @cached_method + def is_hyperbolic(self): + r""" + Return whether ``self`` is hyperbolic. + + EXAMPLES:: + + sage: KnotInfo.K3_1.is_hyperbolic() + False + sage: KnotInfo.K5_2.is_hyperbolic() + True + """ + geometric_type = self[self.items.geometric_type] + if geometric_type == 'hyperbolic': + return True + return False + @cached_method def is_alternating(self): r""" @@ -1218,6 +1326,37 @@ def is_oriented(self): """ return not knotinfo_bool(self[self.items.unoriented]) + @cached_method + def cosmetic_crossing_conjecture_verified(self): + r""" + Return whether the Cosmetic Crossing Conjecture has been verified + for ``self``. + + From the KnotInfo `description page `__: + + A crossing change in a diagram of a knot ``K`` is called cosmetic if + the resulting diagram also represents ``K``. The cosmetic crossing + conjecture posits that for any knot ``K``, the only cosmetic crossing + changes are nugatory, i.e. there exists an embedded 2-sphere in + ``S3`` which intersects K only at the two points of the relevant + crossing. Conversely, it is not hard to see that any nugatory + crossing change is cosmetic. + + EXAMPLES:: + + sage: knots = [K for K in KnotInfo if K.is_knot() and K.crossing_number() < 10] + sage: all(K.cosmetic_crossing_conjecture_verified() for K in knots) + True + """ + cosmetic_crossing = self[self.items.cosmetic_crossing] + if self.crossing_number() == 0: + return True + if not cosmetic_crossing or cosmetic_crossing == 'Unknown': + return False + if not knotinfo_bool(cosmetic_crossing): + return True + raise AssertionError(f'{self} is a counterexample to the cosmetic crossing conjecture') + @cached_method def homfly_polynomial(self, var1='v', var2='z', original=False): r""" @@ -1316,7 +1455,7 @@ def homfly_polynomial(self, var1='v', var2='z', original=False): homfly_polynomial = homfly_polynomial.strip('}') L, M = R.gens() - lc = {'v': L, 'z':M} + lc = {'v': L, 'z': M} return eval_knotinfo(homfly_polynomial, locals=lc) @cached_method @@ -1400,7 +1539,7 @@ def kauffman_polynomial(self, var1='a', var2='z', original=False): return R.one() a, z = R.gens() - lc = {'a': a, 'z': z} + lc = {'a': a, 'z': z} return R(eval_knotinfo(kauffman_polynomial, locals=lc)) @cached_method @@ -1573,24 +1712,24 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False R = SR if not jones_polynomial and self.crossing_number() == 0: - return R(1) + return R.one() t = R(variab) if skein_normalization: if self.is_knot(): - lc = {'t': t**4} + lc = {'t': t**4} else: - lc = {'x': t**2} + lc = {'x': t**2} else: if self.is_knot(): - lc = {'t': t} + lc = {'t': t} elif puiseux: - lc = {'x': t**(1/2)} + lc = {'x': t**(1/2)} elif use_sqrt: from sage.misc.functional import sqrt - lc = {'x': sqrt(t)} + lc = {'x': sqrt(t)} else: - lc = {'x': t} + lc = {'x': t} return R(eval_knotinfo(jones_polynomial, locals=lc)) @@ -1676,7 +1815,7 @@ def alexander_polynomial(self, var='t', original=False, laurent_poly=False): return R.one() t, = R.gens() - lc = {'t': t} + lc = {'t': t} ap = R(eval_knotinfo(alexander_polynomial, locals=lc)) if not laurent_poly or ap.is_constant(): return ap @@ -1747,7 +1886,7 @@ def conway_polynomial(self, var='t', original=False): return R.one() t, = R.gens() - lc = {'z': t} + lc = {'z': t} return R(eval_knotinfo(conway_polynomial, locals=lc)) @cached_method @@ -2163,8 +2302,13 @@ def is_recoverable(self, unique=True): False sage: L5a1_0.is_recoverable(unique=False) True + + TESTS: + + sage: KnotInfo.K12a_165.is_recoverable(unique=False) # optional - database_knotinfo, long time + True """ - def recover(mirror, braid): + def recover(sym_mut, braid): r""" Check if ``self`` can be recovered form its associated Sage link. @@ -2173,37 +2317,42 @@ def recover(mirror, braid): l = self.link(self.items.braid_notation) else: l = self.link() - if mirror: - if self.is_amphicheiral(): - # no need to test again - return True + if sym_mut is SymmetryMutant.mirror_image: l = l.mirror_image() + elif sym_mut is SymmetryMutant.reverse: + l = l.reverse() + elif sym_mut is SymmetryMutant.concordance_inverse: + l = l.mirror_image().reservse() - def check_result(L, m): + def check_result(res): r""" Check a single result from ``get_knotinfo``. """ + if type(res) is tuple: + L, s = res + else: + L, s = res.to_knotinfo()[0] + if not isinstance(L, KnotInfoBase): + return False if L != self: return False - if mirror: - return m is SymmetryMutant.mirror_image - else: - return m is SymmetryMutant.itself + return s == sym_mut try: - L, m = l.get_knotinfo() - if isinstance(L, KnotInfoBase): - return check_result(L,m) - elif unique: - return False + res = l.get_knotinfo(unique=unique) except NotImplementedError: - if unique: - return False - Llist = l.get_knotinfo(unique=False) - return any(check_result(L, m) for (L, m) in Llist) + return False + if unique: + return check_result(res) + else: + return any(check_result(r) for r in res) from sage.misc.misc import some_tuples - return all(recover(mirror, braid) for mirror, braid in some_tuples([True, False], 2, 4)) + if SymmetryMutant.unknown.matches(self): + sym_muts = [SymmetryMutant.unknown] + else: + sym_muts = [s for s in SymmetryMutant if s.is_minimal(self)] + return all(recover(sym, braid) for sym, braid in some_tuples(sym_muts, 2, 8)) def inject(self, verbose=True): """ @@ -2522,7 +2671,7 @@ def lower_list(self, oriented=False, comp=None, det=None, homfly=None): l = [] cr = self._crossing_number if cr > 0: - LS = type(self)(cr - 1, self._is_knot, self._is_alternating, self._name_unoriented ) + LS = type(self)(cr - 1, self._is_knot, self._is_alternating, self._name_unoriented) l = LS.lower_list(oriented=oriented, comp=comp, det=det, homfly=homfly) return l + self.list(oriented=oriented, comp=comp, det=det, homfly=homfly) @@ -2557,7 +2706,7 @@ def __getitem__(self, item): [, , ] """ from sage.rings.integer import Integer - if not type(item) in (int, Integer): + if type(item) not in (int, Integer): raise ValueError('item must be an integer') l = self.list() max_item = len(l) @@ -2600,7 +2749,7 @@ def __call__(self, item): return self[item] from sage.rings.integer import Integer - if not type(item) in (int, Integer): + if type(item) not in (int, Integer): raise ValueError('item must be an integer') l = self.list() max_item = len(l) + 1 diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index ed6a638eedc..e6d23ac01b6 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -1201,7 +1201,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): else: m = matrix(ring, len(bases[(i,j)]), 0) complexes[i] = m.transpose() - if not (i-1, j) in bases: + if (i-1, j) not in bases: complexes[i-1] = matrix(ring, len(bases[(i,j)]), 0) homologies = ChainComplex(complexes).homology() return tuple(sorted(homologies.items())) @@ -2313,7 +2313,7 @@ def seifert_circles(self): while a not in par: par.append(a) posnext = C[(C.index(a) - 1) % 4] - if tails[posnext] == C and not [posnext] in result: + if tails[posnext] == C and [posnext] not in result: a = posnext else: a = C[(C.index(a) + 1) % 4] @@ -3019,8 +3019,8 @@ def homfly_polynomial(self, var1=None, var2=None, normalization='lm'): Comparison with KnotInfo:: sage: # needs sage.libs.homfly - sage: KI, m = K.get_knotinfo(); KI, m - (, ) + sage: KI = K.get_knotinfo(mirror_version=False); KI + sage: K.homfly_polynomial(normalization='vz') == KI.homfly_polynomial() True @@ -3908,7 +3908,7 @@ def _markov_move_cmp(self, braid): to the given braid in the following sense. If both braids have the same number of strands it is checked if they are conjugated to each other in their common braid group (Markov move I). If the number of strands differs, - the braid having less strands is extended by Markov moves II (appendening + the braid having less strands is extended by Markov moves II (appending the largest generator or its inverse recursively) until a common braid group can be achieved, where conjugation is tested. @@ -4003,8 +4003,7 @@ def _knotinfo_matching_list(self): # set the limits for the KnotInfoSeries if cr > 11 and co > 1: cr = 11 - if cr > 13: - cr = 13 + cr = min(cr, 13) Hp = self.homfly_polynomial(normalization='vz') @@ -4057,7 +4056,7 @@ def _knotinfo_matching_list(self): def _knotinfo_matching_dict(self): r""" - Return a dictionary mapping items of the enum :class:`~sage.knots.knotinfo.SymmetryType` + Return a dictionary mapping items of the enum :class:`~sage.knots.knotinfo.SymmetryMutant` to list of links from the KnotInfo and LinkInfo databases which match the properties of the according symmetry mutant of ``self`` as much as possible. @@ -4065,7 +4064,7 @@ def _knotinfo_matching_dict(self): OUTPUT: A pair (``match_lists, proves``) of dictionaries with keys from the - enum :class:`~sage.knots.knotinfo.SymmetryType`. The first dictionary maps these keys to + enum :class:`~sage.knots.knotinfo.SymmetryMutant`. The first dictionary maps these keys to the corresponding matching list and ``proves`` maps them to booleans telling if the entries of the corresponding ``match_lists`` are checked to be isotopic to the symmetry mutant of ``self`` or not. @@ -4077,18 +4076,18 @@ def _knotinfo_matching_dict(self): sage: L4a1_0.link()._knotinfo_matching_dict() ({: [], : [], - : [], - : []}, + : [], + : []}, {: True, : True, - : False, - : False}) + : False, + : False}) """ from sage.knots.knotinfo import SymmetryMutant mutant = {} mutant[SymmetryMutant.itself] = self - mutant[SymmetryMutant.mirror_image] = self.mirror_image() mutant[SymmetryMutant.reverse] = self.reverse() + mutant[SymmetryMutant.mirror_image] = self.mirror_image() mutant[SymmetryMutant.concordance_inverse] = mutant[SymmetryMutant.mirror_image].reverse() match_lists = {k: list(mutant[k]._knotinfo_matching_list()[0]) for k in mutant.keys()} proves = {k: mutant[k]._knotinfo_matching_list()[1] for k in mutant.keys()} @@ -4111,11 +4110,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): OUTPUT: - A tuple ``(K, m)`` where ``K`` is an instance of :class:`~sage.knots.knotinfo.KnotInfoBase` - and ``m`` an instance of :class:`~sage.knots.knotinfo.SymmetryMutant` - (for chiral links) specifying the symmetry mutant of ``K`` to which - ``self`` is isotopic. The value of ``m`` is ``unknown`` if it cannot - be determined uniquely and the keyword option ``unique=False`` is given. + If ``self`` is a knot, then an element of the free monoid over prime + knots constructed from the KnotInfo database is returned. More explicitly + this is an element of :class:`~sage.knots.free_knotinfo_monoid.FreeKnotInfoMonoidElement`. + Else a tuple ``(K, m)`` is returned where ``K`` is an instance of + :class:`~sage.knots.knotinfo.KnotInfoBase` and ``m`` an instance of + :class:`~sage.knots.knotinfo.SymmetryMutant` (for chiral links) specifying + the symmetry mutant of ``K`` to which ``self`` is isotopic. The value of + ``m`` is ``unknown`` if it cannot be determined uniquely and the keyword + option ``unique=False`` is given. For proper links, if the orientation mutant cannot be uniquely determined, K will be a series of links gathering all links having the same unoriented @@ -4149,35 +4152,35 @@ def get_knotinfo(self, mirror_version=True, unique=True): EXAMPLES:: sage: # optional - database_knotinfo - sage: from sage.knots.knotinfo import KnotInfo sage: L = Link([[4,1,5,2], [10,4,11,3], [5,17,6,16], [7,13,8,12], ....: [18,10,19,9], [2,12,3,11], [13,21,14,20], [15,7,16,6], ....: [22,17,1,18], [8,20,9,19], [21,15,22,14]]) sage: L.get_knotinfo() - (, ) + KnotInfo['K11n_121m'] sage: K = KnotInfo.K10_25 sage: l = K.link() sage: l.get_knotinfo() - (, ) + KnotInfo['K10_25'] sage: k11 = KnotInfo.K11n_82.link() sage: k11m = k11.mirror_image() sage: k11mr = k11m.reverse() sage: k11mr.get_knotinfo() - (, ) + KnotInfo['K11n_82m'] sage: k11r = k11.reverse() sage: k11r.get_knotinfo() - (, ) + KnotInfo['K11n_82'] sage: k11rm = k11r.mirror_image() sage: k11rm.get_knotinfo() - (, ) + KnotInfo['K11n_82m'] - Knots with more than 13 and proper links having more than 11 crossings - cannot be identified. In addition non prime links or even links whose - HOMFLY-PT polynomial is not irreducible cannot be identified:: + Knots with more than 13 and multi-component links having more than 11 + crossings cannot be identified. In addition non prime multi-component + links or even links whose HOMFLY-PT polynomial is not irreducible cannot + be identified:: sage: b, = BraidGroup(2).gens() sage: Link(b**13).get_knotinfo() # optional - database_knotinfo - (, ) + KnotInfo['K13a_4878'] sage: Link(b**14).get_knotinfo() Traceback (most recent call last): ... @@ -4197,7 +4200,7 @@ def get_knotinfo(self, mirror_version=True, unique=True): ....: [17,19,8,18], [9,10,11,14], [10,12,13,11], ....: [12,19,15,13], [20,16,14,15], [16,20,17,2]]) sage: L.get_knotinfo() - (, ) + KnotInfo['K0_1'] Usage of option ``mirror_version``:: @@ -4214,10 +4217,7 @@ def get_knotinfo(self, mirror_version=True, unique=True): NotImplementedError: this link cannot be uniquely determined use keyword argument `unique` to obtain more details sage: l.get_knotinfo(unique=False) - [(, ), - (, ), - (, ), - (, )] + [KnotInfo['K10_25'], KnotInfo['K10_56']] sage: t = (1, -2, 1, 1, -2, 1, -2, -2) sage: l8 = Link(BraidGroup(3)(t)) sage: l8.get_knotinfo() @@ -4238,11 +4238,7 @@ def get_knotinfo(self, mirror_version=True, unique=True): use keyword argument `unique` to obtain more details sage: l12.get_knotinfo(unique=False) [(, ), - (, ), - (, ), - (, - ), - (, ), + (, ), (, ), (, )] @@ -4254,10 +4250,8 @@ def get_knotinfo(self, mirror_version=True, unique=True): sage: L2a1.get_knotinfo() (Series of links L2a1, ) sage: L2a1.get_knotinfo(unique=False) - [(, ), - (, ), - (, ), - (, )] + [(, ), + (, )] sage: KnotInfo.L5a1_0.inject() Defining L5a1_0 @@ -4270,21 +4264,19 @@ def get_knotinfo(self, mirror_version=True, unique=True): [, ] sage: l5.get_knotinfo(unique=False) [(, ), - (, ), - (, ), - (, )] + (, )] Clarifying the series around the Perko pair (:wikipedia:`Perko_pair`):: sage: for i in range(160, 166): # optional - database_knotinfo ....: K = Knots().from_table(10, i) ....: print('%s_%s' %(10, i), '--->', K.get_knotinfo()) - 10_160 ---> (, ) - 10_161 ---> (, ) - 10_162 ---> (, ) - 10_163 ---> (, ) - 10_164 ---> (, ) - 10_165 ---> (, ) + 10_160 ---> KnotInfo['K10_160'] + 10_161 ---> KnotInfo['K10_161m'] + 10_162 ---> KnotInfo['K10_162'] + 10_163 ---> KnotInfo['K10_163'] + 10_164 ---> KnotInfo['K10_164'] + 10_165 ---> KnotInfo['K10_165m'] Clarifying ther Perko series against `SnapPy `__:: @@ -4302,16 +4294,16 @@ def get_knotinfo(self, mirror_version=True, unique=True): ....: K = K10(i) ....: k = K.link(K.items.name, snappy=True) ....: print(k, '--->', k.sage_link().get_knotinfo()) - ---> (, ) - ---> (, ) - ---> (, ) - ---> (, ) - ---> (, ) - ---> (, ) + ---> KnotInfo['K10_160'] + ---> KnotInfo['K10_161m'] + ---> KnotInfo['K10_161'] + ---> KnotInfo['K10_162'] + ---> KnotInfo['K10_163'] + ---> KnotInfo['K10_164'] sage: snappy.Link('10_166') sage: _.sage_link().get_knotinfo() - (, ) + KnotInfo['K10_165m'] Another pair of confusion (see the corresponding `Warning `__):: @@ -4320,11 +4312,23 @@ def get_knotinfo(self, mirror_version=True, unique=True): sage: Ks10_86 = snappy.Link('10_86') sage: Ks10_83 = snappy.Link('10_83') sage: Ks10_86.sage_link().get_knotinfo(unique=False) - [(, ), - (, )] + [KnotInfo['K10_83c'], KnotInfo['K10_83m']] sage: Ks10_83.sage_link().get_knotinfo(unique=False) - [(, ), - (, )] + [KnotInfo['K10_86'], KnotInfo['K10_86r']] + + Non prime knots can be detected, as well:: + + sage: b = BraidGroup(4)((1, 2, 2, 2, -1, 2, 2, 2, -3, -3, -3)) + sage: Kb = Knot(b) + sage: Kb.get_knotinfo() + KnotInfo['K3_1']^2*KnotInfo['K3_1m'] + + sage: K = Link([[4, 2, 5, 1], [8, 6, 9, 5], [6, 3, 7, 4], [2, 7, 3, 8], + ....: [10, 15, 11, 16], [12, 21, 13, 22], [14, 11, 15, 12], [16, 9, 17, 10], + ....: [18, 25, 19, 26], [20, 23, 21, 24], [22, 13, 23, 14], [24, 19, 25, 20], + ....: [26, 17, 1, 18]]) + sage: K.get_knotinfo() # optional - database_knotinfo, long time + KnotInfo['K4_1']*KnotInfo['K9_2m'] TESTS:: @@ -4332,18 +4336,10 @@ def get_knotinfo(self, mirror_version=True, unique=True): sage: L = KnotInfo.L10a171_1_1_0 sage: l = L.link(L.items.braid_notation) sage: l.get_knotinfo(unique=False) - [(, - ), - (, - ), - (, - ), - (, - ), - (, ), - (, ), - (, ), - (, )] + [(, ), + (, ), + (, ), + (, )] sage: KnotInfo.L10a151_0_0.link().get_knotinfo() Traceback (most recent call last): ... @@ -4361,9 +4357,6 @@ def get_knotinfo(self, mirror_version=True, unique=True): sage: L1.get_knotinfo() == L2.get_knotinfo() True """ - # ToDo: extension to non prime links in which case an element of the monoid - # over :class:`KnotInfo` should be returned - non_unique_hint = '\nuse keyword argument `unique` to obtain more details' from sage.knots.knotinfo import SymmetryMutant @@ -4375,37 +4368,36 @@ def answer(L): if not mirror_version: return L - chiral = True - ach = L.is_amphicheiral() - achp = L.is_amphicheiral(positive=True) - rev = L.is_reversible() - if ach is None and achp is None and rev is None: - if unique: - raise NotImplementedError('this link cannot be uniquely determined (unknown chirality)%s' % non_unique_hint) - chiral = None - elif ach and achp: - chiral = False + def find_mutant(proved=True): + r""" + Return the according symmetry mutant from the matching list + and removes the entry from the list. + """ + for k in match_lists: + if proved: + prove = proves[k] or any(proves[m] for m in k.matches(L)) + if not prove: + continue + if k.is_minimal(L): + lk = match_lists[k] + if L in lk: + lk.remove(L) + return k sym_mut = None - if chiral is None: + if SymmetryMutant.unknown.matches(L): + if unique: + raise NotImplementedError('this link cannot be uniquely determined (unknown chirality)%s' % non_unique_hint) sym_mut = SymmetryMutant.unknown - elif not chiral: - sym_mut = SymmetryMutant.itself - else: - for k in match_lists: - lk = match_lists[k] - if proves[k] and L in lk: - lk.remove(L) - sym_mut = k - break if not sym_mut: - for k in match_lists: - lk = match_lists[k] - if L in lk: - lk.remove(L) - sym_mut = k - break + sym_mut = find_mutant() + + if not sym_mut: + sym_mut = find_mutant(proved=False) + + if not unique and not sym_mut: + return None if not sym_mut: # In case of a chiral link this means that the HOMFLY-PT @@ -4416,6 +4408,11 @@ def answer(L): if unique and sym_mut is SymmetryMutant.unknown: raise NotImplementedError('symmetry mutant of this link cannot be uniquely determined%s' % non_unique_hint) + if L.is_knot(): + from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + FKIM = FreeKnotInfoMonoid() + return FKIM((L, sym_mut)) + return L, sym_mut def answer_unori(S): @@ -4445,7 +4442,12 @@ def answer_list(l): argument ``unique``. """ if not unique: - return sorted(set([answer(L) for L in l])) + ansl = [] + for L in l: + a = answer(L) + if a: + ansl.append(a) + return sorted(set(ansl)) if len(set(l)) == 1: return answer(l[0]) @@ -4457,6 +4459,16 @@ def answer_list(l): raise NotImplementedError('this link cannot be uniquely determined%s' % non_unique_hint) + H = self.homfly_polynomial(normalization='vz') + num_fac = sum(exp for f, exp in H.factor()) + if num_fac > 1 and self.is_knot(): + # we cannot be sure if this is a prime knot (see the example for the connected + # sum of K4_1 and K5_2 in the doctest of :meth:`_knotinfo_matching_list`) + # Therefor we calculate it directly in the free KnotInfo monoid + from sage.knots.free_knotinfo_monoid import FreeKnotInfoMonoid + FKIM = FreeKnotInfoMonoid() + return FKIM.from_knot(self, unique=unique) + match_lists, proves = self._knotinfo_matching_dict() # first add only proved matching lists @@ -4494,11 +4506,7 @@ def answer_list(l): # we cannot not be sure if this link is recorded in the KnotInfo database raise NotImplementedError('this link having more than 11 crossings cannot be%s determined%s' % uniq_txt) - H = self.homfly_polynomial(normalization='vz') - - if sum(exp for f, exp in H.factor()) > 1: - # we cannot be sure if this is a prime link (see the example for the connected - # sum of K4_1 and K5_2 in the doctest of :meth:`_knotinfo_matching_list`) + if num_fac > 1: raise NotImplementedError('this (possibly non prime) link cannot be%s determined%s' % uniq_txt) if not l: diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index f461690728d..cc06d96c503 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -34,7 +34,6 @@ from sage.structure.sage_object import SageObject from sage.rings.complex_mpfr import ComplexField from sage.rings.integer import Integer -from sage.misc.sage_eval import sage_eval from sage.misc.verbose import verbose import sage.interfaces.gp from sage.env import SAGE_EXTCODE @@ -478,10 +477,6 @@ def repl(m): self.__init = (v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs) - def __to_CC(self, s): - s = s.replace('.E', '.0E').replace(' ', '') - return self.__CC(sage_eval(s, locals={'I': self.__CC.gen(0)})) - def _clear_value_cache(self): del self.__values @@ -515,6 +510,7 @@ def __call__(self, s, c=None): except KeyError: pass z = self._gp_call_inst('L', s) + CC = self.__CC if 'pole' in z: print(z) raise ArithmeticError @@ -525,10 +521,10 @@ def __call__(self, s, c=None): i = z.rfind('\n') msg = z[:i].replace('digits', 'decimal digits') verbose(msg, level=-1) - ans = self.__to_CC(z[i + 1:]) + ans = CC(z[i + 1:]) self.__values[s] = ans return ans - ans = self.__to_CC(z) + ans = CC(z) self.__values[s] = ans return ans diff --git a/src/sage/lfunctions/meson.build b/src/sage/lfunctions/meson.build new file mode 100644 index 00000000000..cf0ffe05e17 --- /dev/null +++ b/src/sage/lfunctions/meson.build @@ -0,0 +1,22 @@ +py.install_sources( + 'all.py', + 'dokchitser.py', + 'lcalc.py', + 'pari.py', + 'sympow.py', + subdir: 'sage/lfunctions', +) + +extension_data = {'zero_sums' : files('zero_sums.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/lfunctions', + install: true, + include_directories: [inc_flint], + dependencies: [py_dep, flint, gmp], + ) +endforeach + diff --git a/src/sage/lfunctions/pari.py b/src/sage/lfunctions/pari.py index 78af0a49b9e..db07629dcfd 100644 --- a/src/sage/lfunctions/pari.py +++ b/src/sage/lfunctions/pari.py @@ -26,7 +26,7 @@ from sage.rings.power_series_ring import PowerSeriesRing -class lfun_generic(): +class lfun_generic: r""" Create a PARI `L`-function (:pari:`lfun` instance). diff --git a/src/sage/libs/arb/meson.build b/src/sage/libs/arb/meson.build new file mode 100644 index 00000000000..aa98fb8ff3a --- /dev/null +++ b/src/sage/libs/arb/meson.build @@ -0,0 +1,33 @@ +py.install_sources( + '__init__.py', + 'acb.pxd', + 'acb_calc.pxd', + 'acb_elliptic.pxd', + 'acb_hypgeom.pxd', + 'acb_mat.pxd', + 'acb_modular.pxd', + 'acb_poly.pxd', + 'arb.pxd', + 'arb_fmpz_poly.pxd', + 'arb_hypgeom.pxd', + 'arb_wrap.h', + 'arf.pxd', + 'bernoulli.pxd', + 'mag.pxd', + 'types.pxd', + subdir: 'sage/libs/arb', +) + +extension_data = {'arith' : files('arith.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/arb', + install: true, + include_directories: [inc_cpython, inc_flint, inc_rings], + dependencies: [py_dep, flint, gmp, mpfr], + ) +endforeach + diff --git a/src/sage/libs/coxeter3/coxeter.pyx b/src/sage/libs/coxeter3/coxeter.pyx index 6f8a651e7eb..cd3e05307c3 100644 --- a/src/sage/libs/coxeter3/coxeter.pyx +++ b/src/sage/libs/coxeter3/coxeter.pyx @@ -423,7 +423,8 @@ cdef class CoxGroup(SageObject): sage: W = CoxGroup(['A', 5]); W Coxeter group of type A and rank 5 """ - return "Coxeter group of type %s and rank %s"%(self.type(), self.rank()) + return "Coxeter group of type %s and rank %s" % (self.type(), + self.rank()) def __iter__(self): """ diff --git a/src/sage/libs/coxeter3/decl.pxd b/src/sage/libs/coxeter3/decl.pxd index 4f9c7b0c186..1f968c418bf 100644 --- a/src/sage/libs/coxeter3/decl.pxd +++ b/src/sage/libs/coxeter3/decl.pxd @@ -33,7 +33,6 @@ cdef extern from "coxeter/coxtypes.h" namespace "coxtypes": ctypedef unsigned short Length ctypedef Ulong StarOp # for numbering star operations - ################# # CoxWord # ################# diff --git a/src/sage/libs/coxeter3/meson.build b/src/sage/libs/coxeter3/meson.build new file mode 100644 index 00000000000..ee51998f27d --- /dev/null +++ b/src/sage/libs/coxeter3/meson.build @@ -0,0 +1,23 @@ +coxeter3 = cc.find_library('coxeter3', required: false, disabler: true) +py.install_sources( + '__init__.py', + 'all__sagemath_coxeter3.py', + 'coxeter.pxd', + 'coxeter_group.py', + 'decl.pxd', + subdir: 'sage/libs/coxeter3', +) + +extension_data_cpp = {'coxeter': files('coxeter.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/coxeter3', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython], + dependencies: [py_dep, cysignals, coxeter3], + ) +endforeach diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index 19472171403..202d0824e37 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -147,7 +147,6 @@ cdef extern from "ecl/ecl.h": ecl_character ecl_char(cl_object s, cl_index i) ecl_character ecl_char_set(cl_object s, cl_index i, ecl_character c) - # S-expr evaluation and function calls cl_object cl_safe_eval(cl_object form, cl_object env, cl_object value) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 07650bd3810..aaede566da6 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -26,7 +26,7 @@ from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from cpython.object cimport Py_EQ, Py_NE -# it would be preferrable to let bint_symbolp wrap an efficient macro +# it would be preferable to let bint_symbolp wrap an efficient macro # but the macro provided in object.h doesn't seem to work cdef bint bint_symbolp(cl_object obj) noexcept: return not(cl_symbolp(obj) == ECL_NIL) diff --git a/src/sage/libs/eclib/homspace.pyx b/src/sage/libs/eclib/homspace.pyx index a502062ff79..f4baaee280a 100644 --- a/src/sage/libs/eclib/homspace.pyx +++ b/src/sage/libs/eclib/homspace.pyx @@ -71,7 +71,7 @@ cdef class ModularSymbols: sage: CremonaModularSymbols(37, cuspidal=True).__repr__() 'Cremona Cuspidal Modular Symbols space of dimension 4 for Gamma_0(37) of weight 2 with sign 0' """ - return "Cremona %sModular Symbols space of dimension %s for Gamma_0(%s) of weight 2 with sign %s"%( + return "Cremona %sModular Symbols space of dimension %s for Gamma_0(%s) of weight 2 with sign %s" % ( 'Cuspidal ' if self.is_cuspidal() else '', self.dimension(), self.level(), self.sign()) diff --git a/src/sage/libs/eclib/mat.pyx b/src/sage/libs/eclib/mat.pyx index a5ef4f45c68..bfdeb6ae5c1 100644 --- a/src/sage/libs/eclib/mat.pyx +++ b/src/sage/libs/eclib/mat.pyx @@ -49,7 +49,7 @@ cdef class Matrix: [-1 1 1 -1 0] [ 0 -1 0 0 0] """ - return "%s x %s Cremona matrix over Rational Field"%(self.nrows(), self.ncols()) + return "%s x %s Cremona matrix over Rational Field" % (self.nrows(), self.ncols()) def str(self): r""" diff --git a/src/sage/libs/eclib/meson.build b/src/sage/libs/eclib/meson.build new file mode 100644 index 00000000000..dd6ecc3f581 --- /dev/null +++ b/src/sage/libs/eclib/meson.build @@ -0,0 +1,31 @@ +py.install_sources( + '__init__.pxd', + '__init__.py', + 'all.py', + 'constructor.py', + 'homspace.pxd', + 'interface.py', + 'mat.pxd', + 'newforms.pxd', + subdir: 'sage/libs/eclib', +) + +extension_data_cpp = { + 'homspace': files('homspace.pyx'), + 'mat': files('mat.pyx'), + 'mwrank': files('mwrank.pyx'), + 'newforms': files('newforms.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/eclib', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_flint, inc_ntl, inc_rings], + dependencies: [py_dep, cysignals, ec, flint, gmp], + ) +endforeach + diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index cdac5a145cb..5bc54a04cdc 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -208,7 +208,7 @@ cdef class _bigint: if s.isdigit() or s[0] == "-" and s[1:].isdigit(): self.x = str_to_bigint(str_to_bytes(s)) else: - raise ValueError("invalid _bigint: %r"%x) + raise ValueError("invalid _bigint: %r" % x) def __dealloc__(self): """ @@ -830,7 +830,7 @@ cdef class _mw: and the computed bound. - ``sat_low_bd`` -- integer (default: 2); only do saturation at - prime not less than this. For exampe, if the points have + prime not less than this. For example, if the points have been found via 2-descent they should already be 2-saturated, and ``sat_low_bd=3`` is appropriate. diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index d2af433a801..c067ef6acb9 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -237,7 +237,7 @@ cdef class ECModularSymbol: sage: M = ECModularSymbol(E, 0); M Modular symbol with sign 0 over Rational Field attached to Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field """ - return "Modular symbol with sign %s over Rational Field attached to %s"%(self.sign, self._E) + return "Modular symbol with sign %s over Rational Field attached to %s" % (self.sign, self._E) def __call__(self, r, sign=None, base_at_infinity=True): r""" diff --git a/src/sage/libs/flint/fmpz_poly_sage.pyx b/src/sage/libs/flint/fmpz_poly_sage.pyx index 5b8e9276732..3904e397e27 100644 --- a/src/sage/libs/flint/fmpz_poly_sage.pyx +++ b/src/sage/libs/flint/fmpz_poly_sage.pyx @@ -87,9 +87,9 @@ cdef class Fmpz_poly(SageObject): sage: f[2] == 10**100000 True """ - if isinstance(value, Integer) : + if isinstance(value, Integer): fmpz_poly_set_coeff_mpz(self.poly, i, (value).value) - else : + else: fmpz_poly_set_coeff_si(self.poly, i, value) def __getitem__(self, i): @@ -336,7 +336,7 @@ cdef class Fmpz_poly(SageObject): fmpz_poly_divrem(Q.poly, R.poly, self.poly, other.poly) return Q, R - def left_shift(self, unsigned long n) : + def left_shift(self, unsigned long n): """ Left shift ``self`` by `n`. @@ -353,7 +353,7 @@ cdef class Fmpz_poly(SageObject): return res - def right_shift(self, unsigned long n) : + def right_shift(self, unsigned long n): """ Right shift ``self`` by `n`. @@ -383,7 +383,7 @@ cdef class Fmpz_poly(SageObject): fmpz_poly_pseudo_divrem(Q.poly, R.poly, &d, self.poly, other.poly) return Q, R, d - def derivative(self) : + def derivative(self): """ Return the derivative of ``self``. diff --git a/src/sage/libs/flint/meson.build b/src/sage/libs/flint/meson.build new file mode 100644 index 00000000000..fe12b28b5f7 --- /dev/null +++ b/src/sage/libs/flint/meson.build @@ -0,0 +1,177 @@ +py.install_sources( + '__init__.py', + 'acb.pxd', + 'acb_calc.pxd', + 'acb_dft.pxd', + 'acb_dirichlet.pxd', + 'acb_elliptic.pxd', + 'acb_hypgeom.pxd', + 'acb_macros.pxd', + 'acb_mat.pxd', + 'acb_mat_macros.pxd', + 'acb_modular.pxd', + 'acb_poly.pxd', + 'acb_poly_macros.pxd', + 'acb_theta.pxd', + 'acf.pxd', + 'aprcl.pxd', + 'arb.pxd', + 'arb_calc.pxd', + 'arb_fmpz_poly.pxd', + 'arb_fpwrap.pxd', + 'arb_hypgeom.pxd', + 'arb_macros.pxd', + 'arb_mat.pxd', + 'arb_mat_macros.pxd', + 'arb_poly.pxd', + 'arf.pxd', + 'arith.pxd', + 'bernoulli.pxd', + 'bool_mat.pxd', + 'ca.pxd', + 'ca_ext.pxd', + 'ca_field.pxd', + 'ca_mat.pxd', + 'ca_poly.pxd', + 'ca_vec.pxd', + 'calcium.pxd', + 'd_mat.pxd', + 'd_vec.pxd', + 'dirichlet.pxd', + 'dlog.pxd', + 'double_extras.pxd', + 'double_interval.pxd', + 'fexpr.pxd', + 'fexpr_builtin.pxd', + 'fft.pxd', + 'flint.pxd', + 'flint_ntl_wrap.h', + 'flint_wrap.h', + 'fmpq.pxd', + 'fmpq_mat.pxd', + 'fmpq_mat_macros.pxd', + 'fmpq_mpoly.pxd', + 'fmpq_mpoly_factor.pxd', + 'fmpq_poly.pxd', + 'fmpq_poly_macros.pxd', + 'fmpq_poly_sage.pxd', + 'fmpq_vec.pxd', + 'fmpz.pxd', + 'fmpz_extras.pxd', + 'fmpz_factor.pxd', + 'fmpz_factor_sage.pxd', + 'fmpz_lll.pxd', + 'fmpz_macros.pxd', + 'fmpz_mat.pxd', + 'fmpz_mat_macros.pxd', + 'fmpz_mod.pxd', + 'fmpz_mod_mat.pxd', + 'fmpz_mod_mpoly.pxd', + 'fmpz_mod_mpoly_factor.pxd', + 'fmpz_mod_poly.pxd', + 'fmpz_mod_poly_factor.pxd', + 'fmpz_mod_vec.pxd', + 'fmpz_mpoly.pxd', + 'fmpz_mpoly_factor.pxd', + 'fmpz_mpoly_q.pxd', + 'fmpz_poly.pxd', + 'fmpz_poly_factor.pxd', + 'fmpz_poly_macros.pxd', + 'fmpz_poly_mat.pxd', + 'fmpz_poly_q.pxd', + 'fmpz_poly_sage.pxd', + 'fmpz_vec.pxd', + 'fmpzi.pxd', + 'fq.pxd', + 'fq_default.pxd', + 'fq_default_mat.pxd', + 'fq_default_poly.pxd', + 'fq_default_poly_factor.pxd', + 'fq_embed.pxd', + 'fq_mat.pxd', + 'fq_nmod.pxd', + 'fq_nmod_embed.pxd', + 'fq_nmod_mat.pxd', + 'fq_nmod_mpoly.pxd', + 'fq_nmod_mpoly_factor.pxd', + 'fq_nmod_poly.pxd', + 'fq_nmod_poly_factor.pxd', + 'fq_nmod_vec.pxd', + 'fq_poly.pxd', + 'fq_poly_factor.pxd', + 'fq_vec.pxd', + 'fq_zech.pxd', + 'fq_zech_embed.pxd', + 'fq_zech_mat.pxd', + 'fq_zech_poly.pxd', + 'fq_zech_poly_factor.pxd', + 'fq_zech_vec.pxd', + 'gr.pxd', + 'gr_generic.pxd', + 'gr_mat.pxd', + 'gr_mpoly.pxd', + 'gr_poly.pxd', + 'gr_special.pxd', + 'gr_vec.pxd', + 'hypgeom.pxd', + 'long_extras.pxd', + 'mag.pxd', + 'mag_macros.pxd', + 'mpf_mat.pxd', + 'mpf_vec.pxd', + 'mpfr_mat.pxd', + 'mpfr_vec.pxd', + 'mpn_extras.pxd', + 'mpoly.pxd', + 'nf.pxd', + 'nf_elem.pxd', + 'nmod.pxd', + 'nmod_mat.pxd', + 'nmod_mpoly.pxd', + 'nmod_mpoly_factor.pxd', + 'nmod_poly.pxd', + 'nmod_poly_factor.pxd', + 'nmod_poly_mat.pxd', + 'nmod_vec.pxd', + 'ntl_interface.pxd', + 'padic.pxd', + 'padic_mat.pxd', + 'padic_poly.pxd', + 'partitions.pxd', + 'perm.pxd', + 'profiler.pxd', + 'qadic.pxd', + 'qfb.pxd', + 'qqbar.pxd', + 'qsieve.pxd', + 'thread_pool.pxd', + 'types.pxd', + 'ulong_extras.pxd', + subdir: 'sage/libs/flint', +) + +extension_data = { + 'arith' : files('arith.pyx'), + 'arith_sage' : files('arith_sage.pyx'), + 'flint_sage' : files('flint_sage.pyx'), + 'fmpq_poly_sage' : files('fmpq_poly_sage.pyx'), + 'fmpz_factor_sage' : files('fmpz_factor_sage.pyx'), + 'fmpz_poly' : files('fmpz_poly.pyx'), + 'fmpz_poly_sage' : files('fmpz_poly_sage.pyx'), + 'qsieve' : files('qsieve.pyx'), + 'qsieve_sage' : files('qsieve_sage.pyx'), + 'ulong_extras' : files('ulong_extras.pyx'), + 'ulong_extras_sage' : files('ulong_extras_sage.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/flint', + install: true, + include_directories: [inc_cpython, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp, mpfr], + ) +endforeach + diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi index db9e5d61204..b1be0216a2e 100644 --- a/src/sage/libs/flint/nmod_poly_linkage.pxi +++ b/src/sage/libs/flint/nmod_poly_linkage.pxi @@ -577,18 +577,37 @@ cdef inline int celement_gcd(nmod_poly_t res, nmod_poly_t a, nmod_poly_t b, unsi True sage: (G//d)*d == G True + + Check that we catch a case where FLINT fails. These generate the unit + ideal, so their GCD should be 1 (or another unit):: + + sage: R. = Integers(121)[] + sage: f = 11*x^2 + 1 + sage: f - 61*x*f.derivative() + 1 + sage: gcd(f, f.derivative()) + Traceback (most recent call last): + ... + RuntimeError: FLINT gcd calculation failed """ if celement_is_zero(b, n): nmod_poly_set(res, a) return 0 - # A check that the leading coefficients are invertible is *not* sufficient + # FLINT provides no interface for detecting errors here try: sig_on() - nmod_poly_gcd(res, a, b) - sig_off() + try: + nmod_poly_gcd(res, a, b) + finally: + sig_off() except RuntimeError: - raise ValueError("non-invertible elements encountered during GCD") + raise RuntimeError("FLINT gcd calculation failed") + + cdef unsigned long leadcoeff = nmod_poly_get_coeff_ui(res, nmod_poly_degree(res)) + cdef unsigned long modulus = nmod_poly_modulus(res) + if n_gcd(modulus, leadcoeff) == 1: + nmod_poly_make_monic(res, res) cdef inline int celement_xgcd(nmod_poly_t res, nmod_poly_t s, nmod_poly_t t, nmod_poly_t a, nmod_poly_t b, unsigned long n) except -2: """ @@ -617,8 +636,26 @@ cdef inline int celement_xgcd(nmod_poly_t res, nmod_poly_t s, nmod_poly_t t, nmo True sage: (G//d)*d == G True + + TESTS: + + Ensure that :issue:`38537` is fixed:: + + sage: k = Zmod(2**16) + sage: R. = k[] + sage: u = x + 10161 + sage: v = x + 10681 + sage: u.xgcd(v) + Traceback (most recent call last): + ... + ValueError: non-invertible elements encountered during XGCD """ - nmod_poly_xgcd(res, s, t, a, b) + try: + sig_on() + nmod_poly_xgcd(res, s, t, a, b) + sig_off() + except RuntimeError: + raise ValueError("non-invertible elements encountered during XGCD") cdef factor_helper(Polynomial_zmod_flint poly, bint squarefree=False): diff --git a/src/sage/libs/flint/qsieve.pxd b/src/sage/libs/flint/qsieve.pxd index 7bd1bf5b862..9f40fb53daa 100644 --- a/src/sage/libs/flint/qsieve.pxd +++ b/src/sage/libs/flint/qsieve.pxd @@ -24,7 +24,7 @@ cdef extern from "flint_wrap.h": void qsieve_init_poly_next(qs_t qs_inf, slong i) noexcept void qsieve_compute_C(fmpz_t C, qs_t qs_inf, qs_poly_t poly) noexcept void qsieve_do_sieving(qs_t qs_inf, unsigned char * sieve, qs_poly_t poly) noexcept - void qsieve_do_sieving2(qs_t qs_inf, unsigned char * seive, qs_poly_t poly) noexcept + void qsieve_do_sieving2(qs_t qs_inf, unsigned char * sieve, qs_poly_t poly) noexcept slong qsieve_evaluate_candidate(qs_t qs_inf, ulong i, unsigned char * sieve, qs_poly_t poly) noexcept slong qsieve_evaluate_sieve(qs_t qs_inf, unsigned char * sieve, qs_poly_t poly) noexcept slong qsieve_collect_relations(qs_t qs_inf, unsigned char * sieve) noexcept diff --git a/src/sage/libs/gap/context_managers.py b/src/sage/libs/gap/context_managers.py index 539b721af83..611708ad96b 100644 --- a/src/sage/libs/gap/context_managers.py +++ b/src/sage/libs/gap/context_managers.py @@ -45,7 +45,7 @@ from sage.libs.gap.libgap import libgap -class GlobalVariableContext(): +class GlobalVariableContext: def __init__(self, variable, value): """ diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index d70e8bad6b2..c24408f401a 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1320,12 +1320,22 @@ cdef class GapElement(RingElement): sage: p.sage().parent() Fraction Field of Univariate Polynomial Ring in x over Integer Ring + sage: G0 = libgap.SymplecticGroup(4,2) + sage: P = G0.IsomorphismFpGroup().Range() + sage: G = P.sage() + sage: G.gap() == P + True + + sage: F0 = libgap.FreeGroup(2) + sage: F = F0.sage() + sage: F.gap() is F0 + True + TESTS: Check :issue:`30496`:: sage: x = libgap.Integers.Indeterminate("x") - sage: p = x^2 - 2*x sage: p.sage() x^2 - 2*x @@ -1373,6 +1383,20 @@ cdef class GapElement(RingElement): # that we can convert return [item.sage() for item in self.AsList()] + elif self.IsFreeGroup(): + from sage.groups.free_group import FreeGroup_class + names = tuple(str(g) for g in self.GeneratorsOfGroup()) + return FreeGroup_class(names, gap_group=self) + + elif self.IsFpGroup(): + from sage.groups.free_group import FreeGroup + from sage.groups.finitely_presented import FinitelyPresentedGroup + # names = tuple(str(g).replace(".", "_") for g in self.FreeGroupOfFpGroup().GeneratorsOfGroup()) + F = self.FreeGroupOfFpGroup().sage() + relations = tuple(F(rel.LetterRepAssocWord().sage()) + for rel in self.RelatorsOfFpGroup()) + return FinitelyPresentedGroup(F, relations, libgap_fpgroup=self) + raise NotImplementedError('cannot construct equivalent Sage object') @@ -2413,31 +2437,22 @@ cdef class GapElement_Function(GapElement): sage: b Sym( [ 1 .. 4 ] ) - sage: sorted(a(b)) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(a(b))] + ["1", "S4", "A4", "C2 x C2"] sage: libgap.eval("a := NormalSubgroups") sage: libgap.eval("b := SymmetricGroup(4)") Sym( [ 1 .. 4 ] ) sage: libgap.collect() - sage: sorted(libgap.eval('a') (libgap.eval('b'))) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(libgap.eval('a') (libgap.eval('b')))] + ["1", "S4", "A4", "C2 x C2"] sage: a = libgap.eval('a') sage: b = libgap.eval('b') sage: libgap.collect() - sage: sorted(a(b)) - [Group(()), - Sym( [ 1 .. 4 ] ), - Alt( [ 1 .. 4 ] ), - Group([ (1,4)(2,3), (1,2)(3,4) ])] + sage: [x.StructureDescription() for x in sorted(a(b))] + ["1", "S4", "A4", "C2 x C2"] Not every ``GapElement`` is callable:: diff --git a/src/sage/libs/gap/meson.build b/src/sage/libs/gap/meson.build new file mode 100644 index 00000000000..2302a169cb2 --- /dev/null +++ b/src/sage/libs/gap/meson.build @@ -0,0 +1,38 @@ +py.install_sources( + 'all.py', + 'all_documented_functions.py', + 'assigned_names.py', + 'context_managers.py', + 'element.pxd', + 'gap_functions.py', + 'gap_globals.py', + 'gap_includes.pxd', + 'operations.py', + 'sage.gaprc', + 'saved_workspace.py', + 'test.py', + 'test_long.py', + 'util.pxd', + subdir: 'sage/libs/gap', +) + +# Ensure that the gaprc file is installed also in editable mode +fs.copyfile('sage.gaprc') + +extension_data = { + 'element' : files('element.pyx'), + 'libgap' : files('libgap.pyx'), + 'util' : files('util.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/gap', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gap, gmp], + ) +endforeach + diff --git a/src/sage/libs/gap/test.py b/src/sage/libs/gap/test.py index 72daa93acb6..73dad049dc9 100644 --- a/src/sage/libs/gap/test.py +++ b/src/sage/libs/gap/test.py @@ -20,7 +20,7 @@ def test_write_to_file(): fname = tmp_filename() message = "Ceci n'est pas une groupe" libgap.PrintTo(fname, message) - with open(fname, 'r') as f: + with open(fname) as f: assert f.read() == message SystemFile = libgap.function_factory('StringFile') assert SystemFile(fname).sage() == message diff --git a/src/sage/libs/gap/test_long.py b/src/sage/libs/gap/test_long.py index 262db5ad287..c92ff9d5223 100644 --- a/src/sage/libs/gap/test_long.py +++ b/src/sage/libs/gap/test_long.py @@ -15,7 +15,7 @@ def test_loop_1(): """ libgap.collect() for i in range(10000): - G = libgap.CyclicGroup(2) + _ = libgap.CyclicGroup(2) def test_loop_2(): diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index e585431cda9..29fa347ff30 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -368,7 +368,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: # here if the error handler was set; but in case it wasn't # let's still check the result... nresults = GAP_LenList(result) - if nresults > 1: # to mimick the old libGAP + if nresults > 1: # to mimic the old libGAP # TODO: Get rid of this restriction eventually? raise GAPError("can only evaluate a single statement") diff --git a/src/sage/libs/giac/__init__.py b/src/sage/libs/giac/__init__.py index 208fc9af930..ef2267c4378 100644 --- a/src/sage/libs/giac/__init__.py +++ b/src/sage/libs/giac/__init__.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.libs.giac """ Wrappers for Giac functions @@ -35,7 +36,7 @@ # Remarks for doctests: # 1) The first time that the c++ library giac is loaded a message appears. -# This message is version and arch dependant. +# This message is version and arch dependent. # 2) When proba_epsilon is too bad (>1e-6?) setting it to a better value # will give an additional message like the following one: # Restoring proba epsilon to 1e-6 from 1e-12 @@ -146,7 +147,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, for giac. If ``None``, the global ``giacpy_sage.giacsettings.threads`` is considered. - - ``prot`` -- boolean (default: ``False``); if ``True`` print detailled informations + - ``prot`` -- boolean (default: ``False``); if ``True`` print detailed information - ``elim_variables`` -- (default: ``None``) a list of variables to eliminate from the ideal @@ -208,7 +209,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, ... Time: CPU 168.98 s, Wall: 94.13 s - You can get detailled information by setting ``prot=True`` + You can get detailed information by setting ``prot=True`` :: diff --git a/src/sage/libs/giac/giac.pxd b/src/sage/libs/giac/giac.pxd index 1d9da88d8d4..d069c04a8a1 100644 --- a/src/sage/libs/giac/giac.pxd +++ b/src/sage/libs/giac/giac.pxd @@ -193,7 +193,7 @@ cdef extern from "misc.h": int giacgencmp( gen & , gen & , context *) except + int giacgenrichcmp( gen & , gen & , int, context *) except + #NB: we use the following multiplication otherwise some giac errors make python quit: - #l=giac([1,2]); l.tranpose()*l + #l=giac([1,2]); l.transpose()*l gen GIAC_giacmul "giacmul"( gen & , gen & , context *) except + gen GIAC_giacdiv "giacdiv"( gen & , gen & , context *) except + gen GIAC_giacmod "giacmod"( gen & , gen & , context *) except + diff --git a/src/sage/libs/giac/giac.pyx b/src/sage/libs/giac/giac.pyx index 3042220118c..aeeee3185fa 100644 --- a/src/sage/libs/giac/giac.pyx +++ b/src/sage/libs/giac/giac.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.libs.giac # distutils: libraries = giac # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -170,15 +171,8 @@ from sage.interfaces.giac import giac # Python3 compatibility ############################ -def decstring23(s): - return s.decode() - - def encstring23(s): return bytes(s, 'UTF-8') - - -listrange = list, range # End of Python3 compatibility ##################### @@ -216,7 +210,7 @@ def _giac(s): sage: (x+2*y).cos().texpand() cos(x)*(2*cos(y)^2-1)-sin(x)*2*cos(y)*sin(y) - Coercion, Pygen and internal giac variables: The most usefull objects will + Coercion, Pygen and internal giac variables: The most useful objects will be the Python object of type Pygen.:: sage: x,y,z = libgiac('x,y,z') @@ -296,7 +290,7 @@ def _giac(s): sage: A [[44,2],[3,4]] - Sparse Matrices are avaible via the table function: + Sparse Matrices are available via the table function: :: @@ -517,7 +511,7 @@ def _giac(s): ``q2a``, ``isom``, ``mkisom`` - - *Finite Fieds* + - *Finite Fields* * ``%``, ``% 0``, ``mod``, ``GF``, ``powmod`` @@ -652,7 +646,7 @@ cdef class GiacSetting(Pygen): l = Pygen('cas_setup()').eval() pl = [ i for i in l ] pl[6] = value - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property sqrtflag: r""" @@ -668,7 +662,7 @@ cdef class GiacSetting(Pygen): pl[9]=1 else: pl[9]=0 - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property complexflag: r""" @@ -696,7 +690,7 @@ cdef class GiacSetting(Pygen): pl[2] = 1 else: pl[2] = 0 - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property eval_level: r""" @@ -722,7 +716,7 @@ cdef class GiacSetting(Pygen): l = Pygen('cas_setup()').eval() pl = [ i for i in l ] pl[7] = [l[7][0],l[7][1],l[7][2], value] - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property proba_epsilon: r""" @@ -746,7 +740,7 @@ cdef class GiacSetting(Pygen): l = Pygen('cas_setup()').eval() pl = [ i for i in l ] pl[5] = [l[5][0],value] - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property epsilon: r""" @@ -769,7 +763,7 @@ cdef class GiacSetting(Pygen): l = Pygen('cas_setup()').eval() pl = [ i for i in l ] pl[5] = [value,l[5][1]] - Pygen('cas_setup(%s)'%(pl)).eval() + Pygen('cas_setup(%s)' % pl).eval() property threads: r""" @@ -779,7 +773,7 @@ cdef class GiacSetting(Pygen): return (self.cas_setup()[7][0])._val def __set__(self,value): - Pygen('threads:=%s'%(str(value))).eval() + Pygen('threads:=%s' % str(value)).eval() ######################################################## # # @@ -797,7 +791,7 @@ cdef class Pygen(GiacMethods_base): #NB: the != here gives problems with the __richcmp__ function #if (s!=None): # so it's better to use isinstance - if (isinstance(s,None.__class__)): + if (isinstance(s, None.__class__)): # Do NOT replace with: self=GIACNULL (cf the doctest in __repr__ sig_on() self.gptr = new gen ((GIACNULL).gptr[0]) @@ -848,7 +842,7 @@ cdef class Pygen(GiacMethods_base): self.gptr = new gen((s).gptr[0]) sig_off() - elif isinstance(s, listrange): + elif isinstance(s, (list, range)): sig_on() self.gptr = new gen(_wrap_pylist(s),0) sig_off() @@ -866,7 +860,7 @@ cdef class Pygen(GiacMethods_base): s = s._giac_init_() except AttributeError: s = SRexpressiontoGiac(s) - if not(isinstance(s, str)): #modif python3 + if not isinstance(s, str): s = s.__str__() sig_on() self.gptr = new gen(encstring23(s),context_ptr) @@ -880,14 +874,14 @@ cdef class Pygen(GiacMethods_base): sig_on() t=GIAC_taille(self.gptr[0], 6000) sig_off() - if (t<6000) : + if t < 6000: sig_on() - result=decstring23(GIAC_print(self.gptr[0], context_ptr).c_str()) #python3 + result = GIAC_print(self.gptr[0], context_ptr).c_str().decode() sig_off() return result else: sig_on() - result=str(self.type)+"\nResult is too big for Display. If you really want to see it use print" + result = str(self.type) + "\nResult is too big for Display. If you really want to see it use print" sig_off() return result @@ -895,7 +889,7 @@ cdef class Pygen(GiacMethods_base): #if self.gptr == NULL: # return '' sig_on() - result=decstring23(GIAC_print(self.gptr[0], context_ptr).c_str()) #python3 + result = GIAC_print(self.gptr[0], context_ptr).c_str().decode() sig_off() return result @@ -926,7 +920,7 @@ cdef class Pygen(GiacMethods_base): TESTS:: sage: from sage.libs.giac.giac import libgiac - sage: l=libgiac(list(range(10^6)));l[5] #python3 + sage: l=libgiac(list(range(10^6)));l[5] 5 sage: l[35:50:7] [35,42,49] @@ -960,19 +954,19 @@ cdef class Pygen(GiacMethods_base): if(i<0): i=i+n sig_on() - result=self.gptr[0][i] + result = self.gptr[0][i] sig_off() return _wrap_gen(result) else: - raise IndexError('list index %s out of range'%(i)) + raise IndexError('list index %s out of range' % i) else: - if isinstance(i,slice): + if isinstance(i, slice): sig_on() - result=gen(_getgiacslice(self,i),self._subtype) + result = gen(_getgiacslice(self,i),self._subtype) sig_off() return _wrap_gen(result) # add support for multi indexes - elif isinstance(i,tuple): + elif isinstance(i, tuple): if(len(i)==2): return self[i[0]][i[1]] elif(len(i)==1): @@ -984,17 +978,18 @@ cdef class Pygen(GiacMethods_base): raise TypeError('gen indexes are not yet implemented') # Here we add support to formal variable indexes: else: - cmd='%s[%s]'%(self,i) - ans=Pygen(cmd).eval() + cmd = '%s[%s]' % (self, i) + ans = Pygen(cmd).eval() # if the answer is a string, it must be an error message because self is not a list or a string if (ans._type == 12): - raise TypeError("Error executing code in Giac\nCODE:\n\t%s\nGiac ERROR:\n\t%s"%(cmd, ans)) + raise TypeError("Error executing code in Giac\nCODE:\n\t%s\nGiac ERROR:\n\t%s" % (cmd, ans)) return ans def __setitem__(self, key, value): """ Set the value of a coefficient of a giac vector or matrix or list. - Warning: It is an in place affectation. + + Warning: It is an in place affectation. TESTS:: @@ -1034,9 +1029,9 @@ cdef class Pygen(GiacMethods_base): sig_on() cdef gen g = gen(encstring23('GIACPY_TMP_NAME050268070969290100291003'),context_ptr) GIAC_sto((self).gptr[0],g,1,context_ptr) - g=gen(encstring23('GIACPY_TMP_NAME050268070969290100291003[%s]'%(str(key))),context_ptr) + g = gen(encstring23('GIACPY_TMP_NAME050268070969290100291003[%s]' % str(key)), context_ptr) v=((Pygen(value).eval())).gptr[0] - GIAC_sto(v,g,1,context_ptr) + GIAC_sto(v, g, 1, context_ptr) Pygen('purge(GIACPY_TMP_NAME050268070969290100291003):;').eval() sig_off() return @@ -1065,7 +1060,7 @@ cdef class Pygen(GiacMethods_base): def eval(self): cdef gen result sig_on() - result=GIAC_protecteval(self.gptr[0],giacsettings.eval_level,context_ptr) + result = GIAC_protecteval(self.gptr[0],giacsettings.eval_level,context_ptr) sig_off() return _wrap_gen(result) @@ -1078,7 +1073,7 @@ cdef class Pygen(GiacMethods_base): if not isinstance(self, Pygen): self=Pygen(self) sig_on() - result= (self).gptr[0] + (right).gptr[0] + result = (self).gptr[0] + (right).gptr[0] sig_off() return _wrap_gen(result) @@ -1128,7 +1123,7 @@ cdef class Pygen(GiacMethods_base): if not isinstance(self, Pygen): self=Pygen(self) sig_on() - result= (self).gptr[0] - (right).gptr[0] + result = (self).gptr[0] - (right).gptr[0] sig_off() return _wrap_gen(result) @@ -1144,19 +1139,19 @@ cdef class Pygen(GiacMethods_base): """ cdef gen result if not isinstance(right, Pygen): - right=Pygen(right) + right = Pygen(right) if not isinstance(self, Pygen): - self=Pygen(self) - #result= (self).gptr[0] * (right).gptr[0] + self = Pygen(self) + #result = (self).gptr[0] * (right).gptr[0] #NB: with the natural previous method, the following error generated by #giac causes python to quit instead of an error message. #l=Pygen([1,2]);l.transpose()*l; sig_on() - result= GIAC_giacmul((self).gptr[0] , (right).gptr[0],context_ptr) + result = GIAC_giacmul((self).gptr[0], (right).gptr[0],context_ptr) sig_off() return _wrap_gen(result) -#PB / in python3 is truediv + # PB / in python3 is truediv def __div__(self, right): """ TESTS:: @@ -1169,57 +1164,57 @@ cdef class Pygen(GiacMethods_base): """ cdef gen result if not isinstance(right, Pygen): - right=Pygen(right) + right = Pygen(right) if not isinstance(self, Pygen): - self=Pygen(self) + self = Pygen(self) sig_on() - result= GIAC_giacdiv((self).gptr[0] , (right).gptr[0],context_ptr) + result = GIAC_giacdiv((self).gptr[0], (right).gptr[0],context_ptr) sig_off() return _wrap_gen(result) def __truediv__(self, right): cdef gen result if not isinstance(right, Pygen): - right=Pygen(right) + right = Pygen(right) if not isinstance(self, Pygen): - self=Pygen(self) + self = Pygen(self) sig_on() - result= (self).gptr[0] / (right).gptr[0] + result = (self).gptr[0] / (right).gptr[0] sig_off() return _wrap_gen(result) - def __pow__(self, right ,ignored): + def __pow__(self, right, ignored): cdef gen result if not isinstance(right, Pygen): - right=Pygen(right) + right = Pygen(right) if not isinstance(self, Pygen): - self=Pygen(self) + self = Pygen(self) sig_on() - result= GIAC_pow((self).gptr[0],(right).gptr[0], context_ptr ) + result = GIAC_pow((self).gptr[0], (right).gptr[0], context_ptr ) sig_off() return _wrap_gen(result) def __mod__(self, right): cdef gen result if not isinstance(right, Pygen): - right=Pygen(right) + right = Pygen(right) if not isinstance(self, Pygen): - self=Pygen(self) - #result= gen(GIAC_makenewvecteur((self).gptr[0],(right).gptr[0]),1) + self = Pygen(self) + #result = gen(GIAC_makenewvecteur((self).gptr[0],(right).gptr[0]),1) #to have an integer output: - #result= GIAC_smod(result,context_ptr) + #result = GIAC_smod(result,context_ptr) #we give a modular output: sig_on() - result= GIAC_giacmod((self).gptr[0],(right).gptr[0],context_ptr) + result = GIAC_giacmod((self).gptr[0], (right).gptr[0],context_ptr) sig_off() return _wrap_gen(result) def __neg__(self): cdef gen result if not isinstance(self, Pygen): - self=Pygen(self) + self = Pygen(self) sig_on() - result= GIAC_neg((self).gptr[0]) + result = GIAC_neg((self).gptr[0]) sig_off() return _wrap_gen(result) @@ -1232,12 +1227,12 @@ cdef class Pygen(GiacMethods_base): def savegen(self, str filename): """ - Archive a Pygen element to a file in giac compressed format. + Archive a Pygen element to a file in giac compressed format. - Use the loadgiacgen command to get back the Pygen from the file. - In C++ these files can be opened with ``giac::unarchive``. + Use the loadgiacgen command to get back the Pygen from the file. + In C++ these files can be opened with ``giac::unarchive``. - EXAMPLES:: + EXAMPLES:: sage: from sage.libs.giac.giac import * sage: f=libgiac('(x+y+z+2)**10'); g=f.normal() @@ -1287,7 +1282,7 @@ cdef class Pygen(GiacMethods_base): # def htmlhelp(self, str lang='en'): # """ - # Open the giac html detailled help about ``self`` in an external browser + # Open the giac html detailed help about ``self`` in an external browser # There are currently 3 supported languages: 'en', 'fr', 'el' @@ -1296,8 +1291,8 @@ cdef class Pygen(GiacMethods_base): # if (not lang in ['en', 'fr', 'el']): # lang='en' # try: - # url=decstring23(browser_help(self.gptr[0],l[lang])) #python3 - # giacbasedir=decstring23(GIAC_giac_aide_dir()) # python3 + # url=browser_help(self.gptr[0],l[lang]).decode() + # giacbasedir=GIAC_giac_aide_dir().decode() # except: # raise RuntimeError('giac docs dir not found') # print(url) @@ -1338,7 +1333,7 @@ cdef class Pygen(GiacMethods_base): \frac{...x^{4}...-...y...}{...y^{2}-3...x...} """ sig_on() - result=decstring23(GIAC_gen2tex(self.gptr[0], context_ptr).c_str()) #python3 + result = GIAC_gen2tex(self.gptr[0], context_ptr).c_str().decode() sig_off() return result @@ -1473,15 +1468,15 @@ cdef class Pygen(GiacMethods_base): """ typ = self._type - if (typ != 7) : + if typ != 7: # self is not a list - if ( typ == 0 or typ == 2): + if typ == 0 or typ == 2: return ZZ(self) - elif (typ == 10): + elif typ == 10: return QQ(self) - elif (typ == 15): + elif typ == 15: # modular integer sig_on() a = _wrap_gen( (self.gptr.ref_MODptr())[0]) @@ -1490,10 +1485,10 @@ cdef class Pygen(GiacMethods_base): sig_off() return result - elif (typ == 12): + elif typ == 12: # string sig_on() - result=eval(self.__str__()) + result = eval(self.__str__()) sig_off() return result @@ -1503,7 +1498,7 @@ cdef class Pygen(GiacMethods_base): else: # self is a list sig_on() - result=[entry.sage() for entry in self] + result = [entry.sage() for entry in self] sig_off() return result @@ -1533,7 +1528,7 @@ cdef class Pygen(GiacMethods_base): sage: libgiac.integrate(cos(y), y).sage() sin(π) """ - if isinstance(R,SR.__class__): + if isinstance(R, SR.__class__): # Try to convert some functions names to the symbolic ring lsymbols = symbol_table['giac'].copy() #lsymbols.update(locals) @@ -1547,7 +1542,7 @@ cdef class Pygen(GiacMethods_base): raise NotImplementedError("Unable to parse Giac output: %s" % self.__repr__()) else: try: - result=R(self.__str__()) + result = R(self.__str__()) return result except Exception: @@ -1601,7 +1596,7 @@ cdef class Pygen(GiacMethods_base): sage: vector(v+v/3,QQ) (0, 4/3, 8/3, 4, 16/3, 20/3, 8, 28/3, 32/3, 12) """ - if(isinstance(R, None.__class__)): + if isinstance(R, None.__class__): R=ZZ v = self.dim() @@ -1668,7 +1663,7 @@ cdef class Pygen(GiacMethods_base): if not isinstance(self, Pygen): self = Pygen(self) sig_on() - result= giacgenrichcmp((self).gptr[0],(other).gptr[0], op, context_ptr ) + result = giacgenrichcmp((self).gptr[0],(other).gptr[0], op, context_ptr ) sig_off() return result == 1 @@ -1686,7 +1681,7 @@ cdef class Pygen(GiacMethods_base): property _subtype: def __get__(self): sig_on() - result=self.gptr.subtype + result = self.gptr.subtype sig_off() return result @@ -1787,9 +1782,9 @@ cdef vecteur _wrap_pylist(L) except +: cdef vecteur * V cdef int i - if (isinstance(L, tuple) or isinstance(L, listrange)): - n=len(L) - V=new vecteur() + if isinstance(L, (tuple, list, range)): + n = len(L) + V = new vecteur() sig_on() for i in range(n): @@ -1807,14 +1802,14 @@ cdef vecteur _getgiacslice(Pygen L,slice sl) except +: cdef vecteur * V cdef int u - if (L.type()=="DOM_LIST"): + if L.type()=="DOM_LIST": n=len(L) V=new vecteur() sig_on() # for u in range(n)[sl]: #pb python3 - (b,e,st)=sl.indices(n) - for u in range(b,e,st): + b, e, st = sl.indices(n) + for u in range(b, e, st): V.push_back((L.gptr[0])[u]) sig_off() return V[0] @@ -1862,7 +1857,7 @@ cdef gen pylongtogen(a) except +: #def giaceval(Pygen self): # cdef gen result # try: -# result=GIAC_protecteval(self.gptr[0],1,context_ptr) +# result = GIAC_protecteval(self.gptr[0],1,context_ptr) # return _wrap_gen(result) # except: # raise @@ -1872,7 +1867,7 @@ cdef gen pylongtogen(a) except +: # # cdef gen result # try: -# result=GIAC_factor(self.gptr[0],context_ptr) +# result = GIAC_factor(self.gptr[0],context_ptr) # return _wrap_gen(result) # except: # raise @@ -1882,7 +1877,7 @@ cdef gen pylongtogen(a) except +: #def giacfactors(Pygen self): # cdef gen result # try: -# result=GIAC_factors(self.gptr[0],context_ptr) +# result = GIAC_factors(self.gptr[0],context_ptr) # return _wrap_gen(result) # except: # raise @@ -1893,7 +1888,7 @@ cdef gen pylongtogen(a) except +: #def giacnormal(Pygen self): # cdef gen result # try: -# result=GIAC_normal(self.gptr[0],context_ptr) +# result = GIAC_normal(self.gptr[0],context_ptr) # return _wrap_gen(result) # except: # raise @@ -1902,8 +1897,8 @@ cdef gen pylongtogen(a) except +: #def giacgcd(Pygen a, Pygen b): # cdef gen result # try: -# result=gen( GIAC_makenewvecteur(a.gptr[0],b.gptr[0]) ,1) -# result=GIAC_gcd(result,context_ptr) +# result = gen( GIAC_makenewvecteur(a.gptr[0],b.gptr[0]) ,1) +# result = GIAC_gcd(result,context_ptr) # return _wrap_gen(result) # except: # raise @@ -1920,7 +1915,7 @@ class GiacFunction(Pygen): # a class to evaluate args before call """ A Subclass of Pygen to create functions with evaluating all the args - before call so that they are substitued by their value. + before call so that they are substituted by their value. EXAMPLES:: @@ -1983,11 +1978,12 @@ for i in moremethods: GiacMethods[i] = tmp for i in mostkeywords+moremethods: - GiacMethods[i].__doc__ = eval("Pygen."+i+".__doc__") + GiacMethods[i].__doc__ = eval("Pygen." + i + ".__doc__") # To avoid conflicts we export only these few ones. Most giac keywords will be -# avaible through: libgiac.keywordname -__all__=['Pygen','giacsettings','libgiac','loadgiacgen','GiacFunction','GiacMethods','GiacMethods_base'] +# available through: libgiac.keywordname +__all__ = ['Pygen', 'giacsettings', 'libgiac', 'loadgiacgen', 'GiacFunction', + 'GiacMethods', 'GiacMethods_base'] def loadgiacgen(str filename): @@ -2015,7 +2011,7 @@ def loadgiacgen(str filename): """ cdef gen result sig_on() - result=GIAC_unarchive( encstring23(filename), context_ptr) + result = GIAC_unarchive( encstring23(filename), context_ptr) sig_off() return _wrap_gen(result) diff --git a/src/sage/libs/giac/meson.build b/src/sage/libs/giac/meson.build new file mode 100644 index 00000000000..6dda5a6c8a7 --- /dev/null +++ b/src/sage/libs/giac/meson.build @@ -0,0 +1,17 @@ +giac = cc.find_library('giac', required: false, disabler: true) + +py.install_sources('__init__.py', 'giac.pxd', 'misc.h', subdir: 'sage/libs/giac') + +extension_data_cpp = {'giac': files('giac.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/giac', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_rings], + dependencies: [py_dep, cysignals, giac, gmp], + ) +endforeach diff --git a/src/sage/libs/glpk/meson.build b/src/sage/libs/glpk/meson.build new file mode 100644 index 00000000000..ee265cc1579 --- /dev/null +++ b/src/sage/libs/glpk/meson.build @@ -0,0 +1,9 @@ +py.install_sources( + '__init__.py', + 'constants.pxd', + 'env.pxd', + 'graph.pxd', + 'lp.pxd', + 'types.pxd', + subdir: 'sage/libs/glpk', +) diff --git a/src/sage/libs/gmp/meson.build b/src/sage/libs/gmp/meson.build new file mode 100644 index 00000000000..31fb4f3a5fa --- /dev/null +++ b/src/sage/libs/gmp/meson.build @@ -0,0 +1,29 @@ +py.install_sources( + '__init__.py', + 'all.pxd', + 'binop.pxd', + 'misc.pxd', + 'mpf.pxd', + 'mpn.pxd', + 'mpq.pxd', + 'mpz.pxd', + 'pylong.pxd', + 'random.pxd', + 'randomize.pxd', + 'types.pxd', + subdir: 'sage/libs/gmp', +) + +extension_data = {'pylong' : files('pylong.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/gmp', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/libs/gsl/meson.build b/src/sage/libs/gsl/meson.build new file mode 100644 index 00000000000..ea5eac5e93a --- /dev/null +++ b/src/sage/libs/gsl/meson.build @@ -0,0 +1,82 @@ +py.install_sources( + '__init__.py', + 'airy.pxd', + 'all.pxd', + 'array.pxd', + 'bessel.pxd', + 'blas.pxd', + 'blas_types.pxd', + 'block.pxd', + 'chebyshev.pxd', + 'clausen.pxd', + 'combination.pxd', + 'complex.pxd', + 'coulomb.pxd', + 'coupling.pxd', + 'dawson.pxd', + 'debye.pxd', + 'dilog.pxd', + 'eigen.pxd', + 'elementary.pxd', + 'ellint.pxd', + 'elljac.pxd', + 'erf.pxd', + 'errno.pxd', + 'exp.pxd', + 'expint.pxd', + 'fermi_dirac.pxd', + 'fft.pxd', + 'fit.pxd', + 'gamma.pxd', + 'gegenbauer.pxd', + 'histogram.pxd', + 'hyperg.pxd', + 'integration.pxd', + 'interp.pxd', + 'laguerre.pxd', + 'lambert.pxd', + 'legendre.pxd', + 'linalg.pxd', + 'log.pxd', + 'math.pxd', + 'matrix.pxd', + 'matrix_complex.pxd', + 'min.pxd', + 'monte.pxd', + 'ntuple.pxd', + 'odeiv.pxd', + 'permutation.pxd', + 'poly.pxd', + 'pow_int.pxd', + 'psi.pxd', + 'qrng.pxd', + 'random.pxd', + 'rng.pxd', + 'roots.pxd', + 'sort.pxd', + 'statistics.pxd', + 'sum.pxd', + 'synchrotron.pxd', + 'transport.pxd', + 'trig.pxd', + 'types.pxd', + 'vector.pxd', + 'vector_complex.pxd', + 'wavelet.pxd', + 'zeta.pxd', + subdir: 'sage/libs/gsl', +) + +extension_data = {'array' : files('array.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/gsl', + install: true, + include_directories: [inc_gsl], + dependencies: [py_dep, cysignals, gmp, gsl], + ) +endforeach + diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pyx b/src/sage/libs/lcalc/lcalc_Lfunction.pyx index c528b136f93..f1173543ab1 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pyx +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pyx @@ -398,7 +398,7 @@ cdef class Lfunction: result.clear() return returnvalue - # Needs to be overriden + # Needs to be overridden cdef void _init_fun(self, char *NAME, int what_type, dirichlet_coeff, long long Period, double q, c_Complex w, int A, double *g, c_Complex *l, int n_poles, c_Complex *p, c_Complex *r) noexcept: raise NotImplementedError @@ -491,7 +491,7 @@ cdef class Lfunction_I(Lfunction): cdef void _init_fun(self, char *NAME, int what_type, dirichlet_coeff, long long Period, double q, c_Complex w, int A, double *g, c_Complex *l, int n_poles, c_Complex *p, c_Complex *r) noexcept: cdef int N = len(dirichlet_coeff) cdef Integer tmpi - cdef int * coeffs = new_ints(N+1) #lcalc ignores 0the coefficient + cdef int * coeffs = new_ints(N+1) # lcalc ignores 0th coefficient for i from 0 <= i< N by 1: tmpi=Integer(dirichlet_coeff[i]) coeffs[i+1] = mpz_get_si(tmpi.value) @@ -628,9 +628,9 @@ cdef class Lfunction_D(Lfunction): cdef int i cdef RealNumber tmpr cdef int N = len(dirichlet_coeff) - cdef double * coeffs = new_doubles(N+1)#lcalc ignores 0th position - for i from 0 <= i< N by 1: - tmpr=RRR(dirichlet_coeff[i]) + cdef double * coeffs = new_doubles(N+1) # lcalc ignores 0th position + for i in range(N): + tmpr = RRR(dirichlet_coeff[i]) coeffs[i+1] = mpfr_get_d(tmpr.value, MPFR_RNDN) self.thisptr=new_c_Lfunction_D(NAME, what_type, N, coeffs, Period, q, w, A, g, l, n_poles, p, r) del_doubles(coeffs) diff --git a/src/sage/libs/lcalc/meson.build b/src/sage/libs/lcalc/meson.build new file mode 100644 index 00000000000..aa6d9296948 --- /dev/null +++ b/src/sage/libs/lcalc/meson.build @@ -0,0 +1,23 @@ +lcalc = dependency('lcalc', version: '>= 2.0.0') + +py.install_sources( + '__init__.py', + 'lcalc_Lfunction.pxd', + 'lcalc_sage.h', + subdir: 'sage/libs/lcalc', +) + +extension_data_cpp = {'lcalc_Lfunction': files('lcalc_Lfunction.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/lcalc', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cypari2, cysignals, gmp, lcalc, m, mpfr, ntl], + ) +endforeach + diff --git a/src/sage/libs/libecm.pyx b/src/sage/libs/libecm.pyx index 9d53d7284d4..6e0fc8668a5 100644 --- a/src/sage/libs/libecm.pyx +++ b/src/sage/libs/libecm.pyx @@ -169,7 +169,7 @@ def ecmfactor(number, double B1, verbose=False, sigma=0): sage_int_sigma = Integer(sigma) if number <= 1: - raise ValueError("Input number (%s) must be greater than 1"%number) + raise ValueError("Input number (%s) must be greater than 1" % number) if verbose: print("Performing one curve with B1=%1.0f" % B1) diff --git a/src/sage/libs/linbox/fflas.pxd b/src/sage/libs/linbox/fflas.pxd index d5b077cf045..886f5c44cfa 100644 --- a/src/sage/libs/linbox/fflas.pxd +++ b/src/sage/libs/linbox/fflas.pxd @@ -28,7 +28,6 @@ cdef extern from "fflas-ffpack/fflas-ffpack.h" namespace "FFLAS": FflasNoTrans FflasTrans - ctypedef enum FFLAS_SIDE: FflasRight diff --git a/src/sage/libs/linbox/meson.build b/src/sage/libs/linbox/meson.build new file mode 100644 index 00000000000..252a6ae0f9e --- /dev/null +++ b/src/sage/libs/linbox/meson.build @@ -0,0 +1,26 @@ +py.install_sources( + '__init__.py', + 'conversion.pxd', + 'fflas.pxd', + 'givaro.pxd', + 'linbox.pxd', + 'linbox_flint_interface.pxd', + subdir: 'sage/libs/linbox', +) + +extension_data_cpp = { + 'linbox_flint_interface': files('linbox_flint_interface.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/linbox', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_flint], + dependencies: [py_dep, blas, flint, fplll, givaro, gmp, gmpxx, linbox], + ) +endforeach + diff --git a/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi index f019a1832be..69cde5e4074 100644 --- a/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi +++ b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi @@ -260,7 +260,7 @@ cdef inline bint cisunit(celement a, PowComputer_ prime_pow) except -1: cdef inline int cshift(celement out, celement rem, celement a, long n, long prec, PowComputer_ prime_pow, bint reduce_afterward) except -1: """ - Mulitplies by a power of the uniformizer. + Multiplies by a power of the uniformizer. INPUT: @@ -291,7 +291,7 @@ cdef inline int cshift(celement out, celement rem, celement a, long n, long prec cdef inline int cshift_notrunc(celement out, celement a, long n, long prec, PowComputer_ prime_pow, bint reduce_afterward) except -1: """ - Mulitplies by a power of the uniformizer, assuming that the + Multiplies by a power of the uniformizer, assuming that the valuation of a is at least -n. INPUT: diff --git a/src/sage/libs/m4ri.pxd b/src/sage/libs/m4ri.pxd index a9c6c792c05..7fbdd35b856 100644 --- a/src/sage/libs/m4ri.pxd +++ b/src/sage/libs/m4ri.pxd @@ -19,7 +19,7 @@ cdef extern from "m4ri/m4ri.h": cdef int m4ri_radix ############## - # Maintainance + # Maintenance ############## # builds all gray codes up to a certain size diff --git a/src/sage/libs/m4rie.pxd b/src/sage/libs/m4rie.pxd index 51bbf8303d9..c56e33b6945 100644 --- a/src/sage/libs/m4rie.pxd +++ b/src/sage/libs/m4rie.pxd @@ -173,7 +173,6 @@ cdef extern from "m4rie/m4rie.h": void mzd_slice_row_add(mzd_slice_t *A, size_t sourcerow, size_t destrow) - void mzd_slice_row_clear_offset(mzd_slice_t *A, size_t row, size_t coloffset) void mzd_slice_print(mzd_slice_t *A) diff --git a/src/sage/libs/meataxe.pxd b/src/sage/libs/meataxe.pxd index 0a928e19c37..a3bbc810cdc 100644 --- a/src/sage/libs/meataxe.pxd +++ b/src/sage/libs/meataxe.pxd @@ -113,7 +113,6 @@ cdef extern from "meataxe.h": Matrix_t *MatLoad(char *fn) except? NULL int MatSave(Matrix_t *mat, char *fn) except -1 - ## Basic Arithmetic ## general rule: dest is changed, src/mat are unchanged! Matrix_t *MatTransposed(Matrix_t *src) except NULL Matrix_t *MatAdd(Matrix_t *dest, Matrix_t *src) except NULL diff --git a/src/sage/libs/meson.build b/src/sage/libs/meson.build new file mode 100644 index 00000000000..61b36da51f5 --- /dev/null +++ b/src/sage/libs/meson.build @@ -0,0 +1,86 @@ +sirocco = cc.find_library('sirocco', required: false, disabler: true) +# cannot be found via pkg-config +ecl = cc.find_library('ecl') +braiding = cc.find_library('braiding') +gc = cc.find_library('gc') +homfly = cc.find_library('homfly', has_headers: ['homfly.h']) + +py.install_sources( + 'all.py', + 'all__sagemath_coxeter3.py', + 'all__sagemath_meataxe.py', + 'all__sagemath_objects.py', + 'all__sagemath_sirocco.py', + 'ecl.pxd', + 'eclsig.h', + 'gmpxx.pxd', + 'iml.pxd', + 'm4ri.pxd', + 'm4rie.pxd', + 'meataxe.pxd', + subdir: 'sage/libs', +) + +extension_data = { + 'ecl' : files('ecl.pyx'), + 'homfly' : files('homfly.pyx'), + 'libecm' : files('libecm.pyx'), + 'sirocco': files('sirocco.pyx'), + 'meataxe': files('meataxe.pyx'), +} + +dependencies = [py_dep, braiding, cysignals, ecl, ecm, gc, gmp, homfly] + +foreach name, pyx : extension_data + deps = dependencies + if name == 'sirocco' + deps += [sirocco] + elif name == 'meataxe' + deps += [mtx, meataxe] + endif + + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: deps, + ) +endforeach + +extension_data_cpp = {'braiding': files('braiding.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_rings], + dependencies: dependencies, + ) +endforeach + +subdir('arb') +subdir('coxeter3') +install_subdir('cremona', install_dir: sage_install_dir / 'libs') +subdir('eclib') +subdir('flint') +subdir('gap') +subdir('giac') +subdir('glpk') +subdir('gmp') +subdir('gsl') +subdir('lcalc') +subdir('linbox') +install_subdir('linkages', install_dir: sage_install_dir / 'libs') +install_subdir('lrcalc', install_dir: sage_install_dir / 'libs') +install_subdir('mpfr', install_dir: sage_install_dir / 'libs') +subdir('mpmath') +install_subdir('mwrank', install_dir: sage_install_dir / 'libs') +subdir('ntl') +subdir('pari') +subdir('singular') +subdir('symmetrica') diff --git a/src/sage/libs/mpmath/ext_impl.pyx b/src/sage/libs/mpmath/ext_impl.pyx index 97f135d7219..fdbbfa80a3a 100644 --- a/src/sage/libs/mpmath/ext_impl.pyx +++ b/src/sage/libs/mpmath/ext_impl.pyx @@ -1261,7 +1261,7 @@ cdef MPF_complex_sqrt(MPF *c, MPF *d, MPF *a, MPF *b, MPopts opts): bneg = MPF_sgn(b) <= 0 if apos: # real part - MPF_hypot(&t, a, b, wpopts) #t = abs(a+bi) + a + MPF_hypot(&t, a, b, wpopts) # t = abs(a+bi) + a MPF_add(&t, &t, a, wpopts) MPF_set(&u, &t) mpz_sub_ui(u.exp, u.exp, 1) # u = t / 2 @@ -1276,10 +1276,10 @@ cdef MPF_complex_sqrt(MPF *c, MPF *d, MPF *a, MPF *b, MPopts opts): MPF_sub(&t, &t, a, wpopts) MPF_set(&u, &t) mpz_sub_ui(u.exp, u.exp, 1) # u = t / 2 - MPF_sqrt(d, &u, opts) # im = sqrt(u) + MPF_sqrt(d, &u, opts) # im = sqrt(u) mpz_add_ui(t.exp, t.exp, 1) # t = 2*t MPF_sqrt(&u, &t, wpopts) # u = sqrt(t) - MPF_div(c, &v, &u, opts) # re = b / u + MPF_div(c, &v, &u, opts) # re = b / u if bneg: MPF_neg(c, c) MPF_neg(d, d) diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 96d5e9ff828..96111f7b942 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -386,7 +386,7 @@ cdef MPopts global_opts global_context = None cdef class Context: - cdef public mpf, mpc, constant #, def_mp_function + cdef public mpf, mpc, constant # , def_mp_function cdef public trap_complex cdef public pretty @@ -983,7 +983,6 @@ cdef class Context: TESTS:: sage: from mpmath import mp - sage: mp.pretty = True sage: (x, T) = mp._convert_param(3) sage: (x, type(x).__name__, T) (3, 'int', 'Z') @@ -991,11 +990,11 @@ cdef class Context: sage: (x, type(x).__name__, T) (mpq(5,2), 'mpq', 'Q') sage: (x, T) = mp._convert_param(2.3) - sage: (x, type(x).__name__, T) - (2.3, 'mpf', 'R') + sage: (str(x), type(x).__name__, T) + ('2.3', 'mpf', 'R') sage: (x, T) = mp._convert_param(2+3j) sage: (x, type(x).__name__, T) - ((2.0 + 3.0j), 'mpc', 'C') + (mpc(real='2.0', imag='3.0'), 'mpc', 'C') sage: mp.pretty = False """ cdef MPF v @@ -1056,7 +1055,6 @@ cdef class Context: TESTS:: sage: from mpmath import * - sage: mp.pretty = True sage: mag(10), mag(10.0), mag(mpf(10)), int(ceil(log(10,2))) (4, 4, 4, 4) sage: mag(10j), mag(10+10j) @@ -1064,7 +1062,7 @@ cdef class Context: sage: mag(0.01), int(ceil(log(0.01,2))) (-6, -6) sage: mag(0), mag(inf), mag(-inf), mag(nan) - (-inf, +inf, +inf, nan) + (mpf('-inf'), mpf('+inf'), mpf('+inf'), mpf('nan')) :: @@ -2203,12 +2201,13 @@ cdef class constant(mpf_base): Represent ``self`` as a string. With mp.pretty=False, the representation differs from that of an ordinary mpf:: - sage: from mpmath import mp, pi - sage: mp.pretty = True - sage: repr(pi) + sage: from mpmath import mp + sage: mp2 = mp.clone() + sage: mp2.pretty = True + sage: repr(mp2.pi) '3.14159265358979' - sage: mp.pretty = False - sage: repr(pi) + sage: mp2.pretty = False + sage: repr(mp2.pi) '' """ if global_context.pretty: @@ -2374,11 +2373,12 @@ cdef class mpc(mpnumber): TESTS:: sage: from mpmath import mp - sage: mp.pretty = True - sage: repr(mp.mpc(2,3)) + sage: mp2 = mp.clone() + sage: mp2.pretty = True + sage: repr(mp2.mpc(2,3)) '(2.0 + 3.0j)' - sage: mp.pretty = False - sage: repr(mp.mpc(2,3)) + sage: mp2.pretty = False + sage: repr(mp2.mpc(2,3)) "mpc(real='2.0', imag='3.0')" """ if global_context.pretty: diff --git a/src/sage/libs/mpmath/meson.build b/src/sage/libs/mpmath/meson.build new file mode 100644 index 00000000000..4659da1563a --- /dev/null +++ b/src/sage/libs/mpmath/meson.build @@ -0,0 +1,27 @@ +py.install_sources( + '__init__.py', + 'all.py', + 'ext_impl.pxd', + 'ext_main.pxd', + 'utils.pxd', + subdir: 'sage/libs/mpmath', +) + +extension_data = { + 'ext_impl' : files('ext_impl.pyx'), + 'ext_libmp' : files('ext_libmp.pyx'), + 'ext_main' : files('ext_main.pyx'), + 'utils' : files('utils.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/mpmath', + install: true, + include_directories: [inc_cpython, inc_ext, inc_rings], + dependencies: [py_dep, cypari2, cysignals, gmp, mpfr], + ) +endforeach + diff --git a/src/sage/libs/ntl/GF2X.pxd b/src/sage/libs/ntl/GF2X.pxd index 9342f63244c..2d3b5edafad 100644 --- a/src/sage/libs/ntl/GF2X.pxd +++ b/src/sage/libs/ntl/GF2X.pxd @@ -58,7 +58,6 @@ cdef extern from "ntlwrap.h": void GF2XModulus_build "build"(GF2XModulus_c F, GF2X_c f) # MUST be called before using the modulus long GF2XModulus_deg "deg"(GF2XModulus_c F) - GF2X_c GF2XModulus_GF2X "GF2X" (GF2XModulus_c m) GF2X_c GF2X_IrredPolyMod "IrredPolyMod" (GF2X_c g, GF2XModulus_c F) @@ -72,7 +71,6 @@ cdef extern from "ntlwrap.h": void GF2X_PowerXPlusAMod_pre "PowerXPlusAMod"(GF2X_c x, GF2_c a, GF2_c e, GF2XModulus_c F) void GF2X_PowerXPlusAMod_long_pre "PowerXPlusAMod"(GF2X_c x, GF2_c a, long e, GF2XModulus_c F) - # x = g(h) mod f; deg(h) < n void GF2X_CompMod "CompMod"(GF2X_c x, GF2X_c g, GF2X_c h, GF2XModulus_c F) # xi = gi(h) mod f (i=1,2), deg(h) < n. diff --git a/src/sage/libs/ntl/meson.build b/src/sage/libs/ntl/meson.build new file mode 100644 index 00000000000..7f78db280fe --- /dev/null +++ b/src/sage/libs/ntl/meson.build @@ -0,0 +1,90 @@ +py.install_sources( + 'GF2.pxd', + 'GF2E.pxd', + 'GF2EX.pxd', + 'GF2X.pxd', + 'ZZ.pxd', + 'ZZX.pxd', + 'ZZ_p.pxd', + 'ZZ_pE.pxd', + 'ZZ_pEX.pxd', + 'ZZ_pX.pxd', + '__init__.py', + 'all.py', + 'conversion.pxd', + 'convert.pxd', + 'lzz_p.pxd', + 'lzz_pX.pxd', + 'mat_GF2.pxd', + 'mat_GF2E.pxd', + 'mat_ZZ.pxd', + 'ntl_GF2.pxd', + 'ntl_GF2E.pxd', + 'ntl_GF2EContext.pxd', + 'ntl_GF2EX.pxd', + 'ntl_GF2X.pxd', + 'ntl_ZZ.pxd', + 'ntl_ZZX.pxd', + 'ntl_ZZ_p.pxd', + 'ntl_ZZ_pContext.pxd', + 'ntl_ZZ_pE.pxd', + 'ntl_ZZ_pEContext.pxd', + 'ntl_ZZ_pEX.pxd', + 'ntl_ZZ_pX.pxd', + 'ntl_lzz_p.pxd', + 'ntl_lzz_pContext.pxd', + 'ntl_lzz_pX.pxd', + 'ntl_mat_GF2.pxd', + 'ntl_mat_GF2E.pxd', + 'ntl_mat_ZZ.pxd', + 'ntl_tools.pxd', + 'ntlwrap.h', + 'ntlwrap_impl.h', + 'types.pxd', + 'vec_GF2.pxd', + 'vec_GF2E.pxd', + subdir: 'sage/libs/ntl', +) + +extension_data_cpp = { + 'convert': files('convert.pyx'), + 'error': files('error.pyx'), + 'ntl_GF2': files('ntl_GF2.pyx'), + 'ntl_GF2E': files('ntl_GF2E.pyx'), + 'ntl_GF2EContext': files('ntl_GF2EContext.pyx'), + 'ntl_GF2EX': files('ntl_GF2EX.pyx'), + 'ntl_GF2X': files('ntl_GF2X.pyx'), + 'ntl_ZZ': files('ntl_ZZ.pyx'), + 'ntl_ZZX': files('ntl_ZZX.pyx'), + 'ntl_ZZ_p': files('ntl_ZZ_p.pyx'), + 'ntl_ZZ_pContext': files('ntl_ZZ_pContext.pyx'), + 'ntl_ZZ_pE': files('ntl_ZZ_pE.pyx'), + 'ntl_ZZ_pEContext': files('ntl_ZZ_pEContext.pyx'), + 'ntl_ZZ_pEX': files('ntl_ZZ_pEX.pyx'), + 'ntl_ZZ_pX': files('ntl_ZZ_pX.pyx'), + 'ntl_lzz_p': files('ntl_lzz_p.pyx'), + 'ntl_lzz_pContext': files('ntl_lzz_pContext.pyx'), + 'ntl_lzz_pX': files('ntl_lzz_pX.pyx'), + 'ntl_mat_GF2': files('ntl_mat_GF2.pyx'), + 'ntl_mat_GF2E': files('ntl_mat_GF2E.pyx'), + 'ntl_mat_ZZ': files('ntl_mat_ZZ.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/ntl', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cysignals, gmp, m, ntl], + ) +endforeach + diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 4082db003b4..f7e69c52128 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -957,7 +957,7 @@ cdef class ntl_ZZ_pX(): sage: f.derivative() [7 0 19] """ - cdef ntl_ZZ_pX r = self._new() #restores context + cdef ntl_ZZ_pX r = self._new() # restores context sig_on() ZZ_pX_diff(r.x, self.x) sig_off() diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index 2eee4fe34f2..ae33d4fbb49 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -409,7 +409,7 @@ cdef class ntl_mat_GF2(): [0 0 0 0 0 0 0 0 0 0] ] - ``Abar`` is in row echolon form now:: + ``Abar`` is in row echelon form now:: sage: first_nonzero_indices = [Abar._sage_().row(i).nonzero_positions()[0] for i in range(A.rank())] sage: all(first_nonzero_indices[i] < first_nonzero_indices[i+1] for i in range(A.rank()-1)) @@ -573,7 +573,7 @@ cdef class ntl_mat_GF2(): sage: A_image.row_space() == Abar_image.row_space() True - X is in row echolon form:: + X is in row echelon form:: sage: first_nonzero_indices = [row.nonzero_positions()[0] for row in Abar_image.rows()] sage: all(first_nonzero_indices[i] < first_nonzero_indices[i+1] for i in range(Abar_image.nrows() - 1)) diff --git a/src/sage/libs/ntl/ntl_mat_ZZ.pyx b/src/sage/libs/ntl/ntl_mat_ZZ.pyx index 69340c00f31..310bbb0006c 100644 --- a/src/sage/libs/ntl/ntl_mat_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_mat_ZZ.pyx @@ -1271,7 +1271,7 @@ cdef class ntl_mat_ZZ(): rank = int(mat_ZZ_LLL(&det2,&self.x,int(a),int(b),int(verbose))) return rank, make_ZZ_sig_off(det2) - def LLL_FP(self, delta=0.75 , return_U=False, verbose=False): + def LLL_FP(self, delta=0.75, return_U=False, verbose=False): r""" Perform approximate LLL reduction of \code{self} (puts \code{self} in an LLL form) subject to the following diff --git a/src/sage/libs/pari/convert_sage.pyx b/src/sage/libs/pari/convert_sage.pyx index 64386bcf632..e26238d7c38 100644 --- a/src/sage/libs/pari/convert_sage.pyx +++ b/src/sage/libs/pari/convert_sage.pyx @@ -379,7 +379,7 @@ cpdef set_integer_from_gen(Integer self, Gen x): sig_on() x = new_gen(FF_to_FpXQ_i((x).g)) else: - raise TypeError("Unable to coerce PARI %s to an Integer"%x) + raise TypeError("Unable to coerce PARI %s to an Integer" % x) # Now we have a true PARI integer, convert it to Sage INT_to_mpz(self.value, (x).g) diff --git a/src/sage/libs/pari/meson.build b/src/sage/libs/pari/meson.build new file mode 100644 index 00000000000..5952060267c --- /dev/null +++ b/src/sage/libs/pari/meson.build @@ -0,0 +1,36 @@ +py.install_sources( + '__init__.py', + 'all.py', + 'convert_flint.pxd', + 'convert_gmp.pxd', + 'convert_sage.pxd', + 'convert_sage_complex_double.pxd', + 'convert_sage_real_double.pxd', + 'convert_sage_real_mpfr.pxd', + 'misc.pxd', + 'tests.py', + subdir: 'sage/libs/pari', +) + +extension_data = { + 'convert_flint' : files('convert_flint.pyx'), + 'convert_gmp' : files('convert_gmp.pyx'), + 'convert_sage' : files('convert_sage.pyx'), + 'convert_sage_complex_double' : files('convert_sage_complex_double.pyx'), + 'convert_sage_matrix' : files('convert_sage_matrix.pyx'), + 'convert_sage_real_double' : files('convert_sage_real_double.pyx'), + 'convert_sage_real_mpfr' : files('convert_sage_real_mpfr.pyx'), + 'misc' : files('misc.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/pari', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_gsl, inc_rings], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, gsl, mpfr, pari], + ) +endforeach + diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 0c9f2cc993c..dddc452fd98 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -151,7 +151,6 @@ cdef extern from "singular/Singular/libsingular.h": void (*cfWrite)(number* a, const n_Procs_s* r) void (*cfNormalize)(number* a, const n_Procs_s* r) - bint (*cfDivBy)(number* a, number* b, const n_Procs_s* r) bint (*cfEqual)(number* a,number* b, const n_Procs_s* ) bint (*cfIsZero)(number* a, const n_Procs_s* ) # algebraic number comparison with zero @@ -160,7 +159,6 @@ cdef extern from "singular/Singular/libsingular.h": bint (*cfGreaterZero)(number* a, const n_Procs_s* ) void (*cfPower)(number* a, int i, number* * result, const n_Procs_s* r) # algebraic number power - ring *extRing int ch mpz_ptr modBase @@ -211,7 +209,6 @@ cdef extern from "singular/Singular/libsingular.h": int pCompIndex # index of components unsigned long bitmask # mask for getting single exponents - n_Procs_s* cf # coefficient field/ring int ref @@ -793,7 +790,6 @@ cdef extern from "singular/Singular/libsingular.h": number *nlCopy(number *) - # number to integer handle long SR_TO_INT(number *) @@ -813,7 +809,6 @@ cdef extern from "singular/Singular/libsingular.h": void id_Delete(ideal **, ring *) - # lifting ideal *idLift(ideal *mod, ideal *submod, ideal **rest, int goodShape, int isSB, int divide) diff --git a/src/sage/libs/singular/function_factory.py b/src/sage/libs/singular/function_factory.py index c4b0b52372f..939b52fab5b 100644 --- a/src/sage/libs/singular/function_factory.py +++ b/src/sage/libs/singular/function_factory.py @@ -15,7 +15,7 @@ from sage.libs.singular.function import singular_function, lib, list_of_functions -class SingularFunctionFactory(): +class SingularFunctionFactory: """ A convenient interface to libsingular functions. """ diff --git a/src/sage/libs/singular/meson.build b/src/sage/libs/singular/meson.build new file mode 100644 index 00000000000..52ece586caa --- /dev/null +++ b/src/sage/libs/singular/meson.build @@ -0,0 +1,34 @@ +py.install_sources( + '__init__.py', + 'decl.pxd', + 'function.pxd', + 'function_factory.py', + 'groebner_strategy.pxd', + 'polynomial.pxd', + 'ring.pxd', + 'singular.pxd', + 'standard_options.py', + subdir: 'sage/libs/singular', +) + +extension_data_cpp = { + 'function': files('function.pyx'), + 'groebner_strategy': files('groebner_strategy.pyx'), + 'option': files('option.pyx'), + 'polynomial': files('polynomial.pyx'), + 'ring': files('ring.pyx'), + 'singular': files('singular.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/singular', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ntl, inc_rings, inc_rings_finite], + dependencies: [py_dep, cysignals, givaro, gmp, singular], + ) +endforeach + diff --git a/src/sage/libs/singular/option.pyx b/src/sage/libs/singular/option.pyx index e729255e2d9..3855fb2f732 100644 --- a/src/sage/libs/singular/option.pyx +++ b/src/sage/libs/singular/option.pyx @@ -20,15 +20,17 @@ By default, tail reductions are performed:: sage: from sage.libs.singular.option import opt, opt_ctx sage: opt['red_tail'] True - sage: std(I)[-1] + sage: red = std(I)[-1]; red d^2*e^6 + 28*b*c*d + ... If we don't want this, we can create an option context, which disables this:: sage: with opt_ctx(red_tail=False, red_sb=False): - ....: std(I)[-1] - d^2*e^6 + 8*c^3 + ... + ....: notred = std(I)[-1]; notred + d^2*e^6 + ... + sage: len(list(red)) < len(list(notred)) + True However, this does not affect the global state:: diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index e9ce19c6553..6f7f576cb02 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -467,7 +467,7 @@ cdef object singular_polynomial_latex(poly *p, ring *r, object base, object late sage: latex(10*x^2 + 1/2*y) 10 x^{2} + \frac{1}{2} y - Demonstrate that coefficients over non-atomic representated rings are + Demonstrate that coefficients over non-atomic represented rings are properly parenthesized (:issue:`11186`):: sage: x = var('x') diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index ef91e60a2a4..f770cc483a5 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -293,12 +293,14 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: if isinstance(base_ring, RationalField): characteristic = 0 - _ring = rDefault( characteristic ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault(characteristic, nvars, _names, nblcks, + _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, FractionField_generic) and isinstance(base_ring.base(), (MPolynomialRing_libsingular, PolynomialRing_field)) and isinstance(base_ring.base().base_ring(), RationalField): characteristic = 1 k = PolynomialRing(RationalField(), - names=base_ring.variable_names(), order='lex', implementation='singular') + names=base_ring.variable_names(), order='lex', + implementation='singular') ngens = len(k.gens()) @@ -317,7 +319,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: if (_cf is NULL): raise RuntimeError("Failed to allocate _cf ring.") - _ring = rDefault (_cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault (_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, FractionField_generic) and isinstance(base_ring.base(), (MPolynomialRing_libsingular, PolynomialRing_field)) and isinstance(base_ring.base().base_ring(), FiniteField_generic): if not base_ring.base_ring().is_prime_field(): @@ -343,7 +345,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: if (_cf is NULL): raise RuntimeError("Failed to allocate _cf ring.") - _ring = rDefault (_cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault (_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, NumberField) and base_ring.is_absolute(): characteristic = 1 @@ -369,11 +371,11 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: if (_cf is NULL): raise RuntimeError("Failed to allocate _cf ring.") - _ring = rDefault (_cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault (_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, IntegerRing_class): _cf = nInitChar( n_Z, NULL) # integer coefficient ring - _ring = rDefault (_cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault (_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif (isinstance(base_ring, FiniteField_generic) and base_ring.is_prime_field()): if base_ring.characteristic() <= 2147483647: @@ -384,7 +386,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: # example for simpler ring creation interface without monomial orderings: #_ring = rDefault(characteristic, nvars, _names) - _ring = rDefault( characteristic , nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault(characteristic, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, FiniteField_generic): if base_ring.characteristic() <= 2147483647: @@ -418,7 +420,7 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: if (_cf is NULL): raise RuntimeError("Failed to allocate _cf ring.") - _ring = rDefault (_cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault (_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) elif isinstance(base_ring, sage.rings.abc.IntegerModRing): @@ -472,12 +474,12 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: mpz_init_set_ui(_info.base, characteristic) _info.exp = 1 _cf = nInitChar( n_Zn, &_info ) - _ring = rDefault( _cf ,nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) + _ring = rDefault(_cf, nvars, _names, nblcks, _order, _block0, _block1, _wvhdl) else: raise NotImplementedError(f"polynomials over {base_ring} are not supported in Singular") - if (_ring is NULL): + if _ring is NULL: raise ValueError("Failed to allocate Singular ring.") _ring.ShortOut = 0 diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 6a848a405e0..9c7b4078583 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1188,8 +1188,8 @@ cdef number *sa2si_transext_QQ(object elem, ring *_ring) noexcept: if nMapFuncPtr is NULL: raise RuntimeError("Failed to determine nMapFuncPtr") - numerdic = elem.numerator().dict() - denomdic = elem.denominator().dict() + numerdic = elem.numerator().monomial_coefficients() + denomdic = elem.denominator().monomial_coefficients() if numerdic and not isinstance(list(numerdic)[0], (tuple, ETuple)): numerdic = {(k,):b for k,b in numerdic.items()} @@ -1303,8 +1303,8 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring) noexcept: if nMapFuncPtr is NULL: raise RuntimeError("Failed to determine nMapFuncPtr") - numerdic = elem.numerator().dict() - denomdic = elem.denominator().dict() + numerdic = elem.numerator().monomial_coefficients() + denomdic = elem.denominator().monomial_coefficients() if numerdic and not isinstance(list(numerdic)[0], (tuple, ETuple)): numerdic = {(k,):b for k,b in numerdic.items()} @@ -1321,11 +1321,11 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring) noexcept: a = _ring.cf.cfParameter(j+1, _ring.cf) for k in range(ex): aux1 = naCoeff - naCoeff = _ring.cf.cfMult(aux1, a ,_ring.cf) + naCoeff = _ring.cf.cfMult(aux1, a, _ring.cf) _ring.cf.cfDelete(&aux1, _ring.cf) _ring.cf.cfDelete(&a, _ring.cf) aux2 = numerator - numerator = _ring.cf.cfAdd(aux2, naCoeff,_ring.cf) + numerator = _ring.cf.cfAdd(aux2, naCoeff, _ring.cf) _ring.cf.cfDelete(&naCoeff, _ring.cf) _ring.cf.cfDelete(&aux2, _ring.cf) @@ -1338,7 +1338,7 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring) noexcept: a = _ring.cf.cfParameter(j+1, _ring.cf) for k in range(ex): aux1 = naCoeff - naCoeff = _ring.cf.cfMult(aux1, a ,_ring.cf) + naCoeff = _ring.cf.cfMult(aux1, a, _ring.cf) _ring.cf.cfDelete(&aux1, _ring.cf) _ring.cf.cfDelete(&a, _ring.cf) aux2 = denominator @@ -1425,11 +1425,11 @@ cdef number *sa2si_NF(object elem, ring *_ring) noexcept: rComplete(qqr,1) qqr.ShortOut = 0 - nMapFuncPtr = naSetMap( qqr.cf , _ring.cf ) # choose correct mapping function + nMapFuncPtr = naSetMap(qqr.cf, _ring.cf) # choose correct mapping function cdef poly *_p for i from 0 <= i < len(elem): nlCoeff = nlInit2gmp( mpq_numref((elem[i]).value), mpq_denref((elem[i]).value), qqr.cf ) - naCoeff = nMapFuncPtr(nlCoeff, qqr.cf , _ring.cf ) + naCoeff = nMapFuncPtr(nlCoeff, qqr.cf, _ring.cf) nlDelete(&nlCoeff, _ring.cf) # faster would be to assign the coefficient directly @@ -1548,9 +1548,9 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring) noexcept: _name = omStrDup("a") _ext_names = omAlloc0(sizeof(char*)) _ext_names[0] = omStrDup(_name) - _cf = nInitChar( n_Z, NULL) # integer coefficient ring - ZZr = rDefault (_cf ,1, _ext_names) - rComplete(ZZr,1) + _cf = nInitChar(n_Z, NULL) # integer coefficient ring + ZZr = rDefault (_cf, 1, _ext_names) + rComplete(ZZr, 1) ZZr.ShortOut = 0 nn = nrzInit(0, ZZr.cf) diff --git a/src/sage/libs/symmetrica/meson.build b/src/sage/libs/symmetrica/meson.build new file mode 100644 index 00000000000..9294ebe3b03 --- /dev/null +++ b/src/sage/libs/symmetrica/meson.build @@ -0,0 +1,18 @@ +# Cannot be found by pkg-config +symmetrica = cc.find_library('symmetrica') + +py.install_sources('__init__.py', 'all.py', subdir: 'sage/libs/symmetrica') + +extension_data = {'symmetrica' : files('symmetrica.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/libs/symmetrica', + install: true, + include_directories: [], + dependencies: [py_dep, cysignals, gmp, symmetrica], + ) +endforeach + diff --git a/src/sage/libs/symmetrica/sb.pxi b/src/sage/libs/symmetrica/sb.pxi index 5ea0b3ad62c..b3bbb061596 100644 --- a/src/sage/libs/symmetrica/sb.pxi +++ b/src/sage/libs/symmetrica/sb.pxi @@ -51,7 +51,6 @@ def mult_schubert_schubert_symmetrica(a, b): freeall(cres) raise err - sig_on() mult_schubert_schubert(ca, cb, cres) sig_off() diff --git a/src/sage/libs/symmetrica/schur.pxi b/src/sage/libs/symmetrica/schur.pxi index a5257cf304b..4fe867ce93a 100644 --- a/src/sage/libs/symmetrica/schur.pxi +++ b/src/sage/libs/symmetrica/schur.pxi @@ -28,7 +28,6 @@ cdef extern from 'symmetrica/def.h': INT t_HOMSYM_MONOMIAL(OP a, OP b) INT t_HOMSYM_ELMSYM(OP a, OP b) - INT t_POWSYM_SCHUR(OP a, OP b) INT t_SCHUR_POWSYM(OP a, OP b) INT t_POWSYM_HOMSYM(OP a, OP b) diff --git a/src/sage/libs/symmetrica/symmetrica.pxi b/src/sage/libs/symmetrica/symmetrica.pxi index 0a147d6c743..def9544e2c6 100644 --- a/src/sage/libs/symmetrica/symmetrica.pxi +++ b/src/sage/libs/symmetrica/symmetrica.pxi @@ -941,7 +941,7 @@ cdef void* _op_elmsym(object d, OP res) noexcept: #Elementary symmetric function pointer = s_s_n(pointer) -cdef object _py_homsym(OP a): #Homogenous symmetric functions +cdef object _py_homsym(OP a): # Homogeneous symmetric functions late_import() z_elt = _py_schur_general(a) if len(z_elt) == 0: @@ -954,7 +954,7 @@ cdef object _py_homsym(OP a): #Homogenous symmetric functions z._monomial_coefficients = z_elt return z -cdef void* _op_homsym(object d, OP res) noexcept: #Homogenous symmetric functions +cdef void* _op_homsym(object d, OP res) noexcept: # Homogeneous symmetric functions cdef OP pointer = res _op_schur_general(d, res) while pointer != NULL: diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index 817132fb8f8..d22c0443461 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -565,15 +565,12 @@ def truthtable(self, start=0, end=-1): exponential time function requiring `O(2^n)` time, where `n` is the number of variables in the expression. """ - max = 2 ** len(self.__vars_order) + maximum = 2 ** len(self.__vars_order) if end < 0: - end = max - if end > max: - end = max - if start < 0: - start = 0 - if start > max: - start = max + end = maximum + end = min(end, maximum) + start = max(start, 0) + start = min(start, maximum) keys, table = [], [] vars = {} for var in self.__vars_order: diff --git a/src/sage/manifolds/calculus_method.py b/src/sage/manifolds/calculus_method.py index e4a217bbbab..a4888559c0f 100644 --- a/src/sage/manifolds/calculus_method.py +++ b/src/sage/manifolds/calculus_method.py @@ -172,7 +172,7 @@ class CalculusMethod(SageObject): """ _default = 'SR' # default calculus method _methods = ('SR', 'sympy') # implemented methods - _tranf = {'SR': _Sympy_to_SR, 'sympy': _SR_to_Sympy} # translators + _tranf = {'SR': _Sympy_to_SR, 'sympy': _SR_to_Sympy} # translators def __init__(self, current=None, base_field_type='real'): r""" diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index 0cbd69b6768..c3c40c42b9a 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -93,6 +93,7 @@ def Minkowski(positive_spacelike=True, names=None): g[1,1], g[2,2], g[3,3] = sgn, sgn, sgn return M + def Kerr(m=1, a=0, coordinates='BL', names=None): """ Generate a Kerr spacetime. @@ -217,6 +218,7 @@ def Kerr(m=1, a=0, coordinates='BL', names=None): raise NotImplementedError("coordinates system not implemented, see help" " for details") + def Torus(R=2, r=1, names=None): """ Generate a 2-dimensional torus embedded in Euclidean space. diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 86c3087d101..d0d3148be7e 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -3270,6 +3270,7 @@ def _plot_xx_list(xx_list, rem_coords, ranges, steps, number_values): # ***************************************************************************** + class CoordChange(SageObject): r""" Transition map between two charts of a topological manifold. diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index fed078a2baa..aae7a2b1fff 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -2613,6 +2613,7 @@ def collect_common_factors(self): self._del_derived() return self + class ChartFunctionRing(Parent, UniqueRepresentation): """ Ring of all chart functions on a chart. diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index f715d2e5738..2c30b796aab 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -34,6 +34,7 @@ from sage.categories.homset import Hom from sage.categories.morphism import Morphism + class ContinuousMap(Morphism): r""" Continuous map between two topological manifolds. @@ -1062,7 +1063,7 @@ def display(self, chart1=None, chart2=None): EXAMPLES: - A simple reparamentrization:: + A simple reparametrization:: sage: R. = manifolds.RealLine() sage: I = R.open_interval(0, 2*pi) diff --git a/src/sage/manifolds/continuous_map_image.py b/src/sage/manifolds/continuous_map_image.py index cf3b073e875..bbcfa672a5a 100644 --- a/src/sage/manifolds/continuous_map_image.py +++ b/src/sage/manifolds/continuous_map_image.py @@ -19,6 +19,7 @@ from sage.manifolds.subset import ManifoldSubset + class ImageManifoldSubset(ManifoldSubset): r""" Subset of a topological manifold that is a continuous image of a manifold subset. diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py index 2c24b7c3ba9..d1e25cc3e31 100644 --- a/src/sage/manifolds/differentiable/affine_connection.py +++ b/src/sage/manifolds/differentiable/affine_connection.py @@ -35,6 +35,7 @@ from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism + class AffineConnection(SageObject): r""" Affine connection on a smooth manifold. diff --git a/src/sage/manifolds/differentiable/automorphismfield.py b/src/sage/manifolds/differentiable/automorphismfield.py index 4143e29228a..3556fc56e95 100644 --- a/src/sage/manifolds/differentiable/automorphismfield.py +++ b/src/sage/manifolds/differentiable/automorphismfield.py @@ -28,6 +28,7 @@ from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal + class AutomorphismField(TensorField): r""" Field of automorphisms of tangent spaces to a generic (a priori diff --git a/src/sage/manifolds/differentiable/automorphismfield_group.py b/src/sage/manifolds/differentiable/automorphismfield_group.py index 78a906466b3..c6315670c9e 100644 --- a/src/sage/manifolds/differentiable/automorphismfield_group.py +++ b/src/sage/manifolds/differentiable/automorphismfield_group.py @@ -48,6 +48,7 @@ from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) + class AutomorphismFieldGroup(UniqueRepresentation, Parent): r""" General linear group of the module of vector fields along a differentiable diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index af74ac85669..bcfa37dc237 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -1069,6 +1069,7 @@ def restrict(self, subset, restrictions=None): #****************************************************************************** + class DiffCoordChange(CoordChange): r""" Transition map between two charts of a differentiable manifold. diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index 8b88a0351eb..e28e44b8adb 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -37,6 +37,7 @@ from sage.manifolds.point import ManifoldPoint from sage.manifolds.differentiable.diff_map import DiffMap + class DifferentiableCurve(DiffMap): r""" Curve in a differentiable manifold. diff --git a/src/sage/manifolds/differentiable/de_rham_cohomology.py b/src/sage/manifolds/differentiable/de_rham_cohomology.py index 53a5b50aaa0..9bc766f5a78 100644 --- a/src/sage/manifolds/differentiable/de_rham_cohomology.py +++ b/src/sage/manifolds/differentiable/de_rham_cohomology.py @@ -53,6 +53,7 @@ from .characteristic_cohomology_class import (CharacteristicCohomologyClassRing, CharacteristicCohomologyClassRingElement) + class DeRhamCohomologyClass(AlgebraElement): r""" Define a cohomology class in the de Rham cohomology ring. @@ -298,6 +299,7 @@ def __eq__(self, other): return True raise NotImplementedError('comparison via exact forms is currently not supported') + class DeRhamCohomologyRing(Parent, UniqueRepresentation): r""" The de Rham cohomology ring of a de Rham complex. diff --git a/src/sage/manifolds/differentiable/degenerate.py b/src/sage/manifolds/differentiable/degenerate.py index da83139ed6e..b34fefc3a89 100644 --- a/src/sage/manifolds/differentiable/degenerate.py +++ b/src/sage/manifolds/differentiable/degenerate.py @@ -22,6 +22,7 @@ ############################################################################### + class DegenerateManifold(DifferentiableManifold): r""" @@ -370,6 +371,7 @@ def open_subset(self, name, latex_name=None, coord_def={}): from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal from sage.manifolds.differentiable.tensorfield import TensorField + class TangentTensor(TensorFieldParal): r""" Let ``S`` be a lightlike submanifold embedded in a pseudo-Riemannian diff --git a/src/sage/manifolds/differentiable/degenerate_submanifold.py b/src/sage/manifolds/differentiable/degenerate_submanifold.py index 5b35607d53c..a119fb57802 100644 --- a/src/sage/manifolds/differentiable/degenerate_submanifold.py +++ b/src/sage/manifolds/differentiable/degenerate_submanifold.py @@ -172,6 +172,7 @@ if TYPE_CHECKING: from sage.manifolds.differentiable.metric import DegenerateMetric + class DegenerateSubmanifold(DegenerateManifold, DifferentiableSubmanifold): r""" Degenerate submanifolds. @@ -452,12 +453,12 @@ def set_transverse(self, rigging=None, normal=None): sage: S.set_transverse(rigging=t) """ if isinstance(rigging, (list, tuple)): - rigging = [elt for elt in rigging] + rigging = list(rigging) else: if rigging is not None: rigging = [rigging] if isinstance(normal, (list, tuple)): - normal = [elt for elt in normal] + normal = list(normal) else: if normal is not None: normal = [normal] @@ -540,11 +541,11 @@ def screen(self, name, screen, rad, latex_name=None): Lorentzian manifold M """ if isinstance(screen, (list, tuple)): - screen = [elt for elt in screen] + screen = list(screen) else: screen = [screen] if isinstance(rad, (list, tuple)): - rad = [elt for elt in rad] + rad = list(rad) else: rad = [rad] if name in self._screens: @@ -1288,7 +1289,7 @@ def principal_directions(self, screen=None): for eigen_vector in eigen_space[1]: v = self._ambient.vector_field(name="e_{}".format(next(counter)) ).along(self.immersion()) - v[frame, :] = [elt for elt in eigen_vector] + [0] + v[frame, :] = list(eigen_vector) + [0] res.append((TangentTensor(v, self.immersion()), self.scalar_field( {chart: eigen_space[0] for chart in self.top_charts()}))) #res[-1][0].set_name("e_{}".format(next(counter))) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index ae924713ffd..fb58189796e 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -770,7 +770,7 @@ def hodge_dual( nondegenerate_tensor = self._vmodule._ambient_domain.metric() p = self.tensor_type()[1] - # For performance reasons, we raise the indicies of the volume form + # For performance reasons, we raise the indices of the volume form # and not of the differential form; in the symplectic case this is wrong by # a factor of (-1)^p, which will be corrected below eps = nondegenerate_tensor.volume_form(p) @@ -787,7 +787,7 @@ def hodge_dual( result = result * nondegenerate_tensor._indic_signat from sage.manifolds.differentiable.symplectic_form import SymplecticForm if isinstance(nondegenerate_tensor, SymplecticForm): - # correction because we lifted the indicies of the volume (see above) + # correction because we lifted the indices of the volume (see above) result = result * (-1)**p result.set_name( diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 291893b6cc9..ee0dd856697 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -570,6 +570,7 @@ def degree(self): # ***************************************************************************** + class DiffFormFreeModule(ExtPowerDualFreeModule): r""" Free module of differential forms of a given degree `p` (`p`-forms) along diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index c238bcd54d5..6ac44fa7fb6 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -43,6 +43,7 @@ from sage.manifolds.point import ManifoldPoint from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleMorphism + class DiffMap(ContinuousMap): r""" Differentiable map between two differentiable manifolds. @@ -1024,8 +1025,8 @@ def _pullback_chart(diff_map, tensor, chart1, chart2): if nproc != 1: # Parallel computation lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] - ind_list = [ind for ind in ptcomp.non_redundant_index_generator()] - ind_step = max(1, int(len(ind_list)/nproc/2)) + ind_list = list(ptcomp.non_redundant_index_generator()) + ind_step = max(1, (len(ind_list) // nproc) // 2) local_list = lol(ind_list, ind_step) # list of input parameters listParalInput = [(tcomp, chart1, chart2, coord2_1, jacob, diff --git a/src/sage/manifolds/differentiable/examples/euclidean.py b/src/sage/manifolds/differentiable/examples/euclidean.py index 555881193cf..ec0f30be82c 100644 --- a/src/sage/manifolds/differentiable/examples/euclidean.py +++ b/src/sage/manifolds/differentiable/examples/euclidean.py @@ -1,10 +1,10 @@ r""" Euclidean Spaces -An *Euclidean space of dimension* `n` is an affine space `E`, whose associated +A *Euclidean space of dimension* `n` is an affine space `E`, whose associated vector space is a `n`-dimensional vector space over `\RR` and is equipped with a positive definite symmetric bilinear form, called the *scalar product* or -*dot product* [Ber1987]_. An Euclidean space of dimension `n` can also be +*dot product* [Ber1987]_. A Euclidean space of dimension `n` can also be viewed as a Riemannian manifold that is diffeomorphic to `\RR^n` and that has a flat metric `g`. The Euclidean scalar product is then that defined by the Riemannian metric `g`. @@ -419,11 +419,12 @@ ############################################################################### + class EuclideanSpace(PseudoRiemannianManifold): r""" Euclidean space. - An *Euclidean space of dimension* `n` is an affine space `E`, whose + A *Euclidean space of dimension* `n` is an affine space `E`, whose associated vector space is a `n`-dimensional vector space over `\RR` and is equipped with a positive definite symmetric bilinear form, called the *scalar product* or *dot product*. @@ -506,7 +507,7 @@ class EuclideanSpace(PseudoRiemannianManifold): sage: latex(F) \mathcal{F} - By default, an Euclidean space is created with a single coordinate chart: + By default, a Euclidean space is created with a single coordinate chart: that of Cartesian coordinates:: sage: E.atlas() @@ -556,7 +557,7 @@ class EuclideanSpace(PseudoRiemannianManifold): sage: latex(xi+ze) {\xi} + {\zeta} - Thanks to the argument ``coordinates``, an Euclidean space can be + Thanks to the argument ``coordinates``, a Euclidean space can be constructed with curvilinear coordinates initialized instead of the Cartesian ones:: @@ -729,14 +730,14 @@ def __init__(self, n, name=None, latex_name=None, category=None, init_coord_methods=None, unique_tag=None): r""" - Construct an Euclidean space. + Construct a Euclidean space. INPUT: This class also takes the following input: - ``base_manifold`` -- (default: ``None``) if not ``None``, must be - an Euclidean space; the created object is then an open subset + a Euclidean space; the created object is then an open subset of ``base_manifold`` - ``category`` -- (default: ``None``) to specify the category; if ``None``, @@ -1041,18 +1042,19 @@ def sphere(self, radius=1, center=None, name=None, latex_name=None, ############################################################################### + class EuclideanPlane(EuclideanSpace): r""" Euclidean plane. - An *Euclidean plane* is an affine space `E`, whose associated vector space + A *Euclidean plane* is an affine space `E`, whose associated vector space is a 2-dimensional vector space over `\RR` and is equipped with a positive definite symmetric bilinear form, called the *scalar product* or *dot product*. The class :class:`EuclideanPlane` inherits from :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold` - (via :class:`EuclideanSpace`) since an Euclidean plane can be viewed + (via :class:`EuclideanSpace`) since a Euclidean plane can be viewed as a Riemannian manifold that is diffeomorphic to `\RR^2` and that has a flat metric `g`. The Euclidean scalar product is the one defined by the Riemannian metric `g`. @@ -1109,7 +1111,7 @@ class EuclideanPlane(EuclideanSpace): EXAMPLES: - One creates an Euclidean plane ``E`` with:: + One creates a Euclidean plane ``E`` with:: sage: E. = EuclideanSpace(); E Euclidean plane E^2 @@ -1165,7 +1167,7 @@ def __init__(self, name=None, latex_name=None, coordinates='Cartesian', symbols=None, metric_name='g', metric_latex_name=None, start_index=1, base_manifold=None, category=None, unique_tag=None): r""" - Construct an Euclidean plane. + Construct a Euclidean plane. TESTS:: @@ -1598,7 +1600,7 @@ class Euclidean3dimSpace(EuclideanSpace): - ``start_index`` -- (default: 1) integer; lower value of the range of indices used for "indexed objects" in the Euclidean 3-space, e.g. coordinates of a chart - - ``base_manifold`` -- (default: ``None``) if not ``None``, must be an + - ``base_manifold`` -- (default: ``None``) if not ``None``, must be a Euclidean 3-space; the created object is then an open subset of ``base_manifold`` - ``category`` -- (default: ``None``) to specify the category; if ``None``, @@ -1686,7 +1688,7 @@ def __init__(self, name=None, latex_name=None, coordinates='Cartesian', symbols=None, metric_name='g', metric_latex_name=None, start_index=1, base_manifold=None, category=None, unique_tag=None): r""" - Construct an Euclidean 3-space. + Construct a Euclidean 3-space. TESTS:: @@ -2439,7 +2441,7 @@ def scalar_triple_product(self, name=None, latex_name=None): Return the scalar triple product operator, as a 3-form. The *scalar triple product* (also called *mixed product*) of three - vector fields `u`, `v` and `w` defined on an Euclidean space `E` + vector fields `u`, `v` and `w` defined on a Euclidean space `E` is the scalar field .. MATH:: diff --git a/src/sage/manifolds/differentiable/examples/real_line.py b/src/sage/manifolds/differentiable/examples/real_line.py index 746bae34c91..52b3dfad182 100644 --- a/src/sage/manifolds/differentiable/examples/real_line.py +++ b/src/sage/manifolds/differentiable/examples/real_line.py @@ -33,6 +33,7 @@ from sage.manifolds.structure import RealDifferentialStructure from sage.categories.manifolds import Manifolds + class OpenInterval(DifferentiableManifold): r""" Open interval as a 1-dimensional differentiable manifold over `\RR`. diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index ceb9ad849e1..43956a3ba1a 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -173,6 +173,7 @@ from sage.rings.real_mpfr import RR from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace + class Sphere(PseudoRiemannianSubmanifold): r""" Sphere smoothly embedded in Euclidean Space. diff --git a/src/sage/manifolds/differentiable/examples/symplectic_space.py b/src/sage/manifolds/differentiable/examples/symplectic_space.py index 6ce44f821d4..5a78277d567 100644 --- a/src/sage/manifolds/differentiable/examples/symplectic_space.py +++ b/src/sage/manifolds/differentiable/examples/symplectic_space.py @@ -16,7 +16,7 @@ # ***************************************************************************** from __future__ import annotations -from typing import Optional, Tuple +from typing import Optional from sage.categories.manifolds import Manifolds from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace @@ -43,7 +43,7 @@ def __init__( symplectic_latex_name: Optional[str] = None, start_index: int = 1, base_manifold: Optional[StandardSymplecticSpace] = None, - names: Optional[Tuple[str]] = None, + names: Optional[tuple[str]] = None, ): r""" INPUT: diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 6867448cdce..135e6d53d32 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -1066,7 +1066,7 @@ def solve(self, step=None, method='odeint', solution_key=None, t_min = self.domain().lower_bound() t_max = self.domain().upper_bound() - eqns_num = [eq for eq in self._equations_rhs] + eqns_num = list(self._equations_rhs) # 'self._equations_rhs' needs not to be modified ever, because we # want to keep track of the most general form of the equations # defining self, since those may contain parameters (which, for @@ -2864,6 +2864,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, aspect_ratio=aspect_ratio, color=color, style=style, label_axes=label_axes) + class IntegratedAutoparallelCurve(IntegratedCurve): r""" Autoparallel curve on the manifold with respect to a given @@ -3648,6 +3649,7 @@ def system(self, verbose=False): return [self._equations_rhs, v0, chart] + class IntegratedGeodesic(IntegratedAutoparallelCurve): r""" Geodesic on the manifold with respect to a given metric. diff --git a/src/sage/manifolds/differentiable/levi_civita_connection.py b/src/sage/manifolds/differentiable/levi_civita_connection.py index a995cf804cd..55c4aca9416 100644 --- a/src/sage/manifolds/differentiable/levi_civita_connection.py +++ b/src/sage/manifolds/differentiable/levi_civita_connection.py @@ -34,6 +34,7 @@ from sage.manifolds.differentiable.affine_connection import AffineConnection from sage.manifolds.differentiable.vectorframe import CoordFrame + class LeviCivitaConnection(AffineConnection): r""" Levi-Civita connection on a pseudo-Riemannian manifold. diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index 52a75ac61ad..d26e1cfea91 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -49,6 +49,7 @@ from sage.manifolds.differentiable.integrated_curve import IntegratedAutoparallelCurve from sage.manifolds.differentiable.integrated_curve import IntegratedGeodesic + class DifferentiableManifoldHomset(TopologicalManifoldHomset): r""" Set of differentiable maps between two differentiable manifolds. @@ -508,6 +509,7 @@ def _an_element_(self): #****************************************************************************** + class IntegratedCurveSet(DifferentiableCurveSet): r""" Set of integrated curves in a differentiable manifold. @@ -962,6 +964,7 @@ def one(self): #****************************************************************************** + class IntegratedAutoparallelCurveSet(IntegratedCurveSet): r""" Set of integrated autoparallel curves in a differentiable manifold. @@ -1415,6 +1418,7 @@ def _an_element_(self): #****************************************************************************** + class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): r""" Set of integrated geodesic in a differentiable manifold. diff --git a/src/sage/manifolds/differentiable/mixed_form_algebra.py b/src/sage/manifolds/differentiable/mixed_form_algebra.py index c3ab8bbbbe9..26339c0d6ad 100644 --- a/src/sage/manifolds/differentiable/mixed_form_algebra.py +++ b/src/sage/manifolds/differentiable/mixed_form_algebra.py @@ -37,6 +37,7 @@ from sage.symbolic.ring import SR from sage.manifolds.differentiable.mixed_form import MixedForm + class MixedFormAlgebra(Parent, UniqueRepresentation): r""" An instance of this class represents the graded algebra of mixed forms. diff --git a/src/sage/manifolds/differentiable/multivector_module.py b/src/sage/manifolds/differentiable/multivector_module.py index 2dc036a878c..fa2d5fce099 100644 --- a/src/sage/manifolds/differentiable/multivector_module.py +++ b/src/sage/manifolds/differentiable/multivector_module.py @@ -489,6 +489,7 @@ def degree(self): #*********************************************************************** + class MultivectorFreeModule(ExtPowerFreeModule): r""" Free module of multivector fields of a given degree `p` (`p`-vector diff --git a/src/sage/manifolds/differentiable/multivectorfield.py b/src/sage/manifolds/differentiable/multivectorfield.py index 08b6d4d75a8..1e739dfdd17 100644 --- a/src/sage/manifolds/differentiable/multivectorfield.py +++ b/src/sage/manifolds/differentiable/multivectorfield.py @@ -40,6 +40,7 @@ from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal + class MultivectorField(TensorField): r""" Multivector field with values on a generic (i.e. a priori not diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian.py b/src/sage/manifolds/differentiable/pseudo_riemannian.py index 3ad49659de8..505900ad0df 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian.py @@ -274,6 +274,7 @@ ############################################################################### + class PseudoRiemannianManifold(DifferentiableManifold): r""" PseudoRiemannian manifold. diff --git a/src/sage/manifolds/differentiable/scalarfield_algebra.py b/src/sage/manifolds/differentiable/scalarfield_algebra.py index 205f8cef841..fc716f5fcc4 100644 --- a/src/sage/manifolds/differentiable/scalarfield_algebra.py +++ b/src/sage/manifolds/differentiable/scalarfield_algebra.py @@ -35,6 +35,7 @@ class `C^k` over a topological field `K` (in from sage.manifolds.scalarfield_algebra import ScalarFieldAlgebra from sage.manifolds.differentiable.scalarfield import DiffScalarField + class DiffScalarFieldAlgebra(ScalarFieldAlgebra): r""" Commutative algebra of differentiable scalar fields on a differentiable diff --git a/src/sage/manifolds/differentiable/symplectic_form_test.py b/src/sage/manifolds/differentiable/symplectic_form_test.py index c38feb90cef..804c8956b6e 100644 --- a/src/sage/manifolds/differentiable/symplectic_form_test.py +++ b/src/sage/manifolds/differentiable/symplectic_form_test.py @@ -158,6 +158,7 @@ def test_omega_on_one_forms_is_omega_on_dual_vectors( b = M.one_form(3,4) assert omega.on_forms(a, b) == omega(a.up(omega), b.up(omega)) + def generic_scalar_field(M: DifferentiableManifold, name: str) -> DiffScalarField: chart_functions = {chart: function(name)(*chart[:]) for chart in M.atlas()} return M.scalar_field(chart_functions, name=name) diff --git a/src/sage/manifolds/differentiable/tangent_space.py b/src/sage/manifolds/differentiable/tangent_space.py index fd2576f8917..0320b70bb93 100644 --- a/src/sage/manifolds/differentiable/tangent_space.py +++ b/src/sage/manifolds/differentiable/tangent_space.py @@ -34,6 +34,7 @@ if TYPE_CHECKING: from sage.manifolds.point import ManifoldPoint + class TangentSpace(FiniteRankFreeModule): r""" Tangent space to a differentiable manifold at a given point. diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 3ef7ccff001..562c62cefc8 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -52,7 +52,7 @@ # ***************************************************************************** from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Optional, TypeVar, Union from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -72,7 +72,7 @@ from sage.tensor.modules.comp import Components -TensorType = Tuple[int, int] +TensorType = tuple[int, int] T = TypeVar("T", bound='TensorField') diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index fcdd93a9f0f..22d42050b89 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -587,6 +587,7 @@ def zero(self): #*********************************************************************** + class TensorFieldFreeModule(TensorFreeModule): r""" Free module of tensor fields of a given type `(k,l)` along a diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index 2c34656d757..cb0e1db14cd 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -1414,12 +1414,12 @@ def lie_derivative(self, vector): # get n processes nproc = Parallelism().get('tensor') - if nproc != 1 : + if nproc != 1: # Parallel computation lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] - ind_list = [ind for ind in resc.non_redundant_index_generator()] - ind_step = max(1, int(len(ind_list)/nproc)) + ind_list = list(resc.non_redundant_index_generator()) + ind_step = max(1, len(ind_list) // nproc) local_list = lol(ind_list, ind_step) # list of input parameters: listParalInput = [(self, vector, coord_frame, chart, ind_part) for ind_part in local_list] diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index 4a2c5372419..79e940a19e6 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -37,6 +37,7 @@ from sage.misc.superseded import deprecated_function_alias from sage.rings.rational_field import QQ + class DifferentiableVectorBundle(TopologicalVectorBundle): r""" An instance of this class represents a differentiable vector bundle @@ -333,6 +334,7 @@ def total_space(self): # ***************************************************************************** + class TensorBundle(DifferentiableVectorBundle): r""" Tensor bundle over a differentiable manifold along a differentiable map. @@ -1642,7 +1644,7 @@ def orientation(self): EXAMPLES: - In the trivial case, i.e. if the destination map is the identitiy + In the trivial case, i.e. if the destination map is the identity and the tangent bundle is covered by one frame, the orientation is easily obtained:: diff --git a/src/sage/manifolds/differentiable/vectorfield.py b/src/sage/manifolds/differentiable/vectorfield.py index 6b0b51582a3..44ae84089de 100644 --- a/src/sage/manifolds/differentiable/vectorfield.py +++ b/src/sage/manifolds/differentiable/vectorfield.py @@ -1376,6 +1376,7 @@ def cross_product(self, other, metric=None): #****************************************************************************** + class VectorFieldParal(FiniteRankFreeModuleElement, MultivectorFieldParal, VectorField): r""" diff --git a/src/sage/manifolds/differentiable/vectorframe.py b/src/sage/manifolds/differentiable/vectorframe.py index a284c6487b6..594d2e9a729 100644 --- a/src/sage/manifolds/differentiable/vectorframe.py +++ b/src/sage/manifolds/differentiable/vectorframe.py @@ -1554,6 +1554,7 @@ def set_name(self, symbol, latex_symbol=None, indices=None, #****************************************************************************** + class CoordCoFrame(CoFrame): r""" Coordinate coframe on a differentiable manifold. @@ -1667,6 +1668,7 @@ def _repr_(self): #****************************************************************************** + class CoordFrame(VectorFrame): r""" Coordinate frame on a differentiable manifold. diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index f9c3404535e..565fa2401cc 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -28,6 +28,7 @@ from functools import total_ordering from sage.sets.family import FiniteFamily + @total_ordering class ManifoldObjectFiniteFamily(FiniteFamily): @@ -181,6 +182,7 @@ def _latex_(self): """ return self._latex_name + class ManifoldSubsetFiniteFamily(ManifoldObjectFiniteFamily): r""" diff --git a/src/sage/manifolds/local_frame.py b/src/sage/manifolds/local_frame.py index 99fcbe4acff..42e1579640d 100644 --- a/src/sage/manifolds/local_frame.py +++ b/src/sage/manifolds/local_frame.py @@ -408,6 +408,7 @@ def set_name(self, symbol, latex_symbol=None, indices=None, #****************************************************************************** + class LocalFrame(FreeModuleBasis): r""" Local frame on a vector bundle. @@ -1232,6 +1233,7 @@ def set_name(self, symbol, latex_symbol=None, indices=None, #****************************************************************************** + class TrivializationCoFrame(LocalCoFrame): r""" Trivialization coframe on a vector bundle. @@ -1364,6 +1366,7 @@ def _repr_(self): #****************************************************************************** + class TrivializationFrame(LocalFrame): r""" Trivialization frame on a topological vector bundle. diff --git a/src/sage/manifolds/manifold_homset.py b/src/sage/manifolds/manifold_homset.py index 78c9bc9984c..c390233ca55 100644 --- a/src/sage/manifolds/manifold_homset.py +++ b/src/sage/manifolds/manifold_homset.py @@ -33,6 +33,7 @@ from sage.manifolds.continuous_map import ContinuousMap from sage.misc.cachefunc import cached_method + class TopologicalManifoldHomset(UniqueRepresentation, Homset): r""" Set of continuous maps between two topological manifolds. diff --git a/src/sage/manifolds/operators.py b/src/sage/manifolds/operators.py index 636c11db149..eed5df22eb5 100644 --- a/src/sage/manifolds/operators.py +++ b/src/sage/manifolds/operators.py @@ -43,6 +43,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** + def grad(scalar): r""" Gradient operator. @@ -94,6 +95,7 @@ def grad(scalar): """ return scalar.gradient() + def div(tensor): r""" Divergence operator. @@ -167,6 +169,7 @@ def div(tensor): """ return tensor.divergence() + def curl(vector): r""" Curl operator. @@ -234,6 +237,7 @@ def curl(vector): """ return vector.curl() + def laplacian(field): r""" Laplace-Beltrami operator. @@ -294,6 +298,7 @@ def laplacian(field): """ return field.laplacian() + def dalembertian(field): r""" d'Alembert operator. diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index 3971a85c776..b3647cc1447 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -92,6 +92,7 @@ from sage.symbolic.expression import Expression from sage.rings.integer_ring import ZZ + class ManifoldPoint(Element): r""" Point of a topological manifold. @@ -689,7 +690,7 @@ def __eq__(self, other): diff = xs - xo period = periods[ind] if period is not None: - if not (diff/period in ZZ): + if diff/period not in ZZ: return False else: if isinstance(diff, Expression) and not diff.is_trivial_zero(): diff --git a/src/sage/manifolds/scalarfield_algebra.py b/src/sage/manifolds/scalarfield_algebra.py index 311f749adf3..960b4514387 100644 --- a/src/sage/manifolds/scalarfield_algebra.py +++ b/src/sage/manifolds/scalarfield_algebra.py @@ -38,6 +38,7 @@ from sage.symbolic.ring import SymbolicRing, SR from sage.manifolds.scalarfield import ScalarField + class ScalarFieldAlgebra(UniqueRepresentation, Parent): r""" Commutative algebra of scalar fields on a topological manifold. diff --git a/src/sage/manifolds/section.py b/src/sage/manifolds/section.py index 50ab4531cb6..75af42a57e5 100644 --- a/src/sage/manifolds/section.py +++ b/src/sage/manifolds/section.py @@ -25,6 +25,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ + class Section(ModuleElementWithMutability): r""" Section in a vector bundle. @@ -2250,6 +2251,7 @@ def set_immutable(self): #****************************************************************************** + class TrivialSection(FiniteRankFreeModuleElement, Section): r""" Section in a trivial vector bundle. diff --git a/src/sage/manifolds/section_module.py b/src/sage/manifolds/section_module.py index 5c654762977..6192045f45a 100644 --- a/src/sage/manifolds/section_module.py +++ b/src/sage/manifolds/section_module.py @@ -34,6 +34,7 @@ from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.manifolds.section import Section, TrivialSection + class SectionModule(UniqueRepresentation, Parent): r""" Module of sections over a vector bundle `E \to M` of class `C^k` on a domain @@ -476,6 +477,7 @@ def set_default_frame(self, basis): #****************************************************************************** + class SectionFreeModule(FiniteRankFreeModule): r""" Free module of sections over a vector bundle `E \to M` of class `C^k` on a diff --git a/src/sage/manifolds/structure.py b/src/sage/manifolds/structure.py index cf6a9466f9a..ed166b6438a 100644 --- a/src/sage/manifolds/structure.py +++ b/src/sage/manifolds/structure.py @@ -35,6 +35,8 @@ # This is a slight abuse by making this a Singleton, but there is no # need to have different copies of this object. + + class TopologicalStructure(Singleton): """ The structure of a topological manifold over a general topological field. @@ -82,6 +84,7 @@ def subcategory(self, cat): """ return cat + class DifferentialStructure(Singleton): """ The structure of a differentiable manifold over a general topological @@ -130,6 +133,7 @@ def subcategory(self, cat): """ return cat + class PseudoRiemannianStructure(Singleton): """ The structure of a pseudo-Riemannian manifold. @@ -153,6 +157,7 @@ def subcategory(self, cat): """ return cat + class RiemannianStructure(Singleton): """ The structure of a Riemannian manifold. @@ -176,6 +181,7 @@ def subcategory(self, cat): """ return cat + class LorentzianStructure(Singleton): """ The structure of a Lorentzian manifold. @@ -199,6 +205,7 @@ def subcategory(self, cat): """ return cat + class DegenerateStructure(Singleton): """ The structure of a degenerate manifold. diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 5a3f9238a9a..453fa02de2f 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -75,6 +75,7 @@ from sage.manifolds.family import ManifoldObjectFiniteFamily, ManifoldSubsetFiniteFamily from sage.manifolds.point import ManifoldPoint + class ManifoldSubset(UniqueRepresentation, Parent): r""" Subset of a topological manifold. diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index 9be9f7f9939..b1eb8d37835 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -17,6 +17,7 @@ from sage.manifolds.subset import ManifoldSubset + class ManifoldSubsetClosure(ManifoldSubset): r""" diff --git a/src/sage/manifolds/trivialization.py b/src/sage/manifolds/trivialization.py index 940ce857132..4c718db8ffe 100644 --- a/src/sage/manifolds/trivialization.py +++ b/src/sage/manifolds/trivialization.py @@ -24,6 +24,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.manifolds.local_frame import TrivializationFrame + class Trivialization(UniqueRepresentation, SageObject): r""" A local trivialization of a given vector bundle. @@ -302,6 +303,7 @@ def coframe(self): # ***************************************************************************** + class TransitionMap(SageObject): r""" Transition map between two trivializations. diff --git a/src/sage/manifolds/utilities.py b/src/sage/manifolds/utilities.py index b51b7ace03d..190213eeb41 100644 --- a/src/sage/manifolds/utilities.py +++ b/src/sage/manifolds/utilities.py @@ -672,6 +672,7 @@ def simplify_chain_generic(expr): expr = expr.expand_sum() return expr + def simplify_chain_generic_sympy(expr): r""" Apply a chain of simplifications to a sympy expression. @@ -731,6 +732,7 @@ def simplify_chain_generic_sympy(expr): expr = expr.simplify() return expr + def simplify_chain_real_sympy(expr): r""" Apply a chain of simplifications to a sympy expression, assuming the @@ -804,6 +806,7 @@ def simplify_chain_real_sympy(expr): #****************************************************************************** + class ExpressionNice(Expression): r""" Subclass of :class:`~sage.symbolic.expression.Expression` for a @@ -1229,6 +1232,7 @@ def _list_functions(ex, list_f): #****************************************************************************** + def set_axes_labels(graph, xlabel, ylabel, zlabel, **kwds): r""" Set axes labels for a 3D graphics object ``graph``. @@ -1278,6 +1282,7 @@ def set_axes_labels(graph, xlabel, ylabel, zlabel, **kwds): graph += text3d(' ' + zlabel, (xmin1, ymin1, z1), **kwds) return graph + def exterior_derivative(form): r""" Exterior derivative of a differential form. diff --git a/src/sage/manifolds/vector_bundle.py b/src/sage/manifolds/vector_bundle.py index fa2d277d012..ad830d68469 100644 --- a/src/sage/manifolds/vector_bundle.py +++ b/src/sage/manifolds/vector_bundle.py @@ -41,6 +41,7 @@ from sage.rings.integer import Integer from sage.manifolds.vector_bundle_fiber import VectorBundleFiber + class TopologicalVectorBundle(CategoryObject, UniqueRepresentation): r""" An instance of this class is a topological vector bundle `E \to B` over a diff --git a/src/sage/manifolds/vector_bundle_fiber.py b/src/sage/manifolds/vector_bundle_fiber.py index da255d46fd7..325ac1582b2 100644 --- a/src/sage/manifolds/vector_bundle_fiber.py +++ b/src/sage/manifolds/vector_bundle_fiber.py @@ -21,6 +21,7 @@ from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.manifolds.vector_bundle_fiber_element import VectorBundleFiberElement + class VectorBundleFiber(FiniteRankFreeModule): r""" Fiber of a given vector bundle at a given point. diff --git a/src/sage/manifolds/vector_bundle_fiber_element.py b/src/sage/manifolds/vector_bundle_fiber_element.py index 19eaf08bb77..39f18691463 100644 --- a/src/sage/manifolds/vector_bundle_fiber_element.py +++ b/src/sage/manifolds/vector_bundle_fiber_element.py @@ -20,6 +20,7 @@ from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement + class VectorBundleFiberElement(FiniteRankFreeModuleElement): r""" Vector in a fiber of a vector bundle at the given point. diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 6d74468e179..332eaf82df8 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -857,7 +857,7 @@ cdef class MatrixArgs: cdef list L if self.typ == MA_ENTRIES_SEQ_FLAT and not convert: - # Try to re-use existing list + # Try to reuse existing list if type(self.entries) is not list: L = list(self.entries) else: diff --git a/src/sage/matrix/benchmark.py b/src/sage/matrix/benchmark.py index 8567f869511..b411de7d119 100644 --- a/src/sage/matrix/benchmark.py +++ b/src/sage/matrix/benchmark.py @@ -121,6 +121,7 @@ def report_ZZ(**kwds): # Integer Nullspace + def nullspace_ZZ(n=200, min=0, max=2**32, system='sage'): """ Nullspace over ZZ: @@ -240,6 +241,7 @@ def rank_ZZ(n=700, min=0, max=9, system='sage'): else: raise ValueError('unknown system "%s"' % system) + def rank2_ZZ(n=400, min=0, max=2**64, system='sage'): """ Rank 2 over ZZ: @@ -281,6 +283,7 @@ def rank2_ZZ(n=400, min=0, max=2**64, system='sage'): # Smith Form + def smithform_ZZ(n=128, min=0, max=9, system='sage'): """ Smith Form over ZZ: @@ -366,6 +369,7 @@ def matrix_multiply_ZZ(n=300, min=-9, max=9, system='sage', times=1): else: raise ValueError('unknown system "%s"' % system) + def matrix_add_ZZ(n=200, min=-9, max=9, system='sage', times=50): """ Matrix addition over ZZ @@ -413,6 +417,7 @@ def matrix_add_ZZ(n=200, min=-9, max=9, system='sage', times=50): else: raise ValueError('unknown system "%s"' % system) + def matrix_add_ZZ_2(n=200, bits=16, system='sage', times=50): """ Matrix addition over ZZ. @@ -435,6 +440,7 @@ def matrix_add_ZZ_2(n=200, bits=16, system='sage', times=50): b = 2**bits return matrix_add_ZZ(n=n, min=-b, max=b,system=system, times=times) + def det_ZZ(n=200, min=1, max=100, system='sage'): """ Dense integer determinant over ZZ. @@ -602,6 +608,7 @@ def report_GF(p=16411, **kwds): # Nullspace over GF + def nullspace_GF(n=300, p=16411, system='sage'): """ Given a n+1 x n matrix over GF(p) with random @@ -679,6 +686,7 @@ def charpoly_GF(n=100, p=16411, system='sage'): else: raise ValueError('unknown system "%s"' % system) + def matrix_add_GF(n=1000, p=16411, system='sage',times=100): """ Given two n x n matrix over GF(p) with random entries, add them. @@ -805,6 +813,7 @@ def rank_GF(n=500, p=16411, system='sage'): else: raise ValueError('unknown system "%s"' % system) + def rank2_GF(n=500, p=16411, system='sage'): """ Rank over GF(p): Given a (n + 10) x n matrix over GF(p) with @@ -842,6 +851,7 @@ def rank2_GF(n=500, p=16411, system='sage'): else: raise ValueError('unknown system "%s"' % system) + def det_GF(n=400, p=16411 , system='sage'): """ Dense determinant over GF(p). @@ -905,6 +915,7 @@ def hilbert_matrix(n): # Reduced row echelon form over QQ + def echelon_QQ(n=100, min=0, max=9, system='sage'): """ Given a n x (2*n) matrix over QQ with random integer entries @@ -945,6 +956,7 @@ def echelon_QQ(n=100, min=0, max=9, system='sage'): # Invert a matrix over QQ. + def inverse_QQ(n=100, min=0, max=9, system='sage'): """ Given a n x n matrix over QQ with random integer entries @@ -1067,6 +1079,8 @@ def det_hilbert_QQ(n=80, system='sage'): return float(magma.eval('s')) # inverse of Hilbert matrix + + def invert_hilbert_QQ(n=40, system='sage'): """ Run the benchmark for calculating the inverse of the hilbert @@ -1101,6 +1115,7 @@ def invert_hilbert_QQ(n=40, system='sage'): magma.eval(code) return float(magma.eval('s')) + def MatrixVector_QQ(n=1000,h=100,system='sage',times=1): """ Compute product of square ``n`` matrix by random vector with num and diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 28c990d0954..c0d9f93e804 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -3605,14 +3605,14 @@ cdef class Matrix(sage.structure.element.Matrix): [-4 -5 2] [ 3 4 5] """ - self.check_row_bounds_and_mutability(i,i) + self.check_row_bounds_and_mutability(i, i) if r < 0 or r >= A.nrows(): raise IndexError("invalid row") # this function exists just because it is useful for modular symbols presentations. cdef Py_ssize_t l l = 0 for k in cols: - self.set_unsafe(i,l,-A.get_unsafe(r,k)) #self[i,l] = -A[r,k] + self.set_unsafe(i, l, -A.get_unsafe(r, k)) # self[i,l] = -A[r,k] l += 1 def reverse_rows_and_columns(self): diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 3edb9ead54d..96a175825b3 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -216,21 +216,21 @@ cdef class Matrix(Matrix0): EXAMPLES:: sage: M = matrix(ZZ, 2, range(4)) - sage: giac(M) # needs sage.libs.giac + sage: giac(M) # needs giac [[0,1],[2,3]] sage: M = matrix(QQ, 3, [1,2,3, 4/3,5/3,6/4, 7,8,9]) - sage: giac(M) # needs sage.libs.giac + sage: giac(M) # needs giac [[1,2,3],[4/3,5/3,3/2],[7,8,9]] sage: P. = ZZ[] sage: M = matrix(P, 2, [-9*x^2-2*x+2, x-1, x^2+8*x, -3*x^2+5]) - sage: giac(M) # needs sage.libs.giac + sage: giac(M) # needs giac [[-9*sageVARx^2-2*sageVARx+2,sageVARx-1],[sageVARx^2+8*sageVARx,-3*sageVARx^2+5]] sage: y = var('y') # needs sage.symbolic sage: M = matrix(SR, 2, [y+sin(y), y - 4, 1/y, dilog(y)]) # needs sage.symbolic - sage: giac(M).det().sage() # needs sage.libs.giac sage.symbolic + sage: giac(M).det().sage() # needs giac sage.symbolic (y^2*dilog(y) + y*dilog(y)*sin(y) - y + 4)/y """ s = ','.join('[' + ','.join(cf._giac_init_() for cf in row) + ']' @@ -670,7 +670,7 @@ cdef class Matrix(Matrix0): entries = [[sib(v, 2) for v in row] for row in self.rows()] return sib.name('matrix')(self.base_ring(), entries) - def numpy(self, dtype=None): + def numpy(self, dtype=None, copy=True): """ Return the Numpy matrix associated to this matrix. @@ -680,6 +680,10 @@ cdef class Matrix(Matrix0): then the type will be determined as the minimum type required to hold the objects in the sequence. + - ``copy`` -- if `self` is already an `ndarray`, then this flag + determines whether the data is copied (the default), or whether + a view is constructed. + EXAMPLES:: sage: # needs numpy @@ -731,7 +735,7 @@ cdef class Matrix(Matrix0): (3, 4) """ import numpy - A = numpy.matrix(self.list(), dtype=dtype) + A = numpy.matrix(self.list(), dtype=dtype, copy=copy) return numpy.resize(A,(self.nrows(), self.ncols())) # Define the magic "__array__" function so that numpy.array(m) can convert diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 84ee5d0a760..3682c2be14e 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -898,7 +898,7 @@ cdef class Matrix(Matrix1): # first coerce both elements to parent over same base ring P = K if L is K else coercion_model.common_parent(K, L) if P not in _Fields and P.is_integral_domain() and extend: - # the non-integral-domain case is handled separatedly below + # the non-integral-domain case is handled separately below P = P.fraction_field() if L is not P: B = B.change_ring(P) @@ -4554,7 +4554,7 @@ cdef class Matrix(Matrix1): Over inexact rings: - For inexact rings one should avoid echolonizing if possible:: + For inexact rings one should avoid echelonizing if possible:: sage: A = Matrix( ....: [[ 0.0, 0.5, 0.8090169944], @@ -4801,7 +4801,7 @@ cdef class Matrix(Matrix1): :meth:`kernel` is exactly equal to :meth:`left_kernel`. For inexact rings use :meth:`right_kernel_matrix` with - ``basis='computed'`` to avoid echolonizing. + ``basis='computed'`` to avoid echelonizing. INPUT: @@ -5161,6 +5161,7 @@ cdef class Matrix(Matrix1): def left_kernel(self, *args, **kwds): r""" Return the left kernel of this matrix, as a vector space or free module. + This is the set of vectors ``x`` such that ``x*self = 0``. .. NOTE:: @@ -5169,7 +5170,7 @@ cdef class Matrix(Matrix1): :meth:`kernel` is exactly equal to :meth:`left_kernel`. For inexact rings use :meth:`right_kernel_matrix` with - ``basis='computed'`` (on the transpose of the matrix) to avoid echolonizing. + ``basis='computed'`` (on the transpose of the matrix) to avoid echelonizing. INPUT: @@ -12524,7 +12525,7 @@ cdef class Matrix(Matrix1): _, SA = A.jordan_form(transformation=True) _, SB = B.jordan_form(transformation=True) return (True, SB * SA.inverse()) - except (ValueError, RuntimeError, NotImplementedError): + except (ValueError, RuntimeError, NotImplementedError, TypeError): raise RuntimeError('unable to compute transformation for similar matrices') def symplectic_form(self): @@ -15900,7 +15901,7 @@ cdef class Matrix(Matrix1): sage: a.exp() # needs sage.symbolic [ 1/11882424341266*((11*sqrt(227345670387496707609) + 5941212170633)*e^(3/1275529100*sqrt(227345670387496707609)) - 11*sqrt(227345670387496707609) + 5941212170633)*e^(-3/2551058200*sqrt(227345670387496707609) + 101/200) 445243650/75781890129165569203*(sqrt(227345670387496707609)*e^(3/1275529100*sqrt(227345670387496707609)) - sqrt(227345670387496707609))*e^(-3/2551058200*sqrt(227345670387496707609) + 101/200)] [ 10000/53470909535697*(sqrt(227345670387496707609)*e^(3/1275529100*sqrt(227345670387496707609)) - sqrt(227345670387496707609))*e^(-3/2551058200*sqrt(227345670387496707609) + 101/200) -1/11882424341266*((11*sqrt(227345670387496707609) - 5941212170633)*e^(3/1275529100*sqrt(227345670387496707609)) - 11*sqrt(227345670387496707609) - 5941212170633)*e^(-3/2551058200*sqrt(227345670387496707609) + 101/200)] - sage: a.change_ring(RDF).exp() # rel tol 1e-14 # needs sage.symbolic + sage: a.change_ring(RDF).exp() # rel tol 6e-14 # needs sage.symbolic [42748127.31532951 7368259.244159399] [234538976.1381042 40426191.45156228] @@ -16342,7 +16343,7 @@ cdef class Matrix(Matrix1): Transform the matrix in place to hermite normal form and optionally return the transformation matrix. - The matrix is assumed to be over an Euclidean domain. In particular, + The matrix is assumed to be over a Euclidean domain. In particular, ``xgcd()`` method should be available for the elements of the domain. INPUT: @@ -16843,7 +16844,7 @@ cdef class Matrix(Matrix1): nonzero = j break if (nonzero != -1): - # swap column wih nonzero entry just outside block + # swap column with nonzero entry just outside block if nonzero != c+1: Z.swap_columns(c+1, nonzero) Z.swap_rows(c+1, nonzero) @@ -17098,7 +17099,7 @@ cdef class Matrix(Matrix1): sage: A.eigenvalues() Traceback (most recent call last): ... - NotImplementedError: algebraic closures of finite fields are only implemented for prime fields + TypeError: no canonical coercion from Finite Field in a of size 5^4 to Finite Field in z4 of size 5^4 Subdivisions are optional. :: @@ -17460,7 +17461,7 @@ cdef class Matrix(Matrix1): sage: A.eigenvalues() Traceback (most recent call last): ... - NotImplementedError: algebraic closures of finite fields are only implemented for prime fields + TypeError: no canonical coercion from Finite Field in a of size 7^2 to Finite Field in z2 of size 7^2 Companion matrices may be selected as any one of four different types. See the documentation for the companion matrix constructor, diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index ab5542dda7b..f32d738df0d 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -985,7 +985,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): INPUT: - - ``col`` -- integer indicating the column; must be coercable to + - ``col`` -- integer indicating the column; must be coercible to ``int``, and this must lie between 0 (inclusive) and ``self._ncols`` (exclusive), since no bounds-checking is performed - ``nump1`` -- integer; numerator bound plus one diff --git a/src/sage/matrix/matrix_dense.pyx b/src/sage/matrix/matrix_dense.pyx index b0de42539a0..f26078bde7e 100644 --- a/src/sage/matrix/matrix_dense.pyx +++ b/src/sage/matrix/matrix_dense.pyx @@ -171,19 +171,19 @@ cdef class Matrix_dense(matrix.Matrix): [4|1] [3|0] """ - (nc, nr) = (self.ncols(), self.nrows()) + nc, nr = self.ncols(), self.nrows() cdef Matrix_dense atrans - atrans = self.new_matrix(nrows = nc, ncols = nr, + atrans = self.new_matrix(nrows=nc, ncols=nr, copy=False, coerce=False) - cdef Py_ssize_t i,j - cdef Py_ssize_t ri,rj # reversed i and j + cdef Py_ssize_t i, j + cdef Py_ssize_t ri, rj # reversed i and j rj = nc for j from 0 <= j < nc: ri = nr - rj = rj-1 + rj -= 1 for i from 0 <= i < nr: - ri = ri-1 - atrans.set_unsafe(j , i, self.get_unsafe(ri,rj)) + ri -= 1 + atrans.set_unsafe(j, i, self.get_unsafe(ri, rj)) if self._subdivisions is not None: row_divs, col_divs = self.subdivisions() @@ -256,8 +256,8 @@ cdef class Matrix_dense(matrix.Matrix): prod = self.new_matrix(nr, nc, copy=False, coerce=False) for r in range(nr): for c in range(nc): - entry = self.get_unsafe(r,c)*other.get_unsafe(r,c) - prod.set_unsafe(r,c,entry) + entry = self.get_unsafe(r, c)*other.get_unsafe(r, c) + prod.set_unsafe(r, c, entry) return prod def _derivative(self, var=None, R=None): diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 255523b0273..714edb22b32 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -340,7 +340,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): import scipy import scipy.linalg from numpy.linalg import LinAlgError - try: ## Standard error reporting for Sage. + try: # Standard error reporting for Sage. M._matrix_numpy = scipy.linalg.inv(self._matrix_numpy) except LinAlgError: raise ZeroDivisionError("input matrix must be nonsingular") @@ -537,7 +537,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): p = -numpy.inf elif p == 'frob': p = 'fro' - elif p == 'sv' : + elif p == 'sv': p = None else: try: @@ -1286,7 +1286,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): from sage.rings.real_double import RDF from sage.rings.complex_double import CDF if isinstance(other, str): - # for backward compatibilty, allow algorithm to be passed as first + # for backward compatibility, allow algorithm to be passed as first # positional argument and tol as second positional argument from sage.misc.superseded import deprecation deprecation(29243, '"algorithm" and "tol" should be used as ' @@ -3663,7 +3663,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): sage: A = matrix(RDF, 2, [1,2,3,4]); A [1.0 2.0] [3.0 4.0] - sage: A.exp() # tol 1e-14 + sage: A.exp() # tol 5e-14 [51.968956198705044 74.73656456700327] [112.10484685050491 164.07380304920997] sage: A = matrix(CDF, 2, [1,2+I,3*I,4]); A # needs sage.symbolic @@ -3676,7 +3676,7 @@ cdef class Matrix_double_dense(Matrix_numpy_dense): TESTS:: sage: A = matrix(RDF, 2, [1,2,3,4]) - sage: A.exp() # tol 1e-14 + sage: A.exp() # tol 5e-14 [51.968956198705044 74.73656456700327] [112.10484685050491 164.07380304920997] diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index b8660ad08dc..5b7fd1fe2af 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -443,7 +443,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): finite field is small, there is a very high chance that ``e * B[j]`` is computed more than once for any ``e`` in the finite field. Instead, we compute all possible - multiples of ``B[j]`` and re-use this data in the inner loop. + multiples of ``B[j]`` and reuse this data in the inner loop. This is what is called a "Newton-John" table in M4RIE. INPUT: @@ -1125,7 +1125,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): """ mzed_col_swap(self._entries, col1, col2) - def augment(self, Matrix_gf2e_dense right): + def augment(self, right): """ Augments ``self`` with ``right``. @@ -1167,21 +1167,50 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: N = Matrix(K, 0, 1, 0) sage: M.augment(N) [] + + sage: A = matrix(K, 3, range(12)) + sage: B = vector(QQ, [2,5/7,1.2]) # see issue: 38448 + sage: A.augment(B).ncols() + 5 + + sage: B = vector([]) + sage: A.augment(B) == A + True """ + cdef Matrix_gf2e_dense _right cdef Matrix_gf2e_dense A - if self._nrows != right._nrows: + if not isinstance(right, Matrix_gf2e_dense): + # See issue: #36761 - Allow Vectors to be augmented + if hasattr(right, '_vector_'): + rsize = len(right) + if rsize==0: + return self.__copy__() + if self._nrows != rsize: + raise TypeError("Both numbers of rows must match.") + if self.base_ring() is not right.base_ring(): + right = right.change_ring(self.base_ring()) + from sage.matrix.matrix_space import MatrixSpace + M = MatrixSpace(self.base_ring(), nrows=rsize, ncols=1) + _right = (M(right)) + else: + raise TypeError("a matrix must be augmented with another matrix, " + "or a vector") + else: + _right = right + + if self._nrows != _right._nrows: raise TypeError("Both numbers of rows must match.") if self._ncols == 0: - return right.__copy__() - if right._ncols == 0: + return _right.__copy__() + if _right._ncols == 0: return self.__copy__() - A = self.new_matrix(ncols = self._ncols + right._ncols) + A = self.new_matrix(ncols = self._ncols + _right._ncols) if self._nrows == 0: return A - A._entries = mzed_concat(A._entries, self._entries, right._entries) + A._entries = mzed_concat(A._entries, self._entries, _right._entries) return A cdef _stack_impl(self, bottom): diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index e561c459e4a..c0e0a1fff44 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -3297,7 +3297,7 @@ cdef class Matrix_integer_dense(Matrix_dense): else: return R - def is_LLL_reduced(self, delta=None, eta=None): + def is_LLL_reduced(self, delta=None, eta=None, algorithm='fpLLL'): r""" Return ``True`` if this lattice is `(\delta, \eta)`-LLL reduced. See ``self.LLL`` for a definition of LLL reduction. @@ -3308,6 +3308,8 @@ cdef class Matrix_integer_dense(Matrix_dense): - ``eta`` -- (default: `0.501`) parameter `\eta` as described above + - ``algorithm`` -- either ``'fpLLL'`` (default) or ``'sage'`` + EXAMPLES:: sage: A = random_matrix(ZZ, 10, 10) @@ -3316,6 +3318,15 @@ cdef class Matrix_integer_dense(Matrix_dense): False sage: L.is_LLL_reduced() True + + The ``'sage'`` algorithm currently does not work for matrices with + linearly dependent rows:: + + sage: A = matrix(ZZ, [[1, 2, 3], [2, 4, 6]]) + sage: A.is_LLL_reduced(algorithm='sage') + Traceback (most recent call last): + ... + ValueError: linearly dependent input for module version of Gram-Schmidt """ if eta is None: eta = 0.501 @@ -3330,20 +3341,27 @@ cdef class Matrix_integer_dense(Matrix_dense): if eta < 0.5: raise TypeError("eta must be >= 0.5") - # this is pretty slow - import sage.modules.misc - G, mu = sage.modules.misc.gram_schmidt(self.rows()) - #For any $i>j$, we have $|mu_{i, j}| <= \eta$ - for e in mu.list(): - if e.abs() > eta: - return False - - #For any $ij$, we have $|mu_{i, j}| <= \eta$ + for e in mu.list(): + if e.abs() > eta: + return False + + # For any $iu)*T_rows[i][k] + (v)*T_rows[j][k])%R @@ -5697,12 +5715,12 @@ cdef class Matrix_integer_dense(Matrix_dense): [4|1] [3|0] """ - nr , nc = (self._nrows, self._ncols) + nr, nc = self._nrows, self._ncols cdef Matrix_integer_dense A A = self._new(nc,nr) - cdef Py_ssize_t i,j - cdef Py_ssize_t ri,rj # reversed i and j + cdef Py_ssize_t i, j + cdef Py_ssize_t ri, rj # reversed i and j sig_on() ri = nr for i from 0 <= i < nr: @@ -5710,7 +5728,8 @@ cdef class Matrix_integer_dense(Matrix_dense): ri = ri-1 for j from 0 <= j < nc: rj = rj-1 - fmpz_init_set(fmpz_mat_entry(A._matrix,rj,ri),fmpz_mat_entry(self._matrix,i,j)) + fmpz_init_set(fmpz_mat_entry(A._matrix, rj, ri), + fmpz_mat_entry(self._matrix, i, j)) sig_off() if self._subdivisions is not None: diff --git a/src/sage/matrix/matrix_integer_dense_saturation.py b/src/sage/matrix/matrix_integer_dense_saturation.py index b537b041e08..fdd812597f5 100644 --- a/src/sage/matrix/matrix_integer_dense_saturation.py +++ b/src/sage/matrix/matrix_integer_dense_saturation.py @@ -68,6 +68,7 @@ def p_saturation(A, p, proof=True): H = H.stack(C).hermite_form(include_zero_rows=False, proof=proof) verbose("done saturating", tm) + def random_sublist_of_size(k, n): """ INPUT: @@ -181,6 +182,7 @@ def solve_system_with_difficult_last_row(B, A): verbose("Done getting linear combinations.", tm) return X + def saturation(A, proof=True, p=0, max_dets=5): r""" Compute a saturation matrix of `A`. @@ -283,6 +285,7 @@ def saturation(A, proof=True, p=0, max_dets=5): C = solve_system_with_difficult_last_row(B, A) return C.change_ring(ZZ)._insert_zero_columns(zero_cols) + def index_in_saturation(A, proof=True): r""" The index of A in its saturation. diff --git a/src/sage/matrix/matrix_mod2_dense.pxd b/src/sage/matrix/matrix_mod2_dense.pxd index ea3575ef3be..8b9965f89d8 100644 --- a/src/sage/matrix/matrix_mod2_dense.pxd +++ b/src/sage/matrix/matrix_mod2_dense.pxd @@ -1,4 +1,4 @@ -from .matrix_dense cimport Matrix_dense +from sage.matrix.matrix_dense cimport Matrix_dense from sage.libs.m4ri cimport * cdef class Matrix_mod2_dense(Matrix_dense): diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index ebf1bdeb7e9..36830da0549 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -854,7 +854,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): A.subdivide(*self.subdivisions()) return A - cpdef _add_(self, right): r""" Add two dense matrices over `\Z/n\Z`. @@ -898,7 +897,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sig_off() return M - cpdef _sub_(self, right): r""" Subtract two dense matrices over `\Z/n\Z`. @@ -1404,7 +1402,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): self.cache(cache_key, g) return g - def minpoly(self, var='x', algorithm='linbox', proof=None): """ Return the minimal polynomial of ``self``. diff --git a/src/sage/matrix/matrix_mpolynomial_dense.pyx b/src/sage/matrix/matrix_mpolynomial_dense.pyx index 66462800f30..a85918a003f 100644 --- a/src/sage/matrix/matrix_mpolynomial_dense.pyx +++ b/src/sage/matrix/matrix_mpolynomial_dense.pyx @@ -386,12 +386,12 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): nr,nc = self.nrows(),self.ncols() F = self.base_ring().base_ring() - cdef Matrix d = matrix(F,nr,nc) + cdef Matrix d = matrix(F, nr, nc) start_row = 0 for r from 0 <= r < nr: for c from 0 <= c < nc: - p = self.get_unsafe(r,c) + p = self.get_unsafe(r, c) if p.is_constant(): d.set_unsafe(r, c, p.constant_coefficient()) @@ -402,13 +402,13 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): r = rc break if r!=-1: - a_inverse = ~self.get_unsafe(r,c) - self.rescale_row_c(r, a_inverse , c) + a_inverse = ~self.get_unsafe(r, c) + self.rescale_row_c(r, a_inverse, c) self.swap_rows_c(r, start_row) for i from 0 <= i < nr: if i != start_row: - minus_b = -self.get_unsafe(i,c) + minus_b = -self.get_unsafe(i, c) self.add_multiple_of_row(i, start_row, minus_b, 0) start_row +=1 @@ -416,10 +416,10 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): d = d._parent(0) for i from start_row <= i < nr: for j from c+1 <= j < nc: - if self.get_unsafe(i,j).is_constant(): - d.set_unsafe(i,j, self.get_unsafe(i,j).constant_coefficient()) + if self.get_unsafe(i, j).is_constant(): + d.set_unsafe(i, j, self.get_unsafe(i, j).constant_coefficient()) - self.cache('in_echelon_form_row_reduction',True) + self.cache('in_echelon_form_row_reduction', True) def swapped_columns(self): """ diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 6d01a42a126..5b3c56ae573 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -2845,7 +2845,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): with the required degree property In the latter case (rank-deficient or strictly fewer rows than columns, - with no solution to `A = XB`), there might stil be a quotient and + with no solution to `A = XB`), there might still be a quotient and remainder, in which case this method will find it via normal form computation:: diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index ee22058d4fe..533f6b55c67 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -2955,6 +2955,9 @@ cdef class Matrix_rational_dense(Matrix_dense): [ 1/28 -1/40 -1/18] [ 1/28 -1/40 1/18] [ 0 -3/40 0] + sage: L, U = A.LLL(transformation=True) + sage: U * A == L + True sage: A = random_matrix(QQ, 10, 10) sage: d = lcm(a.denom() for a in A.list()) @@ -2962,6 +2965,9 @@ cdef class Matrix_rational_dense(Matrix_dense): True """ A, d = self._clear_denom() + if kwargs.get('transformation', False): + L, U = A.LLL(*args, **kwargs) + return L / d, U return A.LLL(*args, **kwargs) / d def is_LLL_reduced(self, delta=None, eta=None): diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 431cf6f5bd9..2ba59292d54 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -383,7 +383,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): return Matrix_generic_dense if implementation == 'gap': - from .matrix_gap import Matrix_gap + from sage.matrix.matrix_gap import Matrix_gap return Matrix_gap raise ValueError("unknown matrix implementation %r over %r" % (implementation, R)) @@ -2664,6 +2664,7 @@ def _from_dict(self, d, coerce=True, remove_zeros=True): """ return self.element_class(self, d, coerce=coerce) + def dict_to_list(entries, nrows, ncols): r""" Given a dictionary of coordinate tuples, return the list given by diff --git a/src/sage/matrix/matrix_sparse.pxd b/src/sage/matrix/matrix_sparse.pxd index 7546645a6c9..7d8878ea4d8 100644 --- a/src/sage/matrix/matrix_sparse.pxd +++ b/src/sage/matrix/matrix_sparse.pxd @@ -1,4 +1,4 @@ -from .matrix cimport Matrix +from sage.matrix.matrix cimport Matrix cdef class Matrix_sparse(Matrix): pass diff --git a/src/sage/matrix/meson.build b/src/sage/matrix/meson.build new file mode 100644 index 00000000000..c0841d77f34 --- /dev/null +++ b/src/sage/matrix/meson.build @@ -0,0 +1,199 @@ +iml = cc.find_library('iml') + + +py.install_sources( + 'action.pxd', + 'all.py', + 'all__sagemath_meataxe.py', + 'args.pxd', + 'benchmark.py', + 'berlekamp_massey.py', + 'compute_J_ideal.py', + 'docs.py', + 'matrix.pxd', + 'matrix0.pxd', + 'matrix1.pxd', + 'matrix2.pxd', + 'matrix_cdv.pxd', + 'matrix_complex_ball_dense.pxd', + 'matrix_complex_double_dense.pxd', + 'matrix_cyclo_dense.pxd', + 'matrix_dense.pxd', + 'matrix_domain_dense.pxd', + 'matrix_domain_sparse.pxd', + 'matrix_double_dense.pxd', + 'matrix_double_sparse.pxd', + 'matrix_gap.pxd', + 'matrix_generic_dense.pxd', + 'matrix_generic_sparse.pxd', + 'matrix_gf2e_dense.pxd', + 'matrix_gfpn_dense.pxd', + 'matrix_integer_dense.pxd', + 'matrix_integer_dense_hnf.py', + 'matrix_integer_dense_saturation.py', + 'matrix_integer_sparse.pxd', + 'matrix_laurent_mpolynomial_dense.pxd', + 'matrix_misc.py', + 'matrix_mod2_dense.pxd', + 'matrix_modn_dense_double.pxd', + 'matrix_modn_dense_float.pxd', + 'matrix_modn_sparse.pxd', + 'matrix_mpolynomial_dense.pxd', + 'matrix_numpy_dense.pxd', + 'matrix_numpy_integer_dense.pxd', + 'matrix_polynomial_dense.pxd', + 'matrix_rational_dense.pxd', + 'matrix_rational_sparse.pxd', + 'matrix_real_double_dense.pxd', + 'matrix_space.py', + 'matrix_sparse.pxd', + 'matrix_symbolic_dense.pxd', + 'matrix_symbolic_sparse.pxd', + 'matrix_window.pxd', + 'operation_table.py', + 'special.py', + 'symplectic_basis.py', + 'template.pxd', + 'tests.py', + subdir: 'sage/matrix', +) + +extension_data = { + 'action' : files('action.pyx'), + 'args' : files('args.pyx'), + 'change_ring' : files('change_ring.pyx'), + 'constructor' : files('constructor.pyx'), + 'echelon_matrix' : files('echelon_matrix.pyx'), + 'matrix0' : files('matrix0.pyx'), + 'matrix1' : files('matrix1.pyx'), + 'matrix2' : files('matrix2.pyx'), + 'matrix_cdv' : files('matrix_cdv.pyx'), + 'matrix_complex_ball_dense' : files('matrix_complex_ball_dense.pyx'), + 'matrix_complex_double_dense' : files('matrix_complex_double_dense.pyx'), + 'matrix_dense' : files('matrix_dense.pyx'), + 'matrix_double_dense' : files('matrix_double_dense.pyx'), + 'matrix_double_sparse' : files('matrix_double_sparse.pyx'), + 'matrix_gap' : files('matrix_gap.pyx'), + 'matrix_generic_dense' : files('matrix_generic_dense.pyx'), + 'matrix_generic_sparse' : files('matrix_generic_sparse.pyx'), + 'matrix_gfpn_dense' : files('matrix_gfpn_dense.pyx'), + 'matrix_laurent_mpolynomial_dense' : files( + 'matrix_laurent_mpolynomial_dense.pyx', + ), + 'matrix_numpy_dense' : files('matrix_numpy_dense.pyx'), + 'matrix_numpy_integer_dense' : files('matrix_numpy_integer_dense.pyx'), + 'matrix_polynomial_dense' : files('matrix_polynomial_dense.pyx'), + 'matrix_rational_sparse' : files('matrix_rational_sparse.pyx'), + 'matrix_real_double_dense' : files('matrix_real_double_dense.pyx'), + 'matrix_sparse' : files('matrix_sparse.pyx'), + 'matrix_symbolic_dense' : files('matrix_symbolic_dense.pyx'), + 'matrix_symbolic_sparse' : files('matrix_symbolic_sparse.pyx'), + 'matrix_window' : files('matrix_window.pyx'), + 'misc' : files('misc.pyx'), + 'misc_flint' : files('misc_flint.pyx'), + 'misc_mpfr' : files('misc_mpfr.pyx'), + 'strassen' : files('strassen.pyx'), +} + +foreach name, pyx : extension_data + dependencies = [ + py_dep, + blas, + cypari2, + cysignals, + fflas, + flint, + gd, + givaro, + gmp, + gmpxx, + iml, + linbox, + m, + m4ri, + m4rie, + mpfi, + mpfr, + ntl, + pari, + png, + zlib, + ] + if name == 'matrix_gfpn_dense' + dependencies += [mtx, meataxe] + endif + + py.extension_module( + name, + sources: pyx, + subdir: 'sage/matrix', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: dependencies, + ) +endforeach + +extension_data_cpp = { + 'matrix_cyclo_dense': files('matrix_cyclo_dense.pyx'), + 'matrix_gf2e_dense': files('matrix_gf2e_dense.pyx'), + 'matrix_integer_dense': files('matrix_integer_dense.pyx'), + 'matrix_integer_sparse': files('matrix_integer_sparse.pyx'), + 'matrix_mod2_dense': files('matrix_mod2_dense.pyx'), + 'matrix_modn_dense_double': files('matrix_modn_dense_double.pyx'), + 'matrix_modn_dense_float': files('matrix_modn_dense_float.pyx'), + 'matrix_modn_sparse': files('matrix_modn_sparse.pyx'), + 'matrix_mpolynomial_dense': files('matrix_mpolynomial_dense.pyx'), + 'matrix_rational_dense': files('matrix_rational_dense.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/matrix', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: [ + py_dep, + blas, + cypari2, + cysignals, + fflas, + flint, + gd, + givaro, + gmp, + gmpxx, + iml, + linbox, + m, + m4ri, + m4rie, + mpfi, + mpfr, + ntl, + pari, + png, + singular, + zlib, + ], + ) +endforeach + diff --git a/src/sage/matrix/misc.pyx b/src/sage/matrix/misc.pyx index 17277336e3c..de0afe6c9e7 100644 --- a/src/sage/matrix/misc.pyx +++ b/src/sage/matrix/misc.pyx @@ -209,7 +209,7 @@ def matrix_rational_echelon_form_multimodular(Matrix self, height_guess=None, pr from sage.structure.proof.proof import get_flag proof = get_flag(proof, "linear_algebra") - verbose("Multimodular echelon algorithm on %s x %s matrix"%(self._nrows, self._ncols), caller_name="multimod echelon") + verbose("Multimodular echelon algorithm on %s x %s matrix" % (self._nrows, self._ncols), caller_name="multimod echelon") cdef Matrix E if self._nrows == 0 or self._ncols == 0: return self, () @@ -219,7 +219,7 @@ def matrix_rational_echelon_form_multimodular(Matrix self, height_guess=None, pr height = self.height() if height_guess is None: height_guess = 10000000*(height+100) - tm = verbose("height_guess = %s"%height_guess, level=2, caller_name="multimod echelon") + tm = verbose("height_guess = %s" % height_guess, level=2, caller_name="multimod echelon") if proof: M = self._ncols * height_guess * height + 1 @@ -244,7 +244,7 @@ def matrix_rational_echelon_form_multimodular(Matrix self, height_guess=None, pr problem = problem + 1 if problem > 50: verbose("echelon multi-modular possibly not converging?", caller_name="multimod echelon") - t = verbose("echelon modulo p=%s (%.2f%% done)"%( + t = verbose("echelon modulo p=%s (%.2f%% done)" % ( p, 100*float(len(str(prod))) / len(str(M))), level=2, caller_name="multimod echelon") # We use denoms=False, since we made self integral by calling clear_denom above. diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 421ba420347..89943f90b3b 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -576,8 +576,7 @@ def _name_maker(self, names): width = 0 for e in self._elts: estr = repr(e) - if len(estr) > width: - width = len(estr) + width = max(len(estr), width) name_list.append(estr) elif isinstance(names, list): if len(names) != self._n: @@ -588,8 +587,7 @@ def _name_maker(self, names): if not isinstance(name, str): raise ValueError( 'list of element names must only contain strings, not %s' % name) - if len(name) > width: - width = len(name) + width = max(len(name), width) name_list.append(name) else: raise ValueError( diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index 56c4bf183ad..978773e3a27 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -1,19 +1,19 @@ """ Generic Asymptotically Fast Strassen Algorithms -Sage implements asymptotically fast echelon form and matrix +This implements asymptotically fast echelon form and matrix multiplication algorithms. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2005, 2006 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.matrix.matrix_window cimport MatrixWindow @@ -27,11 +27,13 @@ def strassen_window_multiply(C, A,B, cutoff): multiplied, and that C is the correct size to receive the product, and that they are all defined over the same ring. - Uses strassen multiplication at high levels and then uses - MatrixWindow methods at low levels. EXAMPLES: The following matrix + Uses Strassen multiplication at high levels and then uses + MatrixWindow methods at low levels. + + EXAMPLES: The following matrix dimensions are chosen especially to exercise the eight possible parity combinations that could occur while subdividing the matrix - in the strassen recursion. The base case in both cases will be a + in the Strassen recursion. The base case in both cases will be a (4x5) matrix times a (5x6) matrix. :: @@ -96,9 +98,9 @@ cdef strassen_window_multiply_c(MatrixWindow C, MatrixWindow A, # "Memory efficient scheduling of Strassen-Winograd's matrix multiplication algorithm", # Table 1). - cdef MatrixWindow S0, S1, S2, S3, T0, T1 ,T2, T3, P0, P1, P2, P3, P4, P5, P6, U0, U1, U2, U3, U4, U5, U6 + cdef MatrixWindow S0, S1, S2, S3, T0, T1, T2, T3, P0, P1, P2, P3, P4, P5, P6, U0, U1, U2, U3, U4, U5, U6 cdef MatrixWindow X, Y - X = A.new_empty_window(A_sub_nrows, max(A_sub_ncols,B_sub_ncols)) + X = A.new_empty_window(A_sub_nrows, max(A_sub_ncols, B_sub_ncols)) Y = B.new_empty_window(A_sub_ncols, B_sub_ncols) # 1 S2 = A00-A10 in X @@ -110,7 +112,7 @@ cdef strassen_window_multiply_c(MatrixWindow C, MatrixWindow A, T2.set_to_diff(B11, B01) # 3 P6 = S2*T2 in C10 - P6 = C.matrix_window(A_sub_nrows, 0, A_sub_nrows, B_sub_ncols) + P6 = C.matrix_window(A_sub_nrows, 0, A_sub_nrows, B_sub_ncols) if have_cutoff: P6.set_to_prod(S2, T2) else: @@ -248,7 +250,9 @@ cdef subtract_strassen_product(MatrixWindow result, MatrixWindow A, MatrixWindow def strassen_echelon(MatrixWindow A, cutoff): """ Compute echelon form, in place. Internal function, call with - M.echelonize(algorithm='strassen') Based on work of Robert Bradshaw + M.echelonize(algorithm='strassen') + + Based on work of Robert Bradshaw and David Harvey at MSRI workshop in 2006. INPUT: @@ -324,13 +328,13 @@ cdef strassen_echelon_c(MatrixWindow A, Py_ssize_t cutoff, Py_ssize_t mul_cutoff nrows = A.nrows() ncols = A.ncols() - if (nrows <= cutoff or ncols <= cutoff): + if nrows <= cutoff or ncols <= cutoff: return list(A.echelon_in_place()) cdef Py_ssize_t top_h, bottom_cut, bottom_h, bottom_start, top_cut cdef Py_ssize_t prev_pivot_count cdef Py_ssize_t split - split = nrows / 2 + split = nrows // 2 cdef MatrixWindow top, bottom, top_left, top_right, bottom_left, bottom_right, clear @@ -485,7 +489,7 @@ class int_range: Repetitions are not considered. - Useful class for dealing with pivots in the strassen echelon, could + Useful class for dealing with pivots in the Strassen echelon, could have much more general application INPUT: @@ -555,7 +559,7 @@ class int_range: if indices is None: self._intervals = [] return - elif range is not None: + if range is not None: self._intervals = [(int(indices), int(range))] else: self._intervals = [] @@ -629,11 +633,8 @@ class int_range: sage: I.to_list() [1, 2, 3] """ - all = [] - for iv in self._intervals: - for i in range(iv[0], iv[0]+iv[1]): - all.append(i) - return all + return [i for iv0, iv1 in self._intervals + for i in range(iv0, iv0 + iv1)] def __iter__(self): r""" @@ -676,10 +677,7 @@ class int_range: sage: len(I) 3 """ - len = 0 - for iv in self._intervals: - len = len + iv[1] - return int(len) + return sum(iv1 for _, iv1 in self._intervals) def __add__(self, right): r""" @@ -705,9 +703,9 @@ class int_range: sage: I + J [(1, 6), (20, 4)] """ - all = self.to_list() - all.extend(right.to_list()) - return int_range(all) + full_list = self.to_list() + full_list.extend(right.to_list()) + return int_range(full_list) def __sub__(self, right): r""" @@ -733,11 +731,9 @@ class int_range: sage: J - I [(6, 1), (20, 4)] """ - all = self.to_list() - for i in right.to_list(): - if i in all: - all.remove(i) - return int_range(all) + right_list = set(right.to_list()) + diff = [i for i in self.to_list() if i not in right_list] + return int_range(diff) def __mul__(self, right): r""" @@ -757,17 +753,16 @@ class int_range: sage: J * I [(4, 2)] """ - intersection = [] - all = self.to_list() - for i in right.to_list(): - if i in all: - intersection.append(i) + self_list = set(self.to_list()) + intersection = [i for i in right.to_list() if i in self_list] return int_range(intersection) # Useful test code: def test(n, m, R, c=2): r""" + Test code for the Strassen algorithm. + INPUT: - ``n`` -- integer @@ -799,56 +794,57 @@ def test(n, m, R, c=2): # below aren't callable now without using Pyrex. -## todo: doc cutoff parameter as soon as I work out what it really means +# todo: doc cutoff parameter as soon as I work out what it really means + +# EXAMPLES: -## EXAMPLES: -## The following matrix dimensions are chosen especially to exercise the -## eight possible parity combinations that could occur while subdividing -## the matrix in the strassen recursion. The base case in both cases will -## be a (4x5) matrix times a (5x6) matrix. +# The following matrix dimensions are chosen especially to exercise the +# eight possible parity combinations that could occur while subdividing +# the matrix in the strassen recursion. The base case in both cases will +# be a (4x5) matrix times a (5x6) matrix. -## TODO -- the doctests below are currently not -## tested/enabled/working -- enable them when linear algebra -## restructing gets going. +# TODO -- the doctests below are currently not +# tested/enabled/working -- enable them when linear algebra +# restructing gets going. -## sage: dim1 = 64; dim2 = 83; dim3 = 101 -## sage: R = MatrixSpace(QQ, dim1, dim2) -## sage: S = MatrixSpace(QQ, dim2, dim3) -## sage: T = MatrixSpace(QQ, dim1, dim3) +# sage: dim1 = 64; dim2 = 83; dim3 = 101 +# sage: R = MatrixSpace(QQ, dim1, dim2) +# sage: S = MatrixSpace(QQ, dim2, dim3) +# sage: T = MatrixSpace(QQ, dim1, dim3) -## sage: A = R.random_element(range(-30, 30)) -## sage: B = S.random_element(range(-30, 30)) -## sage: C = T(0) -## sage: D = T(0) +# sage: A = R.random_element(range(-30, 30)) +# sage: B = S.random_element(range(-30, 30)) +# sage: C = T(0) +# sage: D = T(0) -## sage: A_window = A.matrix_window(0, 0, dim1, dim2) -## sage: B_window = B.matrix_window(0, 0, dim2, dim3) -## sage: C_window = C.matrix_window(0, 0, dim1, dim3) -## sage: D_window = D.matrix_window(0, 0, dim1, dim3) +# sage: A_window = A.matrix_window(0, 0, dim1, dim2) +# sage: B_window = B.matrix_window(0, 0, dim2, dim3) +# sage: C_window = C.matrix_window(0, 0, dim1, dim3) +# sage: D_window = D.matrix_window(0, 0, dim1, dim3) -## sage: from sage.matrix.strassen import strassen_window_multiply -## sage: strassen_window_multiply(C_window, A_window, B_window, 2) # use strassen method -## sage: D_window.set_to_prod(A_window, B_window) # use naive method -## sage: C_window == D_window -## True +# sage: from sage.matrix.strassen import strassen_window_multiply +# sage: strassen_window_multiply(C_window, A_window, B_window, 2) # use strassen method +# sage: D_window.set_to_prod(A_window, B_window) # use naive method +# sage: C_window == D_window +# True -## sage: dim1 = 79; dim2 = 83; dim3 = 101 -## sage: R = MatrixSpace(QQ, dim1, dim2) -## sage: S = MatrixSpace(QQ, dim2, dim3) -## sage: T = MatrixSpace(QQ, dim1, dim3) +# sage: dim1 = 79; dim2 = 83; dim3 = 101 +# sage: R = MatrixSpace(QQ, dim1, dim2) +# sage: S = MatrixSpace(QQ, dim2, dim3) +# sage: T = MatrixSpace(QQ, dim1, dim3) -## sage: A = R.random_element(range(30)) -## sage: B = S.random_element(range(30)) -## sage: C = T(0) -## sage: D = T(0) +# sage: A = R.random_element(range(30)) +# sage: B = S.random_element(range(30)) +# sage: C = T(0) +# sage: D = T(0) -## sage: A_window = A.matrix_window(0, 0, dim1, dim2) -## sage: B_window = B.matrix_window(0, 0, dim2, dim3) -## sage: C_window = C.matrix_window(0, 0, dim1, dim3) +# sage: A_window = A.matrix_window(0, 0, dim1, dim2) +# sage: B_window = B.matrix_window(0, 0, dim2, dim3) +# sage: C_window = C.matrix_window(0, 0, dim1, dim3) -## sage: strassen_window_multiply(C, A, B, 2) # use strassen method -## sage: D.set_to_prod(A, B) # use naive method +# sage: strassen_window_multiply(C, A, B, 2) # use strassen method +# sage: D.set_to_prod(A, B) # use naive method -## sage: C == D -## True +# sage: C == D +# True diff --git a/src/sage/matroids/basis_exchange_matroid.pxd b/src/sage/matroids/basis_exchange_matroid.pxd index dc4b4aeb114..de57ed845a9 100644 --- a/src/sage/matroids/basis_exchange_matroid.pxd +++ b/src/sage/matroids/basis_exchange_matroid.pxd @@ -46,19 +46,19 @@ cdef class BasisExchangeMatroid(Matroid): cpdef _move_current_basis(self, X, Y) cpdef frozenset _max_independent(self, frozenset F) - cpdef int _rank(self, frozenset F) + cpdef int _rank(self, frozenset F) except? -1 cpdef frozenset _circuit(self, frozenset F) cpdef frozenset _fundamental_circuit(self, frozenset B, e) cpdef frozenset _closure(self, frozenset F) cpdef frozenset _max_coindependent(self, frozenset F) - cpdef int _corank(self, frozenset F) + cpdef int _corank(self, frozenset F) noexcept cpdef frozenset _cocircuit(self, frozenset F) cpdef frozenset _fundamental_cocircuit(self, frozenset B, e) cpdef frozenset _coclosure(self, frozenset F) cpdef frozenset _augment(self, frozenset X, frozenset Y) - cpdef bint _is_independent(self, frozenset F) + cpdef bint _is_independent(self, frozenset F) noexcept cpdef list whitney_numbers2(self) cdef _whitney_numbers2_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long rnk) @@ -90,6 +90,6 @@ cdef class BasisExchangeMatroid(Matroid): cpdef _is_isomorphism(self, other, morphism) cdef bint __is_isomorphism(self, BasisExchangeMatroid other, morphism) noexcept - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) cdef bint nxksrd(bitset_s *b, long n, long k, bint succ) noexcept diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index a3c4c897d16..7cf056c4292 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -652,7 +652,7 @@ cdef class BasisExchangeMatroid(Matroid): self.__max_independent(self._output, self._input) return self.__unpack(self._output) - cpdef int _rank(self, frozenset F): + cpdef int _rank(self, frozenset F) except? -1: """ Compute the rank of a subset of the groundset. @@ -796,7 +796,7 @@ cdef class BasisExchangeMatroid(Matroid): self.__max_coindependent(self._output, self._input) return self.__unpack(self._output) - cpdef int _corank(self, frozenset F): + cpdef int _corank(self, frozenset F) noexcept: """ Return the corank of a set. @@ -940,7 +940,7 @@ cdef class BasisExchangeMatroid(Matroid): self.__augment(self._output, self._input, self._input2) return self.__unpack(self._output) - cpdef bint _is_independent(self, frozenset F): + cpdef bint _is_independent(self, frozenset F) noexcept: """ Test if input is independent. @@ -2231,7 +2231,7 @@ cdef class BasisExchangeMatroid(Matroid): return self._characteristic_setsystem()._isomorphism(other._characteristic_setsystem(), PS, PO) is not None - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. @@ -2242,7 +2242,11 @@ cdef class BasisExchangeMatroid(Matroid): * if `X` and `Y` are in `B`, and `x` is in `X - Y`, then there is a `y` in `Y - X` such that `(X - x) + y` is again a member of `B`. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -2251,8 +2255,8 @@ cdef class BasisExchangeMatroid(Matroid): sage: M.is_valid() True sage: M = Matroid(groundset='abcd', bases=['ab', 'cd']) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, {'error': 'exchange axiom failed'}) TESTS: @@ -2278,7 +2282,7 @@ cdef class BasisExchangeMatroid(Matroid): if not bitset_eq(self._current_basis, BB._subsets[pointerY]): # We failed to set the current basis to Y through basis exchanges. # Therefore, the exchange axioms are violated! - return False + return False if not certificate else (False, {"error": "exchange axiom failed"}) bitset_difference(self._input, BB._subsets[pointerX], BB._subsets[pointerY]) bitset_difference(self._input2, BB._subsets[pointerY], BB._subsets[pointerX]) x = bitset_first(self._input) @@ -2292,11 +2296,11 @@ cdef class BasisExchangeMatroid(Matroid): else: y = bitset_next(self._input2, y + 1) if not foundpair: - return False + return False if not certificate else (False, {"error": "exchange axiom failed"}) x = bitset_next(self._input, x + 1) pointerY += 1 pointerX += 1 - return True + return True if not certificate else (True, {}) cdef bint nxksrd(bitset_s* b, long n, long k, bint succ) noexcept: """ diff --git a/src/sage/matroids/basis_matroid.pxd b/src/sage/matroids/basis_matroid.pxd index 5789ad98937..fd240b01a38 100644 --- a/src/sage/matroids/basis_matroid.pxd +++ b/src/sage/matroids/basis_matroid.pxd @@ -15,7 +15,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): cdef reset_current_basis(self) - cpdef bint _is_basis(self, frozenset X) + cpdef bint _is_basis(self, frozenset X) noexcept cpdef bases_count(self) cpdef SetSystem bases(self) diff --git a/src/sage/matroids/basis_matroid.pyx b/src/sage/matroids/basis_matroid.pyx index f9710eaac7c..b74a76af956 100644 --- a/src/sage/matroids/basis_matroid.pyx +++ b/src/sage/matroids/basis_matroid.pyx @@ -298,7 +298,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): # a function that is very efficient for this class - cpdef bint _is_basis(self, frozenset X): + cpdef bint _is_basis(self, frozenset X) noexcept: """ Test if input is a basis. diff --git a/src/sage/matroids/circuit_closures_matroid.pxd b/src/sage/matroids/circuit_closures_matroid.pxd index 878443d86f3..b724a49faf4 100644 --- a/src/sage/matroids/circuit_closures_matroid.pxd +++ b/src/sage/matroids/circuit_closures_matroid.pxd @@ -5,9 +5,9 @@ cdef class CircuitClosuresMatroid(Matroid): cdef dict _circuit_closures # _CC cdef int _matroid_rank # _R cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cpdef full_rank(self) - cpdef bint _is_independent(self, frozenset F) + cpdef bint _is_independent(self, frozenset F) noexcept cpdef frozenset _max_independent(self, frozenset F) cpdef frozenset _circuit(self, frozenset F) cpdef dict circuit_closures(self) diff --git a/src/sage/matroids/circuit_closures_matroid.pyx b/src/sage/matroids/circuit_closures_matroid.pyx index 7dd29ee8143..a4251298ebc 100644 --- a/src/sage/matroids/circuit_closures_matroid.pyx +++ b/src/sage/matroids/circuit_closures_matroid.pyx @@ -179,7 +179,7 @@ cdef class CircuitClosuresMatroid(Matroid): """ return frozenset(self._groundset) - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: """ Return the rank of a set ``X``. @@ -221,7 +221,7 @@ cdef class CircuitClosuresMatroid(Matroid): """ return self._matroid_rank - cpdef bint _is_independent(self, frozenset F): + cpdef bint _is_independent(self, frozenset F) noexcept: """ Test if input is independent. diff --git a/src/sage/matroids/circuits_matroid.pxd b/src/sage/matroids/circuits_matroid.pxd index f7b938a4d6f..d7526ee2712 100644 --- a/src/sage/matroids/circuits_matroid.pxd +++ b/src/sage/matroids/circuits_matroid.pxd @@ -9,9 +9,9 @@ cdef class CircuitsMatroid(Matroid): cdef list _sorted_C_lens cdef bint _nsc_defined cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cpdef full_rank(self) - cpdef bint _is_independent(self, frozenset X) + cpdef bint _is_independent(self, frozenset X) noexcept cpdef frozenset _max_independent(self, frozenset X) cpdef frozenset _circuit(self, frozenset X) cpdef frozenset _closure(self, frozenset X) @@ -27,11 +27,11 @@ cdef class CircuitsMatroid(Matroid): # properties cpdef girth(self) - cpdef bint is_paving(self) + cpdef bint is_paving(self) noexcept # isomorphism and relabeling cpdef _is_isomorphic(self, other, certificate=*) cpdef relabel(self, mapping) # verification - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index ae3be51d36f..cd44db7772b 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -100,7 +100,7 @@ cdef class CircuitsMatroid(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: """ Return the rank of a set ``X``. @@ -140,7 +140,7 @@ cdef class CircuitsMatroid(Matroid): """ return self._matroid_rank - cpdef bint _is_independent(self, frozenset X): + cpdef bint _is_independent(self, frozenset X) noexcept: """ Test if input is independent. @@ -850,7 +850,7 @@ cdef class CircuitsMatroid(Matroid): from sage.rings.infinity import infinity return min(self._k_C, default=infinity) - cpdef bint is_paving(self): + cpdef bint is_paving(self) noexcept: """ Return if ``self`` is paving. @@ -869,13 +869,17 @@ cdef class CircuitsMatroid(Matroid): # verification - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if ``self`` obeys the matroid axioms. For a matroid defined by its circuits, we check the circuit axioms. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -901,8 +905,12 @@ cdef class CircuitsMatroid(Matroid): False sage: C = [[1, 2, 3], [3, 4, 5]] sage: M = Matroid(circuits=C) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, + {'circuit 1': frozenset({...}), + 'circuit 2': frozenset({...}), + 'element': 3, + 'error': 'elimination axiom failed'}) """ from itertools import combinations_with_replacement cdef int i, j @@ -911,7 +919,7 @@ cdef class CircuitsMatroid(Matroid): # loop through all circuit length pairs (i, j) with i <= j for C1 in self._k_C[i]: if not C1: # the empty set can't be a circuit - return False + return False if not certificate else (False, {"error": "the empty set can't be a circuit"}) for C2 in self._k_C[j]: I12 = C1 & C2 if not I12: # C1 and C2 are disjoint; nothing to test @@ -920,10 +928,10 @@ cdef class CircuitsMatroid(Matroid): if len(C1) == len(C2): # they are the same circuit break # C1 < C2; a circuit can't be a subset of another circuit - return False + return False if not certificate else (False, {"error": "a circuit can't be a subset of another circuit", "circuit 1": C1, "circuit 2": C2}) # check circuit elimination axiom U12 = C1 | C2 for e in I12: if self._is_independent(U12 - {e}): - return False - return True + return False if not certificate else (False, {"error": "elimination axiom failed", "circuit 1": C1, "circuit 2": C2, "element": e}) + return True if not certificate else (True, {}) diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index a71674e2987..8548b2bfede 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -815,7 +815,7 @@ def Matroid(groundset=None, data=None, **kwds): key = 'matroid' elif isinstance(data, str): key = 'revlex' - elif isinstance(data, dict) or isinstance(data, FiniteLatticePoset): + elif isinstance(data, (dict, FiniteLatticePoset)): key = 'flats' elif data is None: raise TypeError("no input data given for Matroid()") diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py index b2c6ca5e055..4c5eee2b5c0 100644 --- a/src/sage/matroids/database_matroids.py +++ b/src/sage/matroids/database_matroids.py @@ -1462,7 +1462,7 @@ def K5(groundset='abcdefghij'): Return the graphic matroid `M(K_5)`. `M(K_5)` is an excluded minor for the class of cographic matroids. It is - the `3`-dimensional Desargues configuration. + the `3`-dimensional Desargues configuration. EXAMPLES:: diff --git a/src/sage/matroids/dual_matroid.py b/src/sage/matroids/dual_matroid.py index 76ad7aaa600..d4dce31ddc2 100644 --- a/src/sage/matroids/dual_matroid.py +++ b/src/sage/matroids/dual_matroid.py @@ -545,13 +545,17 @@ def relabel(self, mapping): M = self._matroid.relabel(mapping).dual() return M - def is_valid(self): + def is_valid(self, certificate=False): """ Test if ``self`` obeys the matroid axioms. For a :class:`DualMatroid`, we check its dual. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -564,4 +568,11 @@ def is_valid(self): sage: M.dual().is_valid() False """ + if certificate: + v, c = self._matroid.is_valid(certificate) + if v: + return True, {} + else: + c["error"] = "the dual matroid is not valid: " + c["error"] + return v, c return self._matroid.is_valid() diff --git a/src/sage/matroids/flats_matroid.pxd b/src/sage/matroids/flats_matroid.pxd index 956e30f859d..6eb99792794 100644 --- a/src/sage/matroids/flats_matroid.pxd +++ b/src/sage/matroids/flats_matroid.pxd @@ -8,9 +8,9 @@ cdef class FlatsMatroid(Matroid): cdef object _L # lattice of flats cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cpdef frozenset _closure(self, frozenset X) - cpdef bint _is_closed(self, frozenset X) + cpdef bint _is_closed(self, frozenset X) noexcept cpdef full_rank(self) @@ -24,4 +24,4 @@ cdef class FlatsMatroid(Matroid): cpdef relabel(self, mapping) # verification - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) diff --git a/src/sage/matroids/flats_matroid.pyx b/src/sage/matroids/flats_matroid.pyx index 9c6a3c76aeb..ff1b21fec0c 100644 --- a/src/sage/matroids/flats_matroid.pyx +++ b/src/sage/matroids/flats_matroid.pyx @@ -114,7 +114,7 @@ cdef class FlatsMatroid(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: """ Return the rank of a set ``X``. @@ -191,7 +191,7 @@ cdef class FlatsMatroid(Matroid): if f >= X: return f - cpdef bint _is_closed(self, frozenset X): + cpdef bint _is_closed(self, frozenset X) noexcept: """ Test if input is a closed set. @@ -539,7 +539,7 @@ cdef class FlatsMatroid(Matroid): # verification - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if ``self`` obeys the matroid axioms. @@ -548,7 +548,11 @@ cdef class FlatsMatroid(Matroid): If the lattice of flats has already been computed, we instead perform the equivalent check of whether it forms a geometric lattice. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -579,15 +583,15 @@ cdef class FlatsMatroid(Matroid): True sage: Matroid(flats=['', 'a', 'b', 'ab']).is_valid() True - sage: M = Matroid(flats=['', # missing an extention of flat ['5'] by '6' + sage: M = Matroid(flats=['', # missing an extension of flat ['5'] by '6' ....: '0','1','2','3','4','5','6','7','8','9','a','b','c', ....: '45','46','47','4c','57','5c','67','6c','7c', ....: '048','149','24a','34b','059','15a','25b','358', ....: '06a','16b','268','369','07b','178','279','37a', ....: '0123c','89abc', ....: '0123456789abc']) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, {'error': 'the lattice of flats is not geometric'}) sage: Matroid(matroids.catalog.Fano().lattice_of_flats()).is_valid() True @@ -609,30 +613,37 @@ cdef class FlatsMatroid(Matroid): TESTS:: - sage: Matroid(flats={0: [], 1: [[0], [1]], 2: [[0, 1]]}).is_valid() # missing an intersection - False - sage: Matroid(flats={0: [[]], 2: [[0], [1]], 3: [[0, 1]]}).is_valid() # invalid ranks - False - sage: Matroid(flats={0: [[]], 1: [[0], [1]], 2: [[0], [0, 1]]}).is_valid() # duplicates - False - sage: Matroid(flats={0: [[]], 1: [[0], [1], [0, 1]]}).is_valid() - False - sage: Matroid(flats={0: [[]], 1: [[0, 1], [2]], 2: [[0], [1], [0, 1, 2]]}).is_valid() - False - sage: M = Matroid(flats={0: [''], # missing an extention of flat ['5'] by '6' + sage: Matroid(flats={0: [], 1: [[0], [1]], 2: [[0, 1]]}).is_valid(certificate=True) # missing an intersection + (False, {'error': 'flats dictionary has invalid ranks'}) + sage: Matroid(flats={0: [[]], 2: [[0], [1]], 3: [[0, 1]]}).is_valid(certificate=True) # invalid ranks + (False, {'error': 'flats dictionary has invalid ranks'}) + sage: Matroid(flats={0: [[]], 1: [[0], [1]], 2: [[0], [0, 1]]}).is_valid(certificate=True) # duplicates + (False, {'error': 'flats dictionary has repeated flats'}) + sage: Matroid(flats={0: [[]], 1: [[0], [1], [0, 1]]}).is_valid(certificate=True) + (False, + {'error': 'a single element extension of a flat must be a subset of exactly one flat', + 'flat': frozenset()}) + sage: Matroid(flats={0: [[]], 1: [[0, 1], [2]], 2: [[0], [1], [0, 1, 2]]}).is_valid(certificate=True) + (False, + {'error': 'the intersection of two flats must be a flat', + 'flat 1': frozenset({0, 1}), + 'flat 2': frozenset({1})}) + sage: M = Matroid(flats={0: [''], # missing an extension of flat ['5'] by '6' ....: 1: ['0','1','2','3','4','5','6','7','8','9','a','b','c'], ....: 2: ['45','46','47','4c','57','5c','67','6c','7c', ....: '048','149','24a','34b','059','15a','25b','358', ....: '06a','16b','268','369','07b','178','279','37a', ....: '0123c','89abc'], ....: 3: ['0123456789abc']}) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, + {'error': 'a single element extension of a flat must be a subset of exactly one flat', + 'flat': frozenset({'...'})}) sage: M = Matroid(flats=[[], [0], [1], [0], [0, 1]]) # duplicates are ignored sage: M.lattice_of_flats() Finite lattice containing 4 elements - sage: M.is_valid() - True + sage: M.is_valid(certificate=True) + (True, {}) sage: M = Matroid(flats=['', ....: '0','1','2','3','4','5','6','7','8','9','a','b','c', ....: '45','46','47','4c','56','57','5c','67','6c','7c', @@ -640,10 +651,16 @@ cdef class FlatsMatroid(Matroid): ....: '06a','16b','268','369','07b','178','279','37a', ....: '0123c','89abc', ....: '0123456789abc']) - sage: M.is_valid() - True + sage: M.is_valid(certificate=True) + (True, {}) """ if self._L is not None: # if the lattice of flats is available + if certificate: + if not self._is_closed(self._groundset): + return False, {"error": "the groundset must be a flat"} + if not self._L.is_geometric(): + return False, {"error": "the lattice of flats is not geometric"} + return True, {} return self._is_closed(self._groundset) and self._L.is_geometric() cdef int i, j, k @@ -654,14 +671,14 @@ cdef class FlatsMatroid(Matroid): # check flats dictionary for invalid ranks and repeated flats ranks = list(self._F) if ranks != list(range(len(ranks))): - return False + return False if not certificate else (False, {"error": "flats dictionary has invalid ranks"}) flats_lst = [F for i in self._F for F in self._F[i]] if len(flats_lst) != len(set(flats_lst)): - return False + return False if not certificate else (False, {"error": "flats dictionary has repeated flats"}) # the groundset must be a flat if not self._is_closed(self._groundset): - return False + return False if not certificate else (False, {"error": "the groundset must be a flat"}) # a single element extension of a flat must be a subset of exactly one flat for i in ranks[:-1]: @@ -671,7 +688,7 @@ cdef class FlatsMatroid(Matroid): if F2 >= F1: cover.extend(F1 ^ F2) if len(cover) != len(F1 ^ self._groundset) or set(cover) != F1 ^ self._groundset: - return False + return False if not certificate else (False, {"error": "a single element extension of a flat must be a subset of exactly one flat", "flat": F1}) # the intersection of two flats must be a flat for i in ranks: @@ -688,6 +705,6 @@ cdef class FlatsMatroid(Matroid): if flag: break if not flag: - return False + return False if not certificate else (False, {"error": "the intersection of two flats must be a flat", "flat 1": F1, "flat 2": F2}) - return True + return True if not certificate else (True, {}) diff --git a/src/sage/matroids/graphic_matroid.pxd b/src/sage/matroids/graphic_matroid.pxd index 8ec8dd9e8b0..b9902e967d6 100644 --- a/src/sage/matroids/graphic_matroid.pxd +++ b/src/sage/matroids/graphic_matroid.pxd @@ -7,23 +7,23 @@ cdef class GraphicMatroid(Matroid): cdef dict _vertex_map cdef dict _groundset_edge_map cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cpdef _vertex_stars(self) cpdef _minor(self, contractions, deletions) cpdef _has_minor(self, N, bint certificate=*) - cpdef int _corank(self, frozenset X) - cpdef bint _is_circuit(self, frozenset X) + cpdef int _corank(self, frozenset X) noexcept + cpdef bint _is_circuit(self, frozenset X) noexcept cpdef frozenset _closure(self, frozenset X) cpdef frozenset _max_independent(self, frozenset X) cpdef frozenset _max_coindependent(self, frozenset X) cpdef frozenset _circuit(self, frozenset X) cpdef frozenset _coclosure(self, frozenset X) - cpdef bint _is_closed(self, frozenset X) + cpdef bint _is_closed(self, frozenset X) noexcept cpdef _is_isomorphic(self, other, certificate=*) cpdef _isomorphism(self, other) - cpdef bint is_valid(self) - cpdef bint is_graphic(self) - cpdef bint is_regular(self) + cpdef is_valid(self, certificate=*) + cpdef bint is_graphic(self) noexcept + cpdef bint is_regular(self) noexcept cpdef graph(self) cpdef vertex_map(self) cpdef list groundset_to_edges(self, X) diff --git a/src/sage/matroids/graphic_matroid.pyx b/src/sage/matroids/graphic_matroid.pyx index a01cef13fe3..53bd8adef39 100644 --- a/src/sage/matroids/graphic_matroid.pyx +++ b/src/sage/matroids/graphic_matroid.pyx @@ -238,7 +238,7 @@ cdef class GraphicMatroid(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: """ Return the rank of a set ``X``. @@ -625,7 +625,7 @@ cdef class GraphicMatroid(Matroid): N = N.regular_matroid() return M._has_minor(N, certificate=certificate) - cpdef int _corank(self, frozenset X): + cpdef int _corank(self, frozenset X) noexcept: """ Return the corank of the set `X` in the matroid. @@ -653,7 +653,7 @@ cdef class GraphicMatroid(Matroid): DS_vertices.union(u, v) return len(X) - (DS_vertices.number_of_subsets() - Integer(1)) - cpdef bint _is_circuit(self, frozenset X): + cpdef bint _is_circuit(self, frozenset X) noexcept: """ Test if input is a circuit. @@ -918,7 +918,7 @@ cdef class GraphicMatroid(Matroid): g.add_edge(e) return frozenset(XX) - cpdef bint _is_closed(self, frozenset X): + cpdef bint _is_closed(self, frozenset X) noexcept: """ Test if input is a closed set. @@ -1093,13 +1093,17 @@ cdef class GraphicMatroid(Matroid): """ return self.is_isomorphic(other, certificate=True)[1] - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): """ Test if the data obey the matroid axioms. Since a graph is used for the data, this is always the case. - OUTPUT: ``True`` + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: ``True``, or ``(True, {})`` EXAMPLES:: @@ -1108,9 +1112,9 @@ cdef class GraphicMatroid(Matroid): sage: M.is_valid() True """ - return True + return True if not certificate else (True, {}) - cpdef bint is_graphic(self): + cpdef bint is_graphic(self) noexcept: r""" Return if ``self`` is graphic. @@ -1124,7 +1128,7 @@ cdef class GraphicMatroid(Matroid): """ return True - cpdef bint is_regular(self): + cpdef bint is_regular(self) noexcept: r""" Return if ``self`` is regular. diff --git a/src/sage/matroids/linear_matroid.pxd b/src/sage/matroids/linear_matroid.pxd index 085a68be0f6..1bff3fc87dc 100644 --- a/src/sage/matroids/linear_matroid.pxd +++ b/src/sage/matroids/linear_matroid.pxd @@ -62,7 +62,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): cpdef _is_3connected_shifting(self, certificate=*) cpdef _is_4connected_shifting(self, certificate=*) - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) cdef class BinaryMatroid(LinearMatroid): cdef tuple _b_invariant, _b_partition @@ -91,8 +91,8 @@ cdef class BinaryMatroid(LinearMatroid): cpdef _fast_isom_test(self, other) cpdef relabel(self, mapping) - cpdef bint is_graphic(self) - cpdef bint is_valid(self) + cpdef bint is_graphic(self) noexcept + cpdef is_valid(self, certificate=*) cdef class TernaryMatroid(LinearMatroid): @@ -122,7 +122,7 @@ cdef class TernaryMatroid(LinearMatroid): cpdef _fast_isom_test(self, other) cpdef relabel(self, mapping) - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) cdef class QuaternaryMatroid(LinearMatroid): cdef object _x_zero, _x_one @@ -149,7 +149,7 @@ cdef class QuaternaryMatroid(LinearMatroid): cpdef _fast_isom_test(self, other) cpdef relabel(self, mapping) - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) cdef class RegularMatroid(LinearMatroid): cdef _bases_count, _r_invariant @@ -172,6 +172,6 @@ cdef class RegularMatroid(LinearMatroid): cpdef has_line_minor(self, k, hyperlines=*, certificate=*) cpdef _linear_extension_chains(self, F, fundamentals=*) - cpdef bint is_regular(self) - cpdef bint is_graphic(self) - cpdef bint is_valid(self) + cpdef bint is_regular(self) noexcept + cpdef bint is_graphic(self) noexcept + cpdef is_valid(self, certificate=*) diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 0310b8c2cd3..f180976979d 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -2614,13 +2614,19 @@ cdef class LinearMatroid(BasisExchangeMatroid): cochains = self.linear_coextension_cochains(F, cosimple=cosimple, fundamentals=fundamentals) return self._linear_coextensions(element, cochains) - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data represent an actual matroid. Since this matroid is linear, we test the representation matrix. - OUTPUT: + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) + + The boolean output value is: - ``True`` if the matrix is over a field. - ``True`` if the matrix is over a ring and all cross ratios are @@ -2649,19 +2655,19 @@ cdef class LinearMatroid(BasisExchangeMatroid): sage: from sage.matroids.advanced import * # LinearMatroid sage: M = LinearMatroid(ring=ZZ, reduced_matrix=Matrix(ZZ, ....: [[1, 0, 1], [1, 1, 0], [0, 1, 1]])) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, {'error': 'not all cross ratios are invertible'}) """ if self.base_ring().is_field(): - return True + return True if not certificate else (True, {}) try: CR = self.cross_ratios() except (ArithmeticError, TypeError, ValueError): - return False + return False if not certificate else (False, {"error": "can't compute cross ratios"}) for x in CR: if not x ** (-1) in self.base_ring(): - return False - return True + return False if not certificate else (False, {"error": "not all cross ratios are invertible"}) + return True if not certificate else (True, {}) # connectivity @@ -3793,7 +3799,7 @@ cdef class BinaryMatroid(LinearMatroid): keep_initial_representation=False) # graphicness test - cpdef bint is_graphic(self): + cpdef bint is_graphic(self) noexcept: """ Test if the binary matroid is graphic. @@ -3862,14 +3868,18 @@ cdef class BinaryMatroid(LinearMatroid): # now self is graphic iff there is a binary vector x so that M*x = 0 and x_0 = 1, so: return BinaryMatroid(m).corank(frozenset([0])) > 0 - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. Since this is a linear matroid over the field `\GF{2}`, this is always the case. - OUTPUT: ``True`` + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: ``True``, or ``(True, {})`` EXAMPLES:: @@ -3877,7 +3887,7 @@ cdef class BinaryMatroid(LinearMatroid): sage: M.is_valid() True """ - return True + return True if not certificate else (True, {}) # representability @@ -4724,14 +4734,18 @@ cdef class TernaryMatroid(LinearMatroid): basis=bas, keep_initial_representation=False) - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. Since this is a linear matroid over the field `\GF{3}`, this is always the case. - OUTPUT: ``True`` + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: ``True``, or ``(True, {})`` EXAMPLES:: @@ -4739,7 +4753,7 @@ cdef class TernaryMatroid(LinearMatroid): sage: M.is_valid() True """ - return True + return True if not certificate else (True, {}) # representability @@ -5488,14 +5502,18 @@ cdef class QuaternaryMatroid(LinearMatroid): basis=bas, keep_initial_representation=False) - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. Since this is a linear matroid over the field `\GF{4}`, this is always the case. - OUTPUT: ``True`` + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: ``True``, or ``(True, {})`` EXAMPLES:: @@ -5503,7 +5521,7 @@ cdef class QuaternaryMatroid(LinearMatroid): sage: M.is_valid() # needs sage.rings.finite_rings True """ - return True + return True if not certificate else (True, {}) def __reduce__(self): """ @@ -6239,7 +6257,7 @@ cdef class RegularMatroid(LinearMatroid): fundamentals = set([1]) return LinearMatroid._linear_extension_chains(self, F, fundamentals) - cpdef bint is_graphic(self): + cpdef bint is_graphic(self) noexcept: """ Test if the regular matroid is graphic. @@ -6270,7 +6288,7 @@ cdef class RegularMatroid(LinearMatroid): """ return BinaryMatroid(reduced_matrix=self._reduced_representation()).is_graphic() - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. @@ -6278,7 +6296,11 @@ cdef class RegularMatroid(LinearMatroid): representation matrix is *totally unimodular*, i.e. if all square submatrices have determinant in `\{-1, 0, 1\}`. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -6287,19 +6309,21 @@ cdef class RegularMatroid(LinearMatroid): ....: [0, 1, 0, 1, 0, 1, 1], ....: [0, 0, 1, 0, 1, 1, 1]]), ....: regular=True, check=False) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, {'error': 'the representation matrix is not totally unimodular'}) sage: M = Matroid(graphs.PetersenGraph()) sage: M.is_valid() True """ M = LinearMatroid(ring=QQ, reduced_matrix=self.representation(self.basis(), True, False)) CR = M.cross_ratios() - return CR.issubset(set([1])) + if CR.issubset(set([1])): + return True if not certificate else (True, {}) + return False if not certificate else (False, {"error": "the representation matrix is not totally unimodular"}) # representation - cpdef bint is_regular(self): + cpdef bint is_regular(self) noexcept: r""" Return if ``self`` is regular. diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index ca197f605b9..bbd025c1ed5 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -9,28 +9,28 @@ cdef class Matroid(SageObject): # virtual methods cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 # internal methods, assuming verified input cpdef frozenset _max_independent(self, frozenset X) cpdef frozenset _circuit(self, frozenset X) cpdef frozenset _fundamental_circuit(self, frozenset B, e) cpdef frozenset _closure(self, frozenset X) - cpdef int _corank(self, frozenset X) + cpdef int _corank(self, frozenset X) noexcept cpdef frozenset _max_coindependent(self, frozenset X) cpdef frozenset _cocircuit(self, frozenset X) cpdef frozenset _fundamental_cocircuit(self, frozenset B, e) cpdef frozenset _coclosure(self, frozenset X) cpdef frozenset _augment(self, frozenset X, frozenset Y) - cpdef bint _is_independent(self, frozenset X) - cpdef bint _is_basis(self, frozenset X) - cpdef bint _is_circuit(self, frozenset X) - cpdef bint _is_closed(self, frozenset X) - cpdef bint _is_coindependent(self, frozenset X) - cpdef bint _is_cobasis(self, frozenset X) - cpdef bint _is_cocircuit(self, frozenset X) - cpdef bint _is_coclosed(self, frozenset X) + cpdef bint _is_independent(self, frozenset X) noexcept + cpdef bint _is_basis(self, frozenset X) noexcept + cpdef bint _is_circuit(self, frozenset X) noexcept + cpdef bint _is_closed(self, frozenset X) noexcept + cpdef bint _is_coindependent(self, frozenset X) noexcept + cpdef bint _is_cobasis(self, frozenset X) noexcept + cpdef bint _is_cocircuit(self, frozenset X) noexcept + cpdef bint _is_coclosed(self, frozenset X) noexcept cpdef _minor(self, contractions, deletions) cpdef _has_minor(self, N, bint certificate=*) @@ -105,7 +105,7 @@ cdef class Matroid(SageObject): cpdef is_coclosed(self, X) # verification - cpdef bint is_valid(self) + cpdef is_valid(self, certificate=*) # enumeration cpdef SetSystem circuits(self, k=*) @@ -184,8 +184,8 @@ cdef class Matroid(SageObject): cpdef _is_3connected_CE(self, certificate=*) cpdef _is_3connected_BC(self, certificate=*) cpdef _is_3connected_BC_recursion(self, basis, fund_cocircuits) - cpdef bint is_paving(self) - cpdef bint is_sparse_paving(self) + cpdef bint is_paving(self) noexcept + cpdef bint is_sparse_paving(self) noexcept cpdef girth(self) # representability @@ -195,8 +195,8 @@ cdef class Matroid(SageObject): cpdef _local_ternary_matroid(self, basis=*) cpdef ternary_matroid(self, randomized_tests=*, verify=*) cpdef is_ternary(self, randomized_tests=*) - cpdef bint is_regular(self) - cpdef bint is_graphic(self) + cpdef bint is_regular(self) noexcept + cpdef bint is_graphic(self) noexcept # matroid k-closed cpdef is_k_closed(self, int k) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index bfcf7d361a2..9e1f1571db1 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -492,7 +492,7 @@ cdef class Matroid(SageObject): """ raise NotImplementedError("subclasses need to implement this") - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: r""" Return the rank of a set ``X``. @@ -513,8 +513,9 @@ cdef class Matroid(SageObject): sage: M = sage.matroids.matroid.Matroid() sage: M._rank(frozenset([0, 1, 2])) - NotImplementedError: subclasses need to implement this + Traceback (most recent call last): ... + NotImplementedError: subclasses need to implement this """ raise NotImplementedError("subclasses need to implement this") @@ -732,7 +733,7 @@ cdef class Matroid(SageObject): XX.pop() return frozenset(XX) - cpdef int _corank(self, frozenset X): + cpdef int _corank(self, frozenset X) noexcept: """ Return the corank of a set. @@ -904,7 +905,7 @@ cdef class Matroid(SageObject): # override the following methods for even better efficiency - cpdef bint _is_independent(self, frozenset X): + cpdef bint _is_independent(self, frozenset X) noexcept: """ Test if input is independent. @@ -925,7 +926,7 @@ cdef class Matroid(SageObject): """ return len(X) == self._rank(X) - cpdef bint _is_basis(self, frozenset X): + cpdef bint _is_basis(self, frozenset X) noexcept: """ Test if input is a basis. @@ -958,7 +959,7 @@ cdef class Matroid(SageObject): """ return self._is_independent(X) - cpdef bint _is_circuit(self, frozenset X): + cpdef bint _is_circuit(self, frozenset X) noexcept: """ Test if input is a circuit. @@ -990,7 +991,7 @@ cdef class Matroid(SageObject): Z.add(x) return True - cpdef bint _is_closed(self, frozenset X): + cpdef bint _is_closed(self, frozenset X) noexcept: """ Test if input is a closed set. @@ -1019,7 +1020,7 @@ cdef class Matroid(SageObject): XX.discard(y) return True - cpdef bint _is_coindependent(self, frozenset X): + cpdef bint _is_coindependent(self, frozenset X) noexcept: """ Test if input is coindependent. @@ -1040,7 +1041,7 @@ cdef class Matroid(SageObject): """ return self._corank(X) == len(X) - cpdef bint _is_cobasis(self, frozenset X): + cpdef bint _is_cobasis(self, frozenset X) noexcept: """ Test if input is a cobasis. @@ -1068,7 +1069,7 @@ cdef class Matroid(SageObject): """ return self._is_basis(self.groundset().difference(X)) - cpdef bint _is_cocircuit(self, frozenset X): + cpdef bint _is_cocircuit(self, frozenset X) noexcept: """ Test if input is a cocircuit. @@ -1100,7 +1101,7 @@ cdef class Matroid(SageObject): Z.add(x) return True - cpdef bint _is_coclosed(self, frozenset X): + cpdef bint _is_coclosed(self, frozenset X) noexcept: """ Test if input is a coclosed set. @@ -2305,7 +2306,7 @@ cdef class Matroid(SageObject): # verification - cpdef bint is_valid(self): + cpdef is_valid(self, certificate=False): r""" Test if the data obey the matroid axioms. @@ -2319,7 +2320,11 @@ cdef class Matroid(SageObject): Certain subclasses may check other axioms instead. - OUTPUT: boolean + INPUT: + + - ``certificate`` -- boolean (default: ``False``) + + OUTPUT: boolean, or (boolean, dictionary) EXAMPLES:: @@ -2340,8 +2345,10 @@ cdef class Matroid(SageObject): sage: def r(X): ....: return -1 sage: M = Matroid(groundset=[0,1,2], rank_function=r) - sage: M.is_valid() - False + sage: M.is_valid(certificate=True) + (False, + {'error': "the rank must be between 0 and the set's cardinality", + 'set': frozenset()}) """ cdef int i, j, rX, rY cdef frozenset X, Y, E = self.groundset() @@ -2350,17 +2357,17 @@ cdef class Matroid(SageObject): X = frozenset(Xt) rX = self._rank(X) if rX > i or rX < 0: - return False + return False if not certificate else (False, {"error": "the rank must be between 0 and the set's cardinality", "set": X}) for j in range(i, len(E) + 1): for Yt in combinations(E, j): Y = frozenset(Yt) rY = self._rank(Y) if X.issubset(Y) and rX > rY: - return False + return False if not certificate else (False, {"error": "the rank function must be monotonic", "set 1": X, "set 2": Y}) if (self._rank(X.union(Y)) + self._rank(X.intersection(Y)) > rX + rY): - return False - return True + return False if not certificate else (False, {"error": "the rank function must be submodular", "set 1": X, "set 2": Y}) + return True if not certificate else (True, {}) # enumeration @@ -5393,7 +5400,7 @@ cdef class Matroid(SageObject): E = set(self.groundset()) Q = set(list(E)[:m]) E = E-Q - for r in range(len(Q)/2 + 1): + for r in range(len(Q) // 2 + 1): R = set(list(E)[:r]) for Q1 in map(set, combinations(Q, r)): Q2 = Q-Q1 @@ -6187,7 +6194,7 @@ cdef class Matroid(SageObject): return False return True - cpdef bint is_paving(self): + cpdef bint is_paving(self) noexcept: """ Return if ``self`` is paving. @@ -6210,7 +6217,7 @@ cdef class Matroid(SageObject): return False return True - cpdef bint is_sparse_paving(self): + cpdef bint is_sparse_paving(self) noexcept: """ Return if ``self`` is sparse-paving. @@ -6577,7 +6584,7 @@ cdef class Matroid(SageObject): """ return self.ternary_matroid(randomized_tests=randomized_tests, verify=True) is not None - cpdef bint is_graphic(self): + cpdef bint is_graphic(self) noexcept: r""" Return if ``self`` is graphic. @@ -6611,7 +6618,7 @@ cdef class Matroid(SageObject): return False return True - cpdef bint is_regular(self): + cpdef bint is_regular(self) noexcept: r""" Return if ``self`` is regular. diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 5f09905844b..8cfea95f12e 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -859,7 +859,6 @@ def geomrep(M1, B1=None, lineorders1=None, pd=None, sp=False): pl = [list(x) for x in pts2.values()] lims = tracklims([None, None, None, None], [pt[0] for pt in pl], [pt[1] for pt in pl]) - j = 0 for ll in trilines: if len(ll) >= 3: ptsx, ptsy, x_i, y_i = createline(pts2, ll, lineorders1) diff --git a/src/sage/matroids/meson.build b/src/sage/matroids/meson.build new file mode 100644 index 00000000000..43c80789811 --- /dev/null +++ b/src/sage/matroids/meson.build @@ -0,0 +1,56 @@ +py.install_sources( + 'advanced.py', + 'all.py', + 'basis_exchange_matroid.pxd', + 'basis_matroid.pxd', + 'catalog.py', + 'circuit_closures_matroid.pxd', + 'circuits_matroid.pxd', + 'constructor.py', + 'database_collections.py', + 'database_matroids.py', + 'dual_matroid.py', + 'extension.pxd', + 'flats_matroid.pxd', + 'graphic_matroid.pxd', + 'lean_matrix.pxd', + 'linear_matroid.pxd', + 'matroid.pxd', + 'matroids_catalog.py', + 'matroids_plot_helpers.py', + 'minor_matroid.py', + 'named_matroids.py', + 'rank_matroid.py', + 'set_system.pxd', + 'union_matroid.pxd', + 'utilities.py', + subdir: 'sage/matroids', +) + +extension_data = { + 'basis_exchange_matroid' : files('basis_exchange_matroid.pyx'), + 'basis_matroid' : files('basis_matroid.pyx'), + 'circuit_closures_matroid' : files('circuit_closures_matroid.pyx'), + 'circuits_matroid' : files('circuits_matroid.pyx'), + 'extension' : files('extension.pyx'), + 'lean_matrix' : files('lean_matrix.pyx'), + 'linear_matroid' : files('linear_matroid.pyx'), + 'matroid' : files('matroid.pyx'), + 'set_system' : files('set_system.pyx'), + 'union_matroid' : files('union_matroid.pyx'), + 'unpickling' : files('unpickling.pyx'), + 'flats_matroid' : files('flats_matroid.pyx'), + 'graphic_matroid' : files('graphic_matroid.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/matroids', + install: true, + include_directories: [inc_cpython, inc_data_structures, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/matroids/union_matroid.pxd b/src/sage/matroids/union_matroid.pxd index 518ca54bba9..6542629010e 100644 --- a/src/sage/matroids/union_matroid.pxd +++ b/src/sage/matroids/union_matroid.pxd @@ -1,20 +1,19 @@ from sage.matroids.matroid cimport Matroid - cdef class MatroidUnion(Matroid): cdef list matroids cdef frozenset _groundset cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cdef class MatroidSum(Matroid): cdef list summands cdef frozenset _groundset cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 cdef class PartitionMatroid(Matroid): cdef dict p cdef frozenset _groundset cpdef frozenset groundset(self) - cpdef int _rank(self, frozenset X) + cpdef int _rank(self, frozenset X) except? -1 diff --git a/src/sage/matroids/union_matroid.pyx b/src/sage/matroids/union_matroid.pyx index ef40d0fc1b2..e371d5fb313 100644 --- a/src/sage/matroids/union_matroid.pyx +++ b/src/sage/matroids/union_matroid.pyx @@ -66,7 +66,7 @@ cdef class MatroidUnion(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: r""" Return the rank of a set ``X``. @@ -195,7 +195,7 @@ cdef class MatroidSum(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: r""" Return the rank of a set ``X``. @@ -290,7 +290,7 @@ cdef class PartitionMatroid(Matroid): """ return self._groundset - cpdef int _rank(self, frozenset X): + cpdef int _rank(self, frozenset X) except? -1: r""" Return the rank of a set ``X``. diff --git a/src/sage/meson.build b/src/sage/meson.build new file mode 100644 index 00000000000..7179555ec27 --- /dev/null +++ b/src/sage/meson.build @@ -0,0 +1,144 @@ +fs = import('fs') +sage_install_dir = py.get_install_dir() / 'sage' + +# Generate the configuration file +conf_data = configuration_data() +conf_data.set('PACKAGE_VERSION', '1.2.3') +conf_data.set('SAGE_ROOT', meson.current_source_dir() / '..' / '..') +# We use Python's prefix here to make it work with conda +prefix = py.get_variable('prefix', '') +conf_data.set('prefix', prefix) +datadir = fs.expanduser(get_option('datadir')) +if not fs.is_absolute(datadir) + datadir = prefix / datadir +endif +conf_data.set('SAGE_SHARE', datadir) +if not fs.exists(datadir / 'cremona') + message( + 'Warning: The specified datadir does not contain the necessary Cremona database. Either specify a different datadir or specify a correct the correct path via the environment variable SAGE_SHARE during runtime.', + ) +endif +conf_data.set('SAGE_MAXIMA', maxima.full_path()) +# Conda's ecl does not have any problems with Maxima, so nothing needs to be set here: +conf_data.set('SAGE_MAXIMA_FAS', '') +# Kenzo cannot yet be provided by the system, so we always use the SAGE_LOCAL path for now. +conf_data.set('SAGE_KENZO_FAS', '\'${prefix}\'/lib/ecl/kenzo.fas') +# It can be found, so we don't have to set anything here: +conf_data.set('NTL_INCDIR', '') +conf_data.set('NTL_LIBDIR', '') +ecl_config = find_program('ecl-config', required: true) +conf_data.set('SAGE_ECL_CONFIG', ecl_config.full_path()) +conf_data.set('SAGE_ARCHFLAGS', 'unset') +# not needed when using conda, as we then don't build any pc files +conf_data.set('SAGE_PKG_CONFIG_PATH', '') +openmp = dependency('openmp', required: false, disabler: true) +if openmp.found() + conf_data.set('OPENMP_CFLAGS', '-fopenmp') + conf_data.set('OPENMP_CXXFLAGS', '-fopenmp') +endif +gap_exe = find_program('gap') +if gap_exe.found() + gaprun = run_command( + gap_exe, + '-r', + '-q', + '--bare', + '--nointeract', + '-c', + 'Display(JoinStringsWithSeparator(GAPInfo.RootPaths,";"));', + check: true, + ) + gap_root_paths = gaprun.stdout().strip() + gap_root_paths = '${prefix}/lib/gap;${prefix}/share/gap;' + gaprun.stdout().strip() +endif +conf_data.set('GAP_ROOT_PATHS', gap_root_paths) +ecm_bin = find_program(['ecm', 'gmp-ecm'], required: true) +conf_data.set('SAGE_ECMBIN', ecm_bin.full_path()) + +config_file = configure_file( + input: '../../pkgs/sage-conf_conda/_sage_conf/_conf.py.in', + output: 'config.py', + install_dir: py.get_install_dir() / 'sage', + install: true, + configuration: conf_data, +) + +# Packages that need no processing and can be installed directly +no_processing = [ + 'databases', + 'doctest', + 'ext_data', + 'features', + 'game_theory', + 'homology', + 'knots', + 'logic', + 'manifolds', + 'parallel', + 'repl', + 'sandpiles', + 'tensor', + 'topology', + 'typeset', +] +foreach package : no_processing + install_subdir(package, install_dir: sage_install_dir) +endforeach + +py.install_sources( + 'all.py', + 'all__sagemath_bliss.py', + 'all__sagemath_categories.py', + 'all__sagemath_coxeter3.py', + 'all__sagemath_environment.py', + 'all__sagemath_mcqd.py', + 'all__sagemath_meataxe.py', + 'all__sagemath_objects.py', + 'all__sagemath_repl.py', + 'all__sagemath_sirocco.py', + 'all__sagemath_tdlib.py', + 'all_cmdline.py', + 'env.py', + 'version.py', + subdir: 'sage', +) + +subdir('cpython') +subdir('libs') +subdir('misc') +subdir('structure') +subdir('algebras') +subdir('arith') +subdir('ext') +subdir('calculus') +subdir('categories') +subdir('coding') +subdir('combinat') +subdir('crypto') +subdir('data_structures') +subdir('functions') +subdir('games') +subdir('geometry') +subdir('graphs') +subdir('groups') +subdir('interacts') +subdir('interfaces') +subdir('lfunctions') +subdir('matrix') +subdir('matroids') +subdir('modular') +subdir('modules') +subdir('monoids') +subdir('numerical') +subdir('plot') +subdir('probability') +subdir('quadratic_forms') +subdir('quivers') +subdir('rings') +subdir('schemes') +subdir('sets') +subdir('stats') +subdir('symbolic') +subdir('tests') +subdir('dynamics') +subdir('sat') diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 76445ff4dfb..e072d5f2ae9 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -624,11 +624,11 @@ def C3_merge(list lists): cdef bint next_item_found while nbheads: - for i in range(nbheads): # from 0 <= i < nbheads: + for i in range(nbheads): O = heads[i] # Does O appear in none of the tails? ``all(O not in tail for tail in tailsets)`` next_item_found = True - for j in range(nbheads): #from 0 <= j < nbheads: + for j in range(nbheads): if j == i: continue tailset = tailsets[j] @@ -640,7 +640,7 @@ def C3_merge(list lists): # Clear O from other heads, removing the line altogether # if the tail is already empty. # j goes down so that ``del heads[j]`` does not screw up the numbering - for j in range(nbheads-1, -1, -1): # from nbheads > j >= 0: + for j in range(nbheads-1, -1, -1): if heads[j] == O: # is O tail = tails[j] if tail: @@ -656,7 +656,7 @@ def C3_merge(list lists): break if not next_item_found: # No head is available - raise ValueError("Cannot merge the items %s."%', '.join(repr(head) for head in heads)) + raise ValueError("Cannot merge the items %s." % ', '.join(repr(head) for head in heads)) return out @@ -860,7 +860,7 @@ cpdef tuple C3_sorted_merge(list lists, key=identity): # Find the position of the largest head which will become the next item max_i = 0 max_key = key(heads[0]) - for i in range(1, nbheads): #from 1 <= i < nbheads: + for i in range(1, nbheads): O = heads[i] O_key = key(O) if O_key > max_key: @@ -870,14 +870,14 @@ cpdef tuple C3_sorted_merge(list lists, key=identity): # Find all the bad choices max_bad = None - for i in range(max_i): #from 0 <= i < max_i: + for i in range(max_i): O = heads[i] # Does O appear in none of the tails? O_key = key(O) # replace the closure # if any(O_key in tailsets[j] for j in range(nbheads) if j != i): continue cont = False - for j from 0<=j j >= 0: + for j in range(nbheads-1, -1, -1): if heads[j] == max_value: tail = tails[j] if tail: @@ -1053,7 +1053,7 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , <... 'object'>] """ @staticmethod - def __classcall__(cls, value, succ, key = None): + def __classcall__(cls, value, succ, key=None): """ EXAMPLES:: @@ -1353,7 +1353,7 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): super_classes = tuple(self._from_value(base).cls for base in self._bases_controlled) if not super_classes: super_classes = (object,) - return dynamic_class("%s.cls"%self, super_classes) + return dynamic_class("%s.cls" % self, super_classes) @cached_method def all_bases(self): diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index c7590c6e9ad..f5fc873e5a9 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -1751,7 +1751,7 @@ cdef class CachedMethodCaller(CachedFunction): """ # initialize CachedFunction. Since the cached method is actually bound # to an instance, it now makes sense to initialise the ArgumentFixer - # and re-use it for all bound cached method callers of the unbound + # and reuse it for all bound cached method callers of the unbound # cached method. if cachedmethod._cachedfunc._argument_fixer is None: cachedmethod._cachedfunc.argfix_init() @@ -2096,7 +2096,7 @@ cdef class CachedMethodCaller(CachedFunction): pass try: if inst._cached_methods is None: - inst._cached_methods = {self._cachedmethod._cachedfunc.__name__ : Caller} + inst._cached_methods = {self._cachedmethod._cachedfunc.__name__: Caller} else: (inst._cached_methods)[self._cachedmethod._cachedfunc.__name__] = Caller except AttributeError: @@ -2471,7 +2471,7 @@ cdef class CachedMethodCallerNoArgs(CachedFunction): pass try: if inst._cached_methods is None: - inst._cached_methods = {self.__name__ : Caller} + inst._cached_methods = {self.__name__: Caller} else: (inst._cached_methods)[self.__name__] = Caller except AttributeError: @@ -2852,7 +2852,7 @@ cdef class CachedMethod(): pass try: if inst._cached_methods is None: - inst._cached_methods = {name : Caller} + inst._cached_methods = {name: Caller} else: (inst._cached_methods)[name] = Caller except AttributeError: @@ -3509,9 +3509,9 @@ class FileCache(): sage: print(N[len(dir):]) foo-1_2 """ - a,k = key - kwdstr = '_'.join(['%s-%s'%x for x in k]) - argstr = '_'.join(['%s'%x for x in a]) + a, k = key + kwdstr = '_'.join('%s-%s' % x for x in k) + argstr = '_'.join('%s' % x for x in a) if kwdstr and argstr: keystr = kwdstr + '.' + argstr else: diff --git a/src/sage/misc/constant_function.pyx b/src/sage/misc/constant_function.pyx index 59cb785883d..2e9c3d9fe44 100644 --- a/src/sage/misc/constant_function.pyx +++ b/src/sage/misc/constant_function.pyx @@ -94,7 +94,7 @@ cdef class ConstantFunction(SageObject): sage: ConstantFunction(1) The constant function (...) -> 1 """ - return "The constant function (...) -> %s"%self._value + return "The constant function (...) -> %s" % self._value def __call__(self, *args): """ diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 3f90fde6031..b11b2078129 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -310,7 +310,7 @@ def find_object_modules(obj): from sage.misc import sageinspect # see if the object is defined in its own module - # might be wrong for class instances as the instanciation might appear + # might be wrong for class instances as the instantiation might appear # outside of the module !! module_name = None if sageinspect.isclassinstance(obj): diff --git a/src/sage/misc/fast_methods.pyx b/src/sage/misc/fast_methods.pyx index b5e6cc41516..d38c1802c45 100644 --- a/src/sage/misc/fast_methods.pyx +++ b/src/sage/misc/fast_methods.pyx @@ -194,7 +194,7 @@ cdef class FastHashable_class: This is for internal use only. The class has a cdef attribute ``_hash``, that needs to be assigned (for example, by calling - the init method, or by a direct assignement using + the init method, or by a direct assignment using cython). This is slower than using :func:`provide_hash_by_id`, but has the advantage that the hash can be prescribed, by assigning a cdef attribute ``_hash``. diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx index 8cf4d6e2efa..fc9241ca79d 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -132,16 +132,16 @@ cdef class ArgumentFixer: else: self._default_tuple = tuple(defaults) - #code = f.__code__ + # code = f.__code__ self.f = f self._ndefault = len(defaults) if classmethod: - self._nargs = len(arg_names)-1 #code.co_argcount-1 - self._arg_names = tuple(arg_names[1:]) #code.co_varnames[1:self._nargs+1] + self._nargs = len(arg_names)-1 # code.co_argcount-1 + self._arg_names = tuple(arg_names[1:]) # code.co_varnames[1:self._nargs+1] else: - self._nargs = len(arg_names) #code.co_argcount - self._arg_names = tuple(arg_names) #code.co_varnames[:self._nargs] + self._nargs = len(arg_names) # code.co_argcount + self._arg_names = tuple(arg_names) # code.co_varnames[:self._nargs] self._classmethod = classmethod cdef dict default_map @@ -157,7 +157,7 @@ cdef class ArgumentFixer: sage: g = ArgumentFixer(number_of_partitions); g # needs sage.combinat Argument Fixer of """ - return "Argument Fixer of %s"%self.f + return "Argument Fixer of %s" % self.f def fix_to_named(self, *args,**kwargs): """ @@ -295,7 +295,7 @@ cdef class ArgumentFixer: cdef Py_ssize_t i for i in range(lenargs, nargs): # in addition to the positional arguments, we take the - # ones with default values, unless they are overridded by + # ones with default values, unless they are overridden by # the named arguments. name = arg_names[i] if name in kwargs: diff --git a/src/sage/misc/gperftools.py b/src/sage/misc/gperftools.py index 8e89dadef6d..056a09d6f97 100644 --- a/src/sage/misc/gperftools.py +++ b/src/sage/misc/gperftools.py @@ -100,28 +100,6 @@ def _repr_(self): """ return 'Profiler logging to {0}'.format(self.filename()) - def _libc(self): - """ - Return libc. - - OUTPUT: a ctypes shared library handle - - EXAMPLES:: - - sage: from sage.misc.gperftools import Profiler - sage: Profiler()._libc() - - """ - global libc - if libc is not None: - return libc - name = find_library('c') - if name: - libc = ctypes.CDLL(name) - return libc - else: - raise ImportError('failed to open libc') - def _libprofiler(self): """ Return libprofiler. @@ -159,10 +137,11 @@ def start(self): PROFILE: interrupts/evictions/bytes = ... """ from signal import SIGPROF, SIG_DFL - self._previous_sigprof_handler = self._libc().signal(SIGPROF, SIG_DFL) + from cysignals.pysignals import setossignal + self._previous_sigprof_handler = setossignal(SIGPROF, SIG_DFL) profiler = self._libprofiler() self._t_start = time.time() - rc = profiler.ProfilerStart(self.filename()) + rc = profiler.ProfilerStart(str.encode(self.filename())) if rc < 0: raise ValueError('profiler failed to start') diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 2d5c6ae7475..9d8fcd4a341 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -1683,7 +1683,9 @@ def _latex_file_(objects, title='SAGE', debug=False, s = LATEX_HEADER + '\n' + MACROS + s + '\n\\end{document}' if debug: + print('----') print(s) + print('----') return s @@ -1827,7 +1829,6 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, ... ValueError: Unsupported LaTeX engine. """ - if tightpage: if margin is None: margin_str = "" @@ -1870,16 +1871,16 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, tmp.cleanup() return output_file = os.path.join(tmp.name, "sage." + suffix) - # this should get changed if we switch the stuff in misc.viewer to - # producing lists + if debug: - print('viewer: "{}"'.format(viewer)) + print(f'temporary file: "{output_file}"') + print(f'viewer: "{viewer}"') # Return immediately but only clean up the temporary file after # the viewer has closed. This function is synchronous and waits # for the process to complete... def run_viewer(): - run([viewer, output_file], capture_output=True) + run([*viewer.split(), output_file], capture_output=True) tmp.cleanup() # ...but we execute it asynchronously so that view() completes @@ -1890,7 +1891,78 @@ def run_viewer(): t.daemon = True t.start() - return + +def pdf(x, filename, tiny=False, tightpage=True, margin=None, engine=None, debug=False): + """ + Create an image from the latex representation of ``x`` and save it as a pdf + file with the given filename. + + INPUT: + + - ``x`` -- a Sage object + + - ``filename`` -- the filename with which to save the image + + - ``tiny`` -- boolean (default: ``False``); if ``True``, use a tiny font + + - ``tightpage`` -- boolean (default: ``True``); use the LaTeX package + ``preview`` with the 'tightpage' option + + - ``margin`` -- float (default: no margin); width of border, only effective + with 'tight page' + + - ``engine`` -- (default: ``None``) ``'latex'``, ``'pdflatex'``, + ``'xelatex'`` or ``'lualatex'``; if ``None``, the value defined in the + LaTeX global preferences ``latex.engine()`` is used + + - ``debug`` -- boolean (default: ``False``); if ``True``, print verbose output + + EXAMPLES:: + + sage: # optional - latex + sage: from sage.misc.latex import pdf + sage: import tempfile + sage: with tempfile.NamedTemporaryFile(suffix=".pdf") as f: # random + ....: pdf(ZZ[x], f.name) + """ + from sage.plot.graphics import Graphics + if isinstance(x, Graphics): + x.save(filename) + return + + if tightpage: + if margin is None: + margin_str = "" + else: + margin_str = '\n\\setlength\\PreviewBorder{%fmm}' % margin + latex_options = {'extra_preamble': + '\\usepackage[tightpage,active]{preview}\n' + + '\\PreviewEnvironment{page}%s' % margin_str, + 'math_left': '\\begin{page}$', + 'math_right': '$\\end{page}'} + else: + latex_options = {} + + # create a string of latex code to write in a file + s = _latex_file_([x], title='', tiny=tiny, debug=debug, **latex_options) + if engine is None: + engine = _Latex_prefs._option["engine"] + # path name for permanent pdf output + abs_path_to_pdf = os.path.abspath(filename) + # temporary directory to store stuff + with TemporaryDirectory() as tmp: + tex_file = os.path.join(tmp, "sage.tex") + pdf_file = os.path.join(tmp, "sage.pdf") + # write latex string to file + with open(tex_file, 'w') as file: + file.write(s) + # run latex on the file + e = _run_latex_(tex_file, debug=debug, engine=engine) + if e == 'pdf': + # if no errors, copy pdf_file to the appropriate place + shutil.copy(pdf_file, abs_path_to_pdf) + else: + print("Latex error or no pdf was generated.") def png(x, filename, density=150, debug=False, diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index de53979662e..483e8f52035 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -802,10 +802,10 @@ def dvi(self, filename=None, view=True, program='latex'): We test the behavior when a wrong value is provided:: sage: t = Standalone('Hello World') - sage: _ = t.dvi(program='lates') + sage: _ = t.dvi(program='farniente') Traceback (most recent call last): ... - ValueError: program(=lates) should be latex + ValueError: program(=farniente) should be latex """ from sage.features.latex import latex diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index 663856d03e3..7514aee1569 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -66,8 +66,8 @@ cdef class _lazy_attribute(): sage: Parent.element_class - sage: Parent.element_class.__doc__[91:147] - 'The (default) class for the elements of this parent.\n\n ' + sage: "The (default) class for the elements of this parent" in Parent.element_class.__doc__ + True sage: Parent.element_class.__name__ 'element_class' sage: Parent.element_class.__module__ @@ -512,7 +512,7 @@ class lazy_class_attribute(lazy_attribute): A lazy class attribute for a class is like a usual class attribute, except that, instead of being computed when the class is constructed, it is computed on the fly the first time it is accessed, either through the - class itself or trough on of its objects. + class itself or through one of its objects. This is very similar to :class:`lazy_attribute` except that the attribute is a class attribute. More precisely, once computed, the lazy class diff --git a/src/sage/misc/lazy_format.py b/src/sage/misc/lazy_format.py index d2b549c9e11..6ab9b586c5a 100644 --- a/src/sage/misc/lazy_format.py +++ b/src/sage/misc/lazy_format.py @@ -27,7 +27,7 @@ class LazyFormat(str): sage: LazyFormat("Got `%s`; expected %s")%(3, 2/3) Got `3`; expected 2/3 - To demonstrate the lazyness, let us build an object with a broken + To demonstrate the laziness, let us build an object with a broken ``__repr__`` method:: sage: class IDontLikeBeingPrinted(): diff --git a/src/sage/misc/lazy_list.pyx b/src/sage/misc/lazy_list.pyx index 0bbf217c472..65d397996fc 100644 --- a/src/sage/misc/lazy_list.pyx +++ b/src/sage/misc/lazy_list.pyx @@ -823,8 +823,8 @@ cdef class lazy_list_generic(): stop = self.start + stop * self.step if stop > self.stop: stop = self.stop - if stop != PY_SSIZE_T_MAX and stop%step != start%step: - stop = stop - (stop-start)%step + step + if stop != PY_SSIZE_T_MAX and stop % step != start % step: + stop = stop - (stop - start) % step + step if stop <= start: return empty_lazy_list @@ -1087,7 +1087,7 @@ cdef class lazy_list_from_update_function(lazy_list_generic): The update function should take as input a list and make it longer (using either the methods ``append`` or ``extend``). If after a call to the update function the list of values is shorter a - :exc:`RuntimeError` will occurr. If no value is added then the lazy list + :exc:`RuntimeError` will occur. If no value is added then the lazy list is considered finite. - ``cache`` -- an optional list to be used as the cache. Be careful that diff --git a/src/sage/misc/meson.build b/src/sage/misc/meson.build new file mode 100644 index 00000000000..bd05d525252 --- /dev/null +++ b/src/sage/misc/meson.build @@ -0,0 +1,136 @@ +py.install_sources( + 'abstract_method.py', + 'all.py', + 'all__sagemath_environment.py', + 'all__sagemath_objects.py', + 'all__sagemath_repl.py', + 'allocator.pxd', + 'banner.py', + 'benchmark.py', + 'binary_tree.pxd', + 'bindable_class.py', + 'c3_controlled.pxd', + 'cachefunc.pxd', + 'call.py', + 'classcall_metaclass.pxd', + 'classgraph.py', + 'compat.py', + 'converting_dict.py', + 'copying.py', + 'cython.py', + 'decorators.py', + 'defaults.py', + 'dev_tools.py', + 'edit_module.py', + 'element_with_label.py', + 'explain_pickle.py', + 'fast_methods.pxd', + 'flatten.py', + 'func_persist.py', + 'function_mangling.pxd', + 'functional.py', + 'gperftools.py', + 'html.py', + 'inherit_comparison.pxd', + 'inline_fortran.py', + 'latex.py', + 'latex_macros.py', + 'latex_standalone.py', + 'lazy_format.py', + 'lazy_import_cache.py', + 'lazy_list.pxd', + 'lazy_string.pxd', + 'map_threaded.py', + 'mathml.py', + 'messaging.py', + 'method_decorator.py', + 'misc.py', + 'misc_c.pxd', + 'mrange.py', + 'multireplace.py', + 'namespace_package.py', + 'nested_class.pxd', + 'object_multiplexer.py', + 'package.py', + 'package_dir.py', + 'pager.py', + 'prandom.py', + 'profiler.py', + 'proof.py', + 'python.py', + 'random_testing.py', + 'randstate.pxd', + 'remote_file.py', + 'replace_dot_all.py', + 'repr.py', + 'rest_index_of_methods.py', + 'sage_eval.py', + 'sage_input.py', + 'sage_timeit.py', + 'sage_unittest.py', + 'sagedoc.py', + 'sagedoc_conf.py', + 'sageinspect.py', + 'search.pxd', + 'sh.py', + 'sphinxify.py', + 'superseded.py', + 'table.py', + 'temporary_file.py', + 'test_class_pickling.py', + 'test_nested_class.py', + 'timing.py', + 'trace.py', + 'unknown.py', + 'verbose.py', + 'viewer.py', + 'weak_dict.pxd', + subdir: 'sage/misc', +) + +extension_data = { + 'allocator' : files('allocator.pyx'), + 'binary_tree' : files('binary_tree.pyx'), + 'c3' : files('c3.pyx'), + 'c3_controlled' : files('c3_controlled.pyx'), + 'cachefunc' : files('cachefunc.pyx'), + 'callable_dict' : files('callable_dict.pyx'), + 'citation' : files('citation.pyx'), + 'classcall_metaclass' : files('classcall_metaclass.pyx'), + 'constant_function' : files('constant_function.pyx'), + 'derivative' : files('derivative.pyx'), + 'fast_methods' : files('fast_methods.pyx'), + 'fpickle' : files('fpickle.pyx'), + 'function_mangling' : files('function_mangling.pyx'), + 'inherit_comparison' : files('inherit_comparison.pyx'), + 'instancedoc' : files('instancedoc.pyx'), + 'lazy_attribute' : files('lazy_attribute.pyx'), + 'lazy_import' : files('lazy_import.pyx'), + 'lazy_list' : files('lazy_list.pyx'), + 'lazy_string' : files('lazy_string.pyx'), + 'misc_c' : files('misc_c.pyx'), + 'nested_class' : files('nested_class.pyx'), + 'parser' : files('parser.pyx'), + 'persist' : files('persist.pyx'), + 'pickle_old' : files('pickle_old.pyx'), + 'randstate' : files('randstate.pyx'), + 'reset' : files('reset.pyx'), + 'sage_ostools' : files('sage_ostools.pyx'), + 'sage_timeit_class' : files('sage_timeit_class.pyx'), + 'search' : files('search.pyx'), + 'session' : files('session.pyx'), + 'stopgap' : files('stopgap.pyx'), + 'weak_dict' : files('weak_dict.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/misc', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index 5f01348fe81..774008cdd70 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -105,7 +105,7 @@ def _xmrange_iter(iter_list, typ=list): sage: l1 is l2 False - However, if you would like to re-use the list object:: + However, if you would like to reuse the list object:: sage: iter = sage.misc.mrange._xmrange_iter( [[1,2],[1,3]], lambda x: x ) sage: l1 = next(iter) diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index a3a42dfe5d4..e42569abe3e 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -335,7 +335,7 @@ class MainClass(object, metaclass=NestedClassMetaclass): sage: from sage.misc.nested_class import MainClass sage: print(MainClass.NestedClass.NestedSubClass.dummy.__doc__) NestedSubClass.dummy(self, x, *args, r=(1, 2, 3.4), **kwds) - File: sage/misc/nested_class.pyx (starting at line ...) + File: ...sage/misc/nested_class.pyx (starting at line ...) A dummy method to demonstrate the embedding of method signature for nested classes. diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index ca7a9ffd67c..7ccead8cbfe 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -39,7 +39,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from typing import Dict, List, NamedTuple, Optional, Union +from typing import NamedTuple, Optional, Union import sage.env @@ -144,7 +144,7 @@ def spkg_type(name): The type as a string in ``('base', 'standard', 'optional', 'experimental')``. If no ``SPKG`` exists with the given name (or the directory ``SAGE_PKGS`` is - not avaialble), ``None`` is returned. + not available), ``None`` is returned. """ spkg_type = None from sage.env import SAGE_PKGS @@ -230,8 +230,8 @@ def is_installed(self) -> bool: return self.installed_version is not None -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]: +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]: r""" Return a dictionary of information about each package. @@ -402,9 +402,9 @@ def installed_packages(exclude_pip=True): sage: # optional - sage_spkg sage: from sage.misc.package import installed_packages sage: sorted(installed_packages().keys()) - [...'conway_polynomials', ...] - sage: installed_packages()['conway_polynomials'] # random - '0.5' + [...'gnulib', ...] + sage: installed_packages()['gnulib'] # random + 'f9b39c4e337f1dc0dd07c4f3985c476fb875d799' .. SEEALSO:: @@ -439,7 +439,7 @@ def is_package_installed(package, exclude_pip=True): EXAMPLES:: sage: from sage.misc.package import is_package_installed - sage: is_package_installed('conway_polynomials') # optional - sage_spkg + sage: is_package_installed('gnulib') # optional - sage_spkg True Giving just the beginning of the package name is not good enough:: @@ -537,8 +537,8 @@ def package_manifest(package): sage: # optional - sage_spkg sage: from sage.misc.package import package_manifest - sage: manifest = package_manifest('conway_polynomials') - sage: manifest['package_name'] == 'conway_polynomials' + sage: manifest = package_manifest('gnulib') + sage: manifest['package_name'] == 'gnulib' True sage: 'files' in manifest True diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index 55540dc27b7..a89db8557d1 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -1232,7 +1232,7 @@ def db(name): The database directory is ``$HOME/.sage/db``. """ from sage.misc.misc import SAGE_DB - return load('%s/%s'%(SAGE_DB,name)) + return load('%s/%s' % (SAGE_DB, name)) def db_save(x, name=None): @@ -1245,4 +1245,4 @@ def db_save(x, name=None): x.db(name) except AttributeError: from sage.misc.misc import SAGE_DB - save(x, '%s/%s'%(SAGE_DB,name)) + save(x, '%s/%s' % (SAGE_DB, name)) diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py index 16ac2124377..ea51a9b3159 100644 --- a/src/sage/misc/replace_dot_all.py +++ b/src/sage/misc/replace_dot_all.py @@ -456,8 +456,8 @@ def walkdir_replace_dot_all(dir, file_regex=r'.*[.](py|pyx|pxi)$', package_regex finally: # Print report also when interrupted if verbosity: - log_messages = sorted(log_messages.rstrip().split('\n')) - for i, message in enumerate(log_messages, start=1): + log_messages_split = sorted(log_messages.rstrip().split('\n')) + for i, message in enumerate(log_messages_split, start=1): # add index to each line print(f'{i}. {message.rstrip()}') report = 'REPORT:\n' diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 827fd77e811..0505f6039a9 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -669,10 +669,7 @@ def format(s, embedded=False): EXAMPLES:: sage: from sage.misc.sagedoc import format - sage: identity_matrix(2).rook_vector.__doc__[191:263] # needs sage.modules - 'Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard' - - sage: format(identity_matrix(2).rook_vector.__doc__[191:263]) # needs sage.modules + sage: format('Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard') 'Let A be an m by n (0,1)-matrix. We identify A with a chessboard\n' If the first line of the string is 'nodetex', remove 'nodetex' but diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 87702cd481a..1f2a39d7c78 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -311,7 +311,7 @@ def _extract_embedded_signature(docstring, name): sage: from sage.misc.sageinspect import _extract_embedded_signature sage: from sage.misc.nested_class import MainClass sage: print(_extract_embedded_signature(MainClass.NestedClass.NestedSubClass.dummy.__doc__, 'dummy')[0]) - File: sage/misc/nested_class.pyx (starting at line ...) + File: ...sage/misc/nested_class.pyx (starting at line ...) ... sage: _extract_embedded_signature(MainClass.NestedClass.NestedSubClass.dummy.__doc__, 'dummy')[1] FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) @@ -1850,7 +1850,7 @@ def _sage_getdoc_unformatted(obj): sage: from sage.misc.sageinspect import _sage_getdoc_unformatted sage: print(_sage_getdoc_unformatted(sage.rings.integer.Integer)) Integer(x=None, base=0) - File: sage/rings/integer.pyx (starting at line ...) + File: ...sage/rings/integer.pyx (starting at line ...) The :class:`Integer` class represents arbitrary precision integers. It derives from the :class:`Element` class, so @@ -2255,7 +2255,7 @@ def sage_getsourcelines(obj): sage: sage_getsourcelines(test_func) (['def base(x):\n', ... - ' return x\n'], 7) + ' return x\n'], 8) Here are some cases that were covered in :issue:`11298`; note that line numbers may easily change, and therefore we do diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index a78e7eb37e7..6aaa8e6fb6f 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -327,7 +327,7 @@ class __experimental_self_test: The test below does not issue a warning message because that warning has already been issued by a previous doc-test in the @experimental code. Note that this behaviour cannot be demonstrated within a single documentation - string: Sphinx will itself supress multiple issued warnings. + string: Sphinx will itself suppress multiple issued warnings. TESTS:: diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index ff8c7a751ac..998260be8eb 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -23,11 +23,10 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import io +import atexit import os import tempfile - -import atexit +from typing import IO # Until tmp_dir() and tmp_filename() are removed, we use this directory # as the parent for all temporary files & directories created by them. @@ -41,7 +40,7 @@ # temporary directory ################################################################# -def tmp_dir(name='dir_', ext=''): +def tmp_dir(name='dir_', ext='') -> str: r""" Create and return a temporary directory in ``$HOME/.sage/temp/hostname/pid/`` @@ -84,7 +83,7 @@ def tmp_dir(name='dir_', ext=''): # temporary filename ################################################################# -def tmp_filename(name='tmp_', ext=''): +def tmp_filename(name='tmp_', ext='') -> str: r""" Create and return a temporary file in ``$HOME/.sage/temp/hostname/pid/`` @@ -163,8 +162,8 @@ class atomic_write: mode bits of the file were changed manually). (Not to be confused with the file opening mode.) - - ``binary`` -- boolean (default: ``True`` on Python 2, ``False`` on Python - 3); the underlying file is opened in binary mode. If ``False`` then it is + - ``binary`` -- boolean (default: ``False``); + the underlying file is opened in binary mode. If ``False`` then it is opened in text mode and an encoding with which to write the file may be supplied. @@ -299,7 +298,7 @@ class atomic_write: False """ def __init__(self, target_filename, append=False, mode=0o666, - binary=None, **kwargs): + binary=False, **kwargs) -> None: """ TESTS:: @@ -320,13 +319,11 @@ def __init__(self, target_filename, append=False, mode=0o666, os.umask(umask) self.mode = mode & (~umask) - # 'binary' mode is the default on Python 2, whereas 'text' mode is the - # default on Python 3--this reflects consistent handling of the default - # str type on the two platforms - self.binary = False if binary is None else binary + # 'text' mode is the default on Python 3 + self.binary = binary self.kwargs = kwargs - def __enter__(self): + def __enter__(self) -> IO: """ Create and return a temporary file in ``self.tmpdir`` (normally the same directory as the target file). @@ -372,7 +369,7 @@ def __enter__(self): return self.tempfile - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb) -> None: """ If the ``with`` block was successful, move the temporary file to the target file. Otherwise, delete the temporary file. @@ -457,7 +454,7 @@ class atomic_dir: ....: h.read() 'Second' """ - def __init__(self, target_directory): + def __init__(self, target_directory) -> None: r""" TESTS:: @@ -492,7 +489,7 @@ def __enter__(self): self.tempname = os.path.abspath(tdir.name) return tdir - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb) -> None: """ If the ``with`` block was successful, move the temporary directory to the target directory. Otherwise, delete the temporary directory. @@ -518,7 +515,8 @@ def __exit__(self, exc_type, exc_val, exc_tb): try: os.rename(self.tempname, self.target) except OSError: - # Race: Another thread or process must have created the directory + # Race: Another thread or process must have created + # the directory pass else: # Failure: delete temporary file @@ -528,7 +526,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): _spyx_tmp = None -def spyx_tmp(): +def spyx_tmp() -> str: r""" The temporary directory used to store pyx files. diff --git a/src/sage/misc/test_nested_class.py b/src/sage/misc/test_nested_class.py index 58c1808e6f0..e7791f95a3a 100644 --- a/src/sage/misc/test_nested_class.py +++ b/src/sage/misc/test_nested_class.py @@ -159,8 +159,8 @@ class B: class ABB: class B: """ - This class is broken and can't be pickled. - A warning is emmited during compilation. + This class is broken and cannot be pickled. + A warning is emitted during compilation. """ pass diff --git a/src/sage/misc/viewer.py b/src/sage/misc/viewer.py index 4f62b1c5022..badc70a963a 100644 --- a/src/sage/misc/viewer.py +++ b/src/sage/misc/viewer.py @@ -69,7 +69,7 @@ def default_viewer(viewer=None): elif os.uname()[0] == 'Darwin': # Simple on OS X, since there is an open command that opens # anything, using the user's preferences. - BROWSER = 'open' + BROWSER = 'open -W' DVI_VIEWER = BROWSER PDF_VIEWER = BROWSER PNG_VIEWER = BROWSER diff --git a/src/sage/misc/weak_dict.pyx b/src/sage/misc/weak_dict.pyx index eefd042bf25..ce69b6a840a 100644 --- a/src/sage/misc/weak_dict.pyx +++ b/src/sage/misc/weak_dict.pyx @@ -278,7 +278,7 @@ cdef class WeakValueDictionary(dict): the dictionary values. However, the actual deletion is postponed till after the iteration over the dictionary has finished. Hence, when the callbacks are executed, the values which the callback belongs to has - already been overridded by a new value. Therefore, the callback does not + already been overridden by a new value. Therefore, the callback does not delete the item:: sage: for k in D: # indirect doctest diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index b3d8f3492b0..92b1e9f9abe 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -41,6 +41,7 @@ from sage.categories.modular_abelian_varieties import ModularAbelianVarieties from sage.matrix.constructor import matrix from sage.matrix.special import block_diagonal_matrix, identity_matrix +from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import from sage.misc.misc_c import prod from sage.modular.arithgroup.congroup_gamma0 import Gamma0_class @@ -69,8 +70,7 @@ ['cremona_letter_code', 'CremonaDatabase']) -from . import homspace -from . import lseries +from sage.modular.abvar import homspace, lseries from .morphism import HeckeOperator, Morphism, DegeneracyMap from .torsion_subgroup import RationalTorsionSubgroup, QQbarTorsionSubgroup from .finite_subgroup import (FiniteSubgroup_lattice, FiniteSubgroup, @@ -1728,6 +1728,7 @@ def ambient_morphism(self): self.__ambient_morphism = phi return phi + @cached_method def is_ambient(self) -> bool: """ Return ``True`` if ``self`` equals the ambient product Jacobian. @@ -1746,13 +1747,8 @@ def is_ambient(self) -> bool: sage: (A+B+C).is_ambient() True """ - try: - return self.__is_ambient - except AttributeError: - pass L = self.lattice() - self.__is_ambient = (self.lattice() == ZZ**L.degree()) - return self.__is_ambient + return self.lattice() == ZZ**L.degree() def dimension(self): """ @@ -1890,6 +1886,7 @@ def sturm_bound(self): self.__sturm_bound = B return B + @cached_method def is_hecke_stable(self) -> bool: """ Return ``True`` if ``self`` is stable under the Hecke operators of its @@ -1908,11 +1905,6 @@ def is_hecke_stable(self) -> bool: sage: (J0(33)[0] + J0(33)[1]).is_hecke_stable() True """ - try: - return self._is_hecke_stable - except AttributeError: - pass - # b = self.modular_symbols().sturm_bound() b = max([m.sturm_bound() for m in self._ambient_modular_symbols_spaces()]) @@ -1924,10 +1916,8 @@ def is_hecke_stable(self) -> bool: Tn_matrix = J.hecke_operator(n).matrix() for v in B: if v * Tn_matrix not in L: - self._is_hecke_stable = False return False - self._is_hecke_stable = True return True def is_subvariety(self, other) -> bool: @@ -2381,8 +2371,8 @@ def frobenius_polynomial(self, p, var='x'): from .constructor import AbelianVariety decomp = [AbelianVariety(f) for f in self.newform_decomposition('a')] - return prod((s.frobenius_polynomial(p) for s in - decomp)) + return prod(s.frobenius_polynomial(p) for s in + decomp) f = self.newform('a') Kf = f.base_ring() eps = f.character() @@ -2467,7 +2457,7 @@ def homology(self, base_ring=ZZ): sage: J0(389).homology(ZZ) Integral Homology of Abelian variety J0(389) of dimension 32 """ - from . import homology + from sage.modular.abvar import homology try: return self._homology[base_ring] except AttributeError: diff --git a/src/sage/modular/abvar/abvar_ambient_jacobian.py b/src/sage/modular/abvar/abvar_ambient_jacobian.py index a25d82d5b8c..7674b41807a 100644 --- a/src/sage/modular/abvar/abvar_ambient_jacobian.py +++ b/src/sage/modular/abvar/abvar_ambient_jacobian.py @@ -20,7 +20,7 @@ from sage.modular.modsym.modsym import ModularSymbols from sage.modular.modform.constructor import Newforms from sage.modular.arithgroup.all import Gamma0_class, Gamma1_class -from . import morphism +from sage.modular.abvar import morphism _cache = {} @@ -409,7 +409,7 @@ def newform_decomposition(self, names=None): if self.dimension() == 0: return [] G = self.group() - if not (isinstance(G, Gamma0_class) or isinstance(G, Gamma1_class)): + if not isinstance(G, (Gamma0_class, Gamma1_class)): return [S.newform(names=names) for S in self.decomposition()] Gtype = G.parent() N = G.level() diff --git a/src/sage/modular/abvar/abvar_newform.py b/src/sage/modular/abvar/abvar_newform.py index 1159de73894..9568e5481cc 100644 --- a/src/sage/modular/abvar/abvar_newform.py +++ b/src/sage/modular/abvar/abvar_newform.py @@ -22,7 +22,7 @@ from sage.modular.arithgroup.all import Gamma0_class, Gamma1_class, GammaH_class from .abvar import ModularAbelianVariety_modsym_abstract -from . import homspace +from sage.modular.abvar import homspace lazy_import('sage.databases.cremona', 'cremona_letter_code') diff --git a/src/sage/modular/abvar/constructor.py b/src/sage/modular/abvar/constructor.py index a39f7819393..53561063eaa 100644 --- a/src/sage/modular/abvar/constructor.py +++ b/src/sage/modular/abvar/constructor.py @@ -19,10 +19,11 @@ from sage.modular.modsym.space import ModularSymbolsSpace from .abvar_newform import ModularAbelianVariety_newform import sage.modular.modform.element -from . import abvar +from sage.modular.abvar import abvar _cache = {} + def _get(key): """ Return the cached abelian variety with given key. This is used @@ -49,6 +50,7 @@ def _get(key): return z raise ValueError("element not in cache") + def _saved(key, J): """ Return the cached abelian variety with given key. This is used @@ -95,6 +97,7 @@ def J0(N): J = Gamma0(N).modular_abelian_variety() return _saved(key, J) + def J1(N): """ Return the Jacobian `J_1(N)` of the modular curve @@ -112,6 +115,7 @@ def J1(N): from sage.modular.arithgroup.all import Gamma1 return _saved(key, Gamma1(N).modular_abelian_variety()) + def JH(N, H): """ Return the Jacobian `J_H(N)` of the modular curve @@ -129,6 +133,7 @@ def JH(N, H): from sage.modular.arithgroup.all import GammaH return _saved(key, GammaH(N, H).modular_abelian_variety()) + def AbelianVariety(X): """ Create the abelian variety corresponding to the given defining diff --git a/src/sage/modular/abvar/cuspidal_subgroup.py b/src/sage/modular/abvar/cuspidal_subgroup.py index c50a2b109f8..7a0921076d9 100644 --- a/src/sage/modular/abvar/cuspidal_subgroup.py +++ b/src/sage/modular/abvar/cuspidal_subgroup.py @@ -59,15 +59,15 @@ True """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2007 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.matrix.constructor import matrix from sage.modular.arithgroup.all import Gamma0_class diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index 347e45fa418..cfaf762eae2 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -88,15 +88,15 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sage.rings.abc diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 2333bbacd00..30ad76f3601 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -187,7 +187,7 @@ from sage.misc.lazy_attribute import lazy_attribute -from . import morphism +from sage.modular.abvar import morphism from sage.rings.infinity import Infinity diff --git a/src/sage/modular/abvar/torsion_subgroup.py b/src/sage/modular/abvar/torsion_subgroup.py index 516acf5f7fd..6ae7bff58c7 100644 --- a/src/sage/modular/abvar/torsion_subgroup.py +++ b/src/sage/modular/abvar/torsion_subgroup.py @@ -79,15 +79,15 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import divisors, gcd from sage.misc.misc_c import prod @@ -468,7 +468,7 @@ def multiple_of_order(self, maxp=None, proof=True): return self._multiple_of_order_proof_false # The Gamma0 and Gamma1 case - if all((isinstance(G, Gamma0_class) or isinstance(G, Gamma1_class) for G in A.groups())): + if all(isinstance(G, (Gamma0_class, Gamma1_class)) for G in A.groups()): self._multiple_of_order = self.multiple_of_order_using_frobp() return self._multiple_of_order @@ -566,13 +566,13 @@ def multiple_of_order_using_frobp(self, maxp=None): pass A = self.abelian_variety() if A.dimension() == 0: - T = ZZ(1) + T = ZZ.one() self.__multiple_of_order_using_frobp = T return T - if not all((isinstance(G, Gamma0_class) or isinstance(G, Gamma1_class) for G in A.groups())): + if not all(isinstance(G, (Gamma0_class, Gamma1_class)) for G in A.groups()): raise NotImplementedError("torsion multiple only implemented for Gamma0 and Gamma1") - bnd = ZZ(0) + bnd = ZZ.zero() N = A.level() cnt = 0 if maxp is None: diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index 09d65934165..c70defa96a3 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -4,7 +4,7 @@ """ ################################################################################ # -# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/ +# Copyright (C) 2009, The Sage Group -- https://www.sagemath.org/ # # Distributed under the terms of the GNU General Public License (GPL) # @@ -60,7 +60,7 @@ class ArithmeticSubgroup(Group): Element = ArithmeticSubgroupElement - def __init__(self): + def __init__(self) -> None: r""" Standard init routine. @@ -76,7 +76,7 @@ def _repr_(self) -> str: r""" Return the string representation of ``self``. - NOTE: This function should be overridden by all subclasses. + .. NOTE:: This function should be overridden by all subclasses. EXAMPLES:: @@ -104,7 +104,7 @@ def __reduce__(self): r""" Used for pickling ``self``. - NOTE: This function should be overridden by all subclasses. + .. NOTE:: This function should be overridden by all subclasses. EXAMPLES:: @@ -146,8 +146,10 @@ def _element_constructor_(self, x, check=True): def __contains__(self, x): r""" - Test if x is an element of this group. This checks that x defines (is?) a 2x2 integer matrix of determinant 1, and - then hands over to the routine _contains_sl2, which derived classes should implement. + Test if x is an element of this group. + + This checks that x defines (is?) a 2x2 integer matrix of determinant 1, and + then hands over to the routine ``_contains_sl2``, which derived classes should implement. EXAMPLES:: @@ -165,12 +167,12 @@ def __contains__(self, x): # Do not override this function! Derived classes should override # _contains_sl2. if isinstance(x, list) and len(x) == 4: - if not (x[0] in ZZ and x[1] in ZZ and x[2] in ZZ and x[3] in ZZ): + if not all(y in ZZ for y in x): return False - a,b,c,d = map(ZZ, x) + a, b, c, d = map(ZZ, x) if a*d - b*c != 1: return False - return self._contains_sl2(a,b,c,d) + return self._contains_sl2(a, b, c, d) else: if parent(x) is not SL2Z: try: @@ -178,13 +180,14 @@ def __contains__(self, x): except TypeError: return False x = y - return self._contains_sl2(x.a(),x.b(),x.c(),x.d()) + return self._contains_sl2(x.a(), x.b(), x.c(), x.d()) - def _contains_sl2(self, a,b,c,d): + def _contains_sl2(self, a, b, c, d): r""" Test whether the matrix [a,b;c,d], which may be assumed to have - determinant 1, is an element of ``self``. This must be overridden by all - subclasses. + determinant 1, is an element of ``self``. + + This must be overridden by all subclasses. EXAMPLES:: @@ -234,10 +237,11 @@ def matrix_space(self): """ return Mat2Z - def is_parent_of(self, x): + def is_parent_of(self, x) -> bool: r""" - Check whether this group is a valid parent for the element x. Required - by Sage's testing framework. + Check whether this group is a valid parent for the element x. + + Required by Sage's testing framework. EXAMPLES:: @@ -250,7 +254,7 @@ def is_parent_of(self, x): sage: Gamma(3).is_parent_of(SL2Z(1)) True """ - return (parent(x) == SL2Z and x in self) + return parent(x) == SL2Z and x in self def coset_reps(self, G=None): r""" @@ -352,17 +356,17 @@ def todd_coxeter(self, G=None, on_right=True): if G != SL2Z: raise NotImplementedError("Don't know how to compute coset reps for subgroups yet") - id = SL2Z([1,0,0,1]) - l = SL2Z([1,1,0,1]) - s = SL2Z([0,-1,1,0]) + one = SL2Z.one() + l = SL2Z([1, 1, 0, 1]) + s = SL2Z([0, -1, 1, 0]) - reps = [id] # coset representatives - reps_inv = {id:0} # coset representatives index + reps = [one] # coset representatives + reps_inv = {one: 0} # coset representatives index - l_wait_back = [id] # rep with no incoming s_edge - s_wait_back = [id] # rep with no incoming l_edge - l_wait = [id] # rep with no outgoing l_edge - s_wait = [id] # rep with no outgoing s_edge + l_wait_back = [one] # rep with no incoming s_edge + s_wait_back = [one] # rep with no incoming l_edge + l_wait = [one] # rep with no outgoing l_edge + s_wait = [one] # rep with no outgoing s_edge l_edges = [None] # edges for l s_edges = [None] # edges for s @@ -388,7 +392,7 @@ def todd_coxeter(self, G=None, on_right=True): if yy in self: l_edges[reps_inv[x]] = reps_inv[v] del l_wait_back[i] - if yy != id: + if yy != one: gens.append(self(yy)) not_end = False break @@ -420,7 +424,7 @@ def todd_coxeter(self, G=None, on_right=True): if yy in self: s_edges[reps_inv[x]] = reps_inv[v] del s_wait_back[i] - if yy != id: + if yy != one: gens.append(self(yy)) not_end = False break @@ -436,7 +440,7 @@ def todd_coxeter(self, G=None, on_right=True): return reps, gens, l_edges, s_edges - def nu2(self): + def nu2(self) -> int: r""" Return the number of orbits of elliptic points of order 2 for this arithmetic subgroup. @@ -471,8 +475,9 @@ def nu2(self): # precisely when the preimages are not elliptic.) count = 0 + mati = SL2Z([0, 1, -1, 0]) for g in self.coset_reps(): - if g * SL2Z([0,1,-1,0]) * (~g) in self: + if g * mati * (~g) in self: count += 1 return count @@ -505,16 +510,14 @@ def nu3(self): return 0 count = 0 + matj = SL2Z([0, 1, -1, -1]) for g in self.coset_reps(): - if g * SL2Z([0,1,-1,-1]) * (~g) in self: + if g * matj * (~g) in self: count += 1 - if self.is_even(): - return count - else: - return count // 2 + return count if self.is_even() else count // 2 - def is_abelian(self): + def is_abelian(self) -> bool: r""" Return ``True`` if this arithmetic subgroup is abelian. @@ -534,7 +537,7 @@ def is_abelian(self): """ return False - def is_finite(self): + def is_finite(self) -> bool: r""" Return ``True`` if this arithmetic subgroup is finite. @@ -554,12 +557,13 @@ def is_finite(self): """ return False - def is_subgroup(self, right): + def is_subgroup(self, right) -> bool: r""" - Return ``True`` if ``self`` is a subgroup of right, and ``False`` otherwise. For - generic arithmetic subgroups this is done by the absurdly slow - algorithm of checking all of the generators of ``self`` to see if they are - in ``right``. + Return ``True`` if ``self`` is a subgroup of right, and ``False`` otherwise. + + For generic arithmetic subgroups this is done by the absurdly + slow algorithm of checking all of the generators of ``self`` + to see if they are in ``right``. EXAMPLES:: @@ -571,14 +575,9 @@ def is_subgroup(self, right): True """ # ridiculously slow generic algorithm + return all(g in right for g in self.gens()) - w = self.gens() - for g in w: - if g not in right: - return False - return True - - def is_normal(self): + def is_normal(self) -> bool: r""" Return ``True`` precisely if this subgroup is a normal subgroup of ``SL2Z``. @@ -592,11 +591,11 @@ def is_normal(self): """ for x in self.gens(): for y in SL2Z.gens(): - if y*SL2Z(x)*(~y) not in self: + if y * SL2Z(x) * (~y) not in self: return False return True - def is_odd(self): + def is_odd(self) -> bool: r""" Return ``True`` precisely if this subgroup does not contain the matrix -1. @@ -614,7 +613,7 @@ def is_odd(self): """ return not self.is_even() - def is_even(self): + def is_even(self) -> bool: r""" Return ``True`` precisely if this subgroup contains the matrix -1. @@ -675,7 +674,7 @@ def reduce_cusp(self, c): element `\tfrac{r}{s}` with r,s coprime nonnegative integers, s as small as possible, and r as small as possible for that s. - NOTE: This function should be overridden by all subclasses. + .. NOTE:: This function should be overridden by all subclasses. EXAMPLES:: @@ -743,11 +742,13 @@ def _find_cusps(self): ... NotImplementedError - NOTE: There is a generic algorithm implemented at the top level that - uses the coset representatives of ``self``. This is *very slow* and for all - the standard congruence subgroups there is a quicker way of doing it, - so this should usually be overridden in subclasses; but it doesn't have - to be. + .. NOTE:: + + There is a generic algorithm implemented at the top level that + uses the coset representatives of ``self``. This is *very slow* and for all + the standard congruence subgroups there is a quicker way of doing it, + so this should usually be overridden in subclasses; but it doesn't have + to be. """ i = Cusp([1, 0]) L = [i] @@ -815,7 +816,7 @@ def are_equivalent(self, x, y, trans=False): return True return False - def cusp_data(self, c): + def cusp_data(self, c) -> tuple: r""" Return a triple `(g, w, t)` where `g` is an element of ``self`` generating the stabiliser of the given cusp, `w` is the width of the @@ -833,20 +834,21 @@ def cusp_data(self, c): # first find an element of SL2Z sending infinity to the given cusp w = lift_to_sl2z(c.denominator(), c.numerator(), 0) - g = SL2Z([w[3], w[1], w[2],w[0]]) + g = SL2Z([w[3], w[1], w[2], w[0]]) for d in range(1,1+self.index()): - if g * SL2Z([1,d,0,1]) * (~g) in self: + if g * SL2Z([1, d, 0, 1]) * (~g) in self: return (g * SL2Z([1,d,0,1]) * (~g), d, 1) - elif g * SL2Z([-1,-d,0,-1]) * (~g) in self: - return (g * SL2Z([-1,-d,0,-1]) * (~g), d, -1) + elif g * SL2Z([-1, -d, 0, -1]) * (~g) in self: + return (g * SL2Z([-1, -d, 0, -1]) * (~g), d, -1) raise ArithmeticError("Can't get here!") - def is_regular_cusp(self, c): + def is_regular_cusp(self, c) -> bool: r""" Return ``True`` if the orbit of the given cusp is a regular cusp for - ``self``, otherwise ``False``. This is automatically true if -1 is in - ``self``. + ``self``, otherwise ``False``. + + This is automatically true if -1 is in ``self``. EXAMPLES:: @@ -857,7 +859,7 @@ def is_regular_cusp(self, c): """ if self.is_even(): return True - return (self.cusp_data(c)[2] == 1) + return self.cusp_data(c)[2] == 1 def cusp_width(self, c): r""" @@ -887,7 +889,6 @@ def index(self): ... NotImplementedError """ - return len(list(self.coset_reps())) def generalised_level(self): @@ -937,13 +938,12 @@ def projective_index(self): sage: Gamma1(5).projective_index() 12 """ - if self.is_even(): return self.index() else: return self.index() // 2 - def is_congruence(self): + def is_congruence(self) -> bool: r""" Return ``True`` if ``self`` is a congruence subgroup. @@ -956,7 +956,6 @@ def is_congruence(self): ... NotImplementedError """ - raise NotImplementedError def genus(self): @@ -983,8 +982,9 @@ def genus(self): def farey_symbol(self): r""" - Return the Farey symbol associated to this subgroup. See the - :mod:`~sage.modular.arithgroup.farey_symbol` module for more + Return the Farey symbol associated to this subgroup. + + See the :mod:`~sage.modular.arithgroup.farey_symbol` module for more information. EXAMPLES:: @@ -1083,24 +1083,26 @@ def ngens(self): def ncusps(self): r""" - Return the number of cusps of this arithmetic subgroup. This is - provided as a separate function since for dimension formulae in even - weight all we need to know is the number of cusps, and this can be - calculated very quickly, while enumerating all cusps is much slower. + Return the number of cusps of this arithmetic subgroup. + + This is provided as a separate function since for dimension + formulae in even weight all we need to know is the number of + cusps, and this can be calculated very quickly, while + enumerating all cusps is much slower. EXAMPLES:: sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.ncusps(Gamma0(7)) 2 """ - return ZZ(len(self.cusps())) def nregcusps(self): r""" Return the number of cusps of ``self`` that are "regular", i.e. their - stabiliser has a generator with both eigenvalues +1 rather than -1. If - the group contains -1, every cusp is clearly regular. + stabiliser has a generator with both eigenvalues +1 rather than -1. + + If the group contains -1, every cusp is clearly regular. EXAMPLES:: @@ -1113,8 +1115,9 @@ def nirregcusps(self): r""" Return the number of cusps of ``self`` that are "irregular", i.e. their stabiliser can only be generated by elements with both eigenvalues -1 - rather than +1. If the group contains -1, every cusp is clearly - regular. + rather than +1. + + If the group contains -1, every cusp is clearly regular. EXAMPLES:: @@ -1122,14 +1125,15 @@ def nirregcusps(self): 1 """ if self.is_even(): - return 0 - else: - return ZZ(len([c for c in self.cusps() if not self.is_regular_cusp(c)])) + return ZZ.zero() + return ZZ(len([1 for c in self.cusps() if not self.is_regular_cusp(c)])) def dimension_modular_forms(self, k=2): r""" Return the dimension of the space of weight k modular forms for this - group. This is given by a standard formula in terms of k and various + group. + + This is given by a standard formula in terms of k and various invariants of the group; see Diamond + Shurman, "A First Course in Modular Forms", section 3.5 and 3.6. If k is not given, defaults to k = 2. @@ -1162,7 +1166,9 @@ def dimension_modular_forms(self, k=2): def dimension_cusp_forms(self, k=2): r""" Return the dimension of the space of weight k cusp forms for this - group. For `k \ge 2`, this is given by a standard formula in terms of k + group. + + For `k \ge 2`, this is given by a standard formula in terms of k and various invariants of the group; see Diamond + Shurman, "A First Course in Modular Forms", section 3.5 and 3.6. If k is not given, default to k = 2. @@ -1192,7 +1198,7 @@ def dimension_cusp_forms(self, k=2): """ k = ZZ(k) if k <= 0: - return ZZ(0) + return ZZ.zero() if not (k % 2): # k even @@ -1207,7 +1213,7 @@ def dimension_cusp_forms(self, k=2): # k odd if self.is_even(): - return ZZ(0) + return ZZ.zero() else: e_reg = self.nregcusps() @@ -1217,7 +1223,7 @@ def dimension_cusp_forms(self, k=2): return (k-1)*(self.genus()-1) + (k // ZZ(3)) * self.nu3() + (k-2)/ZZ(2) * e_reg + (k-1)/ZZ(2) * e_irr else: if e_reg > 2*self.genus() - 2: - return ZZ(0) + return ZZ.zero() else: raise NotImplementedError("Computation of dimensions of weight 1 cusp forms spaces not implemented in general") @@ -1243,23 +1249,23 @@ def dimension_eis(self, k=2): 4 """ if k < 0: - return ZZ(0) + return ZZ.zero() if k == 0: - return ZZ(1) + return ZZ.one() - if not (k % 2): # k even + if not (k % 2): # k even if k > 2: return self.ncusps() - else: # k = 2 + else: # k = 2 return self.ncusps() - 1 - else: # k odd + else: # k odd if self.is_even(): - return 0 + return ZZ.zero() if k > 1: return self.nregcusps() - else: # k = 1 - return ZZ(self.nregcusps() / ZZ(2)) + else: # k = 1 + return ZZ(self.nregcusps() // ZZ(2)) def as_permutation_group(self): r""" @@ -1287,7 +1293,7 @@ def as_permutation_group(self): sage: P.an_element() in G True """ - _,_,l_edges,s2_edges = self.todd_coxeter() + _, _, l_edges, s2_edges = self.todd_coxeter() n = len(l_edges) s3_edges = [None] * n r_edges = [None] * n diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index 3431dfcac5c..388cabfeed1 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -124,6 +124,7 @@ Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z) Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z) + def sl2z_word_problem(A): r""" Given an element of `\SL_2(\ZZ)`, express it as a word in the generators L = @@ -151,7 +152,7 @@ def sl2z_word_problem(A): output = [] # If A00 is zero - if A[0,0] == 0: + if A[0, 0] == 0: c = A[1,1] if c != 1: A = A*Lm**(c-1)*Rm*Lmi @@ -160,12 +161,12 @@ def sl2z_word_problem(A): A = A*Rm*Lmi output.extend([(1,-1),(0,1)]) - if A[0,0] < 0: # Make sure A00 is positive + if A[0, 0] < 0: # Make sure A00 is positive A = SL2Z(-1)*A output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)]) if A[0,1] < 0: # if A01 is negative make it positive - n = (-A[0,1]/A[0,0]).ceil() #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0] + n = (-A[0,1]/A[0,0]).ceil() # n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0] A = A*Lm**n output.append((0, -n)) # At this point A00>0 and A01>=0 @@ -180,10 +181,10 @@ def sl2z_word_problem(A): A = A*SL2Z([1,-n,0,1]) output.append((0, n)) - if A == SL2Z(1): + if A == SL2Z.one(): pass # done, so don't add R^0 - elif A[0,0] == 0: - c = A[1,1] + elif A[0, 0] == 0: + c = A[1, 1] if c != 1: A = A*Lm**(c-1)*Rm*Lmi output.extend([(0,1-c),(1,-1),(0, 1)]) @@ -199,6 +200,7 @@ def sl2z_word_problem(A): output.reverse() return output + def eval_sl2z_word(w): r""" Given a word in the format output by :func:`sl2z_word_problem`, convert it back @@ -216,6 +218,7 @@ def eval_sl2z_word(w): w1 = w return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm) + def word_of_perms(w, p1, p2): r""" Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations @@ -260,6 +263,7 @@ def word_of_perms(w, p1, p2): return M + def _equalize_perms(l): r""" Transform a list of lists into a list of lists with identical length. Each @@ -286,6 +290,7 @@ def _equalize_perms(l): # interpreted as L and R. Hence the order of the arguments is slightly # different from the class __init__ methods. + def ArithmeticSubgroup_Permutation( L=None, R=None, S2=None, S3=None, relabel=False, @@ -454,6 +459,7 @@ def ArithmeticSubgroup_Permutation( return G + class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup): r""" A subgroup of `\SL_2(\ZZ)` defined by the action of generators on its @@ -1484,7 +1490,7 @@ def is_congruence(self): r = R**d s = l**20 * r**onefifth * l**(-4) * ~r - #Congruence if the seven permutations below are trivial: + # Congruence if the seven permutations below are trivial: rel = ~a*~r*a*r if not rel.is_one(): verbose("Failed relation B1") @@ -1853,6 +1859,7 @@ def ncusps(self): m += 1 return n + m//2 + class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class): r""" An arithmetic subgroup of `\SL(2, \ZZ)` containing `-1`, represented diff --git a/src/sage/modular/arithgroup/congroup.pyx b/src/sage/modular/arithgroup/congroup.pyx index ee988672a4f..d55c0c0ce7c 100644 --- a/src/sage/modular/arithgroup/congroup.pyx +++ b/src/sage/modular/arithgroup/congroup.pyx @@ -116,7 +116,7 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): # total number of coset representatives that we'll find n = Gamma0(N).index() / Gamma0(M).index() k = 0 # number found so far - Ndivt = N / t + Ndivt = N // t R = check_allocarray(4 * n, sizeof(int)) halfmax = 2*(n+10) while k < n: @@ -126,10 +126,10 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa) if g == 0: continue - cc = cc / g + cc = cc // g if cc % M != 0: continue - dd = dd / g + dd = dd // g # Test if we've found a new coset representative. is_new = 1 for i in range(k): @@ -217,7 +217,7 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): # total number of coset representatives that we'll find n = Gamma1(N).index() / Gamma1(M).index() d = arith_int.c_gcd_int(t, N // t) - n = n / d + n = n // d k = 0 # number found so far Ndivt = N // t R = check_allocarray(4 * n, sizeof(int)) @@ -229,10 +229,10 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): g = arith_int.c_xgcd_int(-cc, dd, &bb, &aa) if g == 0: continue - cc = cc / g + cc = cc // g if cc % M != 0: continue - dd = dd / g + dd = dd // g if M != 1 and dd % M != 1: continue # Test if we've found a new coset representative. diff --git a/src/sage/modular/arithgroup/congroup_gamma.py b/src/sage/modular/arithgroup/congroup_gamma.py index 2eec9b267ac..78d878aaf9d 100644 --- a/src/sage/modular/arithgroup/congroup_gamma.py +++ b/src/sage/modular/arithgroup/congroup_gamma.py @@ -2,13 +2,13 @@ Congruence subgroup `\Gamma(N)` """ -#***************************************************************************** +# **************************************************************************** # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import gcd from sage.groups.matrix_gps.finitely_generated import MatrixGroup @@ -26,6 +26,8 @@ _gamma_cache = {} + + def Gamma_constructor(N): r""" Return the congruence subgroup `\Gamma(N)`. diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index a01c1dd1683..ca0d050fe6f 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -8,8 +8,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import kronecker_symbol, divisors, euler_phi, gcd, moebius from sage.misc.cachefunc import cached_method @@ -47,6 +47,8 @@ def is_Gamma0(x): _gamma0_cache = {} + + def Gamma0_constructor(N): """ Return the congruence subgroup Gamma0(N). @@ -143,7 +145,7 @@ def __init__(self, level): # be done if needed by the _generators_for_H and _list_of_elements_in_H # methods. # - #GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()]) + # GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()]) def _repr_(self): """ diff --git a/src/sage/modular/arithgroup/congroup_gamma1.py b/src/sage/modular/arithgroup/congroup_gamma1.py index 18a1c8c0950..c450d98a783 100644 --- a/src/sage/modular/arithgroup/congroup_gamma1.py +++ b/src/sage/modular/arithgroup/congroup_gamma1.py @@ -3,13 +3,13 @@ Congruence subgroup `\Gamma_1(N)` """ -#***************************************************************************** +# **************************************************************************** # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method @@ -44,13 +44,14 @@ def is_Gamma1(x): """ from sage.misc.superseded import deprecation deprecation(38035, "The function is_Gamma1 is deprecated; use 'isinstance(..., Gamma1_class)' instead.") - #from congroup_sl2z import is_SL2Z - #return (isinstance(x, Gamma1_class) or is_SL2Z(x)) + # from congroup_sl2z import is_SL2Z + # return (isinstance(x, Gamma1_class) or is_SL2Z(x)) return isinstance(x, Gamma1_class) _gamma1_cache = {} + def Gamma1_constructor(N): r""" Return the congruence subgroup `\Gamma_1(N)`. @@ -487,7 +488,7 @@ def dimension_cusp_forms(self, k=2, eps=None, algorithm='CohenOesterle'): from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) ) - else: #algorithm not in ["CohenOesterle", "Quer"]: + else: # algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms") def dimension_eis(self, k=2, eps=None, algorithm='CohenOesterle'): @@ -573,7 +574,7 @@ def dimension_eis(self, k=2, eps=None, algorithm='CohenOesterle'): else: return alpha - self.dimension_cusp_forms(k, eps) - else: #algorithm not in ["CohenOesterle", "Quer"]: + else: # algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis") def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm='CohenOesterle'): diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py index 507418bd239..41a09602a88 100644 --- a/src/sage/modular/arithgroup/congroup_generic.py +++ b/src/sage/modular/arithgroup/congroup_generic.py @@ -432,6 +432,7 @@ def image_mod_n(self): """ return self.__G + class CongruenceSubgroup(CongruenceSubgroupFromGroup): r""" One of the "standard" congruence subgroups `\Gamma_0(N)`, `\Gamma_1(N)`, @@ -569,6 +570,7 @@ def _new_group_from_level(self, level): else: raise NotImplementedError + def _minimize_level(G): r""" Utility function. Given a matrix group `G` contained in `SL(2, \ZZ / N\ZZ)` diff --git a/src/sage/modular/arithgroup/congroup_sl2z.py b/src/sage/modular/arithgroup/congroup_sl2z.py index 7e15eaca383..d95c9e18764 100644 --- a/src/sage/modular/arithgroup/congroup_sl2z.py +++ b/src/sage/modular/arithgroup/congroup_sl2z.py @@ -46,6 +46,7 @@ def is_SL2Z(x): deprecation(38035, "The function is_SL2Z is deprecated; use 'isinstance(..., SL2Z_class)' instead.") return isinstance(x, SL2Z_class) + class SL2Z_class(Gamma0_class): r""" The full modular group `\SL_2(\ZZ)`, regarded as a congruence @@ -250,6 +251,7 @@ def random_element(self, bound=100, *args, **kwds): SL2Z = SL2Z_class() + def _SL2Z_ref(): """ Return SL2Z. (Used for pickling SL2Z.). diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index bb48288eec2..c965015b41d 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -279,7 +279,7 @@ cdef class Farey: [ -3 1] [-40 13] """ - gens_dict = {g:i+1 for i,g in enumerate(self.generators())} + gens_dict = {g: i+1 for i, g in enumerate(self.generators())} ans = [] for pm in self.pairing_matrices(): a, b, c, d = pm.matrix().list() @@ -331,7 +331,7 @@ cdef class Farey: sage: (-g.matrix()).is_one() True """ - for i,g in enumerate(self.generators()): + for i, g in enumerate(self.generators()): m = g.matrix() if (-m).is_one(): return [i + 1] @@ -342,15 +342,17 @@ cdef class Farey: return 3 * [i + 1] return [] - def word_problem(self, M, output = 'standard', check = True): + def word_problem(self, M, output='standard', check=True): r""" Solve the word problem (up to sign) using this Farey symbol. INPUT: - ``M`` -- an element `M` of `\SL_2(\ZZ)` - - ``output`` -- (default: ``'standard'``) should be one of ``'standard'``, - ``'syllables'``, ``'gens'``. + + - ``output`` -- (default: ``'standard'``) should be one of + ``'standard'``, ``'syllables'``, ``'gens'``. + - ``check`` -- boolean (default: ``True``); whether to check for correct input and output @@ -359,17 +361,17 @@ cdef class Farey: A solution to the word problem for the matrix `M`. The format depends on the ``output`` parameter, as follows. - - ``standard`` returns the so called the Tietze representation, - consists of a tuple of nonzero integers `i`, where if `i` > 0 - then it indicates the `i`-th generator (that is, ``self.generators()[0]`` - would correspond to `i` = 1), and if `i` < 0 then it indicates - the inverse of the `i`-th generator. - - ``syllables`` returns a tuple of tuples of the form `(i,n)`, where - `(i,n)` represents ``self.generators()[i] ^ n``, + - ``'standard'`` returns the so called Tietze representation, + which consists of a tuple of nonzero integers. A positive + integer `i` indicates the `i`-th generator (that is, + ``self.generators()[i-1]``), while a negative integer `i` + indicates the inverse of the `i`-th generator. + - ``'syllables'`` returns a tuple of tuples of the form + `(i, n)`, where `(i, n)` represents ``self.generators()[i] ^ n``, whose product equals `M` up to sign. - - ``gens`` returns tuple of tuples of the form `(g,n)`, - `(g,n)` such that the product of the matrices `g^n` - equals `M` up to sign. + - ``'gens'`` returns a tuple of pairs `(g, n)`, where `g` is a + matrix and `n` an integer, such that the product of the + matrices `g^n` equals `M` up to sign. EXAMPLES:: @@ -385,7 +387,7 @@ cdef class Farey: sage: g [-5048053 586303] [-5558280 645563] - sage: F.word_problem(g, output = 'gens') + sage: F.word_problem(g, output='gens') (( [109 -10] [120 -11], 1 @@ -402,7 +404,7 @@ cdef class Farey: [17 -2] [60 -7], 1 )) - sage: F.word_problem(g, output = 'syllables') + sage: F.word_problem(g, output='syllables') ((3, 1), (10, 2), (8, -1), (5, 1)) TESTS: @@ -413,7 +415,7 @@ cdef class Farey: sage: G = Gamma0(10) sage: F = G.farey_symbol() sage: g = G([-701,-137,4600,899]) - sage: g1 = prod(F.generators()[i]**a for i,a in F.word_problem(g, output = 'syllables')) + sage: g1 = prod(F.generators()[i]**a for i, a in F.word_problem(g, output='syllables')) sage: g == g1 True @@ -426,15 +428,16 @@ cdef class Farey: Check that :issue:`20347` is solved:: sage: from sage.misc.misc_c import prod - sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)") + sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)", S3="(1,2,3)") sage: S = G.farey_symbol() - sage: g1,g2 = S.generators() + sage: g1, g2 = S.generators() sage: g = g1^3 * g2^-2 * g1 * g2 sage: S.word_problem(g) (2, 2, 2, 1, 1, 1, 2, 1, 2) - sage: h = prod(S.generators()[i]**a for i,a in S.word_problem(g, output = 'syllables')) + sage: h = prod(S.generators()[i]**a for i, a in S.word_problem(g, output='syllables')) sage: g == h True + """ if output not in ['standard', 'syllables', 'gens']: raise ValueError('Unrecognized output format') @@ -464,7 +467,7 @@ cdef class Farey: if sgn == -1: beta, mbeta = mbeta, beta - gens_dict = {g:i+1 for i,g in enumerate(self.generators())} + gens_dict = {g: i+1 for i, g in enumerate(self.generators())} extra_tietze = [] if beta.is_one(): found = True @@ -501,13 +504,13 @@ cdef class Farey: for i in range(len(tietze)): t = tietze[i] tmp = tmp * gens[t-1] if t > 0 else tmp * gens[-t-1]**-1 - assert tmp.matrix() == M.matrix(),'%s %s %s' % (tietze, tmp.matrix(),M.matrix()) + assert tmp.matrix() == M.matrix(), '%s %s %s' % (tietze, tmp.matrix(), M.matrix()) if output == 'standard': return tuple(tietze) if output == 'syllables': - return tuple((a-1,len(list(g))) if a > 0 else (-a-1,-len(list(g))) for a,g in groupby(tietze)) + return tuple((a-1, len(list(g))) if a > 0 else (-a-1, -len(list(g))) for a, g in groupby(tietze)) else: # output == 'gens' - return tuple((gens[a-1],len(list(g))) if a > 0 else (gens[-a-1],-len(list(g))) for a, g in groupby(tietze)) + return tuple((gens[a-1], len(list(g))) if a > 0 else (gens[-a-1], -len(list(g))) for a, g in groupby(tietze)) def __contains__(self, M): r""" @@ -594,9 +597,9 @@ cdef class Farey: EXAMPLES:: - sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'plain') + sage: FareySymbol(Gamma0(11))._latex_(forced_format='plain') '\\left( -\\infty\\underbrace{\\quad}_{1} 0\\underbrace{\\quad}_{2} \\frac{1}{3}\\underbrace{\\quad}_{3} \\frac{1}{2}\\underbrace{\\quad}_{2} \\frac{2}{3}\\underbrace{\\quad}_{3} 1\\underbrace{\\quad}_{1} \\infty\\right)' - sage: FareySymbol(Gamma0(11))._latex_(forced_format = 'xymatrix') + sage: FareySymbol(Gamma0(11))._latex_(forced_format='xymatrix') '\\begin{xy}\\xymatrix{& -\\infty \\ar@{-}@/_1pc/[r]_{1}& 0 \\ar@{-}@/_1pc/[r]_{2}& \\frac{1}{3} \\ar@{-}@/_1pc/[r]_{3}& \\frac{1}{2} \\ar@{-}@/_1pc/[r]_{2}& \\frac{2}{3} \\ar@{-}@/_1pc/[r]_{3}& 1 \\ar@{-}@/_1pc/[r]_{1}& \\infty }\\end{xy}' sage: 'xymatrix' in FareySymbol(Gamma0(11))._latex_() diff --git a/src/sage/modular/arithgroup/meson.build b/src/sage/modular/arithgroup/meson.build new file mode 100644 index 00000000000..c4a68af3217 --- /dev/null +++ b/src/sage/modular/arithgroup/meson.build @@ -0,0 +1,65 @@ +py.install_sources( + 'all.py', + 'arithgroup_generic.py', + 'arithgroup_perm.py', + 'congroup_gamma.py', + 'congroup_gamma0.py', + 'congroup_gamma1.py', + 'congroup_gammaH.py', + 'congroup_generic.py', + 'congroup_sl2z.py', + 'tests.py', + subdir: 'sage/modular/arithgroup', +) + +extension_data = { + 'arithgroup_element' : files('arithgroup_element.pyx'), + 'congroup' : files('congroup.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular/arithgroup', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + +# Manually create header file, which otherwise is not found +farey_symbol_header = custom_target( + 'farey_symbol.h', + output: 'farey_symbol.h', + input: 'farey_symbol.pyx', + command: [ + cython.cmd_array(), + '--cplus', + '@INPUT@', + '-o', + '@OUTPUT@', + '-I', + join_paths(meson.current_source_dir(), '../../../'), + ], +) + +extension_data_cpp = { + 'farey_symbol': [ + files('farey.cpp', 'farey_symbol.pyx', 'sl2z.cpp'), + farey_symbol_header[0], + ], +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular/arithgroup', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_flint, inc_rings, inc_src], + dependencies: [py_dep, cysignals, flint, gmp, gmpxx], + ) +endforeach + diff --git a/src/sage/modular/arithgroup/tests.py b/src/sage/modular/arithgroup/tests.py index b00e39c0cda..f096b0a0429 100644 --- a/src/sage/modular/arithgroup/tests.py +++ b/src/sage/modular/arithgroup/tests.py @@ -311,7 +311,7 @@ def test_congruence_groups(self): if getattr(G, f)() != getattr(GG, f)(): raise AssertionError("results of %s does not coincide for %s" % (f, G)) - if sorted((G.cusp_width(c) for c in G.cusps())) != GG.cusp_widths(): + if sorted(G.cusp_width(c) for c in G.cusps()) != GG.cusp_widths(): raise AssertionError("Cusps widths are different for %s" % G) for _ in range(20): diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index d4bdb13e6b1..280acec8874 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -2914,7 +2914,7 @@ def _get_hecke_data(self, l): while not V: V = [g for g in self._find_elements_in_order(l * p ** nninc) if prod([self._character(ZZ((v * Matrix(ZZ, 4, 1, g))[0, 0])) - / self._character((p ** (nninc // 2))) + / self._character(p ** (nninc // 2)) for v in self.get_extra_embedding_matrices()]) == 1] if not V: nninc += 2 @@ -2927,7 +2927,7 @@ def _get_hecke_data(self, l): letters = self.get_nontorsion_generators() letters += [g for g in self._find_elements_in_order(1) if prod([self._character(ZZ((v * Matrix(ZZ, 4, 1, g))[0, 0])) - / self._character((p ** (nninc // 2))) + / self._character(p ** (nninc // 2)) for v in self.get_extra_embedding_matrices()]) == 1] n_iters = 0 diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 54acfeeeaa9..53a7d2ac6f6 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -1161,7 +1161,7 @@ def basis_matrix(self): d = self._k - 1 for e in self._E: try: - g = next((g for g in S[e.label] if g[2])) + g = next(g for g in S[e.label] if g[2]) C = self._U.acting_matrix(self._Sigma0(self.embed_quaternion(g[0])), d).transpose() # Warning - Need to allow the check = True C -= self._U.acting_matrix(self._Sigma0(Matrix(QQ, 2, 2, p ** g[1])), d).transpose() # Warning - Need to allow the check = True stab_conds.append([e.label, C]) @@ -2113,7 +2113,7 @@ def coleman(self, t1, t2, E=None, method='moments', mult=False): assert 0 value += new if mult: - value_exp *= K.teichmuller(((b - d * t1) / (b - d * t2))) ** Integer(c_e.moment(0).rational_reconstruction()) + value_exp *= K.teichmuller((b - d * t1) / (b - d * t2)) ** Integer(c_e.moment(0).rational_reconstruction()) else: print('The available methods are either "moments" or "riemann_sum". The latter is only provided for consistency check, and should not be used in practice.') diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 117f1223d9e..c261a84e8de 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -1386,7 +1386,8 @@ def gauss_sum(self, a=1): elif isinstance(K, sage.rings.abc.AlgebraicField): L = K zeta = L.zeta(m) - elif isinstance(K, sage.rings.abc.NumberField_cyclotomic) or isinstance(K, RationalField): + elif isinstance(K, (sage.rings.abc.NumberField_cyclotomic, + RationalField)): chi = chi.minimize_base_ring() n = lcm(m, G.zeta_order()) L = CyclotomicField(n) @@ -1467,7 +1468,8 @@ def phi(t): from sage.rings.complex_mpfr import ComplexField CC = ComplexField(prec) phi = CC.coerce_map_from(K) - elif isinstance(K, sage.rings.abc.NumberField_cyclotomic) or isinstance(K, RationalField): + elif isinstance(K, (sage.rings.abc.NumberField_cyclotomic, + RationalField)): phi = K.complex_embedding(prec) CC = phi.codomain() else: @@ -1688,8 +1690,9 @@ def kloosterman_sum_numerical(self, prec=53, a=1, b=0): """ G = self.parent() K = G.base_ring() - if not (isinstance(K, sage.rings.abc.NumberField_cyclotomic) or isinstance(K, RationalField)): - raise NotImplementedError("Kloosterman sums only currently implemented when the base ring is a cyclotomic field or QQ.") + if not isinstance(K, (sage.rings.abc.NumberField_cyclotomic, + RationalField)): + raise NotImplementedError("Kloosterman sums only currently implemented when the base ring is a cyclotomic field or QQ") phi = K.complex_embedding(prec) CC = phi.codomain() g = 0 @@ -2380,7 +2383,8 @@ class DirichletGroupFactory(UniqueFactory): :: - sage: r4 = CyclotomicField(4).ring_of_integers() + sage: K = CyclotomicField(4) + sage: r4 = K.ring_of_integers() sage: G = DirichletGroup(60, r4) sage: G.gens() (Dirichlet character modulo 60 of conductor 4 @@ -2393,8 +2397,7 @@ class DirichletGroupFactory(UniqueFactory): zeta4 sage: parent(val) Gaussian Integers generated by zeta4 in Cyclotomic Field of order 4 and degree 2 - sage: r4_29_0 = r4.residue_field(r4.ideal(29).factor()[0][0]); r4_29_0(val) - doctest:warning ... DeprecationWarning: ... + sage: r4_29_0 = r4.residue_field(K(29).factor()[0][0]); r4_29_0(val) 17 sage: r4_29_0(val) * GF(29)(3) 22 diff --git a/src/sage/modular/drinfeld_modform/element.py b/src/sage/modular/drinfeld_modform/element.py index dd6d48ad99e..59d7a9e23ff 100644 --- a/src/sage/modular/drinfeld_modform/element.py +++ b/src/sage/modular/drinfeld_modform/element.py @@ -25,6 +25,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.polynomial.multi_polynomial import MPolynomial + class DrinfeldModularFormsElement(ModuleElement): r""" Element class of rings of Drinfeld modular forms. diff --git a/src/sage/modular/drinfeld_modform/ring.py b/src/sage/modular/drinfeld_modform/ring.py index c113523fc48..43c41058fe2 100644 --- a/src/sage/modular/drinfeld_modform/ring.py +++ b/src/sage/modular/drinfeld_modform/ring.py @@ -44,6 +44,7 @@ from .element import DrinfeldModularFormsElement + class DrinfeldModularForms(Parent, UniqueRepresentation): r""" Base class for the graded ring of Drinfeld modular forms. @@ -430,13 +431,11 @@ def _coefficient_forms(self, a): [(T^2 + T)*g1, g1^3 + (T^4 + T)*g2, g1^4*g2 + g1*g2^2, g2^5] """ a = a.numerator() - d = a.degree() poly_ring = PolynomialRing(self._base_ring, self.rank(), 'g') poly_ring_gens = poly_ring.gens() Frob = poly_ring.frobenius_endomorphism() gen = [self._base_ring.gen()] - for g in poly_ring_gens: - gen.append(g) + gen.extend(poly_ring_gens) ore_pol_ring = OrePolynomialRing(poly_ring, Frob, 't') gen = ore_pol_ring(gen) f = sum(c*(gen**idx) for idx, c in enumerate(a.coefficients(sparse=False))) @@ -582,8 +581,6 @@ def coefficient_forms(self, a=None): ... TypeError: unable to convert a to an element in Fq[T] """ - K = self._base_ring - T = K.gen() if a is None: return [self._generator_coefficient_form(i) for i in range(1, self.rank() + 1)] diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py index 4cd632db1aa..383a4e0c035 100644 --- a/src/sage/modular/hecke/algebra.py +++ b/src/sage/modular/hecke/algebra.py @@ -26,7 +26,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from typing import Iterator +from collections.abc import Iterator from sage.rings.infinity import infinity from sage.categories.algebras import Algebras @@ -35,12 +35,12 @@ from sage.arith.misc import gcd from sage.misc.latex import latex from sage.matrix.matrix_space import MatrixSpace -from sage.rings.ring import CommutativeRing from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.structure.element import Element from sage.structure.unique_representation import CachedRepresentation from sage.misc.cachefunc import cached_method +from sage.structure.parent import Parent from sage.structure.richcmp import richcmp_method, richcmp @@ -111,7 +111,7 @@ def _heckebasis(M): @richcmp_method -class HeckeAlgebra_base(CachedRepresentation, CommutativeRing): +class HeckeAlgebra_base(CachedRepresentation, Parent): """ Base class for algebras of Hecke operators on a fixed Hecke module. @@ -186,8 +186,7 @@ def __init__(self, M) -> None: raise TypeError(msg) self.__M = M cat = Algebras(M.base_ring()).Commutative() - CommutativeRing.__init__(self, base_ring=M.base_ring(), - category=cat) + Parent.__init__(self, base=M.base_ring(), category=cat) def _an_element_(self): r""" diff --git a/src/sage/modular/hypergeometric_misc.pxd b/src/sage/modular/hypergeometric_misc.pxd index 00bf9a97e9a..ccd013ca2ee 100644 --- a/src/sage/modular/hypergeometric_misc.pxd +++ b/src/sage/modular/hypergeometric_misc.pxd @@ -1,2 +1,3 @@ -cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, +cpdef hgm_coeffs(long long p, unsigned int f, + int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 3be8e4dd545..f3e37b515d9 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -6,7 +6,8 @@ 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, +cpdef hgm_coeffs(long long p, unsigned int f, + int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): r""" Compute coefficients for the hypergeometric trace formula. @@ -35,8 +36,8 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, """ from sage.rings.padics.factory import Zp - cdef int gl, j, k, l, v, gv - cdef long long i, q1, w, w1, w2, q2, r, r1 + cdef int gl, j, k, v, gv + cdef long long i, l, q1, w, w1, w2, q2, r, r1 cdef bint flip, use_longlongs q1 = p ** f - 1 diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 32c7c88c0cf..5418a1fff58 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -187,6 +187,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f data[k] = sign * coeffs[d - k] * q**(i * (k - d / 2)) return ring(data) + def enumerate_hypergeometric_data(d, weight=None): r""" Return an iterator over parameters of hypergeometric motives (up to swapping). diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index 029dcc822b0..04d9123ab57 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -37,6 +37,7 @@ from .type_space import TypeSpace from .smoothchar import SmoothCharacterGroupQp, SmoothCharacterGroupUnramifiedQuadratic, SmoothCharacterGroupRamifiedQuadratic + def LocalComponent(f, p, twist_factor=None): r""" Calculate the local component at the prime `p` of the automorphic @@ -115,6 +116,7 @@ def LocalComponent(f, p, twist_factor=None): mintwist = LocalComponent(g, p, twist_factor) return ImprimitiveLocalComponent(f, p, twist_factor, mintwist, chi) + class LocalComponentBase(SageObject): r""" Base class for local components of newforms. Not to be directly instantiated; use the :func:`~LocalComponent` constructor function. @@ -316,6 +318,7 @@ def __ne__(self, other): """ return not (self == other) + class PrimitiveLocalComponent(LocalComponentBase): r""" Base class for primitive (twist-minimal) local components. @@ -346,6 +349,7 @@ def minimal_twist(self): """ return self + class PrincipalSeries(PrimitiveLocalComponent): r""" A principal series representation. This is an abstract base class, not to @@ -399,6 +403,7 @@ def characters(self): """ pass + class UnramifiedPrincipalSeries(PrincipalSeries): r""" An unramified principal series representation of `{\rm GL}_2(\QQ_p)` @@ -469,6 +474,7 @@ def characters(self): G = SmoothCharacterGroupQp(self.prime(), d.parent()) return Sequence([G.character(0, [d]), G.character(0, [self.newform()[self.prime()] - d])], cr=True, universe=G) + class PrimitivePrincipalSeries(PrincipalSeries): r""" A ramified principal series of the form `\pi(\chi_1, \chi_2)` @@ -500,6 +506,7 @@ def characters(self): chi2 = G.character(0, [self.prime()]) * self.central_character() / chi1 return Sequence([chi1, chi2], cr=True, universe=G) + class PrimitiveSpecial(PrimitiveLocalComponent): r""" A primitive special representation: that is, the Steinberg representation @@ -586,6 +593,7 @@ def check_tempered(self): for sigma in K.embeddings(QQbar): assert sigma(c1(p)).abs() == w + class PrimitiveSupercuspidal(PrimitiveLocalComponent): r""" A primitive supercuspidal representation. @@ -971,6 +979,7 @@ def check_tempered(self): for sigma in K.embeddings(QQbar): assert sigma(c1(p)).abs() == sigma(c2(p)).abs() == w + class ImprimitiveLocalComponent(LocalComponentBase): r""" A smooth representation which is not of minimal level among its character diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 1a05d448d9b..e2cadd4219a 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -1103,6 +1103,7 @@ def quadratic_chars(self): nr = self.character(0, [-1]) return sorted([nr] + list(ram) + [f*nr for f in ram]) + class SmoothCharacterGroupQuadratic(SmoothCharacterGroupGeneric): r""" The group of smooth characters of `E^\times`, where `E` is a quadratic extension of `\QQ_p`. @@ -1391,6 +1392,7 @@ def extend_character(self, level, chi, vals, check=True): raise ValueError("Invalid values for extension") return chiE + class SmoothCharacterGroupUnramifiedQuadratic(SmoothCharacterGroupQuadratic): r""" The group of smooth characters of `\QQ_{p^2}^\times`, where `\QQ_{p^2}` is diff --git a/src/sage/modular/local_comp/type_space.py b/src/sage/modular/local_comp/type_space.py index 9e20c121108..ea43ec89ccf 100644 --- a/src/sage/modular/local_comp/type_space.py +++ b/src/sage/modular/local_comp/type_space.py @@ -62,6 +62,7 @@ def example_type_space(example_no=0): # a ramified (odd p-power level) case return TypeSpace(Newform_constructor('27a'), 3) + def find_in_space(f, A, base_extend=False): r""" Given a Newform object `f`, and a space `A` of modular symbols of the same @@ -147,6 +148,7 @@ def find_in_space(f, A, base_extend=False): return D + class TypeSpace(SageObject): r""" The modular symbol type space associated to a newform, at a prime dividing @@ -401,12 +403,11 @@ def minimal_twist(self): V = A.submodule(VV, check=False) D = V.decomposition()[0] - #if len(D.star_eigenvalues()) == 2: + # if len(D.star_eigenvalues()) == 2: # D = D.sign_submodule(1) D1 = D.modular_symbols_of_sign(1) M = ModularForms(D1.group(), D1.weight(), D1.base_ring()) - ff = Newform(M, D1, names='a') - return ff + return Newform(M, D1, names='a') ##################################### # The group action on the type space. @@ -634,7 +635,7 @@ def _discover_torus_action(self): mats = self._intertwining_basis(a) V = self.t_space.nonembedded_free_module() v = self.eigensymbol_subspace().gen(0) - w = V.submodule_with_basis([m * v for m in mats]).coordinates(v) #v * self.e_space.diamond_eigenvalue(crt(a, 1, f, self.tame_level()))) + w = V.submodule_with_basis([m * v for m in mats]).coordinates(v) # v * self.e_space.diamond_eigenvalue(crt(a, 1, f, self.tame_level()))) self._a = a self._amat = sum([mats[i] * w[i] for i in range(len(mats))]) diff --git a/src/sage/modular/meson.build b/src/sage/modular/meson.build new file mode 100644 index 00000000000..d334cf975c8 --- /dev/null +++ b/src/sage/modular/meson.build @@ -0,0 +1,47 @@ +py.install_sources( + 'all.py', + 'buzzard.py', + 'congroup.py', + 'congroup_element.py', + 'cusps.py', + 'cusps_nf.py', + 'dims.py', + 'dirichlet.py', + 'etaproducts.py', + 'hypergeometric_misc.pxd', + 'hypergeometric_motive.py', + 'multiple_zeta.py', + 'multiple_zeta_F_algebra.py', + subdir: 'sage/modular', +) + +extension_data = {'hypergeometric_misc' : files('hypergeometric_misc.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular', + install: true, + include_directories: [inc_cpython], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +install_subdir('abvar', install_dir: sage_install_dir / 'modular') +subdir('arithgroup') +install_subdir('btquotients', install_dir: sage_install_dir / 'modular') +install_subdir('drinfeld_modform', install_dir: sage_install_dir / 'modular') +install_subdir('hecke', install_dir: sage_install_dir / 'modular') +install_subdir('local_comp', install_dir: sage_install_dir / 'modular') +subdir('modform') +install_subdir( + 'modform_hecketriangle', + install_dir: sage_install_dir / 'modular', +) +subdir('modsym') +install_subdir('overconvergent', install_dir: sage_install_dir / 'modular') +subdir('pollack_stevens') +install_subdir('quasimodform', install_dir: sage_install_dir / 'modular') +install_subdir('quatalg', install_dir: sage_install_dir / 'modular') +install_subdir('ssmod', install_dir: sage_install_dir / 'modular') diff --git a/src/sage/modular/modform/ambient_R.py b/src/sage/modular/modform/ambient_R.py index fcf8a598721..c07597e25dc 100644 --- a/src/sage/modular/modform/ambient_R.py +++ b/src/sage/modular/modform/ambient_R.py @@ -16,6 +16,7 @@ from sage.rings.integer_ring import ZZ from sage.misc.cachefunc import cached_method + class ModularFormsAmbient_R(ambient.ModularFormsAmbient): def __init__(self, M, base_ring): """ diff --git a/src/sage/modular/modform/ambient_eps.py b/src/sage/modular/modform/ambient_eps.py index cf2ab2b9db5..ddd4ed467bc 100644 --- a/src/sage/modular/modform/ambient_eps.py +++ b/src/sage/modular/modform/ambient_eps.py @@ -94,6 +94,7 @@ from . import cuspidal_submodule from . import eisenstein_submodule + class ModularFormsAmbient_eps(ModularFormsAmbient): """ A space of modular forms with character. diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 30a264905bc..425015535c6 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -19,15 +19,15 @@ ] """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004-2006 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import weakref import re @@ -94,13 +94,13 @@ def canonical_parameters(group, level, weight, base_ring): raise NotImplementedError("weight must be at least 1") if isinstance(group, dirichlet.DirichletCharacter): - if ( group.level() != Integer(level) ): + if group.level() != Integer(level): raise ValueError("group.level() and level do not match.") group = group.minimize_base_ring() level = Integer(level) elif isinstance(group, arithgroup.CongruenceSubgroupBase): - if ( Integer(level) != group.level() ): + if Integer(level) != group.level(): raise ValueError("group.level() and level do not match.") # normalize the case of SL2Z if isinstance(group, arithgroup.SL2Z_class) or \ @@ -132,6 +132,7 @@ def canonical_parameters(group, level, weight, base_ring): _cache = {} + def ModularForms_clear_cache(): """ Clear the cache of modular forms. @@ -309,8 +310,8 @@ def ModularForms(group=1, if base_ring is None: base_ring = QQ - if isinstance(group, dirichlet.DirichletCharacter) \ - or isinstance(group, arithgroup.CongruenceSubgroupBase): + if isinstance(group, (dirichlet.DirichletCharacter, + arithgroup.CongruenceSubgroupBase)): level = group.level() else: level = group diff --git a/src/sage/modular/modform/cuspidal_submodule.py b/src/sage/modular/modform/cuspidal_submodule.py index 2f5a94cbf58..5de0805b386 100644 --- a/src/sage/modular/modform/cuspidal_submodule.py +++ b/src/sage/modular/modform/cuspidal_submodule.py @@ -52,6 +52,7 @@ from .submodule import ModularFormsSubmodule from . import weight1 + class CuspidalSubmodule(ModularFormsSubmodule): """ Base class for cuspidal submodules of ambient spaces of modular forms. @@ -211,6 +212,7 @@ def change_ring(self, R): """ return self.ambient_module().change_ring(R).cuspidal_submodule() + class CuspidalSubmodule_R(CuspidalSubmodule): """ Cuspidal submodule over a non-minimal base ring. @@ -626,11 +628,13 @@ def _compute_diamond_matrix(self, d): symbs = self.modular_symbols(sign=1) return _convert_matrix_from_modsyms(symbs, symbs.diamond_bracket_matrix(d))[0] + class CuspidalSubmodule_g1_Q(CuspidalSubmodule_gH_Q): r""" Space of cusp forms for `\Gamma_1(N)` over `\QQ`. """ + class CuspidalSubmodule_eps(CuspidalSubmodule_modsym_qexp): """ Space of cusp forms with given Dirichlet character. @@ -659,6 +663,7 @@ class CuspidalSubmodule_eps(CuspidalSubmodule_modsym_qexp): """ pass + def _convert_matrix_from_modsyms(symbs, T): r""" Given a space of modular symbols and a matrix T acting on it, calculate the @@ -694,17 +699,19 @@ def _convert_matrix_from_modsyms(symbs, T): # we repeatedly use these matrices below, so we store them # once as lists to save time. - hecke_matrix_ls = [ symbs.hecke_matrix(m).list() for m in range(1,r+1) ] - hecke_image_ls = [ (T*symbs.hecke_matrix(m)).list() for m in range(1,r+1) ] + hecke_matrix_ls = [symbs.hecke_matrix(m).list() + for m in range(1, r + 1)] + hecke_image_ls = [(T * symbs.hecke_matrix(m)).list() + for m in range(1, r + 1)] # compute the q-expansions of some cusp forms and their # images under T_n for i in range(d**2): - v = X([ hecke_matrix_ls[m][i] for m in range(r) ]) + v = X([hecke_matrix_ls[m][i] for m in range(r)]) Ynew = Y.span(Y.basis() + [v]) if Ynew.rank() > Y.rank(): basis.append(v) - basis_images.append(X([ hecke_image_ls[m][i] for m in range(r) ])) + basis_images.append(X([hecke_image_ls[m][i] for m in range(r)])) Y = Ynew if len(basis) == d: break diff --git a/src/sage/modular/modform/eis_series.py b/src/sage/modular/modform/eis_series.py index dbdf09d6512..73ea6dcaf1c 100644 --- a/src/sage/modular/modform/eis_series.py +++ b/src/sage/modular/modform/eis_series.py @@ -159,7 +159,7 @@ def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'): E._unsafe_mutate(0, a0) return R(E, prec) # The following is an older slower alternative to the above three lines: - #return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False) + # return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False) else: # This used to work with check=False, but that can only be regarded as # an improbable lucky miracle. Enabling checking is a noticeable speed @@ -170,6 +170,7 @@ def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'): else: return R(eisenstein_series_poly(k, prec).list(), prec=prec, check=True) + def __common_minimal_basering(chi, psi): """ Find the smallest basering over which chi and psi are valued, and @@ -279,7 +280,7 @@ def __find_eisen_chars(character, k): if L not in C: continue GL = C[L] - for R in divisors(N/L): + for R in divisors(N // L): if R not in C: continue GR = C[R] @@ -288,8 +289,8 @@ def __find_eisen_chars(character, k): if chi*psi == eps: chi0, psi0 = __common_minimal_basering(chi, psi) for t in divisors(N//(R*L)): - if k != 1 or ((psi0, chi0, t) not in params): - params.append( (chi0,psi0,t) ) + if k != 1 or (psi0, chi0, t) not in params: + params.append((chi0, psi0, t)) return params @@ -365,8 +366,6 @@ def __find_eisen_chars_gamma1(N, k): pairs.append((psi, chi)) else: pairs.append((chi, psi)) - #end fors - #end if triples = [] for chi, psi in pairs: @@ -376,14 +375,14 @@ def __find_eisen_chars_gamma1(N, k): if k == 2 and chi.is_trivial() and psi.is_trivial(): D.remove(1) chi, psi = __common_minimal_basering(chi, psi) - for t in D: - triples.append((chi, psi, t)) + triples.extend((chi, psi, t) for t in D) + return triples def eisenstein_series_lseries(weight, prec=53, - max_imaginary_part=0, - max_asymp_coeffs=40): + max_imaginary_part=0, + max_asymp_coeffs=40): r""" Return the `L`-series of the weight `2k` Eisenstein series on `\SL_2(\ZZ)`. diff --git a/src/sage/modular/modform/eis_series_cython.pyx b/src/sage/modular/modform/eis_series_cython.pyx index 8126d1f8135..60df3add6f0 100644 --- a/src/sage/modular/modform/eis_series_cython.pyx +++ b/src/sage/modular/modform/eis_series_cython.pyx @@ -107,12 +107,12 @@ cpdef Ek_ZZ(int k, int prec=10): # compute the valuation of n at p additional_p_powers = 0 - temp_index = ind / p + temp_index = ind // p remainder = 0 while not remainder: additional_p_powers += 1 prev_index = temp_index - temp_index = temp_index / p + temp_index = temp_index // p remainder = prev_index - p*temp_index # if we need a new sum, it has to be the next uncomputed one. @@ -184,7 +184,7 @@ cpdef eisenstein_series_poly(int k, int prec=10): a0 = -bernoulli(k) / (2*k) cdef long p, ppow - for p in primes(1, prec) : + for p in primes(1, prec): ppow = p mpz_set_si(mult, p) diff --git a/src/sage/modular/modform/eisenstein_submodule.py b/src/sage/modular/modform/eisenstein_submodule.py index 2110ccf695f..fbbe2e06da3 100644 --- a/src/sage/modular/modform/eisenstein_submodule.py +++ b/src/sage/modular/modform/eisenstein_submodule.py @@ -119,6 +119,7 @@ def modular_symbols(self, sign=0): A = self.ambient_module() return A.modular_symbols(sign).eisenstein_submodule() + class EisensteinSubmodule_params(EisensteinSubmodule): @cached_method @@ -586,9 +587,9 @@ class EisensteinSubmodule_eps(EisensteinSubmodule_params): ] """ # TODO - #def _compute_q_expansion_basis(self, prec): - #B = EisensteinSubmodule_params._compute_q_expansion_basis(self, prec) - #raise NotImplementedError, "must restrict scalars down correctly." + # def _compute_q_expansion_basis(self, prec): + # B = EisensteinSubmodule_params._compute_q_expansion_basis(self, prec) + # raise NotImplementedError("must restrict scalars down correctly.") def cyclotomic_restriction(L, K): @@ -667,8 +668,8 @@ def cyclotomic_restriction_tower(L, K): f = L.defining_polynomial() R = K['x'] g = R(f) - h_ls = [ t[0] for t in g.factor() if t[0](L.gen(0)) == 0 ] - if len(h_ls) == 0: + h_ls = [t[0] for t in g.factor() if t[0](L.gen(0)) == 0] + if not h_ls: raise ValueError(r"K (= Q(\zeta_%s)) is not contained in L (= Q(\zeta_%s))" % (K._n(), L._n())) h = h_ls[0] diff --git a/src/sage/modular/modform/half_integral.py b/src/sage/modular/modform/half_integral.py index 801e3715e21..49e205cafab 100644 --- a/src/sage/modular/modform/half_integral.py +++ b/src/sage/modular/modform/half_integral.py @@ -16,6 +16,7 @@ from .theta import theta2_qexp, theta_qexp from copy import copy + def half_integral_weight_modform_basis(chi, k, prec): r""" A basis for the space of weight `k/2` forms with character diff --git a/src/sage/modular/modform/hecke_operator_on_qexp.py b/src/sage/modular/modform/hecke_operator_on_qexp.py index 7191b52abfd..5133c7612c9 100644 --- a/src/sage/modular/modform/hecke_operator_on_qexp.py +++ b/src/sage/modular/modform/hecke_operator_on_qexp.py @@ -26,6 +26,7 @@ from sage.modular.dirichlet import DirichletGroup, DirichletCharacter from .element import ModularFormElement + def hecke_operator_on_qexp(f, n, k, eps=None, prec=None, check=True, _return_list=False): r""" @@ -87,7 +88,7 @@ def hecke_operator_on_qexp(f, n, k, eps=None, # ZZ can coerce to GF(p), but QQ can't. eps = DirichletGroup(1, base_ring=ZZ)[0] if check: - if not (isinstance(f, PowerSeries) or isinstance(f, ModularFormElement)): + if not isinstance(f, (PowerSeries, ModularFormElement)): raise TypeError("f (=%s) must be a power series or modular form" % f) if not isinstance(eps, DirichletCharacter): raise TypeError("eps (=%s) must be a Dirichlet character" % eps) diff --git a/src/sage/modular/modform/j_invariant.py b/src/sage/modular/modform/j_invariant.py index 48b7c91fcb0..a74d42b91b2 100644 --- a/src/sage/modular/modform/j_invariant.py +++ b/src/sage/modular/modform/j_invariant.py @@ -6,6 +6,7 @@ from .vm_basis import delta_qexp from sage.rings.rational_field import QQ + def j_invariant_qexp(prec=10, K=QQ): r""" Return the `q`-expansion of the `j`-invariant to diff --git a/src/sage/modular/modform/meson.build b/src/sage/modular/modform/meson.build new file mode 100644 index 00000000000..7276059448d --- /dev/null +++ b/src/sage/modular/modform/meson.build @@ -0,0 +1,47 @@ +py.install_sources( + 'all.py', + 'ambient.py', + 'ambient_R.py', + 'ambient_eps.py', + 'ambient_g0.py', + 'ambient_g1.py', + 'constructor.py', + 'cuspidal_submodule.py', + 'defaults.py', + 'eis_series.py', + 'eisenstein_submodule.py', + 'element.py', + 'find_generators.py', + 'half_integral.py', + 'hecke_operator_on_qexp.py', + 'j_invariant.py', + 'l_series_gross_zagier.py', + 'notes.py', + 'numerical.py', + 'periods.py', + 'ring.py', + 'space.py', + 'submodule.py', + 'tests.py', + 'theta.py', + 'vm_basis.py', + 'weight1.py', + subdir: 'sage/modular/modform', +) + +extension_data = { + 'eis_series_cython' : files('eis_series_cython.pyx'), + 'l_series_gross_zagier_coeffs' : files('l_series_gross_zagier_coeffs.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular/modform', + install: true, + include_directories: [inc_cpython, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + diff --git a/src/sage/modular/modform/numerical.py b/src/sage/modular/modform/numerical.py index 253047b2eab..dd14ff15995 100644 --- a/src/sage/modular/modform/numerical.py +++ b/src/sage/modular/modform/numerical.py @@ -3,15 +3,15 @@ Numerical computation of newforms """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004-2006 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.fast_arith import prime_range from sage.matrix.constructor import matrix @@ -31,6 +31,7 @@ # This variable controls importing the SciPy library sparingly scipy = None + @richcmp_method class NumericalEigenforms(SageObject): """ @@ -488,6 +489,7 @@ def systems_of_abs(self, bound): v.set_immutable() return v + def support(v, eps): """ Given a vector `v` and a threshold eps, return all diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 21d28730de6..dacfda54bae 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -10,7 +10,7 @@ - William Stein (2007-08-24): first version - David Ayotte (2021-06): implemented category and Parent/Element frameworks """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # 2021 David Ayotte # @@ -18,8 +18,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from random import shuffle @@ -143,6 +143,7 @@ def _span_of_forms_in_weight(forms, weight, prec, stop_dim=None, use_random=Fals verbose('span has dimension %s' % W.rank(), t) return W + @richcmp_method class ModularFormsRing(Parent): r""" @@ -200,8 +201,9 @@ def __init__(self, group, base_ring=QQ): - ``group`` -- a congruence subgroup of `\SL_2(\ZZ)`, or a positive integer `N` (interpreted as `\Gamma_0(N)`) - - ``base_ring``-- ring (default: `\QQ`); a base ring, which should be - `\QQ`, `\ZZ`, or the integers mod `p` for some prime `p` + - ``base_ring`` -- ring (default: `\QQ`); a base ring, which + should be `\QQ`, `\ZZ`, or the integers mod `p` for some prime + `p` TESTS: @@ -241,17 +243,18 @@ def __init__(self, group, base_ring=QQ): self.__cached_gens = [] self.__cached_cusp_maxweight = ZZ(-1) self.__cached_cusp_gens = [] - Parent.__init__(self, base=base_ring, category=GradedAlgebras(base_ring)) + cat = GradedAlgebras(base_ring).Commutative() + Parent.__init__(self, base=base_ring, category=cat) def change_ring(self, base_ring): r""" - Return the ring of modular forms over the given base ring and the same - group as ``self``. + Return a ring of modular forms over a new base ring of the same + congruence subgroup. INPUT: - - ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or the - integers mod `p` for some prime `p` + - ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or + the integers mod `p` for some prime `p` EXAMPLES:: @@ -266,7 +269,7 @@ def change_ring(self, base_ring): def some_elements(self): r""" - Return a list of generators of ``self``. + Return some elements of this ring. EXAMPLES:: @@ -278,7 +281,7 @@ def some_elements(self): def group(self): r""" - Return the congruence subgroup for which this is the ring of modular forms. + Return the congruence subgroup of this ring of modular forms. EXAMPLES:: @@ -290,14 +293,13 @@ def group(self): def gen(self, i): r""" - Return the `i`-th generator of ``self``. + Return the `i`-th generator of this ring. INPUT: - - ``i`` -- integer; correspond to the `i`-th modular form generating - the ring of modular forms + - ``i`` -- integer - OUTPUT: a ``GradedModularFormElement`` + OUTPUT: an instance of :class:`~sage.modular.modform.GradedModularFormElement` EXAMPLES:: @@ -313,7 +315,7 @@ def gen(self, i): def ngens(self): r""" - Return the number of generators of ``self``. + Return the number of generators of this ring. EXAMPLES:: @@ -333,19 +335,25 @@ def ngens(self): def polynomial_ring(self, names, gens=None): r""" - Return a polynomial ring of which ``self`` is a quotient. + Return a polynomial ring of which this ring of modular forms is + a quotient. INPUT: - - ``names`` -- list or tuple of names (strings), or a comma separated - string - - ``gens`` -- (default: ``None``) list of generator of - ``self``. If ``gens`` is ``None`` then the generators returned by + - ``names`` -- a list or tuple of names (strings), or a comma + separated string; consists in the names of the polynomial + ring variables + - ``gens`` -- list of modular forms generating this ring + (default: ``None``); if ``gens`` is ``None`` then the list of + generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` - is used instead + is used instead. Note that we do not check if the list is + indeed a generating set. - OUTPUT: a multivariate polynomial ring in the variable ``names``. Each variable of the - polynomial ring correspond to a generator given in gens (following the ordering of the list). + OUTPUT: a multivariate polynomial ring in the variable + ``names``. Each variable of the polynomial ring correspond to a + generator given in the list ``gens`` (following the ordering of + the list). EXAMPLES:: @@ -358,7 +366,8 @@ def polynomial_ring(self, names, gens=None): sage: M.polynomial_ring('g', gens) Multivariate Polynomial Ring in g0, g1, g2 over Rational Field - The degrees of the variables are the weights of the corresponding forms:: + The degrees of the variables are the weights of the + corresponding forms:: sage: M = ModularFormsRing(1) sage: P. = M.polynomial_ring() @@ -372,12 +381,13 @@ def polynomial_ring(self, names, gens=None): if gens is None: gens = self.gen_forms() degs = [f.weight() for f in gens] - return PolynomialRing(self.base_ring(), len(gens), names, order=TermOrder('wdeglex', degs)) # Should we remove the deg lexicographic ordering here? + return PolynomialRing(self.base_ring(), len(gens), names, + order=TermOrder('wdeglex', degs)) def _generators_variables_dictionnary(self, poly_parent, gens): r""" - Utility function that returns a dictionary giving an association between - polynomial ring generators and generators of modular forms ring. + Return a dictionary giving an association between polynomial + ring generators and generators of modular forms ring. INPUT: @@ -398,23 +408,28 @@ def _generators_variables_dictionnary(self, poly_parent, gens): nb_var = poly_parent.ngens() nb_gens = self.ngens() if nb_var != nb_gens: - raise ValueError('the number of variables (%s) must be equal to the number of generators of the modular forms ring (%s)' % (nb_var, self.ngens())) + raise ValueError('the number of variables (%s) must be equal to' + ' the number of generators of the modular forms' + ' ring (%s)' % (nb_var, self.ngens())) return {poly_parent.gen(i): self(gens[i]) for i in range(0, nb_var)} def from_polynomial(self, polynomial, gens=None): r""" - Convert the given polynomial to a graded form living in ``self``. If - ``gens`` is ``None`` then the list of generators given by the method - :meth:`gen_forms` will be used. Otherwise, ``gens`` should be a list of - generators. + Return a graded modular form constructed by evaluating a given + multivariate polynomial at a set of generators. INPUT: - - ``polynomial`` -- a multivariate polynomial. The variables names of - the polynomial should be different from ``'q'``. The number of - variable of this polynomial should equal the number of generators - - ``gens`` -- list (default: ``None``); of generators of the modular - forms ring + - ``polynomial`` -- a multivariate polynomial. The variables + names of the polynomial should be different from ``'q'``. The + number of variable of this polynomial should equal the number + of given generators. + - ``gens`` -- list of modular forms generating this ring + (default: ``None``); if ``gens`` is ``None`` then the list of + generators returned by the method + :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` + is used instead. Note that we do not check if the list is + indeed a generating set. OUTPUT: a ``GradedModularFormElement`` given by the polynomial relation ``polynomial`` @@ -437,7 +452,8 @@ def from_polynomial(self, polynomial, gens=None): sage: M.from_polynomial(P(1/2)) 1/2 - Note that the number of variables must be equal to the number of generators:: + Note that the number of variables must be equal to the number of + generators:: sage: x, y = polygens(QQ, 'x, y') sage: M(x + y) @@ -470,7 +486,7 @@ def from_polynomial(self, polynomial, gens=None): def _element_constructor_(self, forms_datum): r""" - The call method of ``self``. + Return the graded modular form corresponding to the given data. INPUT: @@ -524,7 +540,7 @@ def _element_constructor_(self, forms_datum): else: raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s' % (forms_datum.group(), forms_datum.base_ring(), self)) elif forms_datum in self.base_ring(): - forms_dictionary = {0:forms_datum} + forms_dictionary = {0: forms_datum} elif isinstance(forms_datum, MPolynomial): return self.from_polynomial(forms_datum) elif isinstance(forms_datum, PowerSeries_poly): @@ -577,7 +593,8 @@ def one(self): def _coerce_map_from_(self, M): r""" - Code to make ModularFormRing work well with coercion framework. + Return ``True`` if there is a coercion map from ``M`` to this + ring. TESTS:: @@ -623,7 +640,7 @@ def __richcmp__(self, other, op): def _repr_(self): r""" - String representation of ``self``. + Return the string representation of ``self``. EXAMPLES:: @@ -636,7 +653,8 @@ def _repr_(self): def modular_forms_of_weight(self, weight): """ - Return the space of modular forms on this group of the given weight. + Return the space of modular forms of the given weight and the + same congruence subgroup. EXAMPLES:: @@ -650,23 +668,30 @@ def modular_forms_of_weight(self, weight): def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): r""" - If `R` is the base ring of self, then this function calculates a set of - modular forms which generate the `R`-algebra of all modular forms of - weight up to ``maxweight`` with coefficients in `R`. + Return a list of generator of this ring as a list of pairs + `(k, f)` where `k` is an integer and `f` is a univariate power + series in `q` corresponding to the `q`-expansion of a modular + form of weight `k`. + + More precisely, if `R` is the base ring of self, then this + function calculates a set of modular forms which generate the + `R`-algebra of all modular forms of weight up to ``maxweight`` + with coefficients in `R`. INPUT: - - ``maxweight``-- integer (default: 8); check up to this weight for - generators + - ``maxweight`` -- integer (default: 8); check up to this weight + for generators - - ``prec``-- integer (default: 10); return `q`-expansions to this - precision + - ``prec`` -- integer (default: 10); return `q`-expansions to + this precision - - ``start_gens``-- list (default: ``[]``); list of pairs `(k, f)`, or - triples `(k, f, F)`, where: + - ``start_gens`` -- list (default: ``[]``); list of pairs + `(k, f)`, or triples `(k, f, F)`, where: - `k` is an integer, - - `f` is the `q`-expansion of a modular form of weight `k`, as a power series over the base ring of self, + - `f` is the `q`-expansion of a modular form of weight `k`, + as a power series over the base ring of self, - `F` (if provided) is a modular form object corresponding to F. If this list is nonempty, we find a minimal generating set containing @@ -787,7 +812,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): for x in start_gens: if len(x) == 2: if x[1].prec() < prec: - raise ValueError("Requested precision cannot be higher than precision of approximate starting generators!") + raise ValueError("Requested precision cannot be higher" + " than precision of approximate starting generators!") sgs.append((x[0], x[1], None)) else: sgs.append(x) @@ -807,22 +833,22 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): r""" - Return a list of modular forms generating this ring (as an algebra over - the appropriate base ring). + Return a list of modular forms generating this ring (as an algebra + over the appropriate base ring). This method differs from :meth:`generators` only in that it returns graded modular form objects, rather than bare `q`-expansions. INPUT: - - ``maxweight``-- integer (default: 8); calculate forms generating all - forms up to this weight + - ``maxweight`` -- integer (default: 8); calculate forms + generating all forms up to this weight - - ``start_gens``-- list (default: ``[]``); list of modular forms. If - this list is nonempty, we find a minimal generating set containing - these forms + - ``start_gens`` -- list (default: ``[]``); a list of + modular forms. If this list is nonempty, we find a minimal + generating set containing these forms. - - ``start_weight``-- integer (default: 2); calculate the graded + - ``start_weight`` -- integer (default: 2); calculate the graded subalgebra of forms of weight at least ``start_weight`` .. NOTE:: @@ -842,18 +868,20 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): sage: A[0].parent() Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field """ - sgs = tuple( (F.weight(), None, F) for F in start_gens ) + sgs = tuple((F.weight(), None, F) for F in start_gens) G = self._find_generators(maxweight, sgs, start_weight) - return [F for k,f,F in G] + return [F for k, f, F in G] gens = gen_forms def _find_generators(self, maxweight, start_gens, start_weight): r""" - For internal use. This function is called by :meth:`generators` and - :meth:`gen_forms`: it returns a list of triples `(k, f, F)` where `F` - is a modular form of weight `k` and `f` is its `q`-expansion coerced - into the base ring of ``self``. + Returns a list of triples `(k, f, F)` where `F` is a modular + form of weight `k` and `f` is its `q`-expansion coerced into the + base ring of self. + + For internal use. This function is called by :meth:`generators` + and :meth:`gen_forms`. INPUT: @@ -973,19 +1001,19 @@ def _find_generators(self, maxweight, start_gens, start_weight): @cached_method def q_expansion_basis(self, weight, prec=None, use_random=True): r""" - Calculate a basis of `q`-expansions for the space of modular forms of the - given weight for this group, calculated using the ring generators given - by ``find_generators``. + Return a basis of `q`-expansions for the space of modular forms + of the given weight for this group, calculated using the ring + generators given by ``find_generators``. INPUT: - - ``weight`` -- integer; the weight - - ``prec`` -- integer or ``None`` (default: ``None``); power series - precision. If ``None``, the precision defaults to the Sturm bound for - the requested level and weight. - - ``use_random`` -- boolean (default: ``True``); whether or not to use a - randomized algorithm when building up the space of forms at the given - weight from known generators of small weight + - ``weight`` -- the weight + - ``prec`` -- integer (default: ``None``); power series + precision. If ``None``, the precision defaults to the Sturm + bound for the requested level and weight. + - ``use_random`` -- boolean (default: ``True``); whether or not to + use a randomized algorithm when building up the space of forms + at the given weight from known generators of small weight. EXAMPLES:: @@ -1041,8 +1069,8 @@ def q_expansion_basis(self, weight, prec=None, use_random=True): def cuspidal_ideal_generators(self, maxweight=8, prec=None): r""" - Calculate generators for the ideal of cuspidal forms in this ring, as a - module over the whole ring. + Return a set of generators for the ideal of cuspidal forms in + this ring, as a module over the whole ring. EXAMPLES:: @@ -1063,7 +1091,7 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None): for j,f,F in self.__cached_cusp_gens: if f.prec() >= working_prec: f = F.qexp(working_prec).change_ring(self.base_ring()) - G.append( (j,f,F) ) + G.append((j, f, F)) else: k = 2 G = [] @@ -1114,22 +1142,23 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None): if prec is None: return G elif prec <= working_prec: - return [ (k, f.truncate_powerseries(prec), F) for k,f,F in G] + return [(k, f.truncate_powerseries(prec), F) for k,f,F in G] else: # user wants increased precision, so we may as well cache that - Gnew = [ (k, F.qexp(prec).change_ring(self.base_ring()), F) for k,f,F in G] + Gnew = [(k, F.qexp(prec).change_ring(self.base_ring()), F) for k, f, F in G] self.__cached_cusp_gens = Gnew return Gnew def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): r""" - Calculate a basis of `q`-expansions for the space of cusp forms of + Return a basis of `q`-expansions for the space of cusp forms of weight ``weight`` for this group. INPUT: - - ``weight`` -- integer; the weight - - ``prec`` -- integer or ``None`` precision of `q`-expansions to return + - ``weight`` -- the weight + - ``prec`` -- integer (default: ``None``) precision of + `q`-expansions to return ALGORITHM: Uses the method :meth:`cuspidal_ideal_generators` to calculate generators of the ideal of cusp forms inside this ring. Then diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 9d4e9caa7f1..202ea687953 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1300,8 +1300,7 @@ def _compute_hecke_matrix_prime(self, p, prec=None): except AttributeError: pass else: - if prec < cur: - prec = cur + prec = max(prec, cur) B = self.q_expansion_basis(prec) eps = self.character() if eps is None: @@ -1699,15 +1698,15 @@ def newforms(self, names=None): """ M = self.modular_symbols(sign=1) factors = M.cuspidal_subspace().new_subspace().decomposition() - large_dims = [ X.dimension() for X in factors if X.dimension() != 1 ] - if len(large_dims) > 0 and names is None: + large_dims = [X.dimension() for X in factors if X.dimension() != 1] + if large_dims and names is None: raise ValueError("Please specify a name to be used when generating names for generators of Hecke eigenvalue fields corresponding to the newforms.") elif names is None: # In this case, we don't need a variable name, so insert # something to get passed along below names = 'a' - return [ Newform(self, factors[i], names=(names+str(i)) ) - for i in range(len(factors)) ] + return [Newform(self, factors[i], names=names + str(i)) + for i in range(len(factors))] @cached_method def eisenstein_submodule(self): diff --git a/src/sage/modular/modform/theta.py b/src/sage/modular/modform/theta.py index 92c5f945a1c..fbe34473f98 100644 --- a/src/sage/modular/modform/theta.py +++ b/src/sage/modular/modform/theta.py @@ -11,6 +11,7 @@ from math import sqrt + def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" Return the `q`-expansion of the series `\theta_2 = \sum_{n \text{ odd}} q^{n^2}`. @@ -58,6 +59,7 @@ def theta2_qexp(prec=10, var='q', K=ZZ, sparse=False): R = PowerSeriesRing(K, sparse=sparse, names=var) return R(v, prec=prec) + def theta_qexp(prec=10, var='q', K=ZZ, sparse=False): r""" Return the `q`-expansion of the standard `\theta` series diff --git a/src/sage/modular/modform/vm_basis.py b/src/sage/modular/modform/vm_basis.py index bbbbd9b4513..58f36fd0f1a 100644 --- a/src/sage/modular/modform/vm_basis.py +++ b/src/sage/modular/modform/vm_basis.py @@ -19,14 +19,14 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import math @@ -156,22 +156,22 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): ls[i] = err return Sequence(ls, cr=True) - F6 = eisenstein_series_poly(6,prec) + F6 = eisenstein_series_poly(6, prec) if e == 0: A = Fmpz_poly(1) elif e == 4: - A = eisenstein_series_poly(4,prec) + A = eisenstein_series_poly(4, prec) elif e == 6: A = F6 elif e == 8: - A = eisenstein_series_poly(8,prec) + A = eisenstein_series_poly(8, prec) elif e == 10: - A = eisenstein_series_poly(10,prec) - else: # e == 14 - A = eisenstein_series_poly(14,prec) + A = eisenstein_series_poly(10, prec) + else: # e == 14 + A = eisenstein_series_poly(14, prec) - if A[0] == -1 : + if A[0] == -1: A = -A if n == 0: @@ -186,9 +186,9 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): if cusp_only: ls = [Fmpz_poly(0)] + [A] * n else: - ls = [A] * (n+1) + ls = [A] * (n + 1) - for i in range(1,n+1): + for i in range(1, n + 1): ls[n-i] *= Fprod ls[i] *= Dprod ls[n-i]._unsafe_mutate_truncate(prec) @@ -201,17 +201,17 @@ def victor_miller_basis(k, prec=10, cusp_only=False, var='q'): P = PowerSeriesRing(ZZ, var) if cusp_only: - for i in range(1,n+1) : - for j in range(1, i) : + for i in range(1, n + 1): + for j in range(1, i): ls[j] = ls[j] - ls[j][i]*ls[i] - return Sequence([P(l.list()).add_bigoh(prec) for l in ls[1:]],cr=True) - else : - for i in range(1,n+1): - for j in range(i): - ls[j] = ls[j] - ls[j][i]*ls[i] + return Sequence([P(l.list()).add_bigoh(prec) for l in ls[1:]], cr=True) + + for i in range(1, n + 1): + for j in range(i): + ls[j] = ls[j] - ls[j][i] * ls[i] - return Sequence([P(l.list()).add_bigoh(prec) for l in ls], cr=True) + return Sequence([P(l.list()).add_bigoh(prec) for l in ls], cr=True) def _delta_poly(prec=10): @@ -284,9 +284,9 @@ def _delta_poly_modulo(N, prec=10): OUTPUT: - the polynomial of degree ``prec``-1 which is the truncation - of `\Delta` modulo `N`, as an element of the polynomial - ring in `q` over the integers modulo `N`. + the polynomial of degree ``prec``-1 which is the truncation + of `\Delta` modulo `N`, as an element of the polynomial + ring in `q` over the integers modulo `N`. EXAMPLES:: @@ -297,7 +297,7 @@ def _delta_poly_modulo(N, prec=10): 2*q^11 + 7*q^9 + 6*q^7 + 2*q^6 + 8*q^4 + 2*q^3 + 6*q^2 + q """ if prec <= 0: - raise ValueError( "prec must be positive" ) + raise ValueError("prec must be positive") v = [0] * prec # Let F = \sum_{n >= 0} (-1)^n (2n+1) q^(floor(n(n+1)/2)). @@ -324,7 +324,7 @@ def _delta_poly_modulo(N, prec=10): return f -def delta_qexp(prec=10, var='q', K=ZZ) : +def delta_qexp(prec=10, var='q', K=ZZ): r""" Return the `q`-expansion of the weight 12 cusp form `\Delta` as a power series with coefficients in the ring K (`= \ZZ` by default). diff --git a/src/sage/modular/modform/weight1.py b/src/sage/modular/modform/weight1.py index a2f5aa10108..9aa75074676 100644 --- a/src/sage/modular/modform/weight1.py +++ b/src/sage/modular/modform/weight1.py @@ -20,6 +20,7 @@ from sage.modular.arithgroup.all import Gamma0, GammaH from sage.modular.arithgroup.arithgroup_generic import ArithmeticSubgroup + @cached_function def modular_ratio_space(chi): r""" @@ -100,6 +101,7 @@ def modular_ratio_to_prec(chi, qexp, prec): fB_elt = C(fB, check=False) return fB_elt.qexp(prec) / B + @cached_function def hecke_stable_subspace(chi, aux_prime=ZZ(2)): r""" @@ -187,6 +189,7 @@ def hecke_stable_subspace(chi, aux_prime=ZZ(2)): qexps = Sequence(A(x.list()).add_bigoh(R) for x in J.gens()) return qexps + @cached_function def dimension_wt1_cusp_forms(chi): r""" @@ -200,6 +203,7 @@ def dimension_wt1_cusp_forms(chi): """ return len(hecke_stable_subspace(chi)) + @cached_function def dimension_wt1_cusp_forms_gH(group): r""" diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index 89c86ca41c2..79352b97ffe 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -263,14 +263,14 @@ def _element_constructor_(self, el): return self.construct_quasi_form(el) if isinstance(el, FreeModuleElement) and (self.module() is P or self.ambient_module() is P): return self.element_from_ambient_coordinates(el) - if (not self.is_ambient()) and (isinstance(el, list) or isinstance(el, tuple) or isinstance(el, FreeModuleElement)) and len(el) == self.rank(): + if not self.is_ambient() and isinstance(el, (list, tuple, FreeModuleElement)) and len(el) == self.rank(): try: return self.element_from_coordinates(el) except (ArithmeticError, TypeError): pass if self.ambient_module() and self.ambient_module().has_coerce_map_from(P): return self.element_from_ambient_coordinates(self.ambient_module()(el)) - if (isinstance(el,list) or isinstance(el, tuple)) and len(el) == self.degree(): + if isinstance(el, (list, tuple)) and len(el) == self.degree(): try: return self.element_from_ambient_coordinates(el) except (ArithmeticError, TypeError): diff --git a/src/sage/modular/modform_hecketriangle/constructor.py b/src/sage/modular/modform_hecketriangle/constructor.py index e245e1d2394..e3525879e6e 100644 --- a/src/sage/modular/modform_hecketriangle/constructor.py +++ b/src/sage/modular/modform_hecketriangle/constructor.py @@ -117,11 +117,11 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): analytic_type = AT(["quasi", "mero"]) - R = PolynomialRing(base_ring,'x,y,z,d') + R = PolynomialRing(base_ring, 'x,y,z,d') F = FractionField(R) - (x,y,z,d) = R.gens() + x, y, z, d = R.gens() R2 = PolynomialRing(PolynomialRing(base_ring, 'd'), 'x,y,z') - dhom = R.hom( R2.gens() + (R2.base().gen(),), R2) + dhom = R.hom(R2.gens() + (R2.base().gen(),), R2) f = F(f) @@ -131,12 +131,12 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): ep_denom = {ZZ.one() - 2*((sum([g.exponents()[0][m] for m in [1, 2]])) % 2) for g in dhom(denom).monomials()} if (n == infinity): - hom_num = R( num.subs(x=x**4, y=y**2, z=z**2) ) - hom_denom = R( denom.subs(x=x**4, y=y**2, z=z**2) ) + hom_num = R(num.subs(x=x**4, y=y**2, z=z**2)) + hom_denom = R(denom.subs(x=x**4, y=y**2, z=z**2)) else: n = ZZ(n) - hom_num = R( num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) - hom_denom = R( denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) + hom_num = R(num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2)))) + hom_denom = R(denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2)))) # Determine whether the denominator of f is homogeneous if (len(ep_denom) == 1 and dhom(hom_denom).is_homogeneous()): @@ -146,9 +146,9 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): return (False, False, None, None, None) # Determine whether f is homogeneous - if (len(ep_num) == 1 and dhom(hom_num).is_homogeneous()): + if len(ep_num) == 1 and dhom(hom_num).is_homogeneous(): homo = True - if (n == infinity): + if n == infinity: weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) else: weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) / (n-2) @@ -160,17 +160,17 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): ep = None # Note that we intentionally leave out the d-factor! - if (n == infinity): + if n == infinity: finf_pol = (x-y**2) else: finf_pol = x**n-y**2 # Determine whether f is modular - if not ( (num.degree(z) > 0) or (denom.degree(z) > 0) ): + if not (num.degree(z) > 0 or denom.degree(z) > 0): analytic_type = analytic_type.reduce_to("mero") # Determine whether f is holomorphic - if (dhom(denom).is_constant()): + if dhom(denom).is_constant(): analytic_type = analytic_type.reduce_to(["quasi", "holo"]) # Determine whether f is cuspidal in the sense that finf divides it... # Bug in singular: finf_pol.divides(1.0) fails over RR @@ -180,7 +180,7 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): else: # -> Because of a bug with singular in some cases try: - while (finf_pol.divides(denom)): + while finf_pol.divides(denom): # a simple "denom /= finf_pol" is strangely not enough for non-exact rings # and dividing would/may result with an element of the quotient ring of the polynomial ring denom = denom.quo_rem(finf_pol)[0] diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index df9568bb2ab..e7c56fd0e94 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -25,11 +25,11 @@ from sage.rings.infinity import infinity from sage.rings.cc import CC -lazy_import('sage.rings.qqbar', 'AA') - from sage.groups.matrix_gps.group_element import MatrixGroupElement_generic from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicPlane +lazy_import('sage.rings.qqbar', 'AA') + # We want to simplify p after the coercion (pari bug for AA) def coerce_AA(p): @@ -51,7 +51,7 @@ def coerce_AA(p): """ el = AA(p) el.simplify() - #el.exactify() + # el.exactify() return el @@ -221,13 +221,13 @@ def _word_S_T_data(self): half = one / ZZ(2) while True: - a,b,c,d = M.list() + a, b, c, d = M.list() mshift = coerce_AA((4*a*c + b*d) / (4*c*c + d*d)) m = (mshift / lam + half).floor() if m != zero: res.append((one, m),) M = T**(-m) * M - a,b,c,d = M.list() + a, b, c, d = M.list() abs_t = coerce_AA((4*a*a + b*b) / (4*c*c + d*d)) if coerce_AA(abs_t) < 1: @@ -452,7 +452,7 @@ def string_repr(self, method='default'): return "-1" if sgn < 0 else "1" Lstr = list(L) - for i,(v0,v1) in enumerate(Lstr): + for i, (v0, v1) in enumerate(Lstr): if v0 == 0: Lstr[i] = "S" elif v1 == 1: @@ -479,7 +479,7 @@ def string_repr(self, method='default'): if sgn < 0: repr_str = repr_str[1:] - #if self != R.inverse().acton(self): + # if self != R.inverse().acton(self): if R.is_identity(): repr_str = "{}{}".format("-" if sgn < 0 else "", repr_str) else: @@ -496,7 +496,7 @@ def string_repr(self, method='default'): (L, R, sgn) = self._block_decomposition_data() if self.is_elliptic(): - L = [ L ] + L = [L] repr_str = "" begin = True @@ -633,7 +633,7 @@ def continued_fraction(self): if p == infinity: # TODO: The choice of r doesn't matter? r = ZZ.zero() - #elif self.is_elliptic(): + # elif self.is_elliptic(): # r = ZZ(emb(p/lam).real().floor() + 1) else: emb_res = emb(p/lam) @@ -645,7 +645,7 @@ def continued_fraction(self): cf_index += one preperiod_len = cf_dict[p] - #period_len = cf_index - preperiod_len + # period_len = cf_index - preperiod_len return (tuple(L[:preperiod_len]), tuple(L[preperiod_len:])) @@ -807,7 +807,7 @@ def _primitive_block_decomposition_data(self): L = (one, one) else: raise RuntimeError("There is something wrong in the method " - "_primitive_block_decomposition_data. Please contact sage-devel@googlegroups.com") + "_primitive_block_decomposition_data. Please contact sage-devel@googlegroups.com") return (L, R) @@ -835,11 +835,11 @@ def _primitive_block_decomposition_data(self): initial_ones = number_of_ones.pop(0) if not list_larger: - list_v1 = [-ZZ(1)] - list_vlarger = [ initial_ones + 2 ] + list_v1 = [-ZZ.one()] + list_vlarger = [initial_ones + 2] else: - list_v1 = [ v-2 for v in list_larger ] - list_vlarger = [ v+2 for v in number_of_ones ] + list_v1 = [v - 2 for v in list_larger] + list_vlarger = [v + 2 for v in number_of_ones] list_vlarger[-1] += initial_ones L = [] @@ -1363,14 +1363,14 @@ def primitive_power(self, method='cf'): for j in range(1, G.n()): Uj *= U if U_power == Uj: - #L = [one, ZZ(j)] + # L = [one, ZZ(j)] break elif U_power == -Uj: - #L = [one, ZZ(-j)] + # L = [one, ZZ(-j)] break else: raise RuntimeError("There is a problem in the method " - "'primitive_power'. Please contact sage-devel@googlegroups.com") + "'primitive_power'. Please contact sage-devel@googlegroups.com") if abs(j) < G.n()/two: return j @@ -1514,7 +1514,7 @@ def block_length(self, primitive=False): else: return sum(abs(v[1]) for v in L) - #@cached_method + # @cached_method def _block_decomposition_data(self): r""" Return a tuple ``(L, R, sgn)`` which describes the @@ -1771,7 +1771,7 @@ def block_decomposition(self): P = G.U() return ((P**L[1],), R, sgn) else: - return (tuple(G.V(v[0])**v[1] for v in L ), R, sgn) + return (tuple(G.V(v[0])**v[1] for v in L), R, sgn) def conjugacy_type(self, ignore_sign=True, primitive=False): r""" @@ -2206,7 +2206,7 @@ def is_translation(self, exclude_one=False): sage: (-HeckeTriangleGroup(n=7).I()).is_translation(exclude_one=True) False """ - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if not (c.is_zero() and a == d and (a.is_one() or (-a).is_one())): return False @@ -2488,7 +2488,7 @@ def is_simple(self): return False # The last condition is/should be equivalent to: - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() return (coerce_AA(a) > 0 and coerce_AA(b) > 0 and coerce_AA(c) > 0 and coerce_AA(d) > 0) def is_hecke_symmetric(self): @@ -2675,7 +2675,7 @@ def rational_period_function(self, k): if k % 2: raise TypeError except TypeError: - raise ValueError("k={} must be an even integer!".format(k)) + raise ValueError(f"k={k} must be an even integer!") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing P = PolynomialRing(self.parent().base_ring(), 'z') @@ -2683,14 +2683,14 @@ def rational_period_function(self, k): s = P.zero() - #L1 = [] + # L1 = [] for v in self.simple_elements(): - a,b,c,d = v._matrix.list() + a, b, c, d = v._matrix.list() Q = c*z**2 + (d - a)*z - b s += Q**(-k/ZZ(2)) for v in self.inverse().simple_elements(): - a,b,c,d = v._matrix.list() + a, b, c, d = v._matrix.list() Q = c*z**2 + (d - a)*z - b s -= ZZ(-1)**(k/ZZ(2)) * Q**(-k/ZZ(2)) @@ -3049,7 +3049,7 @@ def fixed_points(self, embedded=False, order='default'): else: e = self.root_extension_field().gen() - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if order == "none": sgn = ZZ(1) @@ -3170,7 +3170,7 @@ def acton(self, tau): model = tau.model() tau = tau.to_model('UHP').coordinates() - a,b,c,d = self._matrix.list() + a, b, c, d = self._matrix.list() if tau == infinity: if c.is_zero(): diff --git a/src/sage/modular/modsym/heilbronn.pyx b/src/sage/modular/modsym/heilbronn.pyx index a14f2e6ccac..82adf9efa20 100644 --- a/src/sage/modular/modsym/heilbronn.pyx +++ b/src/sage/modular/modsym/heilbronn.pyx @@ -383,7 +383,7 @@ cdef class HeilbronnCremona(Heilbronn): # NOTE: In C, -p/2 means "round toward 0", but in Python it # means "round down." sig_on() - for r in range(-p/2, p/2+1): + for r in range(-p // 2, p // 2 + 1): x1 = p x2 = -r y1 = 0 @@ -394,7 +394,7 @@ cdef class HeilbronnCremona(Heilbronn): x3 = 0 y3 = 0 q = 0 - list_append4(L, x1,x2,y1,y2) + list_append4(L, x1, x2, y1, y2) while b: q = roundf(a / b) c = a - b*q @@ -406,8 +406,8 @@ cdef class HeilbronnCremona(Heilbronn): y3 = q*y2 - y1 y1 = y2 y2 = y3 - list_append4(L, x1,x2, y1,y2) - self.length = L.i/4 + list_append4(L, x1, x2, y1, y2) + self.length = L.i // 4 sig_off() @@ -491,7 +491,7 @@ cdef class HeilbronnMerel(Heilbronn): for a in range(1, n+1): ## We have ad-bc=n so c=0 and ad=n, or b=(ad-n)/c ## Must have ad - n >= 0, so d must be >= Ceiling(n/a). - q = n/a + q = n // a if q*a == n: d = q for b in range(a): @@ -503,10 +503,10 @@ cdef class HeilbronnMerel(Heilbronn): ## Divisor c of bc must satisfy Floor(bc/c) lt a and c lt d. ## c ge (bc div a + 1) <=> Floor(bc/c) lt a (for integers) ## c le d - 1 <=> c lt d - for c in range(bc/a + 1, d): + for c in range(bc // a + 1, d): if bc % c == 0: - list_append4(L,a,bc/c,c,d) - self.length = L.i/4 + list_append4(L, a, bc // c, c, d) + self.length = L.i // 4 sig_off() diff --git a/src/sage/modular/modsym/meson.build b/src/sage/modular/modsym/meson.build new file mode 100644 index 00000000000..f05d0776246 --- /dev/null +++ b/src/sage/modular/modsym/meson.build @@ -0,0 +1,41 @@ +py.install_sources( + 'all.py', + 'ambient.py', + 'apply.pxd', + 'boundary.py', + 'element.py', + 'g1list.py', + 'ghlist.py', + 'hecke_operator.py', + 'manin_symbol.pxd', + 'manin_symbol_list.py', + 'modsym.py', + 'modular_symbols.py', + 'p1list.pxd', + 'p1list_nf.py', + 'relation_matrix.py', + 'space.py', + 'subspace.py', + 'tests.py', + subdir: 'sage/modular/modsym', +) + +extension_data = { + 'apply' : files('apply.pyx'), + 'heilbronn' : files('heilbronn.pyx'), + 'manin_symbol' : files('manin_symbol.pyx'), + 'p1list' : files('p1list.pyx'), + 'relation_matrix_pyx' : files('relation_matrix_pyx.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular/modsym', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_rings], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 5e48d6b1369..7ceda12d5c8 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -94,8 +94,8 @@ cdef int c_p1_normalize_int(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -105,8 +105,8 @@ cdef int c_p1_normalize_int(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = (v*Ng) % N t = 1 for k in range(2, g + 1): @@ -355,8 +355,8 @@ cdef int c_p1_normalize_llong(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -367,8 +367,8 @@ cdef int c_p1_normalize_llong(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = ((v * Ng) % ll_N) t = 1 for k in range(2, g + 1): @@ -640,8 +640,8 @@ cdef int p1_normalize_xgcdtable(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while t_g[s] != 1: # while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -651,8 +651,8 @@ cdef int p1_normalize_xgcdtable(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = (v*Ng) % N t = 1 for k in range(2, g + 1): @@ -1237,17 +1237,17 @@ def lift_to_sl2z_int(int c, int d, int N): # compute prime-to-d part of m. while True: - g = arith_int.c_gcd_int(m,d) + g = arith_int.c_gcd_int(m, d) if g == 1: break - m = m / g + m = m // g # compute prime-to-N part of m. while True: - g = arith_int.c_gcd_int(m,N) + g = arith_int.c_gcd_int(m, N) if g == 1: break - m = m / g + m = m // g d += N * m g = arith_int.c_xgcd_int(c, d, &z1, &z2) @@ -1299,7 +1299,7 @@ def lift_to_sl2z_llong(llong c, llong d, int N): g = arith_llong.c_xgcd_longlong(c, d, &z1, &z2) # We're lucky: z1*c + z2*d = 1. - if g==1: + if g == 1: return [z2, -z1, c, d] # Have to try harder. @@ -1307,17 +1307,17 @@ def lift_to_sl2z_llong(llong c, llong d, int N): # compute prime-to-d part of m. while True: - g = arith_llong.c_gcd_longlong(m,d) + g = arith_llong.c_gcd_longlong(m, d) if g == 1: break - m = m / g + m = m // g # compute prime-to-N part of m. while True: - g = arith_llong.c_gcd_longlong(m,N) + g = arith_llong.c_gcd_longlong(m, N) if g == 1: break - m = m / g + m = m // g d += N * m g = arith_llong.c_xgcd_longlong(c, d, &z1, &z2) diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 590a73c109d..a77d9279a6b 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -1383,8 +1383,7 @@ def _q_expansion_basis_hecke_dual(self, prec): prec = Integer(prec) if prec < 1: raise ValueError("prec (=%s) must be >= 1" % prec) - if d >= prec - 1: - d = prec - 1 + d = min(prec - 1, d) K = self.base_ring() A = VectorSpace(K, prec - 1) diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py index a8ca0884127..8bcbb1101de 100644 --- a/src/sage/modular/multiple_zeta.py +++ b/src/sage/modular/multiple_zeta.py @@ -167,7 +167,7 @@ # **************************************************************************** from __future__ import annotations import numbers -from typing import Iterator +from collections.abc import Iterator from itertools import product from sage.misc.fast_methods import Singleton diff --git a/src/sage/modular/multiple_zeta_F_algebra.py b/src/sage/modular/multiple_zeta_F_algebra.py index 41aeb62b8c4..fca676694ee 100644 --- a/src/sage/modular/multiple_zeta_F_algebra.py +++ b/src/sage/modular/multiple_zeta_F_algebra.py @@ -26,7 +26,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator from sage.arith.misc import bernoulli from sage.categories.rings import Rings diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 2576c9ddc18..e1df5066186 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -278,7 +278,7 @@ def OverconvergentModularForms(prime, weight, radius, base_ring=QQ, prec=20, cha Space of 3-adic 1/2-overconvergent modular forms of weight-character (3, 3, [-1]) over Rational Field """ - if isinstance(prime, Gamma0_class) or isinstance(prime, Gamma1_class): + if isinstance(prime, (Gamma0_class, Gamma1_class)): prime = prime.level() else: prime = ZZ(prime) diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index fedf6160c50..455e9e1bad2 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -87,6 +87,7 @@ # AUXILIARY CODE: SPACES OF MODULAR FORMS AND LINEAR ALGEBRA + def compute_G(p, F): r""" Given a power series `F \in R[[q]]^\times`, for some ring `R`, and an @@ -151,6 +152,7 @@ def low_weight_bases(N, p, m, NN, weightbound): generators.append(list(b)) return generators + def random_low_weight_bases(N,p,m,NN,weightbound): r""" Return list of random integral bases of modular forms of level `N` and @@ -194,6 +196,7 @@ def random_low_weight_bases(N,p,m,NN,weightbound): return RandomLWB + def low_weight_generators(N,p,m,NN): r""" Return a list of lists of modular forms, and an even natural number. @@ -244,6 +247,7 @@ def low_weight_generators(N,p,m,NN): return generators, weightbound + def random_solution(B,K): r""" Return a random solution in nonnegative integers to the equation `a_1 + 2 @@ -398,6 +402,7 @@ def random_new_basis_modp(N, p, k, LWBModp, TotalBasisModp, elldash, bound): return NewBasisCode + def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound): r""" Return a list of lists of lists of lists ``[j, a]``. The pairs ``[j, a]`` @@ -446,6 +451,7 @@ def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound): return CompSpacesCode + def complementary_spaces(N, p, k0, n, mdash, elldashp, elldash, modformsring, bound): r""" Return a list ``Ws``, each element in which is a list ``Wi`` of @@ -523,6 +529,7 @@ def complementary_spaces(N, p, k0, n, mdash, elldashp, elldash, modformsring, bo # AUXILIARY CODE: KATZ EXPANSIONS + def higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, bound): r""" Return a matrix `e` of size ``ell x elldashp`` over the integers modulo @@ -587,6 +594,7 @@ def higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, b return M, Ep1 + def compute_elldash(p, N, k0, n): r""" Return the "Sturm bound" for the space of modular forms of level @@ -615,6 +623,7 @@ def compute_elldash(p, N, k0, n): # *** DEGREE BOUND ON HECKE SERIES *** + def hecke_series_degree_bound(p, N, k, m): r""" Return the ``Wan bound`` on the degree of the characteristic series of the @@ -659,6 +668,7 @@ def hecke_series_degree_bound(p, N, k, m): # Returns matrix A modulo p^m from Step 6 of Algorithm 2. + def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): r""" Return a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` @@ -874,6 +884,7 @@ def compute_Wi(k, p, h, hj, E4, E6): return Wi, hj + def katz_expansions(k0, p, ellp, mdash, n): r""" Return a list `e` of `q`-expansions, and the Eisenstein series `E_{p-1} = 1 + @@ -926,6 +937,7 @@ def katz_expansions(k0, p, ellp, mdash, n): # *** MAIN FUNCTION FOR LEVEL 1 *** + def level1_UpGj(p, klist, m, extra_data=False): r""" Return a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` @@ -1038,6 +1050,7 @@ def level1_UpGj(p, klist, m, extra_data=False): # *** CODE FOR GENERAL LEVEL *** + def is_valid_weight_list(klist, p): r""" This function checks that ``klist`` is a nonempty list of integers all of @@ -1070,6 +1083,7 @@ def is_valid_weight_list(klist, p): if (klist[i] % (p-1)) != k0: raise ValueError("List of weights must be all congruent modulo p-1 = %s, but given list contains %s and %s which are not congruent" % (p - 1, klist[0], klist[i])) + def hecke_series(p, N, klist, m, modformsring=False, weightbound=6): r""" Return the characteristic series modulo `p^m` of the Atkin operator `U_p` @@ -1138,9 +1152,9 @@ def hecke_series(p, N, klist, m, modformsring=False, weightbound=6): oneweight = False # convert single weight to list - if ((isinstance(klist, int)) or (isinstance(klist, Integer))): + if isinstance(klist, (int, Integer)): klist = [klist] - oneweight = True # input is single weight + oneweight = True # input is single weight # algorithm may finish with false output unless: is_valid_weight_list(klist, p) diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index c159ac5eee3..40110016c52 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -85,6 +85,8 @@ _wscache = {} + + def WeightSpace_constructor(p, base_ring=None): r""" Construct the `p`-adic weight space for the given prime p. diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 94ba0843131..04120ba5d7d 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -159,7 +159,7 @@ def unimod_matrices_from_infty(r, s): return [] -class ManinMap(): +class ManinMap: r""" Map from a set of right coset representatives of `\Gamma_0(N)` in `SL_2(\ZZ)` to a coefficient module that satisfies the Manin diff --git a/src/sage/modular/pollack_stevens/meson.build b/src/sage/modular/pollack_stevens/meson.build new file mode 100644 index 00000000000..d22947db12c --- /dev/null +++ b/src/sage/modular/pollack_stevens/meson.build @@ -0,0 +1,26 @@ +py.install_sources( + 'all.py', + 'dist.pxd', + 'distributions.py', + 'fund_domain.py', + 'manin_map.py', + 'modsym.py', + 'padic_lseries.py', + 'sigma0.py', + 'space.py', + subdir: 'sage/modular/pollack_stevens', +) + +extension_data = {'dist' : files('dist.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modular/pollack_stevens', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index c892748174d..8df8655e5e9 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -114,6 +114,7 @@ def _iterate_Up(Phi, p, M, ap, q, aq, check): Phi = ~(q ** (k + 1) + 1 - aq) * Phi return Phi + class PSModSymAction(Action): def __init__(self, actor, MSspace): r""" diff --git a/src/sage/modular/pollack_stevens/sigma0.py b/src/sage/modular/pollack_stevens/sigma0.py index c5ae3741f2f..2659f12cfa3 100644 --- a/src/sage/modular/pollack_stevens/sigma0.py +++ b/src/sage/modular/pollack_stevens/sigma0.py @@ -106,6 +106,7 @@ def __call__(self, g): """ return tuple(g.list()) + class Sigma0_factory(UniqueFactory): r""" Create the monoid of non-singular matrices, upper triangular mod `N`. diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 23b11fef749..c8bbdd1a9a3 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -31,6 +31,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.integer_ring import ZZ + class QuasiModularFormsElement(ModuleElement): r""" A quasimodular forms ring element. Such an element is described by @@ -142,11 +143,11 @@ def q_expansion(self, prec=6): sage: E2.q_expansion(prec=10) 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 - 288*q^6 - 192*q^7 - 360*q^8 - 312*q^9 + O(q^10) """ - E2 = eisenstein_series_qexp(2, prec=prec, K=self.base_ring(), normalization='constant') #normalization -> to force integer coefficients + E2 = eisenstein_series_qexp(2, prec=prec, K=self.base_ring(), normalization='constant') # normalization -> to force integer coefficients coefficients = self._polynomial.coefficients(sparse=False) - return sum(f.q_expansion(prec=prec)*E2**idx for idx, f in enumerate(coefficients)) + return sum(f.q_expansion(prec=prec) * E2**idx for idx, f in enumerate(coefficients)) - qexp = q_expansion # alias + qexp = q_expansion # alias def _repr_(self): r""" diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index c846411a421..e91e6950e76 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -140,7 +140,9 @@ Order of Quaternion Algebra (-1, -23) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k) sage: B.right_ideals() - (Fractional ideal (2 + 2*j, 2*i + 2*k, 4*j, 4*k), Fractional ideal (2 + 2*j, 2*i + 6*k, 8*j, 8*k), Fractional ideal (2 + 10*j + 8*k, 2*i + 8*j + 6*k, 16*j, 16*k)) + (Fractional ideal (4, 4*i, 2 + 2*j, 2*i + 2*k), + Fractional ideal (8, 8*i, 2 + 2*j, 6*i + 2*k), + Fractional ideal (16, 16*i, 10 + 8*i + 2*j, 8 + 6*i + 2*k)) sage: B.hecke_matrix(2) [1 2 0] @@ -395,9 +397,9 @@ def basis_for_left_ideal(R, gens): sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)]) doctest:...: DeprecationWarning: The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras See https://github.com/sagemath/sage/issues/37090 for details. - [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] + [1, 1/2 + 1/2*i, j, 1/3*i + 1/2*j + 1/6*k] sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)]) - [3/2 + 1/2*i + k, i + 2*k, 3/2*j + 3/2*k, 3*k] + [3, 3/2 + 3/2*i, 3*j, i + 3/2*j + 1/2*k] """ from sage.misc.superseded import deprecation deprecation(37090, "The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras") @@ -426,7 +428,7 @@ def right_order(R, basis): See https://github.com/sagemath/sage/issues/37090 for details. Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) sage: basis - [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] + [1, 1/2 + 1/2*i, j, 1/3*i + 1/2*j + 1/6*k] sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens() sage: basis = B.maximal_order()._left_ideal_basis([i*j - j]) @@ -803,14 +805,14 @@ def cyclic_submodules(self, I, p): sage: B = BrandtModule(11) sage: I = B.order_of_level_N().unit_ideal() sage: B.cyclic_submodules(I, 2) - [Fractional ideal (1/2 + 3/2*j + k, 1/2*i + j + 1/2*k, 2*j, 2*k), - Fractional ideal (1/2 + 1/2*i + 1/2*j + 1/2*k, i + k, j + k, 2*k), - Fractional ideal (1/2 + 1/2*j + k, 1/2*i + j + 3/2*k, 2*j, 2*k)] + [Fractional ideal (2, 2*i, 3/2 + i + 1/2*j, 1 + 1/2*i + 1/2*k), + Fractional ideal (2, 1 + i, 1 + j, 1/2 + 1/2*i + 1/2*j + 1/2*k), + Fractional ideal (2, 2*i, 1/2 + i + 1/2*j, 1 + 3/2*i + 1/2*k)] sage: B.cyclic_submodules(I, 3) - [Fractional ideal (1/2 + 1/2*j, 1/2*i + 5/2*k, 3*j, 3*k), - Fractional ideal (1/2 + 3/2*j + 2*k, 1/2*i + 2*j + 3/2*k, 3*j, 3*k), - Fractional ideal (1/2 + 3/2*j + k, 1/2*i + j + 3/2*k, 3*j, 3*k), - Fractional ideal (1/2 + 5/2*j, 1/2*i + 1/2*k, 3*j, 3*k)] + [Fractional ideal (3, 3*i, 1/2 + 1/2*j, 5/2*i + 1/2*k), + Fractional ideal (3, 3*i, 3/2 + 2*i + 1/2*j, 2 + 3/2*i + 1/2*k), + Fractional ideal (3, 3*i, 3/2 + i + 1/2*j, 1 + 3/2*i + 1/2*k), + Fractional ideal (3, 3*i, 5/2 + 1/2*j, 1/2*i + 1/2*k)] sage: B.cyclic_submodules(I, 11) Traceback (most recent call last): ... @@ -873,16 +875,17 @@ def cyclic_submodules(self, I, p): # right multiplication by X changes something to be written # in terms of the basis for I. - Y = I.basis_matrix() - X = Y**(-1) + basis = basis_for_quaternion_lattice(I.basis(), reverse=False) + Y = matrix(map(list, basis)) + X = ~Y # Compute the matrix of right multiplication by alpha acting on # our fixed choice of basis for this ideal. M_alpha = (matrix([(i * alpha).coefficient_tuple() - for i in I.basis()]) * X).change_ring(GF(p)) + for i in basis]) * X).change_ring(GF(p)) M_beta = (matrix([(i * beta).coefficient_tuple() - for i in I.basis()]) * X).change_ring(GF(p)) + for i in basis]) * X).change_ring(GF(p)) # step 2: Find j such that if f=I[j], then mod 2 we have span(I[0],alpha*I[i]) # has trivial intersection with span(I[j],alpha*I[j]). @@ -1251,9 +1254,9 @@ def right_ideals(self, B=None): sage: B = BrandtModule(23) sage: B.right_ideals() - (Fractional ideal (2 + 2*j, 2*i + 2*k, 4*j, 4*k), - Fractional ideal (2 + 2*j, 2*i + 6*k, 8*j, 8*k), - Fractional ideal (2 + 10*j + 8*k, 2*i + 8*j + 6*k, 16*j, 16*k)) + (Fractional ideal (4, 4*i, 2 + 2*j, 2*i + 2*k), + Fractional ideal (8, 8*i, 2 + 2*j, 6*i + 2*k), + Fractional ideal (16, 16*i, 10 + 8*i + 2*j, 8 + 6*i + 2*k)) TESTS:: @@ -1332,16 +1335,16 @@ def _ideal_products(self, diagonal_only=False): sage: B = BrandtModule(37) sage: B._ideal_products() - [[Fractional ideal (8 + 8*j + 8*k, 4*i + 8*j + 4*k, 16*j, 16*k)], - [Fractional ideal (8 + 24*j + 8*k, 4*i + 8*j + 4*k, 32*j, 32*k), - Fractional ideal (16 + 16*j + 48*k, 4*i + 8*j + 36*k, 32*j + 32*k, 64*k)], - [Fractional ideal (8 + 24*j + 24*k, 4*i + 24*j + 4*k, 32*j, 32*k), - Fractional ideal (8 + 4*i + 16*j + 28*k, 8*i + 16*j + 8*k, 32*j, 64*k), - Fractional ideal (16 + 16*j + 16*k, 4*i + 24*j + 4*k, 32*j + 32*k, 64*k)]] + [[Fractional ideal (16, 16*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k)], + [Fractional ideal (32, 32*i, 8 + 24*i + 8*j, 24 + 12*i + 4*k), + Fractional ideal (32, 64*i, 16 + 48*i + 16*j, 36*i + 8*j + 4*k)], + [Fractional ideal (32, 32*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k), + Fractional ideal (64, 32 + 32*i, 16 + 16*i + 16*j, 40 + 12*i + 4*k), + Fractional ideal (32, 64*i, 16 + 16*i + 16*j, 16 + 52*i + 8*j + 4*k)]] sage: B._ideal_products(diagonal_only=True) - [Fractional ideal (8 + 8*j + 8*k, 4*i + 8*j + 4*k, 16*j, 16*k), - Fractional ideal (16 + 16*j + 48*k, 4*i + 8*j + 36*k, 32*j + 32*k, 64*k), - Fractional ideal (16 + 16*j + 16*k, 4*i + 24*j + 4*k, 32*j + 32*k, 64*k)] + [Fractional ideal (16, 16*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k), + Fractional ideal (32, 64*i, 16 + 48*i + 16*j, 36*i + 8*j + 4*k), + Fractional ideal (32, 64*i, 16 + 16*i + 16*j, 16 + 52*i + 8*j + 4*k)] """ L = self.right_ideals() n = len(L) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 5f076475e8c..4aaaa354d66 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -486,6 +486,16 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): sage: vector(S, 3) # needs sage.rings.finite_rings ... (0, 0, 0) + + We check that ``sparse`` is respected for numpy arrays:: + + sage: # needs numpy + sage: import numpy + sage: a = numpy.array([1,2,3], dtype=numpy.float64) + sage: v = vector(a, sparse=True); v + (1.0, 2.0, 3.0) + sage: v.is_sparse() + True """ from sage.modules.free_module import FreeModule # We first efficiently handle the important special case of the zero vector @@ -563,7 +573,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): except ImportError: pass else: - if isinstance(v, ndarray): + if isinstance(v, ndarray) and not sparse: if len(v.shape) != 1: raise TypeError("cannot convert %r-dimensional array to a vector" % len(v.shape)) from sage.modules.free_module import VectorSpace @@ -1007,20 +1017,20 @@ cdef class FreeModuleElement(Vector): # abstract base class EXAMPLES:: sage: v = vector(ZZ, 4, range(4)) - sage: giac(v) + v # needs sage.libs.giac + sage: giac(v) + v # needs giac [0,2,4,6] :: sage: v = vector(QQ, 3, [2/3, 0, 5/4]) - sage: giac(v) # needs sage.libs.giac + sage: giac(v) # needs giac [2/3,0,5/4] :: sage: P. = ZZ[] sage: v = vector(P, 3, [x^2 + 2, 2*x + 1, -2*x^2 + 4*x]) - sage: giac(v) # needs sage.libs.giac + sage: giac(v) # needs giac [sageVARx^2+2,2*sageVARx+1,-2*sageVARx^2+4*sageVARx] """ return self.list() @@ -2231,7 +2241,7 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: maple(v) # optional - maple Vector[row](3, [x^2+2,2*x+1,-2*x^2+4*x]) """ - return "Vector[row](%s)"%(str(self.list())) + return "Vector[row](%s)" % str(self.list()) def degree(self): """ @@ -2685,7 +2695,7 @@ cdef class FreeModuleElement(Vector): # abstract base class return self._parent.coordinate_ring().zero() return self._dot_product_(r) if self._degree != r._degree: - raise ArithmeticError("degrees (%s and %s) must be the same"%(self.degree(), right.degree())) + raise ArithmeticError("degrees (%s and %s) must be the same" % (self.degree(), right.degree())) # Base rings are not equal => use dot product with coercion return self._dot_product_coerce_(r) diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 78884501a91..6e4171113e5 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -32,6 +32,7 @@ ############################################################################## from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.modules.free_module import FreeModule_submodule_with_basis_pid, FreeModule_ambient_pid @@ -784,16 +785,11 @@ def CVPP_2V(t, V, voronoi_cell): i -= 1 return t - t_new - def approximate_closest_vector(self, t, delta=None, *args, **kwargs): + def approximate_closest_vector(self, t, delta=None, algorithm='embedding', *args, **kwargs): r""" - Compute a vector `w` such that - - .. MATH:: - - |t-w|<(\frac{1}{\delta-\frac{1}{4}})^{d/2}|t-u| - - where `u` is the closest lattice point to `t`, `\delta` is the LLL - reduction parameter, and `d` is the dimension of the lattice. + Compute a vector `w` in this lattice which is close to the target vector `t`. + The ratio `\frac{|t-w|}{|t-u|}`, where `u` is the closest lattice vector to `t`, + is exponential in the dimension of the lattice. This will check whether the basis is already `\delta`-LLL-reduced and otherwise it will run LLL to make sure that it is. For more @@ -805,6 +801,18 @@ def approximate_closest_vector(self, t, delta=None, *args, **kwargs): - ``delta`` -- (default: ``0.99``) the LLL reduction parameter + - ``algorithm`` -- string (default: 'embedding'): + + - ``'embedding'`` -- embeds the lattice in a d+1 dimensional space + and seeks short vectors using LLL. This calls LLL twice but is + usually still quick. + + - ``'nearest_plane'`` -- uses the "NEAREST PLANE" algorithm from [Bab86]_ + + - ``'rounding_off'`` -- uses the "ROUNDING OFF" algorithm from [Bab86]_. + This yields slightly worse results than the other algorithms but is + at least faster than ``'nearest_plane'``. + - ``*args`` -- passed through to :meth:`LLL` - ``**kwds`` -- passed through to :meth:`LLL` @@ -825,30 +833,62 @@ def approximate_closest_vector(self, t, delta=None, *args, **kwargs): ....: [0, 0, 101, 0], [-28, 39, 45, 1]], lll_reduce=False) sage: t = vector([1337]*4) sage: L.approximate_closest_vector(t, delta=0.26) - (1348, 1340, 1383, 1337) + (1331, 1324, 1349, 1334) sage: L.approximate_closest_vector(t, delta=0.99) (1326, 1349, 1339, 1345) sage: L.closest_vector(t) (1326, 1349, 1339, 1345) - ALGORITHM: - - Uses the algorithm from [Bab86]_. + sage: # Checking that the other algorithms work + sage: L.approximate_closest_vector(t, algorithm='nearest_plane') + (1326, 1349, 1339, 1345) + sage: L.approximate_closest_vector(t, algorithm='rounding_off') + (1331, 1324, 1349, 1334) """ if delta is None: delta = ZZ(99)/ZZ(100) - # bound checks on delta are performed in is_LLL_reduced + # Bound checks on delta are performed in is_LLL_reduced if not self._reduced_basis.is_LLL_reduced(delta=delta): self.LLL(*args, delta=delta, **kwargs) B = self._reduced_basis - G = B.gram_schmidt()[0] t = vector(t) - b = t - for i in reversed(range(G.nrows())): - b -= B[i] * ((b * G[i]) / (G[i] * G[i])).round("even") - return (t - b).change_ring(ZZ) + if algorithm == 'embedding': + L = matrix(QQ, B.nrows()+1, B.ncols()+1) + L.set_block(0, 0, B) + L.set_block(B.nrows(), 0, matrix(t)) + weight = (B[-1]*B[-1]).isqrt()+1 # Norm of the largest vector + L[-1, -1] = weight + + # The vector should be the last row but we iterate just in case + for v in reversed(L.LLL(delta=delta, *args, **kwargs).rows()): + if abs(v[-1]) == weight: + return t - v[:-1]*v[-1].sign() + raise ValueError('No suitable vector found in basis.' + 'This is a bug, please report it.') + + elif algorithm == 'nearest_plane': + G = B.gram_schmidt()[0] + + b = t + for i in reversed(range(G.nrows())): + b -= B[i] * ((b * G[i]) / (G[i] * G[i])).round("even") + return (t - b).change_ring(ZZ) + + elif algorithm == 'rounding_off': + # t = x*B might not have a solution over QQ so we instead solve + # the system x*B*B^T = t*B^T which will be the "closest" solution + # if it does not exist, same effect as using the psuedo-inverse + sol = (B*B.T).solve_left(t*B.T) + return vector(ZZ, [QQ(x).round('even') for x in sol])*B - babai = approximate_closest_vector \ No newline at end of file + else: + raise ValueError("algorithm must be one of 'embedding', 'nearest_plane' or 'rounding_off'") + + def babai(self, *args, **kwargs): + """ + Alias for :meth:`approximate_closest_vector`. + """ + return self.approximate_closest_vector(*args, **kwargs) diff --git a/src/sage/modules/free_quadratic_module_integer_symmetric.py b/src/sage/modules/free_quadratic_module_integer_symmetric.py index c7473002448..c58f0f3ea1e 100644 --- a/src/sage/modules/free_quadratic_module_integer_symmetric.py +++ b/src/sage/modules/free_quadratic_module_integer_symmetric.py @@ -51,8 +51,9 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - +from pathlib import Path from copy import copy + from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer from sage.rings.rational_field import QQ @@ -84,7 +85,7 @@ def IntegralLattice(data, basis=None): - ``data`` -- can be one of the following: * a symmetric matrix over the rationals -- the inner product matrix - * an integer -- the dimension for an Euclidean lattice + * an integer -- the dimension for a Euclidean lattice * a symmetric Cartan type or anything recognized by :class:`CartanMatrix` (see also :mod:`Cartan types `) @@ -119,7 +120,7 @@ def IntegralLattice(data, basis=None): [ 2 1] [ 1 -2] - We can define an Euclidean lattice just by its dimension:: + We can define a Euclidean lattice just by its dimension:: sage: IntegralLattice(3) Lattice of degree 3 and rank 3 over Integer Ring @@ -1480,13 +1481,12 @@ def LLL(self): p, n = self.signature_pair() if p * n != 0: from sage.env import SAGE_EXTCODE - from sage.interfaces.gp import gp from sage.libs.pari import pari - m = self.gram_matrix().__pari__() - gp.read(SAGE_EXTCODE + "/pari/simon/qfsolve.gp") - m = gp.eval('qflllgram_indefgoon(%s)' % m) - # convert the output string to sage - G, U = pari(m).sage() + m = self.gram_matrix() + pari.read(Path(SAGE_EXTCODE) / "pari" / "simon" / "qfsolve.gp") + m = pari('qflllgram_indefgoon')(m) + # convert the output to sage + G, U = m.sage() U = U.T else: e = 1 @@ -1537,13 +1537,15 @@ def _fplll_enumerate(self, target=None): sage: L = IntegralLattice('A4') sage: t = vector([1.2, -3/11, 5.5, -9.1]) sage: short = L.enumerate_short_vectors() # implicit doctest - sage: [next(short) for _ in range(10)] - [(0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 1, 1), (1, 1, 1, 1), (0, 0, 1, 0), - (1, 1, 1, 0), (0, 1, 1, 0), (0, 1, 0, 0), (1, 1, 0, 0), (1, 0, 0, 0)] + sage: vecs = [next(short) for _ in range(10)] + sage: sorted(vecs, key=lambda v: (L(v).inner_product(L(v)), v)) + [(0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 1, 0), + (0, 1, 1, 1), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 1)] sage: close = L.enumerate_close_vectors(t) # implicit doctest - sage: [next(close) for _ in range(10)] - [(1, 0, 6, -9), (1, -1, 5, -9), (2, 0, 6, -9), (1, 0, 5, -9), (1, -1, 5, -10), - (2, 1, 6, -9), (1, 0, 5, -10), (2, 0, 5, -9), (1, 0, 6, -8), (1, -1, 6, -9)] + sage: vecs = [next(close) for _ in range(10)] + sage: sorted(vecs, key=lambda v: (L(v).inner_product(L(v)), v)) + [(1, 0, 6, -8), (1, 0, 5, -9), (2, 0, 5, -9), (1, -1, 5, -9), (2, 1, 6, -9), + (1, 0, 6, -9), (2, 0, 6, -9), (1, 0, 5, -10), (1, -1, 6, -9), (1, -1, 5, -10)] """ L = self.LLL() dim = L.dimension() @@ -1598,11 +1600,24 @@ def enumerate_short_vectors(self): sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]]) sage: short = L.enumerate_short_vectors() - sage: [next(short) for _ in range(20)] + sage: vecs = [next(short) for _ in range(20)] + sage: sorted(vecs, key=lambda v: (L(v).inner_product(L(v)), v)) [(1, -1, 1, 0), (2, -2, 2, 0), (3, -3, 3, 0), (0, 3, 2, 4), (1, 2, 3, 4), - (4, 4, 1, 0), (3, 2, -2, -4), (3, 5, 0, 0), (4, 1, -1, -4), (-1, 4, 1, 4), - (2, 1, 4, 4), (5, 3, 2, 0), (2, 3, -3, -4), (2, 6, -1, 0), (5, 0, 0, -4), - (-2, 5, 0, 4), (4, -4, 4, 0), (6, 2, 3, 0), (1, 4, -4, -4), (3, 0, 5, 4)] + (3, 2, -2, -4), (4, 4, 1, 0), (-1, 4, 1, 4), (3, 5, 0, 0), (4, 1, -1, -4), + (2, 1, 4, 4), (2, 3, -3, -4), (5, 3, 2, 0), (2, 6, -1, 0), (5, 0, 0, -4), + (-2, 5, 0, 4), (4, -4, 4, 0), (1, 4, -4, -4), (6, 2, 3, 0), (3, 0, 5, 4)] + + This example demonstrates that the lattice inner product is used for the norm:: + + sage: Q = Matrix(QQ, [[1000, 0], [0, 1]]) + sage: B = [[1, 1], [1, -1]] + sage: L = IntegralLattice(Q, basis=B) + sage: short = L.enumerate_short_vectors() + sage: vecs = [next(short) for _ in range(20)] + sage: sorted(vecs, key=lambda v: (L(v).inner_product(L(v)), v)) + [(0, -2), (0, -4), (0, -6), (0, -8), (0, -10), (0, -12), (0, -14), (0, -16), + (0, -18), (0, -20), (0, -22), (0, -24), (0, -26), (0, -28), (0, -30), (-1, -1), + (-1, 1), (-1, -3), (-1, 3), (0, -32)] """ yield from self._fplll_enumerate() @@ -1621,9 +1636,10 @@ def enumerate_close_vectors(self, target): sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]]) sage: t = vector([1/2, -133/7, 123.44, -11]) sage: close = L.enumerate_close_vectors(t) - sage: [next(close) for _ in range(10)] - [(1, -18, 123, 148), (2, -19, 124, 148), (0, -17, 122, 148), (3, -20, 125, 148), (-1, -16, 121, 148), - (-2, -20, 125, 152), (-2, -23, 123, 148), (4, -21, 126, 148), (-3, -22, 122, 148), (-3, -19, 124, 152)] + sage: vecs = [next(close) for _ in range(10)] + sage: sorted(vecs, key=lambda v: (L(v).inner_product(L(v)), v)) + [(-1, -16, 121, 148), (0, -17, 122, 148), (-3, -22, 122, 148), (1, -18, 123, 148), (-2, -23, 123, 148), + (2, -19, 124, 148), (3, -20, 125, 148), (4, -21, 126, 148), (-3, -19, 124, 152), (-2, -20, 125, 152)] """ yield from self._fplll_enumerate(target) diff --git a/src/sage/modules/matrix_morphism.py b/src/sage/modules/matrix_morphism.py index 5736d86932d..175d152f364 100644 --- a/src/sage/modules/matrix_morphism.py +++ b/src/sage/modules/matrix_morphism.py @@ -578,7 +578,7 @@ def __mul__(self, right): Codomain: Vector space of dimension 2 over Rational Field - We can test interraction between morphisms with different ``side``:: + We can test interaction between morphisms with different ``side``:: sage: V = ZZ^2 sage: m = matrix(2, [1,1,0,1]) diff --git a/src/sage/modules/meson.build b/src/sage/modules/meson.build new file mode 100644 index 00000000000..bc505da9372 --- /dev/null +++ b/src/sage/modules/meson.build @@ -0,0 +1,101 @@ +py.install_sources( + 'all.py', + 'complex_double_vector.py', + 'diamond_cutting.py', + 'filtered_vector_space.py', + 'finite_submodule_iter.pxd', + 'free_module.py', + 'free_module_element.pxd', + 'free_module_homspace.py', + 'free_module_integer.py', + 'free_module_morphism.py', + 'free_quadratic_module.py', + 'free_quadratic_module_integer_symmetric.py', + 'matrix_morphism.py', + 'misc.py', + 'module.pxd', + 'module_functors.py', + 'multi_filtered_vector_space.py', + 'quotient_module.py', + 'real_double_vector.py', + 'submodule.py', + 'tensor_operations.py', + 'torsion_quadratic_module.py', + 'tutorial_free_modules.py', + 'vector_callable_symbolic_dense.py', + 'vector_complex_double_dense.pxd', + 'vector_double_dense.pxd', + 'vector_integer_dense.pxd', + 'vector_integer_sparse.pxd', + 'vector_mod2_dense.pxd', + 'vector_modn_dense.pxd', + 'vector_modn_sparse.pxd', + 'vector_numpy_dense.pxd', + 'vector_numpy_integer_dense.pxd', + 'vector_rational_dense.pxd', + 'vector_rational_sparse.pxd', + 'vector_real_double_dense.pxd', + 'vector_space_homspace.py', + 'vector_space_morphism.py', + 'vector_symbolic_dense.py', + 'vector_symbolic_sparse.py', + subdir: 'sage/modules', +) + +extension_data = { + 'finite_submodule_iter' : files('finite_submodule_iter.pyx'), + 'free_module_element' : files('free_module_element.pyx'), + 'module' : files('module.pyx'), + 'vector_complex_double_dense' : files('vector_complex_double_dense.pyx'), + 'vector_double_dense' : files('vector_double_dense.pyx'), + 'vector_integer_dense' : files('vector_integer_dense.pyx'), + 'vector_integer_sparse' : files('vector_integer_sparse.pyx'), + 'vector_modn_dense' : files('vector_modn_dense.pyx'), + 'vector_modn_sparse' : files('vector_modn_sparse.pyx'), + 'vector_numpy_dense' : files('vector_numpy_dense.pyx'), + 'vector_numpy_integer_dense' : files('vector_numpy_integer_dense.pyx'), + 'vector_rational_dense' : files('vector_rational_dense.pyx'), + 'vector_rational_sparse' : files('vector_rational_sparse.pyx'), + 'vector_real_double_dense' : files('vector_real_double_dense.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modules', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cysignals, gd, gmp, m4ri, png], + ) +endforeach + +extension_data_cpp = {'vector_mod2_dense': files('vector_mod2_dense.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modules', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cysignals, gd, gmp, m4ri, png], + ) +endforeach + +install_subdir('fg_pid', install_dir: sage_install_dir / 'modules') +install_subdir('fp_graded', install_dir: sage_install_dir / 'modules') +subdir('with_basis') diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index 3605f4cf865..9517b8487d8 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -325,7 +325,7 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): name = singular._next_var_name() values = str(self.list())[1:-1] - singular.eval("intvec %s = %s"%(name, values)) + singular.eval("intvec %s = %s" % (name, values)) from sage.interfaces.singular import SingularElement return SingularElement(singular, 'foobar', name, True) diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index 35286c51457..ca14af9b313 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -201,13 +201,13 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): xi = x[i] if isinstance(xi, (IntegerMod_int, int, Integer)): # the if/else statement is because in some compilers, (-1)%2 is -1 - mzd_write_bit(self._entries, 0, i, 1 if xi%2 else 0) + mzd_write_bit(self._entries, 0, i, 1 if xi % 2 else 0) elif isinstance(xi, Rational): if not (xi.denominator() % 2): raise ZeroDivisionError("inverse does not exist") mzd_write_bit(self._entries, 0, i, 1 if (xi.numerator() % 2) else 0) else: - mzd_write_bit(self._entries, 0, i, xi%2) + mzd_write_bit(self._entries, 0, i, xi % 2) elif x != 0: raise TypeError("can't initialize vector from nonzero non-list") elif self._degree: @@ -502,8 +502,8 @@ def unpickle_v0(parent, entries, degree, is_immutable): for i in range(degree): if isinstance(entries[i], (IntegerMod_int, int, Integer)): xi = entries[i] - mzd_write_bit(v._entries, 0, i, xi%2) + mzd_write_bit(v._entries, 0, i, xi % 2) else: - mzd_write_bit(v._entries, 0, i, entries[i]%2) + mzd_write_bit(v._entries, 0, i, entries[i] % 2) v._is_immutable = int(is_immutable) return v diff --git a/src/sage/modules/vector_modn_dense.pyx b/src/sage/modules/vector_modn_dense.pyx index 79e27b5b3b9..f129632bd03 100644 --- a/src/sage/modules/vector_modn_dense.pyx +++ b/src/sage/modules/vector_modn_dense.pyx @@ -168,19 +168,34 @@ cdef class Vector_modn_dense(free_module_element.FreeModuleElement): self._init(parent.degree(), parent, parent.base_ring().order()) def __init__(self, parent, x, coerce=True, copy=True): + """ + Create an element. + + TESTS: + + Note that ``coerce=False`` is dangerous:: + + sage: V = VectorSpace(GF(7), 3) + sage: v = V([2, 9, -5], coerce=False) + sage: v[0] == v[1] + False + sage: v[0] + 1 == v[1] + 1 + True + sage: v[0] == v[2] + False + """ cdef Py_ssize_t i - cdef mod_int a, p - if isinstance(x, xrange): + cdef mod_int a + if isinstance(x, range): x = tuple(x) if isinstance(x, (list, tuple)): if len(x) != self._degree: raise TypeError("x must be a list of the right length") if coerce: R = parent.base_ring() - p = R.order() for i from 0 <= i < self._degree: a = int(R(x[i])) - self._entries[i] = a % p + self._entries[i] = a else: for i from 0 <= i < self._degree: self._entries[i] = x[i] diff --git a/src/sage/modules/vector_modn_sparse.pyx b/src/sage/modules/vector_modn_sparse.pyx index a6b59ae9ca5..58ccb2415d4 100644 --- a/src/sage/modules/vector_modn_sparse.pyx +++ b/src/sage/modules/vector_modn_sparse.pyx @@ -251,7 +251,7 @@ cdef int add_c_vector_modint_init(c_vector_modint* sum, c_vector_modint* v, while i < v.num_nonzero or j < w.num_nonzero: if i >= v.num_nonzero: # just copy w in z.positions[k] = w.positions[j] - z.entries[k] = (multiple*w.entries[j])%v.p + z.entries[k] = (multiple * w.entries[j]) % v.p j = j + 1 k = k + 1 elif j >= w.num_nonzero: # just copy v in @@ -265,7 +265,7 @@ cdef int add_c_vector_modint_init(c_vector_modint* sum, c_vector_modint* v, i = i + 1 k = k + 1 elif v.positions[i] > w.positions[j]: # copy entry from w in - s = (multiple*w.entries[j])%v.p + s = (multiple * w.entries[j]) % v.p if s != 0: z.positions[k] = w.positions[j] z.entries[k] = s diff --git a/src/sage/modules/vector_rational_sparse.pyx b/src/sage/modules/vector_rational_sparse.pyx index fcdaf2b7cf8..f4a7d4b52f8 100644 --- a/src/sage/modules/vector_rational_sparse.pyx +++ b/src/sage/modules/vector_rational_sparse.pyx @@ -267,7 +267,7 @@ cdef int add_mpq_vector_init(mpq_vector* sum, Initialize sum and set sum = v + multiple*w. """ if v.degree != w.degree: - print("Can't add vectors of degree %s and %s"%(v.degree, w.degree)) + print("Can't add vectors of degree %s and %s" % (v.degree, w.degree)) raise ArithmeticError("The vectors must have the same degree.") cdef Py_ssize_t nz, i, j, k, do_multiply diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 47e2fe1cc6f..73917208342 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -405,7 +405,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): pass for monomial, c in terms: - b = repr_monomial(monomial) # PCR + b = repr_monomial(monomial) # PCR if c != 0: break_points = [] coeff = coeff_repr(c, False) @@ -416,17 +416,17 @@ cdef class IndexedFreeModuleElement(ModuleElement): coeff = "-" elif b._l > 0: if one_basis is not None and len(coeff) > 0 and monomial == one_basis and strip_one: - b = empty_ascii_art # "" + b = empty_ascii_art # "" else: b = AsciiArt([scalar_mult]) + b if not first: if len(coeff) > 0 and coeff[0] == "-": - coeff = " - %s"%coeff[1:] + coeff = " - %s" % coeff[1:] else: - coeff = " + %s"%coeff + coeff = " + %s" % coeff break_points = [2] else: - coeff = "%s"%coeff + coeff = "%s" % coeff if coeff: chunks.append(AsciiArt([coeff], break_points)) if b._l: @@ -435,10 +435,9 @@ cdef class IndexedFreeModuleElement(ModuleElement): s = ascii_art(*chunks) if first: return AsciiArt(["0"]) - elif s == empty_ascii_art: + if s == empty_ascii_art: return AsciiArt(["1"]) - else: - return s + return s def _unicode_art_(self): r""" diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 0c96c6a5b9d..5c4202419a5 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -310,9 +310,9 @@ def _repr_(self): M = M._module return f"({self._semigroup})-invariant submodule of {M}" - def _latex_(self): + def _latex_(self) -> str: r""" - Return a latex representaion of ``self``. + Return a latex representation of ``self``. EXAMPLES:: diff --git a/src/sage/modules/with_basis/meson.build b/src/sage/modules/with_basis/meson.build new file mode 100644 index 00000000000..1956c6ac99c --- /dev/null +++ b/src/sage/modules/with_basis/meson.build @@ -0,0 +1,24 @@ +py.install_sources( + 'all.py', + 'cell_module.py', + 'indexed_element.pxd', + 'invariant.py', + 'morphism.py', + 'representation.py', + 'subquotient.py', + subdir: 'sage/modules/with_basis', +) + +extension_data = {'indexed_element' : files('indexed_element.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/modules/with_basis', + install: true, + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index aff13bf2473..5271c7a6b38 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -685,8 +685,8 @@ def subrepresentation(self, gens, check=True, already_echelonized=False, 5 """ if not is_closed and gens: - R = self.base_ring() - repr_mats = [self.representation_matrix(g) for g in self._semigroup.gens()] + repr_mats = [self.representation_matrix(g) + for g in self._semigroup.gens()] amb_dim = self.dimension() SM = matrix([v._vector_() for v in gens]) SM.echelonize() @@ -1489,7 +1489,7 @@ def __classcall_private__(cls, reps, **options): reps = sum((module._sets if isinstance(module, Representation_Tensor) else (module,) for module in reps), ()) if all('FiniteDimensional' in M.category().axioms() for M in reps): options['category'] = options['category'].FiniteDimensional() - return super(Representation_Tensor, cls).__classcall__(cls, reps, **options) + return super().__classcall__(cls, reps, **options) def __init__(self, reps, **options): r""" @@ -2892,7 +2892,6 @@ def __init__(self, V, shape): keys = list(V.basis().keys()) ambient = tensor([V]*d) - I = ambient.indices() cla = SymmetricGroupAlgebra(R, SymmetricGroup(d)).young_symmetrizer(shape) mc = cla.monomial_coefficients(copy=False) gens = [ambient.sum_of_terms((tuple([k[i-1] for i in p.tuple()]), coeff) diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 907414e4291..3b8fe3f0e07 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -114,7 +114,7 @@ def FreeAbelianMonoid(index_set=None, names=None, **kwds): Return a free abelian monoid on `n` generators or with the generators indexed by a set `I`. - We construct free abelian monoids by specifing either: + We construct free abelian monoids by specifying either: - the number of generators and/or the names of the generators - the indexing set for the generators (this ignores the other two inputs) diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 9e8553184d1..e7a2a9f6532 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -73,7 +73,7 @@ class FreeMonoid(Monoid_class, UniqueRepresentation): Return a free monoid on `n` generators or with the generators indexed by a set `I`. - We construct free monoids by specifing either: + We construct free monoids by specifying either: - the number of generators and/or the names of the generators - the indexing set for the generators diff --git a/src/sage/monoids/meson.build b/src/sage/monoids/meson.build new file mode 100644 index 00000000000..df2a4ae36be --- /dev/null +++ b/src/sage/monoids/meson.build @@ -0,0 +1,32 @@ +py.install_sources( + 'all.py', + 'automatic_semigroup.py', + 'free_abelian_monoid.py', + 'free_abelian_monoid_element.pxd', + 'free_monoid.py', + 'free_monoid_element.py', + 'hecke_monoid.py', + 'indexed_free_monoid.py', + 'monoid.py', + 'string_monoid.py', + 'string_monoid_element.py', + 'string_ops.py', + 'trace_monoid.py', + subdir: 'sage/monoids', +) + +extension_data = { + 'free_abelian_monoid_element' : files('free_abelian_monoid_element.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/monoids', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/monoids/trace_monoid.py b/src/sage/monoids/trace_monoid.py index 6b72feca4b5..cb1c036e039 100644 --- a/src/sage/monoids/trace_monoid.py +++ b/src/sage/monoids/trace_monoid.py @@ -109,7 +109,7 @@ def _repr_(self) -> str: return "1" return f"[{self.value}]" - def _richcmp_(self, other, op): + def _richcmp_(self, other, op) -> bool: r""" Compare two traces by their lexicographic normal forms. @@ -147,7 +147,7 @@ def lex_normal_form(self): """ return self.value - def foata_normal_form(self): + def foata_normal_form(self) -> tuple: r""" Return the Foata normal form of ``self``. @@ -300,12 +300,12 @@ def min_hasse_diagram(self): elements.reverse() independence = self.parent()._independence reachable = {} - min = set() + mini = set() graph = DiGraph({}) for i, x in enumerate(elements): reachable[i] = set() - front = min.copy() + front = mini.copy() while front: used = set() for j in list(front): @@ -314,14 +314,14 @@ def min_hasse_diagram(self): graph.add_edge(i, j) reachable[i].add(j) reachable[i].update(reachable[j]) - if j in min: - min.remove(j) + if j in mini: + mini.remove(j) used.add(j) forbidden = set(chain.from_iterable(reachable[v] for v in used)) front = {dest for _, dest in graph.outgoing_edges(front, labels=False)} front = front - forbidden - min.add(i) + mini.add(i) length = len(elements) graph.relabel(length - 1 - i for i in range(length)) @@ -432,7 +432,7 @@ class TraceMonoid(UniqueRepresentation, Monoid_class): Return a free partially commuting monoid (trace monoid) on `n` generators over independence relation `I`. - We construct a trace monoid by specifing: + We construct a trace monoid by specifying: - a free monoid and independence relation - or generator names and independence relation, @@ -495,7 +495,7 @@ def __classcall_private__(cls, M=None, I=frozenset(), names=None): rels = set() gen_from_str = {names[i]: gen for i, gen in enumerate(M.gens())} - for (x, y) in I: + for x, y in I: try: if isinstance(x, str): x = gen_from_str[x] @@ -513,7 +513,7 @@ def __classcall_private__(cls, M=None, I=frozenset(), names=None): return super().__classcall__(cls, M, I, names) - def __init__(self, M, I, names): + def __init__(self, M, I, names) -> None: r""" Initialize ``self``. @@ -673,7 +673,7 @@ def _compute_lex_normal_form(self, x): return prod(elements) @cached_method - def _compute_foata_normal_form(self, x): + def _compute_foata_normal_form(self, x) -> tuple: r""" Return Foata normal form of the monoid element. @@ -916,7 +916,7 @@ def words(self, length): if not ((list(word.value)[-1][0], suffix.value) in self._independence and list(word.value)[-1][0] > suffix.value)]) - def _sorted_independence(self): + def _sorted_independence(self) -> list: r""" Return independence relation over the monoid. @@ -951,7 +951,7 @@ def _repr_(self) -> str: ", ".join(f"{{{x}, {y}}}" for (x, y) in self._sorted_independence())) - def _latex_(self): + def _latex_(self) -> str: r""" LaTeX representation of trace monoids. diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 4c51803b5f9..14c60dc3167 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -40,7 +40,7 @@ cdef class CVXOPTBackend(GenericBackend): Mixed Integer Program (no objective, 0 variables, 0 constraints) """ - cdef list objective_function #c_matrix + cdef list objective_function # c_matrix cdef list G_matrix cdef str prob_name cdef bint is_maximize @@ -65,7 +65,7 @@ cdef class CVXOPTBackend(GenericBackend): sage: p = get_solver(solver='CVXOPT') """ - self.objective_function = [] #c_matrix in the example for cvxopt + self.objective_function = [] # c_matrix in the example for cvxopt self.G_matrix = [] self.prob_name = '' self.obj_constant_term = 0 @@ -79,12 +79,12 @@ cdef class CVXOPTBackend(GenericBackend): self.row_name_var = [] self.col_name_var = [] - self.param = {"show_progress":False, - "maxiters":100, - "abstol":1e-7, - "reltol":1e-6, - "feastol":1e-7, - "refinement":0 } + self.param = {"show_progress": False, + "maxiters": 100, + "abstol": 1e-7, + "reltol": 1e-6, + "feastol": 1e-7, + "refinement": 0} self.answer = {} if maximization: self.set_sense(+1) diff --git a/src/sage/numerical/backends/glpk_graph_backend.pyx b/src/sage/numerical/backends/glpk_graph_backend.pyx index 514e1b9676b..ca3907e2fdb 100644 --- a/src/sage/numerical/backends/glpk_graph_backend.pyx +++ b/src/sage/numerical/backends/glpk_graph_backend.pyx @@ -484,11 +484,11 @@ cdef class GLPKGraphBackend(): cdef c_v_data * vdata = vert.data return { - "rhs" : vdata.rhs, - "pi" : vdata.pi, - "cut" : vdata.cut, - "es" : vdata.es, - "ls" : vdata.ls + "rhs": vdata.rhs, + "pi": vdata.pi, + "cut": vdata.cut, + "es": vdata.es, + "ls": vdata.ls } cpdef dict get_vertices(self, verts): @@ -840,7 +840,7 @@ cdef class GLPKGraphBackend(): cdef int i = self._find_vertex(vert) if i < 0: - raise RuntimeError("Vertex %s does not exist."%(vert)) + raise RuntimeError("Vertex %s does not exist." % vert) cdef int num[2] num[1] = i + 1 @@ -880,7 +880,7 @@ cdef class GLPKGraphBackend(): verts_val = [self._find_vertex(v) for v in verts] if -1 in verts_val: i = verts_val.index(-1) - raise RuntimeError("Vertex %s does not exist."%(verts[i])) + raise RuntimeError("Vertex %s does not exist." % verts[i]) cdef int * num = check_allocarray(len(verts_val) + 1, sizeof(int)) cdef int ndel = len(verts_val) diff --git a/src/sage/numerical/backends/meson.build b/src/sage/numerical/backends/meson.build new file mode 100644 index 00000000000..a6a53e97033 --- /dev/null +++ b/src/sage/numerical/backends/meson.build @@ -0,0 +1,66 @@ +# Cannot be found via pkg-config +glpk = cc.find_library('glpk') + +py.install_sources( + 'all.py', + 'all__sagemath_polyhedra.py', + 'cvxopt_backend_test.py', + 'cvxpy_backend.pxd', + 'cvxpy_backend_test.py', + 'generic_backend.pxd', + 'generic_backend_test.py', + 'generic_sdp_backend.pxd', + 'glpk_backend.pxd', + 'glpk_backend_test.py', + 'glpk_exact_backend.pxd', + 'glpk_exact_backend_test.py', + 'glpk_graph_backend.pxd', + 'interactivelp_backend.pxd', + 'interactivelp_backend_test.py', + 'logging_backend.py', + 'matrix_sdp_backend.pxd', + 'ppl_backend_test.py', + 'scip_backend.pxd', + 'scip_backend_test.py', + subdir: 'sage/numerical/backends', +) + +extension_data = { + 'cvxopt_backend' : files('cvxopt_backend.pyx'), + 'cvxopt_sdp_backend' : files('cvxopt_sdp_backend.pyx'), + 'cvxpy_backend' : files('cvxpy_backend.pyx'), + 'generic_backend' : files('generic_backend.pyx'), + 'generic_sdp_backend' : files('generic_sdp_backend.pyx'), + 'glpk_backend' : files('glpk_backend.pyx'), + 'glpk_exact_backend' : files('glpk_exact_backend.pyx'), + 'glpk_graph_backend' : files('glpk_graph_backend.pyx'), + 'interactivelp_backend' : files('interactivelp_backend.pyx'), + 'matrix_sdp_backend' : files('matrix_sdp_backend.pyx'), + 'ppl_backend' : files('ppl_backend.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/numerical/backends', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, glpk, gmp], + ) +endforeach + +extension_data_cpp = {'scip_backend': files('scip_backend.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/numerical/backends', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/numerical/meson.build b/src/sage/numerical/meson.build new file mode 100644 index 00000000000..222deff834e --- /dev/null +++ b/src/sage/numerical/meson.build @@ -0,0 +1,35 @@ +py.install_sources( + 'all.py', + 'all__sagemath_polyhedra.py', + 'interactive_simplex_method.py', + 'knapsack.py', + 'linear_functions.pxd', + 'linear_tensor.py', + 'linear_tensor_constraints.py', + 'linear_tensor_element.pxd', + 'mip.pxd', + 'optimize.py', + 'sdp.pxd', + subdir: 'sage/numerical', +) + +extension_data = { + 'gauss_legendre' : files('gauss_legendre.pyx'), + 'linear_functions' : files('linear_functions.pyx'), + 'linear_tensor_element' : files('linear_tensor_element.pyx'), + 'mip' : files('mip.pyx'), + 'sdp' : files('sdp.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/numerical', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cypari2, gmp, mpfr], + ) +endforeach + +subdir('backends') diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 50d952c2ae0..2395868b43d 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -1836,7 +1836,7 @@ cdef class MixedIntegerLinearProgram(SageObject): cdef int i if obj is None: - f = {-1 : 0} + f = {-1: 0} else: # See if it is a constant R = self.base_ring() @@ -3389,7 +3389,7 @@ cdef class MIPVariable(FiniteFamily): integer=(self._vtype == self._p.__INTEGER), obj=zero, name=name) - v = self._p.linear_functions_parent()({j : 1}) + v = self._p.linear_functions_parent()({j: 1}) self._p._variables[v] = j self._dictionary[i] = v return v diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index b3e42a6a6a2..098fe0ce95f 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -577,7 +577,7 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) min = optimize.fmin_tnc(f, x0, approx_grad=True, bounds=cons, messages=0, **args)[0] elif isinstance(cons[0], (function_type, Expression)): min = optimize.fmin_cobyla(f, x0, cons, **args) - elif isinstance(cons, function_type) or isinstance(cons, Expression): + elif isinstance(cons, (function_type, Expression)): min = optimize.fmin_cobyla(f, x0, cons, **args) return vector(RDF, min) @@ -740,7 +740,7 @@ def error_function(params, x_data, y_data): estimated_params = estimated_params.tolist() if solution_dict: - return {i0: i1 for i0, i1 in zip(parameters, estimated_params)} + return dict(zip(parameters, estimated_params)) return [item[0] == item[1] for item in zip(parameters, estimated_params)] @@ -859,7 +859,7 @@ def binpacking(items, maximum=1, k=None, solver=None, verbose=0, TypeError: parameter items must be a list or a dictionary. """ if isinstance(items, list): - weight = {i:w for i,w in enumerate(items)} + weight = dict(enumerate(items)) elif isinstance(items, dict): weight = items else: diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 069bea9b3a4..7c08ed08e10 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -803,8 +803,8 @@ cdef class SemidefiniteProgram(SageObject): if obj is not None: f = obj.dict() else: - f = {-1 : 0} - d = f.pop(-1,self._backend.zero()) + f = {-1: 0} + d = f.pop(-1, self._backend.zero()) for i in range(self._backend.ncols()): values.append(f.get(i,self._backend.zero())) @@ -1256,7 +1256,7 @@ cdef class SDPVariable(Element): zero = self._p._backend.zero() name = self._name + "[" + str(i) + "]" if self._name else None j = self._p._backend.add_variable( obj=zero, name=name) - v = self._p.linear_function({j : 1}) + v = self._p.linear_function({j: 1}) self._p._variables[v] = j self._dict[i] = v return v diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index b23e997b7e2..a01d680bdcc 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -51,7 +51,7 @@ def normalize_input(a): return ((a,), {}) -class Parallel(): +class Parallel: r""" Create a ``parallel``-decorated function. This is the object created by :func:`parallel`. @@ -119,7 +119,7 @@ def __call__(self, f): @instancedoc -class ParallelFunction(): +class ParallelFunction: """ Class which parallelizes a function or class method. This is typically accessed indirectly through @@ -422,7 +422,7 @@ def parallel(p_iter='fork', ncpus=None, **kwds): # def f(...): ... ################################################################### -class Fork(): +class Fork: """ A ``fork`` decorator class. """ @@ -516,8 +516,6 @@ def fork(f=None, timeout=0, verbose=False): sage: @fork(timeout=0.1, verbose=True) ....: def g(n, m): return factorial(n).ndigits() + m - sage: g(5, m=5) - 8 sage: g(10^7, m=5) Killing subprocess ... with input ((10000000,), {'m': 5}) which took too long 'NO DATA (timed out)' diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 239afb6df8b..a182a136a2f 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -628,7 +628,7 @@ class AbortError(Exception): pass -class ActiveTaskCounterDarwin(): +class ActiveTaskCounterDarwin: r""" Handling the number of active tasks. @@ -741,7 +741,7 @@ def abort(self): self._active_tasks.value = 0 -class ActiveTaskCounterPosix(): +class ActiveTaskCounterPosix: r""" Handling the number of active tasks. @@ -884,7 +884,7 @@ def abort(self): # ActiveTaskCounter = ActiveTaskCounterDarwin # to debug Darwin implementation -class RESetMapReduce(): +class RESetMapReduce: r""" Map-Reduce on recursively enumerated sets. @@ -1133,27 +1133,16 @@ def start_workers(self): TESTS:: + sage: # long time sage: from sage.parallel.map_reduce import RESetMapReduce sage: def children(x): ....: print(f"Starting: {x}", flush=True) - ....: sleep(float(0.5)) - ....: print(f"Finished: {x}", flush=True) ....: return [] sage: S = RESetMapReduce(roots=[1, 2], children=children) sage: S.setup_workers(2) - sage: S.start_workers(); sleep(float(0.4)) + sage: S.start_workers(); sleep(float(5)) Starting: ... Starting: ... - sage: [w.is_alive() for w in S._workers] - [True, True] - sage: sleep(float(1.5)) - Finished: ... - Finished: ... - sage: [not w.is_alive() for w in S._workers] - [True, True] - - Cleanup:: - sage: S.finish() """ if self._nprocess == 0: diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index bc5293e0248..36269127b80 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -20,7 +20,7 @@ from sage.misc.timing import walltime -class WorkerData(): +class WorkerData: """ Simple class which stores data about a running ``p_iter_fork`` worker. @@ -55,7 +55,7 @@ def __init__(self, input_value, starttime=None, failure=""): self.failure = failure -class p_iter_fork(): +class p_iter_fork: """ A parallel iterator implemented using ``fork()``. diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 896249d8cd1..16672405c8e 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -552,7 +552,7 @@ def graphics_array(self, ncols=3): nrows, rem = divmod(n,ncols) if rem > 0: nrows += 1 - return plot.graphics_array(frame_list, nrows, ncols) + return plot.graphics_array(frame_list, nrows, ncols) def gif(self, delay=20, savefile=None, iterations=0, show_path=False, use_ffmpeg=False): diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index 6f45b65fc90..77f09c4a485 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -150,7 +150,7 @@ def get_minmax_data(self): if angle < 0: angle += twopi - epsilon = float(0.0000001) + epsilon = 0.0000001 cos_angle = cos(angle) sin_angle = sin(angle) diff --git a/src/sage/plot/complex_plot.pyx b/src/sage/plot/complex_plot.pyx index 124209f257e..0552a51ff19 100644 --- a/src/sage/plot/complex_plot.pyx +++ b/src/sage/plot/complex_plot.pyx @@ -826,7 +826,7 @@ class ComplexPlot(GraphicPrimitive): sage: isinstance(complex_plot(lambda z: z, (-1,1), (-1,1))[0]._repr_(), str) True """ - return "ComplexPlot defined by a %s x %s data grid"%(self.x_count, self.y_count) + return "ComplexPlot defined by a %s x %s data grid" % (self.x_count, self.y_count) def _render_on_subplot(self, subplot): """ diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 6b526b1c7c4..26874790ec5 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -949,7 +949,7 @@ def f(x,y): return cos(x) + sin(y) # ...make it actually the const_z0 function. xy_data_array.fill(z0) - # We're going to set fill=True in a momemt, so we need to + # We're going to set fill=True in a moment, so we need to # prepend an entry to the cmap so that the user's original # cmap winds up in the right place. if "cmap" in options: diff --git a/src/sage/plot/meson.build b/src/sage/plot/meson.build new file mode 100644 index 00000000000..96a337faf78 --- /dev/null +++ b/src/sage/plot/meson.build @@ -0,0 +1,48 @@ +py.install_sources( + 'all.py', + 'animate.py', + 'arc.py', + 'arrow.py', + 'bar_chart.py', + 'bezier_path.py', + 'circle.py', + 'colors.py', + 'contour_plot.py', + 'density_plot.py', + 'disk.py', + 'ellipse.py', + 'graphics.py', + 'histogram.py', + 'hyperbolic_arc.py', + 'hyperbolic_polygon.py', + 'hyperbolic_regular_polygon.py', + 'line.py', + 'matrix_plot.py', + 'misc.py', + 'multigraphics.py', + 'plot.py', + 'plot_field.py', + 'point.py', + 'polygon.py', + 'primitive.py', + 'scatter_plot.py', + 'step.py', + 'streamline_plot.py', + 'text.py', + subdir: 'sage/plot', +) + +extension_data = {'complex_plot' : files('complex_plot.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/plot', + install: true, + include_directories: [inc_cpython, inc_gsl, inc_numpy, inc_rings], + dependencies: [py_dep, cysignals, gmp, gsl], + ) +endforeach + +subdir('plot3d') diff --git a/src/sage/plot/multigraphics.py b/src/sage/plot/multigraphics.py index d2f70b44526..6ed26974c49 100644 --- a/src/sage/plot/multigraphics.py +++ b/src/sage/plot/multigraphics.py @@ -735,7 +735,7 @@ def _add_subplot(self, figure, index, **options): INPUT: - ``figure`` -- a Matplotlib ``Figure`` object - - ``index`` -- integer specifiying the element of ``self`` + - ``index`` -- integer specifying the element of ``self`` - ``options`` -- extra options to be passed to ``Figure.add_axes`` OUTPUT: a Matplotlib ``Axes`` object @@ -783,7 +783,7 @@ def position(self, index): INPUT: - - ``index`` -- integer specifiying which element of ``self`` + - ``index`` -- integer specifying which element of ``self`` OUTPUT: @@ -1167,7 +1167,7 @@ def _add_subplot(self, figure, index, **options): INPUT: - ``figure`` -- a Matplotlib ``Figure`` object - - ``index`` -- integer specifiying the element of ``self`` + - ``index`` -- integer specifying the element of ``self`` - ``options`` -- extra options to be passed to ``Figure.add_subplot`` OUTPUT: a Matplotlib ``Axes`` object @@ -1254,7 +1254,7 @@ def position(self, index): INPUT: - - ``index`` -- integer specifiying which element of ``self`` + - ``index`` -- integer specifying which element of ``self`` OUTPUT: diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 9075d31d571..f1a9ac7c49b 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -975,7 +975,7 @@ cdef class Graphics3d(SageObject): %s -"""%(self.viewpoint().x3d_str(), self.x3d_str()) +""" % (self.viewpoint().x3d_str(), self.x3d_str()) ################ TACHYON ################ @@ -1001,7 +1001,7 @@ cdef class Graphics3d(SageObject): # Instead, the tachyon aspectratio is set to match nonsquare # drawing area in "figsize". - # Parameters are mostly taken from tachyion.py, + # Parameters are mostly taken from tachyon.py, # but camera_center is renamed camera_position. # Apparently reST strips () from default parameters in the automatic documentation. # Thus, I replaced () by [] as default values. @@ -1249,7 +1249,7 @@ end_scene""".format( f.write('set labelOffset 0 0\n') # Set the scene background color - f.write('background [%s,%s,%s]\n'%tuple([int(a*255) for a in background])) + f.write('background [%s,%s,%s]\n' % tuple(int(a*255) for a in background)) if spin: f.write('spin ON\n') else: @@ -2619,11 +2619,11 @@ class TransformGroup(Graphics3dGroup): """ s = "" """ - return ""%self.pos + return "" % self.pos cdef class PrimitiveObject(Graphics3d): diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index 565b8c41387..8bfa47ab5c6 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -35,7 +35,7 @@ def list_plot3d(v, interpolation_type='default', point_list=None, **kwds): Clough-Tocher scheme. The interpolant is guaranteed to be continuously differentiable. The gradients of the interpolant are chosen so that the curvature of the interpolating surface is - approximatively minimized. + approximately minimized. The option 'spline' interpolates using a bivariate B-spline. diff --git a/src/sage/plot/plot3d/meson.build b/src/sage/plot/plot3d/meson.build new file mode 100644 index 00000000000..46cc4a25ffc --- /dev/null +++ b/src/sage/plot/plot3d/meson.build @@ -0,0 +1,45 @@ +py.install_sources( + 'all.py', + 'base.pxd', + 'implicit_plot3d.py', + 'index_face_set.pxd', + 'introduction.py', + 'list_plot3d.py', + 'parametric_plot3d.py', + 'parametric_surface.pxd', + 'platonic.py', + 'plot3d.py', + 'plot_field3d.py', + 'revolution_plot3d.py', + 'shapes.pxd', + 'shapes2.py', + 'tachyon.py', + 'texture.py', + 'transform.pxd', + 'tri_plot.py', + subdir: 'sage/plot/plot3d', +) + +extension_data = { + 'base' : files('base.pyx'), + 'implicit_surface' : files('implicit_surface.pyx'), + 'index_face_set' : files('index_face_set.pyx'), + 'shapes' : files('shapes.pyx'), + 'transform' : files('transform.pyx'), + 'parametric_surface' : files('parametric_surface.pyx'), +} + +foreach name, pyx : extension_data + dependencies = [py_dep, cysignals, gmp] + if name == 'parametric_surface' + dependencies += [interpreters_dep] + endif + py.extension_module( + name, + sources: pyx, + subdir: 'sage/plot/plot3d', + install: true, + include_directories: [inc_cpython, inc_ext, inc_numpy], + dependencies: dependencies, + ) +endforeach diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 387a5dc04da..ac3dbf15316 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -319,7 +319,9 @@ def to_cartesian(self, func, params=None): from sage.structure.element import Expression from sage.rings.real_mpfr import RealNumber from sage.rings.integer import Integer - if params is not None and (isinstance(func, Expression) or isinstance(func, RealNumber) or isinstance(func, Integer)): + if params is not None and isinstance(func, (Expression, + RealNumber, + Integer)): return self.transform(**{ self.dep_var: func, self.indep_vars[0]: params[0], @@ -1168,8 +1170,7 @@ def plot3d_adaptive(f, x_range, y_range, color='automatic', x, y = var('x,y') sphinx_plot(plot3d_adaptive(sin(x*y), (x,-pi,pi), (y,-pi,pi), initial_depth=5)) """ - if initial_depth >= max_depth: - max_depth = initial_depth + max_depth = max(max_depth, initial_depth) from sage.plot.misc import setup_for_eval_on_grid g, ranges = setup_for_eval_on_grid(f, [x_range,y_range], plot_points=2) diff --git a/src/sage/plot/plot3d/point_c.pxi b/src/sage/plot/plot3d/point_c.pxi index ba9561ab18c..f282c3e9f63 100644 --- a/src/sage/plot/plot3d/point_c.pxi +++ b/src/sage/plot/plot3d/point_c.pxi @@ -110,7 +110,7 @@ cdef inline void point_c_middle(point_c* res, point_c P, point_c Q, double a) no cdef inline void point_c_transform(point_c* res, double* M, point_c P) noexcept: """ - M is a flattened 4x4 matrix, row major, representing an Euclidean Transformation. + M is a flattened 4x4 matrix, row major, representing a Euclidean Transformation. Operate on P as a point. """ res.x = M[0]*P.x + M[1]*P.y + M[2]*P.z + M[3] @@ -119,7 +119,7 @@ cdef inline void point_c_transform(point_c* res, double* M, point_c P) noexcept: cdef inline void point_c_stretch(point_c* res, double* M, point_c P) noexcept: """ - M is a flattened 4x4 matrix, row major, representing an Euclidean Transformation. + M is a flattened 4x4 matrix, row major, representing a Euclidean Transformation. Operate on P as a vector (i.e. ignore the translation component) """ res.x = M[0]*P.x + M[1]*P.y + M[2]*P.z diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx index 6dcf6564714..4acab1a14eb 100644 --- a/src/sage/plot/plot3d/shapes.pyx +++ b/src/sage/plot/plot3d/shapes.pyx @@ -483,7 +483,7 @@ cdef class Cylinder(ParametricSurface): Base %s %s %s Apex %s %s %s Rad %s - %s """%(base[0], base[1], base[2], top[0], top[1], top[2], rad, self.texture.id) + %s """ % (base[0], base[1], base[2], top[0], top[1], top[2], rad, self.texture.id) if self.closed: normal = (0,0,1) if transform is not None: @@ -866,7 +866,7 @@ cdef class Sphere(ParametricSurface): sage: Sphere(12).x3d_geometry() "" """ - return ""%(self.radius) + return "" % (self.radius) def tachyon_repr(self, render_params): r""" @@ -1111,7 +1111,7 @@ class Text(PrimitiveObject): sage: Text("Hi").x3d_geometry() "" """ - return ""%self.string + return "" % self.string def obj_repr(self, render_params): """ @@ -1162,14 +1162,14 @@ class Text(PrimitiveObject): [[['select atomno = 1', 'color atom [102,102,255]', 'label "Hi"']], [['select atomno = 2', 'color atom [102,102,255]', 'label "Bye"']]] """ - cen = (0,0,0) + cen = (0, 0, 0) if render_params.transform is not None: cen = render_params.transform.transform_point(cen) render_params.atom_list.append(cen) atom_no = len(render_params.atom_list) return ['select atomno = %s' % atom_no, self.get_texture().jmol_str("atom"), - 'label "%s"' % self.string] #.replace('\n', '|')] + 'label "%s"' % self.string] # .replace('\n', '|')] def threejs_repr(self, render_params): r""" diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index 1f5f8f4739a..05d6bb6e583 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -437,8 +437,10 @@ def frame3d(lower_left, upper_right, **kwds): """ x0, y0, z0 = lower_left x1, y1, z1 = upper_right - L1 = line3d([(x0, y0, z0), (x0, y1, z0), (x1, y1, z0), (x1, y0, z0), (x0, y0, z0), # top square - (x0, y0, z1), (x0, y1, z1), (x1, y1, z1), (x1, y0, z1), (x0, y0, z1)], # bottom square + L1 = line3d([(x0, y0, z0), (x0, y1, z0), (x1, y1, z0), + (x1, y0, z0), (x0, y0, z0), # top square + (x0, y0, z1), (x0, y1, z1), (x1, y1, z1), + (x1, y0, z1), (x0, y0, z1)], # bottom square **kwds) # 3 additional lines joining top to bottom v2 = line3d([(x0, y1, z0), (x0, y1, z1)], **kwds) diff --git a/src/sage/plot/streamline_plot.py b/src/sage/plot/streamline_plot.py index b69a05b37bf..2801446433a 100644 --- a/src/sage/plot/streamline_plot.py +++ b/src/sage/plot/streamline_plot.py @@ -296,9 +296,8 @@ def streamline_plot(f_g, xrange, yrange, **options): else: options['density'] = float(options['density']) - xpos_array, ypos_array, xvec_array, yvec_array = [], [], [], [] - for x in xsrange(*ranges[0], include_endpoint=True): - xpos_array.append(x) + ypos_array, xvec_array, yvec_array = [], [], [] + xpos_array = list(xsrange(*ranges[0], include_endpoint=True)) for y in xsrange(*ranges[1], include_endpoint=True): ypos_array.append(y) xvec_row, yvec_row = [], [] diff --git a/src/sage/probability/meson.build b/src/sage/probability/meson.build new file mode 100644 index 00000000000..83b6a7e091e --- /dev/null +++ b/src/sage/probability/meson.build @@ -0,0 +1,17 @@ +py.install_sources('all.py', 'random_variable.py', subdir: 'sage/probability') + +extension_data = { + 'probability_distribution' : files('probability_distribution.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/probability', + install: true, + include_directories: [], + dependencies: [py_dep, cysignals, gmp, gsl], + ) +endforeach + diff --git a/src/sage/quadratic_forms/count_local_2.pyx b/src/sage/quadratic_forms/count_local_2.pyx index 55c64e56be7..2cf39e037a3 100644 --- a/src/sage/quadratic_forms/count_local_2.pyx +++ b/src/sage/quadratic_forms/count_local_2.pyx @@ -72,14 +72,14 @@ def count_modp__by_gauss_sum(n, p, m, Qdet): neg1 = -1 if not m % p: if n % 2: - count = p**(n-1) + count = p**(n - 1) else: - count = p**(n-1) + (p-1) * (p**((n-2)//2)) * kronecker_symbol(((neg1**(n//2)) * Qdet) % p, p) + count = p**(n - 1) + (p - 1) * (p**((n - 2) // 2)) * kronecker_symbol(((neg1**(n // 2)) * Qdet) % p, p) else: if n % 2: - count = p**(n-1) + p**((n-1)//2) * kronecker_symbol(((neg1**((n-1)//2)) * Qdet * m) % p, p) + count = p**(n - 1) + p**((n - 1) // 2) * kronecker_symbol(((neg1**((n - 1) // 2)) * Qdet * m) % p, p) else: - count = p**(n-1) - p**((n-2)//2) * kronecker_symbol(((neg1**(n//2)) * Qdet) % p, p) + count = p**(n - 1) - p**((n - 2) // 2) * kronecker_symbol(((neg1**(n // 2)) * Qdet) % p, p) # Return the result return count @@ -115,13 +115,13 @@ cdef CountAllLocalTypesNaive_cdef(Q, p, k, m, zvec, nzvec): # Perform a carry (when value = R-1) until we can increment freely ptr = len(v) - while ((ptr > 0) and (v[ptr-1] == R-1)): - v[ptr-1] += 1 + while ((ptr > 0) and (v[ptr - 1] == R - 1)): + v[ptr - 1] += 1 ptr += -1 # Only increment if we're not already at the zero vector =) if ptr > 0: - v[ptr-1] += 1 + v[ptr - 1] += 1 # Evaluate Q(v) quickly tmp_val = Mod(0, R) @@ -239,7 +239,7 @@ cdef local_solution_type_cdef(Q, p, w, zvec, nzvec): return 1 if p == 2: for i in range(n - 1): - if Q[i, i+1] % p and (w[i] % p or w[i+1] % p): + if Q[i, i + 1] % p and (w[i] % p or w[i + 1] % p): return 1 # 2: Check Zero-type @@ -257,11 +257,11 @@ cdef local_solution_type_cdef(Q, p, w, zvec, nzvec): # Compute the valuation of each index, allowing for off-diagonal terms if Q[i, i] == 0: if i == 0: - val = valuation(Q[i, i+1], p) # Look at the term to the right + val = valuation(Q[i, i + 1], p) # Look at the term to the right elif i == n - 1: - val = valuation(Q[i-1, i], p) # Look at the term above + val = valuation(Q[i - 1, i], p) # Look at the term above else: - val = valuation(Q[i, i+1] + Q[i-1, i], p) # Finds the valuation of the off-diagonal term since only one isn't zero + val = valuation(Q[i, i + 1] + Q[i - 1, i], p) # Finds the valuation of the off-diagonal term since only one isn't zero else: val = valuation(Q[i, i], p) @@ -281,3 +281,83 @@ cdef local_solution_type_cdef(Q, p, w, zvec, nzvec): print(" Solution vector is " + str(w)) print(" and Q is \n" + str(Q) + "\n") raise RuntimeError("Error in IsLocalSolutionType: Should not execute this line... =( \n") + + +def count_all_local_good_types_normal_form(Q, p, k, m, zvec, nzvec): + r""" + This is an internal routine, which is called by + :meth:`sage.quadratic_forms.quadratic_form.QuadraticForm.local_good_density_congruence_even + QuadraticForm.local_good_density_congruence_even`. See the documentation of + that method for more details. + + INPUT: + + - ``Q`` -- quadratic form over `\ZZ` in local normal form at p with no zero blocks mod `p^k` + - ``p`` -- prime number > 0 + - ``k`` -- integer > 0 + - ``m`` -- non-negative integer (depending only on mod `p^k`) + - ``zvec``, ``nzvec`` -- list of integers in ``range(Q.dim())``, or ``None`` + + OUTPUT: + + a non-negative integer giving the number of solutions of Good type. + + EXAMPLES:: + + sage: from sage.quadratic_forms.count_local_2 import count_all_local_good_types_normal_form + sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3]) + sage: Q_local_at2 = Q.local_normal_form(2) + sage: Q_local_at3 = Q.local_normal_form(3) + sage: count_all_local_good_types_normal_form(Q_local_at2, 2, 3, 3, None, None) + 64 + sage: count_all_local_good_types_normal_form(Q_local_at2, 2, 3, 3, [0], None) + 32 + sage: count_all_local_good_types_normal_form(Q_local_at3, 3, 2, 1, None, None) + 54 + """ + n = Q.dim() + if n == 0: + return 0 + + m_range = p**k + if zvec is None: + zvec = [] + if nzvec is None: + nzvec = [] + + # determine local blocks + blocks = [] + i = 0 + while i < n - 1: + if Q[i, i + 1] != 0: + blocks += [(i, i + 1)] + i += 2 + else: + blocks += [(i,)] + i += 1 + if i < n: + blocks += [(i,)] + + solutions = [[0, 0] for _ in range(m_range)] # [good, not good] + solutions[0][1] = 1 + for b in blocks: + Q_part = Q.extract_variables(b) + zvec_local = range(len(b)) if (b[0] in zvec) else None + nzvec_local = range(len(b)) if (b[0] in nzvec) else None + + solutions_part = [[0, 0] for _ in range(m_range)] + for m_part in range(m_range): + cnt = CountAllLocalTypesNaive(Q_part, p, k, m_part, zvec_local, nzvec_local) + solutions_part[m_part][0] = cnt[1] + solutions_part[m_part][1] = cnt[0] - cnt[1] + + # compute convolution of counts + solutions_new = [[0, 0] for _ in range(m_range)] + for m1 in range(m_range): + for m2 in range(m_range): + total = (solutions[m1][0] + solutions[m1][1]) * (solutions_part[m2][0] + solutions_part[m2][1]) + good = total - solutions[m1][1] * solutions_part[m2][1] + solutions_new[(m1 + m2) % m_range][0] += good + solutions_new[(m1 + m2) % m_range][1] += total - good + solutions = solutions_new + return solutions[m % m_range][0] diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 8868999c367..0227fa8319a 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -18,7 +18,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** - +from pathlib import Path from copy import copy, deepcopy from sage.misc.lazy_import import lazy_import @@ -509,12 +509,12 @@ def is_2_adic_genus(genus_symbol_quintuple_list) -> bool: return False if s[1] == 2 and s[3] == 1: if s[2] % 8 in (1, 7): - if not s[4] in (0, 2, 6): + if s[4] not in (0, 2, 6): return False if s[2] % 8 in (3, 5): - if not s[4] in (2, 4, 6): + if s[4] not in (2, 4, 6): return False - if (s[1] - s[4]) % 2 == 1: + if (s[1] - s[4]) % 2: return False if s[3] == 0 and s[4] != 0: return False @@ -2802,7 +2802,8 @@ def direct_sum(self, other): signature_pair = (p1 + p2, n1 + n2) primes = [s.prime() for s in self.local_symbols()] - primes += [s.prime() for s in other.local_symbols() if not s.prime() in primes] + primes.extend(s.prime() for s in other.local_symbols() + if s.prime() not in primes) primes.sort() local_symbols = [] for p in primes: @@ -2930,13 +2931,11 @@ def _compute_representative(self, LLL=True): sig = self.signature_pair_of_matrix() if sig[0] * sig[1] != 0: from sage.env import SAGE_EXTCODE - from sage.interfaces.gp import gp - m = pari(L) - gp.read(SAGE_EXTCODE + "/pari/simon/qfsolve.gp") - m = gp.eval('qflllgram_indefgoon(%s)' % m) + pari.read(Path(SAGE_EXTCODE) / "pari" / "simon" / "qfsolve.gp") + m = pari('qflllgram_indefgoon')(m) # convert the output string to sage - L = pari(m).sage()[0] + L = m.sage()[0] elif sig[1] != 0: U = -(-L).LLL_gram() L = U.T * L * U diff --git a/src/sage/quadratic_forms/meson.build b/src/sage/quadratic_forms/meson.build new file mode 100644 index 00000000000..0e352ed72be --- /dev/null +++ b/src/sage/quadratic_forms/meson.build @@ -0,0 +1,51 @@ +py.install_sources( + 'all.py', + 'binary_qf.py', + 'bqf_class_group.py', + 'constructions.py', + 'extras.py', + 'qfsolve.py', + 'quadratic_form.py', + 'quadratic_form__automorphisms.py', + 'quadratic_form__count_local_2.py', + 'quadratic_form__equivalence_testing.py', + 'quadratic_form__genus.py', + 'quadratic_form__local_density_congruence.py', + 'quadratic_form__local_density_interfaces.py', + 'quadratic_form__local_field_invariants.py', + 'quadratic_form__local_normal_form.py', + 'quadratic_form__local_representation_conditions.py', + 'quadratic_form__mass.py', + 'quadratic_form__mass__Conway_Sloane_masses.py', + 'quadratic_form__mass__Siegel_densities.py', + 'quadratic_form__neighbors.py', + 'quadratic_form__reduction_theory.py', + 'quadratic_form__siegel_product.py', + 'quadratic_form__split_local_covering.py', + 'quadratic_form__ternary_Tornaria.py', + 'quadratic_form__theta.py', + 'quadratic_form__variable_substitutions.py', + 'random_quadraticform.py', + 'special_values.py', + 'ternary_qf.py', + subdir: 'sage/quadratic_forms', +) + +extension_data = { + 'count_local_2' : files('count_local_2.pyx'), + 'quadratic_form__evaluate' : files('quadratic_form__evaluate.pyx'), + 'ternary' : files('ternary.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/quadratic_forms', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + +install_subdir('genera', install_dir: sage_install_dir / 'quadratic_forms') diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 88df6dfcb9c..e303b3292c3 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -1051,9 +1051,9 @@ def __call__(self, v): Q2 = QuadraticForm(self.base_ring(), m) return QFEvaluateMatrix(self, v, Q2) - elif (isinstance(v, Vector) or isinstance(v, (list, tuple))): + elif isinstance(v, (Vector, list, tuple)): # Check the vector/tuple/list has the correct length - if not (len(v) == n): + if len(v) != n: raise TypeError(f"your vector needs to have length {n}") # TO DO: Check that the elements can be coerced into the base ring of Q -- on first elt. diff --git a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py index 6e47726f08f..f688a08a546 100644 --- a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py +++ b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py @@ -281,8 +281,7 @@ def has_equivalent_Jordan_decomposition_at_prime(self, other, p): # Condition (i): Check that their (unit) ratio is a square (but it suffices to check at most mod 8). modulus = norm_list[i] * norm_list[i+1] / (scale_list[i] ** 2) - if modulus > 8: - modulus = 8 + modulus = min(modulus, 8) if (modulus > 1) and (((self_chain_det_list[i] / other_chain_det_list[i]) % modulus) != 1): return False diff --git a/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py b/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py index a959be64455..dc9b30aa763 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py +++ b/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py @@ -13,7 +13,7 @@ from sage.arith.misc import valuation from sage.misc.verbose import verbose -from sage.quadratic_forms.count_local_2 import count_modp__by_gauss_sum +from sage.quadratic_forms.count_local_2 import count_modp__by_gauss_sum, count_all_local_good_types_normal_form def count_modp_solutions__by_Gauss_sum(self, p, m): @@ -210,17 +210,12 @@ def local_good_density_congruence_even(self, m, Zvec, NZvec): [ * * * 20 ] sage: Q.theta_series(20) # needs sage.libs.pari 1 + 2*q^5 + 2*q^10 + 2*q^14 + 2*q^15 + 2*q^16 + 2*q^18 + O(q^20) - sage: Q.local_normal_form(2) # needs sage.libs.pari sage.rings.padics - Quadratic form in 4 variables over Integer Ring with coefficients: - [ 0 1 0 0 ] - [ * 0 0 0 ] - [ * * 0 1 ] - [ * * * 0 ] - sage: Q.local_good_density_congruence_even(1, None, None) + sage: Q_local = Q.local_normal_form(2) # needs sage.libs.pari sage.rings.padics + sage: Q_local.local_good_density_congruence_even(1, None, None) # needs sage.libs.pari sage.rings.padics 3/4 - sage: Q.local_good_density_congruence_even(2, None, None) - 1 - sage: Q.local_good_density_congruence_even(5, None, None) + sage: Q_local.local_good_density_congruence_even(2, None, None) # needs sage.libs.pari sage.rings.padics + 9/8 + sage: Q_local.local_good_density_congruence_even(5, None, None) # needs sage.libs.pari sage.rings.padics 3/4 """ n = self.dim() @@ -296,7 +291,7 @@ def local_good_density_congruence_even(self, m, Zvec, NZvec): # Take cases on the existence of additional nonzero congruence conditions (mod 2) if NZvec is None: total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \ - * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None) + * count_all_local_good_types_normal_form(Q_Not8,2, 3, m, list(Z_Not8), None) else: ZNZ = Z + Set(NZvec) ZNZ_Not8 = Not8.intersection(ZNZ) @@ -310,9 +305,9 @@ def local_good_density_congruence_even(self, m, Zvec, NZvec): verbose("Is8_minus_ZNZ = " + str(Is8_minus_ZNZ)) total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \ - * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None) \ + * count_all_local_good_types_normal_form(Q_Not8, 2, 3, m, list(Z_Not8), None) \ - (4 ** len(ZNZ_Is8)) * (8 ** len(Is8_minus_ZNZ)) \ - * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(ZNZ_Not8), None) + * count_all_local_good_types_normal_form(Q_Not8, 2, 3, m, list(ZNZ_Not8), None) # DIAGNOSTIC verbose("total = " + str(total)) 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 fb869fc7551..aa2478af088 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py +++ b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py @@ -17,7 +17,7 @@ # **************************************************************************** ########################################################################### -# TO DO: Add routines for hasse invariants at all places, anisotropic +# TO DO: Add routines for Hasse invariants at all places, anisotropic # places, is_semi_definite, and support for number fields. ########################################################################### @@ -165,7 +165,19 @@ def rational_diagonal_form(self, return_matrix=False): sage: T[0,0] = 13 Traceback (most recent call last): ... - ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + ValueError: matrix is immutable; please change a copy instead + (i.e., use copy(M) to change a copy of M). + + Test for a singular form:: + + sage: m = matrix(GF(11), [[1,5,0,0], [5,1,9,0], [0,9,1,5], [0,0,5,1]]) + sage: qf = QuadraticForm(m) + sage: Q, T = qf.rational_diagonal_form(return_matrix=True) + sage: T + [ 1 6 5 10] + [ 0 1 10 9] + [ 0 0 1 2] + [ 0 0 0 1] """ Q, T = self._rational_diagonal_form_and_transformation() T.set_immutable() @@ -173,19 +185,17 @@ def rational_diagonal_form(self, return_matrix=False): # Quadratic forms do not support immutability, so we need to make # a copy to be safe. Q = deepcopy(Q) - - if return_matrix: - return Q, T - else: - return Q + return (Q, T) if return_matrix else Q @cached_method def _rational_diagonal_form_and_transformation(self): """ Return a diagonal form equivalent to the given quadratic from and - the corresponding transformation matrix. This is over the fraction - field of the base ring of the given quadratic form. + the corresponding transformation matrix. + + This is over the fraction field of the base ring of the given + quadratic form. OUTPUT: a tuple `(D,T)` where @@ -238,13 +248,13 @@ def _rational_diagonal_form_and_transformation(self): D = MS() for i in range(n): D[i, i] = R[i, i] - Q = Q.parent()(D) + newQ = Q.parent()(D) # Transformation matrix (inverted) T = MS(R.sage()) for i in range(n): T[i, i] = K.one() try: - return Q, ~T + return newQ, ~T except ZeroDivisionError: # Singular case is not fully supported by PARI pass @@ -276,7 +286,8 @@ def _rational_diagonal_form_and_transformation(self): 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. + 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 diff --git a/src/sage/quadratic_forms/quadratic_form__theta.py b/src/sage/quadratic_forms/quadratic_form__theta.py index 2929751bea8..12cffc8fbc1 100644 --- a/src/sage/quadratic_forms/quadratic_form__theta.py +++ b/src/sage/quadratic_forms/quadratic_form__theta.py @@ -82,7 +82,7 @@ def theta_by_pari(self, Max, var_str='q', safe_flag=True): output, or the original output. It is only meaningful when a vector is returned, otherwise a copy is automatically made in creating the power series. By default ``safe_flag=True``, so we - return a copy of the cached information. If this is set to False, + return a copy of the cached information. If this is set to ``False``, then the routine is much faster but the return values are vulnerable to being corrupted by the user. @@ -112,7 +112,7 @@ def theta_by_pari(self, Max, var_str='q', safe_flag=True): self.__theta_vec = theta_vec # Return the answer - if var_str == '': + if not var_str: if safe_flag: return deepcopy(theta_vec) # We must make a copy here to insure the integrity of the cached version! else: @@ -121,16 +121,16 @@ def theta_by_pari(self, Max, var_str='q', safe_flag=True): return PowerSeriesRing(ZZ, var_str)(theta_vec, Max) -# ------------- Compute the theta function by using an explicit Cholesky decomposition ------------ +# -- Compute the theta function by using an explicit Cholesky decomposition -- -########################################################################## -# Routines to compute the Fourier expansion of the theta function of Q ## -# (to a given precision) via a Cholesky decomposition over RR. ## -# ## -# The Cholesky code was taken from: ## -# ~/Documents/290_Project/C/Ver13.2__3-5-2007/Matrix_mpz/Matrix_mpz.cc ## -########################################################################## +######################################################################## +# Routines to compute the Fourier expansion of the theta function of Q # +# (to a given precision) via a Cholesky decomposition over RR. # +# # +# The Cholesky code was taken from: # +# ~/Documents/290_Project/C/Ver13.2__3-5-2007/Matrix_mpz/Matrix_mpz.cc # +######################################################################## def theta_by_cholesky(self, q_prec): @@ -144,24 +144,24 @@ def theta_by_cholesky(self, q_prec): Cohen's "A Course in Computational Algebraic Number Theory" book, p 102. - EXAMPLES:: + EXAMPLES: + + Check the sum of 4 squares form against Jacobi's formula:: - # Check the sum of 4 squares form against Jacobi's formula sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Theta = Q.theta_by_cholesky(10) sage: Theta 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 - sage: Expected = [1] + [8*sum([d for d in divisors(n) if d%4 != 0]) + sage: Expected = [1] + [8*sum(d for d in divisors(n) if d%4) ....: for n in range(1, 11)] sage: Expected [1, 8, 24, 32, 24, 48, 96, 64, 24, 104, 144] sage: Theta.list() == Expected True - :: + Check the form `x^2 + 3y^2 + 5z^2 + 7w^2` represents everything except 2 and 22.:: - # Check the form x^2 + 3y^2 + 5z^2 + 7w^2 represents everything except 2 and 22. sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Theta = Q.theta_by_cholesky(50) sage: Theta_list = Theta.list() @@ -176,10 +176,10 @@ def theta_by_cholesky(self, q_prec): from sage.rings.real_mpfr import RealField n = self.dim() - theta = [0 for i in range(q_prec + 1)] + theta = [0] * (q_prec + 1) PS = PowerSeriesRing(ZZ, 'q') - bit_prec = 53 # TO DO: Set this precision to reflect the appropriate roundoff + bit_prec = 53 # TO DO: Set this precision to reflect the appropriate roundoff Cholesky = self.cholesky_decomposition(bit_prec) # error estimate, to be confident through our desired q-precision. Q = Cholesky # <---- REDUNDANT!!! R = RealField(bit_prec) @@ -187,8 +187,8 @@ def theta_by_cholesky(self, q_prec): # 1. Initialize i = n - 1 - T = [R(0) for j in range(n)] - U = [R(0) for j in range(n)] + T = [R.zero()] * n + U = [R.zero()] * n T[i] = R(q_prec) U[i] = 0 L = [0] * n @@ -251,10 +251,7 @@ def theta_by_cholesky(self, q_prec): theta[Q_val] += 2 # 5. Check if x = 0, for exit condition. =) - done_flag = True - for j in range(n): - if x[j] != 0: - done_flag = False + done_flag = all(x[j] == 0 for j in range(n)) # Set the value: theta[0] = 1 theta[0] = 1 @@ -263,7 +260,7 @@ def theta_by_cholesky(self, q_prec): return PS(theta) -def theta_series_degree_2(Q, prec): +def theta_series_degree_2(Q, prec) -> dict: r""" Compute the theta series of degree 2 for the quadratic form `Q`. @@ -322,14 +319,14 @@ def theta_series_degree_2(Q, prec): H = Q.Hessian_matrix() t = cputime() - max = (X + 1) // 4 - v_list = (Q.vectors_by_length(max)) # assume a>0 - v_list = [[V(_) for _ in vs] for vs in v_list] # coerce vectors into V + maxi = (X + 1) // 4 + v_list = (Q.vectors_by_length(maxi)) # assume a>0 + v_list = [[V(c) for c in vs] for vs in v_list] # coerce vectors into V verbose("Computed vectors_by_length", t) # Deal with the singular part coeffs = {(0, 0, 0): ZZ.one()} - for i in range(1, max + 1): + for i in range(1, maxi + 1): coeffs[(0, 0, i)] = ZZ(2) * len(v_list[i]) # Now deal with the non-singular part diff --git a/src/sage/quadratic_forms/ternary_qf.py b/src/sage/quadratic_forms/ternary_qf.py index 79436ff4f77..dbc73a90f34 100644 --- a/src/sage/quadratic_forms/ternary_qf.py +++ b/src/sage/quadratic_forms/ternary_qf.py @@ -234,7 +234,7 @@ def __call__(self, v): M[1, 2], M[0, 2], M[0, 1]]) else: return QuadraticForm(ZZ, v.transpose() * self.matrix() * v) - elif (isinstance(v, Vector) or isinstance(v, (list, tuple))): + elif isinstance(v, (Vector, list, tuple)): # Check that v has length 3 if len(v) != 3: raise TypeError("your vector needs to have length 3") diff --git a/src/sage/quivers/meson.build b/src/sage/quivers/meson.build new file mode 100644 index 00000000000..cdefdce952b --- /dev/null +++ b/src/sage/quivers/meson.build @@ -0,0 +1,29 @@ +py.install_sources( + 'algebra.py', + 'algebra_elements.pxd', + 'all.py', + 'ar_quiver.py', + 'homspace.py', + 'morphism.py', + 'path_semigroup.py', + 'paths.pxd', + 'representation.py', + subdir: 'sage/quivers', +) + +extension_data = { + 'algebra_elements' : files('algebra_elements.pyx'), + 'paths' : files('paths.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/quivers', + install: true, + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/quivers/path_semigroup.py b/src/sage/quivers/path_semigroup.py index cb141c02751..1b9fe359fba 100644 --- a/src/sage/quivers/path_semigroup.py +++ b/src/sage/quivers/path_semigroup.py @@ -362,7 +362,7 @@ def _element_constructor_(self, data, check=True): path = [] else: # a list of edges if any(len(x) != 3 for x in data): - x = next((x for x in data if len(x) != 3)) + x = next(x for x in data if len(x) != 3) raise ValueError("each edge must be a triple, got {}".format(x)) start = data[0][0] end = data[-1][1] diff --git a/src/sage/repl/configuration.py b/src/sage/repl/configuration.py index a87ee896307..68b2fbb9f8b 100644 --- a/src/sage/repl/configuration.py +++ b/src/sage/repl/configuration.py @@ -37,7 +37,7 @@ SAGE_EXTENSION = 'sage' -class SageIpythonConfiguration(): +class SageIpythonConfiguration: def _doctest_mode(self): """ diff --git a/src/sage/repl/display/fancy_repr.py b/src/sage/repl/display/fancy_repr.py index bb1f41ded56..044f8e3f6b2 100644 --- a/src/sage/repl/display/fancy_repr.py +++ b/src/sage/repl/display/fancy_repr.py @@ -24,7 +24,7 @@ _baseclass_reprs = (object.__repr__,) -class ObjectReprABC(): +class ObjectReprABC: """ The abstract base class of an object representer. diff --git a/src/sage/repl/display/util.py b/src/sage/repl/display/util.py index 450d1e0db62..5b931a67673 100644 --- a/src/sage/repl/display/util.py +++ b/src/sage/repl/display/util.py @@ -16,7 +16,7 @@ # **************************************************************************** -class TallListFormatter(): +class TallListFormatter: """ Special representation for lists with tall entries (e.g. matrices). diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py index 4f40e7c0715..06a6f6a671a 100644 --- a/src/sage/repl/image.py +++ b/src/sage/repl/image.py @@ -135,7 +135,7 @@ def pixels(self): """ return self._pil.load() - def _repr_(self): + def _repr_(self) -> str: """ Return string representation. @@ -148,17 +148,17 @@ def _repr_(self): 16x16px 24-bit RGB image """ modestr = { - '1': '{0}x{1}px BW image', - 'L': '{0}x{1}px 8-bit BW image', - 'P': '{0}x{1}px 8-bit Color image', - 'RGB': '{0}x{1}px 24-bit RGB image', - 'RGBA': '{0}x{1}px 32-bit RGBA image', - 'CMYK': '{0}x{1}px 24-bit CMYK image', + '1': '{0}x{1}px BW image', + 'L': '{0}x{1}px 8-bit BW image', + 'P': '{0}x{1}px 8-bit Color image', + 'RGB': '{0}x{1}px 24-bit RGB image', + 'RGBA': '{0}x{1}px 32-bit RGBA image', + 'CMYK': '{0}x{1}px 24-bit CMYK image', 'YCbCr': '{0}x{1}px 24-bit YCbCr mage', - 'LAB': '{0}x{1}px 24-bit LAB image', - 'HSV': '{0}x{1}px 24-bit HSV image', - 'I': '{0}x{1}px 32-bit signed integer image', - 'F': '{0}x{1}px 32-bit float image', + 'LAB': '{0}x{1}px 24-bit LAB image', + 'HSV': '{0}x{1}px 24-bit HSV image', + 'I': '{0}x{1}px 32-bit signed integer image', + 'F': '{0}x{1}px 32-bit float image', } try: mode = modestr[self.pil.mode] @@ -285,9 +285,9 @@ def _rich_repr_(self, display_manager, **kwds): return types = display_manager.types preferred = ( - ('PNG', types.OutputImagePng), + ('PNG', types.OutputImagePng), ('JPEG', types.OutputImageJpg), - ('GIF', types.OutputImageGif), + ('GIF', types.OutputImageGif), ) from sage.repl.rich_output.buffer import OutputBuffer for format, output_container in preferred: diff --git a/src/sage/repl/interface_magic.py b/src/sage/repl/interface_magic.py index a426dfdda07..d4c28dc49ac 100644 --- a/src/sage/repl/interface_magic.py +++ b/src/sage/repl/interface_magic.py @@ -80,7 +80,7 @@ """ -class InterfaceMagic(): +class InterfaceMagic: @classmethod def all_iter(cls): diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index 73b619e8101..3a269592917 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -75,13 +75,14 @@ sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() - sage: print("dummy line"); shell.run_cell('1/0') # see #25320 for the reason of the `...` and the dummy line in this test + sage: print("dummy line"); shell.run_cell('1/0') # known bug (meson doesn't include the Cython source code) # see #25320 for the reason of the `...` and the dummy line in this test dummy line ... ZeroDivisionError...Traceback (most recent call last) ... ----> 1 Integer(1)/Integer(0) - .../sage/rings/integer.pyx... in sage.rings.integer.Integer...div... + ... + ...integer.pyx... in sage.rings.integer.Integer...div... ... -> ... raise ZeroDivisionError("rational division by zero") ....: x = Rational.__new__(Rational) @@ -197,7 +198,7 @@ def preparser(on=True): ############################## # Sage[Terminal]InteractiveShell ############################## -class SageShellOverride(): +class SageShellOverride: """ Mixin to override methods in IPython's [Terminal]InteractiveShell classes. @@ -312,6 +313,7 @@ def prompt_for_code(self): pythonapi.PyOS_setsig(signal.SIGINT, sigint_os) return text + class SageTestShell(SageShellOverride, TerminalInteractiveShell): """ Test Shell. diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index b218330d7af..b6fc42bbb37 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -71,6 +71,7 @@ from sage.misc.lazy_import import LazyImport from sage.misc.misc import run_once + @magics_class class SageMagics(Magics): @@ -417,7 +418,7 @@ def fortran(self, line, cell): return fortran(cell) -class SageCustomizations(): +class SageCustomizations: def __init__(self, shell=None): """ @@ -484,7 +485,7 @@ def run_init(self): Run Sage's initial startup file. """ try: - with open(SAGE_STARTUP_FILE, 'r') as f: + with open(SAGE_STARTUP_FILE) as f: self.shell.run_cell(f.read(), store_history=False) except OSError: pass diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index 3a2efb4ab64..0b340e86238 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -26,7 +26,7 @@ ) -class SageKernelSpec(): +class SageKernelSpec: def __init__(self, prefix=None): """ diff --git a/src/sage/repl/ipython_kernel/widgets.py b/src/sage/repl/ipython_kernel/widgets.py index 3fa000f70df..42d674b7c9a 100644 --- a/src/sage/repl/ipython_kernel/widgets.py +++ b/src/sage/repl/ipython_kernel/widgets.py @@ -84,7 +84,7 @@ def description(self, value): pass -class TransformWidget(): +class TransformWidget: """ A mixin class for a widget to transform the bare widget value for use in interactive functions. @@ -383,7 +383,7 @@ class Grid(TransformWidget, HBox, ValueWidget): value = List() description = Unicode() - def __init__(self, nrows, ncols, make_widget, description=u"", transform=None): + def __init__(self, nrows, ncols, make_widget, description="", transform=None): """ Create a :class:`Grid` widget. diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index 473e860ba42..2d1be377cad 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -103,7 +103,7 @@ class RichReprWarning(UserWarning): pass -class restricted_output(): +class restricted_output: def __init__(self, display_manager, output_classes): """ @@ -634,10 +634,10 @@ def _rich_output_formatter(self, obj, rich_repr_kwds): rich_output = self._promote_output(rich_output) # check that the output container types are valid for the backend supported = self._backend.supported_output() - if not (type(plain_text) in supported): + if type(plain_text) not in supported: raise OutputTypeException( 'text output container not supported: {0}'.format(type(plain_text))) - if not (type(rich_output) in supported): + if type(rich_output) not in supported: raise OutputTypeException( 'output container not supported: {0}'.format(type(rich_output))) return plain_text, rich_output diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py index 68bbf92df9d..70257f7f4ed 100644 --- a/src/sage/repl/rich_output/output_basic.py +++ b/src/sage/repl/rich_output/output_basic.py @@ -264,9 +264,9 @@ def example(cls): ⎜ 3 -1 0⎟ ⎝ -1 -1 0⎠ """ - return cls(u'⎛-11 0 1⎞\n' - u'⎜ 3 -1 0⎟\n' - u'⎝ -1 -1 0⎠') + return cls('⎛-11 0 1⎞\n' + '⎜ 3 -1 0⎟\n' + '⎝ -1 -1 0⎠') def print_to_stdout(self): """ diff --git a/src/sage/repl/rich_output/output_browser.py b/src/sage/repl/rich_output/output_browser.py index 086a036ad02..77e404151e3 100644 --- a/src/sage/repl/rich_output/output_browser.py +++ b/src/sage/repl/rich_output/output_browser.py @@ -12,6 +12,7 @@ latex_re = re.compile(r'(?P\\\[|\\\()(?P.*)(?P\\\]|\\\))', flags=re.DOTALL) + class OutputHtml(OutputBase): def __init__(self, html): diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index 45a46e4a703..481b83c2415 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -59,18 +59,16 @@ - Clemens Heuberger (2016) - Benjamin Hackl (2016) - ACKNOWLEDGEMENT: - Benjamin Hackl, Clemens Heuberger and Daniel Krenn are supported by the Austrian Science Fund (FWF): P 24644-N26. - Classes and Methods =================== """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Daniel Krenn # Copyright (C) 2016 Clemens Heuberger # @@ -78,8 +76,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.superseded import experimental from sage.structure.sage_object import SageObject @@ -582,7 +580,7 @@ def Binomial_kn_over_n(var, k, precision=None, skip_constant_factor=False): if b.parent() is SR: b = SCR(b).canonicalize_radical() result *= n.rpow(b) - result *= n**(-QQ(1)/QQ(2)) + result *= n**(-QQ((1, 2))) if not skip_constant_factor: result *= (k/((k-1)*2*SCR('pi'))).sqrt() @@ -905,7 +903,7 @@ def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) - s = SR('s') + s = SR.var('s') iga = 1/gamma(alpha) if iga.parent() is SR: try: @@ -999,7 +997,7 @@ def inverse_gamma_derivative(shift, r): else: beta_denominator = 0 L = _sa_coefficients_lambda_(max(1, k_max), beta=beta_denominator) - (k, r) = next(it) + k, r = next(it) result = (n**(-k) * log_n**(-r)).O() if alpha in ZZ and beta == 0: @@ -1009,7 +1007,7 @@ def inverse_gamma_derivative(shift, r): from .misc import NotImplementedOZero raise NotImplementedOZero(A, exact_part=A.zero()) - for (k, r) in it: + for k, r in it: result += binomial(beta, r) * \ sum(L[(k, ell)] * (-1)**ell * inverse_gamma_derivative(ell, r) @@ -1125,26 +1123,26 @@ def ImplicitExpansion(var, phi, tau=None, precision=None): sage: asymptotic_expansions.ImplicitExpansion('Z', phi=lambda u: 1 + 42*u, precision=5) Traceback (most recent call last): ... - ValueError: The function phi does not satisfy the requirements + ValueError: the function phi does not satisfy the requirements sage: asymptotic_expansions.ImplicitExpansion('Z', phi=lambda u: 42*u + u^2, precision=5) Traceback (most recent call last): ... - ValueError: The function phi does not satisfy the requirements + ValueError: the function phi does not satisfy the requirements sage: asymptotic_expansions.ImplicitExpansion('Z', phi=lambda u: 1 + u^2 + u^42, precision=5) Traceback (most recent call last): ... - ValueError: Fundamental constant tau could not be determined + ValueError: fundamental constant tau could not be determined """ from sage.symbolic.ring import SR from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.rings.asymptotic.asymptotic_ring import AsymptoticRing from sage.arith.srange import srange - y, u = SR('y'), SR('u') - one_half = QQ(1)/2 + y, u = SR.var('y'), SR.var('u') + one_half = QQ((1, 2)) - if phi(QQ(0)).is_zero() or phi(u) == phi(0) + u*phi(u).diff(u)(u=0): - raise ValueError('The function phi does not satisfy the requirements') + if phi(QQ.zero()).is_zero() or phi(u) == phi(0) + u*phi(u).diff(u)(u=0): + raise ValueError('the function phi does not satisfy the requirements') if tau is None: tau = _fundamental_constant_implicit_function_(phi=phi) @@ -1161,7 +1159,7 @@ def H(y): def ansatz(prec=precision): if prec < 1: - return A(1).O() + return A.one().O() if prec == 1: return ((1/Z)**one_half).O() return (-(2*tau/phi(tau)/H(y).diff(y, 2)(y=tau)).sqrt() * (1/Z)**one_half @@ -1170,7 +1168,7 @@ def ansatz(prec=precision): # we compare coefficients between a "single" Z and the # following expansion, this allows us to compute the constants d_j - z = SR('z') + z = SR.var('z') z_expansion = sum(H(z).diff(z, k)(z=tau)/k.factorial() * ansatz(prec=precision+2-k)**k for k in srange(2, precision)) + ((1/Z)**(precision * one_half)).O() @@ -1178,7 +1176,7 @@ def ansatz(prec=precision): solution_dict = dict() for k in srange(2, precision-1): coef = z_expansion.monomial_coefficient((1/Z)**((k+1) * one_half)) - current_var = SR('d{k}'.format(k=k)) + current_var = SR.var('d{k}'.format(k=k)) solution_dict[current_var] = coef.subs(solution_dict).simplify_rational().solve(current_var)[0].rhs() return A(tau) + ansatz(prec=precision-1).map_coefficients(lambda term: term.subs(solution_dict).simplify_rational()) @@ -1264,7 +1262,7 @@ def ImplicitExpansionPeriodicPart(var, phi, period, tau=None, precision=None): phi=lambda u: phi(u**(1/period))**period, tau=tau_p, precision=precision) - rho = tau/phi(tau) + rho = tau / phi(tau) Z = aperiodic_expansion.parent().gen() return 1/rho * (aperiodic_expansion/(1 - 1/Z))**(1/period) @@ -1369,7 +1367,7 @@ def InverseFunctionAnalysis(var, phi, tau=None, period=1, precision=None): if tau is None: tau = _fundamental_constant_implicit_function_(phi=phi) - rho = tau/phi(tau) + rho = tau / phi(tau) if period == 1: expansion = asymptotic_expansions.ImplicitExpansion(var=var, phi=phi, @@ -1381,6 +1379,7 @@ def InverseFunctionAnalysis(var, phi, tau=None, period=1, precision=None): n = growth.parent().gen() return growth.subs({n: (n-1)/period}) + def _fundamental_constant_implicit_function_(phi): r""" Return the fundamental constant `\tau` occurring in the analysis of @@ -1411,12 +1410,13 @@ def _fundamental_constant_implicit_function_(phi): 1/2*sqrt(2) """ from sage.symbolic.ring import SR - u = SR('u') + u = SR.var('u') positive_solution = [s for s in (phi(u) - u*phi(u).diff(u)).solve(u) if s.rhs() > 0] if len(positive_solution) == 1: return positive_solution[0].rhs() - raise ValueError('Fundamental constant tau could not be determined') + raise ValueError('fundamental constant tau could not be determined') + def _sa_coefficients_lambda_(K, beta=0): r""" @@ -1460,18 +1460,17 @@ def _sa_coefficients_lambda_(K, beta=0): (4, 4): 5} """ from sage.rings.laurent_series_ring import LaurentSeriesRing - from sage.rings.power_series_ring import PowerSeriesRing + from sage.rings.lazy_series_ring import LazyPowerSeriesRing from sage.rings.rational_field import QQ V = LaurentSeriesRing(QQ, names='v', default_prec=K) v = V.gen() - T = PowerSeriesRing(V, names='t', default_prec=2*K-1) - t = T.gen() + t = LazyPowerSeriesRing(V, names='t').gen() - S = (t - (1+1/v+beta) * (1+v*t).log()).exp() - return dict(((k + L.valuation(), ell), c) - for ell, L in enumerate(S.list()) - for k, c in enumerate(L.list())) + S = (t - (1 + 1/v + beta) * (1 + v*t).log()).exp() + return {(k + L.valuation(), ell): c + for ell, L in enumerate(S[:2 * K - 1]) + for k, c in enumerate(L.list())} # Easy access to the asymptotic expansions generators from the command line: diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 2147409845f..9eaf85376ca 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -3501,7 +3501,7 @@ def sum(self): # Compute the sum's numerator and denominator. R = self.denominator_ring - summy = sum((f.quotient() for f in self)) + summy = sum(f.quotient() for f in self) numer = summy.numerator() denom = R(summy.denominator()) diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index cf9cf74ca70..ca2afc2b92c 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -3671,6 +3671,7 @@ def non_growth_group(self): J = ImaginaryGroup(self.base()) return self._non_growth_group_class_(J, self._var_) + class MonomialGrowthGroupFunctor(AbstractGrowthGroupFunctor): r""" A :class:`construction functor ` diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index 0bdb3d4a1eb..19bb7d975a6 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -253,6 +253,8 @@ def create_object(self, version, args, **kwds): from sage.combinat.posets.cartesian_product import CartesianProductPoset from .growth_group import GenericGrowthGroup + + class GenericProduct(CartesianProductPoset, GenericGrowthGroup): r""" A Cartesian product of growth groups. diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index c16260d298e..588580c3e13 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -4399,7 +4399,7 @@ def __init__(self, parent, growth, valid_from, **kwds): ... ValueError: B-Term has valid_from variables defined which do not occur in the term. """ - # BTerms must have positive cofficients + # BTerms must have positive coefficients coefficient = abs(kwds['coefficient']) super().__init__(parent=parent, growth=growth, coefficient=coefficient) diff --git a/src/sage/rings/bernmm/bern_rat.cpp b/src/sage/rings/bernmm/bern_rat.cpp index 963cb173524..1a1ad87b6c2 100644 --- a/src/sage/rings/bernmm/bern_rat.cpp +++ b/src/sage/rings/bernmm/bern_rat.cpp @@ -87,7 +87,7 @@ struct Item */ struct Item_cmp { - bool operator()(const Item* x, const Item* y) + bool operator()(const Item* x, const Item* y) const { return mpz_cmp(x->modulus, y->modulus) < 0; } @@ -331,9 +331,9 @@ void bern_rat(mpq_t res, long k, int num_threads) #ifdef USE_THREADS for (long i = 0; i < num_threads - 1; i++) pthread_join(threads[i], NULL); -#endif pthread_attr_destroy (&attr); +#endif // reconstruct B_k as a rational number Item* item = *(state.items.begin()); diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 6b04e1ee66f..4166083de4a 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -90,6 +90,7 @@ from numbers import Integral from sage.categories.rings import Rings +from sage.libs.pari import pari from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -102,11 +103,6 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.interfaces.gp import Gp -from sage.misc.sage_eval import sage_eval - -_gp = None - def CFiniteSequences(base_ring, names=None, category=None): r""" @@ -1204,6 +1200,13 @@ def guess(self, sequence, algorithm='sage'): C-finite sequence, generated by -1/2/(x - 1/2) sage: r[0:5] [1, 2, 4, 8, 16] + + Using pari:: + + sage: r = C.guess([1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28], algorithm='pari'); r + C-finite sequence, generated by (-x - 1)/(x^3 + x^2 - 1) + sage: r[0:5] + [1, 1, 1, 2, 2] """ S = self.polynomial_ring() @@ -1221,22 +1224,18 @@ def guess(self, sequence, algorithm='sage'): return CFiniteSequence(numerator / denominator) if algorithm == 'pari': - global _gp if len(sequence) < 6: raise ValueError('sequence too short for guessing') - if _gp is None: - _gp = Gp() - _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\ + pari("ggf(v)=local(l,m,p,q,B);l=length(v);B=l\\2;\ if(B<3,return(0));m=matrix(B,B,x,y,v[x-y+B+1]);\ q=qflll(m,4)[1];if(length(q)==0,return(0));\ p=sum(k=1,B,x^(k-1)*q[k,1]);\ q=Pol(Pol(vector(l,n,v[l-n+1]))*p+O(x^(B+1)));\ if(polcoeff(p,0)<0,q=-q;p=-p);q=q/p;p=Ser(q+O(x^(l+1)));\ for(m=1,l,if(polcoeff(p,m-1)!=v[m],return(0)));q") - _gp.set('gf', sequence) - _gp("gf=ggf(gf)") - num = S(sage_eval(_gp.eval("Vec(numerator(gf))"))[::-1]) - den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1]) + pari_guess = pari("ggf")(sequence) + num = S(pari_guess.numerator().Vec().sage()[::-1]) + den = S(pari_guess.denominator().Vec().sage()[::-1]) if num == 0: return 0 return CFiniteSequence(num / den) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 3f34c71df19..fb9d821a413 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -765,7 +765,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): self('nan'), self('nan', 'nan'), self('inf', 'nan')] def _roots_univariate_polynomial(self, pol, ring, multiplicities, - algorithm, proof=True): + algorithm, proof=True, warn=True): r""" Compute the roots of ``pol``. @@ -890,8 +890,8 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): try: sig_on() while ((isolated < deg or any(acb_rel_accuracy_bits(&roots[i]) < tgtprec - for i in range(deg))) - and prec < maxprec): + for i in range(deg))) + and prec < maxprec): acb_poly_set_round(rounded_poly, poly._poly, prec) maxiter = min(max(deg, 32), prec) if (prec == initial_prec): @@ -905,7 +905,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): if proof: raise ValueError("unable to isolate the roots (try using " "proof=False or increasing the precision)") - else: + elif warn: warnings.warn("roots may have been lost") _acb_vec_sort_pretty(roots, deg) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 7f7c16ceb67..d6e5a7d05b1 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -2619,7 +2619,7 @@ def ComplexDoubleField(): from sage.misc.parser import Parser -cdef cdf_parser = Parser(float, float, {"I" : _CDF.gen(), "i" : _CDF.gen()}) +cdef cdf_parser = Parser(float, float, {"I": _CDF.gen(), "i": _CDF.gen()}) cdef inline double complex extract_double_complex(ComplexDoubleElement x) noexcept: """ diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index ff6b4fe4d6c..e8e528e1453 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -250,14 +250,14 @@ cdef class ComplexIntervalFieldElement(FieldElement): s = self.real().str(base=base, style=style) if not self.imag().is_zero(): y = self.imag() - if s!="": + if s: if y < 0: - s = s+" - " + s += " - " y = -y else: - s = s+" + " - s = s+"%s*I"%y.str(base=base, style=style) - if len(s) == 0: + s += " + " + s += "%s*I" % y.str(base=base, style=style) + if not s: s = "0" return s @@ -2218,7 +2218,7 @@ cdef _circle_invert_standard( # crosses the x-Axis, the north east or south east diagonal, respectively. # # Given standard form, the input also has to cross the north east - # diagonal and x-Axis if it corsses the south east diagonal. + # diagonal and x-Axis if it crosses the south east diagonal. # # Thus, we are left with five cases: # diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index 0b6981cf07b..c090d6cdae2 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -49,6 +49,8 @@ from sage.structure.parent import Parent cache = {} + + def ComplexIntervalField(prec=53, names=None): """ Return the complex interval field with real and imaginary parts having diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 9b56a36ce9f..d6bcb664a49 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -2103,7 +2103,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): cdef MPComplexNumber z, x x = self z = x._new() - mpc_mul_2si(z.value , x.value, n, (x._parent).__rnd) + mpc_mul_2si(z.value, x.value, n, (x._parent).__rnd) return z def __rshift__(self, n): @@ -2122,7 +2122,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): cdef MPComplexNumber z, x x = self z = x._new() - mpc_div_2si(z.value , x.value, n, (x._parent).__rnd) + mpc_div_2si(z.value, x.value, n, (x._parent).__rnd) return z def nth_root(self, n, all=False): diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 415dd00c78f..bec9978401e 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -1917,10 +1917,10 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): mpfr_mul(t1, self.__im, self.__im, rnd) mpfr_add(t0, t0, t1, rnd) # now t0 is the norm - mpfr_div(x.__re, self.__re, t0, rnd) # x.__re = self.__re/norm + mpfr_div(x.__re, self.__re, t0, rnd) # x.__re = self.__re/norm mpfr_neg(t1, self.__im, rnd) - mpfr_div(x.__im, t1, t0, rnd) # x.__im = -self.__im/norm + mpfr_div(x.__im, t1, t0, rnd) # x.__im = -self.__im/norm mpfr_clear(t0) mpfr_clear(t1) diff --git a/src/sage/rings/continued_fraction.py b/src/sage/rings/continued_fraction.py index 62534d69f99..e4825bf3b7e 100644 --- a/src/sage/rings/continued_fraction.py +++ b/src/sage/rings/continued_fraction.py @@ -663,7 +663,7 @@ def _mpfr_(self, R): ....: fields.append(RealField(prec=prec, rnd=rnd)) sage: for n in range(3000): # long time, not tested, known bug (see :issue:`29957`) ....: a = QQ.random_element(num_bound=2^(n%100)) - ....: if a.denominator() % 8 == 0: # not precices enough # :issue:`29957` + ....: if a.denominator() % 8 == 0: # not precise enough # :issue:`29957` ....: continue ....: cf = continued_fraction(a) ....: for R in fields: diff --git a/src/sage/rings/continued_fraction_gosper.py b/src/sage/rings/continued_fraction_gosper.py index bb1a31178e5..619008df426 100644 --- a/src/sage/rings/continued_fraction_gosper.py +++ b/src/sage/rings/continued_fraction_gosper.py @@ -35,7 +35,8 @@ from sage.rings.infinity import Infinity from sage.rings.integer import Integer -class gosper_iterator(): + +class gosper_iterator: r""" Iterable for the partial quotients of `(a*x+b)/(c*x+d)`, where `a, b, c, d` are integers, and `x` is a continued fraction. diff --git a/src/sage/rings/convert/meson.build b/src/sage/rings/convert/meson.build new file mode 100644 index 00000000000..0b485247bf1 --- /dev/null +++ b/src/sage/rings/convert/meson.build @@ -0,0 +1,15 @@ +py.install_sources('all.py', 'mpfi.pxd', subdir: 'sage/rings/convert') + +extension_data = {'mpfi' : files('mpfi.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/convert', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cypari2, gmp, gsl, mpfi, mpfr, pari], + ) +endforeach + diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index ff5873ee14b..70a851990ad 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -64,6 +64,7 @@ def conway_polynomial(p, n): except KeyError: raise RuntimeError("requested Conway polynomial not in database.") + def exists_conway_polynomial(p, n): """ Check whether the Conway polynomial of degree `n` over ``GF(p)`` @@ -99,6 +100,7 @@ def exists_conway_polynomial(p, n): except ImportError: return False + class PseudoConwayLattice(WithEqualityById, SageObject): r""" A pseudo-Conway lattice over a given finite prime field. @@ -328,6 +330,7 @@ def _find_pow_of_frobenius(p, n, x, y): raise RuntimeError("No appropriate power of Frobenius found") return mod(i, n) + def _crt_non_coprime(running, a): """ Extension of the ``crt`` method of ``IntegerMod`` to the case of @@ -361,6 +364,7 @@ def _crt_non_coprime(running, a): a_modulus = a_val_unit[1] return (running % running_modulus).crt(a % a_modulus) + def _frobenius_shift(K, generators, check_only=False): """ Given a field `K` of degree `n` over ``GF(p)`` and a dictionary diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index f54c1ffb2c4..085ce14ce87 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1871,6 +1871,12 @@ cdef class FiniteField(Field): sage: F.gen(3) z3 + For finite fields, the algebraic closure is always (isomorphic + to) the algebraic closure of the prime field:: + + sage: GF(5^2).algebraic_closure() == F + True + The default name is 'z' but you can change it through the option ``name``:: @@ -1890,13 +1896,18 @@ cdef class FiniteField(Field): .. NOTE:: - This is currently only implemented for prime fields. + For non-prime finite fields, this method currently simply + returns the algebraic closure of the prime field. This may + or may not change in the future when extension towers are + supported properly. TESTS:: sage: GF(5).algebraic_closure() is GF(5).algebraic_closure() True """ + if not self.is_prime_field(): + return self.prime_subfield().algebraic_closure() from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField return AlgebraicClosureFiniteField(self, name, **kwds) @@ -1920,6 +1931,76 @@ cdef class FiniteField(Field): return (exists_conway_polynomial(p, n) and self.polynomial() == self.polynomial_ring()(conway_polynomial(p, n))) + def an_embedding(self, K): + r""" + Return some embedding of this field into another field `K`, + and raise a :class:`ValueError` if none exists. + + .. SEEALSO:: + + :meth:`sage.rings.ring.Field.an_embedding` + + EXAMPLES:: + + sage: GF(4,'a').an_embedding(GF(2).algebraic_closure()) + Ring morphism: + From: Finite Field in a of size 2^2 + To: Algebraic closure of Finite Field of size 2 + Defn: a |--> ... + """ + if self.characteristic() != K.characteristic(): + raise ValueError(f'no embedding from {self} to {K}: incompatible characteristics') + try: + return super().an_embedding(K) + except (NotImplementedError, ValueError): + pass + if K not in FiniteFields(): + from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField_generic + if not isinstance(K, AlgebraicClosureFiniteField_generic): + raise NotImplementedError('computing embeddings into this ring not implemented') + g = self.gen() + if (emb := K.coerce_map_from(self)) is not None: + return self.hom([emb(g)]) + try: + r = g.minpoly().change_ring(K).any_root() + except ValueError: + raise ValueError(f'no embedding from {self} to {K}') + return self.hom([r]) + + def embeddings(self, K): + r""" + Return a list of all embeddings of this field in another field `K`. + + EXAMPLES:: + + sage: GF(2).embeddings(GF(4)) + [Ring morphism: + From: Finite Field of size 2 + To: Finite Field in z2 of size 2^2 + Defn: 1 |--> 1] + sage: GF(4).embeddings(GF(2).algebraic_closure()) + [Ring morphism: + From: Finite Field in z2 of size 2^2 + To: Algebraic closure of Finite Field of size 2 + Defn: z2 |--> z2, + Ring morphism: + From: Finite Field in z2 of size 2^2 + To: Algebraic closure of Finite Field of size 2 + Defn: z2 |--> z2 + 1] + """ + if self.characteristic() != K.characteristic(): + return [] + if K not in FiniteFields(): + from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField_generic + if not isinstance(K, AlgebraicClosureFiniteField_generic): + raise NotImplementedError('computing embeddings into this ring not implemented') + g = self.gen() + rs = [] + if (emb := K.coerce_map_from(self)) is not None: + rs.append(emb(g)) + rs += [r for r,_ in g.minpoly().roots(ring=K) if r not in rs] + return [self.hom([r]) for r in rs] + def frobenius_endomorphism(self, n=1): """ INPUT: diff --git a/src/sage/rings/finite_rings/finite_field_pari_ffelt.py b/src/sage/rings/finite_rings/finite_field_pari_ffelt.py index 9549dbc94b4..6bc637c94b3 100644 --- a/src/sage/rings/finite_rings/finite_field_pari_ffelt.py +++ b/src/sage/rings/finite_rings/finite_field_pari_ffelt.py @@ -21,6 +21,7 @@ from .finite_field_base import FiniteField from .finite_field_constructor import GF + class FiniteField_pari_ffelt(FiniteField): """ Finite fields whose cardinality is a prime power (not a prime), diff --git a/src/sage/rings/finite_rings/galois_group.py b/src/sage/rings/finite_rings/galois_group.py index 32e23a40c44..1cdd70fcbb2 100644 --- a/src/sage/rings/finite_rings/galois_group.py +++ b/src/sage/rings/finite_rings/galois_group.py @@ -8,6 +8,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.hom_finite_field import FiniteFieldHomomorphism_generic, FrobeniusEndomorphism_finite_field + class GaloisGroup_GFElement(AbelianGroupElement): def as_hom(self): r""" @@ -52,6 +53,7 @@ def fixed_field(self): """ return self.as_hom().fixed_field() + class GaloisGroup_GF(GaloisGroup_cyc): r""" The Galois group of a finite field. diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index d55e1a0a178..9e825650fca 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -150,7 +150,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): return root raise ValueError("%s is not in the image of %s" % (x, self._inverse)) - def _repr_(self): """ Return a string representation of this section. @@ -167,7 +166,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): """ return "Section of %s" % self._inverse - def _latex_(self): r""" Return a latex representation of this section. @@ -321,7 +319,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): f = f.map_coefficients(bm) return f(self.im_gens()[0]) - def is_injective(self): """ Return ``True`` since a embedding between finite fields is @@ -338,7 +335,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): """ return True - def is_surjective(self): """ Return ``True`` if this embedding is surjective (and hence an @@ -358,7 +354,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): """ return self.domain().cardinality() == self.codomain().cardinality() - @cached_method def section(self): """ @@ -544,7 +539,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): self._q = domain.characteristic() ** self._power RingHomomorphism.__init__(self, Hom(domain, domain)) - def _repr_(self): """ Return a string representation of this endomorphism. @@ -568,7 +562,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s += " %s" % self.domain() return s - def _repr_short(self): """ Return a short string representation of this endomorphism. @@ -591,7 +584,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s = "%s |--> %s^(%s^%s)" % (name, name, self.domain().characteristic(), self._power) return s - def _latex_(self): r""" Return a latex representation of this endomorphism. @@ -615,7 +607,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s = "%s \\mapsto %s^{%s^{%s}}" % (name, name, self.domain().characteristic(), self._power) return s - cpdef Element _call_(self, x): """ TESTS:: @@ -632,7 +623,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): else: return x.pth_power(self._power) - def order(self): """ Return the order of this endomorphism. @@ -673,7 +663,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return self._power - def __pow__(self, n, modulus): """ Return the `n`-th iterate of this endomorphism. @@ -735,7 +724,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): else: return RingHomomorphism._composition(self, right) - def fixed_field(self): """ Return the fixed field of ``self``. @@ -780,7 +768,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): f = FiniteFieldHomomorphism_generic(Hom(k, self.domain())) return k, f - def is_injective(self): """ Return ``True`` since any power of the Frobenius endomorphism @@ -795,7 +782,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return True - def is_surjective(self): """ Return ``True`` since any power of the Frobenius endomorphism @@ -810,7 +796,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return True - def is_identity(self): """ Return ``True`` if this morphism is the identity morphism. diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index 6c15d725f43..617f9da0086 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -43,6 +43,7 @@ from sage.rings.morphism import RingHomomorphism_im_gens from sage.structure.sequence import Sequence + class FiniteFieldHomset(RingHomset_generic): """ Set of homomorphisms with domain a given finite field. diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 5117119dbf4..893fde4e929 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -47,6 +47,8 @@ - Simon King (2011-04-21): allow to prescribe a category - Simon King (2013-09): Only allow to prescribe the category of fields + +- Kyle Hofmann (2024-02): New implementation of root-finding """ # **************************************************************************** @@ -66,6 +68,8 @@ from sage.arith.misc import primitive_root from sage.arith.misc import CRT_basis from sage.rings.ring import Field, CommutativeRing +from sage.misc.mrange import cartesian_product_iterator +import sage.rings.ring as ring import sage.rings.abc from sage.rings.finite_rings import integer_mod import sage.rings.integer as integer @@ -1547,6 +1551,425 @@ def random_element(self, bound=None): a = random.randint(0, self.order() - 1) return self(a) + @staticmethod + def _lift_residue_field_root(p, e, f, fprime, root): + """Lifts a root of f + + INPUT: + + - ``p`` -- integer, a prime number + - ``e`` -- positive integer + - ``f`` -- polynomial with coefficients in ``IntegerModRing(p**e)`` + - ``fprime`` -- derivative of ``f`` + - ``root`` -- Element of ``IntegerModRing(p)`` with ``f(root) = 0`` + + OUTPUT: iterable of roots of ``f`` modulo ``p**e``. Each root is an + ``IntegerModRing(p**e)`` element. + + TESTS:: + + sage: R = Zmod(2) + sage: S. = R[] + sage: R._lift_residue_field_root(2, 1, S.zero(), S.zero(), R(0)) + (0,) + + Lifting roots of the zero polynomial:: + + sage: R = Zmod(41) + sage: S. = R[] + sage: R._lift_residue_field_root(41, 1, S.zero(), S.zero(), R(12)) + (12,) + sage: R = Zmod(5**2) + sage: S. = R[] + sage: R._lift_residue_field_root(5, 2, S.zero(), S.zero(), R(2)) + [2, 7, 12, 17, 22] + sage: R = Zmod(2**3) + sage: S. = R[] + sage: R._lift_residue_field_root(2, 3, S.zero(), S.zero(), R(1)) + [1, 5, 3, 7] + + Trivial case where ``e == 1``:: + + sage: R = Zmod(41) + sage: S. = R[] + sage: f = x^2 - 2 + sage: R._lift_residue_field_root(41, 1, f, f.derivative(), R(17)) + (17,) + + sage: R = Zmod(43) + sage: S. = R[] + sage: f = x^43 - 3 + sage: R._lift_residue_field_root(43, 1, f, f.derivative(), R(3)) + (3,) + + Non-singular cases with one step of lifting:: + + sage: R = Zmod(2**2) + sage: S. = R[] + sage: f = x - 1 + sage: R._lift_residue_field_root(2, 2, f, f.derivative(), R(1)) + (1,) + sage: f = x - 3 + sage: R._lift_residue_field_root(2, 2, f, f.derivative(), R(1)) + (3,) + + sage: R = Zmod(4001**2) + sage: S. = R[] + sage: f = x^3 - 2 + sage: R._lift_residue_field_root(4001, 2, f, f.derivative(), R(3981)) + (5309307,) + sage: f = x^3 - 3 + sage: R._lift_residue_field_root(4001, 2, f, f.derivative(), R(1091)) + (11035849,) + + Non-singular cases with multiple steps of lifting:: + + sage: R = Zmod(2**10) + sage: S. = R[] + sage: f = x + 1 + sage: R._lift_residue_field_root(2, 10, f, f.derivative(), Zmod(2)(1)) + (1023,) + + sage: R = Zmod(2**16) + sage: S. = R[] + sage: f = x + 1 + sage: R._lift_residue_field_root(2, 16, f, f.derivative(), Zmod(2)(1)) + (65535,) + + sage: R = Zmod(7**4) + sage: S. = R[] + sage: f = x^4 - 2 + sage: R._lift_residue_field_root(7, 4, f, f.derivative(), Zmod(7)(2)) + (121,) + + Singular cases:: + + sage: R = Zmod(2**3) + sage: S. = R[] + sage: f = x^2 - 1 + sage: R._lift_residue_field_root(2, 3, f, f.derivative(), Zmod(2)(1)) + [1, 5, 3, 7] + sage: f = 2*x + sage: R._lift_residue_field_root(2, 3, f, f.derivative(), Zmod(2)(0)) + [0, 4] + + sage: R = Zmod(11**2) + sage: S. = R[] + sage: f = x^2 + 13*x + 1 + sage: R._lift_residue_field_root(11, 2, f, f.derivative(), Zmod(11)(10)) + [] + + sage: R = Zmod(11**3) + sage: S. = R[] + sage: f = x^2 + 123*x + 1 + sage: R._lift_residue_field_root(11, 3, f, f.derivative(), Zmod(11)(10)) + [10, 131, 252, 373, 494, 615, 736, 857, 978, 1099, 1220, 109, 230, 351, 472, 593, 714, 835, 956, 1077, 1198, 1319] + """ + if e == 1: + # Nothing to do + return (root,) + deriv = fprime(root) + if deriv: + # Unique lift, use Newton iteration + prec = 1 + while True: + prec = min(2*prec, e) + Zp_prec = Zmod(p**prec) + root = Zp_prec(root.lift()) + deriv = fprime(root) + step = f(root) / deriv + root -= step + if prec >= e: + return (root,) + else: + # Non-unique lift, go one power at a time + prec = 1 + new_power = 1 + new_mod = p + current_roots = (root,) + for _ in range(e - 1): + prec += 1 + new_power = new_mod + new_mod *= p + new_roots = [] + Zp_prec = Zmod(new_mod) + for rt in current_roots: + rt = Zp_prec(rt.lift()) + if f(rt): + continue + new_roots.append(rt) + for _ in range(p - 1): + rt += new_power + new_roots.append(rt) + current_roots = new_roots + + return current_roots + + def _roots_univariate_polynomial(self, f, ring=None, multiplicities=True, algorithm=None): + r""" + Return the roots of ``f`` in the ring ``ring``. + + INPUT: + + - ``f`` - a polynomial defined over this ring + + - ``ring`` - the ring to find roots in. Control flow elsewhere + ensures that the only cases we need to handle are ``self`` and + ``None``. Otherwise we raise ``NotImplementedError``. + + - ``multiplicities`` - bool (default: ``True``). If ``True``, return + list of pairs `(r, n)`, where `r` is a root and `n` is its + multiplicity. If ``False``, just return the unique roots, with no + information about multiplicities. Multiplicities are only defined + over fields, and this method raises ``NotImplementedError`` if this + is ``True`` but the ring is not a field. + + - ``algorithm`` - ignored + + ALGORITHM: + + The algorithm is adapted from [Gou2020]_, section 4.5, and [Coh1993]_, + section 3.5.3. It is a combination of the Chinese Remainder Theorem + and Hensel's lemma. As a base case, if `N` is prime, then we find + roots by factoring `f`. If `N` is a prime power `p^e`, then we find + roots modulo `p` and lift them. Finally, for general `N`, we first + factor the modulus `N` into prime powers, list all roots modulo those + prime powers, and combine the roots using the Chinese Remainder + Theorem. + + Suppose that we are trying to find roots modulo `p^e` and that `r` is + a root of `f(x)` modulo `p`. The easy case is when `f'(r) \not\equiv + 0 \pmod{p}`, for then Hensel's lemma implies that there is a unique + `r_e \in \Zmod{p^e}` with `r_e \equiv r \pmod{p}`. + Moreover, this `r_e` can be found by applying Newton's method for + numerically approximating roots. Each iteration of Newton's method + doubles the precision to which the root is known. + + But if `f'(r) \equiv 0 \pmod{p}`, then this is no longer true. In + fact, in this case roots modulo `p^e` are not the same as `p`-adic + roots, and finding all the latter does not guarantee that we have + found all the former. For example, if `f(x) = 2x` and `p = 2`, then + there is only one `p`-adic root, namely zero. But the solutions of + `2x \equiv 0 \pmod{2^k}` are `0` and `2^{k-1}`; the former lifts to + two roots modulo `2^{k+1}`, namely `0` and `2^k`, while the latter + does not lift at all. We handle this case by lifting one power at a + time. While we can no longer use Newton's method to solve for a lift, + the Taylor series it is based on still yields constraints on the roots + modulo `p^{k+1}`: If `r_k` is a root of `f` modulo `p^k`, then either + every lift of `r_k` to `\Zmod{p^{k + 1}}` is a root of `f` + modulo `p^{k+1}` or none of them are. Consequently we may find roots + modulo `p^e` by lifting one power at a time. + + When `f'(r) \equiv 0 \pmod{p}`, an alternative approach is to change + variables, factor out the root, and then factor out powers of `p`. + This has the advantage that it will eventually reach a situation where + the lift converges quadratically, but it is not presently implemented. + A different form of Hensel's lemma applies once we are close enough to + a `p`-adic root (see [Gou2020]_, problem 120), but it seems delicate + to use it directly to find all roots modulo `p^e` (consider our + earlier example of `f(x) = 2x`), so we do not presently attempt to + apply Hensel's lemma in this way. + + EXAMPLES:: + + sage: R. = Zmod(41)[] + sage: (x^3 + x).roots() + [(0, 1), (32, 1), (9, 1)] + sage: (x^3 + x).roots(multiplicities=False) + [0, 32, 9] + sage: (x^6 + x^5 + 9*x^4 + 20*x^3 + 3*x^2 + 18*x + 7).roots() + [(19, 1), (20, 2), (21, 3)] + sage: (x^6 + x^5 + 9*x^4 + 20*x^3 + 3*x^2 + 18*x + 7).roots(multiplicities=False) + [19, 20, 21] + + We can find roots without multiplicities over a ring whose modulus is + a prime power, even a big power: + + sage: R. = Zmod(7^3)[] + sage: (x^2 + x + 1).roots(multiplicities=False) + [18, 324] + sage: R. = Zmod(2^50)[] + sage: (x + 1).roots(multiplicities=False) + [1125899906842623] + + We can also find roots without multiplicities over a ring whose modulus + is a product of primes or prime powers: + + sage: R. = Zmod(60)[] + sage: (x^2 - 1).roots(multiplicities=False) + [29, 41, 49, 1, 59, 11, 19, 31] + + We may also ask for roots modulo a quotient of the ring over which the + polynomial is defined: + + sage: R. = Zmod(120)[] + sage: (x^2 - 1).roots(multiplicities=False) + [89, 41, 49, 1, 29, 101, 109, 61, 59, 11, 19, 91, 119, 71, 79, 31] + sage: (x^2 - 1).roots(Zmod(60), multiplicities=False) + [29, 41, 49, 1, 59, 11, 19, 31] + + TESTS:: + + sage: R. = Zmod(2)[] + sage: x.roots() + [(0, 1)] + + Test polynomials with content: + + sage: R. = Zmod(4)[] + sage: (2*x).roots(multiplicities=False) + [0, 2] + + sage: R. = Zmod(6)[] + sage: (3*x).roots(multiplicities=False) + [0, 4, 2] + + Test polynomial with many roots: + + sage: R. = Zmod(6)[] + sage: f = x * (x - 1) * (x - 2) * (x - 3) * (x - 4) * (x - 5) + sage: len(f.roots(multiplicities=False)) + 6 + + Test finding roots over large prime powers: + + sage: R. = Zmod(2**16)[] + sage: (x^3 + 5).roots(multiplicities=False) + [45475] + sage: (x^2 + 46*x + 1).roots(multiplicities=False) + [421, 33189, 16805, 49573, 8613, 41381, 24997, 57765, 7725, 40493, 24109, 56877, 15917, 48685, 32301, 65069] + + sage: R. = Zmod(3**16)[] + sage: (x^2 + 2).roots(multiplicities=False) + [24620738, 18425983] + sage: (x^2 + 11*x + 1).roots(multiplicities=False) + [633836, 14982743, 29331650, 13715060, 28063967, 42412874] + sage: (x^3 + 8).roots(multiplicities=False) + [14348905, 28697812, 43046719] + + Test some larger primes: + + sage: R. = Zmod(41**4)[] + sage: (x^2 + 2).roots(multiplicities=False) + [2208905, 616856] + sage: R. = Zmod(43**4)[] + sage: (x^2 + 3).roots(multiplicities=False) + [3269879, 148922] + + We can't find roots with multiplicities in non-fields: + + sage: R. = Zmod(6)[] + sage: (x + 1).roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + sage: R. = Zmod(8)[] + sage: (x + 1).roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + sage: R. = Zmod(12)[] + sage: (x + 1).roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + + The zero polynomial has every residue class as a root, but we don't + support multiplicities even over fields (they would all be infinite). + + sage: R. = Zmod(6)[] + sage: R.zero().roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + sage: R.zero().roots(multiplicities=False) + [0, 1, 2, 3, 4, 5] + + sage: R. = Zmod(7)[] + sage: R.zero().roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + sage: R.zero().roots(multiplicities=False) + [0, 1, 2, 3, 4, 5, 6] + + sage: R. = Zmod(8)[] + sage: R.zero().roots() + Traceback (most recent call last): + ... + NotImplementedError: root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option) + sage: R.zero().roots(multiplicities=False) + [0, 1, 2, 3, 4, 5, 6, 7] + + This method doesn't support root-finding over rings that aren't Z/nZ: + + sage: R. = Zmod(120)[] + sage: f = x^2 - 1 + sage: f.base_ring()._roots_univariate_polynomial(f, ring=RR, multiplicities=False) + Traceback (most recent call last): + ... + NotImplementedError + + Sage allows us to coerce polynomials from one modulus to another, + and that makes the following defined: + + sage: R. = Zmod(100)[] + sage: (x^2 - 1).roots(Zmod(99), multiplicities=False) == (x^2 - 1).change_ring(Zmod(99)).roots(multiplicities=False) + True + """ + + # This function only supports roots in an IntegerModRing + if ring is not self and ring is not None: + raise NotImplementedError + + deg = f.degree() + + if multiplicities: + if deg < 0 or not self.is_field(): + raise NotImplementedError( + "root finding with multiplicities for this polynomial not" + " implemented (try the multiplicities=False option)" + ) + # Roots of non-zero polynomial over finite fields by factorization + return f._roots_from_factorization(f.factor(), multiplicities) + + # Zero polynomial is a base case + if deg < 0: + # All residue classes are roots of the zero polynomial + return [*map(self, range(self.cardinality()))] + + # Finite fields are a base case + if self.is_field(): + return f._roots_from_factorization(f.factor(), False) + + # Otherwise, find roots modulo each prime power + fac = self.factored_order() + prime_power_roots = [] + for p, e in fac: + Zpe = Zmod(p**e) + fpe = f.change_ring(Zpe) + fpe_prime = fpe.derivative() + fp = fpe.change_ring(Zmod(p)) + + mod_p_roots = fp.roots(multiplicities=False) + + this_prime_power = [] + for root in mod_p_roots: + this_prime_power.extend( + self._lift_residue_field_root(p, e, fpe, fpe_prime, root) + ) + prime_power_roots.append(this_prime_power) + + # Combine using Chinese Remainder Theorem + ppwr_basis = CRT_basis([p**e for p, e in fac]) + result = [] + for res in cartesian_product_iterator(prime_power_roots): + root = self.zero() + for c, x in zip(ppwr_basis, res): + root += c*x.lift() + result.append(root) + return result + ####################################################### # Suppose for interfaces ####################################################### diff --git a/src/sage/rings/finite_rings/maps_finite_field.py b/src/sage/rings/finite_rings/maps_finite_field.py index e68bd5c7b94..dff8679dd1e 100644 --- a/src/sage/rings/finite_rings/maps_finite_field.py +++ b/src/sage/rings/finite_rings/maps_finite_field.py @@ -21,6 +21,7 @@ from sage.categories.morphism import Morphism + class FiniteFieldVectorSpaceIsomorphism(Morphism): """ Base class of the vector space isomorphism between a finite field @@ -70,6 +71,7 @@ def is_surjective(self): """ return True + class MorphismVectorSpaceToFiniteField(FiniteFieldVectorSpaceIsomorphism): """ Isomorphisms from vector spaces to finite fields. @@ -130,6 +132,7 @@ def _call_(self, v): w[i*n:(i+1)*n] = v[i]._vector_() return E(w * self._C) + class MorphismFiniteFieldToVectorSpace(FiniteFieldVectorSpaceIsomorphism): """ Isomorphisms from finite fields to vector spaces diff --git a/src/sage/rings/finite_rings/meson.build b/src/sage/rings/finite_rings/meson.build new file mode 100644 index 00000000000..896d69651cf --- /dev/null +++ b/src/sage/rings/finite_rings/meson.build @@ -0,0 +1,81 @@ +py.install_sources( + 'all.py', + 'conway_polynomials.py', + 'element_base.pxd', + 'element_givaro.pxd', + 'element_ntl_gf2e.pxd', + 'element_pari_ffelt.pxd', + 'finite_field_base.pxd', + 'finite_field_constructor.py', + 'finite_field_givaro.py', + 'finite_field_ntl_gf2e.py', + 'finite_field_pari_ffelt.py', + 'finite_field_prime_modn.py', + 'galois_group.py', + 'hom_finite_field.pxd', + 'hom_finite_field_givaro.pxd', + 'hom_prime_finite_field.pxd', + 'homset.py', + 'integer_mod.pxd', + 'integer_mod_limits.h', + 'integer_mod_ring.py', + 'maps_finite_field.py', + 'residue_field.pxd', + 'stdint.pxd', + subdir: 'sage/rings/finite_rings', +) + +extension_data = { + 'element_base' : files('element_base.pyx'), + 'element_pari_ffelt' : files('element_pari_ffelt.pyx'), + 'finite_field_base' : files('finite_field_base.pyx'), + 'hom_finite_field' : files('hom_finite_field.pyx'), + 'hom_prime_finite_field' : files('hom_prime_finite_field.pyx'), + 'integer_mod' : files('integer_mod.pyx'), + 'residue_field' : files('residue_field.pyx'), + 'residue_field_givaro' : files('residue_field_givaro.pyx'), + 'residue_field_ntl_gf2e' : files('residue_field_ntl_gf2e.pyx'), + 'residue_field_pari_ffelt' : files('residue_field_pari_ffelt.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/finite_rings', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cypari2, cysignals, givaro, gmp, m, ntl, pari], + ) +endforeach + +extension_data_cpp = { + 'element_givaro': files('element_givaro.pyx'), + 'element_ntl_gf2e': files('element_ntl_gf2e.pyx'), + 'hom_finite_field_givaro': files('hom_finite_field_givaro.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/finite_rings', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cypari2, cysignals, givaro, gmp, m, ntl, pari], + ) +endforeach + diff --git a/src/sage/rings/function_field/constructor.py b/src/sage/rings/function_field/constructor.py index a316c384bd1..098da7b59da 100644 --- a/src/sage/rings/function_field/constructor.py +++ b/src/sage/rings/function_field/constructor.py @@ -39,6 +39,7 @@ from sage.structure.factory import UniqueFactory + class FunctionFieldFactory(UniqueFactory): """ Return the function field in one variable with constant field ``F``. The diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py index 13247abe3b0..8c72b4cdb98 100644 --- a/src/sage/rings/function_field/divisor.py +++ b/src/sage/rings/function_field/divisor.py @@ -74,6 +74,7 @@ from .place import PlaceSet + def divisor(field, data): """ Construct a divisor from the data. @@ -98,6 +99,7 @@ def divisor(field, data): divisor_group = field.divisor_group() return divisor_group.element_class(divisor_group, data) + def prime_divisor(field, place, m=1): """ Construct a prime divisor from the place. diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index d183848f296..6142ec2fa2d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -31,6 +31,7 @@ lazy_import('sage.rings.lazy_series_ring', 'LazyPowerSeriesRing') + class DrinfeldModule_charzero(DrinfeldModule): r""" This class implements Drinfeld `\mathbb{F}_q[T]`-modules defined @@ -317,7 +318,6 @@ def logarithm(self, name='z'): True """ L = LazyPowerSeriesRing(self._base, name) - zero = self._base.zero() q = self._Fq.cardinality() def coeff_log(k): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index d225820ab18..c4ad9f1376d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -46,6 +46,7 @@ lazy_import('sage.rings.ring_extension', 'RingExtension_generic') + class DrinfeldModule(Parent, UniqueRepresentation): r""" This class implements Drinfeld `\mathbb{F}_q[T]`-modules. diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 15809061112..2269085539d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -162,7 +162,7 @@ def __init__(self, gen, category): self._frobenius_trace = None self._frobenius_charpoly = None - def _frobenius_crystalline_matrix(self): + def _frobenius_matrix_crystalline(self): r""" Return the matrix representing the Frobenius endomorphism on the crystalline cohomology of the Drinfeld module. This is done up to @@ -180,7 +180,7 @@ def _frobenius_crystalline_matrix(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi._frobenius_crystalline_matrix() + sage: phi._frobenius_matrix_crystalline() [...((z2 + 3) + z12 + (4*z2 + 1)*z12^2 + (z2 + 4)*z12^3 + (z2 + 4)*z12^4 + (2*z2 + 3)*z12^5)*T^3 + (2*z2 + 2*z2*z12 + (2*z2 + 3)*z12^2 + ... + (3*z2 + 4)*z12^3 + (2*z2 + 3)*z12^4 + 3*z2*z12^5] ALGORITHM: @@ -257,7 +257,7 @@ def frobenius_endomorphism(self): deg = self.base_over_constants_field().degree_over() return self._Hom_(self, category=self.category())(t**deg) - def frobenius_charpoly(self, var='X', algorithm='crystalline'): + def frobenius_charpoly(self, var='X', algorithm=None): r""" Return the characteristic polynomial of the Frobenius endomorphism. @@ -290,10 +290,30 @@ def frobenius_charpoly(self, var='X', algorithm='crystalline'): INPUT: - - ``var`` -- (default: ``'X'``) the name of the second variable - - ``algorithm`` -- (default: ``'crystalline'``) the algorithm + - ``var`` (default: ``'X'``) -- the name of the second variable + - ``algorithm`` (default: ``None``) -- the algorithm used to compute the characteristic polynomial + .. NOTE: + + Available algorithms are: + + - ``'CSA'`` -- it exploits the fact that `K\{\tau\}` is a + central simple algebra (CSA) over `\mathbb + F_q[\text{Frob}_\phi]` (see Chapter 4 of [CL2023]_). + - ``'crystalline'`` -- it uses the action of the Frobenius + on the crystalline cohomology (see [MS2023]_). + - ``'gekeler'`` -- it tries to identify coefficients by + writing that the characteristic polynomial annihilates the + Frobenius endomorphism; this algorithm may fail is some + cases (see [Gek2008]_). + - ``'motive'`` -- it uses the action of the Frobenius on the + Anderson motive (see Chapter 2 of [CL2023]_). + + The method raises an exception if the user asks for an + unimplemented algorithm, even if the characteristic polynomial + has already been computed. + EXAMPLES:: sage: Fq = GF(25) @@ -310,7 +330,7 @@ def frobenius_charpoly(self, var='X', algorithm='crystalline'): sage: A. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) - sage: chi = phi.frobenius_charpoly() + sage: chi = phi.frobenius_charpoly(algorithm='crystalline') sage: chi X^2 + ((3*z3^2 + z3 + 4)*T + 4*z3^2 + 6*z3 + 3)*X + (5*z3^2 + 2*z3)*T^2 + (4*z3^2 + 3*z3)*T + 5*z3^2 + 2*z3 @@ -319,6 +339,9 @@ def frobenius_charpoly(self, var='X', algorithm='crystalline'): sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() sage: chi(frob_pol, phi(T)) 0 + sage: phi.frobenius_charpoly(algorithm='motive')(phi.frobenius_endomorphism()) + Endomorphism of Drinfeld module defined by T |--> z6*t^2 + 1 + Defn: 0 :: @@ -329,33 +352,99 @@ def frobenius_charpoly(self, var='X', algorithm='crystalline'): ALGORITHM: - By default, this method uses the so-called *crystalline* - algorithm which computes the characteristic polynomial of the - Frobenius acting on the crystalline cohomology of the Drinfeld - module. For further details, see [Ang1997]_. + If the user specifies an algorithm, then the characteristic + polynomial is computed according to the user's input (see + the note above), even if it had already been computed. + + If no algorithm is given, then the function either returns a + cached value, or if no cached value is available, the + function computes the Frobenius characteristic polynomial + from scratch. In that case, if the rank `r` is less than the + extension degree `n`, then the ``crystalline`` algorithm is + used, while the ``CSA`` algorithm is used otherwise. + + TESTS:: + + sage: Fq = GF(9) + sage: A. = Fq[] + sage: k. = Fq.extension(2) + sage: K. = k.extension(3) + + :: + + sage: phi = DrinfeldModule(A, [K(zk), z^8, z^7]) + sage: phi.frobenius_charpoly(algorithm='CSA') + X^2 + (2*T^3 + (2*z2 + 2)*T^2 + (z2 + 1)*T + 2*z2)*X + z2*T^6 + (2*z2 + 2)*T^3 + 2 + sage: phi.frobenius_charpoly(algorithm='crystalline') + X^2 + (2*T^3 + (2*z2 + 2)*T^2 + (z2 + 1)*T + 2*z2)*X + z2*T^6 + (2*z2 + 2)*T^3 + 2 + sage: phi.frobenius_charpoly(algorithm='gekeler') + X^2 + (2*T^3 + (2*z2 + 2)*T^2 + (z2 + 1)*T + 2*z2)*X + z2*T^6 + (2*z2 + 2)*T^3 + 2 + sage: phi.frobenius_charpoly(algorithm='motive') + X^2 + (2*T^3 + (2*z2 + 2)*T^2 + (z2 + 1)*T + 2*z2)*X + z2*T^6 + (2*z2 + 2)*T^3 + 2 + sage: phi.frobenius_charpoly()(phi.frobenius_endomorphism()).ore_polynomial() + 0 - The available options for 'algorithm' are: + :: - - ``'crystalline'`` -- computes the characteristic polynomial of the - Frobenius endomorphism on the crystalline cohomology of a Drinfeld - module. + sage: phi = DrinfeldModule(A, [K(zk), z^2, z^3, z^4]) + sage: phi.frobenius_charpoly(algorithm='CSA') + X^3 + ((z2 + 1)*T^2 + (z2 + 1)*T + z2 + 2)*X^2 + ((z2 + 2)*T^4 + 2*T^3 + z2*T^2 + T + 2*z2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='crystalline') + X^3 + ((z2 + 1)*T^2 + (z2 + 1)*T + z2 + 2)*X^2 + ((z2 + 2)*T^4 + 2*T^3 + z2*T^2 + T + 2*z2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='gekeler') + X^3 + ((z2 + 1)*T^2 + (z2 + 1)*T + z2 + 2)*X^2 + ((z2 + 2)*T^4 + 2*T^3 + z2*T^2 + T + 2*z2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='motive') + X^3 + ((z2 + 1)*T^2 + (z2 + 1)*T + z2 + 2)*X^2 + ((z2 + 2)*T^4 + 2*T^3 + z2*T^2 + T + 2*z2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly()(phi.frobenius_endomorphism()).ore_polynomial() + 0 - - ``'motive'`` -- based on computing the characteristic polynomial of - the Frobenius endomorphism on the motive of a Drinfeld module. This - instantiates the Frobenius as a morphism object and calls its - ``'characteristic_polynomial'`` method. + :: + + sage: phi = DrinfeldModule(A, [K(zk), z^8, z^7, z^20]) + sage: phi.frobenius_charpoly(algorithm='CSA') + X^3 + (z2*T^2 + z2*T + z2 + 1)*X^2 + (T^4 + (2*z2 + 1)*T^3 + (z2 + 2)*T^2 + (2*z2 + 1)*T + z2 + 2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='crystalline') + X^3 + (z2*T^2 + z2*T + z2 + 1)*X^2 + (T^4 + (2*z2 + 1)*T^3 + (z2 + 2)*T^2 + (2*z2 + 1)*T + z2 + 2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='gekeler') + X^3 + (z2*T^2 + z2*T + z2 + 1)*X^2 + (T^4 + (2*z2 + 1)*T^3 + (z2 + 2)*T^2 + (2*z2 + 1)*T + z2 + 2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly(algorithm='motive') + X^3 + (z2*T^2 + z2*T + z2 + 1)*X^2 + (T^4 + (2*z2 + 1)*T^3 + (z2 + 2)*T^2 + (2*z2 + 1)*T + z2 + 2)*X + T^6 + 2*z2*T^3 + 2*z2 + 1 + sage: phi.frobenius_charpoly()(phi.frobenius_endomorphism()).ore_polynomial() + 0 + + Check that ``var`` inputs are taken into account for cached + characteristic polynomials:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(A, [z, 0, 1]) + sage: phi.frobenius_charpoly() + X^2 + X + T^2 + T + 1 + sage: phi.frobenius_charpoly(var='Foo') + Foo^2 + Foo + T^2 + T + 1 + sage: phi.frobenius_charpoly(var='Bar') + Bar^2 + Bar + T^2 + T + 1 """ - # Throw an error if the user asks for an unimplemented algorithm - # even if the char poly has already been computed - method_name = f'_frobenius_charpoly_{algorithm}' - if hasattr(self, method_name): + # If no algorithm is specified, return cached data (if it + # exists), or pick an algorithm: + if algorithm is None: if self._frobenius_charpoly is not None: - return self._frobenius_charpoly - self._frobenius_charpoly = getattr(self, method_name)(var) - return self._frobenius_charpoly - raise NotImplementedError(f'algorithm \"{algorithm}\" not implemented') + return self._frobenius_charpoly.change_variable_name(var) + else: + if self.rank() < self._base_degree_over_constants: + algorithm = 'crystalline' + else: + algorithm = 'CSA' + # If an algorithm is specified, do not use cached data, even + # if it is possible: + method_name = f'_frobenius_charpoly_{algorithm}' + if not hasattr(self, method_name): + raise NotImplementedError(f'algorithm "{algorithm}" not implemented') + self._frobenius_charpoly = getattr(self, method_name)() + return self._frobenius_charpoly.change_variable_name(var) - def _frobenius_charpoly_crystalline(self, var): + def _frobenius_charpoly_CSA(self): r""" Return the characteristic polynomial of the Frobenius endomorphism using Crystalline cohomology. @@ -365,11 +454,59 @@ def _frobenius_charpoly_crystalline(self, var): This method is private and should not be directly called. Instead, use :meth:`frobenius_charpoly` with the option - `algorithm='crystalline'`. + `algorithm='CSA'`. - INPUT: + EXAMPLES:: - - ``var`` -- the name of the second variable + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z^i for i in range(1, 50)]) + sage: phi.frobenius_charpoly(algorithm="CSA") # indirect doctest + X^48 + 4*X^47 + 4*X^46 + X^45 + 3*X^44 + X^42 + 4*X^41 + 4*X^39 + 2*X^37 + 4*X^36 + 3*X^35 + 2*X^33 + (4*T + 2)*X^32 + (4*T + 1)*X^31 + (3*T + 1)*X^30 + 4*T*X^29 + 4*T*X^28 + 2*X^27 + X^26 + (3*T + 4)*X^25 + (4*T + 4)*X^24 + (T + 1)*X^23 + (T + 3)*X^22 + 4*T*X^21 + (2*T + 1)*X^20 + 4*X^19 + 4*T*X^18 + (T + 4)*X^17 + 4*T^2*X^16 + (T^2 + 3*T + 3)*X^15 + (4*T^2 + 3*T + 3)*X^14 + (3*T^2 + 3*T + 3)*X^13 + (3*T^2 + 4*T + 4)*X^12 + (T^2 + 2*T + 2)*X^11 + (3*T^2 + 4*T + 4)*X^10 + (3*T^2 + T + 1)*X^9 + (4*T + 4)*X^8 + (3*T + 3)*X^7 + (T^2 + 3*T + 3)*X^6 + (3*T^2 + T + 1)*X^5 + (2*T^2 + 3*T + 3)*X^4 + (2*T^2 + 3*T + 3)*X^3 + 3*T^2*X^2 + 4*T^2*X + 2*T^3 + T + 1 + + ALGORITHM: + + Compute the characteristic polynomial of the Frobenius from + the reduced characteristic polynomial of the Ore polynomial + `phi_T`. This algorithm is particularly interesting when the + rank of the Drinfeld module is large compared to the degree + of the extension `K/\mathbb F_q`. See [CL2023]_. + """ + E = self._base + EZ = PolynomialRing(E, name='Z') + n = self._base_degree_over_constants + f = self.gen() # phi_T, which is updated in the subsequent loop + t = self.ore_variable() + rows = [] + for i in range(n): + m = f.degree() + 1 + row = [EZ([f[jj] for jj in range(j, m, n)]) for j in range(n)] + rows.append(row) + f = t * f + chi = Matrix(rows).charpoly() + # Format the result + K = self.base_over_constants_field() + A = self.function_ring() + n = self._base_degree_over_constants + r = self.rank() + lc = chi[0][r] + coeffs = [A([K(chi[i][j]/lc).in_base() + for i in range((r-j)*n // r + 1)]) + for j in range(r+1)] + return PolynomialRing(A, name='X')(coeffs) + + def _frobenius_charpoly_crystalline(self): + r""" + Return the characteristic polynomial of the Frobenius + endomorphism using Crystalline cohomology. + + The algorithm works for Drinfeld `\mathbb{F}_q[T]`-modules of + any rank. + + This method is private and should not be directly called. + Instead, use :meth:`frobenius_charpoly` with the option + `algorithm='crystalline'`. OUTPUT: a univariate polynomial with coefficients in the function ring @@ -413,17 +550,96 @@ def _frobenius_charpoly_crystalline(self, var): of section 6.3 in [MS2023]_. """ A = self.function_ring() - K = self.base_over_constants_field() - charpoly_K = self._frobenius_crystalline_matrix() \ - .charpoly(var).coefficients(sparse=False) + charpoly_K = self._frobenius_matrix_crystalline().charpoly().list() # The above line obtains the char poly with coefficients in K[T] # This maps them into A = Fq[T] coeffs_A = [A([x.in_base() for x in coeff]) for coeff in charpoly_K] - return PolynomialRing(A, name=var)(coeffs_A) + return PolynomialRing(A, name='X')(coeffs_A) + + def _frobenius_charpoly_gekeler(self): + r""" + Return the characteristic polynomial of the Frobenius + endomorphism using Gekeler's algorithm. + + The algorithm works for Drinfeld `\mathbb{F}_q[T]`-modules of + any rank, provided that the constant coefficient is a generator + of the base field. + + This method is private and should not be directly called. + Instead, use :meth:`frobenius_charpoly` with the option + `algorithm='gekeler'`. + + .. WARNING: + + This algorithm only works in the generic case when the + corresponding linear system is invertible. Notable cases + where this fails include Drinfeld modules whose minimal + polynomial is not equal to the characteristic polynomial, + and rank 2 Drinfeld modules where the degree 1 coefficient + of `\phi_T` is 0. In that case, an exception is raised. + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(A, [z, 4, 1, z]) + sage: phi.frobenius_charpoly(algorithm='gekeler') # indirect doctest + X^3 + ((z2 + 2)*T^2 + (z2 + 2)*T + 4*z2 + 4)*X^2 + ... + (3*z2 + 2)*T^2 + (3*z2 + 3)*T + 4 + + :: + + sage: Fq = GF(125) + sage: A. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(A, [z, 0, z]) + sage: phi.frobenius_charpoly(algorithm='gekeler') # indirect doctest + Traceback (most recent call last): + NotImplementedError: 'gekeler' algorithm failed + + ALGORITHM: - def _frobenius_charpoly_motive(self, var): + Construct a linear system based on the requirement that the + Frobenius satisfies a degree r polynomial with coefficients in + the function ring. This generalizes the procedure from + [Gek2008]_ for the rank 2 case. + """ + K = self.base_over_constants_field() + A = self.function_ring() + r, n = self.rank(), self._base_degree_over_constants + # Compute constants that determine the block structure of the + # linear system. The system is prepared such that the solution + # vector has the form [a_0, a_1, ... a_{r-1}]^T with each a_i + # corresponding to a block of length (n*(r - i))//r + 1 + shifts = [(n*(r - i))//r + 1 for i in range(r)] + rows, cols = n*r + 1, sum(shifts) + block_shifts = [0] + for i in range(r-1): + block_shifts.append(block_shifts[-1] + shifts[i]) + # Compute the images \phi_T^i for i = 0 .. n. + gen_powers = [self(A.gen()**i).coefficients(sparse=False) + for i in range(0, n + 1)] + sys, vec = Matrix(K, rows, cols), vector(K, rows) + vec[rows - 1] = -1 + for j in range(r): + for k in range(shifts[j]): + for i in range(len(gen_powers[k])): + sys[i + n*j, block_shifts[j] + k] = gen_powers[k][i] + if sys.right_nullity() != 0: + raise NotImplementedError("'gekeler' algorithm failed") + sol = list(sys.solve_right(vec)) + # The system is solved over K, but the coefficients should all + # be in Fq We project back into Fq here. + sol_Fq = [K(x).vector()[0] for x in sol] + char_poly = [] + for i in range(r): + char_poly.append([sol_Fq[block_shifts[i] + j] + for j in range(shifts[i])]) + return PolynomialRing(A, name='X')(char_poly + [1]) + + def _frobenius_charpoly_motive(self): r""" Return the characteristic polynomial of the Frobenius endomorphism using Motivic cohomology. @@ -435,10 +651,6 @@ def _frobenius_charpoly_motive(self, var): Instead, use :meth:`frobenius_charpoly` with the option `algorithm='motive'`. - INPUT: - - - ``var`` -- the name of the second variable - OUTPUT: a univariate polynomial with coefficients in the function ring @@ -470,7 +682,7 @@ def _frobenius_charpoly_motive(self, var): sage: phi.frobenius_charpoly(algorithm='motive') # indirect doctest X^11 + (z3^2 + 2*z3)*X^10 + ((z3 + 1)*T + z3)*X^9 + ((2*z3^2 + z3 + 2)*T^2 + ... + (2*z3^2 + 2*z3 + 2)*T + z3^2 """ - return self.frobenius_endomorphism().characteristic_polynomial(var) + return self.frobenius_endomorphism().characteristic_polynomial('X') def frobenius_norm(self): r""" @@ -478,9 +690,25 @@ def frobenius_norm(self): Let `C(X) = \sum_{i=0}^r a_iX^{i}` denote the characteristic polynomial of the Frobenius endomorphism. The Frobenius norm - is `(-1)^r a_{0}`. This is an element of the regular function ring - and if `n` is the degree of the base field over `\mathbb{F}_q`, - then the Frobenius norm has degree `n`. + is `a_{0}`, and given by the formula + + .. MATH:: + + a_0 = (-1)^{nr - n -r} + \mathrm{Norm}_{K/\mathbb F_q}(\Delta)^{-1} + p^{n / \mathrm{deg}(p)}, + + where `K` is the ground field, which as degree `n` over + `\mathbb F_q`, `r` is the rank of the Drinfeld module, + and `\Delta` is the leading coefficient of the generator. + This formula is given in Theorem~4.2.7 of [Pap2023]_. + + Note that the Frobenius norm computed by this method may be + different than what is computed as the isogeny norm of the + Frobenius endomorphism (see :meth:`norm` on the Frobenius + endomorphism), which is an ideal defined of the function ring + given by its monic generator; the Frobenius norm may not be + monic. EXAMPLES:: @@ -488,19 +716,28 @@ def frobenius_norm(self): sage: A. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) - sage: B = phi.frobenius_norm() - sage: B + sage: frobenius_norm = phi.frobenius_norm() + sage: frobenius_norm (5*z3^2 + 2*z3)*T^2 + (4*z3^2 + 3*z3)*T + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree of the base field over Fq - sage: B.degree() == n + sage: frobenius_norm.degree() == n True :: - sage: B == phi.frobenius_charpoly()[0] + sage: frobenius_norm == phi.frobenius_charpoly()[0] + True + + :: + + sage: lc = frobenius_norm.leading_coefficient() + sage: isogeny_norm = phi.frobenius_endomorphism().norm() + sage: isogeny_norm.gen() == frobenius_norm / lc + True + sage: A.ideal(frobenius_norm) == isogeny_norm True ALGORITHM: @@ -512,12 +749,15 @@ def frobenius_norm(self): return self._frobenius_norm K = self.base_over_constants_field() n = K.degree(self._Fq) - char = self.characteristic() + r = self.rank() + p = self.characteristic() norm = K(self.coefficients()[-1]).norm() - self._frobenius_norm = ((-1)**n)*(char**(n/char.degree())) / norm + self._frobenius_norm = (-1) ** (n*r - n - r) \ + * norm**(-1) \ + * p ** (n//p.degree()) return self._frobenius_norm - def frobenius_trace(self): + def frobenius_trace(self, algorithm=None): r""" Return the Frobenius trace of the Drinfeld module. @@ -527,44 +767,145 @@ def frobenius_trace(self): and if `n` is the degree of the base field over `\mathbb{F}_q`, then the Frobenius trace has degree at most `\frac{n}{r}`. + INPUT: + + - ``algorithm`` (default: ``None``) -- the algorithm + used to compute the characteristic polynomial + + .. NOTE: + + Available algorithms are: + + - ``'CSA'`` -- it exploits the fact that `K\{\tau\}` is a + central simple algebra (CSA) over `\mathbb + F_q[\text{Frob}_\phi]` (see Chapter 4 of [CL2023]_). + - ``'crystalline'`` -- it uses the action of the Frobenius + on the crystalline cohomology (see [MS2023]_). + + The method raises an exception if the user asks for an + unimplemented algorithm, even if the characteristic has already + been computed. See :meth:`frobenius_charpoly` for more details. + EXAMPLES:: sage: Fq = GF(343) sage: A. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) - sage: A = phi.frobenius_trace() - sage: A + sage: frobenius_trace = phi.frobenius_trace() + sage: frobenius_trace (4*z3^2 + 6*z3 + 3)*T + 3*z3^2 + z3 + 4 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 + sage: frobenius_trace.degree() <= n/2 True :: - sage: A == -phi.frobenius_charpoly()[1] + sage: frobenius_trace == -phi.frobenius_charpoly()[1] True + One can specify an algorithm:: + + sage: psi = DrinfeldModule(A, [z6, 1, z6^3, z6 + 1]) + sage: psi.frobenius_trace(algorithm='crystalline') + z3^2 + 6*z3 + 2 + sage: psi.frobenius_trace(algorithm='CSA') + z3^2 + 6*z3 + 2 + ALGORITHM: - We extract the coefficient of `X^{r-1}` from the characteristic - polynomial if it has been previously computed, otherwise we compute - the trace of the matrix of the Frobenius acting on the crystalline - cohomology. + If the user specifies an algorithm, then the trace is + computed according to the user's input (see the note above), + even if the Frobenius trace or the Frobenius characteristic + polynomial had already been computed. + + If no algorithm is given, then the function either returns a + cached value, or if no cached value is available, the + function computes the Frobenius trace from scratch. In that + case, if the rank `r` is less than the extension degree `n`, + then the ``crystalline`` algorithm is used, while the + ``CSA`` algorithm is used otherwise. + + TESTS: + + These test values are taken from :meth:`frobenius_charpoly`:: + + sage: Fq = GF(9) + sage: A. = Fq[] + sage: k. = Fq.extension(2) + sage: K. = k.extension(3) + + :: + + sage: phi = DrinfeldModule(A, [K(zk), z^8, z^7]) + sage: phi.frobenius_trace(algorithm='CSA') + T^3 + (z2 + 1)*T^2 + (2*z2 + 2)*T + z2 + sage: phi.frobenius_trace(algorithm='crystalline') + T^3 + (z2 + 1)*T^2 + (2*z2 + 2)*T + z2 + sage: charpoly = phi.frobenius_charpoly() + sage: trace = phi.frobenius_trace() + sage: trace == -charpoly[1] + True + + :: + + sage: phi = DrinfeldModule(A, [K(zk), z^2, z^3, z^4]) + sage: phi.frobenius_trace(algorithm='CSA') + (2*z2 + 2)*T^2 + (2*z2 + 2)*T + 2*z2 + 1 + sage: phi.frobenius_trace(algorithm='crystalline') + (2*z2 + 2)*T^2 + (2*z2 + 2)*T + 2*z2 + 1 + sage: charpoly = phi.frobenius_charpoly() + sage: trace = phi.frobenius_trace() + sage: trace == -charpoly[2] + True + + :: + + sage: phi = DrinfeldModule(A, [K(zk), z^8, z^7, z^20]) + sage: phi.frobenius_trace(algorithm='CSA') + 2*z2*T^2 + 2*z2*T + 2*z2 + 2 + sage: phi.frobenius_trace(algorithm='crystalline') + 2*z2*T^2 + 2*z2*T + 2*z2 + 2 + sage: charpoly = phi.frobenius_charpoly() + sage: trace = phi.frobenius_trace() + sage: trace == -charpoly[2] + True """ - K = self.base_over_constants_field() - A = self.function_ring() - if self._frobenius_trace is not None: + if algorithm is None: + # If no algorithm is specified, return cached data (if it + # exists), or pick an algorithm: + # However, if an algorithm is specified, do not use cached + # data, even if it is possible + if self._frobenius_trace is not None: + return self._frobenius_trace + if self._frobenius_charpoly is not None: + self._frobenius_trace = -self._frobenius_charpoly \ + .coefficients(sparse=False)[-2] + return self._frobenius_trace + else: + if self.rank() < self._base_degree_over_constants: + algorithm = 'crystalline' + else: + algorithm = 'CSA' + # We first check if a matrix method is available + matrix_method_name = f'_frobenius_matrix_{algorithm}' + if hasattr(self, matrix_method_name): + matrix = getattr(self, matrix_method_name)() + trace = matrix.trace() + A = self.function_ring() + self._frobenius_trace = A([x.in_base() for x in trace]) + return self._frobenius_trace + # If it is not, we look for a charpoly method + charpoly_method_name = f'_frobenius_charpoly_{algorithm}' + if hasattr(self, charpoly_method_name): + charpoly = getattr(self, charpoly_method_name)() + self._frobenius_trace = -charpoly.list()[-2] return self._frobenius_trace - if self._frobenius_charpoly is not None: - self._frobenius_trace = -self._frobenius_charpoly \ - .coefficients(sparse=False)[-2] - self._frobenius_trace = self._frobenius_crystalline_matrix().trace() - self._frobenius_trace = A([x.in_base() for x in self._frobenius_trace]) - return self._frobenius_trace + # If all fail, we raise an error + raise NotImplementedError(f'algorithm "{algorithm}" not implemented') def invert(self, ore_pol): r""" @@ -657,7 +998,6 @@ def invert(self, ore_pol): # Write the system and solve it k = deg // r A = self._function_ring - T = A.gen() mat_rows = [[E.zero() for _ in range(k+1)] for _ in range(k+1)] mat_rows[0][0] = E.one() phiT = self.gen() diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index d198f2694a4..2863732cf3b 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -45,6 +45,8 @@ AUTHORS: - Maarten Derickx (2011-09-11): added doctests, fixed pickling - Kwankyu Lee (2017-04-30): added elements for global function fields + +- Vincent Macri (2024-09-03): added subs method """ # ***************************************************************************** # Copyright (C) 2010 William Stein @@ -57,6 +59,7 @@ AUTHORS: # 2018-2020 Travis Scrimshaw # 2019 Brent Baccala # 2021 Saher Amasha +# 2024 Vincent Macri # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -174,6 +177,269 @@ cdef class FunctionFieldElement(FieldElement): """ return self._x._latex_() + def subs(self, in_dict=None, **kwds): + r""" + Substitute the given generators with given values while not touching + other generators. + + INPUT: + + - ``in_dict`` -- (optional) dictionary of inputs + + - ``**kwds`` -- named parameters + + OUTPUT: new object if substitution is possible, otherwise ``self`` + + EXAMPLES: + + Basic substitution:: + + sage: K = GF(7) + sage: Kx. = FunctionField(K) + sage: y = polygen(Kx) + sage: f = x^6 + 3; f + x^6 + 3 + + We also substitute the generators in any base fields:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: S. = L[] + sage: M. = L.extension(t^2 - x*y) + sage: f = 7 * t + 3*x*y + sage: f.subs(t=9) + 3*x*y + 63 + sage: f.subs(x=2, y=4) + 7*t + 24 + sage: f.subs(t=1, x=2, y=3) + 25 + + Because of the possibility of extension fields, a generator to + substitute must be specified:: + + sage: K. = FunctionField(QQ) + sage: f = x + sage: f.subs(2) + Traceback (most recent call last): + ... + TypeError: in_dict must be a dict + + sage: # needs sage.rings.function_field + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: f = x + y + sage: f.subs(0) + Traceback (most recent call last): + ... + TypeError: in_dict must be a dict + + We can also substitute using dictionary syntax:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: S. = L[] + sage: M. = L.extension(t^2 - x*y) + sage: f = x + y + t + sage: f.subs({x: 1, y: 3, t: 4}) + 8 + sage: f.subs({x: 1, t: 4}) + y + 5 + + TESTS: + + Check that we correctly handle extension fields:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: S. = L[] + sage: M. = L.extension(t^2 - x*y) + sage: f = t + x*y + sage: f.subs(x=1, y=3, t=5) + 8 + sage: f_sub = f.subs(x=1); f_sub + t + y + sage: f_sub.parent() == f.parent() + True + sage: f.subs(y=2) + t + 2*x + sage: f_sub = f.subs(x=1, y=1, t=1); f_sub + 2 + sage: f_sub.parent() == M + True + + Test that substitution works for rational functions:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^4 - 3) + sage: f = x / y + sage: f.subs(x=2) == 2 / y + True + sage: f.subs(y=3) + 9*x + sage: f.subs(t=-1) is f + True + sage: f.subs({x: 2, y: 4}) + 128/3 + + Make sure that we return the same object when there is no + substitution:: + + sage: K = GF(7) + sage: Kx. = FunctionField(K) + sage: y = polygen(Kx) + sage: f = x^6 + 3 + sage: g = f.subs(z=2) + sage: g == f + True + sage: g is f + True + + Same purpose as above but over an extension field over the rationals:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: S. = L[] + sage: M. = L.extension(t^2 - x*y) + sage: f = t + x*y + sage: f.subs() is f + True + sage: f.subs(w=7) is f + True + sage: f.subs(w=7) is f.subs(w=7) + True + sage: f.subs(y=y) is f + True + sage: f.subs({y: y}) is f + True + sage: f.subs(x=x, y=y, t=t) is f + True + + Test proper handling of not making substitutions:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: f = x + sage: f.subs() is f + True + sage: f.subs(dict()) is f + True + sage: f.subs(w=0) is f + True + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: f = 3*y + sage: f.subs(x=0) + 3*y + sage: f = 3*y + sage: f.subs(x=0, y=y) + 3*y + + Test error handling for wrong argument type:: + + sage: K. = FunctionField(QQ) + sage: f = x + sage: f.subs(0) + Traceback (most recent call last): + ... + TypeError: in_dict must be a dict + + Test error handling for dictionary with keys that don't match + generators:: + + sage: K. = FunctionField(QQ) + sage: f = x + sage: f.subs({1: 1}) + Traceback (most recent call last): + ... + TypeError: key does not match any field generators + + Test error handling with ambiguously named generators:: + + sage: # needs sage.rings.function_field + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(x^3 - x) + sage: str(L.gen()) == str(K.gen()) + True + sage: f = K.gen() - L.gen() + sage: f.subs(x=2) + Traceback (most recent call last): + ... + TypeError: multiple generators have the same name, making substitution ambiguous. Rename generators or pass substitution values in using dictionary format + sage: f.subs({K.gen(): 1}) + -x + 1 + sage: f.subs({L.gen(): 2}) + x - 2 + sage: f.subs({K.gen(): 1, L.gen(): 2}) + -1 + sage: f.subs({K.gen(): 2, L.gen(): 1}) + 1 + """ + def sub_recurse(ff_element, sub_dict): + # Helper method to recurse through base fields. + ff = ff_element.parent() + if ff.base_field() == ff: + return ff(ff_element._x.subs({ff.gen(): sub_dict[ff.gen()]})) + total = ff.zero() + for i, v in enumerate(list(ff_element._x)): + total += sub_recurse(v, sub_dict) * sub_dict[ff.gen()]**i + return ff(total) + + if in_dict is None and kwds is None: + return self + + if in_dict is not None and not isinstance(in_dict, dict): + raise TypeError('in_dict must be a dict') + + field_tower = [self.parent()] + ff = self.parent() + + while ff.base_field() != ff: + ff = ff.base_field() + field_tower.append(ff) + sub_dict = {f.gen(): f.gen() for f in field_tower} + + made_substitution = False + if in_dict is not None: + for k, v in in_dict.items(): + if k not in sub_dict: + raise TypeError('key does not match any field generators') + sub_dict[k] = v + if v != k: + made_substitution = True + else: + used_kwds = {k: False for k in kwds} + for g in sub_dict: + strg = str(g) + if strg not in kwds: + continue + v = kwds[strg] + sub_dict[g] = v + + if used_kwds[strg]: + raise TypeError('multiple generators have the ' + 'same name, making substitution ' + 'ambiguous. Rename generators ' + 'or pass substitution values in ' + 'using dictionary format') + used_kwds[strg] = True + if g != v: + made_substitution = True + + if made_substitution: + return sub_recurse(self, sub_dict) + return self + @cached_method def matrix(self, base=None): r""" @@ -248,7 +514,7 @@ cdef class FunctionFieldElement(FieldElement): # with this element; make matrix whose rows are the coefficients of the # result, and transpose V, f, t = self.parent().vector_space(base) - rows = [ t(self*f(b)) for b in V.basis() ] + rows = [t(self*f(b)) for b in V.basis()] from sage.matrix.matrix_space import MatrixSpace MS = MatrixSpace(V.base_field(), V.dimension()) ret = MS(rows) @@ -326,7 +592,7 @@ cdef class FunctionFieldElement(FieldElement): sage: f.degree() 1 """ - return max(self._x.denominator().degree(),self._x.numerator().degree()) + return max(self._x.denominator().degree(), self._x.numerator().degree()) def characteristic_polynomial(self, *args, **kwds): """ @@ -681,7 +947,7 @@ cdef class FunctionFieldElement(FieldElement): v = self.valuation(place) if v > 0: return R.zero() - if v == 0: + if v == 0: return to_R(self) # v < 0 raise ValueError('has a pole at the place') diff --git a/src/sage/rings/function_field/function_field_polymod.py b/src/sage/rings/function_field/function_field_polymod.py index de6cbf47a6c..0fe4c84fafb 100644 --- a/src/sage/rings/function_field/function_field_polymod.py +++ b/src/sage/rings/function_field/function_field_polymod.py @@ -571,7 +571,21 @@ def _repr_(self): sage: L._repr_() 'Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x' """ - return "Function field in %s defined by %s" % (self.variable_name(), self._polynomial) + return f"Function field in {self.variable_name()} defined by {self._polynomial}" + + def _latex_(self): + r""" + Return the LaTeX representation of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: latex(L) + \text{Function field in } y \text{ defined by } y^{5} - 2 x y + \frac{-x^{4} - 1}{x} + """ + return (fr"\text{{Function field in }} {self.variable_name()} " + fr"\text{{ defined by }} {self._polynomial._latex_()}") def base_field(self): """ diff --git a/src/sage/rings/function_field/meson.build b/src/sage/rings/function_field/meson.build new file mode 100644 index 00000000000..16e5fa6fa6f --- /dev/null +++ b/src/sage/rings/function_field/meson.build @@ -0,0 +1,55 @@ +py.install_sources( + 'all.py', + 'constructor.py', + 'derivations.py', + 'derivations_polymod.py', + 'derivations_rational.py', + 'differential.py', + 'divisor.py', + 'element.pxd', + 'extensions.py', + 'function_field.py', + 'function_field_polymod.py', + 'function_field_rational.py', + 'ideal.py', + 'ideal_polymod.py', + 'ideal_rational.py', + 'jacobian_base.py', + 'jacobian_hess.py', + 'jacobian_khuri_makdisi.py', + 'maps.py', + 'order.py', + 'order_basis.py', + 'order_polymod.py', + 'order_rational.py', + 'place.py', + 'place_polymod.py', + 'place_rational.py', + 'valuation.py', + 'valuation_ring.py', + subdir: 'sage/rings/function_field', +) + +extension_data = { + 'element' : files('element.pyx'), + 'element_polymod' : files('element_polymod.pyx'), + 'element_rational' : files('element_rational.pyx'), + 'hermite_form_polynomial' : files('hermite_form_polynomial.pyx'), + 'khuri_makdisi' : files('khuri_makdisi.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/function_field', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + +install_subdir( + 'drinfeld_modules', + install_dir: sage_install_dir / 'rings/function_field', +) diff --git a/src/sage/rings/function_field/order_basis.py b/src/sage/rings/function_field/order_basis.py index e4fa3dfe39e..1cf89e3f966 100644 --- a/src/sage/rings/function_field/order_basis.py +++ b/src/sage/rings/function_field/order_basis.py @@ -102,7 +102,7 @@ def __init__(self, basis, check=True): if check: if self._module.rank() != field.degree(): raise ValueError("basis {} is not linearly independent".format(basis)) - if not to_V(field(1)) in self._module: + if to_V(field(1)) not in self._module: raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) if not all(to_V(a*b) in self._module for a in basis for b in basis): raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) @@ -399,7 +399,7 @@ def __init__(self, basis, check=True): if check: if self._module.rank() != field.degree(): raise ValueError("basis {} is not linearly independent".format(basis)) - if not W.coordinate_vector(to(field(1))) in self._module: + if W.coordinate_vector(to(field(1))) not in self._module: raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) if not all(W.coordinate_vector(to(a*b)) in self._module for a in basis for b in basis): raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) @@ -431,7 +431,7 @@ def _element_constructor_(self, f): V, fr_V, to_V = F.vector_space() W = self._ambient_space - if not W.coordinate_vector(to_V(f)) in self._module: + if W.coordinate_vector(to_V(f)) not in self._module: raise TypeError("{} is not an element of {}".format(f, self)) return f diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 90fa9fe71ce..16ff2d8f2ae 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -74,9 +74,8 @@ def __init__(self, field, ideal_class=FunctionFieldIdeal_polymod): n = len(basis) self._mtable = [] for i in range(n): - row = [] - for j in range(n): - row.append(self._coordinate_vector(basis[i] * basis[j])) + row = [self._coordinate_vector(basis[i] * basis[j]) + for j in range(n)] self._mtable.append(row) zero = vector(R._ring, n * [0]) @@ -643,12 +642,12 @@ def decomposition(self, ideal): # the basis of this order, construct the n n-by-n matrices that show # how to multiply by each of the basis elements. matrices = [matrix(o, [self.coordinate_vector(b1*b2) for b1 in self.basis()]) - for b2 in self.basis()] + for b2 in self.basis()] # Let O denote the maximal order self. When reduced modulo p, # matrices_reduced give the multiplication matrices used to form the # algebra O mod pO. - matrices_reduced = list(map(lambda M: M.mod(p), matrices)) + matrices_reduced = [M.mod(p) for M in matrices] cat = CommutativeAlgebras(k).FiniteDimensional().WithBasis() A = FiniteDimensionalAlgebra(k, matrices_reduced, assume_associative=True, @@ -889,7 +888,7 @@ def ideal(self, *gens): """ if len(gens) == 1: gens = gens[0] - if not type(gens) in (list,tuple): + if type(gens) not in (list,tuple): gens = (gens,) mgens = [g * b for g in gens for b in self._basis] return self.ideal_with_gens_over_base(mgens) @@ -1316,7 +1315,7 @@ def decomposition(self, ideal): Ip = self.p_radical(ideal) Ob = matrix.identity(Fp, n) - def bar(I): # transfer to O/pO + def bar(I): # transfer to O/pO m = [] for v in I._hnf: m.append([to(e) for e in v]) @@ -1325,8 +1324,8 @@ def bar(I): # transfer to O/pO def liftb(Ib): m = [vector([fr(e) for e in v]) for v in Ib] - m += [v for v in pO._hnf] - return self._ideal_from_vectors_and_denominator(m,1) + m.extend(pO._hnf) + return self._ideal_from_vectors_and_denominator(m, 1) def cut_last_zero_rows(h): i = h.nrows() @@ -1334,17 +1333,17 @@ def cut_last_zero_rows(h): i -= 1 return h[:i] - def mul_vec(v1,v2): + def mul_vec(v1, v2): s = 0 for i in range(n): for j in range(n): s += v1[i] * v2[j] * mtable[i][j] return s - def pow(v, r): # r > 0 + def pow(v, r): # r > 0 m = v while r > 1: - m = mul_vec(m,v) + m = mul_vec(m, v) r -= 1 return m diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py index 87cbbdb4858..d90124c6800 100644 --- a/src/sage/rings/function_field/order_rational.py +++ b/src/sage/rings/function_field/order_rational.py @@ -83,7 +83,7 @@ def _element_constructor_(self, f): except TypeError: raise TypeError("unable to convert to an element of {}".format(F)) - if not f.denominator() in self.function_field().constant_base_field(): + if f.denominator() not in self.function_field().constant_base_field(): raise TypeError("%r is not an element of %r" % (f,self)) return f diff --git a/src/sage/rings/function_field/valuation.py b/src/sage/rings/function_field/valuation.py index a192a76beb6..e92600e55b6 100644 --- a/src/sage/rings/function_field/valuation.py +++ b/src/sage/rings/function_field/valuation.py @@ -157,6 +157,7 @@ from sage.rings.valuation.trivial_valuation import TrivialValuation from sage.rings.valuation.mapped_valuation import FiniteExtensionFromLimitValuation, MappedValuation_base + class FunctionFieldValuationFactory(UniqueFactory): r""" Create a valuation on ``domain`` corresponding to ``prime``. diff --git a/src/sage/rings/function_field/valuation_ring.py b/src/sage/rings/function_field/valuation_ring.py index e1fe9751b0b..9269d21ea2d 100644 --- a/src/sage/rings/function_field/valuation_ring.py +++ b/src/sage/rings/function_field/valuation_ring.py @@ -72,6 +72,7 @@ from sage.categories.homset import Hom from sage.categories.rings import Rings + class FunctionFieldValuationRing(UniqueRepresentation, Parent): """ Base class for valuation rings of function fields. diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 4208c63c55c..dc785891828 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1457,6 +1457,7 @@ def divides(self, other): return self.gen().divides(other.gen()) raise NotImplementedError + class Ideal_pid(Ideal_principal): """ An ideal of a principal ideal domain. @@ -1694,6 +1695,7 @@ def residue_field(self): return ZZ.residue_field(self, check=False) raise NotImplementedError("residue_field() is only implemented for ZZ and rings of integers of number fields.") + class Ideal_fractional(Ideal_generic): """ Fractional ideal of a ring. @@ -1717,6 +1719,7 @@ def _repr_(self): # constructors for standard (benchmark) ideals, written uppercase as # these are constructors + def Cyclic(R, n=None, homog=False, singular=None): """ Ideal of cyclic ``n``-roots from 1-st ``n`` variables of ``R`` if ``R`` is @@ -1782,6 +1785,7 @@ def Cyclic(R, n=None, homog=False, singular=None): I = singular.cyclic(n).homog(R2.gen(n-1)) return R2.ideal(I).change_ring(R) + def Katsura(R, n=None, homog=False, singular=None): r""" `n`-th katsura ideal of `R` if `R` is coercible to @@ -1832,6 +1836,7 @@ def Katsura(R, n=None, homog=False, singular=None): I = singular.katsura(n).homog(R2.gen(n-1)) return R2.ideal(I).change_ring(R) + def FieldIdeal(R): r""" Let ``q = R.base_ring().order()`` and `(x_0,...,x_n)` ``= R.gens()`` then diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 11811b9587f..9616e7946bc 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -3278,7 +3278,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): def euclidean_degree(self): r""" - Return the degree of this element as an element of an Euclidean domain. + Return the degree of this element as an element of a Euclidean domain. If this is an element in the ring of integers, this is simply its absolute value. diff --git a/src/sage/rings/invariants/invariant_theory.py b/src/sage/rings/invariants/invariant_theory.py index dc370bdf560..fb953f98813 100644 --- a/src/sage/rings/invariants/invariant_theory.py +++ b/src/sage/rings/invariants/invariant_theory.py @@ -3959,7 +3959,7 @@ def syzygy(self, Delta, Theta, Phi, Theta_prime, Delta_prime, U, V, T, T_prime, ###################################################################### -class InvariantTheoryFactory(): +class InvariantTheoryFactory: """ Factory object for invariants of multilinear forms. diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index f34188ba764..b48e0e6bc38 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -476,7 +476,7 @@ def coefficients(self, n=None): return lazy_list(coeffs) # flatten out the generator in the multivariate case - return lazy_list(chain.from_iterable((coeff.coefficients() for coeff in coeffs))) + return lazy_list(chain.from_iterable(coeff.coefficients() for coeff in coeffs)) if isinstance(self, LazyPowerSeries) and self.parent()._arity == 1: from sage.misc.superseded import deprecation @@ -486,7 +486,7 @@ def coefficients(self, n=None): return list(islice(coeffs, n)) # flatten out the generator in the multivariate case - return list(islice(chain.from_iterable((coeff.coefficients() for coeff in coeffs)), n)) + return list(islice(chain.from_iterable(coeff.coefficients() for coeff in coeffs), n)) def map_coefficients(self, f): r""" @@ -4866,6 +4866,7 @@ def _format_series(self, formatter, format_strings=False): return strformat("O({})".format(formatter(z**m))) return formatter(poly) + strformat(" + O({})".format(formatter(z**m))) + class LazyPowerSeries(LazyCauchyProductSeries): r""" A Taylor series where the coefficients are computed lazily. @@ -6149,6 +6150,7 @@ def _floordiv_(self, other): left._approximate_order += 1 return super()._floordiv_(other) + class LazyPowerSeries_gcd_mixin: """ A lazy power series that also implements the GCD algorithm. @@ -6266,6 +6268,7 @@ def xgcd(self, f): unit = ~unit return (x**val, unit, unit) + class LazyCompletionGradedAlgebraElement(LazyCauchyProductSeries): """ An element of a completion of a graded algebra that is computed lazily. diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 2e54ce22cd0..a5900982a3a 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -93,6 +93,7 @@ from types import GeneratorType + class LazySeriesRing(UniqueRepresentation, Parent): """ Abstract base class for lazy series. @@ -1233,6 +1234,7 @@ def _test_revert(self, **options): # we want to test at least 2 elements tester.assertGreater(count, 1, msg="only %s elements in %s.some_elements() have a compositional inverse" % (count, self)) + class LazyLaurentSeriesRing(LazySeriesRing): r""" The ring of lazy Laurent series. @@ -1488,10 +1490,14 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: TestSuite(L).run() # needs sage.libs.singular sage: L.category() Category of infinite commutative no zero divisors algebras over - (unique factorization domains and commutative algebras over + (unique factorization domains and algebras with basis over (Dedekind domains and euclidean domains - and noetherian rings - and infinite enumerated sets and metric spaces) + and noetherian rings + and infinite enumerated sets and metric spaces) + and commutative algebras over + (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) and infinite sets) sage: L = LazyLaurentSeriesRing(GF(5), 't') @@ -2608,7 +2614,6 @@ def taylor(self, f): BR = R.base_ring() args = f.arguments() subs = {str(va): ZZ.zero() for va in args} - gens = R.gens() ell = len(subs) from sage.combinat.integer_vector import integer_vectors_nk_fast_iter from sage.arith.misc import factorial @@ -3036,6 +3041,7 @@ def some_elements(self): ###################################################################### + class LazySymmetricFunctions(LazyCompletionGradedAlgebra): """ The ring of lazy symmetric functions. @@ -3413,6 +3419,7 @@ def _monomial(self, c, n): except (ValueError, TypeError): return '({})/{}^{}'.format(self.base_ring()(c), n, self.variable_name()) + def _skip_leading_zeros(iterator): """ Return an iterator which discards all leading zeros. diff --git a/src/sage/rings/meson.build b/src/sage/rings/meson.build new file mode 100644 index 00000000000..9aff2d7c8f3 --- /dev/null +++ b/src/sage/rings/meson.build @@ -0,0 +1,217 @@ +pthread = cc.find_library('pthread') + +py.install_sources( + 'abc.pxd', + 'algebraic_closure_finite_field.py', + 'all.py', + 'all__sagemath_categories.py', + 'all__sagemath_objects.py', + 'big_oh.py', + 'cc.py', + 'cfinite_sequence.py', + 'cif.py', + 'commutative_algebra.py', + 'complex_arb.pxd', + 'complex_conversion.pxd', + 'complex_double.pxd', + 'complex_interval.pxd', + 'complex_interval_field.py', + 'complex_mpc.pxd', + 'complex_mpfr.pxd', + 'continued_fraction.py', + 'continued_fraction_gosper.py', + 'derivation.py', + 'fast_arith.pxd', + 'fraction_field.py', + 'fraction_field_FpT.pxd', + 'generic.py', + 'homset.py', + 'ideal.py', + 'ideal_monoid.py', + 'imaginary_unit.py', + 'infinity.py', + 'integer.pxd', + 'integer.pyx', + 'integer_fake.h', + 'integer_fake.pxd', + 'integer_ring.pxd', + 'integer_ring.pyx', + 'laurent_series_ring.py', + 'laurent_series_ring_element.pxd', + 'lazy_series.py', + 'lazy_series_ring.py', + 'localization.py', + 'monomials.py', + 'morphism.pxd', + 'multi_power_series_ring.py', + 'multi_power_series_ring_element.py', + 'numbers_abc.py', + 'pari_ring.py', + 'power_series_mpoly.pxd', + 'power_series_pari.pxd', + 'power_series_poly.pxd', + 'power_series_ring.py', + 'power_series_ring_element.pxd', + 'puiseux_series_ring.py', + 'puiseux_series_ring_element.pxd', + 'qqbar.py', + 'qqbar_decorators.py', + 'quotient_ring.py', + 'quotient_ring_element.py', + 'rational.pxd', + 'rational.pyx', + 'rational_field.py', + 'real_arb.pxd', + 'real_double.pxd', + 'real_double_element_gsl.pxd', + 'real_field.py', + 'real_lazy.pxd', + 'real_mpfi.pxd', + 'real_mpfr.pxd', + 'ring.pxd', + 'ring_extension.pxd', + 'ring_extension_conversion.pxd', + 'ring_extension_element.pxd', + 'ring_extension_homset.py', + 'ring_extension_morphism.pxd', + 'sum_of_squares.pxd', + 'tate_algebra.py', + 'tate_algebra_element.pxd', + 'tate_algebra_ideal.pxd', + 'tests.py', + 'universal_cyclotomic_field.py', + subdir: 'sage/rings', +) + +extension_data = { + 'abc' : files('abc.pyx'), + 'complex_arb' : files('complex_arb.pyx'), + 'complex_conversion' : files('complex_conversion.pyx'), + 'complex_double' : files('complex_double.pyx'), + 'complex_interval' : files('complex_interval.pyx'), + 'complex_mpc' : files('complex_mpc.pyx'), + 'complex_mpfr' : files('complex_mpfr.pyx'), + 'factorint' : files('factorint.pyx'), + 'factorint_flint' : files('factorint_flint.pyx'), + 'factorint_pari' : files('factorint_pari.pyx'), + 'fast_arith' : files('fast_arith.pyx'), + 'fraction_field_element' : files('fraction_field_element.pyx'), + 'integer' : files('integer.pyx'), + 'integer_ring' : files('integer_ring.pyx'), + 'laurent_series_ring_element' : files('laurent_series_ring_element.pyx'), + 'morphism' : files('morphism.pyx'), + 'noncommutative_ideals' : files('noncommutative_ideals.pyx'), + 'power_series_mpoly' : files('power_series_mpoly.pyx'), + 'power_series_pari' : files('power_series_pari.pyx'), + 'power_series_poly' : files('power_series_poly.pyx'), + 'power_series_ring_element' : files('power_series_ring_element.pyx'), + 'puiseux_series_ring_element' : files('puiseux_series_ring_element.pyx'), + 'real_arb' : files('real_arb.pyx'), + 'real_double' : files('real_double.pyx'), + 'real_double_element_gsl' : files('real_double_element_gsl.pyx'), + 'real_interval_absolute' : files('real_interval_absolute.pyx'), + 'real_lazy' : files('real_lazy.pyx'), + 'real_mpfi' : files('real_mpfi.pyx'), + 'real_mpfr' : files('real_mpfr.pyx'), + 'ring' : files('ring.pyx'), + 'ring_extension' : files('ring_extension.pyx'), + 'ring_extension_conversion' : files('ring_extension_conversion.pyx'), + 'ring_extension_element' : files('ring_extension_element.pyx'), + 'ring_extension_morphism' : files('ring_extension_morphism.pyx'), + 'sum_of_squares' : files('sum_of_squares.pyx'), + 'tate_algebra_element' : files('tate_algebra_element.pyx'), + 'tate_algebra_ideal' : files('tate_algebra_ideal.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_gsl, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + flint, + gmp, + gmpy2, + gsl, + m, + mpc, + mpfi, + mpfr, + ntl, + pari, + pthread, + ], + ) +endforeach + +extension_data_cpp = { + 'bernmm': files( + 'bernmm.pyx', + 'bernmm/bern_modp.cpp', + 'bernmm/bern_modp_util.cpp', + 'bernmm/bern_rat.cpp', + ), + 'bernoulli_mod_p': files('bernoulli_mod_p.pyx'), + 'fraction_field_FpT': files('fraction_field_FpT.pyx'), + 'rational': files('rational.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings', + install: true, + cpp_args: ['-DUSE_THREADS=1', '-DTHREAD_STACK_SIZE=4096'], + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_gsl, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + flint, + gmp, + gmpy2, + gsl, + m, + mpc, + mpfi, + mpfr, + ntl, + pari, + pthread, + ], + ) +endforeach + +install_subdir('asymptotic', install_dir: sage_install_dir / 'rings') +subdir('convert') +subdir('finite_rings') +subdir('function_field') +install_subdir('invariants', install_dir: sage_install_dir / 'rings') +subdir('number_field') +subdir('padics') +subdir('polynomial') +subdir('semirings') +install_subdir('valuation', install_dir: sage_install_dir / 'rings') diff --git a/src/sage/rings/monomials.py b/src/sage/rings/monomials.py index ed154ab8c06..b10d599b6b3 100644 --- a/src/sage/rings/monomials.py +++ b/src/sage/rings/monomials.py @@ -1,5 +1,6 @@ "Monomials" + def _monomials(gens, R, n, i): """ Given two lists ``gens`` and ``n`` of exactly the same length, diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 68ee2d5ee8e..3079bfa3974 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2277,7 +2277,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): """ P = self.codomain() try: - return P(dict([(a, self._underlying(b)) for a,b in x.dict().items()])) + return P({a: self._underlying(b) for a, b in x.monomial_coefficients().items()}) except Exception: pass try: @@ -2879,8 +2879,10 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): over Finite Field of size 5 """ from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings from sage.categories.homset import Hom - if not isinstance(domain, CommutativeRing): + if not (domain in CommutativeRings() or + isinstance(domain, CommutativeRing)): # TODO: remove this line raise TypeError("The base ring must be a commutative ring") self._p = domain.characteristic() if not self._p.is_prime(): diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 89fa8548f5d..35c4d4beb32 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -213,8 +213,8 @@ from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base from sage.rings.polynomial.term_order import TermOrder from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic -from sage.rings.ring import CommutativeRing from sage.structure.nonexact import Nonexact +from sage.structure.parent import Parent from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() @@ -389,8 +389,9 @@ def __init__(self, base_ring, num_gens, name_list, # Multivariate power series rings inherit from power series rings. But # apparently we can not call their initialisation. Instead, initialise # CommutativeRing and Nonexact: - CommutativeRing.__init__(self, base_ring, name_list, category=_IntegralDomains if base_ring in - _IntegralDomains else _CommutativeRings) + Parent.__init__(self, base=base_ring, names=name_list, + category=_IntegralDomains if base_ring in + _IntegralDomains else _CommutativeRings) Nonexact.__init__(self, default_prec) # underlying polynomial ring in which to represent elements @@ -1005,7 +1006,7 @@ def gen(self, n=0): if n < 0 or n >= self._ngens: raise ValueError("Generator not defined.") #return self(self._poly_ring().gens()[int(n)]) - return self.element_class(parent=self,x=self._poly_ring().gens()[int(n)], is_gen=True) + return self.element_class(parent=self, x=self._poly_ring().gens()[int(n)], is_gen=True) def ngens(self): """ @@ -1019,6 +1020,18 @@ def ngens(self): """ return self._ngens + def gens(self) -> tuple: + """ + Return the generators of this ring. + + EXAMPLES:: + + sage: M = PowerSeriesRing(ZZ, 3, 'v') + sage: M.gens() + (v0, v1, v2) + """ + return tuple(self.gen(i) for i in range(self._ngens)) + def prec_ideal(self): """ Return the ideal which determines precision; this is the ideal diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index b7731d59f74..8e1789d5672 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -567,7 +567,7 @@ def _subs_formal(self, *x, **kwds): base_map = kwds.get('base_map') if base_map is None: base_map = lambda t: t - for m, c in self.dict().items(): + for m, c in self.monomial_coefficients().items(): y += base_map(c)*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) if self.prec() == infinity: return y @@ -1099,7 +1099,7 @@ def __mod__(self, other): return self.change_ring(Zmod(other)) raise NotImplementedError("Mod on multivariate power series ring elements not defined except modulo an integer.") - def dict(self): + def monomial_coefficients(self): """ Return underlying dictionary with keys the exponents and values the coefficients of this power series. @@ -1116,6 +1116,14 @@ def dict(self): sage: m = 2/3*t0*t1^15*t3^48 - t0^15*t1^21*t2^28*t3^5 sage: m2 = 1/2*t0^12*t1^29*t2^46*t3^6 - 1/4*t0^39*t1^5*t2^23*t3^30 + M.O(100) sage: s = m + m2 + sage: s.monomial_coefficients() + {(1, 15, 0, 48): 2/3, + (12, 29, 46, 6): 1/2, + (15, 21, 28, 5): -1, + (39, 5, 23, 30): -1/4} + + ``dict`` is an alias:: + sage: s.dict() {(1, 15, 0, 48): 2/3, (12, 29, 46, 6): 1/2, @@ -1124,9 +1132,11 @@ def dict(self): """ out_dict = {} for j in self._bg_value.coefficients(): - out_dict.update(j.dict()) + out_dict.update(j.monomial_coefficients()) return out_dict + dict = monomial_coefficients + def polynomial(self): """ Return the underlying polynomial of ``self`` as an element of @@ -1224,7 +1234,7 @@ def coefficients(self): True """ if self.is_sparse(): - return self.dict() + return self.monomial_coefficients() tmp = {} for j in self._bg_value.coefficients(): for m in j.monomials(): @@ -1727,7 +1737,7 @@ def _integral(self, xx): xxe = xx.exponents()[0] pos = [i for i, c in enumerate(xxe) if c != 0][0] # get the position of the variable res = {mon.eadd(xxe): R(co / (mon[pos]+1)) - for mon, co in self.dict().items()} + for mon, co in self.monomial_coefficients().items()} return P( res ).add_bigoh(self.prec()+1) def ogf(self): @@ -2095,7 +2105,7 @@ def laurent_series(self): raise NotImplementedError("laurent_series not defined for multivariate power series.") -class MO(): +class MO: """ Object representing a zero element with given precision. diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 74bdd6e246c..0ffac369720 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -1677,8 +1677,7 @@ def p_adic_LLL_bound(SUK, A, prec=106): Log_p_Mus = [embedding_to_Kp(a, v, local_prec) for a in Log_p_Mus] m0_Kv_new,increase_precision = p_adic_LLL_bound_one_prime(v, m0_Kv_old, Mus, Log_p_Mus, m0, c3_func(SUK, local_prec), local_prec) - if m0_Kv_old > val: - val = m0_Kv_old + val = max(m0_Kv_old, val) LLL_K0_by_finite_place.append(val) return max(LLL_K0_by_finite_place) diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index aca472691d3..c974c3df6ff 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -1025,6 +1025,7 @@ def artin_symbol(self, P): raise ValueError("%s is ramified" % P) return t[0] + class GaloisGroup_subgroup(GaloisSubgroup_perm): r""" A subgroup of a Galois group, as returned by functions such as @@ -1188,6 +1189,7 @@ def fixed_field(self, name=None, polred=None, threshold=None): name = G._field.variable_name() + '0' return L.subfield(x, name=name) + class GaloisGroupElement(PermutationGroupElement): r""" An element of a Galois group. This is stored as a permutation, but may also diff --git a/src/sage/rings/number_field/maps.py b/src/sage/rings/number_field/maps.py index b14c4ca48f3..8d8eb1b69ef 100644 --- a/src/sage/rings/number_field/maps.py +++ b/src/sage/rings/number_field/maps.py @@ -52,6 +52,7 @@ IdentityMap = IdentityMorphism + class NumberFieldIsomorphism(Map): r""" A base class for various isomorphisms between number fields and @@ -101,6 +102,7 @@ def is_surjective(self): """ return True + class MapVectorSpaceToNumberField(NumberFieldIsomorphism): r""" The map to an absolute number field from its underlying `\QQ`-vector space. @@ -191,6 +193,7 @@ def _call_(self, v): f = K.polynomial_ring()(v.list()) return K._element_class(K, f) + class MapNumberFieldToVectorSpace(Map): r""" A class for the isomorphism from an absolute number field to its underlying @@ -249,6 +252,7 @@ def _call_(self, x): v = v + [QQ.zero()] * k return self.codomain()(v) + class MapRelativeVectorSpaceToRelativeNumberField(NumberFieldIsomorphism): r""" EXAMPLES:: @@ -314,6 +318,7 @@ def _call_(self, v): g = K._pari_rnfeq()._eltreltoabs(h) return K._element_class(K, g) + class MapRelativeNumberFieldToRelativeVectorSpace(NumberFieldIsomorphism): r""" EXAMPLES:: @@ -456,6 +461,7 @@ def _call_(self, x): y = x._copy_for_parent(self.codomain()) return y + class MapRelativeToAbsoluteNumberField(NumberFieldIsomorphism): r""" EXAMPLES:: @@ -533,6 +539,7 @@ def _call_(self, x): f = x.polynomial() return A._element_class(A, f) + class MapAbsoluteToRelativeNumberField(NumberFieldIsomorphism): r""" See :class:`~MapRelativeToAbsoluteNumberField` for examples. @@ -565,6 +572,7 @@ def _call_(self, x): f = x.polynomial() return R._element_class(R, f) + class MapVectorSpaceToRelativeNumberField(NumberFieldIsomorphism): r""" The isomorphism to a relative number field from its underlying `\QQ`-vector @@ -609,6 +617,7 @@ def _call_(self, x): """ return self._from_K(self._from_V(x)) + class MapRelativeNumberFieldToVectorSpace(NumberFieldIsomorphism): r""" The isomorphism from a relative number field to its underlying `\QQ`-vector diff --git a/src/sage/rings/number_field/meson.build b/src/sage/rings/number_field/meson.build new file mode 100644 index 00000000000..077c1d918e6 --- /dev/null +++ b/src/sage/rings/number_field/meson.build @@ -0,0 +1,66 @@ +py.install_sources( + 'S_unit_solver.py', + 'all.py', + 'bdd_height.py', + 'class_group.py', + 'galois_group.py', + 'homset.py', + 'maps.py', + 'morphism.py', + 'number_field.py', + 'number_field_base.pxd', + 'number_field_element.pxd', + 'number_field_element_base.pxd', + 'number_field_element_quadratic.pxd', + 'number_field_ideal.py', + 'number_field_ideal_rel.py', + 'number_field_rel.py', + 'order.py', + 'order_ideal.py', + 'selmer_group.py', + 'small_primes_of_degree_one.py', + 'splitting_field.py', + 'structure.py', + 'totallyreal_data.pxd', + 'totallyreal_phc.py', + 'totallyreal_rel.py', + 'unit_group.py', + subdir: 'sage/rings/number_field', +) + +extension_data = { + 'number_field_base' : files('number_field_base.pyx'), + 'number_field_element_base' : files('number_field_element_base.pyx'), + 'number_field_morphisms' : files('number_field_morphisms.pyx'), + 'totallyreal' : files('totallyreal.pyx'), + 'totallyreal_data' : files('totallyreal_data.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/number_field', + install: true, + include_directories: [inc_cpython, inc_ext, inc_flint, inc_ntl, inc_rings], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, mpfi, mpfr, ntl], + ) +endforeach + +extension_data_cpp = { + 'number_field_element': files('number_field_element.pyx'), + 'number_field_element_quadratic': files('number_field_element_quadratic.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/number_field', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_flint, inc_ntl, inc_rings], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, mpfi, mpfr, ntl], + ) +endforeach + diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 5fc599d4263..315e31cf42f 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -3534,7 +3534,7 @@ def ideal(self, *gens, **kwds): try: return self.fractional_ideal(*gens, **kwds) except ValueError: - return Ring.ideal(self, gens, **kwds) + return self.zero_ideal() def idealchinese(self, ideals, residues): r""" @@ -3600,9 +3600,10 @@ def idealchinese(self, ideals, residues): def fractional_ideal(self, *gens, **kwds): r""" Return the ideal in `\mathcal{O}_K` generated by ``gens``. + This overrides the :class:`sage.rings.ring.Field` method to use the :class:`sage.rings.ring.Ring` one instead, since - we're not really concerned with ideals in a field but in its ring + we are not concerned with ideals in a field but in its ring of integers. INPUT: @@ -8285,8 +8286,7 @@ def _coerce_from_other_number_field(self, x): log_half_root_bound = log2abs(f[0]/2)/n for i in range(1, n): bd = log2abs(f[i])/(n-i) - if bd > log_half_root_bound: - log_half_root_bound = bd + log_half_root_bound = max(bd, log_half_root_bound) # Twice the bound on the roots of f, in other words an upper # bound for the distance between two roots. log_double_root_bound = log_half_root_bound + 2.0 # 2.0 = log2(4) @@ -9346,6 +9346,41 @@ def embeddings(self, K): Defn: a |--> 1.25992104989487 ] + Some more (possible and impossible) embeddings of cyclotomic fields:: + + sage: CyclotomicField(5).embeddings(QQbar) + [ + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: Algebraic Field + Defn: zeta5 |--> 0.3090169943749474? + 0.9510565162951536?*I, + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: Algebraic Field + Defn: zeta5 |--> -0.8090169943749474? + 0.5877852522924731?*I, + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: Algebraic Field + Defn: zeta5 |--> -0.8090169943749474? - 0.5877852522924731?*I, + Ring morphism: + From: Cyclotomic Field of order 5 and degree 4 + To: Algebraic Field + Defn: zeta5 |--> 0.3090169943749474? - 0.9510565162951536?*I + ] + sage: CyclotomicField(3).embeddings(CyclotomicField(7)) + [ ] + sage: CyclotomicField(3).embeddings(CyclotomicField(6)) + [ + Ring morphism: + From: Cyclotomic Field of order 3 and degree 2 + To: Cyclotomic Field of order 6 and degree 2 + Defn: zeta3 |--> zeta6 - 1, + Ring morphism: + From: Cyclotomic Field of order 3 and degree 2 + To: Cyclotomic Field of order 6 and degree 2 + Defn: zeta3 |--> -zeta6 + ] + Test that :issue:`15053` is fixed:: sage: K = NumberField(x^3 - 2, 'a') diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 53055e1e648..27432813b2b 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -1155,7 +1155,7 @@ cdef class NumberFieldElement(NumberFieldElement_base): mpfi_set_prec(a, mpfi_get_prec(v.value)) ZZX_evaluation_mpfi(a, self._numerator, v.value) mpfi_div_z(a, a, den) - mpfr_floor(&a.left ,&a.left) + mpfr_floor(&a.left, &a.left) mpfr_floor(&a.right, &a.right) ans = PY_NEW(Integer) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index e3062448708..a0e90d1d63d 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -2866,7 +2866,7 @@ cdef class OrderElement_quadratic(NumberFieldElement_quadratic): -13 sage: w.inverse_mod(13).parent() == OE True - sage: w.inverse_mod(2*OE) + sage: w.inverse_mod(2) Traceback (most recent call last): ... ZeroDivisionError: w is not invertible modulo Fractional ideal (2) diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index fb9bea019a4..3686840ccba 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -1775,6 +1775,7 @@ def basis_to_module(B, K): C = [to_V(K(b)) for b in B] return M.span_of_basis(C) + def is_NumberFieldIdeal(x): """ Return ``True`` if `x` is an ideal of a number field. @@ -2160,7 +2161,7 @@ def reduce(self, f): R = self.number_field().maximal_order() - if not (f in R): + if f not in R: raise TypeError("reduce only defined for integral elements") Rbasis = R.basis() diff --git a/src/sage/rings/number_field/number_field_ideal_rel.py b/src/sage/rings/number_field/number_field_ideal_rel.py index 0a1557110fe..7f6cfd9b1b7 100644 --- a/src/sage/rings/number_field/number_field_ideal_rel.py +++ b/src/sage/rings/number_field/number_field_ideal_rel.py @@ -42,6 +42,7 @@ QQ = rational_field.RationalField() ZZ = integer_ring.IntegerRing() + class NumberFieldFractionalIdeal_rel(NumberFieldFractionalIdeal): """ An ideal of a relative number field. @@ -882,6 +883,7 @@ def valuation(self, p): raise ValueError("p (= %s) must be an ideal in %s" % self.number_field()) return self.absolute_ideal().valuation(p.absolute_ideal()) + def is_NumberFieldFractionalIdeal_rel(x): """ Return ``True`` if `x` is a fractional ideal of a relative number field. diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 9ab6fd4c261..7ea070b113e 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -2072,6 +2072,9 @@ def embeddings(self, K): sage: f[0](a+b) -0.62996052494743693 - 0.091123635971721295*I """ + if K.characteristic(): + return Sequence([], immutable=True, check=False, universe=self.Hom(K)) + try: # this should be concordant with automorphisms return self.__embeddings[K] diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index c214c385179..9dee6a8a481 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -77,8 +77,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.categories.integral_domains import IntegralDomains from sage.misc.cachefunc import cached_method -from sage.rings.ring import IntegralDomain +from sage.structure.parent import Parent from sage.structure.sequence import Sequence from sage.rings.integer_ring import ZZ import sage.rings.abc @@ -426,7 +427,7 @@ def EquationOrder(f, names, **kwds): return K.order(K.gens()) -class Order(IntegralDomain, sage.rings.abc.Order): +class Order(Parent, sage.rings.abc.Order): r""" An order in a number field. @@ -478,8 +479,8 @@ def __init__(self, K): 0.0535229072603327 + 1.20934552493846*I """ self._K = K - IntegralDomain.__init__(self, ZZ, names=K.variable_names(), - normalize=False) + Parent.__init__(self, base=ZZ, names=K.variable_names(), + normalize=False, category=IntegralDomains()) self._populate_coercion_lists_(embedding=self.number_field()) if self.absolute_degree() == 2: self.is_maximal() # cache @@ -503,21 +504,21 @@ def ideal(self, *args, **kwds): """ Return the integral ideal with given generators. + .. NOTE:: + + This method constructs an ideal of this (not necessarily maximal) order. + To construct a fractional ideal in the ambient number field, use + :meth:`~sage.rings.number_field.number_field.NumberField_generic.fractional_ideal`. + EXAMPLES:: sage: x = polygen(ZZ, 'x') sage: K. = NumberField(x^2 + 7) sage: R = K.maximal_order() - sage: R.ideal(2/3 + 7*a, a) - Traceback (most recent call last): - ... - ValueError: ideal must be integral; - use fractional_ideal to create a non-integral ideal. - sage: R.ideal(7*a, 77 + 28*a) - Fractional ideal (7) + sage: R.ideal([7*a, 77 + 28*a]) + Ideal (7/2*a + 7/2, 7*a) of Maximal Order generated by 1/2*a + 1/2 in Number Field in a with defining polynomial x^2 + 7 sage: R = K.order(4*a) sage: R.ideal(8) - doctest:warning ... FutureWarning: ... Ideal (8, 32*a) of Order of conductor 8 generated by 4*a in Number Field in a with defining polynomial x^2 + 7 @@ -526,36 +527,22 @@ def ideal(self, *args, **kwds): sage: R = EquationOrder(x^2 + 2, 'a'); R Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 2 sage: (3,15)*R - doctest:warning ... DeprecationWarning: ... - Fractional ideal (3) + Ideal (3, 3*a) of Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 2 The zero ideal is handled properly:: sage: R.ideal(0) - Ideal (0) of Number Field in a with defining polynomial x^2 + 2 - """ - if kwds.get('future', False) or not self.is_maximal(): - if 'future' in kwds: - del kwds['future'] - from sage.rings.number_field.order_ideal import NumberFieldOrderIdeal - return NumberFieldOrderIdeal(self, *args, **kwds) - if kwds.get('warn', True): - if 'warn' in kwds: - del kwds['warn'] - from sage.misc.superseded import deprecation - deprecation(34198, 'In the future, constructing an ideal of the ring of ' - 'integers of a number field will use an implementation ' - 'compatible with ideals of other (non-maximal) orders, ' - 'rather than returning an integral fractional ideal of ' - 'its containing number field. Use .fractional_ideal(), ' - 'together with an .is_integral() check if desired, to ' - 'emulate the current behavior.\nSet warn=0 to silence ' - 'this warning, and future=1 to activate the upcoming ' - 'behavior already.') - I = self.number_field().ideal(*args, **kwds) - if not I.is_integral(): - raise ValueError("ideal must be integral; use fractional_ideal to create a non-integral ideal.") - return I + Ideal (0) of Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 2 + """ + # these keyword arguments are ignored since there used to be optional + # arguments with these names for controlling deprecated/future behavior; + # see #34806 and #35762 + if 'warn' in kwds: + del kwds['warn'] + if 'future' in kwds: + del kwds['future'] + from sage.rings.number_field.order_ideal import NumberFieldOrderIdeal + return NumberFieldOrderIdeal(self, *args, **kwds) def _coerce_map_from_(self, R): """ @@ -587,10 +574,9 @@ def __mul__(self, right): sage: Ok = k.maximal_order(); Ok Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 5077 sage: Ok * (11, a + 7) - doctest:warning ... DeprecationWarning: ... - Fractional ideal (11, a + 7) + Ideal (8*a + 1, 11*a) of Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 5077 sage: (11, a + 7) * Ok - Fractional ideal (11, a + 7) + Ideal (8*a + 1, 11*a) of Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 5077 """ return self.ideal(right) @@ -610,7 +596,7 @@ def __rmul__(self, left): sage: (6, 1/2*a + 11/2)*Ok # random output Fractional ideal (6, 1/2*a + 11/2) sage: 17*Ok - Fractional ideal (17) + Ideal (17/2*a + 17/2, 17*a) of Maximal Order generated by 1/2*a + 1/2 in Number Field in a with defining polynomial x^2 + 431 """ return self.ideal(left) @@ -680,7 +666,7 @@ def krull_dimension(self): sage: O2.krull_dimension() 1 """ - return ZZ(1) + return ZZ.one() def integral_closure(self): r""" @@ -748,6 +734,20 @@ def ngens(self): """ return self.absolute_degree() + def gens(self) -> tuple: + """ + Return the generators as a tuple. + + EXAMPLES:: + + sage: x = polygen(ZZ, 'x') + sage: K. = NumberField(x^3 + x^2 - 2*x + 8) + sage: O = K.maximal_order() + sage: O.gens() + (1, 1/2*a^2 + 1/2*a, a^2) + """ + return tuple(self.gen(i) for i in range(self.absolute_degree())) + def basis(self): # this must be defined in derived class r""" Return a basis over `\ZZ` of this order. @@ -909,7 +909,7 @@ def ring_generators(self): gens.append(g) n.append(g.absolute_minpoly().degree()) W = A.span([to_V(x) for x in monomials(gens, n)]) - remaining = [x for x in remaining if not to_V(x) in W] + remaining = [x for x in remaining if to_V(x) not in W] return Sequence(gens, immutable=True) @cached_method @@ -1605,7 +1605,7 @@ def _element_constructor_(self, x): if not isinstance(x, Element) or x.parent() is not self._K: x = self._K(x) V, _, embedding = self._K.vector_space() - if not embedding(x) in self._module_rep: + if embedding(x) not in self._module_rep: raise TypeError("Not an element of the order.") return self._element_type(self, x) diff --git a/src/sage/rings/number_field/order_ideal.py b/src/sage/rings/number_field/order_ideal.py index 0f0b2f5d524..772c4ad3a9a 100644 --- a/src/sage/rings/number_field/order_ideal.py +++ b/src/sage/rings/number_field/order_ideal.py @@ -9,13 +9,6 @@ ideals of non-maximal orders (compared to the maximal case). This should hopefully change in the future. -TESTS: - -This module is currently experimental:: - - sage: import sage.rings.number_field.order_ideal - doctest:warning ... - EXAMPLES:: sage: O = QuadraticField(-1).order(5*i) @@ -75,13 +68,11 @@ from sage.rings.polynomial.polynomial_ring import polygens from sage.rings.ideal import Ideal_generic -from sage.misc.superseded import experimental_warning -experimental_warning(34198, 'Ideals of non-maximal orders are an experimental feature. Be wary of bugs.') - import sage.rings.number_field.order #TODO I*u works when u lies in I.ring().number_field(), but u*I doesn't + def NumberFieldOrderIdeal(O, *args, **kwds): r""" Construct either a :class:`NumberFieldOrderIdeal_generic` @@ -117,6 +108,7 @@ def NumberFieldOrderIdeal(O, *args, **kwds): cls = NumberFieldOrderIdeal_generic return cls(O, *args, **kwds) + class NumberFieldOrderIdeal_generic(Ideal_generic): r""" An ideal of a not necessarily maximal order in a number field. @@ -269,6 +261,7 @@ def norm(self): """ return self.free_module().index_in(self.ring().free_module()) + def _positive_sqrt(R, D): r""" Return the "positive" square root of `D` in `R`, which must be @@ -292,6 +285,7 @@ def _positive_sqrt(R, D): sqrtD, = (s for s in R(D).sqrt(all=True) if s.real() > 0 or s.imag() > 0) return sqrtD + def _gens_from_bqf(O, Q): r""" Helper function to compute generators of the ideal associated to @@ -354,6 +348,7 @@ def _gens_from_bqf(O, Q): t = sqrtD if a < 0 else 1 return a*t, g*t + class NumberFieldOrderIdeal_quadratic(NumberFieldOrderIdeal_generic): r""" An ideal of a not necessarily maximal order in a *quadratic* number field. diff --git a/src/sage/rings/number_field/selmer_group.py b/src/sage/rings/number_field/selmer_group.py index 377dbd9e60c..5204cfc80b6 100644 --- a/src/sage/rings/number_field/selmer_group.py +++ b/src/sage/rings/number_field/selmer_group.py @@ -51,6 +51,7 @@ # A utility function to allow the same code to be used over QQ and # over number fields: + def _ideal_generator(I): r""" Return the generator of a principal ideal. @@ -77,6 +78,7 @@ def _ideal_generator(I): except AttributeError: return I.abs() + def _coords_in_C_p(I, C, p): r""" Return coordinates of the ideal ``I`` with respect to a basis of @@ -99,7 +101,7 @@ def _coords_in_C_p(I, C, p): ALGORITHM: Find the coordinates of `[I]` with respect to generators of `C` as - an abelian group, check that coordidates are 0 in cyclic factors + an abelian group, check that coordinates are 0 in cyclic factors of order prime to `p`, and return the list of `c/(n/p)` (mod `p`) for coordinates `c` for each cyclic factor of order `n` which is a multiple of `p`. @@ -130,6 +132,7 @@ def _coords_in_C_p(I, C, p): return [(coords[i] // n) % p for i, n in p_indices] raise ValueError("The {} power of {} is not principal".format(p.ordinal_str(),I)) + def _coords_in_C_mod_p(I,C,p): r""" Return coordinates of the ideal ``I`` with respect to a basis of @@ -181,6 +184,7 @@ def _coords_in_C_mod_p(I,C,p): coords = C(I).exponents() return [coords[i] % p for i in p_indices] + def _root_ideal(I, C, p): r""" Return a `p`-th root of an ideal with respect to the class group. @@ -230,6 +234,7 @@ def _root_ideal(I, C, p): return prod([gen ** wi for wi, gen in zip(w, cyclic_gens)], C.number_field().ideal(1)) + def coords_in_U_mod_p(u, U, p): r""" Return coordinates of a unit ``u`` with respect to a basis of the @@ -277,6 +282,7 @@ def coords_in_U_mod_p(u, U, p): start = 1 - int(p.divides(U.zeta_order())) # 0 or 1 return [c % p for c in coords[start:]] + def basis_for_p_cokernel(S, C, p): r""" Return a basis for the group of ideals supported on ``S`` (mod diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index e7e66f43d02..ff30a006bba 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -95,7 +95,8 @@ from sage.rings.integer_ring import ZZ -class Small_primes_of_degree_one_iter(): + +class Small_primes_of_degree_one_iter: r""" Iterator that finds primes of a number field of absolute degree one and bounded small prime norm. diff --git a/src/sage/rings/number_field/structure.py b/src/sage/rings/number_field/structure.py index 03748c933a3..212d8e2a6f0 100644 --- a/src/sage/rings/number_field/structure.py +++ b/src/sage/rings/number_field/structure.py @@ -56,6 +56,7 @@ from sage.structure.unique_representation import UniqueRepresentation + class NumberFieldStructure(UniqueRepresentation): r""" Abstract base class encapsulating information about a number fields @@ -136,6 +137,7 @@ def create_structure(self, field): """ raise NotImplementedError + class NameChange(NumberFieldStructure): r""" Structure for a number field created by a change in variable name. @@ -179,6 +181,7 @@ def create_structure(self, field): from . import maps return maps.NameChangeMap(field, self.other), maps.NameChangeMap(self.other, field) + class AbsoluteFromRelative(NumberFieldStructure): r""" Structure for an absolute number field created from a relative number @@ -218,6 +221,7 @@ def create_structure(self, field): from . import maps return maps.MapAbsoluteToRelativeNumberField(field, self.other), maps.MapRelativeToAbsoluteNumberField(self.other, field) + class RelativeFromAbsolute(NumberFieldStructure): r""" Structure for a relative number field created from an absolute number @@ -292,6 +296,7 @@ def create_structure(self, field): return field_to_other, other_to_field + class RelativeFromRelative(NumberFieldStructure): r""" Structure for a relative number field created from another relative number diff --git a/src/sage/rings/number_field/totallyreal_phc.py b/src/sage/rings/number_field/totallyreal_phc.py index a8b5488cb0f..db054708bb9 100644 --- a/src/sage/rings/number_field/totallyreal_phc.py +++ b/src/sage/rings/number_field/totallyreal_phc.py @@ -124,7 +124,7 @@ def __lagrange_bounds_phc(n, m, a, tmpfile=None): os.remove(tmpfile + '.phc') os.popen('phc -b ' + tmpfile + ' ' + tmpfile + '.phc') - f = open(tmpfile + '.phc', 'r') + f = open(tmpfile + '.phc') f_str = f.read() pos = f_str.find('= real ') crits = [] diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 6eea9afcd2c..ac3f52a544c 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -227,6 +227,7 @@ def integral_elements_in_box(K, C): eps_global = 10**(-6) + class tr_data_rel: r""" This class encodes the data used in the enumeration of totally real diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index d9a86cba9d9..cee585d48a1 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -99,6 +99,7 @@ ext_table['re', pAdicRingFloatingPoint] = RelativeRamifiedExtensionRingFloatingPoint ext_table['re', pAdicFieldFloatingPoint] = RelativeRamifiedExtensionFieldFloatingPoint + def _canonicalize_show_prec(type, print_mode, show_prec=None): r""" Return a canonical string value for show_prec depending of the type, @@ -328,6 +329,7 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se padic_field_cache = {} DEFAULT_PREC = Integer(20) + class Qp_class(UniqueFactory): r""" A creation function for `p`-adic fields. @@ -1382,6 +1384,7 @@ def Qq(q, prec=None, type='capped-rel', modulus=None, names=None, # Short constructor names for different types ###################################################### + def QpCR(p, prec=None, *args, **kwds): r""" A shortcut function to create capped relative `p`-adic fields. @@ -1396,6 +1399,7 @@ def QpCR(p, prec=None, *args, **kwds): """ return Qp(p, prec, 'capped-rel', *args, **kwds) + def QpFP(p, prec=None, *args, **kwds): r""" A shortcut function to create floating point `p`-adic fields. @@ -1410,6 +1414,7 @@ def QpFP(p, prec=None, *args, **kwds): """ return Qp(p, prec, 'floating-point', *args, **kwds) + def QqCR(q, prec=None, *args, **kwds): r""" A shortcut function to create capped relative unramified `p`-adic @@ -1425,6 +1430,7 @@ def QqCR(q, prec=None, *args, **kwds): """ return Qq(q, prec, 'capped-rel', *args, **kwds) + def QqFP(q, prec=None, *args, **kwds): r""" A shortcut function to create floating point unramified `p`-adic @@ -1440,6 +1446,7 @@ def QqFP(q, prec=None, *args, **kwds): """ return Qq(q, prec, 'floating-point', *args, **kwds) + @experimental(23505) def QpLC(p, prec=None, *args, **kwds): r""" @@ -1455,6 +1462,7 @@ def QpLC(p, prec=None, *args, **kwds): """ return Qp(p, prec, 'lattice-cap', *args, **kwds) + @experimental(23505) def QpLF(p, prec=None, *args, **kwds): r""" @@ -1470,6 +1478,7 @@ def QpLF(p, prec=None, *args, **kwds): """ return Qp(p, prec, 'lattice-float', *args, **kwds) + def QpER(p, prec=None, halt=None, secure=False, *args, **kwds): r""" A shortcut function to create relaxed `p`-adic fields. @@ -1492,6 +1501,7 @@ def QpER(p, prec=None, halt=None, secure=False, *args, **kwds): # ####################################################################################################### + class Zp_class(UniqueFactory): r""" A creation function for `p`-adic rings. @@ -2619,6 +2629,7 @@ def Zq(q, prec=None, type='capped-rel', modulus=None, names=None, # Short constructor names for different types ###################################################### + def ZpCR(p, prec=None, *args, **kwds): r""" A shortcut function to create capped relative `p`-adic rings. @@ -2633,6 +2644,7 @@ def ZpCR(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'capped-rel', *args, **kwds) + def ZpCA(p, prec=None, *args, **kwds): r""" A shortcut function to create capped absolute `p`-adic rings. @@ -2646,6 +2658,7 @@ def ZpCA(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'capped-abs', *args, **kwds) + def ZpFM(p, prec=None, *args, **kwds): r""" A shortcut function to create fixed modulus `p`-adic rings. @@ -2659,6 +2672,7 @@ def ZpFM(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'fixed-mod', *args, **kwds) + def ZpFP(p, prec=None, *args, **kwds): r""" A shortcut function to create floating point `p`-adic rings. @@ -2673,6 +2687,7 @@ def ZpFP(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'floating-point', *args, **kwds) + def ZqCR(q, prec=None, *args, **kwds): r""" A shortcut function to create capped relative unramified `p`-adic rings. @@ -2687,6 +2702,7 @@ def ZqCR(q, prec=None, *args, **kwds): """ return Zq(q, prec, 'capped-rel', *args, **kwds) + def ZqCA(q, prec=None, *args, **kwds): r""" A shortcut function to create capped absolute unramified `p`-adic rings. @@ -2700,6 +2716,7 @@ def ZqCA(q, prec=None, *args, **kwds): """ return Zq(q, prec, 'capped-abs', *args, **kwds) + def ZqFM(q, prec=None, *args, **kwds): r""" A shortcut function to create fixed modulus unramified `p`-adic rings. @@ -2713,6 +2730,7 @@ def ZqFM(q, prec=None, *args, **kwds): """ return Zq(q, prec, 'fixed-mod', *args, **kwds) + def ZqFP(q, prec=None, *args, **kwds): r""" A shortcut function to create floating point unramified `p`-adic rings. @@ -2727,6 +2745,7 @@ def ZqFP(q, prec=None, *args, **kwds): """ return Zq(q, prec, 'floating-point', *args, **kwds) + @experimental(23505) def ZpLC(p, prec=None, *args, **kwds): r""" @@ -2988,6 +3007,7 @@ def ZpLC(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'lattice-cap', *args, **kwds) + @experimental(23505) def ZpLF(p, prec=None, *args, **kwds): r""" @@ -3015,6 +3035,7 @@ def ZpLF(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'lattice-float', *args, **kwds) + def ZpER(p, prec=None, halt=None, secure=False, *args, **kwds): r""" A shortcut function to create relaxed `p`-adic rings. @@ -3494,6 +3515,7 @@ def split(poly, prec): """ raise NotImplementedError("Extensions by general polynomials not yet supported. Please use an unramified or Eisenstein polynomial.") + def truncate_to_prec(poly, R, absprec): r""" Truncates the unused precision off of a polynomial. @@ -3509,6 +3531,7 @@ def truncate_to_prec(poly, R, absprec): """ return R[poly.variable_name()]([R(a, absprec=absprec) for a in poly.list()]) # Is this quite right? We don't want flat necessarily... + def krasner_check(poly, prec): r""" Return ``True`` iff ``poly`` determines a unique isomorphism class of @@ -3526,6 +3549,7 @@ def krasner_check(poly, prec): """ return True #This needs to be implemented + def is_eisenstein(poly): r""" Return ``True`` iff this monic polynomial is Eisenstein. @@ -3555,6 +3579,7 @@ def is_eisenstein(poly): return False return True + def is_unramified(poly): r""" Return ``True`` iff this monic polynomial is unramified. diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 5fb70851043..27ca55b274f 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -70,6 +70,7 @@ def _prec_type(self): """ return 'capped-abs' + class CappedRelativeGeneric(LocalGeneric): def is_capped_relative(self): """ @@ -107,6 +108,7 @@ def _prec_type(self): """ return 'capped-rel' + class FixedModGeneric(LocalGeneric): def is_fixed_mod(self): """ @@ -145,6 +147,7 @@ def _prec_type(self): """ return 'fixed-mod' + class FloatingPointGeneric(LocalGeneric): def is_floating_point(self): """ @@ -257,15 +260,23 @@ def _test_additive_associativity(self, **options): for x,y,z in some_tuples(S, 3, tester._max_runs): tester.assertTrue(((x + y) + z).is_equal_to(x + (y + z), min(x.precision_absolute(), y.precision_absolute(), z.precision_absolute()))) + class FloatingPointRingGeneric(FloatingPointGeneric): pass + + class FloatingPointFieldGeneric(FloatingPointGeneric):#, sage.rings.ring.Field): pass + + class CappedRelativeRingGeneric(CappedRelativeGeneric): pass + + class CappedRelativeFieldGeneric(CappedRelativeGeneric):#, sage.rings.ring.Field): pass + class pAdicLatticeGeneric(pAdicGeneric): r""" An implementation of the `p`-adic rationals with lattice precision. @@ -1339,19 +1350,31 @@ class pAdicFieldGeneric(pAdicGeneric, sage.rings.abc.pAdicField): #def subfields_of_degree(self, n): # raise NotImplementedError + class pAdicFixedModRingGeneric(pAdicRingGeneric, FixedModGeneric): pass + + class pAdicCappedAbsoluteRingGeneric(pAdicRingGeneric, CappedAbsoluteGeneric): pass + + class pAdicCappedRelativeRingGeneric(pAdicRingGeneric, CappedRelativeRingGeneric): pass + + class pAdicCappedRelativeFieldGeneric(pAdicFieldGeneric, CappedRelativeFieldGeneric): pass + + class pAdicFloatingPointRingGeneric(pAdicRingGeneric, FloatingPointRingGeneric): pass + + class pAdicFloatingPointFieldGeneric(pAdicFieldGeneric, FloatingPointFieldGeneric): pass + class pAdicRingBaseGeneric(pAdicBaseGeneric, pAdicRingGeneric): def construction(self, forbid_frac_field=False): """ @@ -1443,6 +1466,7 @@ def random_element(self, algorithm='default'): #def principal_unit_group(self): # raise NotImplementedError + class pAdicFieldBaseGeneric(pAdicBaseGeneric, pAdicFieldGeneric): def composite(self, subfield1, subfield2): r""" diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index 705feeaf3fc..f5a5ce09a76 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -56,6 +56,7 @@ # The number of additional digits used for internal computations STARTING_ADDITIONAL_PREC = 5 + class pRational: r""" This class implements rational numbers viewed as elements of ``Qp``. @@ -1673,8 +1674,7 @@ def reduce(self, index=0, partial=False): col[i] = col[i].reduce(prec) col[i].normalize() dval = col[i].valuation() - prec - if dval < diffval[i-index]: - diffval[i-index] = dval + diffval[i-index] = min(dval, diffval[i-index]) # We update history if self._history is not None: self._history.append(('partial reduce', index, walltime(tme))) @@ -2123,8 +2123,7 @@ def precision_lattice(self, elements=None): col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: - val = valcol + val = min(valcol, val) row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix @@ -2718,8 +2717,7 @@ def precision_lattice(self, elements=None): col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: - val = valcol + val = min(valcol, val) row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix @@ -2740,7 +2738,8 @@ def precision_lattice(self, elements=None): M *= self._p ** val return M -class pAdicLatticeElementWeakProxy(): + +class pAdicLatticeElementWeakProxy: r""" The implementations of :class:`DifferentialPrecisionGeneric` hold weak references to :class:`pAdicLatticeElement`. They are stored in @@ -2851,6 +2850,7 @@ def __repr__(self): """ return "WeakProxy#%s" % (self._id,) + def list_of_padics(elements): r""" Convert a list of `p`-adic composed elements (such as polynomials, matrices) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 397dab2ed8e..58d83e40724 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -1649,8 +1649,7 @@ def _matrix_determinant(self, M): for j in range(n): prec = min(prec, S[i,j].precision_absolute()) prec -= S[i,i].valuation() - if prec < relprec: - relprec = prec + relprec = min(prec, relprec) if prec < 0: relprec_neg += prec if relprec_neg < 0: diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 4fb34b774fe..84143902e86 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -870,7 +870,7 @@ cdef class LocalGenericElement(CommutativeRingElement): def euclidean_degree(self): r""" - Return the degree of this element as an element of an Euclidean domain. + Return the degree of this element as an element of a Euclidean domain. EXAMPLES: diff --git a/src/sage/rings/padics/meson.build b/src/sage/rings/padics/meson.build new file mode 100644 index 00000000000..9cb64492095 --- /dev/null +++ b/src/sage/rings/padics/meson.build @@ -0,0 +1,123 @@ +py.install_sources( + 'all.py', + 'common_conversion.pxd', + 'eisenstein_extension_generic.py', + 'factory.py', + 'generic_nodes.py', + 'lattice_precision.py', + 'local_generic.py', + 'local_generic_element.pxd', + 'misc.py', + 'morphism.pxd', + 'padic_ZZ_pX_CA_element.pxd', + 'padic_ZZ_pX_CR_element.pxd', + 'padic_ZZ_pX_FM_element.pxd', + 'padic_ZZ_pX_element.pxd', + 'padic_base_generic.py', + 'padic_base_leaves.py', + 'padic_capped_absolute_element.pxd', + 'padic_capped_relative_element.pxd', + 'padic_ext_element.pxd', + 'padic_extension_generic.py', + 'padic_extension_leaves.py', + 'padic_fixed_mod_element.pxd', + 'padic_floating_point_element.pxd', + 'padic_generic.py', + 'padic_generic_element.pxd', + 'padic_lattice_element.py', + 'padic_printing.pxd', + 'padic_relaxed_element.pxd', + 'padic_relaxed_errors.pxd', + 'padic_valuation.py', + 'pow_computer.pxd', + 'pow_computer_ext.pxd', + 'pow_computer_flint.pxd', + 'pow_computer_relative.pxd', + 'precision_error.py', + 'qadic_flint_CA.pxd', + 'qadic_flint_CR.pxd', + 'qadic_flint_FM.pxd', + 'qadic_flint_FP.pxd', + 'relative_extension_leaves.py', + 'relative_ramified_CA.pxd', + 'relative_ramified_CR.pxd', + 'relative_ramified_FM.pxd', + 'relative_ramified_FP.pxd', + 'tests.py', + 'tutorial.py', + 'unramified_extension_generic.py', + subdir: 'sage/rings/padics', +) + +extension_data = { + 'common_conversion' : files('common_conversion.pyx'), + 'local_generic_element' : files('local_generic_element.pyx'), + 'morphism' : files('morphism.pyx'), + 'padic_capped_absolute_element' : files('padic_capped_absolute_element.pyx'), + 'padic_capped_relative_element' : files('padic_capped_relative_element.pyx'), + 'padic_fixed_mod_element' : files('padic_fixed_mod_element.pyx'), + 'padic_floating_point_element' : files('padic_floating_point_element.pyx'), + 'padic_generic_element' : files('padic_generic_element.pyx'), + 'padic_relaxed_element' : files('padic_relaxed_element.pyx'), + 'padic_relaxed_errors' : files('padic_relaxed_errors.pyx'), + 'qadic_flint_CA' : files('qadic_flint_CA.pyx'), + 'qadic_flint_CR' : files('qadic_flint_CR.pyx'), + 'qadic_flint_FM' : files('qadic_flint_FM.pyx'), + 'qadic_flint_FP' : files('qadic_flint_FP.pyx'), + 'relative_ramified_CA' : files('relative_ramified_CA.pyx'), + 'relative_ramified_CR' : files('relative_ramified_CR.pyx'), + 'relative_ramified_FM' : files('relative_ramified_FM.pyx'), + 'relative_ramified_FP' : files('relative_ramified_FP.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/padics', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_rings, + inc_rings_finite, + inc_src, + ], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, m, ntl], + ) +endforeach + +extension_data_cpp = { + 'padic_ZZ_pX_CA_element': files('padic_ZZ_pX_CA_element.pyx'), + 'padic_ZZ_pX_CR_element': files('padic_ZZ_pX_CR_element.pyx'), + 'padic_ZZ_pX_FM_element': files('padic_ZZ_pX_FM_element.pyx'), + 'padic_ZZ_pX_element': files('padic_ZZ_pX_element.pyx'), + 'padic_ext_element': files('padic_ext_element.pyx'), + 'padic_printing': files('padic_printing.pyx'), + 'pow_computer': files('pow_computer.pyx'), + 'pow_computer_ext': files('pow_computer_ext.pyx'), + 'pow_computer_flint': files('pow_computer_flint.pyx'), + 'pow_computer_relative': files('pow_computer_relative.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/padics', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_rings, + inc_rings_finite, + ], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, m, ntl], + ) +endforeach + diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 00720ba8bbb..d7ce570a732 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -30,6 +30,7 @@ python_min = min python_max = max + def gauss_sum(a, p, f, prec=20, factored=False, algorithm='pari', parent=None): r""" Return the Gauss sum `g_q(a)` as a `p`-adic number. @@ -184,6 +185,7 @@ def max(*L): except ValueError: return -infinity + def precprint(prec_type, prec_cap, p): """ String describing the precision mode on a `p`-adic ring or field. @@ -209,6 +211,7 @@ def precprint(prec_type, prec_cap, p): 'relaxed':'handled with relaxed arithmetics'} return precD[prec_type] + def trim_zeros(L): r""" Strips trailing zeros/empty lists from a list. diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 0cc295f4e69..64be9291c49 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -404,6 +404,7 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + class pAdicRingFloatingPoint(pAdicRingBaseGeneric, pAdicFloatingPointRingGeneric): r""" An implementation of the `p`-adic integers with floating point @@ -499,6 +500,7 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + class pAdicRingFixedMod(pAdicRingBaseGeneric, pAdicFixedModRingGeneric): r""" An implementation of the `p`-adic integers using fixed modulus. @@ -605,6 +607,7 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + class pAdicFieldCappedRelative(pAdicFieldBaseGeneric, pAdicCappedRelativeFieldGeneric): r""" An implementation of `p`-adic fields with capped relative precision. @@ -740,6 +743,7 @@ def random_element(self, algorithm='default'): else: raise NotImplementedError("Don't know %s algorithm" % algorithm) + class pAdicFieldFloatingPoint(pAdicFieldBaseGeneric, pAdicFloatingPointFieldGeneric): r""" An implementation of the `p`-adic rationals with floating point @@ -841,6 +845,7 @@ def _convert_map_from_(self, R): # Lattice precision ################### + class pAdicRingLattice(pAdicLatticeGeneric, pAdicRingBaseGeneric): """ An implementation of the `p`-adic integers with lattice precision. @@ -958,8 +963,7 @@ def random_element(self, prec=None): prec = self._prec_cap_absolute x = ZZ.random_element(p**prec) relcap = x.valuation(p) + self._prec_cap_relative - if relcap < prec: - prec = relcap + prec = min(relcap, prec) return self._element_class(self, x, prec=prec) else: if prec is None: @@ -972,6 +976,7 @@ def random_element(self, prec=None): x += p**cap * ZZ.random_element(p**v) return self._element_class(self, x, prec=prec) + class pAdicFieldLattice(pAdicLatticeGeneric, pAdicFieldBaseGeneric): """ An implementation of the `p`-adic numbers with lattice precision. @@ -1105,13 +1110,13 @@ def random_element(self, prec=None, integral=False): p = self.prime() x = ZZ.random_element(p**prec) relcap = x.valuation(p) + self._prec_cap_relative - if relcap < prec: - prec = relcap + prec = min(relcap, prec) return self._element_class(self, x*(p**val), prec=prec) # Relaxed ######### + class pAdicRingRelaxed(pAdicRelaxedGeneric, pAdicRingBaseGeneric): r""" An implementation of relaxed arithmetics over `\ZZ_p`. @@ -1150,6 +1155,7 @@ def __init__(self, p, prec, print_mode, names): self._element_class_module = padic_relaxed_element self._element_class_prefix = "pAdicRelaxedElement_" + class pAdicFieldRelaxed(pAdicRelaxedGeneric, pAdicFieldBaseGeneric): r""" An implementation of relaxed arithmetics over `\QQ_p`. diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 2c8ff496f87..e3e97906be2 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -41,6 +41,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.richcmp import rich_to_bool + class pAdicExtensionGeneric(pAdicGeneric): def __init__(self, poly, prec, print_mode, names, element_class): """ @@ -668,6 +669,8 @@ def free_module(self, base=None, basis=None, map=True): # an object in two free module categories with # different base rings. So for now we # just stick with Map. + + class pAdicModuleIsomorphism(Map): r""" A base class for various isomorphisms between `p`-adic rings/fields and free modules. @@ -728,6 +731,7 @@ def _richcmp_(self, other, op): else: return rich_to_bool(op, 1) + class MapFreeModuleToOneStep(pAdicModuleIsomorphism): """ The isomorphism from the underlying module of a one-step `p`-adic extension @@ -763,6 +767,7 @@ def _call_with_args(self, x, args=(), kwds={}): """ return self.codomain()(list(x), *args, **kwds) + class MapOneStepToFreeModule(pAdicModuleIsomorphism): """ The isomorphism from a one-step `p`-adic extension to its underlying free module. @@ -787,6 +792,7 @@ def _call_(self, x): """ return self.codomain()(x._polynomial_list(pad=True)) + class MapFreeModuleToTwoStep(pAdicModuleIsomorphism): """ The isomorphism from the underlying module of a two-step `p`-adic extension @@ -834,6 +840,7 @@ def _call_with_args(self, x, args=(), kwds={}): """ return self.codomain()(self._call_(x), *args, **kwds) + class MapTwoStepToFreeModule(pAdicModuleIsomorphism): """ The isomorphism from a two-step `p`-adic extension to its underlying free module. @@ -861,6 +868,7 @@ def _call_(self, x): v = flatten([c._polynomial_list(pad=True) for c in x._polynomial_list(pad=True)]) return self.codomain()(v) + class DefPolyConversion(Morphism): """ Conversion map between `p`-adic rings/fields with the same defining polynomial. diff --git a/src/sage/rings/padics/padic_extension_leaves.py b/src/sage/rings/padics/padic_extension_leaves.py index c57e633c60d..d17563712fb 100644 --- a/src/sage/rings/padics/padic_extension_leaves.py +++ b/src/sage/rings/padics/padic_extension_leaves.py @@ -95,6 +95,7 @@ def _make_integral_poly(exact_modulus, p, prec): except TypeError: return exact_modulus.change_ring(Zmod(p**prec)).change_ring(ZZ) + class UnramifiedExtensionRingCappedRelative(UnramifiedExtensionGeneric, pAdicCappedRelativeRingGeneric): """ TESTS:: @@ -153,6 +154,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp self.register_coercion(pAdicCoercion_ZZ_CR(self)) self.register_conversion(pAdicConvert_QQ_CR(self)) + class UnramifiedExtensionFieldCappedRelative(UnramifiedExtensionGeneric, pAdicCappedRelativeFieldGeneric): """ TESTS:: @@ -298,6 +300,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp self.register_coercion(pAdicCoercion_ZZ_CA(self)) self.register_conversion(pAdicConvert_QQ_CA(self)) + class UnramifiedExtensionRingFixedMod(UnramifiedExtensionGeneric, pAdicFixedModRingGeneric): """ TESTS:: @@ -360,6 +363,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp # return Morphism_ZpFM_UnrFM(S, self) # return None + class UnramifiedExtensionRingFloatingPoint(UnramifiedExtensionGeneric, pAdicFloatingPointRingGeneric): """ TESTS:: @@ -484,6 +488,7 @@ def _coerce_map_from_(self, R): return super()._coerce_map_from_(R) + class EisensteinExtensionRingCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeRingGeneric): """ TESTS:: @@ -539,6 +544,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) + class EisensteinExtensionFieldCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeFieldGeneric): """ TESTS:: @@ -595,6 +601,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) + class EisensteinExtensionRingCappedAbsolute(EisensteinExtensionGeneric, pAdicCappedAbsoluteRingGeneric): """ TESTS:: @@ -650,6 +657,7 @@ def __init__(self, exact_modulus, poly, prec, print_mode, shift_seed, names, imp self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCAElement) + class EisensteinExtensionRingFixedMod(EisensteinExtensionGeneric, pAdicFixedModRingGeneric): """ TESTS:: diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index ffa0eba1b05..3f041a5e146 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -1743,6 +1743,7 @@ def _richcmp_(self, other, op): # A class for the Teichmüller lift would also be reasonable.... + class ResidueLiftingMap(Morphism): r""" Lifting map to a `p`-adic ring or field from its residue field or ring. @@ -1875,6 +1876,7 @@ def _richcmp_(self, other, op): return NotImplemented return richcmp((self.domain(), self.codomain()), (other.domain(), other.codomain()), op) + def local_print_mode(obj, print_options, pos=None, ram_name=None): r""" Context manager for safely temporarily changing the print_mode diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index 2ac90a5d0f2..c0988388066 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -1401,7 +1401,7 @@ cdef class pAdicPrinter_class(SageObject): return s else: count += 1 - if count == max_unram_terms: #this will never trigger if max_unram_terms == -1 + if count == max_unram_terms: # this will never trigger if max_unram_terms == -1 newj = len(L) - 1 while L[newj] == 0: newj -= 1 @@ -1424,7 +1424,7 @@ cdef class pAdicPrinter_class(SageObject): return s else: count += 1 - if count == max_unram_terms: #this will never trigger if max_unram_terms == -1 + if count == max_unram_terms: # this will never trigger if max_unram_terms == -1 newj = 0 while L[newj] == 0: newj += 1 diff --git a/src/sage/rings/padics/padic_valuation.py b/src/sage/rings/padics/padic_valuation.py index 7ee6124eebe..1050055e1fb 100644 --- a/src/sage/rings/padics/padic_valuation.py +++ b/src/sage/rings/padics/padic_valuation.py @@ -48,6 +48,7 @@ from sage.rings.infinity import infinity + class PadicValuationFactory(UniqueFactory): r""" Create a ``prime``-adic valuation on ``R``. @@ -1391,6 +1392,7 @@ def extensions(self, ring): return [pAdicValuation(ring, approximant)] return super().extensions(ring) + def _fraction_field(ring): r""" Return a fraction field of ``ring``. diff --git a/src/sage/rings/padics/precision_error.py b/src/sage/rings/padics/precision_error.py index e7ef94f949b..3dcf4b08d8e 100644 --- a/src/sage/rings/padics/precision_error.py +++ b/src/sage/rings/padics/precision_error.py @@ -18,5 +18,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** + class PrecisionError(ArithmeticError): pass diff --git a/src/sage/rings/padics/relative_extension_leaves.py b/src/sage/rings/padics/relative_extension_leaves.py index b377a446081..3005ae0825b 100644 --- a/src/sage/rings/padics/relative_extension_leaves.py +++ b/src/sage/rings/padics/relative_extension_leaves.py @@ -28,6 +28,7 @@ from .relative_ramified_FP import RelativeRamifiedFloatingPointElement from .pow_computer_relative import PowComputer_relative_maker + class pAdicRelativeBaseringInjection(Morphism): """ The injection of the unramified base into the two-step extension. @@ -123,6 +124,7 @@ def section(self): """ return pAdicRelativeBaseringSection(self.codomain(), self.domain()) + class pAdicRelativeBaseringSection(Morphism): """ The map from a two-step extension back to its maximal unramified subextension. @@ -193,6 +195,7 @@ def _call_with_args(self, x, args=(), kwds={}): """ return self.codomain()(self._call_(x), *args, **kwds) + class RelativeRamifiedExtensionRingFixedMod(EisensteinExtensionGeneric, pAdicFixedModRingGeneric): """ Two-step extension ring with fixed-mod precision. @@ -230,6 +233,7 @@ def __init__(self, exact_modulus, approx_modulus, prec, print_mode, shift_seed, self.register_coercion(pAdicRelativeBaseringInjection(approx_modulus.base_ring(), self)) self.register_conversion(pAdicConvert_QQ_FM(self)) + class RelativeRamifiedExtensionRingCappedAbsolute(EisensteinExtensionGeneric, pAdicCappedAbsoluteRingGeneric): """ Two-step extension ring with capped absolute precision. @@ -267,6 +271,7 @@ def __init__(self, exact_modulus, approx_modulus, prec, print_mode, shift_seed, self.register_coercion(pAdicRelativeBaseringInjection(approx_modulus.base_ring(), self)) self.register_conversion(pAdicConvert_QQ_CA(self)) + class RelativeRamifiedExtensionRingCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeRingGeneric): """ Two-step extension ring with capped relative precision. @@ -304,6 +309,7 @@ def __init__(self, exact_modulus, approx_modulus, prec, print_mode, shift_seed, self.register_coercion(pAdicRelativeBaseringInjection(approx_modulus.base_ring(), self)) self.register_conversion(pAdicConvert_QQ_CR(self)) + class RelativeRamifiedExtensionFieldCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeFieldGeneric): """ Two-step extension field with capped relative precision. @@ -343,6 +349,7 @@ def __init__(self, exact_modulus, approx_modulus, prec, print_mode, shift_seed, self.register_coercion(pAdicRelativeBaseringInjection(approx_modulus.base_ring().integer_ring(), self)) self.register_coercion(pAdicCoercion_QQ_CR(self)) + class RelativeRamifiedExtensionRingFloatingPoint(EisensteinExtensionGeneric, pAdicFloatingPointRingGeneric): """ Two-step extension ring with floating point precision. @@ -380,6 +387,7 @@ def __init__(self, exact_modulus, approx_modulus, prec, print_mode, shift_seed, self.register_coercion(pAdicRelativeBaseringInjection(approx_modulus.base_ring(), self)) self.register_conversion(pAdicConvert_QQ_FP(self)) + class RelativeRamifiedExtensionFieldFloatingPoint(EisensteinExtensionGeneric, pAdicFloatingPointFieldGeneric): """ Two-step extension field with floating point precision. diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index 61459d8d50d..b8f2b62d5a7 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -124,7 +124,7 @@ def __pow__(self, other): sage: a^2 9 """ - if not (other in PariRing()): + if other not in PariRing(): other = Pari(other) return self.__class__(self.__x ** other.__x, parent=_inst) diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index 0c94bac6035..42cf47cad86 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -90,6 +90,7 @@ def interval_roots(p, rts, prec): return irts + def intervals_disjoint(intvs): """ Given a list of complex intervals, check whether they are pairwise diff --git a/src/sage/rings/polynomial/flatten.py b/src/sage/rings/polynomial/flatten.py index ac0e594ab3c..6354bbcd382 100644 --- a/src/sage/rings/polynomial/flatten.py +++ b/src/sage/rings/polynomial/flatten.py @@ -224,13 +224,13 @@ def _call_(self, p): if isinstance(ring, PolynomialRing_general): for mon, pp in p.items(): assert pp.parent() is ring - for i, j in pp.dict().items(): - new_p[(i,)+(mon)] = j + for i, j in pp.monomial_coefficients().items(): + new_p[(i,) + (mon)] = j elif isinstance(ring, MPolynomialRing_base): for mon, pp in p.items(): assert pp.parent() is ring - for mmon, q in pp.dict().items(): - new_p[tuple(mmon)+mon] = q + for mmon, q in pp.monomial_coefficients().items(): + new_p[tuple(mmon) + mon] = q else: raise RuntimeError p = new_p @@ -642,7 +642,7 @@ def _call_(self, p): # apply _sub_specialization to each coefficient # in the flattened polynomial tmp = {} - for exponent, coefficient in flat.dict().items(): + for exponent, coefficient in flat.monomial_coefficients().items(): # Fix the type of exponent from (a,) to a # (necessary for R(tmp) later) if isinstance(exponent, ETuple) and len(exponent) == 1: diff --git a/src/sage/rings/polynomial/ideal.py b/src/sage/rings/polynomial/ideal.py index 9eba00860b5..bf575cb0339 100644 --- a/src/sage/rings/polynomial/ideal.py +++ b/src/sage/rings/polynomial/ideal.py @@ -19,6 +19,7 @@ from sage.rings.ideal import Ideal_pid + class Ideal_1poly_field(Ideal_pid): """ An ideal in a univariate polynomial ring over a field. diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index db55eff5dd1..b7d10c05db1 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -552,6 +552,41 @@ def is_nilpotent(self): """ return self._p.is_nilpotent() + def numerator(self): + r""" + Return a numerator of ``self``, computed as ``self * self.denominator()``. + + .. WARNING:: + + This is not the numerator of the rational function + defined by ``self``, which would always be ``self`` since it is a + polynomial. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: p = 2/3*x[1] + 4/9*x[2] - 2*x[1]*x[3] + sage: num = p.numerator(); num + -18*x_3*x_1 + 4*x_2 + 6*x_1 + + TESTS:: + + sage: num.parent() + Infinite polynomial ring in x over Rational Field + + Check that :issue:`37756` is fixed:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: P. = QQ[] + sage: FF = P.fraction_field() + sage: FF(a[0]) + Traceback (most recent call last): + ... + TypeError: Could not find a mapping of the passed element to this ring. + """ + P = self.parent() + return InfinitePolynomial(P, self._p.numerator()) + @cached_method def variables(self): """ @@ -569,9 +604,69 @@ def variables(self): () """ if hasattr(self._p, 'variables'): - return tuple(self._p.variables()) + P = self.parent() + return tuple(InfinitePolynomial(P, v) for v in self._p.variables()) return () + def monomials(self): + """ + Return the list of monomials in ``self``. + + The returned list is decreasingly ordered by the term ordering of + ``self.parent()``. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: p = x[1]^3 + x[2] - 2*x[1]*x[3] + sage: p.monomials() + [x_3*x_1, x_2, x_1^3] + + sage: X. = InfinitePolynomialRing(QQ, order='deglex') + sage: p = x[1]^3 + x[2] - 2*x[1]*x[3] + sage: p.monomials() + [x_1^3, x_3*x_1, x_2] + """ + P = self.parent() + return [InfinitePolynomial(P, m) for m in self._p.monomials()] + + def monomial_coefficient(self, mon): + """ + Return the base ring element that is the coefficient of ``mon`` + in ``self``. + + This function contrasts with the function :meth:`coefficient`, + which returns the coefficient of a monomial viewing this + polynomial in a polynomial ring over a base ring having fewer + variables. + + INPUT: + + - ``mon`` -- a monomial in the parent of ``self`` + + OUTPUT: coefficient in base ring + + .. SEEALSO:: + + For coefficients in a base ring of fewer variables, + look at :meth:`coefficient`. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: f = 2*x[0]*x[2] + 3*x[1]^2 + sage: c = f.monomial_coefficient(x[1]^2); c + 3 + sage: c.parent() + Rational Field + + sage: c = f.coefficient(x[2]); c + 2*x_0 + sage: c.parent() + Infinite polynomial ring in x over Rational Field + """ + return self._p.monomial_coefficient(mon._p) + @cached_method def max_index(self): r""" @@ -896,16 +991,14 @@ def symmetric_cancellation_order(self, other): return (0, 1, self.lc() / other.lc()) if self.lm() < other.lm(): rawcmp = -1 - Fsmall = dict([[k[0], [e for e in k[1]]] - for k in self.footprint().items()]) - Fbig = dict([[k[0], [e for e in k[1]]] - for k in other.footprint().items()]) + Fsmall = {k: list(v) for k, v in self.footprint().items()} + Fbig = {k: list(v) for k, v in other.footprint().items()} ltsmall = slt ltbig = olt else: rawcmp = 1 - Fbig = dict([[k[0], [e for e in k[1]]] for k in self.footprint().items()]) - Fsmall = dict([[k[0], [e for e in k[1]]] for k in other.footprint().items()]) + Fbig = {k: list(v) for k, v in self.footprint().items()} + Fsmall = {k: list(v) for k, v in other.footprint().items()} ltbig = slt ltsmall = olt # Case 1: one of the Infinite Polynomials is scalar. @@ -936,7 +1029,7 @@ def symmetric_cancellation_order(self, other): while j < lenBig: found = False if Lbig[j] >= i: - ExpoBigSave = [e for e in Fbig[Lbig[j]]] + ExpoBigSave = list(Fbig[Lbig[j]]) ExpoBig = Fbig[Lbig[j]] found = True for k in gens: @@ -997,42 +1090,42 @@ def coefficient(self, monomial): sage: a.coefficient({x[0]:1, x[1]:1}) 2 """ + P = self.parent() if self._p == 0: - res = 0 - elif isinstance(monomial, self.__class__): - if not (self.parent().has_coerce_map_from(monomial.parent())): - res = 0 + return P.zero() + if isinstance(monomial, self.__class__): + if not P.has_coerce_map_from(monomial.parent()): + return P.zero() + if hasattr(self._p, 'variables'): + VarList = [str(X) for X in self._p.variables()] else: - if hasattr(self._p, 'variables'): - VarList = [str(X) for X in self._p.variables()] - else: - VarList = [] - if hasattr(monomial._p, 'variables'): - VarList.extend([str(X) for X in monomial._p.variables()]) - VarList = list(set(VarList)) - VarList.sort(key=self.parent().varname_key, reverse=True) - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - if len(VarList) == 1: - # 'xx' is guaranteed to be no variable - # name of monomial, since coercions - # were tested before - R = PolynomialRing(self._p.base_ring(), VarList + ['xx'], order=self.parent()._order) - - res = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order)(R(self._p).coefficient(R(monomial._p))) - else: - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - res = R(self._p).coefficient(R(monomial._p)) - elif isinstance(monomial, dict): + VarList = [] + if hasattr(monomial._p, 'variables'): + VarList.extend([str(X) for X in monomial._p.variables()]) + VarList = list(set(VarList)) + VarList.sort(key=P.varname_key, reverse=True) + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + if len(VarList) == 1: + # 'xx' is guaranteed to be no variable + # name of monomial, since coercions + # were tested before + R = PolynomialRing(self._p.base_ring(), VarList + ['xx'], order=P._order) + S = PolynomialRing(self._p.base_ring(), VarList, order=P._order) + res = S(R(self._p).coefficient(R(monomial._p))) + return InfinitePolynomial(P, res) + + R = PolynomialRing(self._p.base_ring(), VarList, order=P._order) + res = R(self._p).coefficient(R(monomial._p)) + return InfinitePolynomial(P, res) + + if isinstance(monomial, dict): if monomial: I = iter(monomial) K = next(I) del monomial[K] - res = self.coefficient(K).coefficient(monomial) - else: - return self - else: - raise TypeError("Objects of type %s have no coefficients in InfinitePolynomials" % (type(monomial))) - return self.parent()(res) + return self.coefficient(K).coefficient(monomial) + return self + raise TypeError("Objects of type %s have no coefficients in InfinitePolynomials" % (type(monomial))) # Essentials for Buchberger def reduce(self, I, tailreduce=False, report=None): diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index afa4a4dc416..05735a21cd2 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -279,8 +279,8 @@ class InfinitePolynomialRingFactory(UniqueFactory): """ A factory for creating infinite polynomial ring elements. It - handles making sure that they are unique as well as handling - pickling. For more details, see + makes sure that they are unique as well as handling pickling. + For more details, see :class:`~sage.structure.factory.UniqueFactory` and :mod:`~sage.rings.polynomial.infinite_polynomial_ring`. @@ -564,7 +564,7 @@ def __init__(self, parent, start): def __next__(self): """ - Return a dictionary that can be used to interprete strings in the base ring of ``self``. + Return a dictionary that can be used to interpret strings in the base ring of ``self``. EXAMPLES:: @@ -701,7 +701,7 @@ def __init__(self, R, names, order): names = ['x'] for n in names: if not (isinstance(n, str) and n.isalnum() and (not n[0].isdigit())): - raise ValueError("generator names must be alpha-numeric strings not starting with a digit, but %s is not" % n) + raise ValueError("generator names must be alphanumeric strings not starting with a digit, but %s is not" % n) if len(names) != len(set(names)): raise ValueError("generator names must be pairwise different") self._names = tuple(names) @@ -884,6 +884,17 @@ def _element_constructor_(self, x): Traceback (most recent call last): ... ValueError: cannot convert 1/3 into an element of Infinite polynomial ring in x over Integer Ring + + .. WARNING:: + + The :issue:`37756` is not yet fixed:: + + sage: L. = QQ[] + sage: R. = InfinitePolynomialRing(QQ) + sage: M = InfinitePolynomialRing(L, names=["a"]) + sage: c = a[0] + sage: M(c) # known bug + a_0 """ from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial # In many cases, the easiest solution is to "simply" evaluate diff --git a/src/sage/rings/polynomial/laurent_polynomial.pxd b/src/sage/rings/polynomial/laurent_polynomial.pxd index 8e9107aeb47..1937490caac 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pxd +++ b/src/sage/rings/polynomial/laurent_polynomial.pxd @@ -7,11 +7,11 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): cpdef _mul_(self, other) cpdef _floordiv_(self, other) cpdef long number_of_terms(self) except -1 - cpdef dict dict(self) + cpdef dict monomial_coefficients(self) + cdef class LaurentPolynomial_univariate(LaurentPolynomial): cdef ModuleElement __u cdef long __n cpdef _normalize(self) cpdef _unsafe_mutate(self, i, value) - diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 70cec491cae..0fed2467c05 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -218,7 +218,7 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): """ return self.number_of_terms() - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Abstract ``dict`` method. @@ -226,13 +226,15 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): sage: R. = LaurentPolynomialRing(ZZ) sage: from sage.rings.polynomial.laurent_polynomial import LaurentPolynomial - sage: LaurentPolynomial.dict(x) + sage: LaurentPolynomial.monomial_coefficients(x) Traceback (most recent call last): ... NotImplementedError """ raise NotImplementedError + dict = monomial_coefficients + def map_coefficients(self, f, new_base_ring=None): """ Apply ``f`` to the coefficients of ``self``. @@ -294,7 +296,8 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R(dict([(k, f(v)) for (k, v) in self.dict().items()])) + return R(dict([(k, f(v)) + for k, v in self.monomial_coefficients().items()])) cdef class LaurentPolynomial_univariate(LaurentPolynomial): @@ -838,7 +841,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): d = {repr(g): R.var(g) for g in self._parent.gens()} return self.subs(**d) - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Return a dictionary representing ``self``. @@ -848,11 +851,18 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: Q. = LaurentPolynomialRing(R) sage: f = (x^3 + y/t^3)^3 + t^2; f y^3*t^-9 + 3*x^3*y^2*t^-6 + 3*x^6*y*t^-3 + x^9 + t^2 + sage: f.monomial_coefficients() + {-9: y^3, -6: 3*x^3*y^2, -3: 3*x^6*y, 0: x^9, 2: 1} + + ``dict`` is an alias:: + sage: f.dict() {-9: y^3, -6: 3*x^3*y^2, -3: 3*x^6*y, 0: x^9, 2: 1} """ - cdef dict d = self.__u.dict() - return {k+self.__n: d[k] for k in d} + cdef dict d = self.__u.monomial_coefficients() + return {k + self.__n: d[k] for k in d} + + dict = monomial_coefficients def coefficients(self): """ @@ -1126,7 +1136,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ return self.__u.is_monomial() - def __pow__(_self, r, dummy): + def __pow__(_self, r, mod): """ EXAMPLES:: @@ -1145,9 +1155,21 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): x^-8 sage: (5*x^-4)^-3 5*x^12 + + Check that using third argument raises an error:: + + sage: L. = LaurentPolynomialRing(R) + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef LaurentPolynomial_univariate self = _self cdef long right = r + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if right != r: raise ValueError("exponent must be an integer") try: @@ -2124,7 +2146,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): Multivariate Polynomial Ring in t, tinv over Rational Field """ dres = {} - for (e, c) in self.dict().items(): + for e, c in self.monomial_coefficients().items(): if e > 0: dres[(e, 0)] = c else: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ideal.py b/src/sage/rings/polynomial/laurent_polynomial_ideal.py index ae21901fee1..0403526ae15 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ideal.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ideal.py @@ -26,6 +26,7 @@ from sage.structure.richcmp import op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE from sage.arith.misc import GCD + class LaurentPolynomialIdeal( Ideal_generic ): def __init__(self, ring, gens, coerce=True, hint=None): r""" diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index e6ea53e1756..17caf047da0 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -108,7 +108,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if isinstance(x, dict): self._mon = ETuple({}, int(parent.ngens())) D = {} - for k, x_k in x.iteritems(): # ETuple-ize keys, set _mon + for k, x_k in x.items(): # ETuple-ize keys, set _mon if not isinstance(k, (tuple, ETuple)) or len(k) != parent.ngens(): self._mon = ETuple({}, int(parent.ngens())) break @@ -119,7 +119,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): else: x = D if not self._mon.is_constant(): # factor out _mon - x = {k.esub(self._mon): x_k for k, x_k in x.iteritems()} + x = {k.esub(self._mon): x_k for k, x_k in x.items()} elif (isinstance(x, LaurentPolynomial_mpair) and parent.variable_names() == x.parent().variable_names()): self._mon = ( < LaurentPolynomial_mpair > x)._mon @@ -281,7 +281,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): # self._parent.variable_names(), # self._parent.base_ring() # ) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.monomial_coefficients() cdef ETuple e if i is None: @@ -309,12 +309,12 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: L. = LaurentPolynomialRing(QQ) sage: a = w^2*z^-1 +3 - sage: a.dict() # indirect doctest + sage: a.monomial_coefficients() # indirect doctest {(0, 0): 3, (2, -1): 1} """ # cdef dict D = self._poly._mpoly_dict_recursive(self._parent.variable_names(), # self._parent.base_ring()) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.monomial_coefficients() cdef dict DD if self._mon.is_constant(): self._prod = PolyDict(D) @@ -448,7 +448,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ cdef ETuple e if self._poly.is_term(): - (e, c), = self.dict().items() + (e, c), = self.monomial_coefficients().items() e = e.emul(-1) P = self._parent try: @@ -460,7 +460,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): return P({e: c}) return super().__invert__() - def __pow__(LaurentPolynomial_mpair self, n, m): + def __pow__(LaurentPolynomial_mpair self, n, mod): """ EXAMPLES:: @@ -480,8 +480,20 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: f = (x+y+z^-1)^2 sage: f.substitute(z=1) x^2 + 2*x*y + y^2 + 2*x + 2*y + 1 + + Check that using third argument raises an error:: + + sage: L. = LaurentPolynomialRing(R) + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef LaurentPolynomial_mpair ans + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if n < 0: return ~(self ** -n) ans = self._new_c() @@ -574,7 +586,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): Multivariate Polynomial Ring in t1, t1inv, t2, t2inv over Rational Field """ dres = {} - for (e, c) in self.dict().items(): + for e, c in self.monomial_coefficients().items(): exps = [] for t in e: if t > 0: @@ -742,7 +754,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): self._compute_polydict() if m._prod is None: m._compute_polydict() - return self._parent(self._prod.coefficient(m.dict())) + return self._parent(self._prod.coefficient(m.monomial_coefficients())) def coefficients(self): """ @@ -781,7 +793,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: f.variables(sort=False) #random (y, z, x) """ - cdef dict d = self.dict() + cdef dict d = self.monomial_coefficients() cdef tuple g = self._parent.gens() cdef Py_ssize_t nvars = len(g) cdef set vars = set() @@ -794,7 +806,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): v.sort() return tuple(v) - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): """ Return ``self`` represented as a ``dict``. @@ -802,6 +814,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: L. = LaurentPolynomialRing(QQ) sage: f = 4*x^7*z^-1 + 3*x^3*y + 2*x^4*z^-2 + x^6*y^-7 + sage: sorted(f.monomial_coefficients().items()) + [((3, 1, 0), 3), ((4, 0, -2), 2), ((6, -7, 0), 1), ((7, 0, -1), 4)] + + ``dict`` is an alias:: + sage: sorted(f.dict().items()) [((3, 1, 0), 3), ((4, 0, -2), 2), ((6, -7, 0), 1), ((7, 0, -1), 4)] """ @@ -809,6 +826,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): self._compute_polydict() return < dict > self._prod.dict() + dict = monomial_coefficients + def _fraction_pair(self): """ Return one representation of ``self`` as a pair @@ -1558,11 +1577,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): except ValueError: # call _derivative() recursively on coefficients return P({m: c._derivative(var) - for (m, c) in self.dict().iteritems()}) + for m, c in self.monomial_coefficients().items()}) # compute formal derivative with respect to generator cdef dict d = {} - for m, c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): if m[index] != 0: new_m = [u for u in m] new_m[index] += -1 @@ -1643,7 +1662,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if R is None: R = LaurentPolynomialRing(self.base_ring(), x) - return R({m[i]: c for m, c in self.dict().iteritems()}) + return R({m[i]: c for m, c in self.monomial_coefficients().items()}) def monomial_reduction(self): """ @@ -1713,7 +1732,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): cdef list f = [] cdef dict d for t in pf: - d = (t[0].dict()) + d = (t[0].monomial_coefficients()) if len(d) == 1: # monomials are units u *= self.parent(d) ** t[1] else: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 680c4c0138c..2e4cf5ce6ea 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -77,6 +77,8 @@ def is_LaurentPolynomialRing(R): _cache = {} + + def LaurentPolynomialRing(base_ring, *args, **kwds): r""" Return the globally unique univariate or multivariate Laurent polynomial @@ -251,6 +253,7 @@ def LaurentPolynomialRing(base_ring, *args, **kwds): _cache[R] = P return P + def _split_dict_(D, indices, group_by=None): r""" Split the dictionary ``D`` by ``indices`` and ``group_by``. @@ -331,6 +334,7 @@ def extract(T, indices): else: return result + def _split_laurent_polynomial_dict_(P, M, d): r""" Helper function for splitting a multivariate Laurent polynomial @@ -388,7 +392,9 @@ def value(d, R): return {k: value(v, P.base_ring()) for k, v in D.items()} except (ValueError, TypeError): pass - return sum(P({k: 1}) * value(v, P) for k, v in D.items()).dict() + return sum(P({k: 1}) * value(v, P) + for k, v in D.items()).monomial_coefficients() + def from_fraction_field(L, x): r""" @@ -444,6 +450,8 @@ def __init__(self, R): if R.ngens() != 1: raise ValueError("must be 1 generator") LaurentPolynomialRing_generic.__init__(self, R) + from sage.rings.integer_ring import IntegerRing + self._indices = IntegerRing() Element = LaurentPolynomial_univariate @@ -535,9 +543,9 @@ def _element_constructor_(self, x): P = x.parent() if set(self.variable_names()) & set(P.variable_names()): if isinstance(x, LaurentPolynomial_univariate): - d = {(k,): v for k, v in x.dict().items()} + d = {(k,): v for k, v in x.monomial_coefficients().items()} else: - d = x.dict() + d = x.monomial_coefficients() x = _split_laurent_polynomial_dict_(self, P, d) x = {k[0]: v for k, v in x.items()} elif P is self.base_ring(): @@ -545,7 +553,7 @@ def _element_constructor_(self, x): elif x.is_constant() and self.has_coerce_map_from(x.parent().base_ring()): return self(x.constant_coefficient()) elif len(self.variable_names()) == len(P.variable_names()): - x = x.dict() + x = x.monomial_coefficients() elif isinstance(x, FractionFieldElement): # since the field of fraction of self is defined corresponding to @@ -560,6 +568,12 @@ def _element_constructor_(self, x): return self.element_class(self, x) + def monomial(self, arg): + r""" + Return the monomial with the given exponent. + """ + return self.element_class(self, {arg: self.base_ring().one()}) + def __reduce__(self): """ Used in pickling. @@ -590,6 +604,9 @@ def __init__(self, R): if not R.base_ring().is_integral_domain(): raise ValueError("base ring must be an integral domain") LaurentPolynomialRing_generic.__init__(self, R) + from sage.modules.free_module import FreeModule + from sage.rings.integer_ring import IntegerRing + self._indices = FreeModule(IntegerRing(), R.ngens()) Element = LazyImport('sage.rings.polynomial.laurent_polynomial_mpair', 'LaurentPolynomial_mpair') @@ -604,7 +621,7 @@ def _repr_(self): """ return "Multivariate Laurent Polynomial Ring in %s over %s" % (", ".join(self._R.variable_names()), self._R.base_ring()) - def monomial(self, *args): + def monomial(self, *exponents): r""" Return the monomial whose exponents are given in argument. @@ -628,14 +645,27 @@ def monomial(self, *args): sage: L.monomial(1, 2, 3) # needs sage.modules Traceback (most recent call last): ... - TypeError: tuple key must have same length as ngens - """ - if len(args) != self.ngens(): - raise TypeError("tuple key must have same length as ngens") + TypeError: tuple key (1, 2, 3) must have same length as ngens (= 2) + + We also allow to specify the exponents in a single tuple:: + sage: L.monomial((-1, 2)) # needs sage.modules + x0^-1*x1^2 + + sage: L.monomial((-1, 2, 3)) # needs sage.modules + Traceback (most recent call last): + ... + TypeError: tuple key (-1, 2, 3) must have same length as ngens (= 2) + """ from sage.rings.polynomial.polydict import ETuple - m = ETuple(args, int(self.ngens())) - return self.element_class(self, self.polynomial_ring().one(), m) + if len(exponents) == 1 and isinstance((e := exponents[0]), (tuple, ETuple)): + exponents = e + + if len(exponents) != self.ngens(): + raise TypeError(f"tuple key {exponents} must have same length as ngens (= {self.ngens()})") + + m = ETuple(exponents, int(self.ngens())) + return self.element_class(self, self.polynomial_ring().base_ring().one(), m) def _element_constructor_(self, x, mon=None): """ @@ -762,9 +792,9 @@ def _element_constructor_(self, x, mon=None): pass elif set(self.variable_names()) & set(P.variable_names()): if isinstance(x, LaurentPolynomial_univariate): - d = {(k,): v for k, v in x.dict().items()} + d = {(k,): v for k, v in x.monomial_coefficients().items()} else: - d = x.dict() + d = x.monomial_coefficients() x = _split_laurent_polynomial_dict_(self, P, d) elif P is self.base_ring(): from sage.rings.polynomial.polydict import ETuple @@ -773,7 +803,7 @@ def _element_constructor_(self, x, mon=None): elif x.is_constant() and self.has_coerce_map_from(P.base_ring()): return self(x.constant_coefficient()) elif len(self.variable_names()) == len(P.variable_names()): - x = x.dict() + x = x.monomial_coefficients() elif isinstance(x, FractionFieldElement): # since the field of fraction of self is defined corresponding to diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py index 71eb20cd701..d9984a656b1 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py @@ -30,6 +30,7 @@ from sage.structure.parent import Parent from sage.combinat.integer_vector import IntegerVectors + class LaurentPolynomialRing_generic(CommutativeRing, Parent): """ Laurent polynomial ring (base class). @@ -42,6 +43,8 @@ class LaurentPolynomialRing_generic(CommutativeRing, Parent): sage: R. = LaurentPolynomialRing(QQ) sage: R.category() Join of Category of unique factorization domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets diff --git a/src/sage/rings/polynomial/meson.build b/src/sage/rings/polynomial/meson.build new file mode 100644 index 00000000000..c1625c5eb94 --- /dev/null +++ b/src/sage/rings/polynomial/meson.build @@ -0,0 +1,174 @@ +py.install_sources( + 'all.py', + 'binary_form_reduce.py', + 'commutative_polynomial.pxd', + 'complex_roots.py', + 'convolution.py', + 'evaluation.pxd', + 'evaluation_flint.pxd', + 'evaluation_ntl.pxd', + 'flatten.py', + 'groebner_fan.py', + 'ideal.py', + 'infinite_polynomial_element.py', + 'infinite_polynomial_ring.py', + 'integer_valued_polynomials.py', + 'laurent_polynomial.pxd', + 'laurent_polynomial_ideal.py', + 'laurent_polynomial_mpair.pxd', + 'laurent_polynomial_ring.py', + 'laurent_polynomial_ring_base.py', + 'msolve.py', + 'multi_polynomial.pxd', + 'multi_polynomial_element.py', + 'multi_polynomial_ideal.py', + 'multi_polynomial_ideal_libsingular.pxd', + 'multi_polynomial_libsingular.pxd', + 'multi_polynomial_ring.py', + 'multi_polynomial_ring_base.pxd', + 'multi_polynomial_sequence.py', + 'omega.py', + 'ore_function_element.py', + 'ore_function_field.py', + 'ore_polynomial_element.pxd', + 'ore_polynomial_ring.py', + 'plural.pxd', + 'polydict.pxd', + 'polynomial_compiled.pxd', + 'polynomial_complex_arb.pxd', + 'polynomial_element.pxd', + 'polynomial_element_generic.py', + 'polynomial_fateman.py', + 'polynomial_gf2x.pxd', + 'polynomial_integer_dense_flint.pxd', + 'polynomial_integer_dense_ntl.pxd', + 'polynomial_modn_dense_ntl.pxd', + 'polynomial_quotient_ring.py', + 'polynomial_quotient_ring_element.py', + 'polynomial_rational_flint.pxd', + 'polynomial_ring.py', + 'polynomial_ring_constructor.py', + 'polynomial_ring_homomorphism.pxd', + 'polynomial_singular_interface.py', + 'polynomial_zmod_flint.pxd', + 'polynomial_zz_pex.pxd', + 'real_roots.pxd', + 'skew_polynomial_element.pxd', + 'skew_polynomial_finite_field.pxd', + 'skew_polynomial_finite_order.pxd', + 'skew_polynomial_ring.py', + 'symmetric_ideal.py', + 'symmetric_reduction.pxd', + 'term_order.py', + 'toy_buchberger.py', + 'toy_d_basis.py', + 'toy_variety.py', + subdir: 'sage/rings/polynomial', +) + +extension_data = { + 'commutative_polynomial' : files('commutative_polynomial.pyx'), + 'cyclotomic' : files('cyclotomic.pyx'), + 'evaluation_flint' : files('evaluation_flint.pyx'), + 'hilbert' : files('hilbert.pyx'), + 'laurent_polynomial' : files('laurent_polynomial.pyx'), + 'laurent_polynomial_mpair' : files('laurent_polynomial_mpair.pyx'), + 'multi_polynomial' : files('multi_polynomial.pyx'), + 'multi_polynomial_ring_base' : files('multi_polynomial_ring_base.pyx'), + 'ore_polynomial_element' : files('ore_polynomial_element.pyx'), + 'polydict' : files('polydict.pyx'), + 'polynomial_compiled' : files('polynomial_compiled.pyx'), + 'polynomial_complex_arb' : files('polynomial_complex_arb.pyx'), + 'polynomial_element' : files('polynomial_element.pyx'), + 'polynomial_number_field' : files('polynomial_number_field.pyx'), + 'polynomial_real_mpfr_dense' : files('polynomial_real_mpfr_dense.pyx'), + 'polynomial_ring_homomorphism' : files('polynomial_ring_homomorphism.pyx'), + 'real_roots' : files('real_roots.pyx'), + 'refine_root' : files('refine_root.pyx'), + 'skew_polynomial_element' : files('skew_polynomial_element.pyx'), + 'skew_polynomial_finite_field' : files('skew_polynomial_finite_field.pyx'), + 'skew_polynomial_finite_order' : files('skew_polynomial_finite_order.pyx'), + 'symmetric_reduction' : files('symmetric_reduction.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/polynomial', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + flint, + givaro, + gmp, + mpfi, + mpfr, + ntl, + pari, + ], + ) +endforeach + +extension_data_cpp = { + 'evaluation_ntl': files('evaluation_ntl.pyx'), + 'multi_polynomial_ideal_libsingular': files( + 'multi_polynomial_ideal_libsingular.pyx', + ), + 'multi_polynomial_libsingular': files('multi_polynomial_libsingular.pyx'), + 'plural': files('plural.pyx'), + 'polynomial_gf2x': files('polynomial_gf2x.pyx'), + 'polynomial_integer_dense_flint': files('polynomial_integer_dense_flint.pyx'), + 'polynomial_integer_dense_ntl': files('polynomial_integer_dense_ntl.pyx'), + 'polynomial_modn_dense_ntl': files('polynomial_modn_dense_ntl.pyx'), + 'polynomial_rational_flint': files('polynomial_rational_flint.pyx'), + 'polynomial_zmod_flint': files('polynomial_zmod_flint.pyx'), + 'polynomial_zz_pex': files('polynomial_zz_pex.pyx'), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/polynomial', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_flint, + inc_ntl, + inc_numpy, + inc_rings, + inc_rings_finite, + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + flint, + givaro, + gmp, + mpfi, + mpfr, + ntl, + pari, + singular, + ], + ) +endforeach + +install_subdir('padics', install_dir: sage_install_dir / 'rings/polynomial') +subdir('pbori') +subdir('weil') diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index fb22236fba9..d261bfd70b9 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -33,6 +33,7 @@ from sage.rings.real_mpfi import RealIntervalField_class, RealIntervalField from sage.structure.sequence import Sequence + def _run_msolve(ideal, options): r""" Internal utility function @@ -63,6 +64,7 @@ def _run_msolve(ideal, options): return msolve_out.stdout + def groebner_basis_degrevlex(ideal, proof=True): r""" Compute a degrevlex Gröbner basis using msolve @@ -111,6 +113,7 @@ def groebner_basis_degrevlex(ideal, proof=True): gbasis = sage_eval(msolve_out[:-2], locals=drlpolring.gens_dict()) return Sequence(gbasis) + def variety(ideal, ring, *, proof=True): r""" Compute the variety of a zero-dimensional ideal using msolve. diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 6863e5a7316..ca25adc23f0 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -243,7 +243,7 @@ cdef class MPolynomial(CommutativePolynomial): - Didier Deshommes """ - d = self.dict() + d = self.monomial_coefficients() return [d[i] for i in self.exponents()] def truncate(self, var, n): @@ -260,8 +260,8 @@ cdef class MPolynomial(CommutativePolynomial): ind = Z.index(var) except ValueError: raise ValueError("var must be one of the generators of the parent polynomial ring.") - d = self.dict() - return R(dict([(k, c) for k, c in d.iteritems() if k[ind] < n])) + return R({k: c for k, c in self.monomial_coefficients().items() + if k[ind] < n}) def _fast_callable_(self, etb): r""" @@ -299,9 +299,9 @@ cdef class MPolynomial(CommutativePolynomial): n = len(x) expr = etb.constant(self.base_ring().zero()) - for (m, c) in self.dict().iteritems(): - monom = prod([ x[i]**m[i] for i in range(n) if m[i] != 0], - etb.constant(c)) + for m, c in self.monomial_coefficients().items(): + monom = prod([x[i] ** m[i] for i in range(n) if m[i] != 0], + etb.constant(c)) expr = expr + monom return expr @@ -426,12 +426,12 @@ cdef class MPolynomial(CommutativePolynomial): d = self.degree(var) B = ring.base_ring() w = {remove_from_tuple(e, ind): val - for e, val in self.dict().iteritems() if not e[ind]} + for e, val in self.monomial_coefficients().items() if not e[ind]} v = [B(w)] # coefficients that don't involve var z = var for i in range(1,d+1): - c = self.coefficient(z).dict() - w = {remove_from_tuple(e, ind): val for e, val in c.iteritems()} + c = self.coefficient(z).monomial_coefficients() + w = {remove_from_tuple(e, ind): val for e, val in c.items()} v.append(B(w)) z *= var return ring(v) @@ -483,7 +483,7 @@ cdef class MPolynomial(CommutativePolynomial): vars = self._parent.variable_names_recursive() cdef tuple my_vars = self._parent.variable_names() if vars == my_vars: - return self.dict() + return self.monomial_coefficients() elif my_vars[-1] not in vars: x = base_ring(self) if base_ring is not None else self const_ix = ETuple((0,)*len(vars)) @@ -511,13 +511,13 @@ cdef class MPolynomial(CommutativePolynomial): new_map[k] -= m tmp = [0] * (len(vars) - m) try: - for ix,a in self.dict().iteritems(): + for ix, a in self.monomial_coefficients().items(): for k in range(len(my_vars)): tmp[new_map[k]] = ix[k] postfix = ETuple(tmp) mpoly = a._mpoly_dict_recursive(prev_vars, base_ring) - for prefix,b in mpoly.iteritems(): - D[prefix+postfix] = b + for prefix, b in mpoly.items(): + D[prefix + postfix] = b return D except AttributeError: @@ -527,7 +527,7 @@ cdef class MPolynomial(CommutativePolynomial): base_ring = None tmp = [0] * len(vars) - for ix,a in self.dict().iteritems(): + for ix, a in self.monomial_coefficients().items(): for k in range(len(my_vars)): tmp[mapping[k]] = ix[k] if base_ring is not None: @@ -578,7 +578,7 @@ cdef class MPolynomial(CommutativePolynomial): cdef long result_mon var_name_hash = [hash(v) for v in self._parent.variable_names()] cdef long c_hash - for m,c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): # I'm assuming (incorrectly) that hashes of zero indicate that the element is 0. # This assumption is not true, but I think it is true enough for the purposes and it # it allows us to write fast code that omits terms with 0 coefficients. This is @@ -913,7 +913,7 @@ cdef class MPolynomial(CommutativePolynomial): """ if isinstance(R, Map): return self.map_coefficients(R) - return self.parent().change_ring(R)(self.dict()) + return self.parent().change_ring(R)(self.monomial_coefficients()) def is_symmetric(self, group=None): r""" @@ -997,7 +997,7 @@ cdef class MPolynomial(CommutativePolynomial): raise ValueError("argument must be a permutation group") gens = [S(g) for g in gens] - cdef dict coeffs = self.dict() + cdef dict coeffs = self.monomial_coefficients() zero = self.base_ring().zero() return all(coeffs.get(g._act_on_etuple_on_position(e), zero) == coeff for e, coeff in coeffs.items() for g in gens) @@ -1114,7 +1114,7 @@ cdef class MPolynomial(CommutativePolynomial): TESTS:: - sage: # needs sage.libs.giac + sage: # needs giac sage: R. = GF(101)['e,i'][] sage: f = R('e*i') * x + y^2 sage: f._giac_init_() @@ -1360,7 +1360,7 @@ cdef class MPolynomial(CommutativePolynomial): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R(dict([(k,f(v)) for (k,v) in self.dict().items()])) + return R({k: f(v) for k, v in self.monomial_coefficients().items()}) def _norm_over_nonprime_finite_field(self): r""" @@ -2611,7 +2611,7 @@ cdef class MPolynomial(CommutativePolynomial): # K[x,y,...] as K[x][y]... if not self.constant_coefficient().is_unit(): return False - cdef dict d = self.dict() + cdef dict d = self.monomial_coefficients() cdef ETuple zero_key = ETuple({}, int(self.parent().ngens())) d.pop(zero_key, None) return all(d[k].is_nilpotent() for k in d) @@ -2646,7 +2646,7 @@ cdef class MPolynomial(CommutativePolynomial): # Section 7.3 Exercise 33). # This generalizes easily to the multivariate case, by considering # K[x,y,...] as K[x][y]... - d = self.dict() + d = self.monomial_coefficients() return all(c.is_nilpotent() for c in d.values()) def _test_subs(self, tester=None, **options): @@ -2665,7 +2665,7 @@ cdef class MPolynomial(CommutativePolynomial): if gens: # substituting all variables (in a polynomial ring with variables) with 0 - d = {str(gen): 0 for gen in gens} + d = {str(gen): self.base_ring().zero() for gen in gens} tester.assertEqual(self.subs(**d).parent(), self.parent().base_ring()) # substituting all variables (in a polynomial ring with variables) @@ -2678,7 +2678,7 @@ cdef class MPolynomial(CommutativePolynomial): if len(gens) > 1: # substituting one variable (in a polynomial ring with variables) with 0 - d = {str(gens[0]): 0} + d = {str(gens[0]): self.base_ring().zero()} tester.assertEqual(self.subs(**d).parent(), self.parent()) # test error checking: partial substitution by elements diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 0e8d831b808..6c4ec02c1b1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -37,7 +37,7 @@ ....: + (a0*b2 - a1*b3 + a2*b0 + a3*b1)^2 + (a0*b3 + a1*b2 - a2*b1 + a3*b0)^2) True """ -#***************************************************************************** +# **************************************************************************** # # Sage: Open Source Mathematical Software # @@ -54,7 +54,7 @@ # The full text of the GPL is available at: # # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** import operator @@ -176,9 +176,14 @@ def __call__(self, *x, **kwds): K = x[0].parent() except AttributeError: K = self.parent().base_ring() - y = K(0) + try: + y = K.zero() + one = K.one() + except (AttributeError, RuntimeError): + y = K(0) + one = K(1) for m, c in self.element().dict().items(): - y += c * prod(v ** e for v, e in zip(x, m) if e) + y += c * prod((v ** e for v, e in zip(x, m) if e), one) return y def _richcmp_(self, right, op): @@ -797,13 +802,28 @@ def monomial_coefficient(self, mon): zero = self.parent().base_ring().zero() return self.element().get(exp, zero) - def dict(self): + def monomial_coefficients(self): """ Return underlying dictionary with keys the exponents and values the coefficients of this polynomial. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = PolynomialRing(QQbar, order='lex') + sage: f = (x^1*y^5*z^2 + x^2*z + x^4*y^1*z^3) + sage: f.monomial_coefficients() + {(1, 5, 2): 1, (2, 0, 1): 1, (4, 1, 3): 1} + + ``dict`` is an alias:: + + sage: f.dict() # needs sage.rings.number_field + {(1, 5, 2): 1, (2, 0, 1): 1, (4, 1, 3): 1} """ return self.element().dict() + dict = monomial_coefficients + def __iter__(self): """ Iterate over ``self`` respecting the term order. @@ -1285,8 +1305,17 @@ def is_homogeneous(self): False sage: (x^2*y + y^2*x).is_homogeneous() True + + The weight of the parent ring is respected:: + + sage: term_order = TermOrder("wdegrevlex", [1, 3]) + sage: R. = PolynomialRing(Qp(5), order=term_order) + sage: (x + y).is_homogeneous() + False + sage: (x^3 + y).is_homogeneous() + True """ - return self.element().is_homogeneous() + return self.element().is_homogeneous(self.parent().term_order().weights()) def _homogenize(self, var): r""" @@ -1589,7 +1618,7 @@ def univariate_polynomial(self, R=None): return R(0) #construct list - lookup = [int(0),] * len(next(iter(monomial_coefficients))) + lookup = [0,] * len(next(iter(monomial_coefficients))) coefficients = [] for degree in range(max(m[var_idx] for m in monomial_coefficients.keys()) + 1): @@ -1840,12 +1869,12 @@ def _floordiv_(self, right): """ # handle division by monomials without using Singular - if len(right.dict()) == 1: + if len(right.monomial_coefficients()) == 1: P = self.parent() ret = P(0) - denC,denM = next(iter(right)) - for c,m in self: - t = c*m + denC, denM = next(iter(right)) + for c, m in self: + t = c * m if denC.divides(c) and P.monomial_divides(denM, m): ret += P.monomial_quotient(t, right, coeff=True) return ret @@ -1901,7 +1930,8 @@ def _derivative(self, var=None): if index == -1: # var is not a generator; do term-by-term differentiation recursively # var may be, for example, a generator of the base ring - d = dict([(e, x._derivative(var)) for (e, x) in self.dict().items()]) + d = {e: x._derivative(var) + for e, x in self.monomial_coefficients().items()} d = polydict.PolyDict(d, check=False) d.remove_zeros() return MPolynomial_polydict(P, d) @@ -1998,7 +2028,7 @@ def integral(self, var=None): # var is not a generator; do term-by-term integration recursively # var may be, for example, a generator of the base ring d = {e: x.integral(var) - for e, x in self.dict().items()} + for e, x in self.monomial_coefficients().items()} d = polydict.PolyDict(d, check=False) d.remove_zeros() else: @@ -2466,6 +2496,7 @@ def reduce(self, I): p -= plt return r + ############################################################### # Useful for some geometry code. ############################################################### diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index bb9ee33b86f..8e358652be3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -176,7 +176,7 @@ The Groebner basis modulo any product of the prime factors is also non-trivial:: sage: I.change_ring(P.change_ring(IntegerModRing(2 * 7))).groebner_basis() - [x + 9*y + 13*z, y^2 + 3*y, y*z + 7*y + 6, 2*y + 6, z^2 + 3, 2*z + 10] + [x + ..., y^2 + 3*y, y*z + 7*y + 6, 2*y + 6, z^2 + 3, 2*z + 10] Modulo any other prime the Groebner basis is trivial so there are no other solutions. For example:: @@ -450,6 +450,7 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) B = PolynomialSequence([R(e) for e in mgb], R, immutable=True) return B + class MPolynomialIdeal_singular_base_repr: @require_field def syzygy_module(self): @@ -2311,6 +2312,7 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): You can use Giac to compute the elimination ideal:: + sage: # needs sage.libs.giac sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm='giac') == J possible output... True @@ -2331,7 +2333,7 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): Check that this method works over QQbar (:issue:`25351`):: - sage: # needs sage.rings.number_field + sage: # needs sage.rings.number_field sage.libs.giac sage: R. = QQbar[] sage: I = R * [x - t, y - t^2, z - t^3, s - x + y^3] sage: J = I.elimination_ideal([t, s]); J @@ -3492,6 +3494,7 @@ def _reduce_using_macaulay2(self, f): R = self.ring() return R(k) + class NCPolynomialIdeal(MPolynomialIdeal_singular_repr, Ideal_nc): def __init__(self, ring, gens, coerce=True, side='left'): r""" @@ -4366,12 +4369,12 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal reverse lexicographical ordering here, in order to test against :issue:`21884`:: + sage: # needs sage.libs.giac sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching sage: J = I.change_ring(P.change_ring(order='degrevlex')) sage: gb = J.groebner_basis('giac') # random sage: gb [c^3 - 79/210*c^2 + 1/30*b + 1/70*c, b^2 - 3/5*c^2 - 1/5*b + 1/5*c, b*c + 6/5*c^2 - 1/10*b - 2/5*c, a + 2*b + 2*c - 1] - sage: J.groebner_basis.set_cache(gb) sage: ideal(J.transformed_basis()).change_ring(P).interreduced_basis() # testing issue #21884 ...[a - 60*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 - 79/7*c^2 + 3/7*c, c^4 - 10/21*c^3 + 1/84*c^2 + 1/84*c] @@ -4379,6 +4382,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Giac's gbasis over `\QQ` can benefit from a probabilistic lifting and multi threaded operations:: + sage: # needs sage.libs.giac sage: A9 = PolynomialRing(QQ, 9, 'x') sage: I9 = sage.rings.ideal.Katsura(A9) sage: print("possible output from giac", flush=True); I9.groebner_basis("giac", proba_epsilon=1e-7) # long time (3s) @@ -4630,7 +4634,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal sage: I.groebner_basis('libsingular:slimgb') [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: # needs sage.rings.number_field + sage: # needs sage.rings.number_field sage.libs.giac sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching sage: J = I.change_ring(P.change_ring(order='degrevlex')) sage: gb = J.groebner_basis('giac') # random diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx index c97dbf9cca3..70b1b3fdcf1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx @@ -253,7 +253,7 @@ def slimgb_libsingular(I): i = sage_ideal_to_singular_ideal(I) r = currRing - if r.OrdSgn!=1 : + if r.OrdSgn != 1: id_Delete(&i, r) raise TypeError("ordering must be global for slimgb") @@ -267,11 +267,11 @@ def slimgb_libsingular(I): result = t_rep_gb(r, i, i.rank, 0) sig_off() - id_Delete(&i,r) + id_Delete(&i, r) - res = singular_ideal_to_sage_sequence(result,r,I.ring()) + res = singular_ideal_to_sage_sequence(result, r, I.ring()) - id_Delete(&result,r) + id_Delete(&result, r) return res @@ -322,19 +322,19 @@ def interred_libsingular(I): singular_options = bck # divide head by coefficients - if r.cf.type != n_Z and r.cf.type != n_Znm and r.cf.type != n_Zn and r.cf.type != n_Z2m : + if r.cf.type != n_Z and r.cf.type != n_Znm and r.cf.type != n_Zn and r.cf.type != n_Z2m: for j from 0 <= j < IDELEMS(result): p = result.m[j] if p: - n = p_GetCoeff(p,r) - n = r.cf.cfInvers(n,r.cf) + n = p_GetCoeff(p, r) + n = r.cf.cfInvers(n, r.cf) result.m[j] = pp_Mult_nn(p, n, r) - p_Delete(&p,r) - n_Delete(&n,r.cf) + p_Delete(&p, r) + n_Delete(&n, r.cf) - id_Delete(&i,r) + id_Delete(&i, r) res = sorted(singular_ideal_to_sage_sequence(result,r,I.ring()),reverse=True) - id_Delete(&result,r) + id_Delete(&result, r) return res diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 318a58e6d9c..d05760d84cd 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -855,7 +855,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if element.parent() == self: bucket = sBucketCreate(_ring) try: - for (m,c) in element.element().dict().iteritems(): + for m, c in element.element().dict().items(): mon = p_Init(_ring) p_SetCoeff(mon, sa2si(c, _ring), _ring) for pos in m.nonzero_positions(): @@ -893,13 +893,13 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if element.parent().ngens() <= self.ngens(): bucket = sBucketCreate(_ring) try: - for (m,c) in element.element().dict().iteritems(): + for m, c in element.element().dict().items(): if check: c = base_ring(c) if not c: continue mon = p_Init(_ring) - p_SetCoeff(mon, sa2si(c , _ring), _ring) + p_SetCoeff(mon, sa2si(c, _ring), _ring) for pos in m.nonzero_positions(): overflow_check(m[pos], _ring) p_SetExp(mon, ind_map[pos], m[pos], _ring) @@ -923,13 +923,13 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): else: bucket = sBucketCreate(_ring) try: - for (m,c) in element.iteritems(): + for m, c in element.items(): if check: c = base_ring(c) if not c: continue mon = p_Init(_ring) - p_SetCoeff(mon, sa2si(c , _ring), _ring) + p_SetCoeff(mon, sa2si(c, _ring), _ring) if len(m) != self.ngens(): raise TypeError("tuple key must have same length as ngens") for pos from 0 <= pos < len(m): @@ -1616,7 +1616,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): res = pMDivide(f._poly, g._poly) if coeff: if r.cf.type == n_unknown or r.cf.cfDivBy(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf): - n = r.cf.cfDiv( p_GetCoeff(f._poly, r) , p_GetCoeff(g._poly, r), r.cf) + n = r.cf.cfDiv(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf) p_SetCoeff0(res, n, r) else: raise ArithmeticError("Cannot divide these coefficients.") @@ -2088,10 +2088,11 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): except TypeError: # give up, evaluate functional sage_res = parent.base_ring().zero() - for m, c in self.dict().iteritems(): + for m, c in self.monomial_coefficients().items(): sage_res += c * mul([x[i] ** m[i] for i in m.nonzero_positions()]) else: - singular_polynomial_call(&res, self._poly, _ring, coerced_x, MPolynomial_libsingular_get_element) + singular_polynomial_call(&res, self._poly, _ring, coerced_x, + MPolynomial_libsingular_get_element) if res == NULL: return res_parent(0) @@ -2350,7 +2351,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): else: return (left._parent).fraction_field()(left,right_ringelement) - def __pow__(MPolynomial_libsingular self, exp, ignored): + def __pow__(MPolynomial_libsingular self, exp, mod): """ Return ``self**(exp)``. @@ -2413,7 +2414,20 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): Traceback (most recent call last): ... TypeError: R is neither an integer nor a rational + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(ZZ) + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + if type(exp) is not Integer: try: exp = Integer(exp) @@ -2983,7 +2997,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): return self._parent._base._zero_element - def dict(self): + def monomial_coefficients(self): """ Return a dictionary representing ``self``. This dictionary is in the same format as the generic MPolynomial: The dictionary @@ -2992,7 +3006,12 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): EXAMPLES:: sage: R. = QQ[] - sage: f=2*x*y^3*z^2 + 1/7*x^2 + 2/3 + sage: f = 2*x*y^3*z^2 + 1/7*x^2 + 2/3 + sage: f.monomial_coefficients() + {(0, 0, 0): 2/3, (1, 3, 2): 2, (2, 0, 0): 1/7} + + ``dict`` is an alias:: + sage: f.dict() {(0, 0, 0): 2/3, (1, 3, 2): 2, (2, 0, 0): 1/7} """ @@ -3016,6 +3035,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): p = pNext(p) return pd + dict = monomial_coefficients + def iterator_exp_coeff(self, as_ETuples=True): """ Iterate over ``self`` as pairs of ((E)Tuple, coefficient). @@ -3607,8 +3628,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): for m, v in items: gg[g.index(m)] = v y = parent.base_ring().zero() - for (m,c) in self.dict().items(): - y += c*mul([ gg[i]**m[i] for i in m.nonzero_positions()]) + for m, c in self.monomial_coefficients().items(): + y += c*mul([gg[i] ** m[i] for i in m.nonzero_positions()]) return y _f = ( v)._poly if p_IsConstant(_f, _ring): @@ -4158,7 +4179,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): quo = p_Add_q(quo, temp, r) p = pNext(p) return new_MP(parent, quo) - if r.cf.type == n_Znm or r.cf.type == n_Zn or r.cf.type == n_Z2m : + if r.cf.type == n_Znm or r.cf.type == n_Zn or r.cf.type == n_Z2m: raise NotImplementedError("Division of multivariate polynomials over non fields by non-monomials not implemented.") count = singular_polynomial_length_bounded(self._poly, 15) @@ -4468,23 +4489,25 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): raise NotImplementedError("Factorization of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.") # I make a temporary copy of the poly in self because singclap_factorize appears to modify it's parameter - ptemp = p_Copy(self._poly,_ring) + ptemp = p_Copy(self._poly, _ring) iv = NULL sig_on() - if _ring!=currRing: rChangeCurrRing(_ring) # singclap_factorize - I = singclap_factorize ( ptemp, &iv , 0, _ring) + if _ring != currRing: + rChangeCurrRing(_ring) # singclap_factorize + I = singclap_factorize(ptemp, &iv, 0, _ring) sig_off() ivv = iv.ivGetVec() - v = [(new_MP(parent, p_Copy(I.m[i],_ring)) , ivv[i]) for i in range(1,I.ncols)] - v = [(f,m) for f,m in v if f!=0] # we might have zero in there - unit = new_MP(parent, p_Copy(I.m[0],_ring)) + v = [(new_MP(parent, p_Copy(I.m[i], _ring)), ivv[i]) + for i in range(1, I.ncols)] + v = [(f, m) for f, m in v if f != 0] # we might have zero in there + unit = new_MP(parent, p_Copy(I.m[0], _ring)) - F = Factorization(v,unit) + F = Factorization(v, unit) F.sort() del iv - id_Delete(&I,_ring) + id_Delete(&I, _ring) return F @@ -4572,20 +4595,21 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): try: f = parent.coerce(f) except TypeError as msg: - id_Delete(&fI,r) - id_Delete(&_I,r) + id_Delete(&fI, r) + id_Delete(&_I, r) raise TypeError(msg) _I.m[i] = p_Copy((f)._poly, r) - i+=1 + i += 1 - fI.m[0]= p_Copy(self._poly, r) + fI.m[0] = p_Copy(self._poly, r) - if r!=currRing: rChangeCurrRing(r) # idLift + if r != currRing: + rChangeCurrRing(r) # idLift sig_on() res = idLift(_I, fI, NULL, 0, 0, 0) sig_off() - if errorreported != 0 : + if errorreported != 0: errorcode = errorreported errorreported = 0 if errorcode == 1: @@ -4602,7 +4626,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): id_Delete(&res, r) return Sequence(l, check=False, immutable=True) - def reduce(self,I): + def reduce(self, I): r""" Return a remainder of this polynomial modulo the polynomials in ``I``. @@ -4858,7 +4882,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): return right if _ring.cf.type != n_unknown: - if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m : + if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m: raise NotImplementedError("GCD over rings not implemented.") if n_GetChar(_ring.cf) > 1<<29: @@ -4929,7 +4953,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): if _ring!=currRing: rChangeCurrRing(_ring) if _ring.cf.type != n_unknown: - if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m : + if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m: raise TypeError("LCM over non-integral domains not available.") if self._parent is not g._parent: @@ -4944,7 +4968,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): + singular_polynomial_length_bounded(_g._poly,20) if count >= 20: sig_on() - if _ring!=currRing: rChangeCurrRing(_ring) # singclap_gcd + if _ring != currRing: + rChangeCurrRing(_ring) # singclap_gcd gcd = singclap_gcd(p_Copy(self._poly, _ring), p_Copy(_g._poly, _ring), _ring ) prod = pp_Mult_qq(self._poly, _g._poly, _ring) ret = p_Divide(prod, gcd, _ring) @@ -5218,7 +5243,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): sage: parent(h(R.0,0)) Univariate Polynomial Ring in x over Rational Field """ - return unpickle_MPolynomial_libsingular, (self._parent, self.dict()) + return unpickle_MPolynomial_libsingular, (self._parent, + self.monomial_coefficients()) def _im_gens_(self, codomain, im_gens, base_map=None): """ @@ -5259,8 +5285,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): if base_map is None: # Just use conversion base_map = codomain - for (m,c) in self.dict().iteritems(): - y += base_map(c)*mul([ im_gens[i]**m[i] for i in range(n) if m[i]]) + for m, c in self.monomial_coefficients().items(): + y += base_map(c) * mul([im_gens[i] ** m[i] for i in range(n) if m[i]]) return y def _derivative(self, MPolynomial_libsingular var): @@ -5387,7 +5413,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): v = ETuple({index: 1}, len(gens)) _p = p_ISet(0, _ring) - for (exp, coeff) in self.dict().iteritems(): + for exp, coeff in self.monomial_coefficients().items(): nexp = exp.eadd(v) # new exponent mon = p_Init(_ring) p_SetCoeff(mon, sa2si(coeff / (1 + exp[index]), _ring), _ring) @@ -5476,12 +5502,15 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): elif not self._parent._base.is_field(): raise ValueError("Resultants require base fields or integer base ring.") - cdef int count = singular_polynomial_length_bounded(self._poly,20) \ + cdef int count = singular_polynomial_length_bounded(self._poly, 20) \ + singular_polynomial_length_bounded(other._poly,20) if count >= 20: sig_on() if _ring != currRing: rChangeCurrRing(_ring) # singclap_resultant - rt = singclap_resultant(p_Copy(self._poly, _ring), p_Copy(other._poly, _ring), p_Copy((variable)._poly , _ring ), _ring) + rt = singclap_resultant(p_Copy(self._poly, _ring), + p_Copy(other._poly, _ring), + p_Copy((variable)._poly, _ring ), + _ring) if count >= 20: sig_off() return new_MP(self._parent, rt) @@ -5890,9 +5919,9 @@ def unpickle_MPolynomial_libsingular(MPolynomialRing_libsingular R, d): rChangeCurrRing(r) bucket = sBucketCreate(r) try: - for mon,c in d.iteritems(): + for mon, c in d.items(): m = p_Init(r) - for i,e in mon.sparse_iter(): + for i, e in mon.sparse_iter(): _i = i if _i >= r.N: p_Delete(&m, r) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 588358ab3d9..43f1c0f6294 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -430,13 +430,16 @@ def __call__(self, x=0, check=True): if P is self: return x - elif P == self: + + if P == self: return MPolynomial_polydict(self, x.element().dict()) - elif self.base_ring().has_coerce_map_from(P): + + if self.base_ring().has_coerce_map_from(P): # it might be in the base ring (i.e. a poly ring over a poly ring) c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple: c}) - elif len(P.variable_names()) == len(self.variable_names()): + + if len(P.variable_names()) == len(self.variable_names()): # Map the variables in some crazy way (but in order, # of course). This is here since R(blah) is supposed # to be "make an element of R if at all possible with @@ -446,63 +449,76 @@ def __call__(self, x=0, check=True): for i, a in D.items(): D[i] = K(a) return MPolynomial_polydict(self, D) - elif set(P.variable_names()).issubset(set(self.variable_names())) and self.base_ring().has_coerce_map_from(P.base_ring()): + + if (set(P.variable_names()).issubset(set(self.variable_names())) + and self.base_ring().has_coerce_map_from(P.base_ring())): # If the named variables are a superset of the input, map the variables by name return MPolynomial_polydict(self, self._extract_polydict(x)) - else: - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) + + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) elif isinstance(x, MPolynomial_libsingular): P = x.parent() if P == self: - return MPolynomial_polydict(self, x.dict()) - elif self.base_ring().has_coerce_map_from(P): + return MPolynomial_polydict(self, x.monomial_coefficients()) + + if self.base_ring().has_coerce_map_from(P): # it might be in the base ring (i.e. a poly ring over a poly ring) c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple: c}) - elif len(P.variable_names()) == len(self.variable_names()): + + if len(P.variable_names()) == len(self.variable_names()): # Map the variables in some crazy way (but in order, # of course). This is here since R(blah) is supposed # to be "make an element of R if at all possible with # no guarantees that this is mathematically solid." K = self.base_ring() - D = x.dict() + D = x.monomial_coefficients() for i, a in D.items(): D[i] = K(a) return MPolynomial_polydict(self, D) - elif set(P.variable_names()).issubset(set(self.variable_names())) and self.base_ring().has_coerce_map_from(P.base_ring()): + + if (set(P.variable_names()).issubset(set(self.variable_names())) + and self.base_ring().has_coerce_map_from(P.base_ring())): # If the named variables are a superset of the input, map the variables by name return MPolynomial_polydict(self, self._extract_polydict(x)) - else: - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) - elif isinstance(x, polynomial_element.Polynomial): - return MPolynomial_polydict(self, x._mpoly_dict_recursive(self.variable_names(), self.base_ring())) + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) + + if isinstance(x, polynomial_element.Polynomial): + return MPolynomial_polydict(self, + x._mpoly_dict_recursive(self.variable_names(), + self.base_ring())) - elif isinstance(x, PolyDict): + if isinstance(x, PolyDict): return MPolynomial_polydict(self, x) - elif isinstance(x, dict): + if isinstance(x, dict): K = self.base_ring() return MPolynomial_polydict(self, {i: K(a) for i, a in x.items()}) - elif isinstance(x, fraction_field_element.FractionFieldElement) and x.parent().ring() == self: + if (isinstance(x, fraction_field_element.FractionFieldElement) + and x.parent().ring() == self): if x.denominator() == 1: return x.numerator() - else: - raise TypeError("unable to coerce since the denominator is not 1") - elif isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: + raise TypeError("unable to coerce since the denominator is not 1") + + if isinstance(x, sage.interfaces.abc.SingularElement) and self._has_singular: self._singular_().set_ring() try: return x.sage_poly(self) except TypeError: raise TypeError("unable to coerce singular object") - elif hasattr(x, '_polynomial_'): + if hasattr(x, '_polynomial_'): return x._polynomial_(self) - elif isinstance(x, str): + if isinstance(x, str): from sage.misc.sage_eval import sage_eval try: x = sage_eval(x, self.gens_dict_recursive()) @@ -510,7 +526,7 @@ def __call__(self, x=0, check=True): raise TypeError("unable to evaluate {!r} in {}".format(x, self)) return self(x) - elif isinstance(x, sage.interfaces.abc.Macaulay2Element): + if isinstance(x, sage.interfaces.abc.Macaulay2Element): try: s = x.sage_polystring() if len(s) == 0: @@ -524,7 +540,7 @@ def __call__(self, x=0, check=True): raise TypeError("Unable to coerce macaulay2 object") return MPolynomial_polydict(self, x) - elif isinstance(x, pari_gen) and x.type() == 't_POL': + if isinstance(x, pari_gen) and x.type() == 't_POL': # This recursive approach is needed because PARI # represents multivariate polynomials as iterated # univariate polynomials. Below, v is the variable @@ -538,9 +554,9 @@ def __call__(self, x=0, check=True): if isinstance(x, dict): return MPolynomial_polydict(self, x) - else: - c = self.base_ring()(x) - return MPolynomial_polydict(self, {self._zero_tuple: c}) + + c = self.base_ring()(x) + return MPolynomial_polydict(self, {self._zero_tuple: c}) # The following methods are handy for implementing Groebner # basis algorithms. They do only superficial type/sanity checks @@ -627,8 +643,8 @@ def monomial_quotient(self, f, g, coeff=False): if not g: raise ZeroDivisionError - fd = f.dict() - gd = g.dict() + fd = f.monomial_coefficients() + gd = g.monomial_coefficients() if not coeff: f = next(iter(fd)) @@ -682,8 +698,8 @@ def monomial_lcm(self, f, g): """ one = self.base_ring().one() - f, = f.dict() - g, = g.dict() + f, = f.monomial_coefficients() + g, = g.monomial_coefficients() length = len(f) @@ -785,8 +801,8 @@ def monomial_divides(self, a, b): if not a: raise ZeroDivisionError - a, = a.dict() - b, = b.dict() + a, = a.monomial_coefficients() + b, = b.monomial_coefficients() return all(b[i] >= a[i] for i in b.common_nonzero_positions(a)) @@ -879,7 +895,7 @@ def addwithcarry(tempvector, maxvector, pos): one = self.base_ring().one() M = list() - v, = t.dict() + v, = t.monomial_coefficients() maxvector = list(v) tempvector = [0] * len(maxvector) @@ -933,31 +949,3 @@ def is_field(self, proof=True): if self.ngens() == 0: return self.base_ring().is_field(proof) return False - - def ideal(self, *gens, **kwds): - """ - Create an ideal in this polynomial ring. - """ - do_coerce = False - if len(gens) == 1: - from sage.rings.ideal import Ideal_generic - if isinstance(gens[0], Ideal_generic): - if gens[0].ring() is self: - return gens[0] - gens = gens[0].gens() - elif isinstance(gens[0], (list, tuple)): - gens = gens[0] - if not self._has_singular: - # pass through - MPolynomialRing_base.ideal(self, gens, **kwds) - - from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal - - if isinstance(gens, (sage.interfaces.abc.SingularElement, sage.interfaces.abc.Macaulay2Element)): - gens = list(gens) - do_coerce = True - elif not isinstance(gens, (list, tuple)): - gens = [gens] - if ('coerce' in kwds and kwds['coerce']) or do_coerce: - gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly! - return MPolynomialIdeal(self, gens, **kwds) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd index 4ce9033dadb..322e25e363b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd @@ -4,6 +4,7 @@ from sage.structure.parent cimport Parent cdef class MPolynomialRing_base(CommutativeRing): cdef object _ngens cdef object _term_order + cdef public object _indices cdef public object _has_singular cdef public object _magma_gens cdef public dict _magma_cache diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 90e736dbadf..19c2ddac7b1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -26,6 +26,7 @@ from sage.rings.polynomial import polynomial_ring from sage.rings.polynomial.term_order import TermOrder from sage.rings.polynomial.polynomial_ring_constructor import (PolynomialRing, polynomial_default_category) +from sage.rings.polynomial.polydict cimport ETuple def is_MPolynomialRing(x): @@ -98,6 +99,8 @@ cdef class MPolynomialRing_base(CommutativeRing): else: category = polynomial_default_category(base_ring.category(), n) Ring.__init__(self, base_ring, names, category=category) + from sage.combinat.integer_vector import IntegerVectors + self._indices = IntegerVectors(self._ngens) def is_integral_domain(self, proof=True): """ @@ -617,14 +620,15 @@ cdef class MPolynomialRing_base(CommutativeRing): a dict with respect to ``self.variable_names()``. """ # This is probably horribly inefficient - from sage.rings.polynomial.polydict import ETuple other_vars = list(x.parent().variable_names()) - name_mapping = [(other_vars.index(var) if var in other_vars else -1) for var in self.variable_names()] + name_mapping = [(other_vars.index(var) if var in other_vars else -1) + for var in self.variable_names()] K = self.base_ring() D = {} var_range = range(len(self.variable_names())) - for ix, a in x.dict().iteritems(): - ix = ETuple([0 if name_mapping[t] == -1 else ix[name_mapping[t]] for t in var_range]) + for ix, a in x.monomial_coefficients().items(): + ix = ETuple([0 if name_mapping[t] == -1 else ix[name_mapping[t]] + for t in var_range]) D[ix] = K(a) return D @@ -1371,7 +1375,22 @@ cdef class MPolynomialRing_base(CommutativeRing): sage: m = R.monomial(1,2,3) sage: R.monomial(*m.degrees()) == m True + + We also allow to specify the exponents in a single tuple:: + + sage: R.monomial(e) + x*y^2*z^3 + + TESTS: + + Check that ETuples also work:: + + sage: from sage.rings.polynomial.polydict import ETuple + sage: R.monomial(ETuple(e)) + x*y^2*z^3 """ + if len(exponents) == 1 and isinstance((e := exponents[0]), (tuple, ETuple)): + return self({e: self.base_ring().one()}) return self({exponents: self.base_ring().one()}) def monomials_of_degree(self, degree): diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index c20666b0403..ff1ec275633 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -377,6 +377,7 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): elif K.degree() > 1: return PolynomialSequence_gf2e(parts, ring, immutable=immutable, cr=cr, cr_str=cr_str) + class PolynomialSequence_generic(Sequence_generic): def __init__(self, parts, ring, immutable=False, cr=False, cr_str=None): """ @@ -1257,6 +1258,7 @@ def is_groebner(self, singular=singular): """ return self.ideal().basis_is_groebner() + class PolynomialSequence_gf2(PolynomialSequence_generic): r""" Polynomial Sequences over `\GF{2}`. @@ -1484,7 +1486,8 @@ def _groebner_strategy(self): g.reduction_strategy.opt_red_tail = True return g - def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, verbose=False, **kwds): + def solve(self, algorithm='polybori', n=1, + eliminate_linear_variables=True, verbose=False, **kwds): r""" Find solutions of this boolean polynomial system. diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index 20a61096548..797200f4e40 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -311,7 +311,7 @@ def MacMahonOmega(var, expression, denominator=None, op=operator.ge, decoded_factors = [] for factor in factors_denominator: factor = L(factor) - D = factor.dict() + D = factor.monomial_coefficients() if not D: raise ZeroDivisionError('Denominator contains a factor 0.') elif len(D) == 1: @@ -331,7 +331,7 @@ def MacMahonOmega(var, expression, denominator=None, op=operator.ge, numerator = L(numerator) / prod(to_numerator) result_numerator, result_factors_denominator = \ - _Omega_(numerator.dict(), decoded_factors) + _Omega_(numerator.monomial_coefficients(), decoded_factors) if result_numerator == 0: return Factorization([], unit=result_numerator) @@ -589,7 +589,7 @@ def subs_power(expression, var, exponent): It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ - p = tuple(var.dict().popitem()[0]).index(1) # var is the p-th generator + p = tuple(var.monomial_coefficients().popitem()[0]).index(1) # var is the p-th generator def subs_e(e): e = list(e) @@ -597,7 +597,8 @@ def subs_e(e): e[p] = e[p] // exponent return tuple(e) parent = expression.parent() - result = parent({subs_e(e): c for e, c in expression.dict().items()}) + result = parent({subs_e(e): c + for e, c in expression.monomial_coefficients().items()}) return result def de_power(expression): diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index fd871fa3b76..d858838f53c 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -715,6 +715,7 @@ def _call_(self, x): return numerator.leading_coefficient() / denominator.leading_coefficient() raise TypeError(f"{x} is not a constant function") + class OreFunctionBaseringInjection(Morphism): r""" Representation of the canonical homomorphism from a field `k` into a Ore diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pxd b/src/sage/rings/polynomial/ore_polynomial_element.pxd index f38bcb0f0c5..771c4d6325a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pxd +++ b/src/sage/rings/polynomial/ore_polynomial_element.pxd @@ -17,7 +17,7 @@ cdef class OrePolynomial(AlgebraElement): cpdef bint is_zero(self) noexcept cpdef bint is_one(self) noexcept - + cdef _left_quo_rem(self, OrePolynomial other) cdef _right_quo_rem(self, OrePolynomial other) cdef OrePolynomial _left_lcm_cofactor(self, OrePolynomial other) @@ -38,7 +38,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef list _mul_list(self, list A) cpdef _mul_(self, other) - cpdef dict dict(self) + cpdef dict monomial_coefficients(self) cpdef list list(self, bint copy=*) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 8307bf9694a..768565840f6 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -2481,7 +2481,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): else: return (self)._coeffs - cpdef dict dict(self): + cpdef dict monomial_coefficients(self): r""" Return a dictionary representation of ``self``. @@ -2491,6 +2491,11 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: a = x^2012 + t*x^1006 + t^3 + 2*t + sage: a.monomial_coefficients() + {0: t^3 + 2*t, 1006: t, 2012: 1} + + ``dict`` is an alias:: + sage: a.dict() {0: t^3 + 2*t, 1006: t, 2012: 1} """ @@ -2503,6 +2508,8 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): X[i] = c return X + dict = monomial_coefficients + cpdef Integer degree(self): r""" Return the degree of ``self``. diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 604764eff35..668f66fe574 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -51,7 +51,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, ab Check that :issue:`13620` has been fixed:: sage: f = R.zero() - sage: R(f.dict()) + sage: R(f.monomial_coefficients()) 0 Check that :issue:`29829` has been fixed:: diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index 5292050d6a7..9ec3a57bb4c 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -79,7 +79,7 @@ def eliminate(polys, on_the_fly=False, prot=False, reduction_function=None, lm = p.lex_lead() if lm.deg() == 1: - if not (lm in linear_leading_monomials): + if lm not in linear_leading_monomials: linear_leading_monomials.add(lm) linear_leads.append(p) else: diff --git a/src/sage/rings/polynomial/pbori/meson.build b/src/sage/rings/polynomial/pbori/meson.build new file mode 100644 index 00000000000..c7541ae492d --- /dev/null +++ b/src/sage/rings/polynomial/pbori/meson.build @@ -0,0 +1,42 @@ +brial = cc.find_library('brial', required: false, disabler: true) +# Cannot be found via pkg-config +brial_groebner = cc.find_library('brial_groebner') + +py.install_sources( + 'PyPolyBoRi.py', + '__init__.py', + 'blocks.py', + 'cnf.py', + 'easy_polynomials.py', + 'fglm.py', + 'frontend.py', + 'gbcore.py', + 'gbrefs.py', + 'heuristics.py', + 'interpolate.py', + 'interred.py', + 'll.py', + 'nf.py', + 'parallel.py', + 'pbori.pxd', + 'randompoly.py', + 'rank.py', + 'specialsets.py', + 'statistics.py', + subdir: 'sage/rings/polynomial/pbori', +) + +extension_data_cpp = {'pbori': files('pbori.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/polynomial/pbori', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_ext, inc_rings, inc_src], + dependencies: [py_dep, brial, brial_groebner, cysignals, gmp, m4ri, png], + ) +endforeach + diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index eb6b091c683..35de7feefe5 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -1403,7 +1403,7 @@ def unpickle_NCPolynomial_plural(NCPolynomialRing_plural R, d): cdef int _i, _e p = p_ISet(0,r) rChangeCurrRing(r) - for mon,c in d.iteritems(): + for mon, c in d.items(): m = p_Init(r) for i,e in mon.sparse_iter(): _i = i @@ -1671,7 +1671,7 @@ cdef class NCPolynomial_plural(RingElement): else: return (left._parent).fraction_field()(left,right) - def __pow__(NCPolynomial_plural self, exp, ignored): + def __pow__(NCPolynomial_plural self, exp, mod): """ Return ``self**(exp)``. @@ -1697,7 +1697,22 @@ cdef class NCPolynomial_plural(RingElement): Traceback (most recent call last): .... OverflowError: exponent overflow (2147483648) + + Check that using third argument raises an error:: + + sage: A. = FreeAlgebra(QQ, 3) + sage: P = A.g_algebra(relations={y*x:-x*y + z}, order='lex') + sage: P.inject_variables() + Defining x, z, y + sage: pow(x + y + z, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) if type(exp) is not Integer: try: exp = Integer(exp) @@ -2203,6 +2218,9 @@ cdef class NCPolynomial_plural(RingElement): sage: f = (2*x*y^3*z^2 + (7)*x^2 + (3)) sage: f.dict() {(0, 0, 0): 3, (1, 2, 3): 2, (2, 0, 0): 7} + + sage: f.monomial_coefficients() + {(0, 0, 0): 3, (1, 2, 3): 2, (2, 0, 0): 7} """ cdef poly *p cdef ring *r diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index 0fbc3fb3860..fe2438c8e4f 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -672,7 +672,7 @@ cdef class PolyDict: ans[ETuple(t)] = self.__repn[S] return self._new(ans) - def is_homogeneous(self): + def is_homogeneous(self, tuple w=None): r""" Return whether this polynomial is homogeneous. @@ -688,12 +688,20 @@ cdef class PolyDict: """ if not self.__repn: return True + cdef size_t s it = iter(self.__repn) - cdef size_t s = ( next(it)).unweighted_degree() - for elt in it: - if ( elt).unweighted_degree() != s: - return False - return True + if w is None: + s = ( next(it)).unweighted_degree() + for elt in it: + if ( elt).unweighted_degree() != s: + return False + return True + else: + s = ( next(it)).weighted_degree(w) + for elt in it: + if ( elt).weighted_degree(w) != s: + return False + return True def is_constant(self): """ @@ -800,7 +808,9 @@ cdef class PolyDict: ring = self.__repn[E[0]].parent() pos_one = ring.one() neg_one = -pos_one - except AttributeError: + except (AttributeError, ArithmeticError): + # AritchmeticError occurs when self.__repn[E[0]] is a tropical + # semiring element # probably self.__repn[E[0]] is not a ring element pos_one = 1 neg_one = -1 @@ -901,7 +911,9 @@ cdef class PolyDict: ring = self.__repn[E[0]].parent() pos_one = ring.one() neg_one = -pos_one - except AttributeError: + except (AttributeError, ArithmeticError): + # AritchmeticError occurs when self.__repn[E[0]] is a tropical + # semiring element # probably self.__repn[E[0]] is not a ring element pos_one = 1 neg_one = -1 diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4f20542237f..7c1ca199ced 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -76,7 +76,6 @@ from sage.arith.power cimport generic_power from sage.arith.misc import crt from sage.arith.long cimport pyobject_to_long from sage.misc.mrange import cartesian_product_iterator -from sage.misc.superseded import deprecated_function_alias from sage.structure.factorization import Factorization from sage.structure.richcmp cimport (richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn) @@ -174,7 +173,6 @@ cpdef is_Polynomial(f): sage: is_Polynomial(f) False """ - from sage.misc.superseded import deprecation deprecation(32709, "the function is_Polynomial is deprecated; use isinstance(x, sage.rings.polynomial.polynomial_element.Polynomial) instead") return isinstance(f, Polynomial) @@ -862,7 +860,7 @@ cdef class Polynomial(CommutativePolynomial): return R(pol) if pol._parent.is_sparse(): coeff_sparse = {} - coeff_dict = pol.dict() + coeff_dict = pol.monomial_coefficients() for i in coeff_dict: coeff_sparse[i * d] = coeff_dict[i] return R(coeff_sparse, check=False) @@ -883,7 +881,7 @@ cdef class Polynomial(CommutativePolynomial): if a.is_monomial(): etup = ETuple(( a).degrees()) if pol._parent.is_sparse(): - coeff_dict = pol.dict() + coeff_dict = pol.monomial_coefficients() return R({etup.emul(key): coeff_dict[key] for key in coeff_dict}) # Dense version return R({etup.emul(i): c for i, c in enumerate(pol)}) @@ -2648,7 +2646,6 @@ cdef class Polynomial(CommutativePolynomial): # ensure that the degree is positive. degree = ZZ(degree) if degree < 0: - from sage.misc.superseded import deprecation deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_equal_deg`.") degree = -degree assume_equal_deg = True @@ -2931,14 +2928,14 @@ cdef class Polynomial(CommutativePolynomial): q = right sparse = self.parent().is_sparse() if sparse: - d = self.dict() + d = self.monomial_coefficients() else: c = self.list(copy=False) while q > 0: q, r = q.quo_rem(p) if r != 0: if sparse: - tmp = self.parent()({e*k : d[k]**e for k in d}) + tmp = self.parent()({e*k: d[k]**e for k in d}) else: tmp = [0] * (e * len(c) - e + 1) for i in range(len(c)): @@ -3526,7 +3523,7 @@ cdef class Polynomial(CommutativePolynomial): - Didier Deshommes (2006-05-25) """ - return self._parent(polynomial_fateman._mul_fateman_mul(self,right)) + return self._parent(polynomial_fateman._mul_fateman_mul(self, right)) @cython.boundscheck(False) @cython.wraparound(False) @@ -3883,7 +3880,7 @@ cdef class Polynomial(CommutativePolynomial): cdef dict D = {} cdef tuple leftovers = (0,) * (len(variables) - len(prev_variables) - 1) for k in range(len(mpolys)): - for i,a in mpolys[k].iteritems(): + for i, a in mpolys[k].items(): j = ETuple((k,) + leftovers) D[i + j] = a @@ -3948,7 +3945,7 @@ cdef class Polynomial(CommutativePolynomial): def euclidean_degree(self): r""" - Return the degree of this element as an element of an Euclidean domain. + Return the degree of this element as an element of a Euclidean domain. If this polynomial is defined over a field, this is simply its :meth:`degree`. @@ -4464,7 +4461,7 @@ cdef class Polynomial(CommutativePolynomial): if self.get_unsafe(n) else zero for n in range(self.degree() + 1)] return S(p) - def dict(self): + def monomial_coefficients(self): """ Return a sparse dictionary representation of this univariate polynomial. @@ -4473,6 +4470,11 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = QQ[] sage: f = x^3 + -1/7*x + 13 + sage: f.monomial_coefficients() + {0: 13, 1: -1/7, 3: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 13, 1: -1/7, 3: 1} """ @@ -4485,6 +4487,8 @@ cdef class Polynomial(CommutativePolynomial): X[i] = c return X + dict = monomial_coefficients + def factor(self, **kwargs): r""" Return the factorization of ``self`` over its base ring. @@ -5953,7 +5957,7 @@ cdef class Polynomial(CommutativePolynomial): # sylvester_matrix() in multi_polynomial.pyx. if self._parent != right.parent(): - a, b = coercion_model.canonical_coercion(self,right) + a, b = coercion_model.canonical_coercion(self, right) variable = a.parent()(self.variables()[0]) #We add the variable to cover the case that right is a multivariate #polynomial @@ -6051,7 +6055,7 @@ cdef class Polynomial(CommutativePolynomial): - Naqi Jaffery (2006-01-24): examples """ - return not self.is_zero() and self[self.degree()] == 1 + return not self.is_zero() and self[self.degree()] == self.base_ring().one() def is_unit(self): r""" @@ -7173,9 +7177,9 @@ cdef class Polynomial(CommutativePolynomial): sage: f = R('e*i') * x + x^2 sage: f._giac_init_() '((1)*1)*sageVARx^2+((1)*sageVARe*sageVARi)*sageVARx' - sage: giac(f) + sage: giac(f) # needs giac sageVARx^2+sageVARe*sageVARi*sageVARx - sage: giac(R.zero()) + sage: giac(R.zero()) # needs giac 0 """ g = 'sageVAR' + self.variable_name() @@ -8404,6 +8408,9 @@ cdef class Polynomial(CommutativePolynomial): UserWarning: roots may have been lost... [[1.00000000000 +/- ...e-12] + [+/- ...e-11]*I, [1.0000000000 +/- ...e-12] + [+/- ...e-12]*I] + sage: ((x - 1)^2).roots(multiplicities=False, proof=False, warn=False) + [[1.00000000000 +/- ...e-12] + [+/- ...e-11]*I, + [1.0000000000 +/- ...e-12] + [+/- ...e-12]*I] Note that coefficients in a number field with defining polynomial `x^2 + 1` are considered to be Gaussian rationals (with the @@ -8989,32 +8996,18 @@ cdef class Polynomial(CommutativePolynomial): self = self//c except (AttributeError, NotImplementedError, TypeError): pass - return self._roots_from_factorization(self.factor(), multiplicities) + try: + fac = self.factor() + except ArithmeticError: + raise NotImplementedError + else: + return self._roots_from_factorization(fac, multiplicities) else: raise NotImplementedError except NotImplementedError: if K.is_finite(): if multiplicities: raise NotImplementedError("root finding with multiplicities for this polynomial not implemented (try the multiplicities=False option)") - elif isinstance(K, sage.rings.abc.IntegerModRing): - # handling via the chinese remainders theorem - N = K.cardinality() - primes = N.prime_divisors() - residue_roots = [] - for p in primes: - local_self = self.change_ring(GF(p)) - local_roots = local_self.roots(multiplicities=False) - residue_roots.append([a.lift() for a in local_roots]) - result = [] - P = ZZ.prod(primes) - for res in cartesian_product_iterator(residue_roots): - lifted = crt(list(res), primes) - candidate = lifted - for k in range(N // P): - if not self(candidate): - result.append(K(candidate)) - candidate += P - return result else: return [a for a in K if not self(a)] @@ -10185,7 +10178,7 @@ cdef class Polynomial(CommutativePolynomial): """ # __getitem__ already returns a polynomial!! # We must not have check=False, since 0 must not have _coeffs = [0]. - return self._parent(self[:n])#, check=False) + return self._parent(self[:n]) # , check=False) cdef _inplace_truncate(self, long prec): return self.truncate(prec) @@ -10592,7 +10585,7 @@ cdef class Polynomial(CommutativePolynomial): R = R.change_ring(new_base_ring) elif isinstance(f, Map): R = R.change_ring(f.codomain()) - return R({k: f(v) for k, v in self.dict().items()}) + return R({k: f(v) for k, v in self.monomial_coefficients().items()}) def is_cyclotomic(self, certificate=False, algorithm='pari'): r""" @@ -12680,11 +12673,11 @@ cpdef list _dict_to_list(dict x, zero): if isinstance(n, tuple): # a mpoly dict n = n[0] v = [zero] * (n+1) - for i, z in x.iteritems(): + for i, z in x.items(): v[i[0]] = z else: v = [zero] * (n+1) - for i, z in x.iteritems(): + for i, z in x.items(): v[i] = z return v diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 3e8a6b45ee1..7bbd4e611a9 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -39,16 +39,16 @@ except ImportError: pari_gen = () -from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn from sage.structure.element import coerce_binop, parent +from sage.structure.factorization import Factorization +from sage.structure.richcmp import richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn from sage.rings.infinity import infinity, Infinity from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer -from sage.structure.factorization import Factorization - from sage.rings.padics.precision_error import PrecisionError + class Polynomial_generic_sparse(Polynomial): """ A generic sparse polynomial. @@ -99,18 +99,18 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): R = parent.base_ring() if isinstance(x, Polynomial): if x.parent() == self.parent(): - x = dict(x.dict()) + x = x.monomial_coefficients() elif x.parent() == R: - x = {0:x} + x = {0: x} else: w = {} - for n, c in x.dict().items(): + for n, c in x.monomial_coefficients().items(): w[n] = R(c) # The following line has been added in github issue #9944. # Apparently, the "else" case has never occurred before. x = w elif isinstance(x, (list, tuple)): - x = dict((i, c) for (i, c) in enumerate(x) if c) + x = {i: c for i, c in enumerate(x) if c} elif isinstance(x, pari_gen): y = {} for i in range(len(x)): @@ -118,7 +118,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): x = y check = True elif not isinstance(x, dict): - x = {0:x} # constant polynomials + x = {0: x} # constant polynomials if check: self.__coeffs = {} for i, z in x.items(): @@ -128,7 +128,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): if check: self.__normalize() - def dict(self): + def monomial_coefficients(self): """ Return a new copy of the dict of the underlying elements of ``self``. @@ -138,14 +138,21 @@ def dict(self): sage: R. = PolynomialRing(Integers(8), sparse=True) sage: f = 5 + w^1997 - w^10000; f 7*w^10000 + w^1997 + 5 - sage: d = f.dict(); d + sage: d = f.monomial_coefficients(); d {0: 5, 1997: 1, 10000: 7} sage: d[0] = 10 + sage: f.monomial_coefficients() + {0: 5, 1997: 1, 10000: 7} + + ``dict`` is an alias:: + sage: f.dict() {0: 5, 1997: 1, 10000: 7} """ return dict(self.__coeffs) + dict = monomial_coefficients + def coefficients(self, sparse=True): """ Return the coefficients of the monomials appearing in ``self``. @@ -1042,6 +1049,7 @@ def number_of_terms(self): """ return len(self.__coeffs) + class Polynomial_generic_domain(Polynomial, IntegralDomainElement): def __init__(self, parent, is_gen=False, construct=False): Polynomial.__init__(self, parent, is_gen=is_gen) @@ -1075,6 +1083,7 @@ def is_unit(self): return False return self[0].is_unit() + class Polynomial_generic_field(Polynomial_singular_repr, Polynomial_generic_domain, EuclideanDomainElement): @@ -1581,6 +1590,7 @@ def _roots(self, secure, minval, hint): class Polynomial_generic_dense_cdv(Polynomial_generic_dense_inexact, Polynomial_generic_cdv): pass + class Polynomial_generic_sparse_cdv(Polynomial_generic_sparse, Polynomial_generic_cdv): pass diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 5879909eb1d..fc2ab9b584e 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -102,7 +102,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): """ fmpz_poly_init(self._poly) - def __dealloc__(self): r""" Call the underlying FLINT fmpz_poly destructor @@ -652,7 +651,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - cpdef _sub_(self, right): r""" Return ``self`` minus ``right``. @@ -672,7 +670,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - cpdef _neg_(self): r""" Return negative of ``self``. @@ -834,7 +831,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - @coerce_binop def lcm(self, right): """ @@ -955,7 +951,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): else: return self._parent(rr), ss, tt - cpdef _mul_(self, right): r""" Return ``self`` multiplied by ``right``. @@ -1040,7 +1035,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - def __pow__(Polynomial_integer_dense_flint self, exp, ignored): + def __pow__(Polynomial_integer_dense_flint self, exp, mod): """ EXAMPLES:: @@ -1119,10 +1114,23 @@ cdef class Polynomial_integer_dense_flint(Polynomial): ... TypeError: no canonical coercion from Univariate Polynomial Ring in R over Integer Ring to Rational Field + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(ZZ, implementation='FLINT') + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef long nn cdef Polynomial_integer_dense_flint res + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + try: nn = pyobject_to_long(exp) except TypeError: @@ -1360,11 +1368,10 @@ cdef class Polynomial_integer_dense_flint(Polynomial): return real_roots(self) -## def __copy__(self): -## f = Polynomial_integer_dense(self.parent()) -## f._poly = self._poly.copy() -## return f - + # def __copy__(self): + # f = Polynomial_integer_dense(self.parent()) + # f._poly = self._poly.copy() + # return f def degree(self, gen=None): """ @@ -1485,7 +1492,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): variable = self.parent().variable_name() return pari(self.list()).Polrev(variable) - def squarefree_decomposition(Polynomial_integer_dense_flint self): """ Return the square-free decomposition of ``self``. This is @@ -1743,7 +1749,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): """ return [self.get_unsafe(i) for i in range(self.degree()+1)] - @coerce_binop def resultant(self, other, proof=True): """ diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 3f94f66f90b..142c7b324ad 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -69,6 +69,7 @@ from sage.libs.ntl.ZZX cimport * from sage.rings.polynomial.evaluation_ntl cimport ZZX_evaluation_mpfr, ZZX_evaluation_mpfi + cdef class Polynomial_integer_dense_ntl(Polynomial): r""" A dense polynomial over the integers, implemented via NTL. @@ -83,7 +84,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): x._is_gen = 0 return x - def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): r""" EXAMPLES:: @@ -236,7 +236,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): mpz_to_ZZ(&y, (a).value) ZZX_SetCoeff(self._poly, i, y) - def content(self): r""" Return the greatest common divisor of the coefficients of this @@ -445,7 +444,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): (right)._poly) return x - cpdef _sub_(self, right): r""" Return ``self`` minus ``right``. @@ -463,7 +461,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): (right)._poly) return x - cpdef _neg_(self): r""" Return negative of ``self``. @@ -479,7 +476,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): ZZX_negate(x._poly, self._poly) return x - @coerce_binop def quo_rem(self, right): r""" @@ -565,7 +561,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): return qq, rr - @coerce_binop def gcd(self, right): r""" @@ -587,7 +582,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): del temp return x - @coerce_binop def lcm(self, right): """ @@ -677,7 +671,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): S = self.parent() return S(rr), ss, tt - cpdef _mul_(self, right): r""" Return ``self`` multiplied by ``right``. @@ -733,7 +726,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): ZZX_mul_ZZ(x._poly, self._poly, _right) return x - def __floordiv__(self, right): """ EXAMPLES:: @@ -785,7 +777,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): mpz_to_ZZ(&y, (value).value) ZZX_SetCoeff(self._poly, n, y) - def real_root_intervals(self): """ Return isolating intervals for the real roots of this polynomial. @@ -803,11 +794,10 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): return real_roots(self) -## def __copy__(self): -## f = Polynomial_integer_dense(self.parent()) -## f._poly = self._poly.copy() -## return f - + # def __copy__(self): + # f = Polynomial_integer_dense(self.parent()) + # f._poly = self._poly.copy() + # return f def degree(self, gen=None): """ @@ -855,7 +845,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): del temp return x - def __pari__(self, variable=None): """ EXAMPLES:: @@ -869,7 +858,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): variable = self.parent().variable_name() return pari(self.list()).Polrev(variable) - def squarefree_decomposition(self): """ Return the square-free decomposition of ``self``. This is @@ -1101,7 +1089,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): """ return [self.get_unsafe(i) for i in range(self.degree()+1)] - @coerce_binop def resultant(self, other, proof=True): """ diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index e91d4860b2d..68605382f1f 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -1253,6 +1253,22 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): sage: S. = PolynomialRing(Integers(5), implementation='NTL') sage: f(y) y^3 + 2 + + TESTS:: + + sage: R. = PolynomialRing(Integers(100), implementation='NTL') + sage: f = x^3 + 7 + sage: f(1).parent() == R.base_ring() + True + sage: f(int(1)).parent() == R.base_ring() + True + sage: f(x + 1).parent() == f.parent() + True + + sage: R. = PolynomialRing(Zmod(12), 'x', implementation='NTL') + sage: u = Zmod(4)(3) + sage: x(u).parent() == u.parent() + True """ if len(args) != 1 or len(kwds) != 0: return Polynomial.__call__(self, *args, **kwds) @@ -1273,7 +1289,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): return Polynomial.__call__(self, *args, **kwds) else: zz_pX_eval(fx.x, self.x, x.x) - return self._parent(int(fx)) + return self._parent._base(int(fx)) cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): @@ -1808,6 +1824,25 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): sage: S. = PolynomialRing(Integers(5), implementation='NTL') sage: f(y) y^3 + 2 + + TESTS:: + + sage: R. = PolynomialRing(Integers(10^30), implementation='NTL') + sage: f = x^3 + 7 + sage: f(1).parent() == R.base_ring() + True + sage: f(int(1)).parent() == R.base_ring() + True + sage: f(x + 1).parent() == f.parent() + True + + sage: R. = PolynomialRing(Zmod(10^30), 'x', implementation='NTL') + sage: u = Zmod(10^29)(3) + sage: x(u).parent() == u.parent() + True + sage: v = Zmod(10)(3) + sage: x(v).parent() == v.parent() + True """ if len(args) != 1 or len(kwds) != 0: return Polynomial.__call__(self, *args, **kwds) @@ -1826,7 +1861,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): return Polynomial.__call__(self, *args, **kwds) else: ZZ_pX_eval(fx.x, self.x, x.x) - return self._parent(fx._integer_()) + return self._parent._base(fx._integer_()) cdef class Polynomial_dense_mod_p(Polynomial_dense_mod_n): diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index e12977e8464..027a8997067 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -262,6 +262,7 @@ def create_object(self, version, key): PolynomialQuotientRing = PolynomialQuotientRingFactory("PolynomialQuotientRing") + def is_PolynomialQuotientRing(x): from sage.misc.superseded import deprecation deprecation(38266, diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 919b4b856d4..c167caeb352 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -145,7 +145,7 @@ def __init__(self, parent, polynomial, check=True): Q = P(0) X = P.gen() while R.degree() >= B.degree(): - S = P((R.leading_coefficient()/B.leading_coefficient())) * X**(R.degree()-B.degree()) + S = P(R.leading_coefficient()/B.leading_coefficient()) * X**(R.degree()-B.degree()) Q = Q + S R = R - S*B polynomial = R diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index df25358ff3b..ceaa9d7b2d5 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -1160,7 +1160,7 @@ cdef class Polynomial_rational_flint(Polynomial): if do_sig: sig_off() return res - def __pow__(Polynomial_rational_flint self, exp, ignored): + def __pow__(Polynomial_rational_flint self, exp, mod): """ Return ``self`` raised to the power of ``exp``. @@ -1266,10 +1266,23 @@ cdef class Polynomial_rational_flint(Polynomial): ... TypeError: no canonical coercion from Univariate Polynomial Ring in R over Rational Field to Rational Field + + Check that using third argument raises an error:: + + sage: R. = PolynomialRing(QQ) + sage: pow(x, 2, x) + Traceback (most recent call last): + ... + NotImplementedError: pow() with a modulus is not implemented for this ring """ cdef Polynomial_rational_flint res cdef long n + if mod is not None: + raise NotImplementedError( + "pow() with a modulus is not implemented for this ring" + ) + try: n = pyobject_to_long(exp) except TypeError: diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 165c7fb72a4..2bda9f6f9c3 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -256,15 +256,25 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: category(ZZ['x']) Join of Category of unique factorization domains + and Category of algebras with basis over + (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets + and metric spaces) and Category of commutative algebras over (Dedekind domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) and Category of infinite sets + sage: category(GF(7)['x']) Join of Category of euclidean domains - and Category of commutative algebras over - (finite enumerated fields and subquotients of monoids and quotients of semigroups) and Category of infinite sets + and Category of algebras with basis over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of commutative algebras over + (finite enumerated fields and subquotients of monoids + and quotients of semigroups) + and Category of infinite sets TESTS: @@ -314,6 +324,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, self.__cyclopoly_cache = {} self._has_singular = False Ring.__init__(self, base_ring, names=name, normalize=True, category=category) + from sage.rings.semirings.non_negative_integer_semiring import NonNegativeIntegerSemiring + self._indices = NonNegativeIntegerSemiring() self._populate_coercion_lists_(convert_method_name='_polynomial_') def __reduce__(self): diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 674a84f0ff7..5cbc74dd1d5 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -537,7 +537,7 @@ def PolynomialRing(base_ring, *args, **kwds): sage: PolynomialRing(4) Traceback (most recent call last): ... - TypeError: base_ring 4 must be a ring + TypeError: base_ring 4 must be a ring or the tropical semiring sage: PolynomialRing(QQ, -1) Traceback (most recent call last): ... @@ -622,8 +622,9 @@ def PolynomialRing(base_ring, *args, **kwds): sage: R. = PolynomialRing(RIF,2) sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) """ - if base_ring not in Rings(): - raise TypeError("base_ring {!r} must be a ring".format(base_ring)) + from sage.rings.semirings.tropical_semiring import TropicalSemiring + if base_ring not in Rings() and not isinstance(base_ring, TropicalSemiring): + raise TypeError("base_ring {!r} must be a ring or the tropical semiring".format(base_ring)) n = -1 # Unknown number of variables names = None # Unknown variable names @@ -792,7 +793,11 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non # Generic implementations if constructor is None: - if base_ring not in _CommutativeRings: + from sage.rings.semirings.tropical_semiring import TropicalSemiring + if isinstance(base_ring, TropicalSemiring): + from sage.rings.semirings.tropical_polynomial import TropicalPolynomialSemiring + constructor = TropicalPolynomialSemiring + elif base_ring not in _CommutativeRings: constructor = polynomial_ring.PolynomialRing_general elif base_ring in _CompleteDiscreteValuationRings: constructor = polynomial_ring.PolynomialRing_cdvr @@ -804,6 +809,7 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non constructor = polynomial_ring.PolynomialRing_integral_domain else: constructor = polynomial_ring.PolynomialRing_commutative + implementation_names = constructor._implementation_names(implementation, base_ring, sparse) # Only use names which are not supported by the specialized class. @@ -861,7 +867,11 @@ def _multi_variate(base_ring, names, sparse=None, order='degrevlex', implementat if R is None and implementation == "generic": from . import multi_polynomial_ring - if base_ring in _Domains: + from sage.rings.semirings.tropical_semiring import TropicalSemiring + if isinstance(base_ring, TropicalSemiring): + from sage.rings.semirings.tropical_mpolynomial import TropicalMPolynomialSemiring + constructor = TropicalMPolynomialSemiring + elif base_ring in _Domains: constructor = multi_polynomial_ring.MPolynomialRing_polydict_domain else: constructor = multi_polynomial_ring.MPolynomialRing_polydict @@ -906,23 +916,30 @@ def polynomial_default_category(base_ring_category, n_variables): EXAMPLES:: sage: from sage.rings.polynomial.polynomial_ring_constructor import polynomial_default_category - sage: polynomial_default_category(Rings(),1) is Algebras(Rings()).Infinite() - True - sage: polynomial_default_category(Rings().Commutative(),1) is Algebras(Rings().Commutative()).Commutative().Infinite() - True - sage: polynomial_default_category(Fields(),1) is EuclideanDomains() & Algebras(Fields()).Infinite() - True - sage: polynomial_default_category(Fields(),2) is UniqueFactorizationDomains() & CommutativeAlgebras(Fields()).Infinite() - True - - sage: QQ['t'].category() is EuclideanDomains() & CommutativeAlgebras(QQ.category()).Infinite() + sage: polynomial_default_category(Rings(), 1) + Category of infinite algebras with basis over rings + sage: polynomial_default_category(Rings().Commutative(), 1) + Category of infinite commutative algebras with basis + over commutative rings + sage: polynomial_default_category(Fields(), 1) + Join of Category of euclidean domains + and Category of algebras with basis over fields + and Category of commutative algebras over fields + and Category of infinite sets + sage: polynomial_default_category(Fields(), 2) + Join of Category of unique factorization domains + and Category of algebras with basis over fields + and Category of commutative algebras over fields + and Category of infinite sets + + sage: QQ['t'].category() is EuclideanDomains() & CommutativeAlgebras(QQ.category()).WithBasis().Infinite() True - sage: QQ['s','t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ.category()).Infinite() + sage: QQ['s','t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ.category()).WithBasis().Infinite() True - sage: QQ['s']['t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ['s'].category()).Infinite() + sage: QQ['s']['t'].category() is UniqueFactorizationDomains() & CommutativeAlgebras(QQ['s'].category()).WithBasis().Infinite() True """ - category = Algebras(base_ring_category) + category = Algebras(base_ring_category).WithBasis() if n_variables: # here we assume the base ring to be nonzero diff --git a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx index b3ce1183e5e..087e520dc23 100644 --- a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx +++ b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx @@ -58,7 +58,7 @@ cdef class PolynomialRingHomomorphism_from_base(RingHomomorphism_from_base): P = self.codomain() f = self.underlying_map() if P.is_sparse(): - return P({a: f(b) for a, b in x.dict().iteritems()}) + return P({a: f(b) for a, b in x.monomial_coefficients().items()}) else: return P([f(b) for b in x]) @@ -88,7 +88,8 @@ cdef class PolynomialRingHomomorphism_from_base(RingHomomorphism_from_base): P = self.codomain() f = self.underlying_map() if P.is_sparse(): - return P({a: f(b) for a, b in x.dict().iteritems()}, *args, **kwds) + return P({a: f(b) for a, b in x.monomial_coefficients().items()}, + *args, **kwds) else: return P([f(b) for b in x], *args, **kwds) diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 64398898d0d..b76d62a9e92 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -371,7 +371,7 @@ cdef class Polynomial_template(Polynomial): sage: f.gcd(g) Traceback (most recent call last): ... - ValueError: non-invertible elements encountered during GCD + RuntimeError: FLINT gcd calculation failed """ if celement_is_zero(&self.x, (self)._cparent): return other @@ -588,7 +588,6 @@ cdef class Polynomial_template(Polynomial): return -2 return result - def __pow__(self, ee, modulus): """ EXAMPLES:: diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index 3eaad290e09..5b3539d6b70 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -762,7 +762,19 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sage: P. = GF(7)[] sage: (6*x+3).squarefree_decomposition() (6) * (x + 4) + + Test zero polynomial:: + + sage: R. = PolynomialRing(GF(65537), implementation="FLINT") + sage: R.zero().squarefree_decomposition() + Traceback (most recent call last): + ... + ArithmeticError: square-free decomposition of 0 is not defined """ + if self.is_zero(): + raise ArithmeticError( + "square-free decomposition of 0 is not defined" + ) if not self.base_ring().is_field(): raise NotImplementedError("square free factorization of polynomials over rings with composite characteristic is not implemented") @@ -803,9 +815,20 @@ cdef class Polynomial_zmod_flint(Polynomial_template): Traceback (most recent call last): ... AlarmInterrupt + + Test zero polynomial:: + + sage: R. = PolynomialRing(GF(65537), implementation="FLINT") + sage: R.zero().factor() + Traceback (most recent call last): + ... + ArithmeticError: factorization of 0 is not defined + """ - R = self.base_ring() + if self.is_zero(): + raise ArithmeticError("factorization of 0 is not defined") + R = self.base_ring() if not R.is_field(): p,e = R.characteristic().is_prime_power(get_data=True) if not e: diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a901022b308..0c8c660c18c 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -100,7 +100,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): N = self._parent._working_center(self.reduced_norm(var=False)) return N.is_irreducible() - def type(self, N): r""" Return the `N`-type of this skew polynomial (see definition below). @@ -209,7 +208,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): self._types[N] = type return type - # Finding divisors # ---------------- @@ -288,7 +286,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): continue return D - def _reduced_norm_factor_uniform(self): r""" Return a factor of the reduced norm of this skew @@ -357,7 +354,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if random < count[i]: return F[i][0] - def _irreducible_divisors(self, bint right): r""" Return an iterator over all irreducible monic @@ -469,7 +465,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): d, _ = quo_rem2(P, d) yield d - def right_irreducible_divisor(self, uniform=False): r""" Return a right irreducible divisor of this skew polynomial. @@ -617,7 +612,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if LD.degree() == degN: return LD - def right_irreducible_divisors(self): r""" Return an iterator over all irreducible monic right divisors @@ -686,7 +680,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ return self._irreducible_divisors(False) - def count_irreducible_divisors(self): r""" Return the number of irreducible monic divisors of @@ -744,7 +737,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): count += (cardL**m - 1) // (cardL - 1) return count - # Finding factorizations # ---------------------- @@ -926,7 +918,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): factors.reverse() return Factorization(factors, sort=False, unit=unit) - def factor(self, uniform=False): r""" Return a factorization of this skew polynomial. @@ -994,7 +985,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): F = self._factorization return F - def count_factorizations(self): r""" Return the number of factorizations (as a product of a diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 0abdfca57bc..f90484cba62 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -518,7 +518,6 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): return center(self._optbound) return self.reduced_norm() - def optimal_bound(self): r""" Return the optimal bound of this skew polynomial (i.e. diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py index 1bed10ba867..cd5cad00f8f 100644 --- a/src/sage/rings/polynomial/term_order.py +++ b/src/sage/rings/polynomial/term_order.py @@ -539,6 +539,7 @@ 'block' : block_description, } + class TermOrder(SageObject): """ A term order. diff --git a/src/sage/rings/polynomial/weil/meson.build b/src/sage/rings/polynomial/weil/meson.build new file mode 100644 index 00000000000..2c9bd006919 --- /dev/null +++ b/src/sage/rings/polynomial/weil/meson.build @@ -0,0 +1,19 @@ +py.install_sources( + 'all.py', + 'power_sums.h', + subdir: 'sage/rings/polynomial/weil', +) + +extension_data = {'weil_polynomials' : files('weil_polynomials.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/polynomial/weil', + install: true, + include_directories: [inc_cpython, inc_flint, inc_rings, inc_src], + dependencies: [py_dep, cysignals, flint, gmp], + ) +endforeach + diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx index 516cf641d2e..e2dd7bd04e7 100755 --- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx +++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx @@ -303,13 +303,13 @@ class WeilPolynomials_iter(): coefflist.append(j) modlist.append(k) # Remove cofactor from initial coefficients - if num_cofactor == 1: #cofactor x + sqrt(q) + if num_cofactor == 1: # cofactor x + sqrt(q) for i in range(1, len(coefflist)): coefflist[i] -= coefflist[i-1]*q.sqrt() - elif num_cofactor == 2: #cofactor x + sqrt(q) + elif num_cofactor == 2: # cofactor x + sqrt(q) for i in range(1, len(coefflist)): coefflist[i] += coefflist[i-1]*q.sqrt() - elif num_cofactor == 3: #cofactor x^2 - q + elif num_cofactor == 3: # cofactor x^2 - q for i in range(2, len(coefflist)): coefflist[i] += coefflist[i-2]*q # Asymmetrize initial coefficients diff --git a/src/sage/rings/power_series_mpoly.pyx b/src/sage/rings/power_series_mpoly.pyx index 42cf467912b..741b0ad94e7 100644 --- a/src/sage/rings/power_series_mpoly.pyx +++ b/src/sage/rings/power_series_mpoly.pyx @@ -58,11 +58,11 @@ cdef class PowerSeries_mpoly(PowerSeries): d = {} if isinstance(B, MPolynomialRing_base): for i in range(len(v)): - for n, c in v[i].dict().iteritems(): + for n, c in v[i].monomial_coefficients().items(): d[tuple(n) + (i,)] = c else: for i in range(len(v)): - for n, c in v[i].dict().iteritems(): + for n, c in v[i].monomial_coefficients().items(): d[(n,i)] = c self.__f = S(d) diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index 0e491cad78a..8f7ff769dd1 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -734,7 +734,7 @@ cdef class PowerSeries_pari(PowerSeries): else: return [R(g)] + [R.zero()] * (n - 1) - def dict(self): + def monomial_coefficients(self): """ Return a dictionary of coefficients for ``self``. @@ -746,10 +746,17 @@ cdef class PowerSeries_pari(PowerSeries): sage: R. = PowerSeriesRing(ZZ, implementation='pari') sage: f = 1 + t^10 + O(t^12) + sage: f.monomial_coefficients() + {0: 1, 10: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 1, 10: 1} """ - return self.polynomial().dict() + return self.polynomial().monomial_coefficients() + + dict = monomial_coefficients def _derivative(self, var=None): """ diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index 91f790ffa16..309d72c8f6a 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -794,7 +794,7 @@ cdef class PowerSeries_poly(PowerSeries): """ return self.__f.list() - def dict(self): + def monomial_coefficients(self): """ Return a dictionary of coefficients for ``self``. @@ -806,10 +806,17 @@ cdef class PowerSeries_poly(PowerSeries): sage: R. = ZZ[[]] sage: f = 1 + t^10 + O(t^12) + sage: f.monomial_coefficients() + {0: 1, 10: 1} + + ``dict`` is an alias:: + sage: f.dict() {0: 1, 10: 1} """ - return self.__f.dict() + return self.__f.monomial_coefficients() + + dict = monomial_coefficients def _derivative(self, var=None): """ diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 3ebcf680801..e6c932aadff 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -44,7 +44,8 @@ x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 + O(x^10) sage: R. = PowerSeriesRing(QQ, default_prec=15) sage: sin(x) - x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 - 1/39916800*x^11 + 1/6227020800*x^13 + O(x^15) + x - 1/6*x^3 + 1/120*x^5 - 1/5040*x^7 + 1/362880*x^9 - 1/39916800*x^11 + + 1/6227020800*x^13 + O(x^15) An iterated example:: @@ -139,8 +140,6 @@ from sage.misc.lazy_import import lazy_import from sage.rings import ( integer, - laurent_series_ring, - laurent_series_ring_element, power_series_mpoly, power_series_poly, power_series_ring_element, @@ -153,6 +152,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.category_object import normalize_names from sage.structure.element import Expression, parent +from sage.structure.parent import Parent from sage.structure.nonexact import Nonexact from sage.structure.unique_representation import UniqueRepresentation @@ -355,7 +355,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, * :func:`sage.misc.defaults.set_series_precision` """ - #multivariate case: + # multivariate case: # examples for first case: # PowerSeriesRing(QQ,'x,y,z') # PowerSeriesRing(QQ,['x','y','z']) @@ -364,7 +364,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, names = name if isinstance(names, (tuple, list)) and len(names) > 1 or (isinstance(names, str) and ',' in names): return _multi_variate(base_ring, num_gens=arg2, names=names, - order=order, default_prec=default_prec, sparse=sparse) + order=order, default_prec=default_prec, sparse=sparse) # examples for second case: # PowerSeriesRing(QQ,3,'t') if arg2 is None and num_gens is not None: @@ -373,7 +373,7 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, if (isinstance(arg2, str) and isinstance(names, (int, integer.Integer))): return _multi_variate(base_ring, num_gens=names, names=arg2, - order=order, default_prec=default_prec, sparse=sparse) + order=order, default_prec=default_prec, sparse=sparse) # univariate case: the arguments to PowerSeriesRing used to be # (base_ring, name=None, default_prec=20, names=None, sparse=False), @@ -385,11 +385,10 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, elif arg2 is not None: default_prec = arg2 - ## too many things (padics, elliptic curves) depend on this behavior, - ## so no warning for now. - ## + # ## too many things (padics, elliptic curves) depend on this behavior, + # ## so no warning for now. - # if isinstance(name, (int,integer.Integer)) or isinstance(arg2,(int,integer.Integer)): + # if isinstance(name, (int, integer.Integer)) or isinstance(arg2, (int, integer.Integer)): # deprecation(issue_number, "This behavior of PowerSeriesRing is being deprecated in favor of constructing multivariate power series rings. (See Github issue #1956.)") # the following is the original, univariate-only code @@ -427,8 +426,9 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, raise TypeError("base_ring must be a commutative ring") return R + def _multi_variate(base_ring, num_gens=None, names=None, - order='negdeglex', default_prec=None, sparse=False): + order='negdeglex', default_prec=None, sparse=False): """ Construct multivariate power series ring. """ @@ -436,7 +436,7 @@ def _multi_variate(base_ring, num_gens=None, names=None, raise TypeError("you must specify a variable name or names") if num_gens is None: - if isinstance(names,str): + if isinstance(names, str): num_gens = len(names.split(',')) elif isinstance(names, (list, tuple)): num_gens = len(names) @@ -458,6 +458,7 @@ def _multi_variate(base_ring, num_gens=None, names=None, def _single_variate(): pass + def is_PowerSeriesRing(R): """ Return ``True`` if this is a *univariate* power series ring. This is in @@ -489,7 +490,8 @@ def is_PowerSeriesRing(R): else: return False -class PowerSeriesRing_generic(UniqueRepresentation, ring.CommutativeRing, Nonexact): + +class PowerSeriesRing_generic(UniqueRepresentation, Parent, Nonexact): """ A power series ring. """ @@ -592,9 +594,9 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, else: raise ValueError('unknown power series implementation: %r' % implementation) - ring.CommutativeRing.__init__(self, base_ring, names=name, - category=getattr(self, '_default_category', - _CommutativeRings)) + Parent.__init__(self, base=base_ring, names=name, + category=getattr(self, '_default_category', + _CommutativeRings)) Nonexact.__init__(self, default_prec) if implementation == 'pari': self.__generator = self.element_class(self, R.gen().__pari__()) @@ -832,7 +834,7 @@ def _element_constructor_(self, f, prec=infinity, check=True): elif isinstance(f, LaurentSeries) and f.parent().power_series_ring() is self: return self(f.power_series(), prec, check=check) elif isinstance(f, MagmaElement) and str(f.Type()) == 'RngSerPowElt': - v = sage_eval(f.Eltseq()) + v = sage_eval(f.Eltseq()) # could use .sage() ? return self(v) * (self.gen(0)**f.Valuation()) elif isinstance(f, FractionFieldElement): if self.base_ring().has_coerce_map_from(f.parent()): @@ -846,7 +848,8 @@ def _element_constructor_(self, f, prec=infinity, check=True): if isinstance(f, SymbolicSeries): if str(f.default_variable()) == self.variable_name(): return self.element_class(self, f.list(), - f.degree(f.default_variable()), check=check) + f.degree(f.default_variable()), + check=check) else: raise TypeError("Can only convert series into ring with same variable name.") else: @@ -1097,6 +1100,18 @@ def gen(self, n=0): raise IndexError("generator n>0 not defined") return self.__generator + def gens(self) -> tuple: + """ + Return the generators of this ring. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(ZZ) + sage: R.gens() + (t,) + """ + return (self.__generator,) + def uniformizer(self): """ Return a uniformizer of this power series ring if it is @@ -1357,6 +1372,7 @@ def _get_action_(self, other, op, self_is_left): return BaseRingFloorDivAction(other, self, is_left=False) return super()._get_action_(other, op, self_is_left) + class PowerSeriesRing_over_field(PowerSeriesRing_domain): _default_category = CompleteDiscreteValuationRings() diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 6ef9f6ab097..b1ca6ea4ab1 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1045,7 +1045,7 @@ cdef class PowerSeries(AlgebraElement): else: n = int(n) v = {} - for k, x in self.dict().iteritems(): + for k, x in self.monomial_coefficients().items(): if k >= n: v[k-n] = x return self._parent(v, self.prec()-n) @@ -2915,11 +2915,11 @@ def _solve_linear_de(R, N, L, a, b, f0): g = _solve_linear_de(R, N, L2, a, b, f0) term1 = R(g) # we must not have check=False, since otherwise [..., 0, 0] is not stripped - term2 = R(a[:L]) #, check=False) + term2 = R(a[:L]) # , check=False) product = (term1 * term2).list() # todo: perhaps next loop could be made more efficient - c = b[L2 : L] + c = b[L2:L] for j in range(L2 - 1, min(L-1, len(product))): c[j - (L2-1)] = c[j - (L2-1)] + product[j] diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 5d2f2ca2216..300d64b8279 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -981,9 +981,9 @@ def _factor_multivariate_polynomial(self, f, proof=True): numfield_polynomial_flat = numfield.polynomial()(nf_gen) - polynomial_flat = sum(flat_ring({(0,)+tuple(k):1}) + polynomial_flat = sum(flat_ring({(0,) + tuple(k): 1}) * v.polynomial()(nf_gen) - for k,v in numfield_f.dict().items()) + for k, v in numfield_f.monomial_coefficients().items()) norm_flat = polynomial_flat.resultant(numfield_polynomial_flat, nf_gen) norm_f = norm_flat((0,)+norm_ring.gens()) @@ -2841,6 +2841,18 @@ def number_field_elements_from_algebraics(numbers, minimal=False, sage: res[0] Number Field in a with defining polynomial y^24 - 107010*y^22 - 24*y^21 + ... + 250678447193040618624307096815048024318853254384 with a = 93.32530798172420? + + Test that the semantic of ``AA`` constructor does not affect this function (:issue:`36735`):: + + sage: AA((-1)^(2/3)) + 1 + sage: number_field_elements_from_algebraics([(-1)^(2/3)]) + (Cyclotomic Field of order 6 and degree 2, + [zeta6 - 1], + Ring morphism: + From: Cyclotomic Field of order 6 and degree 2 + To: Algebraic Field + Defn: zeta6 |--> 0.500000000000000? + 0.866025403784439?*I) """ gen = qq_generator @@ -2862,15 +2874,10 @@ def mk_algebraic(x): return x return QQbar(x) - # Try to cast into AA - try: - aa_numbers = [AA(_) for _ in numbers] - numbers = aa_numbers - real_case = True - except (ValueError, TypeError): - real_case = False - # Make the numbers algebraic numbers = [mk_algebraic(_) for _ in numbers] + real_case = all(x in AA for x in numbers) + if real_case: + numbers = [AA(x) for x in numbers] # Make the numbers have a real exact underlying field real_numbers = [] @@ -4485,6 +4492,26 @@ def as_number_field_element(self, minimal=False, embedded=False, prec=53): """ return number_field_elements_from_algebraics(self, minimal=minimal, embedded=embedded, prec=prec) + def is_integral(self): + r""" + Check if this number is an algebraic integer. + + EXAMPLES:: + + sage: QQbar(sqrt(-23)).is_integral() + True + sage: AA(sqrt(23/2)).is_integral() + False + + TESTS: + + Method should return the same value as :meth:`NumberFieldElement.is_integral`:: + + sage: for a in [QQbar(2^(1/3)), AA(2^(1/3)), QQbar(sqrt(1/2)), AA(1/2), AA(2), QQbar(1/2)]: + ....: assert a.as_number_field_element()[1].is_integral() == a.is_integral() + """ + return all(a in ZZ for a in self.minpoly()) + def exactify(self): """ Compute an exact representation for this number. diff --git a/src/sage/rings/qqbar_decorators.py b/src/sage/rings/qqbar_decorators.py index 7343a0b9b08..9abca887218 100644 --- a/src/sage/rings/qqbar_decorators.py +++ b/src/sage/rings/qqbar_decorators.py @@ -14,6 +14,7 @@ from sage.misc.decorators import decorator_keywords, sage_wraps + @decorator_keywords def handle_AA_and_QQbar(func): r""" diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 4cf3979265f..f98322c2183 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -1545,6 +1545,7 @@ def _contains_(self, other): J = R.cover_ring().ideal(Igens) return other.lift() in J + class QuotientRingIdeal_principal(ideal.Ideal_principal, QuotientRingIdeal_generic): r""" Specialized class for principal quotient-ring ideals. diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 0e6f2e69609..f1b414089e2 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -496,8 +496,7 @@ def range_by_height(self, start, end=None): if end is None: end = start start = 1 - if start < 1: - start = 1 + start = max(start, 1) for height in ZZ.range(start, end): if height == 1: yield self(0) @@ -602,7 +601,8 @@ def signature(self): def embeddings(self, K): r""" - Return list of the one embedding of `\QQ` into `K`, if it exists. + Return the list containing the unique embedding of `\QQ` into `K`, + if it exists, and an empty list otherwise. EXAMPLES:: @@ -613,16 +613,17 @@ def embeddings(self, K): From: Rational Field To: Cyclotomic Field of order 5 and degree 4] - `K` must have characteristic 0:: + The field `K` must have characteristic `0` for an embedding of `\QQ` + to exist:: sage: QQ.embeddings(GF(3)) - Traceback (most recent call last): - ... - ValueError: no embeddings of the rational field into K. + [] """ - if K.characteristic(): - raise ValueError("no embeddings of the rational field into K.") - return [self.hom(K)] + if K.characteristic() == 0: + v = [self.hom(K)] + else: + v = [] + return Sequence(v, check=False, universe=self.Hom(K)) def automorphisms(self): r""" diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 5ad885351cc..f92aa7c0d27 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -18,10 +18,10 @@ The class inheritance hierarchy is: - :class:`Ring` (to be deprecated) - - :class:`Algebra` (to be deprecated) + - :class:`Algebra` (deprecated and essentially removed) - :class:`CommutativeRing` - - :class:`NoetherianRing` (deprecated) + - :class:`NoetherianRing` (deprecated and essentially removed) - :class:`CommutativeAlgebra` (deprecated and essentially removed) - :class:`IntegralDomain` (deprecated) @@ -70,12 +70,12 @@ This is to test a deprecation:: sage: from sage.rings.ring import CommutativeAlgebra sage: class Nein(CommutativeAlgebra): ....: pass - sage: F = Nein(QQ, QQ) + sage: F = Nein(QQ) ...: DeprecationWarning: use the category CommutativeAlgebras See https://github.com/sagemath/sage/issues/37999 for details. sage: F.category() - Category of commutative rings + Category of commutative algebras over Rational Field sage: from sage.rings.ring import PrincipalIdealDomain sage: class Non(PrincipalIdealDomain): @@ -86,6 +86,17 @@ This is to test a deprecation:: See https://github.com/sagemath/sage/issues/37719 for details. sage: F.category() Category of principal ideal domains + + sage: from sage.rings.ring import Algebra + sage: class Nichts(Algebra): + ....: pass + sage: F = Nichts(QQ) + ...: + DeprecationWarning: use the category Algebras + See https://github.com/sagemath/sage/issues/38502 for details. + sage: F.category() + Category of algebras over Rational Field + """ # **************************************************************************** @@ -105,6 +116,8 @@ from sage.structure.parent cimport Parent from sage.structure.category_object cimport check_default_category from sage.misc.prandom import randint from sage.categories.rings import Rings +from sage.categories.algebras import Algebras +from sage.categories.commutative_algebras import CommutativeAlgebras from sage.categories.commutative_rings import CommutativeRings from sage.categories.integral_domains import IntegralDomains from sage.categories.dedekind_domains import DedekindDomains @@ -142,6 +155,7 @@ cdef class Ring(ParentWithGens): Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass + running ._test_monomial_coefficients() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass @@ -171,20 +185,29 @@ cdef class Ring(ParentWithGens): Test against another bug fixed in :issue:`9944`:: sage: QQ['x'].category() - Join of Category of euclidean domains and Category of commutative algebras over - (number fields and quotient fields and metric spaces) and Category of infinite sets + Join of Category of euclidean domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) + and Category of commutative algebras + over (number fields and quotient fields and metric spaces) + and Category of infinite sets sage: QQ['x','y'].category() Join of Category of unique factorization domains + and Category of algebras with basis + over (number fields and quotient fields and metric spaces) and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets sage: PolynomialRing(MatrixSpace(QQ, 2),'x').category() # needs sage.modules - Category of infinite algebras over (finite dimensional algebras with basis over - (number fields and quotient fields and metric spaces) and infinite sets) + Category of infinite algebras with basis + over (finite dimensional algebras with basis + over (number fields and quotient fields and metric spaces) + and infinite sets) sage: PolynomialRing(SteenrodAlgebra(2),'x').category() # needs sage.combinat sage.modules - Category of infinite algebras over (super Hopf algebras with basis - over Finite Field of size 2 and supercocommutative super coalgebras - over Finite Field of size 2) + Category of infinite algebras with basis + over (super Hopf algebras with basis over Finite Field of size 2 + and supercocommutative super coalgebras + over Finite Field of size 2) TESTS:: @@ -338,115 +361,6 @@ cdef class Ring(ParentWithGens): # initialisation has finished. return self._category or _Rings - def ideal(self, *args, **kwds): - """ - Return the ideal defined by ``x``, i.e., generated by ``x``. - - INPUT: - - - ``*x`` -- list or tuple of generators (or several input arguments) - - - ``coerce`` -- boolean (default: ``True``); this must be a keyword - argument. Only set it to ``False`` if you are certain that each - generator is already in the ring. - - - ``ideal_class`` -- callable (default: ``self._ideal_class_()``); - this must be a keyword argument. A constructor for ideals, taking - the ring as the first argument and then the generators. - Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or - :class:`~sage.rings.noncommutative_ideals.Ideal_nc`. - - - Further named arguments (such as ``side`` in the case of - non-commutative rings) are forwarded to the ideal class. - - EXAMPLES:: - - sage: R. = QQ[] - sage: R.ideal(x,y) - Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field - sage: R.ideal(x+y^2) - Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field - sage: R.ideal( [x^3,y^3+x^3] ) - Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field - - Here is an example over a non-commutative ring:: - - sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules - sage: A.ideal(A.1, A.2^2) # needs sage.combinat sage.modules - Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis - sage: A.ideal(A.1, A.2^2, side='left') # needs sage.combinat sage.modules - Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis - - TESTS: - - Make sure that :issue:`11139` is fixed:: - - sage: R. = QQ[] - sage: R.ideal([]) - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - sage: R.ideal(()) - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - sage: R.ideal() - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - """ - if 'coerce' in kwds: - coerce = kwds['coerce'] - del kwds['coerce'] - else: - coerce = True - - from sage.rings.ideal import Ideal_generic - from sage.structure.parent import Parent - gens = args - while isinstance(gens, (list, tuple)) and len(gens) == 1: - first = gens[0] - if isinstance(first, Ideal_generic): - R = first.ring() - m = self.convert_map_from(R) - if m is not None: - gens = [m(g) for g in first.gens()] - coerce = False - else: - m = R.convert_map_from(self) - if m is not None: - raise NotImplementedError - else: - raise TypeError - break - elif isinstance(first, (list, tuple)): - gens = first - elif isinstance(first, Parent) and self.has_coerce_map_from(first): - gens = first.gens() # we have a ring as argument - else: - break - - if len(gens) == 0: - gens = [self.zero()] - - if coerce: - gens = [self(g) for g in gens] - 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, IndexError): - pass - else: - for h in gens[1:]: - g = g.gcd(h) - gens = [g] - if 'ideal_class' in kwds: - C = kwds['ideal_class'] - del kwds['ideal_class'] - else: - C = self._ideal_class_(len(gens)) - if len(gens) == 1 and isinstance(gens[0], (list, tuple)): - gens = gens[0] - return C(self, gens, **kwds) - def __mul__(self, x): """ Return the ideal ``x*R`` generated by ``x``, where ``x`` is either an @@ -629,76 +543,6 @@ cdef class Ring(ParentWithGens): """ return True - def is_subring(self, other): - """ - Return ``True`` if the canonical map from ``self`` to ``other`` is - injective. - - Raises a :exc:`NotImplementedError` if not known. - - EXAMPLES:: - - sage: ZZ.is_subring(QQ) - True - sage: ZZ.is_subring(GF(19)) - False - - TESTS:: - - sage: QQ.is_subring(QQ['x']) - True - sage: QQ.is_subring(GF(7)) - False - sage: QQ.is_subring(CyclotomicField(7)) # needs sage.rings.number_field - True - sage: QQ.is_subring(ZZ) - False - - Every ring is a subring of itself, :issue:`17287`:: - - sage: QQbar.is_subring(QQbar) # needs sage.rings.number_field - True - sage: RR.is_subring(RR) - True - sage: CC.is_subring(CC) # needs sage.rings.real_mpfr - True - sage: x = polygen(ZZ, 'x') - sage: K. = NumberField(x^3 - x + 1/10) # needs sage.rings.number_field - sage: K.is_subring(K) # needs sage.rings.number_field - True - sage: R. = RR[] - sage: R.is_subring(R) - True - """ - if self is other: - return True - try: - return self.Hom(other).natural_map().is_injective() - except (TypeError, AttributeError): - return False - - def is_prime_field(self): - r""" - Return ``True`` if this ring is one of the prime fields `\QQ` or - `\GF{p}`. - - EXAMPLES:: - - sage: QQ.is_prime_field() - True - sage: GF(3).is_prime_field() - True - sage: GF(9, 'a').is_prime_field() # needs sage.rings.finite_rings - False - sage: ZZ.is_prime_field() - False - sage: QQ['x'].is_prime_field() - False - sage: Qp(19).is_prime_field() # needs sage.rings.padics - False - """ - return False - def order(self): """ The number of elements of ``self``. @@ -1147,8 +991,7 @@ cdef class IntegralDomain(CommutativeRing): - ``category`` -- (default: ``None``) a category, or ``None`` This method is used by all the abstract subclasses of - :class:`IntegralDomain`, like :class:`NoetherianRing`, - :class:`Field`, ... in order to + :class:`IntegralDomain`, like :class:`Field`, ... in order to avoid cascade calls Field.__init__ -> IntegralDomain.__init__ -> ... @@ -1234,7 +1077,7 @@ cdef class NoetherianRing(CommutativeRing): _default_category = NoetherianRings() def __init__(self, *args, **kwds): - deprecation(37234, "use the category DedekindDomains") + deprecation(37234, "use the category NoetherianRings") super().__init__(*args, **kwds) @@ -1293,6 +1136,11 @@ _Fields = Fields() cdef class Field(CommutativeRing): """ Generic field + + TESTS:: + + sage: QQ.is_noetherian() + True """ _default_category = _Fields @@ -1355,26 +1203,6 @@ cdef class Field(CommutativeRing): return y.is_zero() return True - def ideal(self, *gens, **kwds): - """ - Return the ideal generated by gens. - - EXAMPLES:: - - sage: QQ.ideal(2) - Principal ideal (1) of Rational Field - sage: QQ.ideal(0) - Principal ideal (0) of Rational Field - """ - if len(gens) == 1 and isinstance(gens[0], (list, tuple)): - gens = gens[0] - if not isinstance(gens, (list, tuple)): - gens = [gens] - for x in gens: - if not self(x).is_zero(): - return self.unit_ideal() - return self.zero_ideal() - def integral_closure(self): """ Return this field, since fields are integrally closed in their @@ -1412,17 +1240,6 @@ cdef class Field(CommutativeRing): """ return True - def is_noetherian(self): - """ - Return ``True`` since fields are Noetherian rings. - - EXAMPLES:: - - sage: QQ.is_noetherian() - True - """ - return True - def krull_dimension(self): """ Return the Krull dimension of this field, which is 0. @@ -1472,34 +1289,72 @@ cdef class Field(CommutativeRing): """ raise NotImplementedError("Algebraic closures of general fields not implemented.") - -cdef class Algebra(Ring): - """ - Generic algebra - """ - def __init__(self, base_ring, names=None, normalize=True, category=None): - """ - Initialize ``self``. + def an_embedding(self, K): + r""" + Return some embedding of this field into another field `K`, + and raise a :class:`ValueError` if none exists. EXAMPLES:: - sage: A = Algebra(ZZ); A # needs sage.modules - + sage: GF(2).an_embedding(GF(4)) + Ring morphism: + From: Finite Field of size 2 + To: Finite Field in z2 of size 2^2 + Defn: 1 |--> 1 + sage: GF(4).an_embedding(GF(8)) + Traceback (most recent call last): + ... + ValueError: no embedding from Finite Field in z2 of size 2^2 to Finite Field in z3 of size 2^3 + sage: GF(4).an_embedding(GF(16)) + Ring morphism: + From: Finite Field in z2 of size 2^2 + To: Finite Field in z4 of size 2^4 + Defn: z2 |--> z4^2 + z4 + + :: + + sage: CyclotomicField(5).an_embedding(QQbar) + Coercion map: + From: Cyclotomic Field of order 5 and degree 4 + To: Algebraic Field + sage: CyclotomicField(3).an_embedding(CyclotomicField(7)) + Traceback (most recent call last): + ... + ValueError: no embedding from Cyclotomic Field of order 3 and degree 2 to Cyclotomic Field of order 7 and degree 6 + sage: CyclotomicField(3).an_embedding(CyclotomicField(6)) + Generic morphism: + From: Cyclotomic Field of order 3 and degree 2 + To: Cyclotomic Field of order 6 and degree 2 + Defn: zeta3 -> zeta6 - 1 """ - # 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 = check_default_category(Algebras(base_ring), category) - Ring.__init__(self,base_ring, names=names, normalize=normalize, - category=category) + if self.characteristic() != K.characteristic(): + raise ValueError(f'no embedding from {self} to {K}: incompatible characteristics') + H = self.Hom(K) + try: + return H.natural_map() + except TypeError: + pass + from sage.categories.sets_cat import EmptySetError + try: + return H.an_element() + except EmptySetError: + raise ValueError(f'no embedding from {self} to {K}') -cdef class CommutativeAlgebra(CommutativeRing): - __default_category = _CommutativeRings +cdef class Algebra(Ring): def __init__(self, base_ring, *args, **kwds): + if 'category' not in kwds: + kwds['category'] = Algebras(base_ring) + deprecation(38502, "use the category Algebras") + super().__init__(base_ring, *args, **kwds) + + +cdef class CommutativeAlgebra(CommutativeRing): + def __init__(self, base_ring, *args, **kwds): + self._default_category = CommutativeAlgebras(base_ring) deprecation(37999, "use the category CommutativeAlgebras") - super().__init__(*args, **kwds) + super().__init__(base_ring, *args, **kwds) def is_Ring(x): diff --git a/src/sage/rings/ring_extension.pxd b/src/sage/rings/ring_extension.pxd index 478bd6eef75..68c2dd0e974 100644 --- a/src/sage/rings/ring_extension.pxd +++ b/src/sage/rings/ring_extension.pxd @@ -1,8 +1,8 @@ from sage.categories.map cimport Map -from sage.rings.ring cimport CommutativeRing +from sage.structure.parent cimport Parent -cdef class RingExtension_generic(CommutativeRing): +cdef class RingExtension_generic(Parent): cdef _type cdef _backend cdef _defining_morphism @@ -15,10 +15,10 @@ cdef class RingExtension_generic(CommutativeRing): cdef type _fraction_field_type cpdef is_defined_over(self, base) - cpdef CommutativeRing _check_base(self, CommutativeRing base) - cpdef _degree_over(self, CommutativeRing base) - cpdef _is_finite_over(self, CommutativeRing base) - cpdef _is_free_over(self, CommutativeRing base) + cpdef Parent _check_base(self, Parent base) + cpdef _degree_over(self, Parent base) + cpdef _is_finite_over(self, Parent base) + cpdef _is_free_over(self, Parent base) cdef Map _defining_morphism_fraction_field(self, bint extend_base) @@ -31,10 +31,11 @@ cdef class RingExtensionWithBasis(RingExtension_generic): cdef _basis_names cdef _basis_latex_names - cpdef _basis_over(self, CommutativeRing base) - # cpdef _free_module(self, CommutativeRing base, bint map) + cpdef _basis_over(self, Parent base) + # cpdef _free_module(self, Parent base, bint map) cdef class RingExtensionWithGen(RingExtensionWithBasis): cdef _gen cdef _name + cdef public object _latex_names diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 174af6dd415..86ac9756351 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -117,12 +117,12 @@ from sage.cpython.getattr import dir_with_other_class from sage.misc.latex import latex, latex_variable_name from sage.structure.factory import UniqueFactory +from sage.structure.parent cimport Parent from sage.structure.element cimport Element from sage.structure.category_object import normalize_names from sage.categories.map cimport Map from sage.categories.commutative_rings import CommutativeRings from sage.categories.fields import Fields -from sage.rings.ring cimport CommutativeRing from sage.rings.integer_ring import ZZ from sage.rings.infinity import Infinity @@ -497,7 +497,7 @@ RingExtension = RingExtensionFactory("sage.rings.ring_extension.RingExtension") # General extensions #################### -cdef class RingExtension_generic(CommutativeRing): +cdef class RingExtension_generic(Parent): r""" A generic class for all ring extensions. @@ -564,8 +564,8 @@ cdef class RingExtension_generic(CommutativeRing): ... ValueError: exotic defining morphism between two rings in the tower; consider using another variable name """ - cdef CommutativeRing base, ring - cdef CommutativeRing b, backend + cdef Parent base, ring + cdef Parent b, backend cdef Map f base = defining_morphism.domain() @@ -575,7 +575,7 @@ cdef class RingExtension_generic(CommutativeRing): # but CommutativeRings() seems safer, especially when dealing with # morphisms which do not need to preserve the base category = CommutativeRings() - CommutativeRing.__init__(self, ZZ, category=category) + Parent.__init__(self, base=ZZ, category=category) self._base = base self._backend = ring self._backend_defining_morphism = defining_morphism @@ -1197,14 +1197,14 @@ cdef class RingExtension_generic(CommutativeRing): :meth:`base`, :meth:`bases`, :meth:`absolute_base` """ - cdef CommutativeRing b + cdef Parent b b = self while isinstance(b, RingExtension_generic): if b is base or (b)._backend is base: return True b = (b)._base return b is base - cpdef CommutativeRing _check_base(self, CommutativeRing base): + cpdef Parent _check_base(self, Parent base): r""" Check if ``base`` is one of the successive bases of this extension and, if it is, normalize it. @@ -1243,7 +1243,7 @@ cdef class RingExtension_generic(CommutativeRing): sage: L._check_base(None) is L.base() # needs sage.rings.finite_rings True """ - cdef CommutativeRing b + cdef Parent b if base is None: return self._base b = self @@ -1450,7 +1450,7 @@ cdef class RingExtension_generic(CommutativeRing): base = self._check_base(base) return self._degree_over(base) - cpdef _degree_over(self, CommutativeRing base): + cpdef _degree_over(self, Parent base): r""" Return the degree of this extension over ``base``. @@ -1572,7 +1572,7 @@ cdef class RingExtension_generic(CommutativeRing): sage: L.is_finite_over() # needs sage.rings.finite_rings True """ - cdef CommutativeRing b + cdef Parent b base = self._check_base(base) if base is self: return True @@ -1590,7 +1590,7 @@ cdef class RingExtension_generic(CommutativeRing): b = (b)._base raise NotImplementedError - cpdef _is_finite_over(self, CommutativeRing base): + cpdef _is_finite_over(self, Parent base): r""" Return whether or not this extension is finite over ``base``. @@ -1635,7 +1635,7 @@ cdef class RingExtension_generic(CommutativeRing): sage: L.is_free_over() # needs sage.rings.finite_rings True """ - cdef CommutativeRing b + cdef Parent b base = self._check_base(base) if base is self or base.is_field(): return True @@ -1653,7 +1653,7 @@ cdef class RingExtension_generic(CommutativeRing): b = (b)._base raise NotImplementedError - cpdef _is_free_over(self, CommutativeRing base): + cpdef _is_free_over(self, Parent base): r""" Return whether or not this extension is finite over ``base``. @@ -2191,7 +2191,7 @@ cdef class RingExtensionWithBasis(RingExtension_generic): b = b.base_ring() return base - cpdef _degree_over(self, CommutativeRing base): + cpdef _degree_over(self, Parent base): r""" Return the degree of this extension over ``base``. @@ -2218,7 +2218,7 @@ cdef class RingExtensionWithBasis(RingExtension_generic): else: return len(self._basis) * self._base._degree_over(base) - cpdef _is_finite_over(self, CommutativeRing base): + cpdef _is_finite_over(self, Parent base): r""" Return whether or not this extension is finite over ``base``. @@ -2237,7 +2237,7 @@ cdef class RingExtensionWithBasis(RingExtension_generic): return True return self._base._is_finite_over(base) - cpdef _is_free_over(self, CommutativeRing base): + cpdef _is_free_over(self, Parent base): r""" Return whether or not this extension is free over ``base``. @@ -2298,7 +2298,7 @@ cdef class RingExtensionWithBasis(RingExtension_generic): base = self._check_base(base) return self._basis_over(base) - cpdef _basis_over(self, CommutativeRing base): + cpdef _basis_over(self, Parent base): r""" Return a basis of this extension over ``base``. @@ -2588,7 +2588,6 @@ cdef class RingExtensionWithGen(RingExtensionWithBasis): RingExtensionWithBasis.__init__(self, defining_morphism, basis, basis_names, check, **kwargs) self._gen = self._backend(gen) self._names = (self._name,) - self._latex_names = (latex_variable_name(self._name),) self._basis_latex_names = basis_latex_names def _repr_topring(self, **options): diff --git a/src/sage/rings/ring_extension_element.pxd b/src/sage/rings/ring_extension_element.pxd index 6b62ad58c06..9bd662f3d0f 100644 --- a/src/sage/rings/ring_extension_element.pxd +++ b/src/sage/rings/ring_extension_element.pxd @@ -1,4 +1,4 @@ -from sage.rings.ring cimport CommutativeRing +from sage.structure.parent cimport Parent from sage.structure.element cimport Element from sage.structure.element cimport CommutativeAlgebraElement from sage.rings.ring_extension cimport RingExtension_generic @@ -13,10 +13,10 @@ cdef class RingExtensionFractionFieldElement(RingExtensionElement): pass cdef class RingExtensionWithBasisElement(RingExtensionElement): - cdef _vector(self, CommutativeRing base) - cdef _matrix(self, CommutativeRing base) - cdef _trace(self, CommutativeRing base) - cdef _norm(self, CommutativeRing base) + cdef _vector(self, Parent base) + cdef _matrix(self, Parent base) + cdef _trace(self, Parent base) + cdef _norm(self, Parent base) cpdef minpoly(self, base=*, var=*) diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index 67b6aa90879..e3184503d77 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -24,6 +24,7 @@ from sage.misc.latex import latex from sage.structure.category_object import normalize_names from sage.structure.element cimport CommutativeAlgebraElement +from sage.structure.parent cimport Parent from sage.rings.integer_ring import ZZ from sage.categories.fields import Fields from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1033,7 +1034,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): base = (self._parent)._check_base(base) return self._vector(base) - cdef _vector(self, CommutativeRing base): + cdef _vector(self, Parent base): r""" Return the vector of coordinates of this element over ``base`` (in the basis output by the method :meth:`basis_over`). @@ -1198,7 +1199,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): raise ValueError("the extension is not finite free") return self._matrix(base) - cdef _matrix(self, CommutativeRing base): + cdef _matrix(self, Parent base): r""" Return the matrix of the multiplication by this element (in the basis output by :meth:`basis_over`). @@ -1286,7 +1287,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): raise ValueError("the extension is not finite free") return self._trace(base) - cdef _trace(self, CommutativeRing base): + cdef _trace(self, Parent base): r""" Return the trace of this element over ``base``. @@ -1314,7 +1315,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): ....: assert((x+y).trace(base) == x.trace(base) + y.trace(base)) """ cdef RingExtensionWithBasis parent = self._parent - cdef CommutativeRing b + cdef Parent b if base is parent: return self b = parent._base @@ -1379,7 +1380,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): raise ValueError("the extension is not finite free") return self._norm(base) - cdef _norm(self, CommutativeRing base): + cdef _norm(self, Parent base): r""" Return the norm of this element over ``base``. @@ -1407,7 +1408,7 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): ....: assert((x*y).norm(base) == x.norm(base) * y.norm(base)) """ cdef RingExtensionWithBasis parent = self._parent - cdef CommutativeRing b + cdef Parent b if base is parent: return self b = parent._base diff --git a/src/sage/rings/ring_extension_homset.py b/src/sage/rings/ring_extension_homset.py index d2c13a11f0e..1ba55906d9c 100644 --- a/src/sage/rings/ring_extension_homset.py +++ b/src/sage/rings/ring_extension_homset.py @@ -20,6 +20,7 @@ from sage.rings.homset import RingHomset_generic from sage.rings.ring_extension_morphism import RingExtensionHomomorphism + class RingExtensionHomset(RingHomset_generic): r""" A generic class for homsets between ring extensions. diff --git a/src/sage/rings/ring_extension_morphism.pyx b/src/sage/rings/ring_extension_morphism.pyx index 2768bdac81c..d4aad7f8253 100644 --- a/src/sage/rings/ring_extension_morphism.pyx +++ b/src/sage/rings/ring_extension_morphism.pyx @@ -21,7 +21,7 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.structure.element cimport Element from sage.categories.map import Map -from sage.rings.ring cimport CommutativeRing +from sage.structure.parent cimport Parent from sage.rings.morphism cimport RingMap from sage.rings.ring_extension cimport RingExtension_generic, RingExtensionWithBasis from sage.rings.ring_extension_element cimport RingExtensionElement @@ -57,7 +57,7 @@ cdef are_equal_morphisms(f, g): sage: H(f^2) == H(g^2) # indirect doctest True """ - cdef CommutativeRing b + cdef Parent b cdef tuple gens if f is None and g is None: return True @@ -821,7 +821,7 @@ cdef class MapRelativeRingToFreeModule(Map): From: Field in z6 with defining polynomial x^2 + (10*z3^2 + z3 + 6)*x + z3 over its base To: Vector space of dimension 2 over Finite Field in z3 of size 11^3 """ - cdef CommutativeRing L, base + cdef Parent L, base self._degree = (E)._degree_over(K) self._basis = [ (x)._backend for x in E.basis_over(K) ] diff --git a/src/sage/rings/semirings/meson.build b/src/sage/rings/semirings/meson.build new file mode 100644 index 00000000000..e1e4e627ba3 --- /dev/null +++ b/src/sage/rings/semirings/meson.build @@ -0,0 +1,22 @@ +py.install_sources( + 'all.py', + 'non_negative_integer_semiring.py', + 'tropical_mpolynomial.py', + 'tropical_polynomial.py', + 'tropical_variety.py', + subdir: 'sage/rings/semirings', +) + +extension_data = {'tropical_semiring' : files('tropical_semiring.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/rings/semirings', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/rings/semirings/non_negative_integer_semiring.py b/src/sage/rings/semirings/non_negative_integer_semiring.py index cc144253328..4fc0309a44d 100644 --- a/src/sage/rings/semirings/non_negative_integer_semiring.py +++ b/src/sage/rings/semirings/non_negative_integer_semiring.py @@ -1,18 +1,19 @@ r""" Non Negative Integer Semiring """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Nicolas Borie # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.sets.non_negative_integers import NonNegativeIntegers from sage.categories.semirings import Semirings from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.sets.family import Family + class NonNegativeIntegerSemiring(NonNegativeIntegers): r""" A class for the semiring of the nonnegative integers. @@ -69,9 +70,10 @@ def __init__(self): Category of facade infinite enumerated commutative semirings sage: TestSuite(NN).run() """ - NonNegativeIntegers.__init__(self, category=(Semirings().Commutative(), InfiniteEnumeratedSets()) ) + NonNegativeIntegers.__init__(self, category=(Semirings().Commutative(), + InfiniteEnumeratedSets())) - def _repr_(self): + def _repr_(self) -> str: r""" EXAMPLES:: diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py new file mode 100644 index 00000000000..d8df667530b --- /dev/null +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -0,0 +1,691 @@ +r""" +Multivariate Tropical Polynomials + +AUTHORS: + +- Verrel Rievaldo Wijaya (2024-06): initial version + +EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: z.parent() + Multivariate Tropical Polynomial Semiring in x, y, z over Rational Field + sage: R(2)*x + R(-1)*x + R(5)*y + R(-3) + (-1)*x + 5*y + (-3) + sage: (x+y+z)^2 + 0*x^2 + 0*x*y + 0*y^2 + 0*x*z + 0*y*z + 0*z^2 + +REFERENCES: + +- [Bru2014]_ +- [Fil2017]_ +""" + +# **************************************************************************** +# Copyright (C) 2024 Verrel Rievaldo Wijaya +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.polynomial.multi_polynomial_element import MPolynomial_polydict + + +class TropicalMPolynomial(MPolynomial_polydict): + r""" + A multivariate tropical polynomial. + + Let `x_1, x_2, \ldots, x_n` be indeterminants. A tropical monomial is + any product of these variables, possibly including repetitions: + `x_1^{i_1}\cdots x_n^{i_n}` where `i_j \in \{0,1,\ldots\}`, for all + `j\in \{1,\ldots,n\}`. A multivariate tropical polynomial is a finite + linear combination of tropical monomials, + `p(x_1, \ldots, x_n) = \sum_{i=1}^n c_i x_1^{i_1}\cdots x_n^{i_n}`. + + In classical arithmetic, we can rewrite the general form of a tropical + monomial: `x_1^{i_1}\cdots x_n^{i_n} \mapsto i_1 x_1 + \cdots + i_n x_n`. + Thus, the tropical polynomial can be viewed as the minimum (maximum) of + a finite collection of linear functions. + + EXAMPLES: + + Construct a multivariate tropical polynomial semiring in two variables:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T); R + Multivariate Tropical Polynomial Semiring in a, b over Rational Field + + Define some multivariate tropical polynomials:: + + sage: p1 = R(3)*a*b + a + R(-1)*b; p1 + 3*a*b + 0*a + (-1)*b + sage: p2 = R(1)*a + R(1)*b + R(1)*a*b; p2 + 1*a*b + 1*a + 1*b + + Some basic arithmetic operations for multivariate tropical polynomials:: + + sage: p1 + p2 + 3*a*b + 1*a + 1*b + sage: p1 * p2 + 4*a^2*b^2 + 4*a^2*b + 4*a*b^2 + 1*a^2 + 1*a*b + 0*b^2 + sage: T(2) * p1 + 5*a*b + 2*a + 1*b + sage: p1(T(1),T(2)) + 6 + + Let us look at the different result for tropical curve and 3d graph + of tropical polynomial in two variables when different algebra is used. + First for the min-plus algebra:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R(3)*a*b + a + R(-1)*b + sage: p1.tropical_variety() + Tropical curve of 3*a*b + 0*a + (-1)*b + sage: p1.tropical_variety().plot() + Graphics object consisting of 3 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=True) + R = PolynomialRing(T, ('a,b')) + a, b = R.gen(), R.gen(1) + p1 = R(3)*a*b + a + R(-1)*b + tv1 = p1.tropical_variety() + sphinx_plot(tv1.plot()) + + Tropical polynomial in two variables will induce a function in three + dimension that consists of a number of surfaces:: + + sage: p1.plot3d() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=True) + R = PolynomialRing(T, ('a,b')) + a, b = R.gen(), R.gen(1) + p1 = R(3)*a*b + a + R(-1)*b + sphinx_plot(p1.plot3d()) + + If we use a max-plus algebra, we will get a slightly different result:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R(3)*a*b + a + R(-1)*b + sage: p1.tropical_variety() + Tropical curve of 3*a*b + 0*a + (-1)*b + sage: p1.tropical_variety().plot() + Graphics object consisting of 3 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('a,b')) + a, b = R.gen(), R.gen(1) + p1 = R(3)*a*b + a + R(-1)*b + tv1 = p1.tropical_variety() + sphinx_plot(tv1.plot()) + + :: + + sage: p1.plot3d() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('a,b')) + a, b = R.gen(), R.gen(1) + p1 = R(3)*a*b + a + R(-1)*b + sphinx_plot(p1.plot3d()) + + TESTS: + + There is no subtraction defined for tropical polynomials:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: a - b + Traceback (most recent call last): + ... + ArithmeticError: cannot negate any non-infinite element + """ + def subs(self, fixed=None, **kwds): + r""" + Fix some given variables in ``self`` and return the changed + tropical multivariate polynomials. + + .. SEEALSO:: + + :meth:`sage.rings.polynomial.multi_polynomial_element.MPolynomial_polydict.subs` + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + y + R(3) + sage: p1((R(4),y)) + 0*y + 8 + sage: p1.subs({x: 4}) + 0*y + 8 + """ + variables = list(self.parent().gens()) + for i in range(len(variables)): + if str(variables[i]) in kwds: + variables[i] = kwds[str(variables[i])] + elif fixed: + if variables[i] in fixed: + variables[i] = fixed[variables[i]] + elif i in fixed: + variables[i] = fixed[i] + if len(kwds) < len(variables): + for i, v in enumerate(variables): + variables[i] = self.parent()(v) + return self(tuple(variables)) + + def plot3d(self, color='random'): + """ + Return the 3d plot of ``self``. + + Only implemented for tropical polynomial in two variables. + The `x`-`y` axes for this 3d plot is the same as the `x`-`y` + axes of the corresponding tropical curve. + + OUTPUT: Graphics3d Object + + EXAMPLES: + + A simple tropical polynomial that consist of only one surface:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + sage: p1.plot3d() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = x**2 + sphinx_plot(p1.plot3d()) + + Tropical polynomials often have graphs that represent a combination + of multiple surfaces:: + + sage: p2 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + sage: p2.plot3d() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p2 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + sphinx_plot(p2.plot3d()) + + :: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p3 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p3.plot3d() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p3 = R(2)*x**2 + x*y + R(2)*y**2 + x + R(-1)*y + R(3) + sphinx_plot(p3.plot3d()) + + TESTS:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x*y*z + x + sage: p1.plot3d() + Traceback (most recent call last): + ... + NotImplementedError: can only plot the graph of tropical + multivariate polynomial in two variables + """ + from random import random + from sage.plot.graphics import Graphics + from sage.geometry.polyhedron.constructor import Polyhedron + from sage.sets.real_set import RealSet + from sage.symbolic.relation import solve + + if len(self.parent().variable_names()) != 2: + raise NotImplementedError("can only plot the graph of tropical " + "multivariate polynomial in two variables") + tv = self.tropical_variety() + axes = tv._axes() + edge = set() + if tv.components(): + v = tv._vars[0] + T = self.parent().base() + R = self.base_ring().base_ring() + + # Finding the point of curve that touch the edge of the axes + for comp in tv.components(): + if len(comp[1]) == 1: + valid_int = RealSet(comp[1][0]) + else: + valid_int = RealSet(comp[1][0]).intersection(RealSet(comp[1][1])) + for i, eqn in enumerate(comp[0]): + j = (i+1) % 2 + if not eqn.is_numeric(): + for k in range(2): + sol = solve(eqn == axes[i][k], v) + if sol[0].rhs() in valid_int: + valid_point = [R(eq.subs(**{str(v): sol[0].rhs()})) for eq in comp[0]] + if valid_point[j] in RealSet(axes[j]): + edge.add(tuple(valid_point)) + + # Combine the edge, vertices, and corner point + vertices = self.tropical_variety().vertices() + corner = set() + for i in axes[0]: + for j in axes[1]: + corner.add((i, j)) + marks = corner | vertices | edge + + # Calculate the value of polynomial at each marked point + variables = self.parent().gens() + terms = [a*variables[0]**b[0] * variables[1]**b[1] for a, b in zip(self.coefficients(), self.exponents())] + point_terms = {} + for mark in marks: + mark_terms = [] + value = self(T(mark[0]), T(mark[1])) + value_terms = [term(T(mark[0]), T(mark[1])) for term in terms] + mark_terms.extend(terms[i] for i in range(len(terms)) + if value_terms[i] == value) + point_terms[(R(mark[0]), R(mark[1]), value.lift())] = mark_terms + + # Plot the points that attained its value at one term only + combined_plot = Graphics() + for elms in point_terms.values(): + if len(elms) == 1: + poly_vert = [] + term = elms[0] + for p, t in point_terms.items(): + if term in t: + poly_vert.append(p) + t.remove(term) + if color == 'random': + rand_color = (random(), random(), random()) + plot = Polyhedron(vertices=poly_vert).plot(color=rand_color) + combined_plot += plot + + # Plot the remaining points + for remain in point_terms.values(): + for term in remain: + poly_vert = [] + for p, t in point_terms.items(): + if term in t: + poly_vert.append(p) + t.remove(term) + if color == 'random': + rand_color = (random(), random(), random()) + plot = Polyhedron(vertices=poly_vert).plot(color=rand_color) + combined_plot += plot + return combined_plot + + def tropical_variety(self): + r""" + Return tropical roots of ``self``. + + In the multivariate case, the roots can be represented by a + tropical variety. In two dimensions, this is known as a tropical + curve. For dimensions higher than two, it is referred to as a + tropical hypersurface. + + OUTPUT: a :class:`sage.rings.semirings.tropical_variety.TropicalVariety` + + EXAMPLES: + + Tropical curve for tropical polynomials in two variables:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + R(0); p1 + 0*x + 0*y + 0 + sage: p1.tropical_variety() + Tropical curve of 0*x + 0*y + 0 + + Tropical hypersurface for tropical polynomials in more than two + variables:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(1)*x*y + R(-1/2)*x*z + R(4)*z^2; p1 + 1*x*y + (-1/2)*x*z + 4*z^2 + sage: p1.tropical_variety() + Tropical surface of 1*x*y + (-1/2)*x*z + 4*z^2 + """ + from sage.rings.semirings.tropical_variety import TropicalCurve, TropicalSurface, TropicalVariety + + if self.parent().ngens() == 2: + return TropicalCurve(self) + if self.parent().ngens() == 3: + return TropicalSurface(self) + return TropicalVariety(self) + + def _repr_(self): + r""" + Return string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: x + R(-1)*y + R(-3) + 0*x + (-1)*y + (-3) + """ + if not self.monomial_coefficients(): + return str(self.parent().base().zero()) + s = super()._repr_() + if self.monomials()[-1].is_constant(): + if self.monomial_coefficient(self.parent()(0)) < 0: + s = s.replace(" - ", " + -") + const = str(self.monomial_coefficient(self.parent(0))) + s = s.replace(f" {const}", f" ({const})") + return s + + def _latex_(self): + r""" + Return the latex representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + R(-1)*x*y + R(-1) + sage: latex(p1) + 0 x^{2} + \left(-1\right) x y + \left(-1\right) + sage: latex(R.zero()) + \infty + """ + if not self.monomial_coefficients(): + return self.parent().base().zero()._latex_() + s = super()._latex_() + if self.monomials()[-1].is_constant(): + if self.monomial_coefficient(self.parent()(0)) < 0: + s = s.replace(" - ", " + -") + const = str(self.monomial_coefficient(self.parent(0))) + s = s.replace(f" {const}", f" \\left({const}\\right)") + return s + + +class TropicalMPolynomialSemiring(UniqueRepresentation, Parent): + r""" + The semiring of tropical polynomials in multiple variables. + + This is the commutative semiring consisting of all finite linear + combinations of tropical monomials under (tropical) addition + and multiplication with coefficients in a tropical semiring. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: f = T(1)*x + T(-1)*y + sage: g = T(2)*x + T(-2)*y + sage: f + g + 1*x + (-2)*y + sage: f * g + 3*x^2 + (-1)*x*y + (-3)*y^2 + sage: f + R.zero() == f + True + sage: f * R.zero() == R.zero() + True + sage: f * R.one() == f + True + """ + def __init__(self, base_semiring, n, names, order): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 5, 'x') + sage: TestSuite(R).run() + """ + from sage.rings.semirings.tropical_semiring import TropicalSemiring + from sage.categories.semirings import Semirings + if not isinstance(base_semiring, TropicalSemiring): + raise ValueError(f"{base_semiring} is not a tropical semiring") + Parent.__init__(self, base=base_semiring, names=names, category=Semirings()) + self._ngens = n + self._term_order = order + + def term_order(self): + """ + Return the defined term order of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: R.term_order() + Degree reverse lexicographic term order + """ + return self._term_order + + Element = TropicalMPolynomial + + def _element_constructor_(self, x): + r"""" + Convert ``x`` into ``self``. + + INPUT: + + - ``x`` -- ``dict``, constant, or :class:`MPolynomial` + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x,y') + sage: dict1 = {(1,0):0, (0,1):-1, (1,1):3} + sage: p1 = R(dict1); p1 + 3*x*y + 0*x + (-1)*y + sage: S. = PolynomialRing(QQ) + sage: f = -x*y + 1 + sage: R(f) + (-1)*x*y + 1 + + TESTS:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: S. = PolynomialRing(T) + sage: R(a + b) + Traceback (most recent call last): + ... + ValueError: can not convert 0*a + 0*b to Multivariate Tropical + Polynomial Semiring in x, y over Rational Field + """ + from sage.rings.polynomial.multi_polynomial import MPolynomial + if isinstance(x, TropicalMPolynomial): + if x.parent() is not self: + raise ValueError(f"can not convert {x} to {self}") + if isinstance(x, MPolynomial): + if x.parent().variable_names() == self.variable_names(): + x = x.monomial_coefficients() + else: + raise ValueError(f"can not convert {x} to {self}") + elif (x in self.base().base_ring()) or (x in self.base()): + term = [0] * self.ngens() + x = {tuple(term): x} + + if isinstance(x, dict): + for key, value in x.items(): + x[key] = self.base()(value) + return self.element_class(self, x) + + @cached_method + def one(self): + r""" + Return the multiplicative identity of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x,y') + sage: R.one() + 0 + """ + exponent = [0] * self.ngens() + return self.element_class(self, {tuple(exponent): self.base().one()}) + + @cached_method + def zero(self): + r""" + Return the additive identity of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x,y') + sage: R.zero() + +infinity + """ + exponent = [0] * self.ngens() + return self.element_class(self, {tuple(exponent): self.base().zero()}) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(RR) + sage: R. = PolynomialRing(T); R + Multivariate Tropical Polynomial Semiring in u, v, w over Real + Field with 53 bits of precision + """ + if self._ngens == 0: + return (f"Multivariate Tropical Polynomial Semiring in no variables" + f" over {self.base_ring().base_ring()}") + return (f"Multivariate Tropical Polynomial Semiring in {', '.join(self.variable_names())}" + f" over {self.base_ring().base_ring()}") + + def random_element(self, degree=2, terms=None, choose_degree=False, + *args, **kwargs): + r""" + Return a random multivariate tropical polynomial from ``self``. + + OUTPUT: a :class:`TropicalMPolynomial` + + .. SEEALSO:: + + :meth:`sage.rings.polynomial.multi_polynomial_ring_base.MPolynomialRing_base.random_element` + + EXAMPLES: + + A random polynomial of at most degree `d` and at most `t` terms:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: f = R.random_element(2, 5) + sage: f.degree() <= 2 + True + sage: f.parent() is R + True + sage: len(list(f)) <= 5 + True + + Choose degrees of monomials randomly first rather than monomials + uniformly random:: + + sage: f = R.random_element(3, 6, choose_degree=True) + sage: f.degree() <= 3 + True + sage: f.parent() is R + True + sage: len(list(f)) <= 6 + True + """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self.base().base_ring(), self.variable_names()) + f = R.random_element(degree=degree, terms=terms, choose_degree=choose_degree, + *args, **kwargs) + new_dict = {key: self.base()(value) + for key, value in f.monomial_coefficients().items()} + return self.element_class(self, new_dict) + + def gen(self, n=0): + r""" + Return the ``n``-th generator of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: R.gen() + 0*a + sage: R.gen(2) + 0*c + + TESTS:: + + sage: R.gen(3) + Traceback (most recent call last): + ... + IndexError: tuple index out of range + """ + return self.gens()[n] + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 5, 'x') + sage: R.gens() + (0*x0, 0*x1, 0*x2, 0*x3, 0*x4) + """ + gens = [] + for i in range(self.ngens()): + exponent = [0] * self.ngens() + exponent[i] = 1 + element = self.element_class(self, {tuple(exponent): self.base().one()}) + gens.append(element) + return tuple(gens) + + def ngens(self): + r""" + Return the number of generators of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 10, 'z') + sage: R.ngens() + 10 + """ + from sage.rings.integer_ring import ZZ + return ZZ(self._ngens) diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py new file mode 100644 index 00000000000..c0dff405ac9 --- /dev/null +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -0,0 +1,993 @@ +r""" +Univariate Tropical Polynomials + +AUTHORS: + +- Verrel Rievaldo Wijaya (2024-06): initial version + +EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: x.parent() + Univariate Tropical Polynomial Semiring in x over Rational Field + sage: (x + R(3)*x) * (x^2 + x) + 3*x^3 + 3*x^2 + sage: (x^2 + R(1)*x + R(-1))^2 + 0*x^4 + 1*x^3 + 2*x^2 + 0*x + (-2) + +REFERENCES: + +- [Bru2014]_ +- [Fil2017]_ +""" + +# **************************************************************************** +# Copyright (C) 2024 Verrel Rievaldo Wijaya +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_sparse + + +class TropicalPolynomial(Polynomial_generic_sparse): + r""" + A univariate tropical polynomial. + + A tropical polynomial is a polynomial whose coefficients come from + a tropical semiring. Tropical polynomial induces a function which is + piecewise linear, where each segment of the function has a non-negative + integer slope. Tropical roots (zeros) of polynomial `P(x)` is defined + as all points `x_0` for which the graph of `P(x)` change its slope. + The difference in the slopes of the two pieces adjacent to this root + gives the order of the root. + + The tropical polynomials are implemented with a sparse format by using + a ``dict`` whose keys are the exponent and values the corresponding + coefficients. Each coefficient is a tropical number. + + EXAMPLES: + + First, we construct a tropical polynomial semiring by defining a base + tropical semiring and then inputting it to :class:`PolynomialRing`:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T); R + Univariate Tropical Polynomial Semiring in x over Rational Field + sage: R.0 + 0*x + + One way to construct an element is to provide a list or tuple of + coefficients. Another way to define an element is to write a polynomial + equation with each coefficient converted to the semiring:: + + sage: p1 = R([1,4,None,0]); p1 + 0*x^3 + 4*x + 1 + sage: p2 = R(1)*x^2 + R(2)*x + R(3); p2 + 1*x^2 + 2*x + 3 + + We can do some basic arithmetic operations for these tropical polynomials. + Remember that numbers given have to be in the tropical semiring. + If not, then it will raise an error:: + + sage: p1 + p2 + 0*x^3 + 1*x^2 + 4*x + 3 + sage: p1 * p2 + 1*x^5 + 2*x^4 + 5*x^3 + 6*x^2 + 7*x + 4 + sage: 2 * p1 + Traceback (most recent call last): + ... + ArithmeticError: cannot negate any non-infinite element + sage: T(2) * p1 + 2*x^3 + 6*x + 3 + sage: p1(3) + Traceback (most recent call last): + ... + TypeError: no common canonical parent for objects with parents: + 'Tropical semiring over Rational Field' and 'Integer Ring' + sage: p1(T(3)) + 9 + + We can find all tropical roots of a tropical polynomial, counted + with their multiplicities:: + + sage: p1.roots() + [-3, 2, 2] + sage: p2.roots() + [1, 1] + + Even though every tropical polynomials have tropical roots, this does not + neccessarily means it can be factored into its linear factors:: + + sage: p1.factor() + (0) * (0*x^3 + 4*x + 1) + sage: p2.factor() + (1) * (0*x + 1)^2 + + Every tropical polynomial `p(x)` have a corresponding unique tropical + polynomial `\bar{p}(x)` with the same roots that can be factored. We + call `\bar{p}(x)` the tropical polynomial split form of `p(x)`:: + + sage: p1.split_form() + 0*x^3 + 2*x^2 + 4*x + 1 + sage: p2.split_form() + 1*x^2 + 2*x + 3 + + Every tropical polynomial induce a piecewise linear function that can be + invoked in the following way:: + + sage: p1.piecewise_function() + piecewise(x|-->1 on (-oo, -3], x|-->x + 4 on (-3, 2), x|-->3*x on [2, +oo); x) + sage: p1.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, 'x') + p1 = R([1,4,None,0]) + sphinx_plot(p1.plot()) + + :: + + sage: p2.piecewise_function() + piecewise(x|-->3 on (-oo, 1], x|-->2*x + 1 on (1, +oo); x) + sage: p2.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, 'x') + x = R.gen() + p2 = R(1)*x**2 + R(2)*x + R(3) + sphinx_plot(plot(p2, xmin=-1, xmax=3)) + + TESTS: + + There is no subtraction for tropical polynomials because element in + tropical semiring doesn't necessarily have additive inverse:: + + sage: -p1 + Traceback (most recent call last): + ... + ArithmeticError: cannot negate any non-infinite element + """ + def roots(self): + r""" + Return the list of all tropical roots of ``self``, counted with + multiplicity. + + OUTPUT: a list of tropical numbers + + ALGORITHM: + + For each pair of monomials in the polynomial, we find the point + where their values are equal. This is the same as solving the + equation `c_1 + a_1 \cdot x = c_2 + a_2 \cdot x` for `x`, where + `(c_1, a_1)` and `(c_2, a_2)` are the coefficients and exponents + of monomials. + + The solution to this equation is `x = (c_2-c_1)/(a_1-a_2)`. We + substitute this `x` to each monomials in polynomial and check if + the maximum (minimum) is achieved by the previous two monomials. + If it is, then `x` is the root of tropical polynomial. In this + case, the order of the root at `x` is the maximum of `|i-j|` for + all possible pairs `i,j` which realise this maximum (minimum). + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R([5,4,1,0,2,4,3]); p1 + 3*x^6 + 4*x^5 + 2*x^4 + 0*x^3 + 1*x^2 + 4*x + 5 + sage: p1.roots() + [-1, -1, -1, 1, 2, 2] + + There will be no tropical root for constant polynomial. Additionaly, + for a monomial, the tropical root is assumed to be the additive + identity of its base tropical semiring:: + + sage: p2 = R(2) + sage: p2.roots() + [] + sage: p3 = x^3 + sage: p3.roots() + [+infinity, +infinity, +infinity] + """ + from itertools import combinations + tropical_roots = [] + data = self.monomial_coefficients() + R = self.parent().base() + if len(data) == 1: + exponent = next(iter(data)) + if not exponent: + return tropical_roots + return [R.zero()] * exponent + dict_root = {} + dict_coeff = {i: c.lift() for i, c in data.items()} + for comb in combinations(dict_coeff, 2): + index1, index2 = comb[0], comb[1] + root = (dict_coeff[index1]-dict_coeff[index2]) / (index2-index1) + val_root = dict_coeff[index1] + index1*root + check_maks = True + for key in dict_coeff: + if key not in comb: + val = dict_coeff[key] + key*root + if R._use_min: + if val < val_root: + check_maks = False + break + elif val > val_root: + check_maks = False + break + if check_maks: + order = abs(index1-index2) + if root not in dict_root: + dict_root[root] = order + elif order > dict_root[root]: + dict_root[root] = order + for root in dict_root: + tropical_roots += [root] * dict_root[root] + return sorted(tropical_roots) + + def split_form(self): + r""" + Return the tropical polynomial which has the same roots as ``self`` + but which can be reduced to its linear factors. + + If a tropical polynomial has roots at `x_1, x_2, \ldots, x_n`, then + its split form is the tropical product of linear terms of the form + `(x + x_i)` for all `i=1,2,\ldots,n`. + + OUTPUT: :class:`TropicalPolynomial` + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R([5,4,1,0,2,4,3]); p1 + 3*x^6 + 4*x^5 + 2*x^4 + 0*x^3 + 1*x^2 + 4*x + 5 + sage: p1.split_form() + 3*x^6 + 2*x^5 + 1*x^4 + 0*x^3 + 1*x^2 + 3*x + 5 + + :: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R([5,4,1,0,2,4,3]) + sage: p1.split_form() + 3*x^6 + 4*x^5 + 21/5*x^4 + 22/5*x^3 + 23/5*x^2 + 24/5*x + 5 + + TESTS: + + We verify that the roots of tropical polynomial and its split form + is really the same:: + + sage: p1.roots() == p1.split_form().roots() + True + """ + roots = self.roots() + R = self.parent() + poly = R(self.monomial_coefficients()[self.degree()].lift()) + for root in roots: + linear = R([root, 0]) + poly *= linear + return poly + + def factor(self): + r""" + Return the factorization of ``self`` into its tropical linear factors. + + Note that the factor `x - x_0` in classical algebra gets transformed + to the factor `x + x_0`, since the root of the tropical polynomial + `x + x_0` is `x_0` and not `-x_0`. However, not every tropical + polynomial can be factored. + + OUTPUT: a :class:'Factorization' + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R([6,3,1,0]); p1 + 0*x^3 + 1*x^2 + 3*x + 6 + sage: factor(p1) + (0) * (0*x + 1) * (0*x + 2) * (0*x + 3) + + Such factorization is not always possible:: + + sage: p2 = R([4,4,2]); p2 + 2*x^2 + 4*x + 4 + sage: p2.factor() + (2) * (0*x^2 + 2*x + 2) + + TESTS: + + The factorization for a constant:: + + sage: p3 = R(3) + sage: p3.factor() + (3) * 0 + """ + from sage.structure.factorization import Factorization + unit = self.monomial_coefficients()[self.degree()] + if self != self.split_form() or not self.roots(): + factor = [(self * self.parent(-unit.lift()), 1)] + return Factorization(factor, unit=unit) + + R = self.parent() + roots_order = {} + for root in self.roots(): + if root in roots_order: + roots_order[root] += 1 + else: + roots_order[root] = 1 + factors = [(R([root, 0]), roots_order[root]) + for root in roots_order] + return Factorization(factors, unit=unit) + + def piecewise_function(self): + r""" + Return the tropical polynomial function of ``self``. + + The function is a piecewise linear function with the domains are + divided by the roots. First we convert each term of polynomial to + its corresponding linear function. Next, we must determine which + term achieves the minimum (maximum) at each interval. + + OUTPUT: A piecewise function + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R([4,2,1,3]); p1 + 3*x^3 + 1*x^2 + 2*x + 4 + sage: p1.piecewise_function() + piecewise(x|-->4 on (-oo, 1/3], x|-->3*x + 3 on (1/3, +oo); x) + + A constant tropical polynomial will result in a constant function:: + + sage: p2 = R(3) + sage: p2.piecewise_function() + 3 + + A monomial will resulted in a linear function:: + + sage: p3 = R(1)*x^3 + sage: p3.piecewise_function() + 3*x + 1 + """ + from sage.symbolic.ring import SR + from sage.functions.piecewise import piecewise + from sage.sets.real_set import RealSet + + x = SR.var('x') + data = self.monomial_coefficients() + R = self.parent().base() + if not self.roots(): + f = data[0].lift() + return f + + if len(data) == 1: + gradient = list(data)[0] + intercept = data[gradient].lift() + f = intercept + gradient*x + return f + + unique_root = sorted(set(self.roots())) + pieces = [] + domain = [] + for i in range(len(unique_root) + 1): + if i == 0: + test_number = R(unique_root[i] - 1) + elif i == len(unique_root): + test_number = R(unique_root[i-1] + 1) + else: + test_number = R((unique_root[i]+unique_root[i-1]) / 2) + terms = {i: c * test_number**i for i, c in data.items()} + if R._use_min: + critical = min(terms.values()) + else: + critical = max(terms.values()) + found_key = None + for key, value in terms.items(): + if value == critical: + found_key = key + break + gradient = found_key + intercept = data[found_key].lift() + + # To make sure all roots is included in the domain + if i == 0: + interval = RealSet.unbounded_below_closed(unique_root[i]) + piecewise_linear = (interval, intercept + gradient*x) + domain.append(interval) + elif i == len(unique_root): + if domain[i-1][0].upper_closed(): + interval = RealSet.unbounded_above_open(unique_root[i-1]) + else: + interval = RealSet.unbounded_above_closed(unique_root[i-1]) + piecewise_linear = (interval, intercept + gradient*x) + domain.append(interval) + else: + if domain[i-1][0].upper_closed(): + interval = RealSet((unique_root[i-1], unique_root[i])) + else: + interval = RealSet([unique_root[i-1], unique_root[i]]) + piecewise_linear = (interval, intercept + gradient*x) + domain.append(interval) + pieces.append(piecewise_linear) + + f = piecewise(pieces) + return f + + def plot(self, xmin=None, xmax=None): + r""" + Return the plot of ``self``, which is the tropical polynomial + function we get from ``self.piecewise_function()``. + + INPUT: + + - ``xmin`` -- (optional) real number + - ``xmax`` -- (optional) real number + + OUTPUT: + + If ``xmin`` and ``xmax`` is given, then it return a plot of + piecewise linear function of ``self`` with the axes start from + ``xmin`` to ``xmax``. Otherwise, the domain will start from the + the minimum root of ``self`` minus 1 to maximum root of ``self`` + plus 1. If the function of ``self`` is constant or linear, then + the default domain will be `[-1,1]`. + + EXAMPLES: + + If the tropical semiring use a max-plus algebra, then the graph + will be of piecewise linear convex function:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R([4,2,1,3]); p1 + 3*x^3 + 1*x^2 + 2*x + 4 + sage: p1.roots() + [1/3, 1/3, 1/3] + sage: p1.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, 'x') + p1 = p1 = R([4,2,1,3]) + sphinx_plot(p1.plot()) + + A different result will be obtained if the tropical semiring employs + a min-plus algebra. Rather, a graph of the piecewise linear concave + function will be obtained:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R([4,2,1,3]) + sage: p1.roots() + [-2, 1, 2] + sage: p1.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=True) + R = PolynomialRing(T, 'x') + p1 = R([4,2,1,3]) + sphinx_plot(plot(p1, xmin=-4, xmax=4)) + + TESTS: + + If ``xmin`` or ``xmax`` is given as an input, then the others also + have to be given. Otherwise it will raise an error:: + + sage: plot(p1, 5) + Traceback (most recent call last): + ... + ValueError: expected 2 inputs for xmin and xmax, but got 1 + + Error also occured when ``xmin`` is greater or equal than``xmax``:: + + sage: plot(p1, 5, 3) + Traceback (most recent call last): + ... + ValueError: xmin = 5 should be less than xmax = 3 + """ + from sage.plot.plot import plot + f = self.piecewise_function() + if (xmin is None) and (xmax is None): + roots = sorted(self.roots()) + if (not roots) or (self.parent().base().zero() in roots): + return plot(f, xmin=-1, xmax=1) + else: + return plot(f, xmin=roots[0]-1, xmax=roots[-1]+1) + elif xmin is None or xmax is None: + raise ValueError("expected 2 inputs for xmin and xmax, but got 1") + elif xmin >= xmax: + raise ValueError(f"xmin = {xmin} should be less than xmax = {xmax}") + return plot(f, xmin=xmin, xmax=xmax) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: R([-3,-1,2,-1]) + (-1)*x^3 + 2*x^2 + (-1)*x + (-3) + """ + import re + if not self.monomial_coefficients(): + return str(self.parent().base().zero()) + + def replace_negatives(expr): + modified_expr = re.sub(r'(-\d+/\d+|-\d+)', r'(\1)', expr) + return modified_expr + + s = super()._repr() + v = self.parent().variable_name() + if s[0] == v: + s = "1*" + s + s = s.replace(" - ", " + -") + s = s.replace(" + "+v, " + 1*"+v) + s = s.replace("-"+v, "-1*"+v) + s = replace_negatives(s) + return s + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: p1 = R([-1,2,None,-3]) + sage: latex(p1) + \left(-3\right) x^{3} + 2 x + \left(-1\right) + """ + s = " " + coeffs = self.list(copy=False) + m = len(coeffs) + name = self.parent().latex_variable_names()[0] + for n in reversed(range(m)): + x = coeffs[n] + x = x._latex_() + if x != self.parent().base().zero()._latex_(): + if n != m-1: + s += " + " + if x.find("-") == 0: + x = "\\left(" + x + "\\right)" + if n > 1: + v = f"|{name}^{{{n}}}" + elif n == 1: + v = f"|{name}" + else: + v = "" + s += f"{x} {v}" + s = s.replace("|", "") + if s == " ": + return self.parent().base().zero()._latex_() + return s[1:].lstrip().rstrip() + + +class TropicalPolynomialSemiring(UniqueRepresentation, Parent): + r""" + The semiring of univariate tropical polynomials. + + This is the commutative semiring consisting of finite linear + combinations of tropical monomials under (tropical) + addition and multiplication with the identity element + being `+\infty` (or `-\infty` depending on whether the + base tropical semiring is using min-plus or max-plus algebra). + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: f = T(1)*x + sage: g = T(2)*x + sage: f + g + 1*x + sage: f * g + 3*x^2 + sage: f + R.zero() == f + True + sage: f * R.zero() == R.zero() + True + sage: f * R.one() == f + True + """ + @staticmethod + def __classcall_private__(cls, base_semiring, names): + """ + Ensures the names parameter is a tuple. + + EXAMPLES:: + + sage: from sage.rings.semirings.tropical_polynomial import TropicalPolynomialSemiring + sage: T = TropicalSemiring(ZZ) + sage: TPS = TropicalPolynomialSemiring + sage: TPS(T, 'x') == TPS(T, ('x')) + True + """ + if isinstance(names, str): + names = (names,) + return super().__classcall__(cls, base_semiring, tuple(names)) + + def __init__(self, base_semiring, names): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: TestSuite(R).run() + + TESTS:: + + sage: from sage.rings.semirings.tropical_polynomial import TropicalPolynomialSemiring + sage: TropicalPolynomialSemiring(ZZ, names='x') + Traceback (most recent call last): + ... + ValueError: Integer Ring is not a tropical semiring + """ + from sage.categories.semirings import Semirings + from sage.rings.semirings.tropical_semiring import TropicalSemiring + if not isinstance(base_semiring, TropicalSemiring): + raise ValueError(f"{base_semiring} is not a tropical semiring") + Parent.__init__(self, base=base_semiring, names=names, category=Semirings()) + + Element = TropicalPolynomial + + def _element_constructor_(self, x=None, check=True): + """ + Convert ``x`` into ``self``, possibly non-canonically. + + INPUT: + + - ``x`` -- a list or tuple of coefficients, a polynomial, or a + dictionary + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: R([1,2,3]) + 3*x^2 + 2*x + 1 + sage: S. = PolynomialRing(QQ) + sage: R(x^2 - x + 1) + 1*x^2 + (-1)*x + 1 + + If ``x`` is a tropical polynomial from different semiring, then it + will converted to constant:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: S. = PolynomialRing(T) + sage: p1 = R(y); p1 + 0*y + sage: p1.parent() + Univariate Tropical Polynomial Semiring in x over Rational Field + sage: p1.degree() + 0 + """ + if isinstance(x, (list, tuple)): + for i, coeff in enumerate(x): + if coeff == 0: + x[i] = self.base().one() + elif isinstance(x, TropicalPolynomial): + if x.parent() is not self: + x = {0: x} + check = False + return self.element_class(self, x, check=check) + + @cached_method + def one(self): + """ + Return the multiplicative identity of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: R.one() + 0 + """ + return self.element_class(self, [self.base().one()]) + + @cached_method + def zero(self): + """ + Return the additive identity of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: R.zero() + +infinity + """ + return self.element_class(self, [self.base().zero()]) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(ZZ) + sage: R. = PolynomialRing(T); R + Univariate Tropical Polynomial Semiring in abc over Integer Ring + """ + return (f"Univariate Tropical Polynomial Semiring in {self.variable_name()}" + f" over {self.base_ring().base_ring()}") + + def gen(self, n=0): + """ + Return the indeterminate generator of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: R.gen() + 0*abc + + TESTS:: + + sage: R.gen(2) + Traceback (most recent call last): + ... + IndexError: generator n not defined + """ + if n != 0: + raise IndexError("generator n not defined") + return self.gens()[n] + + @cached_method + def gens(self): + """ + Return a tuple whose entries are the generators for ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: R.gens() + (0*abc,) + """ + R = self.base() + return (self.element_class(self, [R.zero(), R.one()]),) + + def ngens(self): + """ + Return the number of generators of ``self``, which is 1 + since it is a univariate polynomial ring. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: R.ngens() + 1 + """ + from sage.rings.integer_ring import ZZ + return ZZ.one() + + def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): + r""" + Return a random tropical polynomial of given degrees (bounds). + + OUTPUT: a :class:`TropicalPolynomial` + + .. SEEALSO:: + + :meth:`sage.rings.polynomial.polynomial_ring.PolynomialRing_general.random_element` + + EXAMPLES: + + Tropical polynomial over an integer with each coefficient bounded + between ``x`` and ``y``:: + + sage: T = TropicalSemiring(ZZ) + sage: R = PolynomialRing(T, 'x') + sage: f = R.random_element(8, x=3, y=10) + sage: f.degree() + 8 + sage: f.parent() is R + True + sage: all(a >= T(3) for a in f.coefficients()) + True + sage: all(a < T(10) for a in f.coefficients()) + True + + If a tuple of two integers is provided for the ``degree`` argument, + a polynomial is selected with degrees within that range:: + + sage: all(R.random_element(degree=(1, 5)).degree() in range(1, 6) for _ in range(10^3)) + True + + Note that the zero polynomial (`\pm \infty`) has degree `-1`. + To include it, set the minimum degree to `-1`:: + + sage: while R.random_element(degree=(-1,2), x=-1, y=1) != R.zero(): + ....: pass + + Monic polynomials are chosen among all monic polynomials with degree + between the given ``degree`` argument:: + + sage: all(R.random_element(degree=(-1, 2), monic=True).is_monic() for _ in range(10^3)) + True + """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self.base().base_ring(), self.variable_names()) + f = R.random_element(degree=degree, monic=monic, *args, **kwds) + new_dict = f.monomial_coefficients() + if monic: + new_dict[f.degree()] = 0 + return self.element_class(self, new_dict) + + def is_sparse(self): + """ + Return ``True`` to indicate that the objects are sparse polynomials. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R = PolynomialRing(T, 'x') + sage: R.is_sparse() + True + """ + return True + + def interpolation(self, points): + """ + Return a tropical polynomial with its function is a linear + interpolation of point in ``points`` if possible. + + If there is only one point, then it will give a constant polynomial. + Because we are using linear interpolation, each point is actually + a root of the resulted tropical polynomial. + + INPUT: + + - ``points`` -- a list of tuples ``(x, y)`` + + OUTPUT: a :class:`TropicalPolynomial` + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R = PolynomialRing(T, 'x') + sage: points = [(-2,-3),(1,3),(2,4)] + sage: p1 = R.interpolation(points); p1 + 1*x^2 + 2*x + 4 + sage: p1.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=True) + R = PolynomialRing(T, 'x') + points = [(-2,-3),(1,3),(2,4)] + p1 = R.interpolation(points) + sphinx_plot(p1.plot()) + + :: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R = PolynomialRing(T,'x') + sage: points = [(0,0),(1,1),(2,4)] + sage: p1 = R.interpolation(points); p1 + (-2)*x^3 + (-1)*x^2 + 0*x + 0 + sage: p1.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, 'x') + points = [(0,0),(1,1),(2,4)] + p1 = R.interpolation(points) + sphinx_plot(p1.plot()) + + TESTS: + + Every piecewise linear component of tropical polynomial function + has to have an integer slope:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R = PolynomialRing(T,'x') + sage: points = [(0,0),(2,3)] + sage: R.interpolation(points) + Traceback (most recent call last): + ... + ValueError: the slope is not an integer + + For max-plus algebra, the slope of the componenets has to be + increasing as we move from left to right. Conversely for min-plus + algebra, the slope of the componenets has to be decreasing from + left to right:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R = PolynomialRing(T,'x') + sage: points = [(-2,-3),(1,3),(2,4)] + sage: R.interpolation(points) + Traceback (most recent call last): + ... + ValueError: can not interpolate these points + """ + points = sorted(points, key=lambda point: point[0]) + all_slope = [0] + roots = {} + R = self.base() + if R._use_min: + point_order = range(len(points)-1, 0, -1) + else: + point_order = range(len(points)-1) + for i in point_order: + if R._use_min: + slope = (points[i-1][1]-points[i][1]) / (points[i-1][0]-points[i][0]) + else: + slope = (points[i+1][1]-points[i][1]) / (points[i+1][0]-points[i][0]) + if not slope.is_integer(): + raise ValueError("the slope is not an integer") + if slope < all_slope[-1]: + raise ValueError("can not interpolate these points") + elif slope > all_slope[-1]: + order = slope - all_slope[-1] + all_slope.append(slope) + roots[points[i][0]] = order + + if len(all_slope) == 1: # constant polynomial + return self(points[0][1]) + + result = self.one() + for root, order in roots.items(): + result *= self([root, 0])**order + test_value = result(R(points[0][0])) + unit = R(points[0][1] - test_value.lift()) + result *= unit + return result + + @classmethod + def _implementation_names(cls, implementation, base_ring, sparse): + """ + Return the only implementation currently available for this semiring, + which is ``[None]``. + + EXAMPLES:: + + sage: from sage.rings.semirings.tropical_polynomial import TropicalPolynomialSemiring + sage: T = TropicalSemiring(QQ) + sage: TropicalPolynomialSemiring._implementation_names(None, T, True) + [None] + """ + names = [None] + assert isinstance(names, list) + assert implementation in names + return names diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py new file mode 100644 index 00000000000..3eb04372d13 --- /dev/null +++ b/src/sage/rings/semirings/tropical_variety.py @@ -0,0 +1,1178 @@ +r""" +Tropical Varieties + +A tropical variety is a piecewise-linear geometric object derived from +a classical algebraic variety by using tropical mathematics, where the +tropical semiring replaces the usual arithmetic operations. + +AUTHORS: + +- Verrel Rievaldo Wijaya (2024-06): initial version + +REFERENCES: + +- [Bru2014]_ +- [Fil2017]_ +""" + +# **************************************************************************** +# Copyright (C) 2024 Verrel Rievaldo Wijaya +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.rings.infinity import infinity +from sage.structure.unique_representation import UniqueRepresentation + + +class TropicalVariety(UniqueRepresentation, SageObject): + r""" + A tropical variety in `\RR^n`. + + A tropical variety is defined as a corner locus of tropical polynomial + function. This means it consist of all points in `\RR^n` for which + the minimum (maximum) of the function is attained at least twice. + + We represent the tropical variety as a list of lists, where the + inner list consist of three parts. The first one is a parametric + equations for tropical roots. The second one is the condition + for parameters. The third one is the order of the corresponding + component. + + INPUT: + + - ``poly`` -- a :class:`TropicalMPolynomial` + + ALGORITHM: + + We need to determine a corner locus of this tropical polynomial + function, which is all points `(x_1, x_2, \ldots, x_n)` for which + the maximum (minimum) is obtained at least twice. First, we convert + each monomial to its corresponding linear function. Then for each two + monomials of polynomial, we find the points where their values are + equal. Since we attempt to solve the equality of two equations in `n` + variables, the solution set will be described by `n-1` parameters. + + Next, we need to check if the value of previous two monomials at the + points in solution set is really the maximum (minimum) of function. + We do this by solving the inequality of the previous monomial with all + other monomials in the polynomial after substituting the parameter. + This will give us the condition of parameters. Each of this condition + is then combined by union operator. If this final condition is not an + empty set, then it represent one component of tropical root. Then we + calculate the weight of this particular component by the maximum of + gcd of the numbers `|i-k|` and `|j-l|` for all pairs `(i,j)` and + `(k,l)` such that the value of on this component is given by the + corresponding monomials. + + EXAMPLES: + + We construct a tropical variety in `\RR^2`, where it is called a + tropical curve:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R(1)*x + x*y + R(0); p1 + 0*x*y + 1*x + 0 + sage: tv = p1.tropical_variety(); tv + Tropical curve of 0*x*y + 1*x + 0 + sage: tv.components() + [[(t1, 1), [t1 >= -1], 1], [(-1, t1), [t1 <= 1], 1], [(-t1, t1), [t1 >= 1], 1]] + sage: tv.vertices() + {(-1, 1)} + sage: tv.plot() + Graphics object consisting of 3 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(1)*x + x*y + R(0) + sphinx_plot(p1.tropical_variety().plot()) + + A slightly different result will be obtained if we use min-plus algebra + for the base tropical semiring:: + + sage: T = TropicalSemiring(QQ, use_min=True) + sage: R. = PolynomialRing(T) + sage: p1 = R(1)*x + x*y + R(0) + sage: tv = p1.tropical_variety(); tv + Tropical curve of 0*x*y + 1*x + 0 + sage: tv.components() + [[(t1, 1), [t1 <= -1], 1], [(-1, t1), [t1 >= 1], 1], [(-t1, t1), [t1 <= 1], 1]] + sage: tv.plot() + Graphics object consisting of 3 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=True) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(1)*x + x*y + R(0) + sphinx_plot(p1.tropical_variety().plot()) + + Tropical variety can consist of multiple components with varying orders:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R(7) + T(4)*x + y + R(4)*x*y + R(3)*y^2 + R(-3)*x^2 + sage: tv = p1.tropical_variety(); tv + Tropical curve of (-3)*x^2 + 4*x*y + 3*y^2 + 4*x + 0*y + 7 + sage: tv.components() + [[(3, t1), [t1 <= 0], 1], + [(-t1 + 3, t1), [0 <= t1, t1 <= 2], 1], + [(t1, 2), [t1 <= 1], 2], + [(t1, 0), [3 <= t1, t1 <= 7], 1], + [(7, t1), [t1 <= 0], 1], + [(t1 - 1, t1), [2 <= t1], 1], + [(t1 + 7, t1), [0 <= t1], 1]] + sage: tv.plot() + Graphics object consisting of 8 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(7) + T(4)*x + y + R(4)*x*y + R(3)*y**2 + R(-3)*x**2 + sphinx_plot(p1.tropical_variety().plot()) + + If the tropical polynomial have `n>2` variables, then the result will be + a tropical hypersurface embedded in a real space `\RR^n`:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x*y + R(-1/2)*x*z + R(4)*z^2 + a*x + sage: tv = p1.tropical_variety(); tv + Tropical hypersurface of 0*a*x + 0*x*y + (-1/2)*x*z + 4*z^2 + sage: tv.components() + [[(t1, t2, t3 - 1/2, t3), [t2 - 9/2 <= t3, t3 <= t1 + 1/2, t2 - 5 <= t1], 1], + [(t1, 2*t2 - t3 + 4, t3, t2), [t3 + 1/2 <= t2, t3 <= t1], 1], + [(t1, t2, t1, t3), [max(t1 + 1/2, 1/2*t1 + 1/2*t2 - 2) <= t3], 1], + [(t1, t2 + 9/2, t3, t2), [t2 <= min(t3 + 1/2, t1 + 1/2)], 1], + [(t1 - 1/2, t2, t3, t1), [t2 - 9/2 <= t1, t1 <= t3 + 1/2, t2 - 5 <= t3], 1], + [(2*t1 - t2 + 4, t2, t3, t1), [t1 <= min(1/2*t2 + 1/2*t3 - 2, t2 - 9/2)], 1]] + """ + def __init__(self, poly): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: tv = (x+y).tropical_variety() + sage: TestSuite(tv).run() + + TESTS:: + + sage: from sage.rings.semirings.tropical_variety import TropicalVariety + sage: R. = QQ[] + sage: p1 = x + y + sage: TropicalVariety(p1) + Traceback (most recent call last): + ... + ValueError: x + y is not a multivariate tropical polynomial + """ + import operator + from itertools import combinations + from sage.symbolic.ring import SR + from sage.symbolic.relation import solve + from sage.arith.misc import gcd + from sage.rings.semirings.tropical_mpolynomial import TropicalMPolynomial + + if not isinstance(poly, TropicalMPolynomial): + raise ValueError(f"{poly} is not a multivariate tropical polynomial") + + self._poly = poly + self._hypersurface = [] + tropical_roots = [] + variables = [SR.var(name) + for name in poly.parent().variable_names()] + + # Convert each term to its linear function + linear_eq = {} + pd = poly.monomial_coefficients() + for key in pd: + eq = sum(variables[i] * e for i, e in enumerate(key)) + eq += pd[key].lift() + linear_eq[key] = eq + temp_keys = [] + temp_order = [] + + # Checking for all possible combinations of two terms + for keys in combinations(pd, 2): + sol = solve(linear_eq[keys[0]] == linear_eq[keys[1]], variables) + + # Parametric solution of the chosen two terms + final_sol = [s.right() for s in sol[0]] + xy_interval = [] + xy_interval.append(tuple(final_sol)) + + # Comparing with other terms + min_max = linear_eq[keys[0]] + for i, v in enumerate(variables): + min_max = min_max.subs(**{str(v): final_sol[i]}) + all_sol_compare = [] + no_solution = False + for compare in pd: + if compare not in keys: + temp_compare = linear_eq[compare] + for i, v in enumerate(variables): + temp_compare = temp_compare.subs(**{str(v): final_sol[i]}) + if min_max == temp_compare: + sol_compare = [[]] + elif poly.parent().base()._use_min: + sol_compare = solve(min_max < temp_compare, variables) + else: + sol_compare = solve(min_max > temp_compare, variables) + if sol_compare: + if isinstance(sol_compare[0], list): + if sol_compare[0]: + all_sol_compare.append(sol_compare[0][0]) + else: # solution is unbounded on one side + all_sol_compare.append(sol_compare[0]) + else: + no_solution = True + break + + # Solve the condition for parameter + if not no_solution: + parameter = set() + for sol in all_sol_compare: + parameter = parameter.union(set(sol.variables())) + parameter_solution = solve(all_sol_compare, list(parameter)) + if parameter_solution: + xy_interval.append(parameter_solution[0]) + tropical_roots.append(xy_interval) + # Calculate the order + index_diff = [abs(ai - bi) + for ai, bi in zip(keys[0], keys[1])] + order = gcd(index_diff) + temp_order.append(order) + temp_keys.append(keys) + + # Changing all the operator symbol to be <= or >= + self._keys = [] + components = [] + dim_param = 0 + if tropical_roots: + dim_param = len(tropical_roots[0][0]) - 1 + vars = [SR.var(f't{i}') for i in range(1, dim_param + 1)] + for arg in tropical_roots: + subs_dict = {} + index_vars = 0 + new_eq = [] + for eq in arg[0]: + var_eq = eq.variables() + for var in var_eq: + if var not in subs_dict: + subs_dict[var] = vars[index_vars] + index_vars += 1 + new_eq.append(eq.subs(subs_dict)) + new_eq = tuple(new_eq) + arg.remove(arg[0]) + arg.insert(0, new_eq) + if not arg[1] or not isinstance(arg[1], list): + arg[1] = [] + for var in vars: + expr1 = -infinity < var + expr2 = var < infinity + arg[1].append(expr1) + arg[1].append(expr2) + else: + params = arg[1] + arg.remove(params) + new_param = [] + for param in params: + lhs = param.lhs().subs(subs_dict) + rhs = param.rhs().subs(subs_dict) + if param.operator() == operator.gt: + expr = lhs >= rhs + else: + expr = lhs <= rhs + new_param.append(expr) + arg.insert(1, new_param) + components.append(arg) + + # Determine the order of each component + self._vars = vars + final_order = [] + for i, component in enumerate(components): + if component not in self._hypersurface: + self._hypersurface.append(component) + final_order.append(temp_order[i]) + self._keys.append(temp_keys[i]) + else: + index = self._hypersurface.index(component) + if temp_order[i] > final_order[index]: + final_order[index] = temp_order[i] + self._keys[index] = temp_keys[i] + for i in range(len(self._hypersurface)): + self._hypersurface[i].append(final_order[i]) + + def dimension(self): + """ + Return the dimension of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x*y + R(-1)*x*z + sage: p1.tropical_variety().dimension() + 4 + """ + return self._poly.parent().ngens() + + def number_of_components(self): + """ + Return the number of components that make up ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x*y*a + x*z + y^2 + a*x + y + z + sage: p1.tropical_variety().number_of_components() + 13 + """ + from sage.rings.integer_ring import ZZ + return ZZ(len(self._hypersurface)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: (w).tropical_variety() + Tropical hypersurface of 0*w + """ + return f"Tropical hypersurface of {self._poly}" + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: tv = (R(1)*w^2 + x*y*z + R(-1)).tropical_variety() + sage: latex(tv) + TV\left(0 x y z + 1 w^{2} + \left(-1\right)\right) + """ + return f"TV\\left({self._poly._latex_()}\\right)" + + def components(self): + """ + Return all components of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: tv = (a+x+y+z).tropical_variety() + sage: tv.components() + [[(t1, t1, t2, t3), [t1 <= min(t3, t2)], 1], + [(t1, t2, t1, t3), [t1 <= t3, t1 <= t2], 1], + [(t1, t2, t3, t1), [t1 <= min(t3, t2)], 1], + [(t1, t2, t2, t3), [t2 <= t3, t2 <= t1], 1], + [(t1, t2, t3, t2), [t2 <= min(t3, t1)], 1], + [(t1, t2, t3, t3), [t3 <= min(t1, t2)], 1]] + """ + return self._hypersurface + + def _components_intersection(self): + r""" + Return the intersection of three or more components of ``self``. + + For a tropical variety in `\RR^n`, the intersection is characterized + by a linear equation in `\RR^{n-1}`. Specifically, this becomes a + vertex for tropical curve and an edges for tropical surface. + + OUTPUT: + + A dictionary where the keys represent component indices and the + values are lists of tuples. Each tuple contains a parametric + equation of points and the corresponding parameter's condition. + + EXAMPLES: + + In two dimension, it will provide vertices that are incident with + each component:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: tv = p1.tropical_variety() + sage: tv._components_intersection() + {0: [((-2, 0), {})], + 1: [((-2, 0), {})], + 2: [((-1, -3), {})], + 3: [((-2, 0), {}), ((-1, 0), {})], + 4: [((-1, -3), {}), ((-1, 0), {})], + 5: [((-1, -3), {})], + 6: [((-1, 0), {}), ((3, 4), {})], + 7: [((3, 4), {})], + 8: [((3, 4), {})]} + + In three dimensions, it will provide all parametric equations of + lines that lie within each component:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + z + x^2 + sage: tv = p1.tropical_variety() + sage: tv._components_intersection() + {0: [((t2, t2, t2), {0 <= t2}), ((0, 0, t2), {0 <= t2})], + 1: [((0, t2, 0), {0 <= t2}), ((t2, t2, t2), {0 <= t2})], + 2: [((0, t1, 0), {0 <= t1}), ((0, 0, t2), {0 <= t2})], + 3: [((t1, t1, t1), {0 <= t1}), ((t1, 2*t1, 2*t1), {t1 <= 0})], + 4: [((1/2*t2, t2, t2), {t2 <= 0}), ((0, 0, t2), {0 <= t2})], + 5: [((0, t2, 0), {0 <= t2}), ((1/2*t2, t2, t2), {t2 <= 0})]} + """ + import operator + from sage.functions.min_max import max_symbolic, min_symbolic + from sage.symbolic.relation import solve + from sage.sets.set import Set + + def update_result(result): + sol_param = solve(new_expr, vars) + sol_param_sim = set() + for sol in sol_param: + if sol == []: + for v in vars: + if v != var: + sol_param_sim.add(v < infinity) + elif isinstance(sol, list): + for eqn in sol: + if eqn.operator() == operator.eq: + if not eqn.rhs().is_numeric(): + eqn_var = eqn.rhs().variables() + param_var = [v for v in eqn_var if v in vars] + if not param_var: + v = eqn.lhs() + if v != var: + sol_param_sim.add(v < infinity) + elif eqn.operator() == operator.lt: + sol_param_sim.add(eqn.lhs() <= eqn.rhs()) + elif eqn.operator() == operator.gt: + sol_param_sim.add(eqn.lhs() >= eqn.rhs()) + else: + sol_param_sim.add(sol) + # Checking there are no conditions with the same variables + # that use the <= and >= operators simultaneously + unique_sol_param = set() + temp = list(sol_param_sim) + op_temp = {i: set(temp[i].operands()) for i in range(len(temp))} + for s_value in op_temp.values(): + match_keys = [k for k, v in op_temp.items() if v == s_value] + if len(match_keys) == 1: + for i in match_keys: + unique_sol_param.add(temp[i]) + + if (unique_sol_param) or (self.dimension() == 2): + if not unique_sol_param: + unique_sol_param = Set() + if index not in result: + result[index] = [(tuple(points), unique_sol_param)] + else: + result[index].append((tuple(points), unique_sol_param)) + + result = {} + vars = self._vars + for index, comp in enumerate(self._hypersurface): + for expr in comp[1]: + left = expr.lhs() + right = expr.rhs() + # If the lhs contains a min or max operator + if (left.operator() == max_symbolic) or (left.operator() == min_symbolic): + for operand in expr.lhs().operands(): + points = list(comp[0]) + new_expr = [e.subs(**{str(right): operand}) for e in comp[1]] + for i, p in enumerate(points): + new_eq = p.subs(**{str(right): operand}) + points[i] = new_eq + update_result(result) + # If the rhs contains a min or max operator + elif (right.operator() == max_symbolic) or (right.operator() == min_symbolic): + for operand in expr.rhs().operands(): + points = list(comp[0]) + new_expr = [e.subs(**{str(left): operand}) for e in comp[1]] + for i, p in enumerate(points): + new_eq = p.subs(**{str(left): operand}) + points[i] = new_eq + update_result(result) + else: + var = expr.variables()[0] + points = list(comp[0]) + subs_expr = solve(left == right, var)[0].rhs() + new_expr = [e.subs(**{str(var): subs_expr}) for e in comp[1]] + for i, p in enumerate(points): + new_eq = p.subs(**{str(var): subs_expr}) + points[i] = new_eq + update_result(result) + return result + + +class TropicalSurface(TropicalVariety): + r""" + A tropical surface in `\RR^3`. + + The tropical surface consists of planar regions and facets, which we + can call cells. These cells are connected in such a way that they form + a piecewise linear structure embedded in three-dimensional space. These + cells meet along edges, where the balancing condition is satisfied. + This balancing condition ensures that the sum of the outgoing normal + vectors at each edge is zero, reflecting the equilibrium. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + z + R(0) + sage: tv = p1.tropical_variety(); tv + Tropical surface of 0*x + 0*y + 0*z + 0 + sage: tv.components() + [[(t1, t1, t2), [t2 <= t1, 0 <= t1], 1], + [(t1, t2, t1), [max(0, t2) <= t1], 1], + [(0, t1, t2), [t2 <= 0, t1 <= 0], 1], + [(t1, t2, t2), [max(0, t1) <= t2], 1], + [(t1, 0, t2), [t2 <= 0, t1 <= 0], 1], + [(t1, t2, 0), [t1 <= 0, t2 <= 0], 1]] + """ + def _axes(self): + r""" + Set the default axes for ``self``. + + This default axes is used for the 3d plot. The axes is centered + around where the intersection of the components occured so it + gives a nice visual representation for the interactions between + different components of the surface. Additionally, it enhances + the visibility and interpretation of how the components align + and interact in three-dimensional space. + + OUTPUT: + + A list of three lists, where the first inner list represent value + of x-axis, the second inner list represent value of y-axis, and + the third inner list represent value of z-axis. If there are + either no components or only one component, the axis will be set + to `[[-1, 1], [-1, 1], [-1, 1]]`. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + sage: p1.tropical_variety()._axes() + [[-1, 1], [-1, 1], [-1, 1]] + sage: p2 = x + y + z + x^2 + R(1) + sage: p2.tropical_variety()._axes() + [[-1, 2], [-1, 2], [-1, 2]] + """ + from sage.symbolic.relation import solve + from sage.arith.srange import srange + + if not self._hypersurface: + return [[-1, 1], [-1, 1], [-1, 1]] + elif len(self._hypersurface) == 1: + bound = 1 + for eqn in self._hypersurface[0][0]: + for op in eqn.operands(): + if op.is_numeric(): + bound = max(op, bound) + return [[-bound, bound]] * 3 + + u_set = set() + v_set = set() + for comp in self._hypersurface: + list_expr = [] + temp_u = set() + temp_v = set() + for expr in comp[1]: + if expr.lhs().is_numeric(): + if bool(expr.rhs() == self._vars[0]): + temp_u.add(expr.lhs()) + else: + temp_v.add(expr.lhs()) + elif expr.rhs().is_numeric(): + if bool(expr.lhs() == self._vars[0]): + temp_u.add(expr.rhs()) + else: + temp_v.add(expr.rhs()) + else: + list_expr.append(expr) + if not temp_u: + temp_u.add(0) + if not temp_v: + temp_v.add(0) + for expr in list_expr: + for u in temp_u: + sol = solve(expr.subs(**{str(self._vars[0]): u}), self._vars[1]) + if not sol: + temp_v.add(0) + elif not sol[0]: + temp_v.add(0) + else: + temp_v.add(sol[0][0].rhs()) + for v in temp_v: + sol = solve(expr.subs(**{str(self._vars[1]): v}), self._vars[0]) + if not sol: + temp_u.add(0) + elif not sol[0]: + temp_u.add(0) + else: + temp_u.add(sol[0][0].rhs()) + u_set = u_set.union(temp_u) + v_set = v_set.union(temp_v) + axes = [[min(u_set)-1, max(u_set)+1], [min(v_set)-1, max(v_set)+1]] + + # Finding the z-axis + step = 10 + du = (axes[0][1]-axes[0][0]) / step + dv = (axes[1][1]-axes[1][0]) / step + u_range = srange(axes[0][0], axes[0][1]+du, du) + v_range = srange(axes[1][0], axes[1][1]+dv, dv) + zmin, zmax = None, None + for comp in self._hypersurface: + for u in u_range: + for v in v_range: + checkpoint = True + for exp in comp[1]: + final_exp = exp.subs(**{str(self._vars[0]): u, str(self._vars[1]): v}) + if not final_exp: + checkpoint = False + break + if checkpoint: + z = comp[0][2].subs(**{str(self._vars[0]): u, str(self._vars[1]): v}) + if (zmin is None) and (zmax is None): + zmin = z + zmax = z + else: + zmin = min(z, zmin) + zmax = max(z, zmax) + axes.append([zmin, zmax]) + return axes + + def _polygon_vertices(self): + r""" + Return the vertices of the polygon for each components of ``self`` + to be used for plotting. + + OUTPUT: + + A dictionary where the keys represent component indices and the + values are a set of points in three dimensional space. + + EXAMPLES: + + A tropical surface with only one component:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + z + sage: tv1 = p1.tropical_variety() + sage: tv1._polygon_vertices() + {0: {(-1, -1, -1), (-1, 1, -1), (1, -1, 1), (1, 1, 1)}} + + A tropical surface with multiple components:: + + sage: p2 = x^2 + x + y + z + R(1) + sage: tv2 = p2.tropical_variety() + sage: tv2._polygon_vertices() + {0: {(0, 0, 0), (0, 0, 2), (1, 1, 1), (2, 2, 2)}, + 1: {(0, 0, 0), (0, 2, 0), (1, 1, 1), (2, 2, 2)}, + 2: {(0, 0, 0), (0, 0, 2), (0, 2, 0), (0, 2, 2)}, + 3: {(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2)}, + 4: {(-1/2, -1, -1), (0, 0, 0), (1, 1, 1), (2, -1, -1), (2, 1, 1)}, + 5: {(-1/2, -1, -1), (-1/2, -1, 2), (0, 0, 0), (0, 0, 2)}, + 6: {(1, 1, 1), (1, 1, 2), (2, 1, 1), (2, 1, 2)}, + 7: {(-1/2, -1, -1), (-1/2, 2, -1), (0, 0, 0), (0, 2, 0)}, + 8: {(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1)}} + """ + from sage.sets.real_set import RealSet + from sage.symbolic.relation import solve + from sage.rings.rational_field import QQ + + poly_verts = {i: set() for i in range(self.number_of_components())} + axes = self._axes() + comps = self.components() + vars = self._vars + comps_int = self._components_intersection() + + # Finding the inside vertices + for index, lines in comps_int.items(): + for line in lines: + v = list(line[1])[0].variables()[0] + for param in line[1]: + left = param.lhs() + right = param.rhs() + if left.is_numeric(): + vertex = [QQ(e.subs(**{str(v): left})) for e in line[0]] + poly_verts[index].add(tuple(vertex)) + elif right.is_numeric(): + vertex = [QQ(e.subs(**{str(v): right})) for e in line[0]] + poly_verts[index].add(tuple(vertex)) + + def find_edge_vertices(i): + j = (i+1) % 2 + if i == 0: # interval for t1 + interval = interval1 + else: # interval for t2 + interval = interval2 + for p in [interval.inf(), interval.sup()]: + new_param = [e.subs(**{str(vars[i]): p}) for e in comps[index][1]] + sol = solve(new_param, vars[j]) + if sol: + interval_param = RealSet() + for s in sol: + if s != []: + # Handle cases where 's' is not a list (s = r1 < +Infinity), + # or if 's' is a list, ensure that its elements define a real + # interval (catch invalid cases like s = [t1 == r100]). + try: + interval_param += RealSet(s[0]) + except (IndexError, ValueError): + interval_param += RealSet(-infinity, infinity) + else: + interval_param += RealSet(-infinity, infinity) + interval_param = interval_param.intersection(interval2) + if is_doublevar: + int1 = RealSet() + for s1 in sol1: + subs1 = solve(s1[0].subs(**{str(vars[i]): p}), vars[j]) + try: + int1 += RealSet(subs1[0]) + except TypeError: + int1 += RealSet(subs1[0][0]) + int2 = RealSet() + for s2 in sol2: + subs2 = solve(s2[0].subs(**{str(vars[i]): p}), vars[j]) + try: + int2 += RealSet(subs2[0]) + except TypeError: + int2 += RealSet(subs2[0][0]) + final_int = int1.intersection(int2) + interval_param = interval_param.intersection(final_int) + if interval_param: + vertex1 = [QQ(e.subs(**{str(vars[i]): p, str(vars[j]): interval_param.inf()})) for e in comps[index][0]] + vertex2 = [QQ(e.subs(**{str(vars[i]): p, str(vars[j]): interval_param.sup()})) for e in comps[index][0]] + poly_verts[index].add(tuple(vertex1)) + poly_verts[index].add(tuple(vertex2)) + + # Find the interval of parameter for outer vertex + for index in range(len(comps)): + interval1 = RealSet(-infinity, infinity) # represent t1 + interval2 = RealSet(-infinity, infinity) # represent t2 + is_doublevar = False + for i, point in enumerate(comps[index][0]): + pv = point.variables() + if len(pv) == 1: + temp1 = RealSet(solve(point >= axes[i][0], pv[0])[0][0]) + temp2 = RealSet(solve(point <= axes[i][1], pv[0])[0][0]) + temp = temp1.intersection(temp2) + if pv[0] == vars[0]: + interval1 = interval1.intersection(temp) + else: + interval2 = interval2.intersection(temp) + elif len(pv) == 2: + sol1 = solve(point >= axes[i][0], pv) + sol2 = solve(point <= axes[i][1], pv) + is_doublevar = True + # Finding the edge vertices (those that touch the axes) + find_edge_vertices(0) # t1 fixed + find_edge_vertices(1) # t2 fixed + return poly_verts + + def plot(self, color='random'): + """ + Return the plot of ``self`` by constructing a polyhedron from + vertices in ``self.polygon_vertices()``. + + INPUT: + + - ``color`` -- string or tuple that represent a color (default: + ``random``); ``random`` means each polygon will be assigned + a different color. If instead a specific ``color`` is provided, + then all polygon will be given the same color. + + OUTPUT: Graphics3d Object + + EXAMPLES: + + A tropical surface that consist of only one cell:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + z + sage: tv = p1.tropical_variety() + sage: tv.plot() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y,z')) + x, y, z = R.gen(), R.gen(1), R.gen(2) + p1 = x + z + sphinx_plot(p1.tropical_variety().plot()) + + A tropical surface with multiple cell that exhibit complex and + intriguing geometric structures:: + + sage: p2 = x^2 + x + y + z + R(1) + sage: tv = p2.tropical_variety() + sage: tv.plot() + Graphics3d Object + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y,z')) + x, y, z = R.gen(), R.gen(1), R.gen(2) + p2 = x**2 + x + y + z + R(1) + sphinx_plot(p2.tropical_variety().plot()) + """ + from random import random + from sage.plot.graphics import Graphics + from sage.geometry.polyhedron.constructor import Polyhedron + + if color == 'random': + colors = [] + for _ in range(self.number_of_components()): + color = (random(), random(), random()) + colors.append(color) + elif isinstance(color, str): + colors = [color] * self.number_of_components() + else: + colors = color + + combined_plot = Graphics() + for i, vertex in self._polygon_vertices().items(): + points = list(vertex) + plot = Polyhedron(vertices=points).plot(color=colors[i]) + combined_plot += plot + return combined_plot + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: (x^4+z^2).tropical_variety() + Tropical surface of 0*x^4 + 0*z^2 + """ + return f"Tropical surface of {self._poly}" + + +class TropicalCurve(TropicalVariety): + r""" + A tropical curve in `\RR^2`. + + The tropical curve consists of line segments and half-lines, which we + call edges. These edges are connected in such a way that they form a + piecewise linear graph embedded in the plane. These edges meet at + a vertices, where the balancing condition is satisfied. This balancing + condition ensures that the sum of the outgoing slopes at each vertex + is zero, reflecting the equilibrium. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + R(0) + sage: tv = p1.tropical_variety(); tv + Tropical curve of 0*x + 0*y + 0 + sage: tv.components() + [[(t1, t1), [t1 >= 0], 1], [(0, t1), [t1 <= 0], 1], [(t1, 0), [t1 <= 0], 1]] + """ + def _axes(self): + """ + Set the default axes for ``self``. + + This default axes is used for plot of tropical curve and also the + 3d plot of tropical polynomial function. The axes is chosen by first + find all vertices of this tropical curve. Then we choose the minimum + and maximum of all x-component in this vertices to be the x-axis. + The same apply to the y-axis. + + OUTPUT: + + A list of two lists, where the first inner list represent value of + x-axis and the second inner list represent value of y-axis. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + sage: p1.tropical_variety()._axes() + [[-1, 1], [-1, 1]] + sage: p2 = R(12)*x*y + R(-2)*y^2 + R(16)*y + R(25) + sage: p2.tropical_variety()._axes() + [[-3/2, 1/2], [25/2, 29/2]] + """ + if self.number_of_components() == 0: + return [[-1, 1], [-1, 1]] + if self.number_of_components() <= 2: + bound = 1 + for comps in self._hypersurface: + eqns = comps[0] + temp_operands = [] + for eq in eqns: + if not eq.operator(): + temp_operands.append(eq) + else: + temp_operands += eq.operands() + for op in temp_operands: + if op.is_numeric(): + bound = max(abs(op), bound) + return [[-bound, bound]] * 2 + + verts = self.vertices() + xmin = xmax = list(verts)[0][0] + for vertex in verts: + if vertex[0] < xmin: + xmin = vertex[0] + elif vertex[0] > xmax: + xmax = vertex[0] + ymin = ymax = list(verts)[0][1] + for vertex in verts: + if vertex[1] < ymin: + ymin = vertex[1] + elif vertex[1] > ymax: + ymax = vertex[1] + return [[xmin-1, xmax+1], [ymin-1, ymax+1]] + + def vertices(self): + r""" + Return all vertices of ``self``, which is the point where three or + more edges intersect. + + OUTPUT: A set of `(x,y)` points + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + sage: p1.tropical_variety().vertices() + set() + sage: p2 = R(-2)*x^2 + R(-1)*x + R(1/2)*y + R(1/6) + sage: p2.tropical_variety().vertices() + {(1, -1/2), (7/6, -1/3)} + """ + if len(self._hypersurface) < 3: + return set() + + vertices = set() + for i, component in enumerate(self._hypersurface): + parametric_function = component[0] + var = component[1][0].variables()[0] + interval = self._parameter_intervals()[i] + lower = interval[0].lower() + upper = interval[0].upper() + if lower != -infinity: + x = parametric_function[0].subs(**{str(var): lower}) + y = parametric_function[1].subs(**{str(var): lower}) + vertices.add((x, y)) + if upper != infinity: + x = parametric_function[0].subs(**{str(var): upper}) + y = parametric_function[1].subs(**{str(var): upper}) + vertices.add((x, y)) + return vertices + + def _parameter_intervals(self): + r""" + Return the intervals of each component's parameter of ``self``. + + OUTPUT: A list of ``RealSet`` + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = y + y^2 + sage: p1.tropical_variety()._parameter_intervals() + [(-oo, +oo)] + sage: p2 = x^2 + R(-1)*x*y + R(-1)*x + R(1/3) + sage: p2.tropical_variety()._parameter_intervals() + [(-oo, 0], [0, +oo), [-1, 4/3], (-oo, 0], [0, +oo)] + """ + from sage.sets.real_set import RealSet + + intervals = [] + R = self._poly.parent().base().base_ring() + for component in self._hypersurface: + if len(component[1]) == 1: + interval = RealSet(component[1][0]) + else: + lower = component[1][0].left() + upper = component[1][1].right() + if lower == -infinity: + interval = RealSet(-infinity, infinity) + else: + interval = RealSet([R(lower), R(upper)]) + intervals.append(interval) + return intervals + + def plot(self): + """ + Return the plot of ``self``. + + Generates a visual representation of the tropical curve in cartesian + coordinates. The plot shows piecewise-linear segments representing + each components. The axes are centered around the vertices. + + OUTPUT: + + A Graphics object. The weight of the component will be written if it + is greater or equal than 2. The weight is written near the vertex. + + EXAMPLES: + + A polynomial with only two terms will give one straight line:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: (y+R(1)).tropical_variety().components() + [[(t1, 1), [-Infinity < t1, t1 < +Infinity], 1]] + sage: (y+R(1)).tropical_variety().plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + sphinx_plot((y+R(1)).tropical_variety().plot()) + + An intriguing and fascinating tropical curve can be obtained with + a more complex tropical polynomial:: + + sage: p1 = R(1) + R(2)*x + R(3)*y + R(6)*x*y + R(10)*x*y^2 + sage: p1.tropical_variety().components() + [[(-1, t1), [-2 <= t1], 1], + [(t1, -2), [-1 <= t1], 1], + [(t1 + 1, t1), [-4 <= t1, t1 <= -2], 1], + [(t1, -4), [t1 <= -3], 2], + [(-t1 - 7, t1), [t1 <= -4], 1]] + sage: p1.tropical_variety().plot() + Graphics object consisting of 6 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(1) + R(2)*x + R(3)*y + R(6)*x*y + R(10)*x*y**2 + sphinx_plot(p1.tropical_variety().plot()) + + Another tropical polynomial with numerous components, resulting + in a more intricate structure:: + + sage: p2 = (x^6 + R(4)*x^4*y^2 + R(2)*x^3*y^3 + R(3)*x^2*y^4 + x*y^5 + ....: + R(7)*x^2 + R(5)*x*y + R(3)*y^2 + R(2)*x + y + R(10)) + sage: p2.tropical_variety().plot() + Graphics object consisting of 11 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p2 = x**6 + R(4)*x**4*y**2 + R(2)*x**3*y**3 + R(3)*x**2*y**4 + \ + x*y**5 + R(7)*x**2 + R(5)*x*y + R(3)*y**2 + R(2)*x + y + R(10) + sphinx_plot(p2.tropical_variety().plot()) + """ + from sage.plot.plot import plot + from sage.plot.text import text + from sage.plot.graphics import Graphics + from sage.plot.plot import parametric_plot + + if not self._hypersurface: + return plot(lambda x: float('nan'), {-1, 1}) + + combined_plot = Graphics() + large_int = 100 + intervals = self._parameter_intervals() + for i, component in enumerate(self._hypersurface): + var = component[1][0].variables()[0] + parametric_function = component[0] + order = component[2] + interval = intervals[i] + if interval[0].lower() == -infinity: + lower = interval[0].upper() - large_int + upper = interval[0].upper() + midpoint = upper - 0.5 + elif interval[0].upper() == infinity: + lower = interval[0].lower() + upper = interval[0].lower() + large_int + midpoint = lower + 0.5 + else: + lower = interval[0].lower() + upper = interval[0].upper() + midpoint = (lower+upper) / 2 + + if (lower == infinity) and (upper == infinity): + midpoint = 0 + plot = parametric_plot(parametric_function, (var, -large_int, + large_int), color='red') + else: + plot = parametric_plot(parametric_function, (var, lower, upper), + color='red') + + # Add the order if it is greater than or equal to 2 + if component[2] > 1: + point = [] + for eq in component[0]: + value = eq.subs(**{str(var): midpoint}) + point.append(value) + text_order = text(str(order), (point[0], point[1]), + fontsize=16, color='black') + combined_plot += plot + text_order + else: + combined_plot += plot + + # Set default axes + axes = self._axes() + xmin, xmax = axes[0][0], axes[0][1] + ymin, ymax = axes[1][0], axes[1][1] + combined_plot.set_axes_range(xmin=xmin, xmax=xmax, + ymin=ymin, ymax=ymax) + return combined_plot + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: (x^2+R(0)).tropical_variety() + Tropical curve of 0*x^2 + 0 + """ + return f"Tropical curve of {self._poly}" diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 1c435a2ccec..022b5d1c880 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -1074,7 +1074,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): else: try: poly = parent._polynomial_ring(x) - self._poly = PolyDict(poly.dict(), None) + self._poly = PolyDict(poly.monomial_coefficients(), None) except TypeError: # last chance: we first try to convert to the rational Tate series if parent._integral: @@ -2192,7 +2192,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): """ return [ t.monomial() for t in self.terms() ] - def dict(self): + def monomial_coefficients(self): """ Return a dictionary whose keys are the exponents and whose values are the corresponding coefficients of this series. @@ -2202,12 +2202,19 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): sage: R = Zp(2, prec=10, print_mode='digits') sage: A. = TateAlgebra(R) sage: f = 2*x^2 + x + sage: f.monomial_coefficients() + {(1, 0): ...0000000001, (2, 0): ...00000000010} + + ``dict`` is an alias:: + sage: f.dict() {(1, 0): ...0000000001, (2, 0): ...00000000010} """ self._normalize() return dict(self._poly.__repn) + dict = monomial_coefficients + def coefficient(self, exponent): r""" Return the coefficient corresponding to the given exponent. @@ -2364,7 +2371,9 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): return elt.lift_to_precision(prec) except PrecisionError: return elt.lift_to_precision() - ans._poly = PolyDict({ e: lift_without_error(c) for (e,c) in self._poly.__repn.iteritems() }, None) + ans._poly = PolyDict({e: lift_without_error(c) + for e, c in self._poly.__repn.items()}, + None) if prec is None: prec = self._parent.precision_cap() ans._prec = max(self._prec, prec) diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 35c6b90a76b..8ad6ee44868 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -1656,7 +1656,7 @@ def _factor_univariate_polynomial(self, f): if f.degree() == 1: return Factorization([(f, 1)], unit) - # From now on, we restrict to polynomial with rational cofficients. The + # From now on, we restrict to polynomial with rational coefficients. The # factorization is provided only in the case it is a product of # cyclotomic polynomials and quadratic polynomials. In this situation # the roots belong to UCF and the polynomial factorizes as a product of diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index ff35d1166e9..b9f0229f787 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1390,7 +1390,7 @@ def lift(self, F, report_coefficients=False): F = self.residue_ring().coerce(F) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if F.is_constant(): @@ -1481,7 +1481,7 @@ def lift_to_key(self, F, check=True): F = self.residue_ring().coerce(F) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if check: diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index 9646cf5e429..470e4990bed 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -1375,7 +1375,7 @@ def minimal_representative(self, f): f = self.domain().coerce(f) from sage.categories.fields import Fields - if not self.domain().base_ring() in Fields(): + if self.domain().base_ring() not in Fields(): raise NotImplementedError("only implemented for polynomial rings over fields") if f.is_zero(): diff --git a/src/sage/rings/valuation/valuation.py b/src/sage/rings/valuation/valuation.py index bbc5bdf1db0..2deeb100ffc 100644 --- a/src/sage/rings/valuation/valuation.py +++ b/src/sage/rings/valuation/valuation.py @@ -1043,7 +1043,7 @@ def _ge_(self, other): return super()._ge_(other) -class MacLaneApproximantNode(): +class MacLaneApproximantNode: r""" A node in the tree computed by :meth:`DiscreteValuation.mac_lane_approximants`. diff --git a/src/sage/sat/converters/anf2cnf.py b/src/sage/sat/converters/anf2cnf.py index e6dd330feff..2316551fa2d 100644 --- a/src/sage/sat/converters/anf2cnf.py +++ b/src/sage/sat/converters/anf2cnf.py @@ -8,5 +8,6 @@ - Martin Albrecht (2012): first version """ -class ANF2CNFConverter(): + +class ANF2CNFConverter: pass diff --git a/src/sage/sat/converters/polybori.py b/src/sage/sat/converters/polybori.py index d57f1c12c02..27437c472ec 100644 --- a/src/sage/sat/converters/polybori.py +++ b/src/sage/sat/converters/polybori.py @@ -237,7 +237,7 @@ def choose(s): while not rest.empty(): l = choose(rest) l_variables = set(l.variables()) - block_dict = dict([(v, 1 if v in l_variables else 0) for v in variables]) + block_dict = {v: (1 if v in l_variables else 0) for v in variables} l = l.set() self.random_generator.shuffle(variables) for v in variables: diff --git a/src/sage/sat/meson.build b/src/sage/sat/meson.build new file mode 100644 index 00000000000..a1a0246dfce --- /dev/null +++ b/src/sage/sat/meson.build @@ -0,0 +1,4 @@ +py.install_sources('all.py', 'boolean_polynomials.py', subdir: 'sage/sat') + +install_subdir('converters', install_dir: sage_install_dir / 'sat') +subdir('solvers') diff --git a/src/sage/sat/solvers/dimacs.py b/src/sage/sat/solvers/dimacs.py index 424442930de..61d5460d432 100644 --- a/src/sage/sat/solvers/dimacs.py +++ b/src/sage/sat/solvers/dimacs.py @@ -253,7 +253,7 @@ def write(self, filename=None): tail.close() head = open(headname,"a") - tail = open(self._tail.name,"r") + tail = open(self._tail.name) head.write(tail.read()) tail.close() head.close() @@ -301,7 +301,7 @@ def clauses(self, filename=None): else: tail = self._tail tail.close() - tail = open(self._tail.name,"r") + tail = open(self._tail.name) clauses = [] for line in tail.readlines(): @@ -542,6 +542,7 @@ def __call__(self, assumptions=None): else: raise ValueError("When parsing the output(={}), no line starts with letter v or s".format(self._output)) + class RSat(DIMACS): """ An instance of the RSat solver. @@ -642,6 +643,7 @@ class Glucose(DIMACS): """ command = "glucose -verb=0 -model {input}" + class GlucoseSyrup(DIMACS): """ An instance of the Glucose-syrup parallel solver. @@ -705,6 +707,7 @@ class GlucoseSyrup(DIMACS): """ command = "glucose-syrup -model -verb=0 {input}" + class Kissat(DIMACS): """ An instance of the Kissat SAT solver. diff --git a/src/sage/sat/solvers/meson.build b/src/sage/sat/solvers/meson.build new file mode 100644 index 00000000000..86657c5c854 --- /dev/null +++ b/src/sage/sat/solvers/meson.build @@ -0,0 +1,23 @@ +py.install_sources( + '__init__.py', + 'cryptominisat.py', + 'dimacs.py', + 'picosat.py', + 'sat_lp.py', + 'satsolver.pxd', + subdir: 'sage/sat/solvers', +) + +extension_data = {'satsolver' : files('satsolver.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/sat/solvers', + install: true, + include_directories: [], + dependencies: [py_dep, gmp], + ) +endforeach + diff --git a/src/sage/sat/solvers/sat_lp.py b/src/sage/sat/solvers/sat_lp.py index 76446e1f0f8..5a027f6ae9b 100644 --- a/src/sage/sat/solvers/sat_lp.py +++ b/src/sage/sat/solvers/sat_lp.py @@ -10,6 +10,7 @@ from .satsolver import SatSolver from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException + class SatLP(SatSolver): def __init__(self, solver=None, verbose=0, *, integrality_tolerance=1e-3): r""" diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index a4bd1dd51d5..0eba4c662ec 100755 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -1018,8 +1018,7 @@ def degree(self): poly_numerator = poly.numerator() poly_denominator = poly.denominator() degree = max(poly_numerator.degree(), poly_denominator.degree()) - if degree > max_degree: - max_degree = degree + max_degree = max(degree, max_degree) # polynomial affine map elif poly.degree() > max_degree: max_degree = poly.degree() diff --git a/src/sage/schemes/affine/affine_space.py b/src/sage/schemes/affine/affine_space.py index b21ac4a7de6..42aeae0e2f9 100755 --- a/src/sage/schemes/affine/affine_space.py +++ b/src/sage/schemes/affine/affine_space.py @@ -109,7 +109,7 @@ def AffineSpace(n, R=None, names=None, ambient_projective_space=None, ... NameError: variable names passed to AffineSpace conflict with names in ring """ - if (isinstance(n, MPolynomialRing_base) or isinstance(n, PolynomialRing_general)) and R is None: + if isinstance(n, (MPolynomialRing_base, PolynomialRing_general)) and R is None: R = n if names is not None: # Check for the case that the user provided a variable name diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index cf232a02a71..8e9ee08d2f3 100755 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -92,7 +92,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= # if radius is a list or a tuple, this is a type 4 point if isinstance(radius, (list, tuple)): if error_check: - if not (isinstance(center, list) or isinstance(center, tuple)): + if not isinstance(center, (list, tuple)): raise TypeError("center was passed a list but radius was not a list") if len(radius) != len(center): raise ValueError("the same number of centers and radii " diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d98209bdba1..0b647c8411b 100755 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -579,7 +579,7 @@ def multiplicity(self, P): TypeError: (=(1, 1)) is not a point on (=Affine Plane Curve over Rational Field defined by x^6 - x^3 + y^3) """ - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("curve must be defined over a field") # Check whether P is a point on this curve @@ -866,7 +866,7 @@ def __init__(self, A, X): """ super().__init__(A, X) - if not A.base_ring() in Fields(): + if A.base_ring() not in Fields(): raise TypeError("curve not defined over a field") d = super(Curve_generic, self).dimension() @@ -1320,7 +1320,7 @@ def blowup(self, P=None): self(P) except TypeError: raise TypeError("(=%s) must be a point on this curve" % P) - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("the base ring of this curve must be a field") if not self.is_irreducible(): raise TypeError("this curve must be irreducible") diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index b4c36b1329a..63e8d03f556 100755 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1158,7 +1158,7 @@ def _an_element_(self): @cached_method def ngens(self): """ - Return the number of variables, i.e. 2 or 3, kept for completness. + Return the number of variables, i.e. 2 or 3, kept for completeness. OUTPUT: integer, 2 or 3, depending if the arrangement is projective or affine diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 8eff3715338..0729c5a98ad 100755 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1103,10 +1103,9 @@ def quadratic_transform(self): degs = [G.degree()]*len(L) for F in G.monomials(): for i in range(len(L)): - if F.degree(L[i]) < degs[i]: - degs[i] = F.degree(L[i]) + degs[i] = min(F.degree(L[i]), degs[i]) T = [] - for item in G.dict().items(): + for item in G.monomial_coefficients().items(): tup = tuple([item[0][i] - degs[i] for i in range(len(L))]) T.append((tup, item[1])) G = R(dict(T)) @@ -1281,11 +1280,11 @@ def excellent_position(self, Q): if j == 0: div_pow = min(e[1] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0, v1 - div_pow, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.monomial_coefficients().items()}) else: div_pow = min(e[0] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0 - div_pow, v1, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.monomial_coefficients().items()}) # check the degree again if npoly.degree() != d - r: need_continue = True @@ -1467,7 +1466,7 @@ def extension(self): # make sure the defining polynomial variable names are the same for K, N N = NumberField(K.defining_polynomial().parent()(F.defining_polynomial()), str(K.gen())) return N.composite_fields(K, both_maps=True)[0][1]*F.embeddings(N)[0] - if not self.base_ring() in NumberFields(): + if self.base_ring() not in NumberFields(): raise NotImplementedError("the base ring of this curve must be a number field") if not self.is_irreducible(): raise TypeError("this curve must be irreducible") @@ -1500,7 +1499,7 @@ def extension(self): try: temp_pt = (temp_qua*temp_exc)(temp_exc.domain()(pts[i])) pts.pop(i) - if not PP(list(temp_pt)) in [PP(list(tpt)) for tpt in pts]: + if PP(list(temp_pt)) not in [PP(list(tpt)) for tpt in pts]: pts.append(temp_pt) except (TypeError, ValueError): pass @@ -1520,7 +1519,7 @@ def extension(self): newpts = [PP(list(pt) + [0]) for pt in X.rational_points()] # avoid duplicates for pt in newpts: - if not PP(list(pt)) in [PP(list(tpt)) for tpt in pts]: + if PP(list(pt)) not in [PP(list(tpt)) for tpt in pts]: pts.append(pt) return phi @@ -1605,7 +1604,7 @@ def __init__(self, A, X, category=None): """ super().__init__(A, X, category=category) - if not A.base_ring() in Fields(): + if A.base_ring() not in Fields(): raise TypeError("curve not defined over a field") d = super(Curve_generic, self).dimension() @@ -2297,7 +2296,7 @@ def __init__(self, A, f): ideal = self.defining_ideal() gs = self.ambient_space().gens() for i in range(self.ngens()): - if not gs[i] in ideal: + if gs[i] not in ideal: self._open_affine = self.affine_patch(i) self._open_affine_index = i break @@ -2522,7 +2521,6 @@ def _map_from_function_field(self): sage: C.function(C._map_from_function_field(f)) == f True """ - F = self._function_field S = self.ambient_space().coordinate_ring() phi = self._open_affine._nonsingular_model[2] i = self._open_affine_index @@ -2734,7 +2732,7 @@ def places_on(self, point): # determine the affine patch where the point lies S = prime.ring() for i in range(S.ngens()): - if not S.gen(i) in prime: + if S.gen(i) not in prime: break phi = self._map_to_function_field diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index b2b9d7cd5b2..47485b86340 100755 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -51,7 +51,6 @@ from sage.geometry.voronoi_diagram import VoronoiDiagram from sage.graphs.graph import Graph from sage.groups.braid import BraidGroup -from sage.groups.finitely_presented import wrap_FpGroup from sage.groups.free_group import FreeGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.matrix.constructor import matrix @@ -733,7 +732,7 @@ def roots_interval(f, x0): diam = min((CF(r) - CF(r0)).abs() for r0 in roots[:i] + roots[i + 1:]) / divisor envelop = IF(diam) * IF((-1, 1), (-1, 1)) - while not newton(fx, r, r + envelop) in r + envelop: + while newton(fx, r, r + envelop) not in r + envelop: prec += 53 IF = ComplexIntervalField(prec) CF = ComplexField(prec) @@ -1136,7 +1135,7 @@ def vertical_lines_in_braidmon(pols) -> list: 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 + no other component with vertical asymptote; otherwise it returns an empty list. EXAMPLES:: @@ -1303,7 +1302,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): g = f.parent()(prod(glist)) d = g.degree(y) if not arrangement_v: # change of coordinates only if indices_v is empty - while not g.coefficient(y**d) in F: + while g.coefficient(y**d) not in F: g = g.subs({x: x + y}) d = g.degree(y) arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h) @@ -1537,7 +1536,7 @@ def braid2rels(L): P.SetTzOptions(dic) P.TzGoGo() P.TzGoGo() - gb = wrap_FpGroup(P.FpGroupPresentation()) + gb = P.FpGroupPresentation().sage() U = [rel.Tietze() for rel in gb.relations()] return U @@ -1753,7 +1752,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=True): x, y = g.parent().gens() F = g.parent().base_ring() d = g.degree(y) - while not g.coefficient(y**d) in F: + while g.coefficient(y**d) not in F: g = g.subs({x: x + y}) d = g.degree(y) if projective: diff --git a/src/sage/schemes/elliptic_curves/BSD.py b/src/sage/schemes/elliptic_curves/BSD.py index 73f1f57d41c..7b64cd63883 100755 --- a/src/sage/schemes/elliptic_curves/BSD.py +++ b/src/sage/schemes/elliptic_curves/BSD.py @@ -95,6 +95,7 @@ def simon_two_descent_work(E, two_tor_rk): ... DeprecationWarning: Use E.rank(algorithm="pari") instead, as this script has been ported over to pari. See https://github.com/sagemath/sage/issues/35621 for details. + ... (0, 0, 0, 0, []) sage: E = EllipticCurve('37a') sage: simon_two_descent_work(E, E.two_torsion_rank()) @@ -148,6 +149,7 @@ def mwrank_two_descent_work(E, two_tor_rk): sha2_upper_bd = MWRC.selmer_rank() - two_tor_rk - rank_lower_bd return rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens + def pari_two_descent_work(E): r""" Prepare the output from pari by two-isogeny. @@ -872,8 +874,7 @@ def prove_BSD(E, verbosity=0, two_desc='mwrank', proof=None, secs_hi=5, # Try harder to compute the Heegner index, where it matters if heegner_index is None: - if max_height < 18: - max_height = 18 + max_height = max(max_height, 18) for D in BSD.heegner_index_upper_bound: M = BSD.heegner_index_upper_bound[D] for p in kolyvagin_primes: diff --git a/src/sage/schemes/elliptic_curves/all.py b/src/sage/schemes/elliptic_curves/all.py index 84f7b0d5a50..df527315527 100644 --- a/src/sage/schemes/elliptic_curves/all.py +++ b/src/sage/schemes/elliptic_curves/all.py @@ -27,10 +27,10 @@ lazy_import('sage.schemes.elliptic_curves.jacobian', 'Jacobian') lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'special_supersingular_curve') - lazy_import('sage.schemes.elliptic_curves.ell_rational_field', ['cremona_curves', 'cremona_optimal_curves']) +from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_with_prime_order from sage.schemes.elliptic_curves.cm import (cm_orders, cm_j_invariants, cm_j_invariants_and_orders, diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index faf4f75f2ce..6760719e789 100755 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -287,6 +287,7 @@ def is_HCP(f, check_monic_irreducible=True): return zero return D if f == hilbert_class_polynomial(D) else zero + def OrderClassNumber(D0,h0,f): r""" Return the class number h(f**2 * D0), given h(D0)=h0. @@ -652,6 +653,7 @@ class number `h`, and the number of fundamental negative discriminants with except KeyError: raise NotImplementedError("largest fundamental discriminant not available for class number %s" % h) + def largest_disc_with_class_number(h): r""" Return largest absolute value of any negative discriminant with @@ -723,6 +725,7 @@ class number `h` is also the largest discriminant, but this is not [(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1), (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]]} + def discriminants_with_bounded_class_number(hmax, B=None, proof=None): r"""Return a dictionary with keys class numbers `h\le hmax` and values the list of all pairs `(D_0, f)`, with `D_0<0` a fundamental discriminant such diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index ebbc55e6799..831ee47e280 100755 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -438,6 +438,9 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, # Interpret x as a Cremona or LMFDB label. from sage.databases.cremona import CremonaDatabase x, data = CremonaDatabase().coefficients_and_data(x) + # data is only valid for elliptic curves over QQ. + if R not in (None, QQ): + data = {} # User-provided keywords may override database entries. data.update(kwds) kwds = data @@ -457,7 +460,7 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, return (R, tuple(R(a) for a in x)), kwds - def create_object(self, version, key, **kwds): + def create_object(self, version, key, *, names=None, **kwds): r""" Create an object from a ``UniqueFactory`` key. @@ -467,18 +470,46 @@ def create_object(self, version, key, **kwds): sage: type(E) + ``names`` is ignored at the moment, however it is used to support a convenient way to get a generator:: + + sage: E.

= EllipticCurve(QQ, [1, 3]) + sage: P + (-1 : 1 : 1) + sage: E.

= EllipticCurve(GF(5), [1, 3]) + sage: P + (4 : 1 : 1) + .. NOTE:: Keyword arguments are currently only passed to the constructor for elliptic curves over `\QQ`; elliptic curves over other fields do not support them. + + TESTS:: + + sage: E = EllipticCurve.create_object(0, (QQ, (1, 2, 0, 1, 2)), rank=2) + sage: E = EllipticCurve.create_object(0, (GF(3), (1, 2, 0, 1, 2)), rank=2) + Traceback (most recent call last): + ... + TypeError: unexpected keyword arguments: {'rank': 2} + + Coverage tests:: + + sage: E = EllipticCurve(QQ, [2, 5], modular_degree=944, regulator=1) + sage: E.modular_degree() + 944 + sage: E.regulator() + 1.00000000000000 """ R, x = key if R is QQ: from .ell_rational_field import EllipticCurve_rational_field return EllipticCurve_rational_field(x, **kwds) - elif isinstance(R, NumberField): + elif kwds: + raise TypeError(f"unexpected keyword arguments: {kwds}") + + if isinstance(R, NumberField): from .ell_number_field import EllipticCurve_number_field return EllipticCurve_number_field(R, x) elif isinstance(R, sage.rings.abc.pAdicField): @@ -536,6 +567,7 @@ def EllipticCurve_from_Weierstrass_polynomial(f): """ return EllipticCurve(coefficients_from_Weierstrass_polynomial(f)) + def coefficients_from_Weierstrass_polynomial(f): r""" Return the coefficients `[a_1, a_2, a_3, a_4, a_6]` of a cubic in @@ -1277,6 +1309,7 @@ def tangent_at_smooth_point(C,P): except NotImplementedError: return C.tangents(P,factor=False)[0] + def chord_and_tangent(F, P): r"""Return the third point of intersection of a cubic with the tangent at one point. diff --git a/src/sage/schemes/elliptic_curves/ec_database.py b/src/sage/schemes/elliptic_curves/ec_database.py index 1cc03fdb8b3..7d418feb437 100755 --- a/src/sage/schemes/elliptic_curves/ec_database.py +++ b/src/sage/schemes/elliptic_curves/ec_database.py @@ -72,6 +72,7 @@ from .constructor import EllipticCurve + class EllipticCurves: def rank(self, rank, tors=0, n=10, labels=False): r""" diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index c095eb1ee89..ba752e11c10 100755 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -103,6 +103,8 @@ # Private function for parsing input to determine the type of # algorithm # + + def _isogeny_determine_algorithm(E, kernel): r""" Helper function to infer the algorithm to be used from the @@ -171,6 +173,7 @@ def _isogeny_determine_algorithm(E, kernel): raise ValueError("invalid parameters to EllipticCurveIsogeny constructor") + def isogeny_codomain_from_kernel(E, kernel): r""" Compute the isogeny codomain given a kernel. @@ -223,6 +226,7 @@ def isogeny_codomain_from_kernel(E, kernel): raise NotImplementedError + def compute_codomain_formula(E, v, w): r""" Compute the codomain curve given parameters `v` and `w` (as in @@ -263,6 +267,7 @@ def compute_codomain_formula(E, v, w): return EllipticCurve([a1, a2, a3, A4, A6]) + def compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4): r""" Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of @@ -298,6 +303,7 @@ def compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4): w = x0 * v return v, w + def compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3): r""" Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of @@ -335,6 +341,7 @@ def compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3): w = 3*(s1**3 - 3*s1*s2 + 3*s3) + (b2*temp1 + b4*s1)/2 return v, w + def compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n): r""" Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of odd @@ -373,6 +380,7 @@ def compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n): w = 10*(s1**3 - 3*s1*s2 + 3*s3) + 2*b2*(s1**2 - 2*s2) + 3*b4*s1 + n*b6 return v, w + def compute_codomain_kohel(E, kernel): r""" Compute the codomain from the kernel polynomial using Kohel's @@ -480,6 +488,7 @@ def compute_codomain_kohel(E, kernel): return compute_codomain_formula(E, v, w) + def two_torsion_part(E, psi): r""" Return the greatest common divisor of ``psi`` and the 2-torsion @@ -3424,8 +3433,7 @@ def compute_isogeny_bmss(E1, E2, l): sprec = 8 while sprec < 4 * l: assert sprec % 2 == 0 - if sprec > 2 * l: - sprec = 2 * l + sprec = min(sprec, 2 * l) # s1 => s1 + x^k s2 # 2 s1' s2' - dG/dS(x, s1) s2 = G(x, s1) - s1'2 s1 = S @@ -3456,6 +3464,7 @@ def compute_isogeny_bmss(E1, E2, l): ker = Rx(Q).reverse(degree=l//2) return ker.monic() + def compute_isogeny_stark(E1, E2, ell): r""" Return the kernel polynomial of an isogeny of degree ``ell`` @@ -3659,6 +3668,7 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm=None): raise NotImplementedError(f'unknown algorithm {algorithm}') + def compute_intermediate_curves(E1, E2): r""" Return intermediate curves and isomorphisms. @@ -3755,6 +3765,7 @@ def compute_intermediate_curves(E1, E2): post_iso = WeierstrassIsomorphism(E2w, urst, E2) return E1w, E2w, pre_iso, post_iso + def compute_sequence_of_maps(E1, E2, ell): r""" Return intermediate curves, isomorphisms and kernel polynomial. @@ -3854,6 +3865,7 @@ def compute_sequence_of_maps(E1, E2, ell): # Utility functions for manipulating isogeny degree matrices + def fill_isogeny_matrix(M): """ Return a filled isogeny matrix giving all degrees from one giving only prime degrees. @@ -3913,6 +3925,7 @@ def pr(M1, M2): M2 = pr(M0, M1) return M1 + def unfill_isogeny_matrix(M): """ Reverses the action of ``fill_isogeny_matrix``. diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index decf0a3d0b5..4f694e0f252 100755 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -4,13 +4,13 @@ This module defines the class :class:`EllipticCurve_field`, based on :class:`EllipticCurve_generic`, for elliptic curves over general fields. """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2006 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** import sage.rings.abc from sage.categories.number_fields import NumberFields @@ -27,6 +27,7 @@ from .ell_curve_isogeny import EllipticCurveIsogeny, isogeny_codomain_from_kernel from . import ell_generic + class EllipticCurve_field(ell_generic.EllipticCurve_generic, ProjectivePlaneCurve_field): def __init__(self, R, data, category=None): @@ -1460,7 +1461,7 @@ def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, al if kernel is not None: # Check for multiple points or point of known order - kernel_is_list = isinstance(kernel, list) or isinstance(kernel, tuple) + kernel_is_list = isinstance(kernel, (list, tuple)) if kernel_is_list and kernel[0] in self and len(kernel) > 1: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite return EllipticCurveHom_composite(self, kernel, codomain=codomain, model=model, velu_sqrt_bound=velu_sqrt_bound) @@ -1732,9 +1733,9 @@ def kernel_polynomial_from_divisor(self, f, l, *, check=True): def isogenies_prime_degree(self, l=None, max_l=31): """ - Return a list of all separable isogenies of given prime degree(s) - with domain equal to ``self``, which are defined over the base - field of ``self``. + Return a list of all separable isogenies (up to post-composition with + isomorphisms) of given prime degree(s) with domain equal to ``self``, + which are defined over the base field of ``self``. INPUT: @@ -2016,6 +2017,177 @@ def isogenies_prime_degree(self, l=None, max_l=31): from .isogeny_small_degree import isogenies_prime_degree return sum([isogenies_prime_degree(self, d) for d in L], []) + def isogenies_degree(self, n, *, _intermediate=False): + r""" + Return an iterator of all separable isogenies of given degree (up to + post-composition with isomorphisms) with domain equal to ``self``, + which are defined over the base field of ``self``. + + ALGORITHM: + + The prime factors `p` of `n` are processed one by one in decreasing + order, each time "branching" out by taking isogenies of degree `p`. + + INPUT: + + - ``n`` -- integer, or its + :class:`~sage.structure.factorization.Factorization`. + + - ``_intermediate`` -- (bool, default: False): If set, the isogenies + from this curve to the curves traversed within the depth-first search + are returned. This is for internal use only. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(11), [1, 1]) + sage: list(E.isogenies_degree(23 * 19)) + [Composite morphism of degree 437 = 23*19: + From: Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 + To: Elliptic Curve defined by y^2 = x^3 + 8*x + 7 over Finite Field of size 11, + Composite morphism of degree 437 = 23*19: + From: Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 + To: Elliptic Curve defined by y^2 = x^3 + 6*x + 2 over Finite Field of size 11, + Composite morphism of degree 437 = 23*19: + From: Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 + To: Elliptic Curve defined by y^2 = x^3 + 2*x + 6 over Finite Field of size 11, + Composite morphism of degree 437 = 23*19: + From: Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 + To: Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11] + + :: + + sage: E = EllipticCurve(GF(next_prime(2^32)), j=1728) + sage: sorted([phi.codomain().j_invariant() for phi in E.isogenies_degree(11 * 17 * 19^2)]) + [1348157279, 1348157279, 1713365879, 1713365879, 3153894341, 3153894341, + 3225140514, 3225140514, 3673460198, 3673460198, 3994312564, 3994312564] + sage: it = E.isogenies_degree(2^2); it + + sage: all(phi.degree() == 2^2 for phi in it) + True + + We verify that the isogenies outputted are distinct. Note that we do + not use a ``set`` or any hash-based data structure, as hashing + isogenies is slow:: + + sage: import itertools + sage: all_distinct = lambda arr: all(x != y for x, y in itertools.combinations(arr, 2)) + sage: K. = GF((19, 2)) + sage: E = EllipticCurve(K, [11*z+5, 14*z+3]) + sage: S = list(E.isogenies_degree(5^2)); len(S), all_distinct(S) + (3, True) + sage: S = list(E.isogenies_degree(5^2*11)); len(S), all_distinct(S) + (6, True) + sage: S = list(E.isogenies_degree(5^2*11^4)); len(S), all_distinct(S) # long time (2s) + (15, True) + + For curves over number fields, the number of distinct isogenies will usually be small:: + + sage: E = EllipticCurve(QQ, [0, 1, 0, -2, 0]) + sage: len(list(E.isogenies_degree(2**1))) + 3 + sage: len(list(E.isogenies_degree(2**5))) + 3 + sage: len(list(E.isogenies_degree(2**8))) # long time (8s) + 1 + + :: + + sage: pol = PolynomialRing(QQ, 'x')([529, 782, 1]) + sage: L. = NumberField(pol) + sage: E = EllipticCurve(j=-7072/1127*a + 2016) + sage: len(list(E.isogenies_degree(2))) + 3 + sage: len(list(E.isogenies_degree(2**5))) + 3 + + :: + + sage: pol = PolynomialRing(QQ, 'x')([1, -3, 5, -5, 5, -3, 1]) + sage: L. = NumberField(pol) + sage: js = hilbert_class_polynomial(-23).roots(L, multiplicities=False) + sage: E = EllipticCurve(j=choice(js)) + sage: len(list(E.isogenies_degree(2^3))) # long time (9s) + 10 + + TESTS:: + + sage: E = EllipticCurve(GF(next_prime(2^32)), j=1728) + sage: list(E.isogenies_degree(2^2, _intermediate=True)) + [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 4294967311 + Via: (u,r,s,t) = (1, 0, 0, 0), + Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 4294967311 + to Elliptic Curve defined by y^2 = x^3 + 4294967307*x over Finite Field of size 4294967311, + Composite morphism of degree 4 = 2^2: + From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 4294967311 + To: Elliptic Curve defined by y^2 = x^3 + 16*x over Finite Field of size 4294967311, + Composite morphism of degree 4 = 2^2: + From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 4294967311 + To: Elliptic Curve defined by y^2 = x^3 + 4294967267*x + 4294967199 over Finite Field of size 4294967311, + Composite morphism of degree 4 = 2^2: + From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 4294967311 + To: Elliptic Curve defined by y^2 = x^3 + 4294967267*x + 112 over Finite Field of size 4294967311] + sage: all(isog.domain() is E for isog in _) + True + sage: all(isog.domain() is E for isog in E.isogenies_degree(2^5, _intermediate=True)) + True + + The following curve has no degree-`53` isogenies, so the code is quick:: + + sage: E = EllipticCurve(GF(103), [3, 5]) + sage: E.isogenies_prime_degree(53) + [] + sage: list(E.isogenies_degree(product(prime_range(3, 53)) * 53)) + [] + """ + def compute_key(phi): + """ + Data used in ``hash(phi)`` excluding the expensive `.kernel_polynomial`. + """ + return (phi.domain(), phi.codomain(), phi.degree(), phi.scaling_factor()) + + from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism + from sage.structure.factorization import Factorization + + if not isinstance(n, Factorization): + n = Integer(n).factor() + + if n.value() == 1: + yield identity_morphism(self) + return + + p = n[-1][0] + seen = {} + + def insert_seen(phi) -> bool: + key = compute_key(phi) + if key not in seen: + seen[key] = [phi] + return True + for psi in seen[key]: + if psi == phi: + return False + seen[key].append(phi) + return True + + if _intermediate: + yield identity_morphism(self) + + # isog: self -> E1 + for isog in self.isogenies_prime_degree(p): + if _intermediate: + if insert_seen(isog): + # self -> E1 + yield isog + + Eiso = isog.codomain() + # next_isog : E1 -> E2 + for next_isog in Eiso.isogenies_degree(n / p, _intermediate=_intermediate): + # psi: self -> E2 + psi = next_isog * isog + if insert_seen(psi): + # self -> E2 + yield psi + def is_isogenous(self, other, field=None): """ Return whether or not ``self`` is isogenous to ``other``. @@ -2501,6 +2673,7 @@ def compute_model(E, name): raise NotImplementedError(f'cannot compute {name} model') + def point_of_order(E, n): r""" Given an elliptic curve `E` over a finite field or a number field diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8cf0cdb2cb0..905cc63e149 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -15,6 +15,8 @@ - Lorenz Panny, John Cremona (2023-02): ``.twists()`` - Lorenz Panny (2023): ``special_supersingular_curve()`` + +- Martin Grenouilloux, Gareth Ma (2024-09): ``EllipticCurve_with_prime_order()`` """ # **************************************************************************** @@ -1341,6 +1343,113 @@ def is_ordinary(self, proof=True): """ return not is_j_supersingular(self.j_invariant(), proof=proof) + def has_order(self, value, num_checks=8): + r""" + Return ``True`` if the curve has order ``value``. + + INPUT: + + - ``value`` -- integer in the Hasse-Weil range for this curve + + - ``num_checks``-- integer (default: `8`); the number of times to check + whether ``value`` times a random point on this curve equals the + identity. If ``value`` is a prime and the curve is over a field of + order at least `5`, it is sufficient to pass in ``num_checks=1`` - + see the examples below. + + .. NOTE:: + + Since the method is probabilistic, there is a possibility for the + method to yield false positives (i.e. returning ``True`` even when + the result is ``False``). Even worse, it is possible for this to + happen even when ``num_checks`` is increased arbitrarily. See below + for an example and :issue:`38617` for an open discussion. + + EXAMPLES: + + For curves over small finite fields, the order is computed and compared + directly:: + + sage: E = EllipticCurve(GF(7), [0, 1]) + sage: E.order() + 12 + sage: E.has_order(12, num_checks=0) + True + sage: E.has_order(11, num_checks=0) + False + sage: E.has_order(13, num_checks=0) + False + + This tests the method on a random curve:: + + sage: # long time (10s) + sage: p = random_prime(2**128, lbound=2**127) + sage: K = GF((p, 2), name="a") + sage: E = EllipticCurve(K, [K.random_element() for _ in range(2)]) + sage: N = E.order() + sage: E.has_order(N, num_checks=20) + True + sage: E.has_order(N + 1) + False + + This demonstrates the bug mentioned in the NOTE above. The last return + value should be ``False`` after :issue:`38617` is fixed:: + + sage: E = EllipticCurve(GF(5443568676570036275321323), [0, 13]) + sage: N = 2333145661241 + sage: E.order() == N^2 + True + sage: E.has_order(N^2) + True + sage: del E._order + sage: E.has_order(N^2 + N) + True + + Due to the nature of the algorithm (testing multiple of points) and the Hasse-Weil bound, we see that for testing prime orders, ``num_checks=1`` is sufficient:: + + sage: p = random_prime(1000) + sage: E = EllipticCurve(GF(p), j=randrange(p)) + sage: q = random_prime(p + 20, lbound=p - 20) + sage: E.has_order(q, num_checks=20) == E.has_order(q, num_checks=1) + True + + AUTHORS: + + - Mariah Lenox (2011-02-16): Initial implementation + + - Gareth Ma (2024-01-21): Fix bug for small curves + """ + q = self.base_field().order() + a, b = Hasse_bounds(q, 1) + if not a <= value <= b: + return False + + # For really small values, the random tests are too weak to detect wrong + # orders So we go with computing directly instead. + # In #38341, the bound has been increased to a large value (2^64), but + # it should be decreased (to ~100) after bug #38617 is fixed. + if q <= 2**64 or hasattr(self, "_order"): + return self.order() == value + + # This might be slow + # if value.is_prime(): + # num_checks = 1 + + # Is value * random == identity? + for _ in range(num_checks): + while True: + G = self.random_point() + if not G.is_zero(): + break + + if not (value * G).is_zero(): + return False + + # TODO: uncomment this and remove the line in `set_order` after 38617 is fixed. + # self._order = value + + return True + def set_order(self, value, *, check=True, num_checks=8): r""" Set the value of ``self._order`` to ``value``. @@ -1355,7 +1464,7 @@ def set_order(self, value, *, check=True, num_checks=8): - ``check``-- boolean (default: ``True``); whether or not to run sanity checks on the input - - ``num_checks``-- integer (default: 8); if ``check`` is + - ``num_checks``-- integer (default: `8`); if ``check`` is ``True``, the number of times to check whether ``value`` times a random point on this curve equals the identity @@ -1401,42 +1510,41 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(0) Traceback (most recent call last): ... - ValueError: Value 0 illegal (not an integer in the Hasse range) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 0 sage: E.set_order(1000) Traceback (most recent call last): ... - ValueError: Value 1000 illegal (not an integer in the Hasse range) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 1000 - It is also very likely an error to pass a value which is not - the actual order of this curve. How unlikely is determined by - ``num_checks``, the factorization of the actual order, and the - actual group structure:: + It is also very likely an error to pass a value which is not the actual + order of this curve. How unlikely is determined by ``num_checks``, the + factorization of the actual order, and the actual group structure:: sage: E = EllipticCurve(GF(1009), [0, 1]) # This curve has order 948 sage: E.set_order(947) Traceback (most recent call last): ... - ValueError: Value 947 illegal (multiple of random point not the identity) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 1009 does not have order 947 - For curves over small finite fields, the order is cheap to compute, so it is computed - directly and compared:: + For curves over small finite fields, the order is cheap to compute, so + it is computed directly and compared:: sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: E.set_order(11) Traceback (most recent call last): ... - ValueError: Value 11 illegal (correct order is 12) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 11 TESTS: - The previous version's random tests are not strong enough. In particular, the following used - to work:: + The previous version's random tests are not strong enough. In particular, + the following used to work:: sage: E = EllipticCurve(GF(2), [0, 0, 1, 1, 1]) # This curve has order 1 sage: E.set_order(3) Traceback (most recent call last): ... - ValueError: Value 3 illegal (correct order is 1) + ValueError: Elliptic Curve defined by y^2 + y = x^3 + x + 1 over Finite Field of size 2 does not have order 3 :: @@ -1444,12 +1552,12 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(4, num_checks=0) Traceback (most recent call last): ... - ValueError: Value 4 illegal (correct order is 12) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 4 sage: E.order() 12 - .. TODO:: Add provable correctness check by computing the abelian group structure and - comparing. + .. TODO:: Add provable correctness check by computing the abelian group + structure and comparing. AUTHORS: @@ -1459,24 +1567,8 @@ def set_order(self, value, *, check=True, num_checks=8): """ value = Integer(value) - if check: - # Is value in the Hasse range? - q = self.base_field().order() - a,b = Hasse_bounds(q,1) - if not a <= value <= b: - raise ValueError(f"Value {value} illegal (not an integer in the Hasse range)") - - # For really small values, the random tests are too weak to detect wrong orders - # So we go with computing directly instead. - if q <= 100: - if self.order() != value: - raise ValueError(f"Value {value} illegal (correct order is {self.order()})") - - # Is value*random == identity? - for _ in range(num_checks): - G = self.random_point() - if value * G != self(0): - raise ValueError(f"Value {value} illegal (multiple of random point not the identity)") + if check and not self.has_order(value, num_checks=num_checks): + raise ValueError(f"{self} does not have order {value}") # TODO: It might help some of PARI's algorithms if we # could copy this over to the .pari_curve() as well. @@ -1868,6 +1960,7 @@ def twists(self): return [self, self.quadratic_twist(D)] + def curves_with_j_0(K): r""" Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K`. @@ -1937,6 +2030,7 @@ def curves_with_j_0(K): # then you can also compute the orders! return curves + def curves_with_j_1728(K): r""" Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 1728 over the finite field `K`. @@ -1993,6 +2087,7 @@ def curves_with_j_1728(K): curves = [EllipticCurve(K, [D**i, 0]) for i in range(4)] return curves + def curves_with_j_0_char2(K): r""" Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K` of characteristic 2. @@ -2086,6 +2181,7 @@ def curves_with_j_0_char2(K): return [EllipticCurve(K, ai) for ai in [[0,0,1,0,0], [0,0,1,0,b], [0,0,1,c,0], [0,0,a,0,0], [0,0,a,0,d], [0,0,asq,0,0], [0,0,asq,0,e]]] + def curves_with_j_0_char3(K): r""" Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K` of characteristic 3. @@ -2182,6 +2278,7 @@ def curves_with_j_0_char3(K): supersingular_j_polynomials = {} + def fill_ss_j_dict(): r""" Fill the global cache of supersingular j-_polynomials. @@ -2251,6 +2348,7 @@ def fill_ss_j_dict(): supersingular_j_polynomials[283] = [212, 4, 42, 155, 38, 1, 270, 175, 172, 256, 264, 232, 50, 82, 244, 127, 148, 46, 249, 72, 59, 124, 75, 1] supersingular_j_polynomials[293] = [264, 66, 165, 144, 243, 25, 163, 210, 18, 107, 160, 153, 70, 255, 91, 211, 22, 7, 256, 50, 150, 94, 225, 60, 1] + def supersingular_j_polynomial(p, use_cache=True): r""" Return a polynomial whose roots are the supersingular @@ -2332,6 +2430,7 @@ def supersingular_j_polynomial(p, use_cache=True): supersingular_j_polynomials[p] = R.coefficients(sparse=False) return R + def is_j_supersingular(j, proof=True): r""" Return ``True`` if `j` is a supersingular `j`-invariant. @@ -2465,10 +2564,13 @@ def is_j_supersingular(j, proof=True): return E.trace_of_frobenius() % p == 0 -def special_supersingular_curve(F, *, endomorphism=False): + +def special_supersingular_curve(F, q=None, *, endomorphism=False): r""" - Given a finite field ``F``, construct a "special" supersingular - elliptic curve `E` defined over ``F``. + Given a finite field ``F`` of characteristic `p`, and optionally + a positive integer `q` such that the Hilbert conductor of `-q` + and `-p` equals `p`, construct a "special" supersingular elliptic + curve `E` defined over ``F``. Such a curve @@ -2477,21 +2579,32 @@ def special_supersingular_curve(F, *, endomorphism=False): - has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and `E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`; - - has an endomorphism `\vartheta` of small degree `q` that + - has an endomorphism `\vartheta` of degree `q` that anticommutes with the `\mathbb F_p`-Frobenius on `E`. (The significance of `\vartheta` is that any such endomorphism, together with the `\mathbb F_p`-Frobenius, generates the endomorphism algebra `\mathrm{End}(E) \otimes \QQ`.) + The complexity grows exponentially in `\log(q)`. Automatically + chosen values of `q` lie in `O((\log p)^2)` assuming GRH. + INPUT: - - ``F`` -- finite field `\mathbb F_{p^r}`; + - ``F`` -- finite field `\mathbb F_{p^r}` + + - ``q`` -- positive integer (optional, default ``None``) - ``endomorphism`` -- boolean (default: ``False``); when set to ``True``, it is required that `2 \mid r`, and the function then additionally returns `\vartheta` + .. WARNING:: + + Due to :issue:`38481`, calling this function with a value of `q` + larger than approximately `p/4` may currently fail. This failure + will not occur for automatically chosen values of `q`. + EXAMPLES:: sage: special_supersingular_curve(GF(1013^2), endomorphism=True) @@ -2504,8 +2617,8 @@ def special_supersingular_curve(F, *, endomorphism=False): Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0)) sage: special_supersingular_curve(GF(1021^2), endomorphism=True) - (Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2, - Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2) + (Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2, + Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2) sage: special_supersingular_curve(GF(1031^2), endomorphism=True) (Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2, @@ -2530,6 +2643,20 @@ def special_supersingular_curve(F, *, endomorphism=False): Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2 Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0)) + We can also supply a suitable value of `q` ourselves:: + + sage: special_supersingular_curve(GF(1019), q=99) + Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019 + + sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True) + (Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2, + Isogeny of degree 99 from Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2 to Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2) + + sage: special_supersingular_curve(GF(1013), q=99) + Traceback (most recent call last): + ... + ValueError: invalid choice of q + TESTS:: sage: p = random_prime(1000) @@ -2578,6 +2705,35 @@ def special_supersingular_curve(F, *, endomorphism=False): sage: pi * endo == -endo * pi True + Also try it when `q` is given: + + sage: p = random_prime(300, lbound=10) + sage: k = ZZ(randrange(1, 5)) + sage: while True: + ....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481 + ....: if QuaternionAlgebra(-q, -p).discriminant() == p: + ....: break + sage: E = special_supersingular_curve(GF((p, k)), q) + sage: E.is_supersingular() + True + sage: F. = GF((p, 2*k)) + sage: E, endo = special_supersingular_curve(F, q, endomorphism=True) + sage: E.is_supersingular() + True + sage: E.j_invariant() in GF(p) + True + sage: endo.domain() is endo.codomain() is E + True + sage: endo.degree() == q + True + sage: endo.trace() + 0 + sage: pi = E.frobenius_isogeny() + sage: pi.codomain() is pi.domain() is E + True + sage: pi * endo == -endo * pi + True + .. NOTE:: This function makes no guarantees about the distribution of @@ -2594,42 +2750,49 @@ def special_supersingular_curve(F, *, endomorphism=False): if endomorphism and deg % 2: raise ValueError('endomorphism was requested but is not defined over given field') - E = None + if q is not None: + from sage.arith.misc import hilbert_conductor + if p.divides(q) or hilbert_conductor(-q, -p) != p: + raise ValueError('invalid choice of q') # first find the degree q of our special endomorphism - if p == 2: - q = 3 - E = EllipticCurve(F, [0,0,1,0,0]) - - elif p % 4 == 3: - q = 1 - E = EllipticCurve(F, [1,0]) - - elif p % 3 == 2: - q = 3 - E = EllipticCurve(F, [0,1]) - - elif p % 8 == 5: - q = 2 - E = EllipticCurve(F, [-4320, 96768]) - - else: - from sage.arith.misc import legendre_symbol - for q in map(ZZ, range(3,p,4)): - if not q.is_prime(): - continue - if legendre_symbol(-q, p) == -1: - break + if q is None: + if p == 2: + q = 3 + elif p % 4 == 3: + q = 1 + elif p % 3 == 2: + q = 3 + elif p % 8 == 5: + q = 2 else: - assert False # should never happen - - if E is None: - from sage.arith.misc import fundamental_discriminant - from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial - H = hilbert_class_polynomial(fundamental_discriminant(-q)) - j = H.change_ring(GF(p)).any_root() + from sage.arith.misc import legendre_symbol + for q in map(ZZ, range(3,p,4)): + if not q.is_prime(): + continue + if legendre_symbol(-q, p) == -1: + break + else: # should never happen + assert False, 'bug in special_supersingular_curve()' + q = ZZ(q) + + from sage.arith.misc import fundamental_discriminant + from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial + H = hilbert_class_polynomial(fundamental_discriminant(-q)) + j = H.change_ring(GF(p)).any_root() + if j.is_zero(): + if p == 2: + ainvs = [0,0,1,0,0] + elif p == 3: + ainvs = [1,0] + else: + ainvs = [0,1] + elif j == 1728: + ainvs = [1,0] + else: a = 27 * j / (4 * (1728-j)) - E = EllipticCurve(F, [a,-a]) + ainvs = [a,-a] + E = EllipticCurve(F, ainvs) if ZZ(2).divides(deg): k = deg//2 @@ -2640,34 +2803,38 @@ def special_supersingular_curve(F, *, endomorphism=False): if not endomorphism: return E - if q == 1 or p <= 13: - if q == 1: - endos = E.automorphisms() - else: - endos = (iso*phi for phi in E.isogenies_prime_degree(q) - for iso in phi.codomain().isomorphisms(E)) - endo = next(endo for endo in endos if endo.trace().is_zero()) - + if q.is_one(): + endo = next(auto for auto in E.automorphisms() if auto.trace().is_zero()) else: - from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - iso = WeierstrassIsomorphism(None, (F(-q).sqrt(),0,0,0), E) - if q == 3 and E.a_invariants() == (0,0,0,0,1): - # workaround for #21883 - endo = E.isogeny(E(0,1)) - else: - endo = E.isogeny(None, iso.domain(), degree=q) - endo = iso * endo + iso = E.isomorphism(F(-q).sqrt(), is_codomain=True) + try: + endo = iso * E.isogeny(None, iso.domain(), degree=q) + except (NotImplementedError, ValueError): #FIXME catching ValueError here is a workaround for #38481 + #FIXME this code could be simplified/optimized after #37388 and/or #35949 + def _isogs(E, d): + if d.is_one(): + yield E.identity_morphism() + return + l = d.prime_factors()[-1] + for phi in E.isogenies_prime_degree(l): + for psi in _isogs(phi.codomain(), d//l): + yield psi * phi + endos = (iso*phi for phi in _isogs(E, q) for iso in phi.codomain().isomorphisms(E)) +# endos = (iso*phi for phi in E.isogenies_degree(q) +# for iso in phi.codomain().isomorphisms(E)) + endo = next(endo for endo in endos if endo.trace().is_zero()) endo._degree = ZZ(q) endo.trace.set_cache(ZZ.zero()) return E, endo + def EllipticCurve_with_order(m, *, D=None): r""" Return an iterator for elliptic curves over finite fields with the given order. The curves are computed using the Complex Multiplication (CM) method. - A `:sage:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case + A :class:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case the algorithm is more efficient. If ``D`` is specified, it is used as the discriminant. @@ -2714,8 +2881,8 @@ def EllipticCurve_with_order(m, *, D=None): sage: all(E.order() == 21 for E in Es) True - Indeed, we can verify that this is correct. Hasse's bounds tell us that $p \leq 50$ - (approximately), and the rest can be checked via bruteforce:: + Indeed, we can verify that this is correct. Hasse's bounds tell us that + `p \leq 50` (approximately), and the rest can be checked via bruteforce:: sage: for p in prime_range(50): ....: for j in range(p): @@ -2751,7 +2918,7 @@ def find_q(m, m4_fac, D): m_val = m if D is None: - Ds = (D for D in range(-4 * m_val, 0) if D % 4 in [0, 1]) + Ds = (D for D in range(-1, -4 * m_val - 1, -1) if D % 4 in [0, 1]) else: assert D < 0 and D % 4 in [0, 1] Ds = [D] @@ -2763,17 +2930,325 @@ def find_q(m, m4_fac, D): continue H = hilbert_class_polynomial(D) - K = GF(q) - roots = H.roots(ring=K) - for j0, _ in roots: + for j0 in H.roots(ring=GF(q), multiplicities=False): E = EllipticCurve(j=j0) for Et in E.twists(): if any(Et.is_isomorphic(E) for E in seen): continue - try: - # This tests whether the curve has given order - Et.set_order(m_val) + # This tests whether the curve has given order + if Et.has_order(m_val): + # TODO: remove after 38617 + Et.set_order(m_val, check=False) seen.add(Et) yield Et - except ValueError: - pass + +def EllipticCurve_with_prime_order(N): + r""" + Given a prime number ``N``, find another prime number `p` and construct an + elliptic curve `E` defined over `\mathbb F_p` such that + `\#E(\mathbb F_p) = N`. + + INPUT: + + - ``N`` -- integer; the order for which we seek an elliptic curve. Must be a + prime number. + + OUTPUT: an iterator of (some) elliptic curves `E/\mathbb F_p` of order ``N`` + + ALGORITHM: + + Our algorithm is based on [BS2007]_, Algorithm 2.2, but we deviate for + several key steps. Firstly, the authors in the paper perform the search for + a suitable `D` *incrementally*, by enlarging the table `S` by `log(N)`-size + interval of primes `p` and testing all products of distinct primes `p` (or + rather `p^*`). We find this difficult to implement without testing + duplicate `D`\s, so we instead enlarge the table one prime at a time + (effectively replacing `[r\log(N), (r + 1)\log(N)]` in the paper by `[r, + r]`). To compensate for the speed loss, we begin the algorithm by + prefilling `S` with the primes below `1000` (satisfying quadratic + reciprocity properties). The constant `1000` is determined experimentally + to be fast for many purposes, and for most `N` we tested we are able to + find a suitable small `D` without increasing the size of `S`. + + The paper also doesn't specify how to enumerate such `D`\s, which recall + should be product of distinct values in the table `S`. We implement this + with a priority queue (min heap), which also allows us to search for the + suitable `D`\s in increasing (absolute value) order. This is suitable for + the algorithm because smaller `D` means the Hilbert class polynomial is + computed quicker. + + Finally, to avoid repeatedly testing the same `D`\s, we require the latest + prime to be added to the table to be included as a factor of `D` (see code + for more explanation). As we need to find integers `x, y` such that `x^2 + + (-D)y^2 = 4N` with `D < 0` and `N` prime, we actually need `|D| \leq 4N`, + so we terminate the algorithm when the primes in the table are larger than + that bound. This makes the iterator return all curves it can find in finite + time. + + ALGORITHM: Based on [BS2007]_, Algorithm 2.2 + + EXAMPLES:: + + sage: N = 8314040072427107567 + sage: E = next(EllipticCurve_with_prime_order(N)) + sage: E + Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + 1841350074072114366 + over Finite Field of size 8314040074357871443 + sage: E.has_order(N) + True + + The returned curves are sometimes random because + :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.twists` + is not deterministic. However, it's always isomorphic:: + + sage: E = next(EllipticCurve_with_prime_order(23)); E # random + Elliptic Curve defined by y^2 = x^3 + 12*x + 6 over Finite Field of size 17 + sage: E.is_isomorphic(EllipticCurve(GF(17), [3, 5])) + True + + You can directly iterate over the iterator; here only on the first 10 + curves:: + + sage: N = 54675917 + sage: for _, E in zip(range(10), EllipticCurve_with_prime_order(N)): + ....: assert E.has_order(N) + + It works for large primes:: + + sage: N = 2666207849820848272386538889527600954292544013630953455833 + sage: E = next(EllipticCurve_with_prime_order(N)); E + Elliptic Curve defined by y^2 = x^3 + 2666207849820848272386538889427721639173508298483739490459*x + + 77986137112576 over Finite Field of size 2666207849820848272386538889427721639173508298487130585243 + sage: E.has_order(N) + True + + Another example for large primes:: + + sage: N = next_prime(2^256) + sage: E = next(EllipticCurve_with_prime_order(N)); E # random + Elliptic Curve defined by y^2 = x^3 + 6056521267553273205988520276135607487700943205131813669424576873701361709521*x + + 86942739955486781674010637133214195706465136689012129911736706024465988573567 over Finite Field of size + 115792089237316195423570985008687907853847329310253429036565151476471048389761 + sage: E.j_invariant() + 111836223967433630316209796253554285080540088646141285337487360944738698436350 + sage: E.has_order(N) + True + + Note that the iterator does *not* return all curves with the given order:: + + sage: any(E.base_ring() is GF(7) for E in EllipticCurve_with_prime_order(7)) + False + sage: EllipticCurve(GF(7), [0, 5]).order() + 7 + + However, experimentally it returns many of them. Here it returns all of + them:: + + sage: N = 23 + sage: set_random_seed(1337) # as the function returns random twists of curves + sage: curves = list(EllipticCurve_with_prime_order(N)); curves # random + [Elliptic Curve defined by y^2 = x^3 + 3*x + 5 over Finite Field of size 17, + Elliptic Curve defined by y^2 = x^3 + 19*x + 14 over Finite Field of size 31, + Elliptic Curve defined by y^2 = x^3 + 2*x + 9 over Finite Field of size 19, + Elliptic Curve defined by y^2 = x^3 + 7*x + 18 over Finite Field of size 29, + Elliptic Curve defined by y^2 = x^3 + 20*x + 20 over Finite Field of size 23, + Elliptic Curve defined by y^2 = x^3 + 10*x + 16 over Finite Field of size 23] + sage: import itertools + sage: # These are the only primes, by the Hasse-Weil bound + sage: for q in prime_range(17, 35): + ....: K = GF(q) + ....: for u in itertools.product(range(q), repeat=2): + ....: try: E = EllipticCurve(GF(q), u) + ....: except ArithmeticError: continue + ....: if E.has_order(N): + ....: assert any(E.is_isomorphic(E_) for E_ in curves) + + The algorithm is efficient for small ``N`` due to the low number of suitable + discriminants (see the ``abs_products_under`` internal function of the code + for details):: + + sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) + 534 + sage: len(list(EllipticCurve_with_prime_order(next_prime(50000)))) # long time (6s) + 3841 + + There is different verbose data for level `2` to `4`, though level `3` + rarely logs anything (it logs when a new prime `p` is added to the + smoothness bound):: + + sage: from sage.misc.verbose import set_verbose + sage: set_random_seed(1337) # as the function returns random twists of curves + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 + sage: set_verbose(2) + sage: set_random_seed(1337) + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 + sage: set_verbose(4) + sage: set_random_seed(1337) + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 + ... + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 + ... + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 + + TESTS:: + + sage: list(EllipticCurve_with_prime_order(2)) + [Elliptic Curve defined by y^2 + x*y + y = x^3 + 1 over Finite Field of size 2, + Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2 over Finite Field of size 3, + Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 5] + + sage: set_verbose(0) + sage: for N in prime_range(3, 100): + ....: E = next(EllipticCurve_with_prime_order(N)) + ....: assert E.has_order(N) + + sage: N = 113 + sage: for _, E in zip(range(30), EllipticCurve_with_prime_order(N)): + ....: assert E.has_order(N) + + sage: N = 15175980689839334471 + sage: E = next(EllipticCurve_with_prime_order(N)) + sage: E.has_order(N) + True + + sage: N = next_prime(123456789) + sage: E = next(EllipticCurve_with_prime_order(N)) + sage: E.has_order(N) + True + + sage: N = 123456789 + sage: E = next(EllipticCurve_with_prime_order(N)) + Traceback (most recent call last): + ... + ValueError: input order is not a prime + + sage: E = next(EllipticCurve_with_prime_order(0)) + Traceback (most recent call last): + ... + ValueError: input order is not a prime + + sage: E = next(EllipticCurve_with_prime_order(-7)) + Traceback (most recent call last): + ... + ValueError: input order is not a prime + + AUTHORS: + + - Martin Grenouilloux, Gareth Ma (2024-09): initial implementation + """ + import itertools + from sage.arith.misc import is_prime, legendre_symbol + from sage.misc.verbose import verbose + from sage.quadratic_forms.binary_qf import BinaryQF + from sage.rings.fast_arith import prime_range + from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial + from sage.sets.primes import Primes + + if not is_prime(N): + raise ValueError("input order is not a prime") + + if N == 2: + yield from [ + EllipticCurve(GF(2), [1, 0, 1, 0, 1]), + EllipticCurve(GF(3), [0, 2, 0, 0, 2]), + EllipticCurve(GF(5), [2, 0]) + ] + return + + # We start with small primes directly to accelerate the search. Note that + # 1000 is a magic constant, it's just fast enough to compute without + # sacrificing much speed. + # The if-then-else term is (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. + S = [(-p if p % 4 == 3 else p) for p in prime_range(3, min(1000, 4 * N)) + if legendre_symbol(N, p) == 1] + + def abs_products_under(bound): + """ + This function returns an iterator of all numbers with absolute value not + exceeding ``bound`` expressable as product of distinct elements in ``S`` + in ascending order. + """ + import heapq + hq = [(1, 1, -1)] + while len(hq): + abs_n, n, idx = heapq.heappop(hq) + yield n + for nxt in range(idx + 1, len(S)): + if abs_n * abs(S[nxt]) <= bound: + heapq.heappush(hq, (abs_n * abs(S[nxt]), n * S[nxt], nxt)) + else: + break + + # We add p = 1 to process the small primes. + for p in itertools.chain([1], Primes()): + if p != 1: + if p < abs(S[-1]): + continue + + if legendre_symbol(N, p) != 1: + continue + + # Later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no + # solution, we need p = |p_star| <= |-D| <= 4N. This is a stopping + # condition for the algorithm. + if p > 4 * N: + break + + verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) + + p_star = -p if p % 4 == 3 else p + + for e in abs_products_under(4 * N // p): + # According to the paper, the expected minimum D to work is + # O(log(N)^2) + D = p_star * e + assert abs(D) <= 4 * N + + if D % 8 != 5 or D >= 0: + continue + + verbose(f"Testing {D=}", level=4) + + Q = BinaryQF([1, 0, -D]) + sol = Q.solve_integer(4 * N, algorithm='cornacchia') + if sol is None: + continue + + x, _ = sol + for p_i in [N + 1 - x, N + 1 + x]: + if is_prime(p_i): + verbose(f"Computing the Hilbert class polynomial H_{D}", + level=2) + H = hilbert_class_polynomial(D) + K = GF(p_i) + for j0 in H.roots(ring=K, multiplicities=False): + E = EllipticCurve(K, j=j0) + # `E.twists()` also contains E. + for Et in E.twists(): + # `num_checks=1` is sufficient for prime order + if Et.has_order(N, num_checks=1): + # TODO: remove after 38617 + Et.set_order(N, check=False) + yield Et + + if p != 1: + # Extending our prime list and continuing onto the next round. + S.append(p_star) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index ba10a3c6e1a..b6bdccff654 100755 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -107,6 +107,7 @@ oo = Cusps(infinity) zero = Integer(0) + def modular_symbol_space(E, sign, base_ring, bound=None): r""" Create the space of modular symbols of a given sign over a give base_ring, @@ -220,6 +221,7 @@ def _repr_(self): return "Modular symbol with sign %s over %s attached to %s" % ( self._sign, self._base_ring, self._E) + class ModularSymbolECLIB(ModularSymbol): def __init__(self, E, sign, nap=1000): r"""Modular symbols attached to `E` using ``eclib``. diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 8ccdbcd452d..eea2e5cc243 100755 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -250,15 +250,6 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, listpoints = [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7), 1]] (1, 1, [(1/2*a + 3/2 : -a - 2 : 1)]) - sage: v = E.simon_two_descent(verbose=2) - K = bnfinit(y^2 + 7); - a = Mod(y,K.pol); - bnfellrank(K, [0, 0, 0, 1, a], [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]); - ... - v = [1, 1, [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]] - sage: v - (1, 1, [(1/2*a + 3/2 : -a - 2 : 1)]) - A curve with 2-torsion:: sage: K. = NumberField(x^2 + 7) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 09ed1e20df3..507930474c1 100755 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -1148,12 +1148,19 @@ def division_points(self, m, poly_only=False): 3 sage: [(Q,Q._order) for Q in P.division_points(4)] [((-2 : -7 : 1), 6), ((1 : 2 : 1), 6), ((4 : -7 : 1), 3), ((13 : 38 : 1), 6)] + + Check for :issue:`38796`:: + + sage: E = EllipticCurve(GF(127), [1,1]) + sage: P = E(72, 24) + sage: [-1*Q for Q in P.division_points(-1)] + [(72 : 24 : 1)] """ # Coerce the input m to an integer m = Integer(m) # Check for trivial cases of m = 1, -1 and 0. if m == 1 or m == -1: - return [self] + return [m*self] if m == 0: if self == 0: # then every point Q is a solution, but... return [self] @@ -2379,6 +2386,7 @@ def point_of_jacobian_of_curve(self): G = J.group(self.base_ring()) return G(P - P.degree()*Pinf) + class EllipticCurvePoint_number_field(EllipticCurvePoint_field): """ A point on an elliptic curve over a number field. @@ -2550,6 +2558,101 @@ def has_infinite_order(self): return False return self.order() == oo + def _has_order_at_least(self, bound, *, attempts=999): + r""" + Return ``True`` if this point definitely has order at least ``bound`` + on the elliptic curve, ``False`` if the point has smaller order, and + ``None`` if the result of this test is inconclusive. + + This method can be much faster than calling :meth:`has_infinite_order` + if all that is needed is a lower bound on the order. + + ALGORITHM: Compute the order of the point modulo various small primes + and combine that information using CRT. + + EXAMPLES:: + + sage: E = EllipticCurve('11a3') + sage: P = next(filter(bool, E.torsion_points())) + sage: P._has_order_at_least(5) + True + sage: P._has_order_at_least(6) + sage: P.order() + 5 + sage: Q = E.lift_x(10^42, extend=True) + sage: Q._has_order_at_least(10^100) + True + + :: + + sage: x = polygen(ZZ) + sage: K. = NumberField(x^2 - x + 2) + sage: E = EllipticCurve([1, a-1, a+1, -2*a-2, -5*a+7]) # 2.0.7.1-268.3-b1 + sage: P = next(filter(bool, E.torsion_points())) + sage: P._has_order_at_least(11) + True + sage: P._has_order_at_least(12) + False + sage: P.order() + 11 + sage: Q = E.lift_x(123*a + 456, extend=True) + sage: Q._has_order_at_least(10^100) + True + """ + n = getattr(self, '_order', None) + if n is not None: + return n >= bound + + from sage.sets.primes import Primes + from sage.rings.finite_rings.finite_field_constructor import GF + field_deg = self.curve().base_field().absolute_degree() + if field_deg > 1: + K = self.curve().base_field().absolute_field('T') + _, iso = K.structure() + E = self.curve().change_ring(iso) + P = self.change_ring(iso) + poly = lambda elt: elt.polynomial() + if field_deg == 2: + # Kamienny-Kenku-Momose + bound = min(bound, 18 + 1) + else: + K, E, P = QQ, self.curve(), self + poly = lambda elt: QQ['x'](elt) + # Mazur / Ogg's torsion conjecture + # Torsion points can only have order <= 12, so order of > 12 -> infinite order + bound = min(bound, 12 + 1) + assert P.curve() is E + + n = ZZ.one() + no_progress = 0 + for p in Primes(): + try: + f,_ = K.defining_polynomial().change_ring(GF(p)).factor()[0] + except ZeroDivisionError: + continue + F = GF(p).extension(f,'t') + red = lambda elt: F(f.parent()(poly(elt)).change_ring(GF(p)) % f) + + try: + Ered = E.change_ring(red) + Pred = Ered(*map(red, P)) + except (ZeroDivisionError, ArithmeticError): + continue + + o = Pred.order() + if not o.divides(n): + n = n.lcm(o) + no_progress = 0 + else: + no_progress += 1 + + if n >= bound: + return True + if no_progress >= attempts: + return + + assert False # unreachable unless there are only finitely many primes + def is_on_identity_component(self, embedding=None): r""" Return ``True`` iff this point is on the identity component of diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 2d5da94a8bd..d00187d49a2 100755 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -157,6 +157,13 @@ def __init__(self, ainvs, **kwds): TESTS: + Passing unexpected keyword arguments will raise an error:: + + sage: EllipticCurve.create_object(0, (QQ, (1, 2, 0, 1, 2)), base=QQ) + Traceback (most recent call last): + ... + TypeError: unexpected keyword arguments: {'base': Rational Field} + When constructing a curve from the large database using a label, we must be careful that the copied generators have the right curve (see :issue:`10999`: the following used not to work when @@ -181,21 +188,23 @@ def __init__(self, ainvs, **kwds): EllipticCurve_number_field.__init__(self, Q, ainvs) if 'conductor' in kwds: - self._set_conductor(kwds['conductor']) + self._set_conductor(kwds.pop('conductor')) if 'cremona_label' in kwds: - self._set_cremona_label(kwds['cremona_label']) + self._set_cremona_label(kwds.pop('cremona_label')) if 'gens' in kwds: - self._set_gens(kwds['gens']) + self._set_gens(kwds.pop('gens')) if 'lmfdb_label' in kwds: - self._lmfdb_label = kwds['lmfdb_label'] + self._lmfdb_label = kwds.pop('lmfdb_label') if 'modular_degree' in kwds: - self._set_modular_degree(kwds['modular_degree']) + self._set_modular_degree(kwds.pop('modular_degree')) if 'rank' in kwds: - self._set_rank(kwds['rank']) + self._set_rank(kwds.pop('rank')) if 'regulator' in kwds: - self.__regulator = (kwds['regulator'], True) + self.__regulator = (kwds.pop('regulator'), True) if 'torsion_order' in kwds: - self._set_torsion_order(kwds['torsion_order']) + self._set_torsion_order(kwds.pop('torsion_order')) + if kwds: + raise TypeError(f"unexpected keyword arguments: {kwds}") def _set_rank(self, r): r""" @@ -1828,6 +1837,10 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, ... DeprecationWarning: Use E.rank(algorithm="pari") instead, as this script has been ported over to pari. See https://github.com/sagemath/sage/issues/35621 for details. + doctest:warning + ... + DeprecationWarning: please use the 2-descent algorithm over QQ inside pari + See https://github.com/sagemath/sage/issues/38461 for details. (0, 0, []) sage: E = EllipticCurve('37a1') sage: E.simon_two_descent() @@ -1928,7 +1941,7 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, return rank_low_bd, two_selmer_rank, pts - two_descent_simon = simon_two_descent + two_descent_simon = simon_two_descent # deprecated in #35621 def three_selmer_rank(self, algorithm='UseSUnits'): r""" @@ -2379,21 +2392,9 @@ def _compute_gens(self, proof, TESTS:: sage: P = E.lift_x(611429153205013185025/9492121848205441) - sage: set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) <= set([P+T for T - ....: in E.torsion_points()] + [-P+T for T in E.torsion_points()]) - True - - sage: E = EllipticCurve([-157^2,0]) - sage: E.gens(use_database=False, algorithm='pari') - Traceback (most recent call last): - ... - RuntimeError: generators could not be determined. So far we found []. Hint: increase pari_effort. - sage: ge = E.gens(use_database=False, algorithm='pari',pari_effort=10) - sage: ge #random - [(-166136231668185267540804/2825630694251145858025 : 167661624456834335404812111469782006/150201095200135518108761470235125 : 1)] - sage: P = E.lift_x(-166136231668185267540804/2825630694251145858025) - sage: set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) <= set([P+T for T - ....: in E.torsion_points()] + [-P+T for T in E.torsion_points()]) + sage: ge = set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) + sage: ge <= set([P+T for T in E.torsion_points()] + ....: + [-P+T for T in E.torsion_points()]) True """ # If the optional extended database is installed and an @@ -2693,7 +2694,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): .. NOTE:: - In versons of ``eclib`` up to ``v20190909``, division of + In versions of ``eclib`` up to ``v20190909``, division of points in ``eclib`` was done using floating point methods, without automatic handling of precision, so that `p`-saturation sometimes failed unless @@ -6200,10 +6201,11 @@ def point_preprocessing(free,tor): e1,e2,e3 = ei if r >= 1: #preprocessing of mw_base only necessary if rank > 0 mw_base = point_preprocessing(mw_base, tors_points) - #at most one point in E^{egg} + # at most one point in E^{egg} - elif disc < 0: # one real component => 1 root in RR (=: e3), - # 2 roots in C (e1,e2) + elif disc < 0: + # one real component => 1 root in RR (=: e3), + # 2 roots in C (e1,e2) roots = pol.roots(C,multiplicities=False) e3 = pol.roots(R,multiplicities=False)[0] roots.remove(e3) @@ -6295,8 +6297,7 @@ def point_preprocessing(free,tor): c1_LLL = -R.one() for i in range(n): tmp = R(b1_norm/(m_gram.row(i).norm())) - if tmp > c1_LLL: - c1_LLL = tmp + c1_LLL = max(tmp, c1_LLL) if c1_LLL < 0: raise RuntimeError('Unexpected intermediate result. Please try another Mordell-Weil base') @@ -6621,8 +6622,7 @@ def reduction_at(p): c1_LLL = -R.one() for i in range(n): tmp = R(b1_norm/(m_gram.row(i).norm())) - if tmp > c1_LLL: - c1_LLL = tmp + c1_LLL = max(tmp, c1_LLL) if c1_LLL < 0: raise RuntimeError('Unexpected intermediate result. Please try another Mordell-Weil base') d_L_0 = R(b1_norm**2 / c1_LLL) @@ -6686,16 +6686,16 @@ def test_with_T(R): for T in tors_points: test(R+T) - # For small rank and small H_q perform simple search + # For small rank and small H_q perform simple search if r == 1 and N <= 10: for P in multiples(mw_base[0],N+1): test_with_T(P) return xs - # explicit computation and testing linear combinations - # ni loops through all tuples (n_1,...,n_r) with |n_i| <= N - # stops when (0,0,...,0) is reached because after that, only inverse points of - # previously tested points would be tested + # explicit computation and testing linear combinations + # ni loops through all tuples (n_1,...,n_r) with |n_i| <= N + # stops when (0,0,...,0) is reached because after that, only inverse points of + # previously tested points would be tested E0 = E(0) ni = [-N for i in range(r)] @@ -6848,13 +6848,14 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): prec *= 2 RR = RealField(prec) ei = pol.roots(RR,multiplicities=False) - e1,e2,e3 = ei - elif disc < 0: # one real component => 1 root in RR (=: e3), - # 2 roots in C (e1,e2) + e1, e2, e3 = ei + elif disc < 0: + # one real component => 1 root in RR (=: e3), + # 2 roots in C (e1,e2) roots = pol.roots(C,multiplicities=False) e3 = pol.roots(R,multiplicities=False)[0] roots.remove(e3) - e1,e2 = roots + e1, e2 = roots len_tors = len(tors_points) n = r + 1 @@ -6887,10 +6888,10 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): if verbose: print('k1,k2,k3,k4', k1, k2, k3, k4) sys.stdout.flush() - #H_q -> [PZGH]:N_0 (due to consistency to integral_points()) + # H_q -> [PZGH]:N_0 (due to consistency to integral_points()) H_q = R(((k1/2+k2)/lamda).sqrt()) - #computation of logs + # computation of logs mw_base_log = [(pts.elliptic_logarithm().abs())*(len_tors/w1) for pts in mw_base] mw_base_p_log = [] beta = [] @@ -6900,7 +6901,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): Np = E.Np(p) cp = E.tamagawa_exponent(p) mp_temp = Z(len_tors).lcm(cp*Np) - mp.append(mp_temp) #only necessary because of verbose below + mp.append(mp_temp) # only necessary because of verbose below p_prec = 30+E.discriminant().valuation(p) p_prec_ok = False while not p_prec_ok: @@ -6911,7 +6912,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): p_prec_ok = True except ValueError: p_prec *= 2 - #reorder mw_base_p: last value has minimal valuation at p + # reorder mw_base_p: last value has minimal valuation at p mw_base_p_log_val = [mw_base_p_log[tmp][i].valuation() for i in range(r)] if verbose: print("mw_base_p_log_val = ",mw_base_p_log_val) @@ -6921,7 +6922,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): print("min_psi = ", min_psi) mw_base_p_log[tmp].remove(min_psi) mw_base_p_log[tmp].append(min_psi) - #beta needed for reduction at p later on + # beta needed for reduction at p later on try: beta.append([-mw_base_p_log[tmp][j]/min_psi for j in range(r)]) except ValueError: @@ -6936,7 +6937,8 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): print('mw_base_p_log', mw_base_p_log) sys.stdout.flush() - #constants in reduction (not needed to be computed every reduction step) + # constants in reduction + # (not needed to be computed every reduction step) k5 = R((2*len_tors)/(3*w1)) k6 = R((k2/len_S).exp()) k7 = R(lamda/len_S) @@ -6947,20 +6949,20 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): break_cond = 0 M = MatrixSpace(Z,n) - #Reduction of initial bound + # Reduction of initial bound if verbose: print('initial bound', H_q) sys.stdout.flush() while break_cond < 0.9: - #reduction at infinity + # reduction at infinity bound_list = [] c = R((H_q**n)*100) m = copy(M.identity_matrix()) for i in range(r): m[i, r] = R(c*mw_base_log[i]).round() m[r,r] = max(Z(1), R(c*w1).round()) - #LLL - implemented in sage - operates on rows not on columns + # LLL - implemented in sage - operates on rows not on columns m_LLL = m.LLL() m_gram = m_LLL.gram_schmidt()[0] b1_norm = R(m_LLL.row(0).norm()) @@ -6969,8 +6971,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): c1_LLL = -R.one() for i in range(n): tmp = R(b1_norm/(m_gram.row(i).norm())) - if tmp > c1_LLL: - c1_LLL = tmp + c1_LLL = max(tmp, c1_LLL) if c1_LLL < 0: raise RuntimeError('Unexpected intermediate result. Please try another Mordell-Weil base') d_L_0 = R(b1_norm**2 / c1_LLL) @@ -7097,6 +7098,7 @@ def cremona_curves(conductors): conductors = [conductors] return sage.databases.cremona.CremonaDatabase().iter(conductors) + def cremona_optimal_curves(conductors): r""" Return iterator over all known optimal curves (in database) with @@ -7123,6 +7125,7 @@ def cremona_optimal_curves(conductors): conductors = [conductors] return sage.databases.cremona.CremonaDatabase().iter_optimal(conductors) + def integral_points_with_bounded_mw_coeffs(E, mw_base, N, x_bound): r""" Return the set of integers `x` which are diff --git a/src/sage/schemes/elliptic_curves/ell_wp.py b/src/sage/schemes/elliptic_curves/ell_wp.py index a1a5c8fb5fe..5c8e7930830 100755 --- a/src/sage/schemes/elliptic_curves/ell_wp.py +++ b/src/sage/schemes/elliptic_curves/ell_wp.py @@ -54,6 +54,7 @@ # Note: Part of the documentation is replicated in ell_field.py for # users' convenience. Make sure to keep the two copies synchronized. + def weierstrass_p(E, prec=20, algorithm=None): r""" Compute the Weierstrass `\wp`-function on an elliptic curve. @@ -165,6 +166,7 @@ def weierstrass_p(E, prec=20, algorithm=None): u = E.isomorphism_to(Esh).u return wp(z*u) * u**2 + def compute_wp_pari(E,prec): r""" Compute the Weierstrass `\wp`-function with the ``ellwp`` function @@ -251,6 +253,7 @@ def compute_wp_quadratic(k, A, B, prec): return pe(Z**2).add_bigoh(prec) + def compute_wp_fast(k, A, B, m): r""" Compute the Weierstrass function of an elliptic curve defined by short Weierstrass model: 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 accde11b636..a4eb66a6df7 100755 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -422,6 +422,7 @@ def reducible_primes(self): return [l for l in self.isogeny_bound() if self.E.isogenies_prime_degree(l)] + def _non_surjective(E, patience=100): r""" Return a list of primes `p` including all primes for which the mod-`p` @@ -775,6 +776,7 @@ def _over_numberfield(E): K = K.absolute_field('a') return E.change_ring(K) + def deg_one_primes_iter(K, principal_only=False): r""" Return an iterator over degree 1 primes of ``K``. @@ -823,6 +825,7 @@ def deg_one_primes_iter(K, principal_only=False): if not principal_only or P.is_principal(): yield P + def _semistable_reducible_primes(E, verbose=False): r"""Find a list containing all semistable primes l unramified in K/QQ for which the Galois image for E could be reducible. @@ -1151,6 +1154,7 @@ def _possible_normalizers(E, SA): # elliptiques", Nicolas Billerey, https://arxiv.org/abs/0908.1084 # + def Billerey_P_l(E, l): r""" Return Billerey's `P_l^*` as defined in [Bil2011]_, equation (9). @@ -1186,6 +1190,7 @@ def Billerey_P_l(E, l): P = P.composed_op(E.reduction(q).frobenius_polynomial().adams_operator_on_roots(12*e), mul, monic=True) return P + def Billerey_B_l(E,l,B=0): r""" Return Billerey's `B_l`, adapted from the definition in [Bil2011]_, after (9). diff --git a/src/sage/schemes/elliptic_curves/gp_simon.py b/src/sage/schemes/elliptic_curves/gp_simon.py index 39a60361ec6..6be377e2f74 100644 --- a/src/sage/schemes/elliptic_curves/gp_simon.py +++ b/src/sage/schemes/elliptic_curves/gp_simon.py @@ -16,31 +16,20 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from pathlib import Path -from sage.structure.parent_gens import localvars +from cypari2.handle_error import PariError -from sage.interfaces.gp import Gp -from sage.misc.sage_eval import sage_eval +from sage.env import SAGE_EXTCODE +from sage.libs.pari import pari from sage.misc.randstate import current_randstate +from sage.misc.superseded import deprecation from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ +from sage.structure.parent_gens import localvars -gp = None -def init(): - """ - Function to initialize the gp process - """ - global gp - if gp is None: - import os - from sage.env import DOT_SAGE - logfile = os.path.join(DOT_SAGE, 'gp-simon.log') - gp = Gp(script_subdirectory='simon', logfile=logfile) - gp.read("ellQ.gp") - gp.read("ell.gp") - gp.read("qfsolve.gp") - gp.read("resultant3.gp") +simon_dir = Path(SAGE_EXTCODE) / 'pari' / 'simon' def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, @@ -57,6 +46,9 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, sage: import sage.schemes.elliptic_curves.gp_simon sage: E = EllipticCurve('389a1') sage: sage.schemes.elliptic_curves.gp_simon.simon_two_descent(E) + doctest:warning...: + DeprecationWarning: please use the 2-descent algorithm over QQ inside pari + See https://github.com/sagemath/sage/issues/38461 for details. (2, 2, [(5/4 : 5/8 : 1), (-3/4 : 7/8 : 1)]) TESTS:: @@ -92,37 +84,38 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, sage: E.simon_two_descent() # long time (0, 2, []) """ - init() + pari.read(simon_dir / "ellQ.gp") + pari.read(simon_dir / "ell.gp") + pari.read(simon_dir / "qfsolve.gp") + pari.read(simon_dir / "resultant3.gp") - current_randstate().set_seed_gp(gp) + current_randstate().set_seed_pari() K = E.base_ring() K_orig = K + E_orig = E + # The following is to correct the bug at #5204: the gp script # fails when K is a number field whose generator is called 'x'. # It also deals with relative number fields. - E_orig = E + if K is not QQ: K = K_orig.absolute_field('a') + y = K.gen() from_K, to_K = K.structure() E = E_orig.change_ring(to_K) - known_points = [P.change_ring(to_K) for P in known_points] - # Simon's program requires that this name be y. with localvars(K.polynomial().parent(), 'y'): - gp.eval("K = bnfinit(%s);" % K.polynomial()) - if verbose >= 2: - print("K = bnfinit(%s);" % K.polynomial()) - gp.eval("%s = Mod(y,K.pol);" % K.gen()) - if verbose >= 2: - print("%s = Mod(y,K.pol);" % K.gen()) + # Simon's program requires that this name be y. + K_pari = pari.bnfinit(K.polynomial()) + known_points = [P.change_ring(to_K) for P in known_points] else: + deprecation(38461, "please use the 2-descent algorithm over QQ inside pari") from_K = lambda x: x - to_K = lambda x: x - # The block below mimics the defaults in Simon's scripts, and needs to be changed - # when these are updated. + # The block below mimics the defaults in Simon's scripts. + # They need to be changed when these are updated. if K is QQ: - cmd = 'ellQ_ellrank(%s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) + over_QQ = True if lim1 is None: lim1 = 5 if lim3 is None: @@ -130,7 +123,7 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, if limtriv is None: limtriv = 3 else: - cmd = 'bnfellrank(K, %s, %s);' % (list(E.ainvs()), [P.__pari__() for P in known_points]) + over_QQ = False if lim1 is None: lim1 = 2 if lim3 is None: @@ -138,29 +131,21 @@ def simon_two_descent(E, verbose=0, lim1=None, lim3=None, limtriv=None, if limtriv is None: limtriv = 2 - gp('DEBUGLEVEL_ell=%s; LIM1=%s; LIM3=%s; LIMTRIV=%s; MAXPROB=%s; LIMBIGPRIME=%s;' % ( - verbose, lim1, lim3, limtriv, maxprob, limbigprime)) - - if verbose >= 2: - print(cmd) - s = gp.eval('ans=%s;' % cmd) - if s.find(" *** ") != -1: - raise RuntimeError("\n%s\nAn error occurred while running Simon's 2-descent program" % s) - if verbose > 0: - print(s) - v = gp.eval('ans') - if v == 'ans': # then the call to ellQ_ellrank() or bnfellrank() failed - raise RuntimeError("An error occurred while running Simon's 2-descent program") - if verbose >= 2: - print("v = %s" % v) - - # pari represents field elements as Mod(poly, defining-poly) - # so this function will return the respective elements of K - def _gp_mod(*args): - return args[0] - ans = sage_eval(v, {'Mod': _gp_mod, 'y': K.gen(0)}) - lower = ZZ(ans[0]) - upper = ZZ(ans[1]) - points = [E_orig([from_K(c) for c in list(P)]) for P in ans[2]] + pari('DEBUGLEVEL_ell=%s; LIM1=%s; LIM3=%s; LIMTRIV=%s; MAXPROB=%s; LIMBIGPRIME=%s;' % ( + verbose, lim1, lim3, limtriv, maxprob, limbigprime)) + + try: + if over_QQ: + ans = pari("ellQ_ellrank")(E, known_points) + else: + ans = pari("bnfellrank")(K_pari, E, known_points) + except PariError as err: + raise RuntimeError("an error occurred while running Simon's 2-descent program") from err + + loc = {} if over_QQ else {'y': y} + lower, upper, pts = ans.sage(locals=loc) + lower = ZZ(lower) + upper = ZZ(upper) + points = [E_orig([from_K(c) for c in P]) for P in pts] points = [P for P in points if P.has_infinite_order()] return lower, upper, points diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index fb5306f32b3..1bff085023c 100755 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -138,6 +138,7 @@ # ############################################################################### + def heegner_points(N, D=None, c=None): """ Return all Heegner points of given level `N`. Can also restrict @@ -597,6 +598,7 @@ def is_subfield(self, M): # ################################################################################## + class GaloisGroup(SageObject): """ A Galois group of a ring class field. @@ -2111,6 +2113,7 @@ def discriminants(self, n=10, weak=False): v.append(D) return v + class HeegnerPoints_level_disc(HeegnerPoints): """ Set of Heegner points of given level and all conductors associated @@ -4003,6 +4006,8 @@ def kolyvagin_cohomology_class(self, n=None): ######################################################################################### # Kolyvagin Points P_c ######################################################################################### + + class KolyvaginPoint(HeegnerPoint): """ A Kolyvagin point. @@ -4612,6 +4617,7 @@ def heegner_point(self): """ return self.__kolyvagin_point.heegner_point() + class KolyvaginCohomologyClassEn(KolyvaginCohomologyClass): def _repr_(self): """ @@ -4904,8 +4910,8 @@ def right_ideals(self): EXAMPLES:: sage: heegner_points(11).reduce_mod(3).right_ideals() - (Fractional ideal (2 + 2*j + 28*k, 2*i + 26*k, 4*j + 12*k, 44*k), - Fractional ideal (2 + 2*j + 28*k, 2*i + 4*j + 38*k, 8*j + 24*k, 88*k)) + (Fractional ideal (4, 44*i, 2 + 8*i + 2*j, 34*i + 2*k), + Fractional ideal (8, 88*i, 2 + 52*i + 2*j, 4 + 78*i + 2*k)) """ return self.brandt_module().right_ideals() @@ -5135,14 +5141,10 @@ def cyclic_subideal_p1(self, I, c): sage: H = heegner_points(11).reduce_mod(7) sage: I = H.brandt_module().right_ideals()[0] sage: sorted(H.cyclic_subideal_p1(I, 3).items()) - [((0, 1), - Fractional ideal (2 + 2*j + 32*k, 2*i + 8*j + 82*k, 12*j + 60*k, 132*k)), - ((1, 0), - Fractional ideal (2 + 10*j + 28*k, 2*i + 4*j + 62*k, 12*j + 60*k, 132*k)), - ((1, 1), - Fractional ideal (2 + 2*j + 76*k, 2*i + 4*j + 106*k, 12*j + 60*k, 132*k)), - ((1, 2), - Fractional ideal (2 + 10*j + 116*k, 2*i + 8*j + 38*k, 12*j + 60*k, 132*k))] + [((0, 1), Fractional ideal (12, 132*i, 10 + 76*i + 2*j, 4 + 86*i + 2*k)), + ((1, 0), Fractional ideal (12, 132*i, 2 + 32*i + 2*j, 8 + 130*i + 2*k)), + ((1, 1), Fractional ideal (12, 132*i, 10 + 32*i + 2*j, 8 + 86*i + 2*k)), + ((1, 2), Fractional ideal (12, 132*i, 2 + 76*i + 2*j, 4 + 130*i + 2*k))] sage: len(H.cyclic_subideal_p1(I, 17)) 18 """ @@ -5267,24 +5269,12 @@ def kolyvagin_cyclic_subideals(self, I, p, alpha_quaternion): sage: alpha_quaternion = f(g[0]); alpha_quaternion 1 - 77/192*i - 5/128*j - 137/384*k sage: H.kolyvagin_cyclic_subideals(I, 5, alpha_quaternion) - [(Fractional ideal (2 + 2/3*i + 364*j + 231928/3*k, - 4/3*i + 946*j + 69338/3*k, - 1280*j + 49920*k, 94720*k), 0), - (Fractional ideal (2 + 2/3*i + 108*j + 31480/3*k, - 4/3*i + 434*j + 123098/3*k, - 1280*j + 49920*k, 94720*k), 1), - (Fractional ideal (2 + 2/3*i + 876*j + 7672/3*k, - 4/3*i + 434*j + 236762/3*k, - 1280*j + 49920*k, 94720*k), 2), - (Fractional ideal (2 + 2/3*i + 364*j + 61432/3*k, - 4/3*i + 178*j + 206810/3*k, - 1280*j + 49920*k, 94720*k), 3), - (Fractional ideal (2 + 2/3*i + 876*j + 178168/3*k, - 4/3*i + 1202*j + 99290/3*k, - 1280*j + 49920*k, 94720*k), 4), - (Fractional ideal (2 + 2/3*i + 1132*j + 208120/3*k, - 4/3*i + 946*j + 183002/3*k, - 1280*j + 49920*k, 94720*k), 5)] + [(Fractional ideal (2560, 1280 + 47360*i, 1146 + 37678*i + 4*j, 212 + 54664/3*i + 2*j + 2/3*k), 0), + (Fractional ideal (2560, 1280 + 47360*i, 2426 + 9262*i + 4*j, 2004 + 83080/3*i + 2*j + 2/3*k), 1), + (Fractional ideal (2560, 1280 + 47360*i, 1914 + 9262*i + 4*j, 1748 + 111496/3*i + 2*j + 2/3*k), 2), + (Fractional ideal (2560, 1280 + 47360*i, 2170 + 18734*i + 4*j, 212 + 111496/3*i + 2*j + 2/3*k), 3), + (Fractional ideal (2560, 1280 + 47360*i, 890 + 28206*i + 4*j, 1748 + 54664/3*i + 2*j + 2/3*k), 4), + (Fractional ideal (2560, 1280 + 47360*i, 634 + 37678*i + 4*j, 2516 + 83080/3*i + 2*j + 2/3*k), 5)] """ X = I.cyclic_right_subideals(p, alpha_quaternion) return [(J, i) for i, J in enumerate(X)] @@ -5630,6 +5620,7 @@ def kolyvagin_point_on_curve(self, D, c, E, p, bound=10): V = self.modp_dual_elliptic_curve_factor(E, p, bound) return [b.dot_product(k.element().change_ring(GF(p))) for b in V.basis()] + def kolyvagin_reduction_data(E, q, first_only=True): r""" Given an elliptic curve of positive rank and a prime `q`, this @@ -5836,6 +5827,7 @@ def kernel_of_reduction(ell): BrandtModule(ell_1,N).dimension(), BrandtModule(ell_2,N).dimension()) + class HeegnerQuatAlgEmbedding(SageObject): r""" The homomorphism `\mathcal{O} \to R`, where `\mathcal{O}` is the @@ -6119,6 +6111,7 @@ def quadratic_order(D, c, names='a'): R = K.order([t]) return R, R(t) + def class_number(D): """ Return the class number of the quadratic field with fundamental @@ -6149,6 +6142,7 @@ def class_number(D): raise ValueError("D (=%s) must be a fundamental discriminant" % D) return D.class_number() + def is_inert(D, p): r""" Return ``True`` if p is an inert prime in the field `\QQ(\sqrt{D})`. @@ -6172,6 +6166,7 @@ def is_inert(D, p): F = K.factor(p) return len(F) == 1 and F[0][1] == 1 + def is_split(D, p): r""" Return ``True`` if p is a split prime in the field `\QQ(\sqrt{D})`. @@ -6195,6 +6190,7 @@ def is_split(D, p): F = K.factor(p) return len(F) == 2 + def is_ramified(D, p): r""" Return ``True`` if p is a ramified prime in the field `\QQ(\sqrt{D})`. @@ -6216,6 +6212,7 @@ def is_ramified(D, p): """ return QuadraticField(D,'a').discriminant() % p == 0 + def nearby_rational_poly(f, **kwds): r""" Return a polynomial whose coefficients are rational numbers close @@ -6241,6 +6238,7 @@ def nearby_rational_poly(f, **kwds): R = QQ['X'] return R([a.nearby_rational(**kwds) for a in f]) + def simplest_rational_poly(f, prec): """ Return a polynomial whose coefficients are as simple as possible @@ -6263,6 +6261,7 @@ def simplest_rational_poly(f, prec): Z = RealField(prec) return R([Z(a).simplest_rational() for a in f]) + def satisfies_weak_heegner_hypothesis(N, D): r""" Check that `D` satisfies the weak Heegner hypothesis relative to `N`. @@ -6438,6 +6437,7 @@ def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True): y = HeegnerPointOnX0N(self.conductor(), D, c, f, check=check) return y.map_to_curve(self) + def kolyvagin_point(self, D, c=ZZ(1), check=True): r""" Return the Kolyvagin point on this curve associated to the @@ -6470,6 +6470,7 @@ def kolyvagin_point(self, D, c=ZZ(1), check=True): """ return self.heegner_point(D,c,check=check).kolyvagin_point() + def ell_heegner_discriminants(self, bound): """ Return the list of ``self``'s Heegner discriminants between -1 and @@ -6519,6 +6520,7 @@ def ell_heegner_discriminants_list(self, n): D -= 1 return v + def heegner_point_height(self, D, prec=2, check_rank=True): r""" Use the Gross-Zagier formula to compute the Neron-Tate canonical @@ -7050,6 +7052,7 @@ def _heegner_index_in_EK(self, D): self.__heegner_index_in_EK[D] = index return index + def heegner_sha_an(self, D, prec=53): r""" Return the conjectural (analytic) order of Sha for E over the field `K=\QQ(\sqrt{D})`. @@ -7261,6 +7264,7 @@ def _heegner_forms_list(self, D, beta=None, expected_count=None): return all b += 2*N + def _heegner_best_tau(self, D, prec=None): r""" Given a discriminant `D`, find the Heegner point `\tau` in the @@ -7285,6 +7289,7 @@ def _heegner_best_tau(self, D, prec=None): # TODO: make sure a different choice of b is not better? return (-b + ZZ(D).sqrt(prec=prec)) / (2*N) + def satisfies_heegner_hypothesis(self, D): """ Return ``True`` precisely when `D` is a fundamental discriminant that diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index 88447c3bdbd..53f440f5db4 100755 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -514,6 +514,7 @@ def nonneg_region(f): sign_changes += [infinity] return UnionOfIntervals(sign_changes) + def inf_max_abs(f, g, D): r""" Return `\inf_D(\max(|f|, |g|))`. diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 7bf61914f9a..22f87bfeed0 100755 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -270,6 +270,13 @@ def _richcmp_(self, other, op): if lx != rx: return richcmp_not_equal(lx, rx, op) + # Check the Weierstraß scaling factor, too (should be fast) + + if op == op_EQ or op == op_NE: + lx, rx = self.scaling_factor(), other.scaling_factor() + if lx != rx: + return richcmp_not_equal(lx, rx, op) + # Do self or other have specialized comparison methods? ret = self._comparison_impl(self, other, op) @@ -1118,10 +1125,8 @@ def matrix_on_subgroup(self, domain_gens, codomain_gens=None): imP = self._eval(P) imQ = self._eval(Q) - from sage.groups.additive_abelian.additive_abelian_wrapper import AdditiveAbelianGroupWrapper - H = AdditiveAbelianGroupWrapper(R.parent(), [R,S], [n,n]) - vecP = H.discrete_log(imP) - vecQ = H.discrete_log(imQ) + vecP = imP.log([R, S]) + vecQ = imQ.log([R, S]) from sage.matrix.constructor import matrix from sage.rings.finite_rings.integer_mod_ring import Zmod @@ -1175,21 +1180,30 @@ def compare_via_evaluation(left, right): E = left.domain() F = E.base_ring() + d = left.degree() if isinstance(F, finite_field_base.FiniteField): + # check at a random rational point first + P = E.random_point() + if left(P) != right(P): + return False + + # then extend to a field with enough points to conclude q = F.cardinality() - d = left.degree() e = integer_floor(1 + 2 * (2*d.sqrt() + 1).log(q)) # from Hasse bound e = next(i for i, n in enumerate(E.count_points(e+1), 1) if n > 4*d) EE = E.base_extend(F.extension(e, 'U')) # named extension is faster Ps = EE.gens() return all(left._eval(P) == right._eval(P) for P in Ps) + elif isinstance(F, number_field_base.NumberField): for _ in range(100): P = E.lift_x(F.random_element(), extend=True) - if not P.has_finite_order(): + if P._has_order_at_least(4*d + 1, attempts=50): + # if P.height(precision=250) == 0: # slow sometimes return left._eval(P) == right._eval(P) else: - assert False, "couldn't find a point of infinite order" + assert False, "couldn't find a point of large enough order" + else: raise NotImplementedError('not implemented for this base field') diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index 6952f2a0e11..0ebfe6261fa 100755 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -190,7 +190,7 @@ def _compute_factored_isogeny_prime_power(P, l, n, split=.8, velu_sqrt_bound=Non sage: P, = E.gens() sage: (l,n), = P.order().factor() sage: phis = hom_composite._compute_factored_isogeny_prime_power(P,l,n) - sage: phis == hom_composite._compute_factored_isogeny_prime_power(P,l,n, split=0) + sage: phis == hom_composite._compute_factored_isogeny_prime_power(P,l,n, split=0) # long time -- about 10s True sage: phis == hom_composite._compute_factored_isogeny_prime_power(P,l,n, split=0.1) True diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index c7c6e434ce5..d8cf1971f45 100755 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -135,6 +135,7 @@ from .ell_finite_field import EllipticCurve_finite_field from .hom import EllipticCurveHom, compare_via_evaluation + class _VeluBoundObj: """ Helper object to define the point in which isogeny @@ -165,6 +166,7 @@ def __repr__(self): _velu_sqrt_bound = _VeluBoundObj() + def _choose_IJK(n): r""" Helper function to choose an "index system" for the set @@ -201,6 +203,7 @@ def _choose_IJK(n): K = range(4*b*c+1, n, 2) return I, J, K + def _points_range(rr, P, Q=None): r""" Return an iterator yielding all points `Q + [i]P` where `i` runs diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index 2c710b09f0b..13edc68a022 100755 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -1319,6 +1319,7 @@ def isogeny_degrees_cm(E, verbose=False): print("List of primes after filtering: %s" % L) return L + def possible_isogeny_degrees(E, algorithm='Billerey', max_l=None, num_l=None, exact=True, verbose=False): r""" diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index 92199fb497e..6a0194fb0f9 100755 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -45,6 +45,7 @@ # only finitely many `j`-invariants each. are also implemented. ########################################################################## + @cached_function def Fricke_polynomial(l): r""" @@ -813,6 +814,7 @@ def isogenies_3(E, minimal_models=True): # 6 special cases: `l` = 5, 7, 13 and `j` = 0, 1728. + def isogenies_5_0(E, minimal_models=True): r""" Return a list of all the 5-isogenies with domain ``E`` when the @@ -916,6 +918,7 @@ def isogenies_5_0(E, minimal_models=True): isogs = [isog * iso for isog in isogs] return isogs + def isogenies_5_1728(E, minimal_models=True): r""" Return a list of 5-isogenies with domain ``E`` when the j-invariant is @@ -1051,6 +1054,7 @@ def isogenies_5_1728(E, minimal_models=True): isogs = [isog * iso for isog in isogs] return isogs + def isogenies_7_0(E, minimal_models=True): r""" Return list of all 7-isogenies from E when the j-invariant is 0. @@ -1191,6 +1195,7 @@ def isogenies_7_0(E, minimal_models=True): isogs = [isog * iso for isog in isogs] return isogs + def isogenies_7_1728(E, minimal_models=True): r""" Return list of all 7-isogenies from E when the j-invariant is 1728. @@ -1290,6 +1295,7 @@ def isogenies_7_1728(E, minimal_models=True): isogs = [isog * iso for isog in isogs] return isogs + def isogenies_13_0(E, minimal_models=True): """ Return list of all 13-isogenies from E when the j-invariant is 0. @@ -1599,6 +1605,7 @@ def isogenies_13_1728(E, minimal_models=True): hyperelliptic_primes = [11, 17, 19, 23, 29, 31, 41, 47, 59, 71] + @cached_function def _hyperelliptic_isogeny_data(l): r""" diff --git a/src/sage/schemes/elliptic_curves/meson.build b/src/sage/schemes/elliptic_curves/meson.build new file mode 100644 index 00000000000..3448c5d1c0a --- /dev/null +++ b/src/sage/schemes/elliptic_curves/meson.build @@ -0,0 +1,71 @@ +py.install_sources( + 'BSD.py', + 'Qcurves.py', + 'all.py', + 'cardinality.py', + 'cm.py', + 'constructor.py', + 'ec_database.py', + 'ell_curve_isogeny.py', + 'ell_egros.py', + 'ell_field.py', + 'ell_finite_field.py', + 'ell_generic.py', + 'ell_local_data.py', + 'ell_modular_symbols.py', + 'ell_number_field.py', + 'ell_padic_field.py', + 'ell_point.py', + 'ell_rational_field.py', + 'ell_tate_curve.py', + 'ell_torsion.py', + 'ell_wp.py', + 'formal_group.py', + 'gal_reps.py', + 'gal_reps_number_field.py', + 'gp_simon.py', + 'heegner.py', + 'height.py', + 'hom.py', + 'hom_composite.py', + 'hom_frobenius.py', + 'hom_scalar.py', + 'hom_sum.py', + 'hom_velusqrt.py', + 'homset.py', + 'isogeny_class.py', + 'isogeny_small_degree.py', + 'jacobian.py', + 'kodaira_symbol.py', + 'kraus.py', + 'lseries_ell.py', + 'mod5family.py', + 'mod_poly.py', + 'modular_parametrization.py', + 'padic_lseries.py', + 'padics.py', + 'period_lattice.py', + 'saturation.py', + 'sha_tate.py', + 'weierstrass_morphism.py', + 'weierstrass_transform.py', + subdir: 'sage/schemes/elliptic_curves', +) + +extension_data = { + 'descent_two_isogeny' : files('descent_two_isogeny.pyx'), + 'mod_sym_num' : files('mod_sym_num.pyx'), + 'period_lattice_region' : files('period_lattice_region.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/schemes/elliptic_curves', + install: true, + include_directories: [inc_cpython, inc_flint, inc_numpy, inc_rings], + dependencies: [py_dep, cypari2, cysignals, flint, gmp, mpfr, pari], + ) +endforeach + diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 927df5e5a42..e55500f0d0f 100755 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -256,9 +256,9 @@ cdef llong llxgcd(llong a, llong b, llong *ss, llong *tt) except -1: q = 0 r = 0 s = 1 - while (b): + while b: c = a % b - quot = a/b + quot = a // b a = b b = c new_r = p - quot*r @@ -365,9 +365,9 @@ cdef int proj_normalise(llong N, llong u, llong v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. if g != 1: - d = N / g + d = N // g while llgcd(s, N) != 1: - s = (s+d) % N + s = (s + d) % N # verbose(" now g=%s, s=%s, t=%s" % (g,s,t), level=5) # Multiply [u,v] by s; then [s*u,s*v] = [g,s*v] (mod N) @@ -376,7 +376,7 @@ cdef int proj_normalise(llong N, llong u, llong v, min_v = v min_t = 1 if g != 1: - Ng = N / g + Ng = N // g vNg = (v * Ng) % N t = 1 k = 2 @@ -456,24 +456,24 @@ cdef int best_proj_point(llong u, llong v, llong N, else: # cases like (p:q) mod p*q drop here p = llgcd(u, N) q = llgcd(v, N) - Nnew = N / p / q - w = ((u/p) * llinvmod(v/q, Nnew)) % Nnew - y0 = N/q + Nnew = (N // p) // q + w = ((u // p) * llinvmod(v // q, Nnew)) % Nnew + y0 = N // q y1 = 0 x0 = w*p x1 = q # y will always be the longer and x the shorter - while llabs(x0) + llabs(x1) < llabs(y0)+llabs(y1): + while llabs(x0) + llabs(x1) < llabs(y0) + llabs(y1): if llsign(x0) == llsign(x1): - r = (y0+y1) / (x0+x1) + r = (y0+y1) // (x0+x1) else: - r = (y0-y1) / (x0-x1) + r = (y0-y1) // (x0-x1) t0 = y0 - r * x0 t1 = y1 - r * x1 s0 = t0 - x0 s1 = t1 - x1 - if llabs(s0)+llabs(s1) < llabs(t0)+llabs(t1): + if llabs(s0) + llabs(s1) < llabs(t0) + llabs(t1): t0 = s0 t1 = s1 # t is now the shortest vector on the line y + RR x @@ -593,7 +593,7 @@ cdef class _CuspsForModularSymbolNumerical: a -= m self._r = Rational((a, m)) B = llgcd(m, N) - self._width = N / B + self._width = N // B self._a = a self._m = m self._N_level = N @@ -625,7 +625,7 @@ cdef class _CuspsForModularSymbolNumerical: # verbose(" enter atkin_lehner for cusp r=%s" % self._r, level=5) Q = self._width B = llgcd(self._m, self._N_level) - c = self._m / B + c = self._m // B if llgcd(Q, B) != 1: raise ValueError("This cusp is not in the Atkin-Lehner " "orbit of oo.") @@ -2125,12 +2125,12 @@ cdef class ModularSymbolNumerical: verbose(" yields %s " % int2c, level=3) ans = int2c + int1c else: # use_partials - g = llgcd(Q,QQ) + g = llgcd(Q, QQ) D = Q * QQ D /= g D *= llabs(a*mm-aa*m) - xi = (Q*aa*u+v*mm) * QQ /g * llsign(a*mm-aa*m) - xixi = (QQ*a*uu+vv*m) * Q /g * llsign(aa*m-a*mm) + xi = (Q*aa*u+v*mm) * (QQ // g) * llsign(a*mm-aa*m) + xixi = (QQ*a*uu+vv*m) * (Q // g) * llsign(aa*m-a*mm) z = Q * QQ * (a*mm-aa*m)**2 ka = self._kappa(D, z, eps/2) twopii = TWOPI * complex("j") @@ -2787,33 +2787,33 @@ cdef class ModularSymbolNumerical: else: # (c:d) = (u:v) but c and d are fairly small # in absolute value - Mu = llgcd(u,N) - Qu = N/Mu - Mv = llgcd(v,N) - Qv = N/Mv - isunitary = (llgcd(Qu,Mu) == 1 and llgcd(Qv,Mv) == 1) + Mu = llgcd(u, N) + Qu = N // Mu + Mv = llgcd(v, N) + Qv = N // Mv + isunitary = (llgcd(Qu, Mu) == 1 and llgcd(Qv, Mv) == 1) if isunitary: # unitary case _ = best_proj_point(u, v, self._N_E, &c, &d) else: # at least one of the two cusps is not unitary du = llgcd(Qu,Mu) - dv = llgcd(Qv,Mv) - NMM = N/Mv/Mu + dv = llgcd(Qv, Mv) + NMM = N // Mv // Mu if dv == 1: c = Mu - d = llinvmod(u/Mu, NMM) + d = llinvmod(u // Mu, NMM) d *= v - d = d % (N/Mu) + d = d % (N // Mu) while llgcd(c,d) != 1: - d += N/Mu + d += N // Mu d = d % N # now (u:v) = (c:d) with c as small as possible. else: d = Mv - c = llinvmod(v/Mv, NMM) + c = llinvmod(v // Mv, NMM) c *= u - c = c % (N/Mv) - while llgcd(c,d) != 1: - c += N/Mv + c = c % (N // Mv) + while llgcd(c, d) != 1: + c += N // Mv c = c % N # now (u:v) = (c:d) with d as small as possible. # verbose(" better representant on P^1: " @@ -3170,8 +3170,8 @@ cdef class ModularSymbolNumerical: RR = RealField(53) N = self._N_E - Q = N / llgcd(m,N) - if llgcd(m,Q) > 1: + Q = N // llgcd(m, N) + if llgcd(m, Q) > 1: raise NotImplementedError("Only implemented for cusps that are " "in the Atkin-Lehner orbit of oo") # verbose(" compute all partial sums with denominator m=%s" % m, diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 4721ef67951..1fcc30764b2 100755 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -629,8 +629,7 @@ def _e_bounds(self, n, prec): res = [enj] for j in range(1,prec): bino = valuation(binomial(pn,j),self._p) - if bino < enj: - enj = bino + enj = min(bino, enj) res.append(enj) return res diff --git a/src/sage/schemes/elliptic_curves/padics.py b/src/sage/schemes/elliptic_curves/padics.py index 624b3a11e7d..87f985e7052 100755 --- a/src/sage/schemes/elliptic_curves/padics.py +++ b/src/sage/schemes/elliptic_curves/padics.py @@ -420,6 +420,7 @@ def padic_height_pairing_matrix(self, p, prec=20, height=None, check_hypotheses= return M + def _multiply_point(E, R, P, m): r""" Compute coordinates of a multiple of `P` with entries in a ring. @@ -578,6 +579,7 @@ def _multiply_point(E, R, P, m): return theta, omega, psi_m * d + def _multiple_to_make_good_reduction(E): r""" Return the integer `n_2` such that for all points `P` in `E(\QQ)` @@ -653,6 +655,7 @@ def _multiple_to_make_good_reduction(E): n2 = LCM(li) return n2 + def padic_height(self, p, prec=20, sigma=None, check_hypotheses=True): r""" Compute the cyclotomic `p`-adic height. diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 9db15a6eba7..e6008a09279 100755 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -413,7 +413,7 @@ def basis(self, prec=None, algorithm='sage'): (tuple of Complex) `(\omega_1,\omega_2)` where the lattice is `\ZZ\omega_1 + \ZZ\omega_2`. If the lattice is real then `\omega_1` is real and positive, `\Im(\omega_2)>0` and - `\Re(\omega_1/\omega_2)` is either `0` (for rectangular + `\Re(\omega_2/\omega_1)` is either `0` (for rectangular lattices) or `\frac{1}{2}` (for non-rectangular lattices). Otherwise, `\omega_1/\omega_2` is in the fundamental region of the upper half-plane. If the latter normalisation is required @@ -498,7 +498,7 @@ def gens(self, prec=None, algorithm='sage'): (tuple of Complex) `(\omega_1,\omega_2)` where the lattice is `\ZZ\omega_1 + \ZZ\omega_2`. If the lattice is real then `\omega_1` is real and positive, `\Im(\omega_2)>0` and - `\Re(\omega_1/\omega_2)` is either `0` (for rectangular + `\Re(\omega_2/\omega_1)` is either `0` (for rectangular lattices) or `\frac{1}{2}` (for non-rectangular lattices). Otherwise, `\omega_1/\omega_2` is in the fundamental region of the upper half-plane. If the latter normalisation is required @@ -650,7 +650,7 @@ def _compute_periods_real(self, prec=None, algorithm='sage'): (tuple of Complex) `(\omega_1,\omega_2)` where the lattice has the form `\ZZ\omega_1 + \ZZ\omega_2`, `\omega_1` is real and - `\omega_1/\omega_2` has real part either `0` or `frac{1}{2}`. + `\omega_2/\omega_1` has real part either `0` or `frac{1}{2}`. EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/saturation.py b/src/sage/schemes/elliptic_curves/saturation.py index 5f14db7ac19..16c44f16d0a 100755 --- a/src/sage/schemes/elliptic_curves/saturation.py +++ b/src/sage/schemes/elliptic_curves/saturation.py @@ -54,6 +54,7 @@ from sage.arith.misc import kronecker as kro from sage.structure.sage_object import SageObject + def reduce_mod_q(x, amodq): r"""The reduction of ``x`` modulo the prime ideal defined by ``amodq``. @@ -88,6 +89,7 @@ def reduce_mod_q(x, amodq): except AttributeError: # in case x is in QQ return Fq(x) + class EllipticCurveSaturator(SageObject): r""" Class for saturating points on an elliptic curve over a number field. @@ -384,7 +386,7 @@ def p_saturation(self, Plist, p, sieve=True): -- points were not 2-saturated, gaining index 2 (1, (0 : 1 : 0)) - A CM example where large siecing primes are needed (LMFDB + A CM example where large sieving primes are needed (LMFDB label 2.0.3.1-50625.1-CMb2):: sage: K. = NumberField(x^2 - x + 1) @@ -585,6 +587,7 @@ def p_saturation(self, Plist, p, sieve=True): rankA = newrank count = 0 + def p_projections(Eq, Plist, p, debug=False): r""" diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 1fc2eb751a9..bbd3bc1e9d6 100755 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -32,6 +32,7 @@ from sage.rings.integer import Integer from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + class baseWI: r""" This class implements the basic arithmetic of isomorphisms between @@ -1071,6 +1072,7 @@ def order(self): raise NotImplementedError("the order of the endomorphism is not 1, 2, 3, 4 or 6") + def identity_morphism(E): r""" Given an elliptic curve `E`, return the identity morphism @@ -1088,6 +1090,7 @@ def identity_morphism(E): zero = R.zero() return WeierstrassIsomorphism(E, (R.one(), zero, zero, zero)) + def negation_morphism(E): r""" Given an elliptic curve `E`, return the negation endomorphism diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index ebe79be6471..a0257a065fd 100755 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -134,6 +134,7 @@ from . import ambient_space from . import scheme + def is_AlgebraicScheme(x): """ Test whether ``x`` is an algebraic scheme. @@ -933,9 +934,9 @@ def __init__(self, A, polynomials, category=None): if isinstance(polynomials, Ideal_generic): I = polynomials polynomials = I.gens() - if I.ring() is R: # Otherwise we will recompute I later after + if I.ring() is R: # Otherwise we will recompute I later after self.__I = I # converting generators to the correct ring - if isinstance(polynomials, tuple) or isinstance(polynomials, PolynomialSequence_generic) or is_iterator(polynomials): + if isinstance(polynomials, (tuple, PolynomialSequence_generic)) or is_iterator(polynomials): polynomials = list(polynomials) elif not isinstance(polynomials, list): # Looks like we got a single polynomial diff --git a/src/sage/schemes/generic/glue.py b/src/sage/schemes/generic/glue.py index 72171a57cb5..7733030a2ab 100755 --- a/src/sage/schemes/generic/glue.py +++ b/src/sage/schemes/generic/glue.py @@ -5,7 +5,7 @@ #******************************************************************************* # Copyright (C) 2006 William Stein # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #******************************************************************************* from sage.misc.lazy_import import lazy_import diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index 1468090f395..370049d2238 100755 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -658,6 +658,56 @@ def _element_constructor_(self, *v, **kwds): v = v[0] return self.extended_codomain()._point(self, v, **kwds) + def __iter__(self): + r""" + Return an iterator for the set of rational points on this scheme. + + By default, this calls the :meth:`points` method, which is implemented + when the base ring is a field + + - for affine homsets at :meth:`sage.schemes.affine.affine_homset.SchemeHomset_points_affine.points`; + - for projective homsets at :meth:`sage.schemes.projective.projective_homset.SchemeHomset_points_projective_field.points`; + - and toric homsets at :meth:`sage.schemes.toric.homset.SchemeHomset_points_toric_field._enumerator`. + + OUTPUT: iterator over points + + TESTS:: + + sage: E = EllipticCurve(GF(19), [1, 0]) + sage: list(E.point_homset()) + [(0 : 1 : 0), (0 : 0 : 1), (3 : 7 : 1), (3 : 12 : 1), (4 : 7 : 1), + (4 : 12 : 1), (5 : 4 : 1), (5 : 15 : 1), (8 : 8 : 1), (8 : 11 : 1), + (9 : 4 : 1), (9 : 15 : 1), (12 : 7 : 1), (12 : 12 : 1), (13 : 5 : 1), + (13 : 14 : 1), (17 : 3 : 1), (17 : 16 : 1), (18 : 6 : 1), (18 : 13 : 1)] + sage: _ == list(E) + True + sage: E.point_homset().cardinality() + 20 + + :: + + sage: A. = AffineSpace(2, GF(5)) + sage: list(A.point_homset()) + [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), + (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), + (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), + (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), + (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)] + sage: _ == list(A) + True + sage: A.point_homset().cardinality() + 25 + + :: + + sage: P1 = toric_varieties.P1(base_ring=GF(3)) + sage: list(P1.point_homset()) + [[0 : 1], [1 : 0], [1 : 1], [1 : 2]] + sage: P1.point_homset().cardinality() + 4 + """ + yield from self.points() + def extended_codomain(self): r""" Return the codomain with extended base, if necessary. diff --git a/src/sage/schemes/generic/hypersurface.py b/src/sage/schemes/generic/hypersurface.py index 0560697be25..a6bb63040b5 100755 --- a/src/sage/schemes/generic/hypersurface.py +++ b/src/sage/schemes/generic/hypersurface.py @@ -23,6 +23,7 @@ from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine from sage.schemes.projective.projective_subscheme import AlgebraicScheme_subscheme_projective + def is_Hypersurface(self): """ Return ``True`` if ``self`` is a hypersurface, i.e. an object of the type diff --git a/src/sage/schemes/generic/point.py b/src/sage/schemes/generic/point.py index 4bd18c39a5d..4158b350c16 100755 --- a/src/sage/schemes/generic/point.py +++ b/src/sage/schemes/generic/point.py @@ -16,6 +16,7 @@ # or defined by a morphism. ######################################################## + class SchemePoint(Element): """ Base class for points on a scheme, either topological or defined @@ -72,6 +73,7 @@ def _repr_(self): # Topological points on a scheme ######################################################## + def is_SchemeTopologicalPoint(x): from sage.misc.superseded import deprecation deprecation(38296, @@ -79,6 +81,7 @@ def is_SchemeTopologicalPoint(x): "use 'isinstance(..., SchemeTopologicalPoint)' instead.") return isinstance(x, SchemeTopologicalPoint) + class SchemeTopologicalPoint(SchemePoint): """ Base class for topological points on schemes. diff --git a/src/sage/schemes/hyperelliptic_curves/constructor.py b/src/sage/schemes/hyperelliptic_curves/constructor.py index a39b94b030b..b3c006b2c19 100755 --- a/src/sage/schemes/hyperelliptic_curves/constructor.py +++ b/src/sage/schemes/hyperelliptic_curves/constructor.py @@ -202,7 +202,7 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): # rather than f and h, one of which might be constant. F = h**2 + 4 * f if not isinstance(F, Polynomial): - raise TypeError(f"arguments {f = } and {h = } must be polynomials") + raise TypeError(f"arguments f = {f} and h = {h} must be polynomials") P = F.parent() f = P(f) h = P(h) @@ -220,7 +220,7 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): # characteristic 2 if h == 0: raise ValueError( - f"for characteristic 2, argument {h = } must be nonzero" + f"for characteristic 2, argument h = {h} must be nonzero" ) if h[g + 1] == 0 and f[2 * g + 1] ** 2 == f[2 * g + 2] * h[g] ** 2: raise ValueError( diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx index 4fa3280f6f9..2b14032ffc9 100755 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx @@ -127,8 +127,10 @@ def interval_products(M0, M1, target): cdef long dim = M0.nrows() sig_on() c.restore_c() + sig_off() set_ntl_matrix_modn_dense(mm0, c, M0) set_ntl_matrix_modn_dense(mm1, c, M1) + sig_on() for t in target: targ.push_back(ntl_ZZ(t).x) numintervals = len(target)/2 diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 6074f66e699..61cd934c0a0 100755 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -172,16 +172,44 @@ def _repr_(self): Hyperelliptic Curve over Rational Field defined by y^2 = 4*x^5 - 30*x^3 + 45*x - 22 sage: C = HyperellipticCurve(f,names='u,v'); C Hyperelliptic Curve over Rational Field defined by v^2 = 4*u^5 - 30*u^3 + 45*u - 22 + sage: C = HyperellipticCurve(x^5 + 1, x^3 + 2); C + Hyperelliptic Curve over Rational Field defined by y^2 + (x^3 + 2)*y = x^5 + 1 """ f, h = self._hyperelliptic_polynomials R = self.base_ring() y = self._printing_ring.gen() x = self._printing_ring.base_ring().gen() - if h == 0: + if h.is_zero(): return "Hyperelliptic Curve over %s defined by %s = %s" % (R, y**2, f(x)) - else: - return "Hyperelliptic Curve over %s defined by %s + %s = %s" % (R, y**2, h(x)*y, f(x)) + return "Hyperelliptic Curve over %s defined by %s + %s = %s" % (R, y**2, h(x)*y, f(x)) + + def _latex_(self): + r""" + LaTeX representation of hyperelliptic curves. + + EXAMPLES:: + + sage: P. = QQ[] + sage: f = 4*x^5 - 30*x^3 + 45*x - 22 + sage: C = HyperellipticCurve(f); latex(C) + \text{Hyperelliptic Curve over $\Bold{Q}$ defined by $y^{2} = 4 x^{5} - 30 x^{3} + 45 x - 22$} + sage: C = HyperellipticCurve(f,names='u,v'); latex(C) + \text{Hyperelliptic Curve over $\Bold{Q}$ defined by $v^{2} = 4 u^{5} - 30 u^{3} + 45 u - 22$} + sage: C = HyperellipticCurve(x^5 + 1, x^2 + 3); latex(C) + \text{Hyperelliptic Curve over $\Bold{Q}$ defined by $y^{2} + \left(x^{2} + 3\right) y = x^{5} + 1$} + """ + + f, h = self._hyperelliptic_polynomials + R = self.base_ring() + y = self._printing_ring.gen() + x = self._printing_ring.base_ring().gen() + if h.is_zero(): + return (fr'\text{{Hyperelliptic Curve over ${R._latex_()}$ ' + f'defined by ${(y**2)._latex_()} = {(f(x))._latex_()}$}}') + return (fr'\text{{Hyperelliptic Curve over ${R._latex_()}$ ' + f'defined by ${(y**2)._latex_()} + {(h(x)*y)._latex_()} = ' + f'{(f(x))._latex_()}$}}') def hyperelliptic_polynomials(self, K=None, var='x'): """ diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_g2.py b/src/sage/schemes/hyperelliptic_curves/jacobian_g2.py index b4b4259326e..970bb9cfbf0 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_g2.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_g2.py @@ -22,6 +22,7 @@ # + (4*a4 + 2*c0*c4 + c2^2)*x^4 + (4*a6 + 2*c0*c6 + 2*c2*c4)*x^3 # + (4*a8 + 2*c2*c6 + c4^2)*x^2 + (4*a10 + 2*c4*c6)*x + 4*a12 + c6^2 + class HyperellipticJacobian_g2(jacobian_generic.HyperellipticJacobian_generic): def kummer_surface(self): try: diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_homset.py b/src/sage/schemes/hyperelliptic_curves/jacobian_homset.py index 29c98369259..0fdea5814f0 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_homset.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_homset.py @@ -112,30 +112,40 @@ def __call__(self, P): (x + 2, y) sage: D1 + D2 (x^2 + 2*x + 2, y + 2*x + 1) + + TESTS: + + Test :issue:`38459`:: + + sage: K. = QQ[] + sage: C = HyperellipticCurve(u^5 - 1) + sage: J = C.jacobian() + sage: J(u - 1, 0) + (x - 1, y) """ if isinstance(P, (Integer, int)) and P == 0: - R = PolynomialRing(self.value_ring(), 'x') + R = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()) return JacobianMorphism_divisor_class_field(self, (R.one(), R.zero())) elif isinstance(P, (list, tuple)): if len(P) == 1 and P[0] == 0: - R = PolynomialRing(self.value_ring(), 'x') + R = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()) return JacobianMorphism_divisor_class_field(self, (R.one(), R.zero())) elif len(P) == 2: P1 = P[0] P2 = P[1] if isinstance(P1, Integer) and isinstance(P2, Integer): - R = PolynomialRing(self.value_ring(), 'x') + R = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()) P1 = R(P1) P2 = R(P2) return JacobianMorphism_divisor_class_field(self, (P1, P2)) if isinstance(P1, Integer) and isinstance(P2, Polynomial): - R = PolynomialRing(self.value_ring(), 'x') + R = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()) P1 = R(P1) return JacobianMorphism_divisor_class_field(self, (P1, P2)) if isinstance(P2, Integer) and isinstance(P1, Polynomial): - R = PolynomialRing(self.value_ring(), 'x') + R = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()) P2 = R(P2) return JacobianMorphism_divisor_class_field(self, (P1, P2)) if isinstance(P1, Polynomial) and isinstance(P2, Polynomial): @@ -148,7 +158,7 @@ def __call__(self, P): elif isinstance(P, SchemeMorphism): x0 = P[0] y0 = P[1] - R, x = PolynomialRing(self.value_ring(), 'x').objgen() + R, x = self.curve().hyperelliptic_polynomials()[0].parent().change_ring(self.value_ring()).objgen() return self((x - x0, R(y0))) raise TypeError("argument P (= %s) does not determine a divisor class" % P) diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py index 50ec2c63748..c0cbccd84ea 100755 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py @@ -210,6 +210,7 @@ def cantor_reduction(a, b, f, h, genus): return cantor_reduction(a, b, f, h, genus) return (a, b) + def cantor_composition_simple(D1,D2,f,genus): r""" Given `D_1` and `D_2` two reduced Mumford @@ -268,6 +269,7 @@ def cantor_composition_simple(D1,D2,f,genus): a = a.monic() return (a, b) + def cantor_composition(D1,D2,f,h,genus): r""" EXAMPLES:: diff --git a/src/sage/schemes/hyperelliptic_curves/kummer_surface.py b/src/sage/schemes/hyperelliptic_curves/kummer_surface.py index dadf1200f01..8cf0104c560 100644 --- a/src/sage/schemes/hyperelliptic_curves/kummer_surface.py +++ b/src/sage/schemes/hyperelliptic_curves/kummer_surface.py @@ -25,6 +25,7 @@ # + (2*c3*c9 + c6^2 + 4*a8)*x^4 + (2*c0*c9 + 2*c3*c6 + 4*a6)*x^3 # + (2*c0*c6 + c3^2 + 4*a4)*x^2 + (2*c0*c3 + 4*a2)*x + c0^2 + 4*a0 + class KummerSurface(AlgebraicScheme_subscheme_projective): def __init__(self, J): """ diff --git a/src/sage/schemes/hyperelliptic_curves/meson.build b/src/sage/schemes/hyperelliptic_curves/meson.build new file mode 100644 index 00000000000..e4b017d4192 --- /dev/null +++ b/src/sage/schemes/hyperelliptic_curves/meson.build @@ -0,0 +1,48 @@ +inc_hypellfrob = include_directories('hypellfrob') +py.install_sources( + 'all.py', + 'constructor.py', + 'hyperelliptic_finite_field.py', + 'hyperelliptic_g2.py', + 'hyperelliptic_generic.py', + 'hyperelliptic_padic_field.py', + 'hyperelliptic_rational_field.py', + 'invariants.py', + 'jacobian_endomorphism_utils.py', + 'jacobian_g2.py', + 'jacobian_generic.py', + 'jacobian_homset.py', + 'jacobian_morphism.py', + 'kummer_surface.py', + 'mestre.py', + 'monsky_washnitzer.py', + subdir: 'sage/schemes/hyperelliptic_curves', +) + +extension_data_cpp = { + 'hypellfrob': files( + 'hypellfrob.pyx', + 'hypellfrob/hypellfrob.cpp', + 'hypellfrob/recurrences_ntl.cpp', + ), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/schemes/hyperelliptic_curves', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [ + inc_cpython, + inc_ext, + inc_ntl, + inc_rings, + inc_rings_finite, + inc_hypellfrob, + ], + dependencies: [py_dep, cysignals, gmp, ntl], + ) +endforeach + diff --git a/src/sage/schemes/hyperelliptic_curves/mestre.py b/src/sage/schemes/hyperelliptic_curves/mestre.py index 83a4b209174..c1ea50fbda4 100755 --- a/src/sage/schemes/hyperelliptic_curves/mestre.py +++ b/src/sage/schemes/hyperelliptic_curves/mestre.py @@ -165,19 +165,16 @@ def HyperellipticCurve_from_invariants(i, reduced=True, precision=None, if algorithm == 'magma': from sage.interfaces.magma import magma - from sage.misc.sage_eval import sage_eval if MConic.has_rational_point(algorithm='magma'): - parametrization = [l.replace('$.1', 't').replace('$.2', 'u') - for l in str(magma(MConic).Parametrization()).splitlines()[4:7]] - [F1, F2, F3] = [sage_eval(p, locals={'t': t, 'u': 1, 'a': k.gen()}) - for p in parametrization] + parametrization = magma(MConic).Parametrization().DefiningPolynomials().sage() + F1, F2, F3 = (p(t, 1) for p in parametrization) else: raise ValueError(f"No such curve exists over {k} as there are no " f"rational points on {MConic}") else: if MConic.has_rational_point(): parametrization = MConic.parametrization(morphism=False)[0] - [F1, F2, F3] = [p(t, 1) for p in parametrization] + F1, F2, F3 = (p(t, 1) for p in parametrization) else: raise ValueError(f"No such curve exists over {k} as there are no " f"rational points on {MConic}") diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 8e6e3df0d3c..a533b7d4e92 100755 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -2409,7 +2409,7 @@ def __init__(self, Q, R=None, invert_y=True): if not hasattr(self, '_curve'): if self._Q.degree() == 3: ainvs = [0, self._Q[2], 0, self._Q[1], self._Q[0]] - self._curve = EllipticCurve(ainvs, check_squarefree=R.is_field()) + self._curve = EllipticCurve(ainvs) else: self._curve = HyperellipticCurve(self._Q, check_squarefree=R.is_field()) diff --git a/src/sage/schemes/meson.build b/src/sage/schemes/meson.build new file mode 100644 index 00000000000..c74c532b930 --- /dev/null +++ b/src/sage/schemes/meson.build @@ -0,0 +1,16 @@ +py.install_sources('all.py', 'overview.py', subdir: 'sage/schemes') + +install_subdir('affine', install_dir: sage_install_dir / 'schemes') +install_subdir('berkovich', install_dir: sage_install_dir / 'schemes') +install_subdir('curves', install_dir: sage_install_dir / 'schemes') +install_subdir('cyclic_covers', install_dir: sage_install_dir / 'schemes') +subdir('elliptic_curves') +install_subdir('generic', install_dir: sage_install_dir / 'schemes') +subdir('hyperelliptic_curves') +install_subdir('jacobians', install_dir: sage_install_dir / 'schemes') +install_subdir('plane_conics', install_dir: sage_install_dir / 'schemes') +install_subdir('plane_quartics', install_dir: sage_install_dir / 'schemes') +install_subdir('product_projective', install_dir: sage_install_dir / 'schemes') +install_subdir('projective', install_dir: sage_install_dir / 'schemes') +install_subdir('riemann_surfaces', install_dir: sage_install_dir / 'schemes') +subdir('toric') diff --git a/src/sage/schemes/plane_conics/con_rational_function_field.py b/src/sage/schemes/plane_conics/con_rational_function_field.py index 6ac78706107..26522878918 100755 --- a/src/sage/schemes/plane_conics/con_rational_function_field.py +++ b/src/sage/schemes/plane_conics/con_rational_function_field.py @@ -503,12 +503,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(B): lastpoly = (lastpoly * t) % p - phi_p[A + 2 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + 2 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[A + B + 2] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 2] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(C): lastpoly = (lastpoly * t) % p - phi_p[A + B + 3 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 3 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) for (i, p) in enumerate(supports[1]): @@ -525,12 +525,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(C): lastpoly = (lastpoly * t) % p - phi_p[A + B + 3 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + B + 3 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[0] = vector(F, d, lastpoly.dict()) + phi_p[0] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(A): lastpoly = (lastpoly * t) % p - phi_p[1 + n] = vector(F, d, lastpoly.dict()) + phi_p[1 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) for (i, p) in enumerate(supports[2]): @@ -547,12 +547,12 @@ def find_point(self, supports, roots, case, solution=0): lastpoly = F(1) for n in range(A): lastpoly = (lastpoly * t) % p - phi_p[1 + n] = vector(F, d, lastpoly.dict()) + phi_p[1 + n] = vector(F, d, lastpoly.monomial_coefficients()) lastpoly = -alpha % p - phi_p[A + 1] = vector(F, d, lastpoly.dict()) + phi_p[A + 1] = vector(F, d, lastpoly.monomial_coefficients()) for n in range(B): lastpoly = (lastpoly * t) % p - phi_p[A + 2 + n] = vector(F, d, lastpoly.dict()) + phi_p[A + 2 + n] = vector(F, d, lastpoly.monomial_coefficients()) phi_p[A + B + C + 3] = vector(F, d) phi.append(matrix(phi_p).transpose()) if case == 0: diff --git a/src/sage/schemes/plane_conics/constructor.py b/src/sage/schemes/plane_conics/constructor.py index 38652c9c555..a08089522c2 100755 --- a/src/sage/schemes/plane_conics/constructor.py +++ b/src/sage/schemes/plane_conics/constructor.py @@ -240,7 +240,7 @@ def Conic(base_field, F=None, names=None, unique=True): return ProjectiveConic_rational_field(P2, F) if isinstance(base_field, NumberField): return ProjectiveConic_number_field(P2, F) - if isinstance(base_field, FractionField_generic) and (isinstance(base_field.ring(), PolynomialRing_general) or isinstance(base_field.ring(), MPolynomialRing_base)): + if isinstance(base_field, FractionField_generic) and isinstance(base_field.ring(), (PolynomialRing_general, MPolynomialRing_base)): return ProjectiveConic_rational_function_field(P2, F) return ProjectiveConic_field(P2, F) diff --git a/src/sage/schemes/plane_quartics/quartic_constructor.py b/src/sage/schemes/plane_quartics/quartic_constructor.py index c1dd1a3d0b0..29d49c2d2c4 100755 --- a/src/sage/schemes/plane_quartics/quartic_constructor.py +++ b/src/sage/schemes/plane_quartics/quartic_constructor.py @@ -13,6 +13,7 @@ from .quartic_generic import QuarticCurve_generic + def QuarticCurve(F, PP=None, check=False): """ Return the quartic curve defined by the polynomial ``F``. diff --git a/src/sage/schemes/plane_quartics/quartic_generic.py b/src/sage/schemes/plane_quartics/quartic_generic.py index 514637e1d96..e41eb64d316 100755 --- a/src/sage/schemes/plane_quartics/quartic_generic.py +++ b/src/sage/schemes/plane_quartics/quartic_generic.py @@ -15,12 +15,13 @@ #***************************************************************************** # Copyright (C) 2006 David Kohel # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** import sage.schemes.curves.projective_curve as projective_curve + def is_QuarticCurve(C): """ Check whether ``C`` is a Quartic Curve. diff --git a/src/sage/schemes/product_projective/homset.py b/src/sage/schemes/product_projective/homset.py index d10d2cc0ccb..658417398b9 100755 --- a/src/sage/schemes/product_projective/homset.py +++ b/src/sage/schemes/product_projective/homset.py @@ -27,6 +27,7 @@ from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme from sage.schemes.generic.homset import SchemeHomset_points + class SchemeHomset_points_product_projective_spaces_ring(SchemeHomset_points): r""" Set of rational points of a product of projective spaces. @@ -63,6 +64,7 @@ def _element_constructor_(self, v, **kwds): """ return self.codomain()._point(self, v, **kwds) + class SchemeHomset_points_product_projective_spaces_field(SchemeHomset_points_product_projective_spaces_ring): def points(self, **kwds): r""" diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index 97909025d19..1f29206dfd1 100755 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -173,6 +173,7 @@ def enum_product_projective_rational_field(X, B): return pts + def enum_product_projective_number_field(X, **kwds): r""" Enumerates product projective points on scheme ``X`` defined over a number field. @@ -247,6 +248,7 @@ def enum_product_projective_number_field(X, **kwds): pts.sort() return pts + def enum_product_projective_finite_field(X): r""" Enumerates projective points on scheme ``X`` defined over a finite field. diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index b0b672755da..f183d2676aa 100755 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -1080,6 +1080,7 @@ def segre_embedding(self, PP=None, var='u'): return phi + class ProductProjectiveSpaces_field(ProductProjectiveSpaces_ring): def _point(self, *args, **kwds): """ @@ -1220,6 +1221,7 @@ def points_of_bounded_height(self, **kwds): P[dim_prefix[i] + j] = pt[j] i += 1 + class ProductProjectiveSpaces_finite_field(ProductProjectiveSpaces_field): def _point(self, *args, **kwds): r""" diff --git a/src/sage/schemes/product_projective/subscheme.py b/src/sage/schemes/product_projective/subscheme.py index 8fe4fba1f6b..511f10a973e 100755 --- a/src/sage/schemes/product_projective/subscheme.py +++ b/src/sage/schemes/product_projective/subscheme.py @@ -23,6 +23,7 @@ from sage.schemes.projective.projective_subscheme import AlgebraicScheme_subscheme_projective from sage.schemes.projective.projective_space import ProjectiveSpace + class AlgebraicScheme_subscheme_product_projective(AlgebraicScheme_subscheme_projective): r""" Construct an algebraic subscheme of a product of projective spaces. diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index e0227a42ce7..7f941ec6726 100755 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -50,6 +50,7 @@ _NumberFields = NumberFields() + # -------------------- # Projective varieties # -------------------- diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 9f010391eec..89d4f28f48b 100755 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -248,7 +248,7 @@ def ProjectiveSpace(n, R=None, names=None): sage: P.gens() == R.gens() True """ - if (isinstance(n, MPolynomialRing_base) or isinstance(n, PolynomialRing_general)) and R is None: + if isinstance(n, (MPolynomialRing_base, PolynomialRing_general)) and R is None: if names is not None: # Check for the case that the user provided a variable name # That does not match what we wanted to use from R diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0c3e2724229..ff14a7a7b10 100755 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -358,7 +358,7 @@ def differential_basis_baker(f): return None from sage.geometry.polyhedron.constructor import Polyhedron - D = {(k[0], k[1]): v for k, v in f.dict().items()} + D = {(k[0], k[1]): v for k, v in f.monomial_coefficients().items()} P = Polyhedron(D) kT = k["t"] # here we check the additional genericity conditions: that the polynomials @@ -1010,7 +1010,7 @@ def homotopy_continuation(self, edge): INPUT: - ``edge`` -- tuple ``(z_start, z_end)`` indicating the straight line - over which to perform the homotopy continutation + over which to perform the homotopy continuation OUTPUT: @@ -3013,7 +3013,7 @@ def initialise(z, i): return newg # As multiple calls of the minimal polynomial and it's derivative will - # be required for the homotopy continuaiton, we create fast-callable + # be required for the homotopy continuation, we create fast-callable # versions of these. fc_mp_list = [fast_callable(mp, domain=self._CC) for mp in mp_list] fc_dmp_list = [ @@ -3024,7 +3024,7 @@ def initialise(z, i): prec = self._prec # tau here is playing the role of the desired error. tau = self._RR(2)**(-prec + 3) - one = self._RR(1) + one = self._RR.one() la = self._RR.pi() / 2 # Cutoffs are used to allow us to not have to integrate as close into @@ -3044,7 +3044,7 @@ def initialise(z, i): A = PolynomialRing(self._CC, "xyz") aes = [] for mp in mp_list: - d = mp.dict() + d = mp.monomial_coefficients() mp = sum( [ d[k] * CCzg.gen(0)**k[0] * CCzg.gen(1)**k[1] diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index 77a14643e4b..fcf5d03d476 100755 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -532,9 +532,8 @@ def CPRFanoToricVariety(Delta=None, raise ValueError("the origin (point #%d) cannot be used for a " "coordinate!\nGot: %s" % (Delta_polar.origin(), coordinate_points)) - point_to_ray = {} - for n, point in enumerate(coordinate_points): - point_to_ray[point] = n + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} # This can be simplified if LatticePolytopeClass is adjusted. rays = [Delta_polar.point(p) for p in coordinate_points] # Check/normalize charts and construct the fan based on them. @@ -1229,9 +1228,8 @@ def resolve(self, **kwds): "subdivision!" % Delta_polar.origin()) if new_points: coordinate_points = coordinate_points + new_points - point_to_ray = {} - for n, point in enumerate(coordinate_points): - point_to_ray[point] = n + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} else: point_to_ray = self._point_to_ray new_rays = [Delta_polar.point(point) for point in new_points] @@ -1585,7 +1583,7 @@ def add_variables(field, variables): if isinstance(field, FractionField_generic): # Q(a) ---> Q(a, b) rather than Q(a)(b) R = field.ring() - if isinstance(R, PolynomialRing_general) or isinstance(R, MPolynomialRing_base): + if isinstance(R, (PolynomialRing_general, MPolynomialRing_base)): new_variables = list(R.variable_names()) for v in variables: if v not in new_variables: diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index 41b69d05aca..4c171595e6a 100755 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -354,8 +354,8 @@ def _naive_ideal(self, ring): x = ring.gens() binomials = [] for row in self.ker().matrix().rows(): - xpos = prod(x[i]**max( row[i],0) for i in range(0,len(x))) - xneg = prod(x[i]**max(-row[i],0) for i in range(0,len(x))) + xpos = prod(x[i]**max(row[i], 0) for i in range(len(x))) + xneg = prod(x[i]**max(-row[i], 0) for i in range(len(x))) binomials.append(xpos - xneg) return ring.ideal(binomials) @@ -407,7 +407,7 @@ def subtract(e, power): return tuple([l[0] - power] + l[1:]) def divide_by_x_n(p): - d_old = p.dict() + d_old = p.monomial_coefficients() power = min(e[0] for e in d_old) d_new = {subtract(exponent, power): coefficient for exponent, coefficient in d_old.items()} @@ -445,6 +445,6 @@ def _ideal_HostenSturmfels(self): J = self._naive_ideal(ring) if J.is_zero(): return J - for i in range(0,self.nvariables()): + for i in range(self.nvariables()): J = self._ideal_quotient_by_variable(ring, J, i) return J diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index 1e1ff4d38d8..6f833c5c5d2 100755 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -58,44 +58,44 @@ # The combinatorial data of the toric varieties is stored separately here # since we might want to use it later on to do the reverse lookup. toric_varieties_rays_cones = { - 'dP6':[ + 'dP6': [ [(0, 1), (-1, 0), (-1, -1), (0, -1), (1, 0), (1, 1)], - [[0,1],[1,2],[2,3],[3,4],[4,5],[5,0]] ], - 'dP7':[ + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]], + 'dP7': [ [(0, 1), (-1, 0), (-1, -1), (0, -1), (1, 0)], - [[0,1],[1,2],[2,3],[3,4],[4,0]] ], - 'dP8':[ - [(1,1), (0, 1), (-1, -1), (1, 0)], - [[0,1],[1,2],[2,3],[3,0]] + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]]], + 'dP8': [ + [(1, 1), (0, 1), (-1, -1), (1, 0)], + [[0, 1], [1, 2], [2, 3], [3, 0]] ], - 'P1xP1':[ + 'P1xP1': [ [(1, 0), (-1, 0), (0, 1), (0, -1)], - [[0,2],[2,1],[1,3],[3,0]] ], - 'P1xP1_Z2':[ + [[0, 2], [2, 1], [1, 3], [3, 0]]], + 'P1xP1_Z2': [ [(1, 1), (-1, -1), (-1, 1), (1, -1)], - [[0,2],[2,1],[1,3],[3,0]] ], - 'P1':[ + [[0, 2], [2, 1], [1, 3], [3, 0]]], + 'P1': [ [(1,), (-1,)], - [[0],[1]] ], - 'P2':[ - [(1,0), (0, 1), (-1, -1)], - [[0,1],[1,2],[2,0]] ], - 'A1':[ + [[0], [1]]], + 'P2': [ + [(1, 0), (0, 1), (-1, -1)], + [[0, 1], [1, 2], [2, 0]]], + 'A1': [ [(1,)], - [[0]] ], - 'A2':[ + [[0]]], + 'A2': [ [(1, 0), (0, 1)], - [[0,1]] ], - 'A2_Z2':[ + [[0, 1]]], + 'A2_Z2': [ [(1, 0), (1, 2)], - [[0,1]] ], - 'P1xA1':[ + [[0, 1]]], + 'P1xA1': [ [(1, 0), (-1, 0), (0, 1)], - [[0,2],[2,1]] ], - 'Conifold':[ + [[0, 2], [2, 1]]], + 'Conifold': [ [(0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)], - [[0,1,2,3]] ], - 'dP6xdP6':[ + [[0, 1, 2, 3]]], + 'dP6xdP6': [ [(0, 1, 0, 0), (-1, 0, 0, 0), (-1, -1, 0, 0), (0, -1, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (0, 0, 0, 1), (0, 0, -1, 0), (0, 0, -1, -1), @@ -108,74 +108,74 @@ [3, 4, 8, 9], [3, 4, 9, 10], [3, 4, 10, 11], [3, 4, 6, 11], [4, 5, 6, 7], [4, 5, 7, 8], [4, 5, 8, 9], [4, 5, 9, 10], [4, 5, 10, 11], [4, 5, 6, 11], [0, 5, 6, 7], [0, 5, 7, 8], - [0, 5, 8, 9], [0, 5, 9, 10], [0, 5, 10, 11], [0, 5, 6, 11]] ], - 'Cube_face_fan':[ + [0, 5, 8, 9], [0, 5, 9, 10], [0, 5, 10, 11], [0, 5, 6, 11]]], + 'Cube_face_fan': [ [(1, 1, 1), (1, -1, 1), (-1, 1, 1), (-1, -1, 1), (-1, -1, -1), (-1, 1, -1), (1, -1, -1), (1, 1, -1)], - [[0,1,2,3], [4,5,6,7], [0,1,7,6], [4,5,3,2], [0,2,5,7], [4,6,1,3]] ], - 'Cube_sublattice':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'Cube_sublattice': [ [(1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 1, 1), (-1, 0, 0), (0, -1, 0), (0, 0, -1), (1, -1, -1)], - [[0,1,2,3],[4,5,6,7],[0,1,7,6],[4,5,3,2],[0,2,5,7],[4,6,1,3]] ], - 'Cube_nonpolyhedral':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'Cube_nonpolyhedral': [ [(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1), (-1, -1, -1), (-1, 1, -1), (1, -1, -1), (1, 1, -1)], - [[0,1,2,3],[4,5,6,7],[0,1,7,6],[4,5,3,2],[0,2,5,7],[4,6,1,3]] ], - 'BCdlOG':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'BCdlOG': [ [(-1, 0, 0, 2, 3), # 0 - ( 0,-1, 0, 2, 3), # 1 - ( 0, 0,-1, 2, 3), # 2 - ( 0, 0,-1, 1, 2), # 3 - ( 0, 0, 0,-1, 0), # 4 - ( 0, 0, 0, 0,-1), # 5 - ( 0, 0, 0, 2, 3), # 6 - ( 0, 0, 1, 2, 3), # 7 - ( 0, 0, 2, 2, 3), # 8 - ( 0, 0, 1, 1, 1), # 9 - ( 0, 1, 2, 2, 3), # 10 - ( 0, 1, 3, 2, 3), # 11 - ( 1, 0, 4, 2, 3)], # 12 - [ [0,6,7,1,4], [0,6,10,2,4], [0,6,1,2,4], [0,9,7,1,5], [0,6,7,1,5], - [0,6,10,2,5], [0,6,1,2,5], [0,9,1,4,5], [0,6,10,4,11],[0,6,7,4,11], - [0,6,10,5,11], [0,9,7,5,11], [0,6,7,5,11], [0,9,4,5,11], [0,10,4,5,11], - [0,9,7,1,8], [0,9,1,4,8], [0,7,1,4,8], [0,9,7,11,8], [0,9,4,11,8], - [0,7,4,11,8], [0,10,2,4,3], [0,1,2,4,3], [0,10,2,5,3], [0,1,2,5,3], - [0,10,4,5,3], [0,1,4,5,3], [12,6,7,1,4], [12,6,10,2,4],[12,6,1,2,4], - [12,9,7,1,5], [12,6,7,1,5], [12,6,10,2,5], [12,6,1,2,5], [12,9,1,4,5], - [12,6,10,4,11],[12,6,7,4,11], [12,6,10,5,11],[12,9,7,5,11],[12,6,7,5,11], - [12,9,4,5,11], [12,10,4,5,11],[12,9,7,1,8], [12,9,1,4,8], [12,7,1,4,8], - [12,9,7,11,8], [12,9,4,11,8], [12,7,4,11,8], [12,10,2,4,3],[12,1,2,4,3], - [12,10,2,5,3], [12,1,2,5,3], [12,10,4,5,3], [12,1,4,5,3] ] ], - 'BCdlOG_base':[ + (0, -1, 0, 2, 3), # 1 + (0, 0, -1, 2, 3), # 2 + (0, 0, -1, 1, 2), # 3 + (0, 0, 0, -1, 0), # 4 + (0, 0, 0, 0, -1), # 5 + (0, 0, 0, 2, 3), # 6 + (0, 0, 1, 2, 3), # 7 + (0, 0, 2, 2, 3), # 8 + (0, 0, 1, 1, 1), # 9 + (0, 1, 2, 2, 3), # 10 + (0, 1, 3, 2, 3), # 11 + (1, 0, 4, 2, 3)], # 12 + [[0, 6, 7, 1, 4], [0, 6, 10, 2, 4], [0, 6, 1, 2, 4], [0, 9, 7, 1, 5], [0, 6, 7, 1, 5], + [0, 6, 10, 2, 5], [0, 6, 1, 2, 5], [0, 9, 1, 4, 5], [0, 6, 10, 4, 11], [0, 6, 7, 4, 11], + [0, 6, 10, 5, 11], [0, 9, 7, 5, 11], [0, 6, 7, 5, 11], [0, 9, 4, 5, 11], [0, 10, 4, 5, 11], + [0, 9, 7, 1, 8], [0, 9, 1, 4, 8], [0, 7, 1, 4, 8], [0, 9, 7, 11, 8], [0, 9, 4, 11, 8], + [0, 7, 4, 11, 8], [0, 10, 2, 4, 3], [0, 1, 2, 4, 3], [0, 10, 2, 5, 3], [0, 1, 2, 5, 3], + [0, 10, 4, 5, 3], [0, 1, 4, 5, 3], [12, 6, 7, 1, 4], [12, 6, 10, 2, 4], [12, 6, 1, 2, 4], + [12, 9, 7, 1, 5], [12, 6, 7, 1, 5], [12, 6, 10, 2, 5], [12, 6, 1, 2, 5], [12, 9, 1, 4, 5], + [12, 6, 10, 4, 11], [12, 6, 7, 4, 11], [12, 6, 10, 5, 11], [12, 9, 7, 5, 11], [12, 6, 7, 5, 11], + [12, 9, 4, 5, 11], [12, 10, 4, 5, 11], [12, 9, 7, 1, 8], [12, 9, 1, 4, 8], [12, 7, 1, 4, 8], + [12, 9, 7, 11, 8], [12, 9, 4, 11, 8], [12, 7, 4, 11, 8], [12, 10, 2, 4, 3], [12, 1, 2, 4, 3], + [12, 10, 2, 5, 3], [12, 1, 2, 5, 3], [12, 10, 4, 5, 3], [12, 1, 4, 5, 3]]], + 'BCdlOG_base': [ [(-1, 0, 0), - ( 0,-1, 0), - ( 0, 0,-1), - ( 0, 0, 1), - ( 0, 1, 2), - ( 0, 1, 3), - ( 1, 0, 4)], - [[0,4,2],[0,4,5],[0,5,3],[0,1,3],[0,1,2], - [6,4,2],[6,4,5],[6,5,3],[6,1,3],[6,1,2]] ], - 'P2_112':[ - [(1,0), (0, 1), (-1, -2)], - [[0,1],[1,2],[2,0]] ], - 'P2_123':[ - [(1,0), (0, 1), (-2, -3)], - [[0,1],[1,2],[2,0]] ], - 'P4_11169':[ + (0, -1, 0), + (0, 0, -1), + (0, 0, 1), + (0, 1, 2), + (0, 1, 3), + (1, 0, 4)], + [[0, 4, 2], [0, 4, 5], [0, 5, 3], [0, 1, 3], [0, 1, 2], + [6, 4, 2], [6, 4, 5], [6, 5, 3], [6, 1, 3], [6, 1, 2]]], + 'P2_112': [ + [(1, 0), (0, 1), (-1, -2)], + [[0, 1], [1, 2], [2, 0]]], + 'P2_123': [ + [(1, 0), (0, 1), (-2, -3)], + [[0, 1], [1, 2], [2, 0]]], + 'P4_11169': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1)], - [[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4],[1,2,3,4]] ], - 'P4_11169_resolved':[ + [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]], + 'P4_11169_resolved': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1), (-3, -2, 0, 0)], [[0, 1, 2, 3], [0, 1, 3, 4], [0, 1, 2, 4], [1, 3, 4, 5], [0, 3, 4, 5], - [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]] ], - 'P4_11133':[ + [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]]], + 'P4_11133': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-3, -3, -1, -1)], - [[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4],[1,2,3,4]] ], - 'P4_11133_resolved':[ + [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]], + 'P4_11133_resolved': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-3, -3, -1, -1), (-1, -1, 0, 0)], [[0, 1, 2, 3], [0, 1, 3, 4], [0, 1, 2, 4], [1, 3, 4, 5], [0, 3, 4, 5], - [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]] ] + [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]]] } @@ -264,7 +264,7 @@ def _make_CPRFanoToricVariety(self, name, coordinate_names, base_ring): polytope = LatticePolytope(rays, lattice=ToricLattice(len(rays[0]))) points = [tuple(_) for _ in polytope.points()] ray2point = [points.index(r) for r in rays] - charts = [ [ray2point[i] for i in c] for c in cones ] + charts = [[ray2point[i] for i in c] for c in cones] self.__dict__[dict_key] = \ CPRFanoToricVariety(Delta_polar=polytope, coordinate_points=ray2point, @@ -868,7 +868,7 @@ def Cube_nonpolyhedral(self, names='z+', base_ring=QQ): """ return self._make_ToricVariety('Cube_nonpolyhedral', names, base_ring) - def Cube_deformation(self,k, names=None, base_ring=QQ): + def Cube_deformation(self, k, names=None, base_ring=QQ): r""" Construct, for each `k\in\ZZ_{\geq 0}`, a toric variety with `\ZZ_k`-torsion in the Chow group. @@ -1268,7 +1268,7 @@ def WP(self, *q, **kw): rays = rays + [v] w_c = w[:i] + w[i + 1:] cones = cones + [tuple(w_c)] - fan = Fan(cones,rays) + fan = Fan(cones, rays) return ToricVariety(fan, coordinate_names=names, base_ring=base_ring) def torus(self, n, names='z+', base_ring=QQ): diff --git a/src/sage/schemes/toric/meson.build b/src/sage/schemes/toric/meson.build new file mode 100644 index 00000000000..d9dea45fcc8 --- /dev/null +++ b/src/sage/schemes/toric/meson.build @@ -0,0 +1,32 @@ +py.install_sources( + 'all.py', + 'chow_group.py', + 'divisor.py', + 'fano_variety.py', + 'homset.py', + 'ideal.py', + 'library.py', + 'morphism.py', + 'points.py', + 'toric_subscheme.py', + 'variety.py', + 'weierstrass.py', + 'weierstrass_covering.py', + 'weierstrass_higher.py', + subdir: 'sage/schemes/toric', +) + +extension_data = {'divisor_class' : files('divisor_class.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/schemes/toric', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, gmp], + ) +endforeach + +install_subdir('sheaf', install_dir: sage_install_dir / 'schemes/toric') diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py index 94f465860b7..a2e80dd7fa6 100755 --- a/src/sage/schemes/toric/sheaf/klyachko.py +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -605,7 +605,7 @@ def cohomology_complex(self, m): C = fan.complex() CV = [] F = self.base_ring() - for dim in range(1,fan.dim()+1): + for dim in range(1, fan.dim()+1): codim = fan.dim() - dim d_C = C.differential(codim) d_V = [] @@ -616,7 +616,7 @@ def cohomology_complex(self, m): sigma = fan(dim-1)[i] if sigma.is_face_of(tau): pr = self.E_quotient_projection(sigma, tau, m) - d = d_C[i,j] * pr.matrix().transpose() + d = d_C[i, j] * pr.matrix().transpose() else: E_sigma = self.E_quotient(sigma, m) E_tau = self.E_quotient(tau, m) @@ -695,7 +695,7 @@ def cohomology(self, degree=None, weight=None, dim=False): except KeyError: HH[d] = FreeModule(self.base_ring(), 0) if dim: - HH = vector(ZZ, [HH[i].rank() for i in range(space_dim+1) ]) + HH = vector(ZZ, [HH[i].rank() for i in range(space_dim+1)]) return HH def __richcmp__(self, other, op): diff --git a/src/sage/schemes/toric/toric_subscheme.py b/src/sage/schemes/toric/toric_subscheme.py index 33806674d28..908ef979984 100755 --- a/src/sage/schemes/toric/toric_subscheme.py +++ b/src/sage/schemes/toric/toric_subscheme.py @@ -328,14 +328,14 @@ def pullback_polynomial(p): result = R.zero() for coefficient, monomial in p: exponent = monomial.exponents()[0] - exponent = [ exponent[i] for i in cone.ambient_ray_indices() ] - exponent = vector(ZZ,exponent) + exponent = [exponent[i] for i in cone.ambient_ray_indices()] + exponent = vector(ZZ, exponent) m = n_rho_matrix.solve_right(exponent) assert all(x in ZZ for x in m), \ - 'The polynomial '+str(p)+' does not define a ZZ-divisor!' + f'The polynomial {p} does not define a ZZ-divisor!' m_coeffs = dualcone.Hilbert_coefficients(m) result += coefficient * prod(R.gen(i)**m_coeffs[i] - for i in range(0,R.ngens())) + for i in range(R.ngens())) return result # construct the affine algebraic scheme to use as patch @@ -353,7 +353,7 @@ def pullback_polynomial(p): if cone.is_smooth(): x = ambient.coordinate_ring().gens() phi = [] - for i in range(0,fan.nrays()): + for i in range(fan.nrays()): if i in cone.ambient_ray_indices(): phi.append(pullback_polynomial(x[i])) else: @@ -371,11 +371,10 @@ def pullback_polynomial(p): # it remains to find the preimage of point # map m to the monomial x^{D_m}, see reference. F = ambient.coordinate_ring().fraction_field() - image = [] - for m in dualcone.Hilbert_basis(): - x_Dm = prod([ F.gen(i)**(m*n) for i,n in enumerate(fan.rays()) ]) - image.append(x_Dm) - patch._embedding_center = tuple( f(list(point)) for f in image ) + image = [prod([F.gen(i)**(m * n) + for i, n in enumerate(fan.rays())]) + for m in dualcone.Hilbert_basis()] + patch._embedding_center = tuple(f(list(point)) for f in image) return patch def _best_affine_patch(self, point): @@ -487,7 +486,7 @@ def neighborhood(self, point): phi_reduced = [S(t) for t in phi] patch._embedding_center = patch(point_preimage) - patch._embedding_morphism = patch.hom(phi_reduced,self) + patch._embedding_morphism = patch.hom(phi_reduced, self) return patch def dimension(self): @@ -513,7 +512,7 @@ def dimension(self): if '_dimension' in self.__dict__: return self._dimension npatches = self.ambient_space().fan().ngenerating_cones() - dims = [ self.affine_patch(i).dimension() for i in range(0,npatches) ] + dims = [self.affine_patch(i).dimension() for i in range(npatches)] self._dimension = max(dims) return self._dimension @@ -582,7 +581,8 @@ def is_smooth(self, point=None): if '_smooth' in self.__dict__: return self._smooth npatches = self.ambient_space().fan().ngenerating_cones() - self._smooth = all(self.affine_patch(i).is_smooth() for i in range(0,npatches)) + self._smooth = all(self.affine_patch(i).is_smooth() + for i in range(npatches)) return self._smooth def is_nondegenerate(self): @@ -692,7 +692,7 @@ def restrict(cone): enumerate(SR.subs(divide).gens())]) return ideal, Jac_patch + SR_patch - for dim in range(0, fan.dim() + 1): + for dim in range(fan.dim() + 1): for cone in fan(dim): ideal1, ideal2 = restrict(cone) if ideal1.is_zero() or ideal2.dimension() != -1: diff --git a/src/sage/schemes/toric/weierstrass_covering.py b/src/sage/schemes/toric/weierstrass_covering.py index 87d414d3464..82c300c1f7a 100755 --- a/src/sage/schemes/toric/weierstrass_covering.py +++ b/src/sage/schemes/toric/weierstrass_covering.py @@ -255,9 +255,9 @@ def homogenize(inhomog, degree): result = vector(ZZ, result) result.set_immutable() return result - X_dict = {homogenize(e, 2): v for e, v in X.dict().items()} - Y_dict = {homogenize(e, 3): v for e, v in Y.dict().items()} - Z_dict = {homogenize(e, 1): v for e, v in Z.dict().items()} + X_dict = {homogenize(e, 2): v for e, v in X.monomial_coefficients().items()} + Y_dict = {homogenize(e, 3): v for e, v in Y.monomial_coefficients().items()} + Z_dict = {homogenize(e, 1): v for e, v in Z.monomial_coefficients().items()} # shift to nonnegative exponents if necessary min_deg = [0] * R.ngens() for var in variables: diff --git a/src/sage/sets/disjoint_set.pxd b/src/sage/sets/disjoint_set.pxd index 3c8351983e7..4d981718568 100644 --- a/src/sage/sets/disjoint_set.pxd +++ b/src/sage/sets/disjoint_set.pxd @@ -19,8 +19,8 @@ cdef class DisjointSet_class(SageObject): cpdef number_of_subsets(self) cdef class DisjointSet_of_integers(DisjointSet_class): - cpdef int find(self, int i) - cpdef void union(self, int i, int j) + cpdef int find(self, int i) except -1 + cpdef void union(self, int i, int j) except * cpdef root_to_elements_dict(self) cpdef element_to_root_dict(self) cpdef to_digraph(self) @@ -29,7 +29,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): cdef list _int_to_el cdef dict _el_to_int cpdef find(self, e) - cpdef void union(self, e, f) + cpdef void union(self, e, f) noexcept 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 1c2d93fea2a..ddd9a95a310 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -62,6 +62,10 @@ from sage.rings.integer cimport Integer from sage.structure.sage_object cimport SageObject from cpython.object cimport PyObject_RichCompare from sage.groups.perm_gps.partn_ref.data_structures cimport * +from sage.misc.lazy_import import LazyImport + +SetPartition = LazyImport('sage.combinat.set_partition', 'SetPartition') + cpdef DisjointSet(arg): r""" @@ -103,6 +107,14 @@ cpdef DisjointSet(arg): sage: DisjointSet(['yi', 45, 'cheval']) {{'cheval'}, {'yi'}, {45}} + From a set partition (see :issue:`38693`):: + + sage: SP = SetPartition(DisjointSet(5)) + sage: DisjointSet(SP) + {{0}, {1}, {2}, {3}, {4}} + sage: DisjointSet(SP) == DisjointSet(5) + True + TESTS:: sage: DisjointSet(0) @@ -137,6 +149,8 @@ cpdef DisjointSet(arg): if arg < 0: raise ValueError('arg must be a nonnegative integer (%s given)' % arg) return DisjointSet_of_integers(arg) + elif isinstance(arg, SetPartition): + return DisjointSet(arg.base_set()) else: return DisjointSet_of_hashables(arg) @@ -448,7 +462,7 @@ cdef class DisjointSet_of_integers(DisjointSet_class): for i, parent in enumerate(l): self.union(parent, i) - cpdef int find(self, int i): + cpdef int find(self, int i) except -1: r""" Return the representative of the set that ``i`` currently belongs to. @@ -479,8 +493,9 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: [e.find(i) for i in range(5)] [0, 1, 1, 1, 1] sage: e.find(2**10) - ValueError: i must be between 0 and 4 (1024 given) + Traceback (most recent call last): ... + ValueError: i must be between 0 and 4 (1024 given) .. NOTE:: @@ -492,7 +507,7 @@ cdef class DisjointSet_of_integers(DisjointSet_class): raise ValueError('i must be between 0 and %s (%s given)' % (card - 1, i)) return OP_find(self._nodes, i) - cpdef void union(self, int i, int j): + cpdef void union(self, int i, int j) except *: r""" Combine the set of ``i`` and the set of ``j`` into one. @@ -520,8 +535,9 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d {{0, 1, 2, 4}, {3}} sage: d.union(1, 5) - ValueError: j must be between 0 and 4 (5 given) + Traceback (most recent call last): ... + ValueError: j must be between 0 and 4 (5 given) .. NOTE:: @@ -535,6 +551,26 @@ cdef class DisjointSet_of_integers(DisjointSet_class): raise ValueError('j must be between 0 and %s (%s given)' % (card - 1, j)) OP_join(self._nodes, i, j) + def make_set(self): + r""" + Add a new element into a new set containing only the new element. + + According to :wikipedia:`Disjoint-set_data_structure#Making_new_sets` the + `make_set` operation adds a new element into a new set containing only + the new element. The new set is added at the end of `self`. + + EXAMPLES:: + sage: d = DisjointSet(5) + sage: d.union(1, 2) + sage: d.union(0, 1) + sage: d.make_set() + sage: d + {{0, 1, 2}, {3}, {4}, {5}} + sage: d.find(1) + 1 + """ + OP_make_set(self._nodes) + cpdef root_to_elements_dict(self): r""" Return the dictionary where the keys are the roots of ``self`` and the @@ -797,7 +833,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): cdef int r = OP_find(self._nodes, i) return self._int_to_el[r] - cpdef void union(self, e, f): + cpdef void union(self, e, f) noexcept: r""" Combine the set of ``e`` and the set of ``f`` into one. @@ -832,6 +868,43 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): cdef int j = self._el_to_int[f] OP_join(self._nodes, i, j) + def make_set(self, new_elt=None): + r""" + Add a new element into a new set containing only the new element. + + According to :wikipedia:`Disjoint-set_data_structure#Making_new_sets` + the `make_set` operation adds a new element into a new set containing + only the new element. The new set is added at the end of `self`. + + INPUT: + + - ``new_elt`` -- (optional) element to add. If `None`, then an integer + is added. + + EXAMPLES:: + + sage: e = DisjointSet('abcde') + sage: e.union('d', 'c') + sage: e.union('c', 'e') + sage: e.make_set('f') + sage: e + {{'a'}, {'b'}, {'c', 'd', 'e'}, {'f'}} + sage: e.union('f', 'b') + sage: e + {{'a'}, {'b', 'f'}, {'c', 'd', 'e'}} + sage: e.make_set('e'); e + {{'a'}, {'b', 'f'}, {'c', 'd', 'e'}} + sage: e.make_set(); e + {{'a'}, {'b', 'f'}, {'c', 'd', 'e'}, {6}} + """ + if new_elt is None: + new_elt = self._nodes.degree + if new_elt not in self._int_to_el: + d = self._nodes.degree + self._int_to_el.append(new_elt) + self._el_to_int[new_elt] = d + OP_make_set(self._nodes) + cpdef root_to_elements_dict(self): r""" Return the dictionary where the keys are the roots of ``self`` and the diff --git a/src/sage/sets/finite_set_map_cy.pyx b/src/sage/sets/finite_set_map_cy.pyx index 335e4cfb5e7..f41450d5842 100644 --- a/src/sage/sets/finite_set_map_cy.pyx +++ b/src/sage/sets/finite_set_map_cy.pyx @@ -355,7 +355,7 @@ cdef class FiniteSetMap_MN(ClonableIntArray): n = self._parent._n assert self._len == m, "Wrong number of values" for i in range(m): - assert 0 <= self._list[i] < n, "Wrong value self(%i) = %i"%(i, self._list[i]) + assert 0 <= self._list[i] < n, "Wrong value self(%i) = %i" % (i, self._list[i]) if hasattr(self._parent, 'check_element'): self._parent.check_element(self) diff --git a/src/sage/sets/image_set.py b/src/sage/sets/image_set.py index 7c3095664e5..a518dc58f93 100644 --- a/src/sage/sets/image_set.py +++ b/src/sage/sets/image_set.py @@ -16,7 +16,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from typing import Iterator +from collections.abc import Iterator from sage.categories.map import Map from sage.categories.poor_man_map import PoorManMap @@ -25,7 +25,6 @@ from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity from sage.rings.integer import Integer -from sage.modules.free_module import FreeModule from sage.structure.element import Expression from sage.structure.parent import Parent @@ -91,6 +90,7 @@ def __init__(self, map, domain_subset, *, category=None, is_injective=None, inve if isinstance(map, Expression) and map.is_callable(): domain = map.parent().base() if len(map.arguments()) != 1: + from sage.modules.free_module import FreeModule domain = FreeModule(domain, len(map.arguments())) function = map diff --git a/src/sage/sets/integer_range.py b/src/sage/sets/integer_range.py index 1406ddc9b28..3b95506f591 100644 --- a/src/sage/sets/integer_range.py +++ b/src/sage/sets/integer_range.py @@ -384,7 +384,7 @@ def cardinality(self): sage: IntegerRange(123,12,4).cardinality() 0 """ - return (abs((self._end+self._step-self._begin))-1) // abs(self._step) + return (abs(self._end+self._step-self._begin)-1) // abs(self._step) def _repr_(self): """ diff --git a/src/sage/sets/meson.build b/src/sage/sets/meson.build new file mode 100644 index 00000000000..73faddaf9c7 --- /dev/null +++ b/src/sage/sets/meson.build @@ -0,0 +1,44 @@ +py.install_sources( + 'all.py', + 'all__sagemath_objects.py', + 'cartesian_product.py', + 'condition_set.py', + 'disjoint_set.pxd', + 'disjoint_union_enumerated_sets.py', + 'family.pxd', + 'finite_enumerated_set.py', + 'finite_set_map_cy.pxd', + 'finite_set_maps.py', + 'image_set.py', + 'integer_range.py', + 'non_negative_integers.py', + 'positive_integers.py', + 'primes.py', + 'pythonclass.pxd', + 'real_set.py', + 'recursively_enumerated_set.pxd', + 'set.py', + 'set_from_iterator.py', + 'totally_ordered_finite_set.py', + subdir: 'sage/sets', +) + +extension_data = { + 'disjoint_set' : files('disjoint_set.pyx'), + 'family' : files('family.pyx'), + 'finite_set_map_cy' : files('finite_set_map_cy.pyx'), + 'pythonclass' : files('pythonclass.pyx'), + 'recursively_enumerated_set' : files('recursively_enumerated_set.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/sets', + install: true, + include_directories: [inc_cpython, inc_data_structures], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 5a23d6d18f5..d502dbc4a4a 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -378,7 +378,7 @@ def _repr_(self): s += ']' if self._upper_closed else ')' return s - def _latex_(self): + def _latex_(self) -> str: """ Return a latex representation of ``self``. @@ -392,7 +392,7 @@ def _latex_(self): from sage.misc.latex import latex if self.is_point(): # Converting to str avoids the extra whitespace - # that LatexExpr add on concenation. We do not need + # that LatexExpr add on concatenation. We do not need # the whitespace because we are wrapping it in # non-letter characters. return r'\{' + str(latex(self.lower())) + r'\}' @@ -2537,11 +2537,9 @@ def convex_hull(*real_set_collection): s = RealSet(real_set) if s.n_components() > 0: lower_s = s[0]._scan_lower() - if lower_s < lower_scan: - lower_scan = lower_s + lower_scan = min(lower_s, lower_scan) upper_s = s[-1]._scan_upper() - if upper_s > upper_scan: - upper_scan = upper_s + upper_scan = max(upper_s, upper_scan) if lower_scan < upper_scan: lower, lower_closed = lower_scan[0][0], lower_scan[0][1] == 0 upper, upper_closed = upper_scan[0][0], upper_scan[0][1] > 0 diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 55d5639e706..123c4366af0 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -209,7 +209,7 @@ def Set(X=None, category=None): return Set_object_enumerated(X, category=category) -class Set_base(): +class Set_base: r""" Abstract base class for sets, not necessarily parents. """ @@ -492,7 +492,7 @@ def __init__(self, X, category=None): and 'Integer Ring' """ from sage.rings.integer import Integer - if isinstance(X, int) or isinstance(X, Integer): + if isinstance(X, (int, Integer)): # The coercion model will try to call Set_object(0) raise ValueError('underlying object cannot be an integer') diff --git a/src/sage/sets/set_from_iterator.py b/src/sage/sets/set_from_iterator.py index 0020ca080a1..6f68f1245b8 100644 --- a/src/sage/sets/set_from_iterator.py +++ b/src/sage/sets/set_from_iterator.py @@ -450,7 +450,7 @@ def clear_cache(self): # TODO: move it in sage.misc ? @instancedoc -class Decorator(): +class Decorator: r""" Abstract class that manage documentation and sources of the wrapped object. @@ -838,7 +838,7 @@ def __get__(self, inst, cls): **self.options) -class EnumeratedSetFromIterator_method_decorator(): +class EnumeratedSetFromIterator_method_decorator: r""" Decorator for enumerated set built from a method. diff --git a/src/sage/stats/distributions/discrete_gaussian_lattice.py b/src/sage/stats/distributions/discrete_gaussian_lattice.py index 340aa3b93eb..78f21d29fbb 100644 --- a/src/sage/stats/distributions/discrete_gaussian_lattice.py +++ b/src/sage/stats/distributions/discrete_gaussian_lattice.py @@ -308,8 +308,7 @@ def f_or_hat(x): # However, this might still drift from true value for larger lattices # So optimally one should fix the TODO above BOUND = max(1, (self._RR(10**(4 / self.n)).ceil() - 1) // 2) - if BOUND > 10: - BOUND = 10 + BOUND = min(BOUND, 10) coords = itertools.product(range(-BOUND, BOUND + 1), repeat=self.n) return sum(self.f((vector(u) + base) * self.B) for u in coords) @@ -560,7 +559,7 @@ def __init__(self, B, sigma=1, c=0, r=None, precision=None, sigma_basis=False): except TypeError: self._sigma = matrix(self._RR, sigma) # Will it be "annoying" if a matrix Sigma has different behaviour - # sometimes? There should be a parameter in the consrtuctor + # sometimes? There should be a parameter in the constructor if self._sigma == self._sigma[0, 0]: self._sigma = self._RR(self._sigma[0, 0]) else: diff --git a/src/sage/stats/distributions/meson.build b/src/sage/stats/distributions/meson.build new file mode 100644 index 00000000000..129d5f74d13 --- /dev/null +++ b/src/sage/stats/distributions/meson.build @@ -0,0 +1,31 @@ +py.install_sources( + 'all.py', + 'catalog.py', + 'dgs.pxd', + 'discrete_gaussian_integer.pxd', + 'discrete_gaussian_lattice.py', + 'discrete_gaussian_polynomial.py', + subdir: 'sage/stats/distributions', +) + +extension_data = { + 'discrete_gaussian_integer' : files( + 'dgs_bern.c', + 'dgs_gauss_dp.c', + 'dgs_gauss_mp.c', + 'discrete_gaussian_integer.pyx', + ), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + c_args: ['-D_XOPEN_SOURCE=600'], + subdir: 'sage/stats/distributions', + install: true, + include_directories: [inc_cpython, inc_rings, inc_src], + dependencies: [py_dep, cypari2, cysignals, gmp, mpfr], + ) +endforeach + diff --git a/src/sage/stats/hmm/meson.build b/src/sage/stats/hmm/meson.build new file mode 100644 index 00000000000..cbf4a30caa5 --- /dev/null +++ b/src/sage/stats/hmm/meson.build @@ -0,0 +1,26 @@ +py.install_sources( + 'all.py', + 'distributions.pxd', + 'hmm.pxd', + 'util.pxd', + subdir: 'sage/stats/hmm', +) + +extension_data = { + 'chmm' : files('chmm.pyx'), + 'distributions' : files('distributions.pyx'), + 'hmm' : files('hmm.pyx'), + 'util' : files('util.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/stats/hmm', + install: true, + include_directories: [], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + diff --git a/src/sage/stats/meson.build b/src/sage/stats/meson.build new file mode 100644 index 00000000000..414a909270c --- /dev/null +++ b/src/sage/stats/meson.build @@ -0,0 +1,27 @@ +py.install_sources( + 'all.py', + 'basic_stats.py', + 'intlist.pxd', + 'r.py', + 'time_series.pxd', + subdir: 'sage/stats', +) + +extension_data = { + 'intlist' : files('intlist.pyx'), + 'time_series' : files('time_series.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/stats', + install: true, + include_directories: [inc_cpython, inc_numpy], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +subdir('distributions') +subdir('hmm') diff --git a/src/sage/stats/time_series.pyx b/src/sage/stats/time_series.pyx index 3b36f04ab9b..0baad597dde 100644 --- a/src/sage/stats/time_series.pyx +++ b/src/sage/stats/time_series.pyx @@ -307,10 +307,10 @@ cdef class TimeSeries: if len(self) > max_print: v0 = self[:max_print//2] v1 = self[-max_print//2:] - return '[' + ', '.join(format%x for x in v0) + ' ... ' + \ - ', '.join(format%x for x in v1) + ']' + return '[' + ', '.join(format % x for x in v0) + ' ... ' + \ + ', '.join(format % x for x in v1) + ']' else: - return '[' + ', '.join(format%x for x in self) + ']' + return '[' + ', '.join(format % x for x in self) + ']' def __len__(self): r""" @@ -2024,7 +2024,7 @@ cdef class TimeSeries: sage: v [20.0000, -3.0000, 4.5000, -2.0000] """ - cnumpy.import_array() #This must be called before using the numpy C/api or you will get segfault + cnumpy.import_array() # This must be called before using the numpy C/api or you will get segfault cdef cnumpy.npy_intp dims[1] dims[0] = self._length cdef cnumpy.ndarray n = cnumpy.PyArray_SimpleNewFromData(1, dims, cnumpy.NPY_DOUBLE, self._values) diff --git a/src/sage/structure/__init__.py b/src/sage/structure/__init__.py index bf977ccc379..2534e5a71f8 100644 --- a/src/sage/structure/__init__.py +++ b/src/sage/structure/__init__.py @@ -1,3 +1,3 @@ # sage_setup: distribution = sagemath-objects # Resolve a cyclic import -import sage.structure.element +import sage.structure.element \ No newline at end of file diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index cc15eff82e9..cc2f1124cb4 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -685,7 +685,7 @@ cdef class CoercionModel: sage: cm.record_exceptions() sage: cm._test_exception_stack() sage: cm.exception_stack() - ['Traceback (most recent call last):\n File "sage/structure/coerce.pyx", line ...TypeError: just a test'] + ['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test'] sage: cm.record_exceptions(False) sage: cm._test_exception_stack() sage: cm.exception_stack() @@ -713,7 +713,7 @@ cdef class CoercionModel: [] sage: cm._test_exception_stack() sage: cm.exception_stack() - ['Traceback (most recent call last):\n File "sage/structure/coerce.pyx", line ...TypeError: just a test'] + ['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test'] The function _test_exception_stack is executing the following code:: @@ -743,7 +743,7 @@ cdef class CoercionModel: [] sage: cm._test_exception_stack() sage: cm.exception_stack() - ['Traceback (most recent call last):\n File "sage/structure/coerce.pyx", line ...TypeError: just a test'] + ['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test'] """ try: raise TypeError("just a test") @@ -1271,7 +1271,7 @@ cdef class CoercionModel: if not isinstance(y, Element): op_name = op.__name__ - mul_method = getattr(y, '__r%s__'%op_name, None) + mul_method = getattr(y, '__r%s__' % op_name, None) if mul_method is not None: res = mul_method(x) if res is not None and res is not NotImplemented: @@ -1425,7 +1425,7 @@ cdef class CoercionModel: except Exception: self._record_exception() - raise TypeError("no common canonical parent for objects with parents: '%s' and '%s'"%(xp, yp)) + raise TypeError("no common canonical parent for objects with parents: '%s' and '%s'" % (xp, yp)) cpdef coercion_maps(self, R, S): r""" diff --git a/src/sage/structure/coerce_actions.pyx b/src/sage/structure/coerce_actions.pyx index b25abdf328a..17bbd397c98 100644 --- a/src/sage/structure/coerce_actions.pyx +++ b/src/sage/structure/coerce_actions.pyx @@ -370,18 +370,18 @@ cdef class ModuleAction(Action): # At this point, we can assert it is safe to call _Xmul_ the_ring = G if self.connecting is None else self.connecting.codomain() the_set = S if self.extended_base is None else self.extended_base - assert the_ring is the_set.base(), "BUG in coercion model\n Apparently there are two versions of\n %s\n in the cache."%the_ring + assert the_ring is the_set.base(), "BUG in coercion model\n Apparently there are two versions of\n %s\n in the cache." % the_ring if not check: return if g is None: g = G.an_element() if parent(g) is not G: - raise CoercionException("The parent of %s is not %s but %s"%(g,G,parent(g))) + raise CoercionException("The parent of %s is not %s but %s" % (g, G, parent(g))) if a is None: a = S.an_element() if parent(a) is not S: - raise CoercionException("The parent of %s is not %s but %s"%(a,S,parent(a))) + raise CoercionException("The parent of %s is not %s but %s" % (a, S, parent(a))) if not isinstance(g, Element) or not isinstance(a, ModuleElement): raise CoercionException("not an Element acting on a ModuleElement") res = self.act(g, a) diff --git a/src/sage/structure/dynamic_class.py b/src/sage/structure/dynamic_class.py index 4136c79cfca..36488c76f99 100644 --- a/src/sage/structure/dynamic_class.py +++ b/src/sage/structure/dynamic_class.py @@ -144,7 +144,7 @@ def dynamic_class(name, bases, cls=None, reduction=None, doccls=None, inserted into ``C``, and its bases will be prepended to ``bases`` (unless ``prepend_cls_bases`` is ``False``). - The module, documentation and source instrospection is taken from + The module, documentation and source introspection is taken from ``doccls``, or ``cls`` if ``doccls`` is ``None``, or ``bases[0]`` if both are ``None`` (therefore ``bases`` should be non empty if ``cls`` is ``None``). @@ -305,7 +305,7 @@ class also has a zero ``__dictoffset__``. This means that the sage: pickle.loads(pickle.dumps(FooBar)) == FooBar True - We check that instrospection works reasonably:: + We check that introspection works reasonably:: sage: sage.misc.sageinspect.sage_getdoc(FooBar) 'The Foo class\n' @@ -363,7 +363,7 @@ def dynamic_class_internal(name, bases, cls=None, reduction=None, doccls=None, p sage: Foo3.__doc__ == sage.structure.dynamic_class.TestClass.__doc__ True - We check that instrospection works reasonably:: + We check that introspection works reasonably:: sage: from sage.misc.sageinspect import sage_getfile, sage_getsourcelines sage: sage_getfile(Foo2) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index e62726dedf7..18a58f46103 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -625,7 +625,7 @@ cdef class Element(SageObject): D = self.__dict__ except AttributeError: return res - for k,v in D.iteritems(): + for k, v in D.iteritems(): try: setattr(res, k, v) except AttributeError: @@ -711,7 +711,7 @@ cdef class Element(SageObject): AssertionError: self.an_element() is not in self """ tester = self._tester(**options) - SageObject._test_category(self, tester = tester) + SageObject._test_category(self, tester=tester) category = self.category() # Tests that self inherits methods from the categories if can_assign_class(self): @@ -760,13 +760,13 @@ cdef class Element(SageObject): # 100% sure we indeed call the operators == and !=, whatever # the version of Python is (see #11236) tester.assertTrue(self == self, - LazyFormat("broken equality: %s == itself is False")%self) + LazyFormat("broken equality: %s == itself is False") % self) tester.assertFalse(self == None, - LazyFormat("broken equality: %s == None")%self) + LazyFormat("broken equality: %s == None") % self) tester.assertFalse(self != self, - LazyFormat("broken non-equality: %s != itself")%self) + LazyFormat("broken non-equality: %s != itself") % self) tester.assertTrue(self != None, - LazyFormat("broken non-equality: %s is not != None")%self) + LazyFormat("broken non-equality: %s is not != None") % self) def parent(self, x=None): """ @@ -959,9 +959,9 @@ cdef class Element(SageObject): if self._is_atomic(): s = repr(self) else: - s = "(%s)"%repr(self) + s = "(%s)" % repr(self) if no_space: - return s.replace(' ','') + return s.replace(' ', '') return s def _latex_coeff_repr(self): @@ -972,7 +972,7 @@ cdef class Element(SageObject): if self._is_atomic(): return s else: - return "\\left(%s\\right)"%s + return "\\left(%s\\right)" % s def _is_atomic(self): """ @@ -1032,7 +1032,7 @@ cdef class Element(SageObject): try: zero = self._parent.zero() except Exception: - return True # by convention + return True # by convention return self != zero @@ -2365,7 +2365,7 @@ cdef class ElementWithCachedMethod(Element): attr = getattr_from_other_class(self, self._parent.category().element_class, name) - self._cached_methods = {name : attr} + self._cached_methods = {name: attr} return attr @@ -2434,14 +2434,15 @@ cdef class ModuleElement(Element): Scalar multiplication for module elements with the module element on the left and the scalar on the right. - Returning None indicates that this action is not implemented here. + Returning ``None`` indicates that this action is not implemented here. """ return None ################################################## # Other properties ################################################## - def order(self): ### DO NOT OVERRIDE THIS!!! Instead, override additive_order. + def order(self): + # DO NOT OVERRIDE THIS!!! Instead, override additive_order. """ Return the additive order of ``self``. """ @@ -2718,7 +2719,7 @@ cdef class RingElement(ModuleElement): Exponent overflow should throw an :exc:`OverflowError` (:issue:`2956`):: sage: K. = AA[] # needs sage.rings.number_field - sage: x^(2^64 + 12345) # needs sage.rings.number_field + sage: x^(2^64 + 12345) # known bug: macos # needs sage.rings.number_field Traceback (most recent call last): ... OverflowError: exponent overflow (2147483648) @@ -3101,26 +3102,27 @@ cdef class CommutativeRingElement(RingElement): # First we test some generic conditions: try: if x.is_zero(): - return True # everything divides 0 + return True # everything divides 0 except (AttributeError, NotImplementedError): pass try: if self.is_zero(): - return False # 0 divides nothing else + return False # 0 divides nothing else except (AttributeError, NotImplementedError): pass try: if self.is_unit(): - return True # units divide everything + return True # units divide everything except (AttributeError, NotImplementedError): pass try: if self.is_one(): - return True # 1 divides everything - # (is_unit() may not be implemented) + return True + # 1 divides everything + # (is_unit() may not be implemented) except (AttributeError, NotImplementedError): pass @@ -3132,7 +3134,7 @@ cdef class CommutativeRingElement(RingElement): raise NotImplementedError else: - #Different parents, use coercion + # Different parents, use coercion a, b = coercion_model.canonical_coercion(self, x) return a.divides(b) @@ -3205,7 +3207,6 @@ cdef class CommutativeRingElement(RingElement): from sage.rings.ideal import Ideal_generic if not isinstance(I, Ideal_generic) or not I.ring() is self._parent: I = self._parent.ideal(I) - #raise TypeError, "I = %s must be an ideal in %s"%(I, self.parent()) return I.reduce(self) ################################################## @@ -3353,8 +3354,8 @@ cdef class CommutativeRingElement(RingElement): ... ValueError: trying to take square root of non-square 1/(x^2 - 3) with extend = False """ - #This code is very general, it works for all integral domains that have the - #is_square(root = True) option + # This code is very general, it works for all integral domains that have the + # is_square(root = True) option from sage.categories.integral_domains import IntegralDomains P = self._parent @@ -3364,15 +3365,15 @@ cdef class CommutativeRingElement(RingElement): if P not in IntegralDomains(): raise NotImplementedError('sqrt() with all=True is only implemented for integral domains, not for %s' % P) if P.characteristic()==2 or sq_rt==0: - #0 has only one square root, and in characteristic 2 everything also has only 1 root - return [ sq_rt ] - return [ sq_rt, -sq_rt ] + # 0 has only one square root, and in characteristic 2 everything also has only 1 root + return [sq_rt] + return [sq_rt, -sq_rt] return sq_rt - #from now on we know that self is not a square + # from now on we know that self is not a square if P not in IntegralDomains(): raise NotImplementedError('sqrt() of non squares is only implemented for integral domains, not for %s' % P) if not extend: - #all square roots of a non-square should be an empty list + # all square roots of a non-square should be an empty list if all: return [] raise ValueError('trying to take square root of non-square %s with extend = False' % self) @@ -3385,8 +3386,8 @@ cdef class CommutativeRingElement(RingElement): sq_rt = PY.quotient(y**2-self, names = name)(y) if all: if P.characteristic() == 2: - return [ sq_rt ] - return [ sq_rt, -sq_rt ] + return [sq_rt] + return [sq_rt, -sq_rt] return sq_rt ############################################## @@ -3703,7 +3704,7 @@ cdef class Vector(ModuleElementWithMutability): raise bin_op_exception('*', left, right) cpdef _pairwise_product_(Vector left, Vector right): - raise TypeError("unsupported operation for '%s' and '%s'"%(parent(left), parent(right))) + raise TypeError("unsupported operation for '%s' and '%s'" % (parent(left), parent(right))) def __truediv__(self, right): """ @@ -3779,7 +3780,7 @@ cdef class Vector(ModuleElementWithMutability): """ V = magma(self._parent) v = [x._magma_init_(magma) for x in self.list()] - return '%s![%s]'%(V.name(), ','.join(v)) + return '%s![%s]' % (V.name(), ','.join(v)) def is_Vector(x): @@ -4576,7 +4577,7 @@ cpdef canonical_coercion(x, y): [1 0], [0 1] ) """ - return coercion_model.canonical_coercion(x,y) + return coercion_model.canonical_coercion(x, y) cpdef bin_op(x, y, op): diff --git a/src/sage/structure/global_options.py b/src/sage/structure/global_options.py index 854a93e6009..0f3308cc80f 100644 --- a/src/sage/structure/global_options.py +++ b/src/sage/structure/global_options.py @@ -514,7 +514,7 @@ class options(GlobalOptions): from sage.misc.instancedoc import instancedoc -class Option(): +class Option: r""" An option. diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index c0b7312d9dc..7586eb06aaf 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -14,7 +14,7 @@ from sage.structure.category_object import normalize_names -class IndexedGenerators(): +class IndexedGenerators: r"""nodetex Abstract base class for parents whose elements consist of generators indexed by an arbitrary set. @@ -650,6 +650,7 @@ def _latex_generator(self, m): return left + s + right return "%s_{%s}" % (prefix, s) + def split_index_keywords(kwds): """ Split the dictionary ``kwds`` into two dictionaries, one containing diff --git a/src/sage/structure/list_clone_timings.py b/src/sage/structure/list_clone_timings.py index f91eaab558b..a072b7287a5 100644 --- a/src/sage/structure/list_clone_timings.py +++ b/src/sage/structure/list_clone_timings.py @@ -134,6 +134,7 @@ def add1_internal(bla): blo.check() return blo + def add1_immutable(bla): """ TESTS:: @@ -147,6 +148,7 @@ def add1_immutable(bla): lbla[i] += 1 return bla.__class__(bla.parent(), lbla) + def add1_mutable(bla): """ TESTS:: @@ -162,6 +164,7 @@ def add1_mutable(bla): blo.check() return blo + def add1_with(bla): """ TESTS:: diff --git a/src/sage/structure/meson.build b/src/sage/structure/meson.build new file mode 100644 index 00000000000..6f4c75d58a4 --- /dev/null +++ b/src/sage/structure/meson.build @@ -0,0 +1,73 @@ +py.install_sources( + '__init__.py', + 'all.py', + 'category_object.pxd', + 'coerce.pxd', + 'coerce_actions.pxd', + 'coerce_dict.pxd', + 'coerce_exceptions.py', + 'coerce_maps.pxd', + 'debug_options.pxd', + 'dynamic_class.py', + 'element.pxd', + 'element_wrapper.pxd', + 'factorization.py', + 'factorization_integer.py', + 'formal_sum.py', + 'gens_py.py', + 'global_options.py', + 'indexed_generators.py', + 'list_clone.pxd', + 'list_clone_timings.py', + 'mutability.pxd', + 'nonexact.py', + 'parent.pxd', + 'parent_base.pxd', + 'parent_gens.pxd', + 'parent_old.pxd', + 'richcmp.pxd', + 'sage_object.pxd', + 'sage_object_test.py', + 'sequence.py', + 'set_factories.py', + 'set_factories_example.py', + 'support_view.py', + 'test_factory.py', + 'unique_representation.py', + subdir: 'sage/structure', +) + +extension_data = { + 'category_object' : files('category_object.pyx'), + 'coerce' : files('coerce.pyx'), + 'coerce_actions' : files('coerce_actions.pyx'), + 'coerce_dict' : files('coerce_dict.pyx'), + 'coerce_maps' : files('coerce_maps.pyx'), + 'debug_options' : files('debug_options.pyx'), + 'element' : files('element.pyx'), + 'element_wrapper' : files('element_wrapper.pyx'), + 'factory' : files('factory.pyx'), + 'list_clone' : files('list_clone.pyx'), + 'list_clone_demo' : files('list_clone_demo.pyx'), + 'list_clone_timings_cy' : files('list_clone_timings_cy.pyx'), + 'mutability' : files('mutability.pyx'), + 'parent' : files('parent.pyx'), + 'parent_base' : files('parent_base.pyx'), + 'parent_gens' : files('parent_gens.pyx'), + 'parent_old' : files('parent_old.pyx'), + 'richcmp' : files('richcmp.pyx'), + 'sage_object' : files('sage_object.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/structure', + install: true, + include_directories: [inc_cpython, inc_ext, inc_rings], + dependencies: [py_dep, cysignals, gmp, gmpy2, mpc, mpfr], + ) +endforeach + +install_subdir('proof', install_dir: sage_install_dir / 'structure') diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 44c23ff8fb0..8e8940b5846 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1680,7 +1680,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): ....: ....: def _act_(self, g, a): ....: D = {} - ....: for k, v in a.dict().items(): + ....: for k, v in a.monomial_coefficients().items(): ....: nk = [0]*len(k) ....: for i in range(len(k)): ....: nk[g(i+1)-1] = k[i] diff --git a/src/sage/structure/proof/proof.py b/src/sage/structure/proof/proof.py index 6da86141cc6..c8c2df4cafe 100644 --- a/src/sage/structure/proof/proof.py +++ b/src/sage/structure/proof/proof.py @@ -3,6 +3,7 @@ from sage.structure.sage_object import SageObject + class _ProofPref(SageObject): """ An object that holds global proof preferences. For now these are merely @@ -203,7 +204,8 @@ def polynomial(self, t=None): self._require_proof["polynomial"] = bool(t) -_proof_prefs = _ProofPref(True) #Creates the global object that stores proof preferences. +_proof_prefs = _ProofPref(True) # Creates the global object that stores proof preferences. + def get_flag(t=None, subsystem=None): """ @@ -230,7 +232,7 @@ def get_flag(t=None, subsystem=None): return t -class WithProof(): +class WithProof: """ 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 diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index fb6d4d0f686..7102316be70 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -719,7 +719,7 @@ cdef class SageObject: try: s = self._interface_init_(I) except Exception: - raise NotImplementedError("coercion of object %s to %s not implemented:\n%s\n%s" % (repr(self), I)) + raise NotImplementedError("coercion of object %s to %s not implemented" % (repr(self), I)) X = I(s) if c: try: diff --git a/src/sage/structure/test_factory.py b/src/sage/structure/test_factory.py index 9835a06f909..707feb0d409 100644 --- a/src/sage/structure/test_factory.py +++ b/src/sage/structure/test_factory.py @@ -21,10 +21,11 @@ from sage.structure.factory import UniqueFactory -class A(): +class A: # something we can weakref pass + class UniqueFactoryTester(UniqueFactory): def create_key(self, *args, **kwds): diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index a8678861b91..3a7b7f553a6 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -213,11 +213,11 @@ def unify_arguments(self, x): temp = set() # Sorting remaining variables. for j in range(i, len(a)): - if not a[j] in temp: + if a[j] not in temp: temp.add(a[j]) for j in range(i, len(b)): - if not b[j] in temp: + if b[j] not in temp: temp.add(b[j]) new_list.extend(sorted(temp, key=repr)) diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index f7177a24994..20a293fbb7b 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -273,7 +273,7 @@ def unpickle_Constant(class_name, name, conversions, latex, mathml, domain): @richcmp_method -class Constant(): +class Constant: def __init__(self, name, conversions=None, latex=None, mathml='', domain='complex'): """ diff --git a/src/sage/symbolic/constants_c_impl.pxi b/src/sage/symbolic/constants_c_impl.pxi index 63a90c71fd9..0ebd28e8aa3 100644 --- a/src/sage/symbolic/constants_c_impl.pxi +++ b/src/sage/symbolic/constants_c_impl.pxi @@ -159,7 +159,7 @@ cdef class E(Expression): [e 0] [0 e] sage: A = matrix(RDF, [[1,2],[3,4]]) - sage: e^A # rel tol 1e-14 + sage: e^A # rel tol 5e-14 [51.968956198705044 74.73656456700327] [112.10484685050491 164.07380304920997] """ diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 62abb4a0863..f221fd64de0 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -1,4 +1,4 @@ -# distutils: sources = sage/symbolic/ginac/add.cpp sage/symbolic/ginac/archive.cpp sage/symbolic/ginac/assume.cpp sage/symbolic/ginac/basic.cpp sage/symbolic/ginac/cmatcher.cpp sage/symbolic/ginac/constant.cpp sage/symbolic/ginac/context.cpp sage/symbolic/ginac/ex.cpp sage/symbolic/ginac/expair.cpp sage/symbolic/ginac/expairseq.cpp sage/symbolic/ginac/exprseq.cpp sage/symbolic/ginac/fderivative.cpp sage/symbolic/ginac/function.cpp sage/symbolic/ginac/function_info.cpp sage/symbolic/ginac/infinity.cpp sage/symbolic/ginac/infoflagbase.cpp sage/symbolic/ginac/inifcns.cpp sage/symbolic/ginac/inifcns_comb.cpp sage/symbolic/ginac/inifcns_gamma.cpp sage/symbolic/ginac/inifcns_hyperb.cpp sage/symbolic/ginac/inifcns_hyperg.cpp sage/symbolic/ginac/inifcns_nstdsums.cpp sage/symbolic/ginac/inifcns_orthopoly.cpp sage/symbolic/ginac/inifcns_trans.cpp sage/symbolic/ginac/inifcns_trig.cpp sage/symbolic/ginac/inifcns_zeta.cpp sage/symbolic/ginac/lst.cpp sage/symbolic/ginac/matrix.cpp sage/symbolic/ginac/mpoly-giac.cpp sage/symbolic/ginac/mpoly-ginac.cpp sage/symbolic/ginac/mpoly-singular.cpp sage/symbolic/ginac/mpoly.cpp sage/symbolic/ginac/mul.cpp sage/symbolic/ginac/normal.cpp sage/symbolic/ginac/numeric.cpp sage/symbolic/ginac/operators.cpp sage/symbolic/ginac/order.cpp sage/symbolic/ginac/power.cpp sage/symbolic/ginac/print.cpp sage/symbolic/ginac/pseries.cpp sage/symbolic/ginac/py_funcs.cpp sage/symbolic/ginac/registrar.cpp sage/symbolic/ginac/relational.cpp sage/symbolic/ginac/remember.cpp sage/symbolic/ginac/sum.cpp sage/symbolic/ginac/symbol.cpp sage/symbolic/ginac/templates.cpp sage/symbolic/ginac/upoly-ginac.cpp sage/symbolic/ginac/useries.cpp sage/symbolic/ginac/utils.cpp sage/symbolic/ginac/wildcard.cpp +# distutils: sources = sage/symbolic/ginac/add.cpp sage/symbolic/ginac/archive.cpp sage/symbolic/ginac/assume.cpp sage/symbolic/ginac/basic.cpp sage/symbolic/ginac/cmatcher.cpp sage/symbolic/ginac/constant.cpp sage/symbolic/ginac/context.cpp sage/symbolic/ginac/ex.cpp sage/symbolic/ginac/expair.cpp sage/symbolic/ginac/expairseq.cpp sage/symbolic/ginac/exprseq.cpp sage/symbolic/ginac/fderivative.cpp sage/symbolic/ginac/function.cpp sage/symbolic/ginac/function_info.cpp sage/symbolic/ginac/infinity.cpp sage/symbolic/ginac/infoflagbase.cpp sage/symbolic/ginac/inifcns.cpp sage/symbolic/ginac/inifcns_comb.cpp sage/symbolic/ginac/inifcns_gamma.cpp sage/symbolic/ginac/inifcns_hyperb.cpp sage/symbolic/ginac/inifcns_hyperg.cpp sage/symbolic/ginac/inifcns_nstdsums.cpp sage/symbolic/ginac/inifcns_orthopoly.cpp sage/symbolic/ginac/inifcns_trans.cpp sage/symbolic/ginac/inifcns_trig.cpp sage/symbolic/ginac/inifcns_zeta.cpp sage/symbolic/ginac/lst.cpp sage/symbolic/ginac/matrix.cpp sage/symbolic/ginac/mpoly-ginac.cpp sage/symbolic/ginac/mpoly-singular.cpp sage/symbolic/ginac/mpoly.cpp sage/symbolic/ginac/mul.cpp sage/symbolic/ginac/normal.cpp sage/symbolic/ginac/numeric.cpp sage/symbolic/ginac/operators.cpp sage/symbolic/ginac/order.cpp sage/symbolic/ginac/power.cpp sage/symbolic/ginac/print.cpp sage/symbolic/ginac/pseries.cpp sage/symbolic/ginac/py_funcs.cpp sage/symbolic/ginac/registrar.cpp sage/symbolic/ginac/relational.cpp sage/symbolic/ginac/remember.cpp sage/symbolic/ginac/sum.cpp sage/symbolic/ginac/symbol.cpp sage/symbolic/ginac/templates.cpp sage/symbolic/ginac/upoly-ginac.cpp sage/symbolic/ginac/useries.cpp sage/symbolic/ginac/utils.cpp sage/symbolic/ginac/wildcard.cpp # distutils: language = c++ # distutils: libraries = flint gmp SINGULAR_LIBRARIES # distutils: extra_compile_args = -std=c++11 SINGULAR_CFLAGS @@ -5846,12 +5846,12 @@ cdef class Expression(Expression_abc): if kwds: # Ensure that the keys are symbolic variables. - varkwds = {self._parent.var(k): v for k,v in kwds.iteritems()} + varkwds = {self._parent.var(k): v for k,v in kwds.items()} # Check for duplicate _dict_update_check_duplicate(sdict, varkwds) cdef GExMap smap - for k, v in sdict.iteritems(): + for k, v in sdict.items(): smap.insert(make_pair((self.coerce_in(k))._gobj, (self.coerce_in(v))._gobj)) res = self._gobj.subs_map(smap, 0) @@ -5999,7 +5999,7 @@ cdef class Expression(Expression_abc): if kwds: # Ensure that the keys are functions. - funkwds = {_find_func(k): v for k,v in kwds.iteritems()} + funkwds = {_find_func(k): v for k,v in kwds.items()} # Check for duplicate _dict_update_check_duplicate(sdict, funkwds) @@ -7503,7 +7503,7 @@ cdef class Expression(Expression_abc): [[-5, 0], [6, 2]] sage: g.polynomial(QQ).list() [-5, 0, 6] - sage: g.polynomial(QQ).dict() + sage: g.polynomial(QQ).monomial_coefficients() {0: -5, 2: 6} :: @@ -10589,7 +10589,7 @@ cdef class Expression(Expression_abc): 1/2*I*x + 1/2*I*sqrt(x^2 - 1) + 1/(2*I*x + 2*I*sqrt(x^2 - 1)) sage: ex.simplify(algorithm='sympy') I*(x^2 + sqrt(x^2 - 1)*x - 1)/(x + sqrt(x^2 - 1)) - sage: ex.simplify(algorithm='giac') + sage: ex.simplify(algorithm='giac') # needs giac I*sqrt(x^2 - 1) sage: ex.simplify(algorithm='fricas') # optional - fricas (I*x^2 + I*sqrt(x^2 - 1)*x - I)/(x + sqrt(x^2 - 1)) @@ -13045,6 +13045,7 @@ cdef class Expression(Expression_abc): Use Giac to perform this summation:: + sage: # needs giac sage: (sum(1/(1+k^2), k, -oo, oo, algorithm = 'giac')).factor() pi*(e^(2*pi) + 1)/((e^pi + 1)*(e^pi - 1)) @@ -13196,9 +13197,16 @@ cdef class Expression(Expression_abc): sage: integral(f, z) (x, y) |--> (x + y)*z - We check that :issue:`13097` is resolved:: + We check that :issue:`13097` is resolved (sage doesn't + crash). If giac is available, you may even get a usable + answer:: - sage: integrate(ln(1+4/5*sin(x)), x, -3.1415, 3.1415) # tol 10e-6 + sage: f = ln(1+4/5*sin(x)) + sage: integrate(f, x, -3.1415, 3.1415) # random + integrate(log(4/5*sin(x) + 1), x, -3.14150000000000, + 3.14150000000000) + sage: # needs sage.libs.giac + sage: integrate(f, x, -3.1415, 3.1415) # tol 10e-6 -1.40205228301000 """ from sage.symbolic.integration.integral import \ diff --git a/src/sage/symbolic/expression_conversion_sympy.py b/src/sage/symbolic/expression_conversion_sympy.py index ecfffc6841d..df7fda7fbed 100644 --- a/src/sage/symbolic/expression_conversion_sympy.py +++ b/src/sage/symbolic/expression_conversion_sympy.py @@ -26,6 +26,8 @@ ######### # Sympy # ######### + + class SympyConverter(Converter): """ Convert any expression to SymPy. diff --git a/src/sage/symbolic/function_factory.py b/src/sage/symbolic/function_factory.py index d626eb9bd65..057d1e07811 100644 --- a/src/sage/symbolic/function_factory.py +++ b/src/sage/symbolic/function_factory.py @@ -150,7 +150,7 @@ def unpickle_function(name, nargs, latex_name, conversions, evalf_params_first, return function_factory(*args) -def function(s, **kwds) -> Union[SymbolicFunction, list[SymbolicFunction]]: +def function(s, **kwds) -> SymbolicFunction | list[SymbolicFunction]: r""" Create a formal symbolic function with the name *s*. diff --git a/src/sage/symbolic/ginac/basic.h b/src/sage/symbolic/ginac/basic.h index b4d572e5c52..d7adf245b33 100644 --- a/src/sage/symbolic/ginac/basic.h +++ b/src/sage/symbolic/ginac/basic.h @@ -43,19 +43,6 @@ struct _object; typedef _object PyObject; #endif -#ifdef PYNAC_HAVE_LIBGIAC -namespace giac -{ - class gen; - template class tensor; - typedef class tensor polynome; -} -namespace GiNaC -{ -struct ex_is_less; -} -#endif - namespace GiNaC { class ex; @@ -315,9 +302,6 @@ class basic : public refcounted const basic & clearflag(unsigned f) const {flags &= ~f; return *this;} void ensure_if_modifiable() const; -#ifdef PYNAC_HAVE_LIBGIAC - const giac::polynome to_polynome(ex_int_map& map, exvector& revmap); -#endif // member variables tinfo_t tinfo_key; ///< type info diff --git a/src/sage/symbolic/ginac/ex.h b/src/sage/symbolic/ginac/ex.h index 6a164af270f..7b53b283b49 100644 --- a/src/sage/symbolic/ginac/ex.h +++ b/src/sage/symbolic/ginac/ex.h @@ -230,9 +230,6 @@ class ex { ex to_rational(lst & repl_lst) const; ex to_polynomial(exmap & repl) const; ex to_polynomial(lst & repl_lst) const; -#ifdef PYNAC_HAVE_LIBGIAC - const giac::polynome to_polynome(ex_int_map& map, exvector& revmap) const; -#endif const CanonicalForm to_canonical(ex_int_umap& map, power_ocvector_map& pomap, exvector& revmap) const; void collect_powers(power_ocvector_map& pomap) const; diff --git a/src/sage/symbolic/ginac/matrix.cpp b/src/sage/symbolic/ginac/matrix.cpp index c42f8f68c41..4955ddb26be 100644 --- a/src/sage/symbolic/ginac/matrix.cpp +++ b/src/sage/symbolic/ginac/matrix.cpp @@ -1108,7 +1108,7 @@ unsigned matrix::rank() const * more than once. According to W.M.Gentleman and S.C.Johnson this algorithm * is better than elimination schemes for matrices of sparse multivariate * polynomials and also for matrices of dense univariate polynomials if the - * matrix' dimesion is larger than 7. + * matrix' dimension is larger than 7. * * @return the determinant as a new expression (in expanded form) * @see matrix::determinant() */ diff --git a/src/sage/symbolic/ginac/mpoly-giac.cpp b/src/sage/symbolic/ginac/mpoly-giac.cpp deleted file mode 100644 index e980d542c65..00000000000 --- a/src/sage/symbolic/ginac/mpoly-giac.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/** @file mpoly-giac.cpp - * - * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany - * Copyright (C) 2016 Ralf Stephan - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "pynac-config.h" - -#ifdef PYNAC_HAVE_LIBGIAC - -#include -#include -#include -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE - -#include "upoly.h" -#include "basic.h" -#include "ex.h" -#include "add.h" -#include "constant.h" -#include "expairseq.h" -#include "mul.h" -#include "numeric.h" -#include "power.h" -#include "operators.h" -#include "pseries.h" -#include "relational.h" -#include "symbol.h" -#include "function.h" -#include "utils.h" - -#include -#include - -namespace GiNaC { - -static unsigned int the_dimension = 7; - -static giac::context * context_ptr=nullptr; -static giac::gen giac_zero; -static giac::gen giac_one; - - -inline giac::polynome gen2pol(const giac::gen& g) { - return giac::polynome(giac::monomial(g, the_dimension)); -} - -inline giac::gen num2gen(const numeric& n) { - auto gp = n.to_giacgen(context_ptr); - if (gp != nullptr) - return std::move(*gp); - else { - std::stringstream ss; - ss << n; - return giac::gen(std::string(ss.str()), context_ptr); - } -} - -static giac::polynome replace_with_symbol(const ex& e, ex_int_map& map, exvector& revmap) -{ - // Expression already replaced? Then return the assigned symbol - auto it = map.find(e); - if (it != map.end()) { - const int index = it->second; - giac::monomial mon(giac_one, index, the_dimension); - return giac::polynome(mon); - } - - // Otherwise create new symbol and add to dict - const int index = revmap.size() + 1; - map.insert(std::make_pair(e, index)); - revmap.push_back(e); - giac::monomial mon(giac_one, index, the_dimension); - return giac::polynome(mon); -} - -const giac::polynome basic::to_polynome(ex_int_map& map, exvector& revmap) -{ - std::cerr << *this << std::endl; - throw std::runtime_error("basic::to_polynome: can't happen"); -} - -const giac::polynome num_to_polynome(const numeric& num, - ex_int_map& map, exvector& revmap) -{ - if (num.is_real()) { - if (num.is_integer() or num.is_rational()) - return gen2pol(num2gen(num)); - else - return replace_with_symbol(num, map, revmap); - } else { // complex - numeric re = num.real(); - numeric im = num.imag(); - giac::polynome re_p(the_dimension); - giac::polynome im_p(the_dimension); - if (re.is_integer() or re.is_rational()) - re_p = gen2pol(num2gen(re)); - else - re_p = replace_with_symbol(re, map, revmap); - if (im.is_integer() or im.is_rational()) - im_p = gen2pol(num2gen(im)); - else - im_p = replace_with_symbol(im, map, revmap); - giac::polynome r = re_p + im_p * replace_with_symbol(I, map, revmap); - return r; - } -} - -// Convert to giac polynomial over QQ, filling replacement dicts -// TODO: special case numeric mpz_t, int instead of string interface -const giac::polynome ex::to_polynome(ex_int_map& map, exvector& revmap) const -{ - if (is_exactly_a(*this)) - { - const add& a = ex_to(*this); - giac::polynome p = gen2pol(giac_zero); - for (const auto& termex : a.seq) { - p = p + a.recombine_pair_to_ex(termex).to_polynome(map, revmap); - } - p = p + num_to_polynome(a.overall_coeff, map, revmap); - return p; - } - else if (is_exactly_a(*this)) - { - return num_to_polynome(ex_to(*this), map, revmap); - } - else if (is_exactly_a(*this)) - { - const mul& m = ex_to(*this); - giac::polynome p = gen2pol(giac_one); - for (const auto& termex : m.seq) { - p *= m.recombine_pair_to_ex(termex).to_polynome(map, revmap); - } - p *= num_to_polynome(m.overall_coeff, map, revmap); - return p; - } - else if (is_exactly_a(*this)) - { - const power& pow = ex_to(*this); - if (is_exactly_a(pow.exponent)) { - numeric expo = ex_to(pow.exponent); - if (pow.exponent.info(info_flags::posint)) - return std::move(giac::pow(pow.basis.to_polynome(map, revmap), expo.to_int())); - } - return replace_with_symbol(*this, map, revmap); - } - - return replace_with_symbol(*this, map, revmap); -} - -static ex gen2ex(const giac::gen& gen) -{ - // we need to handle giac types _INT_, _ZINT, and _CPLX - switch (gen.type) { - case giac::_INT_: - return numeric(gen.val); - case giac::_ZINT: - mpz_t bigint; - mpz_init_set(bigint, *(gen.ref_ZINTptr())); - return numeric(bigint); - case giac::_CPLX: - return (gen2ex(gen.ref_CPLXptr()[0]) - + I * gen2ex(gen.ref_CPLXptr()[1])); - case giac::_FRAC: - return (gen2ex(gen.ref_FRACptr()->num) - / gen2ex(gen.ref_FRACptr()->den)); - default: - std::ostringstream os; - os << "gen2ex: can't happen: " << int(gen.type) << std::flush; - throw std::runtime_error(os.str()); - } -} - -static ex polynome_to_ex(const giac::polynome& p, const exvector& revmap) -{ - ex e = _ex0; - for (const auto& mon : p.coord) { - ex prod = _ex1; - for (unsigned int varno=0; varno(a) && is_exactly_a(b)) { - numeric g = gcd(ex_to(a), ex_to(b)); - if ((ca != nullptr) || (cb != nullptr)) { - if (g.is_zero()) { - if (ca != nullptr) - *ca = _ex0; - if (cb != nullptr) - *cb = _ex0; - } else { - if (ca != nullptr) - *ca = ex_to(a) / g; - if (cb != nullptr) - *cb = ex_to(b) / g; - } - } - return g; - } - - // Partially factored cases (to avoid expanding large expressions) - if (is_exactly_a(a)) { - if (is_exactly_a(b) && b.nops() > a.nops()) - goto factored_b; -factored_a: - size_t num = a.nops(); - exvector g; g.reserve(num); - exvector acc_ca; acc_ca.reserve(num); - ex part_b = b; - for (size_t i=0; isetflag(status_flags::dynallocated); - if (cb != nullptr) - *cb = part_b; - return (new mul(g))->setflag(status_flags::dynallocated); - } else if (is_exactly_a(b)) { - if (is_exactly_a(a) && a.nops() > b.nops()) - goto factored_a; -factored_b: - size_t num = b.nops(); - exvector g; g.reserve(num); - exvector acc_cb; acc_cb.reserve(num); - ex part_a = a; - for (size_t i=0; isetflag(status_flags::dynallocated); - return (new mul(g))->setflag(status_flags::dynallocated); - } - - // Input polynomials of the form poly^n are sometimes also trivial - if (is_exactly_a(a)) { - ex p = a.op(0); - const ex& exp_a = a.op(1); - if (is_exactly_a(b)) { - ex pb = b.op(0); - const ex& exp_b = b.op(1); - if (p.is_equal(pb)) { - // a = p^n, b = p^m, gcd = p^min(n, m) - if (exp_a < exp_b) { - if (ca != nullptr) - *ca = _ex1; - if (cb != nullptr) - *cb = power(p, exp_b - exp_a); - return power(p, exp_a); - } else { - if (ca != nullptr) - *ca = power(p, exp_a - exp_b); - if (cb != nullptr) - *cb = _ex1; - return power(p, exp_b); - } - } else { - ex p_co, pb_co; - ex p_gcd = gcdpoly(p, pb, &p_co, &pb_co, check_args); - if (p_gcd.is_one()) { - // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> - // gcd(a,b) = 1 - if (ca != nullptr) - *ca = a; - if (cb != nullptr) - *cb = b; - return _ex1; - // XXX: do I need to check for p_gcd = -1? - } else { - // there are common factors: - // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> - // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m - if (exp_a < exp_b) { - return power(p_gcd, exp_a)* - gcdpoly(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); - } else { - return power(p_gcd, exp_b)* - gcdpoly(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); - } - } // p_gcd.is_equal(_ex1) - } // p.is_equal(pb) - - } else { - if (p.is_equal(b)) { - // a = p^n, b = p, gcd = p - if (ca != nullptr) - *ca = power(p, a.op(1) - 1); - if (cb != nullptr) - *cb = _ex1; - return p; - } - - ex p_co, bpart_co; - ex p_gcd = gcdpoly(p, b, &p_co, &bpart_co, false); - - if (p_gcd.is_one()) { - // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1 - if (ca != nullptr) - *ca = a; - if (cb != nullptr) - *cb = b; - return _ex1; - } else { - // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) - return p_gcd*gcdpoly(power(p_gcd, exp_a-1)*power(p_co, exp_a), bpart_co, ca, cb, false); - } - } // is_exactly_a(b) - - } else if (is_exactly_a(b)) { - ex p = b.op(0); - if (p.is_equal(a)) { - // a = p, b = p^n, gcd = p - if (ca != nullptr) - *ca = _ex1; - if (cb != nullptr) - *cb = power(p, b.op(1) - 1); - return p; - } - - ex p_co, apart_co; - const ex& exp_b(b.op(1)); - ex p_gcd = gcdpoly(a, p, &apart_co, &p_co, false); - if (p_gcd.is_one()) { - // b=p(x)^n, gcd(a, p) = 1 ==> gcd(a, b) == 1 - if (ca != nullptr) - *ca = a; - if (cb != nullptr) - *cb = b; - return _ex1; - } else { - // there are common factors: - // a(x) = g(x) A(x), b(x) = g(x)^n B(x)^n ==> gcd = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) - - return p_gcd*gcdpoly(apart_co, power(p_gcd, exp_b-1)*power(p_co, exp_b), ca, cb, false); - } // p_gcd.is_equal(_ex1) - } - - // Conversion necessary to count needed symbols beforehand - exmap repl; - ex poly_a = a.to_rational(repl); - ex poly_b = b.to_rational(repl); - - symbolset s1 = poly_a.symbols(); - const symbolset& s2 = poly_b.symbols(); - s1.insert(s2.begin(), s2.end()); - the_dimension = s1.size(); - - ex_int_map map; - exvector revmap; - - giac::polynome p = poly_a.to_polynome(map, revmap); - giac::polynome q = poly_b.to_polynome(map, revmap); - giac::polynome d(the_dimension); - giac::gcd(p, q, d); - - if (ca != nullptr) { - giac::polynome quo(the_dimension); - if (giac::exactquotient(p, d, quo)) - *ca = polynome_to_ex(quo, revmap).subs(repl, subs_options::no_pattern); - else - throw(std::runtime_error("can't happen in gcdpoly")); - } - if (cb != nullptr) { - giac::polynome quo(the_dimension); - if (giac::exactquotient(q, d, quo)) - *cb = polynome_to_ex(quo, revmap).subs(repl, subs_options::no_pattern); - else - throw(std::runtime_error("can't happen in gcdpoly")); - } - return polynome_to_ex(d, revmap).subs(repl, subs_options::no_pattern); -} - -#if 0 -ex resultantpoly(const ex & ee1, const ex & ee2, const ex & s) -{ - // Conversion necessary to count needed symbols beforehand - exmap repl; - ex poly_a = ee1.to_rational(repl); - ex poly_b = ee2.to_rational(repl); - - symbolset s1 = poly_a.symbols(); - const symbolset& s2 = poly_b.symbols(); - s1.insert(s2.begin(), s2.end()); - s1.insert(ex_to(s)); - the_dimension = s1.size(); - - ex_int_map map; - exvector revmap; - - giac::polynome p = poly_a.to_polynome(map, revmap); - giac::polynome q = poly_b.to_polynome(map, revmap); - giac::polynome d = giac::resultant(p, q); - return polynome_to_ex(d, revmap).subs(repl, subs_options::no_pattern); -} -#endif - -bool factorpoly(const ex& the_ex, ex& res_prod) -{ - if (is_exactly_a(the_ex) - or is_exactly_a(the_ex) - or is_exactly_a(the_ex) - or is_exactly_a(the_ex)) - return false; - - if (is_exactly_a(the_ex)) { - const mul& m = ex_to(the_ex); - exvector ev; - bool mchanged = false; - for (size_t i=0; i(the_ex)) { - const power& pow = ex_to(the_ex); - ex prod; - bool res = factorpoly(pow.op(0), prod); - if (not res) - return false; - res_prod = power(prod, pow.op(1)); - return true; - } - - if (not is_exactly_a(the_ex)) - throw(std::runtime_error("can't happen in factor")); - - if (context_ptr == nullptr) { - context_ptr=new giac::context(); - giac_zero = giac::gen(std::string("0"), context_ptr); - giac_one = giac::gen(std::string("1"), context_ptr); - } - - exmap repl; - ex poly = the_ex.to_rational(repl); - symbolset s1 = poly.symbols(); - the_dimension = s1.size(); - - ex_int_map map; - exvector revmap; - giac::polynome p = the_ex.to_polynome(map, revmap); - giac::polynome p_content(the_dimension); - giac::factorization f; - bool res = factor(p, p_content, f, - false, false, false, giac_one, giac_one); - if (not res) - return false; - res_prod = polynome_to_ex(p_content, revmap).subs(repl, subs_options::no_pattern); - for (auto fpair : f) - res_prod = mul(res_prod, - power(polynome_to_ex(fpair.fact, revmap).subs(repl, subs_options::no_pattern), - fpair.mult)); - return true; -} - -ex poly_mul_expand(const ex& a, const ex& b) -{ - exmap repl; - ex poly_a = a.to_rational(repl); - ex poly_b = b.to_rational(repl); - - symbolset s1 = poly_a.symbols(); - const symbolset& s2 = poly_b.symbols(); - s1.insert(s2.begin(), s2.end()); - the_dimension = s1.size(); - - ex_int_map map; - exvector revmap; - - giac::polynome p = poly_a.to_polynome(map, revmap); - giac::polynome q = poly_b.to_polynome(map, revmap); - giac::polynome d(the_dimension); - d = p * q; - - return polynome_to_ex(d, revmap).subs(repl, subs_options::no_pattern); -} - -} // namespace GiNaC - -#endif // HAVE_LIBGIAC diff --git a/src/sage/symbolic/ginac/mpoly-ginac.cpp b/src/sage/symbolic/ginac/mpoly-ginac.cpp index 608e0f6f207..02a07504a73 100644 --- a/src/sage/symbolic/ginac/mpoly-ginac.cpp +++ b/src/sage/symbolic/ginac/mpoly-ginac.cpp @@ -51,8 +51,6 @@ namespace GiNaC { -#ifndef PYNAC_HAVE_LIBGIAC - // If comparing expressions (ex::compare()) is fast, you can set this to 1. // Some routines like quo(), rem() and gcd() will then return a quick answer // when they are called with two identical arguments. @@ -547,7 +545,5 @@ ex sqrfree(const ex &a, const lst &l) #endif -#endif // HAVE_LIBGIAC - } // namespace GiNaC diff --git a/src/sage/symbolic/ginac/mpoly-singular.cpp b/src/sage/symbolic/ginac/mpoly-singular.cpp index 3050a96bac6..e66694ec1e0 100644 --- a/src/sage/symbolic/ginac/mpoly-singular.cpp +++ b/src/sage/symbolic/ginac/mpoly-singular.cpp @@ -344,8 +344,6 @@ static ex canonical_to_ex(const CanonicalForm& f, const exvector& revmap) } // GCD of two exes which are in polynomial form -// If giac is requested we stand back -#ifndef PYNAC_HAVE_LIBGIAC ex gcdpoly(const ex &a, const ex &b, ex *ca=nullptr, ex *cb=nullptr, bool check_args=true) { if (a.is_zero()) @@ -648,8 +646,6 @@ ex poly_mul_expand(const ex& a, const ex& b) return res; } -#endif //PYNAC_HAVE_LIBGIAC - ex resultantpoly(const ex & ee1, const ex & ee2, const ex & s) { ex_int_umap map; diff --git a/src/sage/symbolic/ginac/normal.cpp b/src/sage/symbolic/ginac/normal.cpp index d2c9e7be637..303f12d1aa7 100644 --- a/src/sage/symbolic/ginac/normal.cpp +++ b/src/sage/symbolic/ginac/normal.cpp @@ -1093,13 +1093,6 @@ static ex find_common_factor(const ex & e, ex & factor, exmap & repl) if (gc.is_one()) return e; -#ifdef PYNAC_HAVE_LIBGIAC - else { - ex f = 1; - gc = find_common_factor(gc, f, repl); - gc *= f; - } -#endif // The GCD is the factor we pull out factor *= gc; diff --git a/src/sage/symbolic/ginac/numeric.cpp b/src/sage/symbolic/ginac/numeric.cpp index 4bcf45e8793..a9bbfba9e15 100644 --- a/src/sage/symbolic/ginac/numeric.cpp +++ b/src/sage/symbolic/ginac/numeric.cpp @@ -74,15 +74,6 @@ #include "factory/factory.h" #pragma clang diagnostic pop -#ifdef PYNAC_HAVE_LIBGIAC -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE - -#include -#include -#include -#endif - //#define Logging_refctr #if defined(Logging_refctr) #undef Py_INCREF @@ -3258,33 +3249,6 @@ void numeric::canonicalize() } } -#ifdef PYNAC_HAVE_LIBGIAC -giac::gen* numeric::to_giacgen(giac::context* cptr) const -{ - if (t == LONG) - return new giac::gen(v._long); - if (t == MPZ) { - mpz_t bigint; - mpz_init_set(bigint, v._bigint); - auto ret = new giac::gen(bigint); - mpz_clear(bigint); - return ret; - } - if (t == MPQ) { - mpz_t bigint; - mpz_init_set(bigint, mpq_numref(v._bigrat)); - giac::gen gn(bigint); - mpz_set(bigint, mpq_denref(v._bigrat)); - giac::gen gd(bigint); - giac::Tfraction frac(gn, gd); - mpz_clear(bigint); - return new giac::gen(frac); - } - else - return nullptr; -} -#endif - CanonicalForm numeric::to_canonical() const { if (t == LONG) diff --git a/src/sage/symbolic/ginac/numeric.h b/src/sage/symbolic/ginac/numeric.h index 50b2b3d5e31..03697b2a474 100644 --- a/src/sage/symbolic/ginac/numeric.h +++ b/src/sage/symbolic/ginac/numeric.h @@ -64,12 +64,6 @@ PyObject* CC_get(); class CanonicalForm; -#ifdef PYNAC_HAVE_LIBGIAC -namespace giac { - class context; -} -#endif - namespace GiNaC { enum Type { @@ -246,9 +240,6 @@ class numeric : public basic { const numeric try_py_method(const std::string& s, const numeric& x2) const; const numeric to_dict_parent(PyObject* dict) const; -#ifdef PYNAC_HAVE_LIBGIAC - giac::gen* to_giacgen(giac::context*) const; -#endif CanonicalForm to_canonical() const; const numeric real() const; diff --git a/src/sage/symbolic/ginac/pynac-config.h b/src/sage/symbolic/ginac/pynac-config.h index 4d686828c7f..00d7445049a 100644 --- a/src/sage/symbolic/ginac/pynac-config.h +++ b/src/sage/symbolic/ginac/pynac-config.h @@ -7,8 +7,5 @@ /* Current GiNaC archive file version number */ #define PYNAC_ARCHIVE_VERSION 3 -/* Define if you have libgiac */ -/* #undef PYNAC_HAVE_LIBGIAC */ - /* once: _PYNAC_CONFIG_H */ #endif diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 968d0e7e7c3..5f647228564 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -285,7 +285,15 @@ def libgiac_integrator(expression, v, a=None, b=None): sage: (F.derivative(x) - f).simplify_trig() 0 """ - from sage.libs.giac import libgiac + try: + from sage.libs.giac import libgiac + except ImportError: + # If libgiac isn't available, return a symbolic answer + # (without actually integrating anything). This is essentially + # the failure case of any integration: see below for what we + # do if libgiac is *available* but unable to do much. + return expression.integrate(v, a, b, hold=True) + from sage.libs.giac.giac import Pygen # We call Pygen on first argument because otherwise some expressions # involving derivatives result in doctest failures in interfaces/sympy.py diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 70bb877e5a1..0877d132030 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -71,17 +71,22 @@ def __init__(self): sage: (f*f).integrate(x, algorithm='mathematica_free') # optional -- internet -b*log(e^(a/b) + e^(x/b)) + x + b/(e^(-(a - x)/b) + 1) - Check for :issue:`25119`:: + After :issue:`25119` we can integrate the following function, + although giac and sympy give different-looking answers:: sage: result = integrate(sqrt(x^2)/x,x) ... - sage: result - x*sgn(x) + sage: result in [x*sgn(x), sqrt(x^2)] + True """ # The automatic evaluation routine will try these integrators # in the given order. This is an attribute of the class instead of # a global variable in this module to enable customization by # creating a subclasses which define a different set of integrators + # + # The libgiac integrator may immediately return a symbolic + # (unevaluated) answer if libgiac is unavailable. This essentially + # causes it to be skipped. self.integrators = [external.maxima_integrator, external.libgiac_integrator, external.sympy_integrator] @@ -106,8 +111,10 @@ def _eval_(self, f, x): sage: integrate(1/(x^4 + x^3 + 1), x) integrate(1/(x^4 + x^3 + 1), x) - Check that :issue:`32002` is fixed:: + Check that :issue:`32002` is fixed. This needs giac since only + giac can integrate it in any case:: + sage: # needs sage.libs.giac sage: result = integral(2*min_symbolic(x,2*x),x) ... sage: result @@ -942,21 +949,38 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): Some integrals are now working (:issue:`27958`, using giac or sympy):: - sage: result = integrate(1/(1 + abs(x)), x) + sage: result = integrate(abs(x^2 - 1), x, -2, 2) ... sage: result - log(abs(x*sgn(x) + 1))/sgn(x) + 4 - sage: result = integrate(cos(x + abs(x)), x) + sage: result = integrate(sgn(x) - sgn(1-x), x) ... sage: result - sin(x*sgn(x) + x)/(sgn(x) + 1) + abs(x - 1) + abs(x) + + sage: result = integrate(1 / (1 + abs(x-5)), x, -5, 6) + ... + sage: result + log(11) + log(2) sage: result = integrate(abs(x^2 - 1), x, -2, 2) ... sage: result 4 + Examples that only giac can correctly integrate (for now):: + + sage: # needs sage.libs.giac + sage: g = abs(sin(x)*cos(x)) + sage: result = g.integrate(x, 0, 2*pi) + ... + sage: result + 2 + + :: + + sage: # needs sage.libs.giac sage: f = sqrt(x + 1/x^2) sage: actual = integrate(f, x) ... @@ -965,42 +989,46 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): sage: bool(actual == expected) True - sage: g = abs(sin(x)*cos(x)) - sage: result = g.integrate(x, 0, 2*pi) - ... - sage: result - 2 + :: - sage: result = integrate(1/sqrt(abs(x)), x) + sage: # needs sage.libs.giac + sage: result = integrate(cos(x + abs(x)), x) ... sage: result - 2*sqrt(x*sgn(x))/sgn(x) + sin(x*sgn(x) + x)/(sgn(x) + 1) - sage: result = integrate(sgn(x) - sgn(1-x), x) + :: + + sage: # needs sage.libs.giac + sage: result = integrate(1/(1 + abs(x)), x) ... sage: result - abs(x - 1) + abs(x) + log(abs(x*sgn(x) + 1))/sgn(x) - sage: result = integrate(1 / (1 + abs(x-5)), x, -5, 6) + :: + + sage: # needs sage.libs.giac + sage: result = integrate(1/sqrt(abs(x)), x) ... sage: result - log(11) + log(2) + 2*sqrt(x*sgn(x))/sgn(x) + :: + + sage: # needs sage.libs.giac sage: result = integrate(1/(1 + abs(x)), x) ... sage: result log(abs(x*sgn(x) + 1))/sgn(x) + :: + + sage: # needs sage.libs.giac sage: result = integrate(cos(x + abs(x)), x) ... sage: result sin(x*sgn(x) + x)/(sgn(x) + 1) - sage: result = integrate(abs(x^2 - 1), x, -2, 2) - ... - sage: result - 4 - Some tests for :issue:`17468`:: sage: integral(log(abs(2*sin(x))), x, 0, pi/3) diff --git a/src/sage/symbolic/meson.build b/src/sage/symbolic/meson.build new file mode 100644 index 00000000000..a9ca57ebf43 --- /dev/null +++ b/src/sage/symbolic/meson.build @@ -0,0 +1,152 @@ +inc_ginac = include_directories('ginac') +inc_pynac = include_directories('.') + +py.install_sources( + 'all.py', + 'assumptions.py', + 'benchmark.py', + 'callable.py', + 'complexity_measures.py', + 'constants.py', + 'expression.pxd', + 'expression_conversion_algebraic.py', + 'expression_conversion_sympy.py', + 'expression_conversions.py', + 'function.pxd', + 'function_factory.py', + 'maxima_wrapper.py', + 'operators.py', + 'pynac_wrap.h', + 'random_tests.py', + 'relation.py', + 'ring.pxd', + 'subring.py', + 'symbols.py', + 'symengine.py', + 'tests.py', + 'units.py', + subdir: 'sage/symbolic', +) + +extension_data = { + 'function' : files('function.pyx'), + 'ring' : files('ring.pyx'), +} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/symbolic', + install: true, + include_directories: [ + inc_cpython, + inc_ext, + inc_ginac, + inc_gsl, + inc_pynac, + inc_rings, + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + flint, + gmp, + gsl, + mpfr, + pari, + singular, + ], + ) +endforeach + +extension_data_cpp = { + 'expression': files( + 'expression.pyx', + 'ginac/add.cpp', + 'ginac/archive.cpp', + 'ginac/assume.cpp', + 'ginac/basic.cpp', + 'ginac/cmatcher.cpp', + 'ginac/constant.cpp', + 'ginac/context.cpp', + 'ginac/ex.cpp', + 'ginac/expair.cpp', + 'ginac/expairseq.cpp', + 'ginac/exprseq.cpp', + 'ginac/fderivative.cpp', + 'ginac/function.cpp', + 'ginac/function_info.cpp', + 'ginac/infinity.cpp', + 'ginac/infoflagbase.cpp', + 'ginac/inifcns.cpp', + 'ginac/inifcns_comb.cpp', + 'ginac/inifcns_gamma.cpp', + 'ginac/inifcns_hyperb.cpp', + 'ginac/inifcns_hyperg.cpp', + 'ginac/inifcns_nstdsums.cpp', + 'ginac/inifcns_orthopoly.cpp', + 'ginac/inifcns_trans.cpp', + 'ginac/inifcns_trig.cpp', + 'ginac/inifcns_zeta.cpp', + 'ginac/lst.cpp', + 'ginac/matrix.cpp', + 'ginac/mpoly-ginac.cpp', + 'ginac/mpoly-singular.cpp', + 'ginac/mpoly.cpp', + 'ginac/mul.cpp', + 'ginac/normal.cpp', + 'ginac/numeric.cpp', + 'ginac/operators.cpp', + 'ginac/order.cpp', + 'ginac/power.cpp', + 'ginac/print.cpp', + 'ginac/pseries.cpp', + 'ginac/py_funcs.cpp', + 'ginac/registrar.cpp', + 'ginac/relational.cpp', + 'ginac/remember.cpp', + 'ginac/sum.cpp', + 'ginac/symbol.cpp', + 'ginac/templates.cpp', + 'ginac/upoly-ginac.cpp', + 'ginac/useries.cpp', + 'ginac/utils.cpp', + 'ginac/wildcard.cpp', + ), +} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/symbolic', + install: true, + override_options: ['cython_language=cpp'], + cpp_args: '-std=c++11', + include_directories: [ + inc_cpython, + inc_ext, + inc_ginac, + inc_gsl, + inc_pynac, + inc_rings, + include_directories('../libs/gmp'), + ], + dependencies: [ + py_dep, + cypari2, + cysignals, + gmp, + flint, + gsl, + m, + mpfr, + pari, + singular, + ], + ) +endforeach + +install_subdir('integration', install_dir: sage_install_dir / 'symbolic') diff --git a/src/sage/symbolic/operators.py b/src/sage/symbolic/operators.py index 4e1debed2b7..fcada6cdcdf 100644 --- a/src/sage/symbolic/operators.py +++ b/src/sage/symbolic/operators.py @@ -72,7 +72,7 @@ def mul_vararg(first, *rest): operator.ge: '>='} -class FDerivativeOperator(): +class FDerivativeOperator: r""" Function derivative operators. @@ -201,7 +201,7 @@ def parameter_set(self): return self._parameter_set -class DerivativeOperator(): +class DerivativeOperator: """ Derivative operator. @@ -226,7 +226,7 @@ class DerivativeOperator(): sage: D[0, 1](f)(x, x^2) D[0, 1](f)(x, x^2) """ - class DerivativeOperatorWithParameters(): + class DerivativeOperatorWithParameters: def __init__(self, parameter_set): self._parameter_set = parameter_set diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index cba2493de95..3291fe3cea0 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -258,8 +258,7 @@ def random_expr_helper(n_nodes, internal, leaves, verbose): n_nodes -= 1 n_children = r[2] n_spare_nodes = n_nodes - n_children - if n_spare_nodes <= 0: - n_spare_nodes = 0 + n_spare_nodes = max(0, n_spare_nodes) nodes_per_child = random_integer_vector(n_spare_nodes, n_children) children = [random_expr_helper(n + 1, internal, leaves, verbose) for n in nodes_per_child] diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index 59605d2e72b..d0098871834 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -621,7 +621,8 @@ def solve(f, *args, **kwds): - ``algorithm`` -- string (default: ``'maxima'``); to use SymPy's solvers set this to 'sympy'. Note that SymPy is always used - for diophantine equations. Another choice is 'giac'. + for diophantine equations. Another choice, if it is installed, + is 'giac'. - ``domain`` -- string (default: ``'complex'``); setting this to 'real' changes the way SymPy solves single equations; inequalities @@ -926,13 +927,16 @@ def solve(f, *args, **kwds): A basic interface to Giac is provided:: + sage: # needs sage.libs.giac sage: solve([(2/3)^x-2], [x], algorithm='giac') ...[[-log(2)/(log(3) - log(2))]] + sage: # needs sage.libs.giac sage: f = (sin(x) - 8*cos(x)*sin(x))*(sin(x)^2 + cos(x)) - (2*cos(x)*sin(x) - sin(x))*(-2*sin(x)^2 + 2*cos(x)^2 - cos(x)) sage: solve(f, x, algorithm='giac') ...[-2*arctan(sqrt(2)), 0, 2*arctan(sqrt(2)), pi] + sage: # needs sage.libs.giac sage: x, y = SR.var('x,y') sage: solve([x+y-4,x*y-3],[x,y],algorithm='giac') [[1, 3], [3, 1]] @@ -1434,17 +1438,20 @@ def _giac_solver(f, x, solution_dict=False): EXAMPLES:: + sage: # needs sage.libs.giac sage: solve([(2/3)^x-2], [x], algorithm='giac') ...[[-log(2)/(log(3) - log(2))]] sage: solve([(2/3)^x-2], [x], algorithm='giac', solution_dict=True) ...[{x: -log(2)/(log(3) - log(2))}] + sage: # needs sage.libs.giac sage: f = (sin(x) - 8*cos(x)*sin(x))*(sin(x)^2 + cos(x)) - (2*cos(x)*sin(x) - sin(x))*(-2*sin(x)^2 + 2*cos(x)^2 - cos(x)) sage: solve(f, x, algorithm='giac') ...[-2*arctan(sqrt(2)), 0, 2*arctan(sqrt(2)), pi] sage: solve(f, x, algorithm='giac', solution_dict=True) ...[{x: -2*arctan(sqrt(2))}, {x: 0}, {x: 2*arctan(sqrt(2))}, {x: pi}] + sage: # needs sage.libs.giac sage: x, y = SR.var('x,y') sage: solve([x+y-7,x*y-10],[x,y],algorithm='giac') [[2, 5], [5, 2]] @@ -1458,7 +1465,7 @@ def _giac_solver(f, x, solution_dict=False): if not sols: return [] if isinstance(sols[0], list): - return [{v: sv for v, sv in zip(x, solution)} for solution in sols] + return [dict(zip(x, solution)) for solution in sols] return [{x: sx} for sx in sols] return sols @@ -1803,7 +1810,7 @@ def solve_ineq_fourier(ineq, vars=None): setvars = set([]) for i in (ineq): setvars = setvars.union(set(i.variables())) - vars = [i for i in setvars] + vars = list(setvars) ineq0 = [i._maxima_() for i in ineq] ineq0[0].parent().eval("if fourier_elim_loaded#true then (fourier_elim_loaded:true,load(\"fourier_elim\"))") sol = ineq0[0].parent().fourier_elim(ineq0, vars) diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index 899b8087b96..2593b67476e 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -40,7 +40,7 @@ sage: t.convert() 1/100000*kilogram*meter/second^2 -Giving improper units to convert to raises a ValueError:: +Giving improper units to convert to raises a :exc:`ValueError`:: sage: t.convert(units.charge.coulomb) Traceback (most recent call last): @@ -55,7 +55,7 @@ sage: s.convert() 293.150000000000*kelvin -Trying to multiply temperatures by another unit then converting raises a ValueError:: +Trying to multiply temperatures by another unit then converting raises a :exc:`ValueError`:: sage: wrong = 50*units.temperature.celsius*units.length.foot sage: wrong.convert() @@ -94,20 +94,23 @@ from .expression import Expression from sage.interfaces.tab_completion import ExtraTabCompletion from sage.misc.instancedoc import instancedoc +from sage.rings.rational_field import QQ ############################################################################### # Unit conversions dictionary. ############################################################################### +one = QQ.one() + unitdict = { 'acceleration': - {'gal': '1/100', - 'galileo': '1/100', + {'gal': one / 100, + 'galileo': one / 100, 'gravity': '9.80665000000000'}, 'amount_of_substance': {'elementary_entity': '1/6.02214129000000e23', - 'mole': '1'}, + 'mole': 1}, 'angles': {'arc_minute': '1/10800*pi', @@ -115,173 +118,173 @@ 'degree': '1/180*pi', 'grade': '1/200*pi', 'quadrant': '1/2*pi', - 'radian': '1', + 'radian': 1, 'right_angle': '1/2*pi'}, 'area': - {'acre': '316160658/78125', - 'are': '100', - 'barn': '1/10000000000000000000000000000', - 'hectare': '10000', - 'rood': '158080329/156250', - 'section': '40468564224/15625', - 'square_chain': '158080329/390625', - 'square_meter': '1', - 'township': '1456868312064/15625'}, + {'acre': QQ(316160658) / 78125, + 'are': 100, + 'barn': one / 10000000000000000000000000000, + 'hectare': 10000, + 'rood': QQ(158080329) / 156250, + 'section': QQ(40468564224) / 15625, + 'square_chain': QQ(158080329) / 390625, + 'square_meter': 1, + 'township': QQ(1456868312064) / 15625}, 'capacitance': - {'abfarad': '1000000000', - 'farad': '1', - 'statfarad': '25000/22468879468420441'}, + {'abfarad': 1000000000, + 'farad': 1, + 'statfarad': QQ(25000) / 22468879468420441}, 'charge': - {'abcoulomb': '10', - 'coulomb': '1', + {'abcoulomb': 10, + 'coulomb': 1, 'elementary_charge': '1.60217646200000e-19', 'faraday': '96485.3399000000', - 'franklin': '1/2997924580', - 'statcoulomb': '1/2997924580'}, + 'franklin': one / 2997924580, + 'statcoulomb': one / 2997924580}, 'conductance': - {'abmho': '1000000000', - 'mho': '1', - 'siemens': '1'}, + {'abmho': 1000000000, + 'mho': 1, + 'siemens': 1}, 'current': - {'abampere': '10', - 'amp': '1', - 'ampere': '1', - 'biot': '10', - 'statampere': '1/2997924580'}, + {'abampere': 10, + 'amp': 1, + 'ampere': 1, + 'biot': 10, + 'statampere': one / 2997924580}, 'electric_potential': - {'abvolt': '1/100000000', - 'statvolt': '149896229/500000', - 'volt': '1'}, + {'abvolt': one / 100000000, + 'statvolt': QQ(149896229) / 500000, + 'volt': 1}, 'energy': - {'british_thermal_unit': '52752792631/50000000', - 'btu': '52752792631/50000000', - 'calorie': '10467/2500', + {'british_thermal_unit': QQ(52752792631) / 50000000, + 'btu': QQ(52752792631) / 50000000, + 'calorie': QQ(10467) / 2500, 'electron_volt': '1.60217733000000e-19', - 'erg': '1/10000000', + 'erg': one / 10000000, 'ev': '1.60217733000000e-19', - 'joule': '1', + 'joule': 1, 'rydberg': '2.17987200000000e-18', - 'therm': '52752792631/500'}, + 'therm': QQ(52752792631) / 500}, 'fiber_linear_mass_density': - {'denier': '1/9000000', - 'tex': '1/1000000'}, + {'denier': one / 9000000, + 'tex': one / 1000000}, 'force': - {'dyne': '1/100000', - 'gram_weight': '196133/20000000', - 'kilogram_force': '196133/20000', - 'kilogram_weight': '196133/20000', - 'newton': '1', - 'pound_force': '8896443230521/2000000000000', - 'pound_weight': '8896443230521/2000000000000', - 'poundal': '17281869297/125000000000', - 'ton_force': '8896443230521/1000000000'}, + {'dyne': one / 100000, + 'gram_weight': QQ(196133) / 20000000, + 'kilogram_force': QQ(196133) / 20000, + 'kilogram_weight': QQ(196133) / 20000, + 'newton': 1, + 'pound_force': QQ(8896443230521) / 2000000000000, + 'pound_weight': QQ(8896443230521) / 2000000000000, + 'poundal': QQ(17281869297) / 125000000000, + 'ton_force': QQ(8896443230521) / 1000000000}, 'frequency': - {'1/second': '1', - 'hertz': '1'}, + {'1/second': 1, + 'hertz': 1}, 'illuminance': - {'foot_candle': '1562500/145161', - 'lux': '1', - 'phot': '10000'}, + {'foot_candle': QQ(1562500) / 145161, + 'lux': 1, + 'phot': 10000}, 'inductance': - {'abhenry': '1/1000000000', - 'henry': '1', - 'stathenry': '22468879468420441/25000'}, + {'abhenry': one / 1000000000, + 'henry': 1, + 'stathenry': QQ(22468879468420441) / 25000}, 'information': - {'bit': '1', - 'byte': '8', - 'nibble': '4'}, + {'bit': 1, + 'byte': 8, + 'nibble': 4}, 'information_rate': - {'baud': '1'}, + {'baud': 1}, 'inverse_length': - {'diopter': '1', - 'kayser': '100'}, + {'diopter': 1, + 'kayser': 100}, 'length': - {'angstrom': '1/10000000000', - 'astronomical_unit': '149597870691', - 'bolt': '4572/125', - 'cable_international': '926/5', - 'cable_us': '27432/125', - 'caliber': '127/500000', - 'centimeter': '1/100', - 'chain': '12573/625', - 'cicero': '125/27706', - 'cubit': '1143/2500', - 'didot': '125/332472', - 'dtp_point': '127/360000', - 'ell': '1143/1000', - 'fathom': '1143/625', - 'feet': '381/1250', - 'fermi': '1/1000000000000000', - 'foot': '381/1250', - 'furlong': '25146/125', - 'hand': '127/1250', - 'inch': '127/5000', - 'kilometer': '1000', - 'league': '603504/125', - 'light_year': '9460730472580800', - 'link': '12573/62500', - 'meter': '1', - 'micron': '1/1000000', - 'mil': '127/5000000', - 'millimeter': '1/1000', - 'mile': '201168/125', - 'nautical_mile': '1852', + {'angstrom': one / 10000000000, + 'astronomical_unit': 149597870691, + 'bolt': QQ(4572) / 125, + 'cable_international': QQ(926) / 5, + 'cable_us': QQ(27432) / 125, + 'caliber': QQ(127) / 500000, + 'centimeter': one / 100, + 'chain': QQ(12573) / 625, + 'cicero': QQ(125) / 27706, + 'cubit': QQ(1143) / 2500, + 'didot': QQ(125) / 332472, + 'dtp_point': QQ(127) / 360000, + 'ell': QQ(1143) / 1000, + 'fathom': QQ(1143) / 625, + 'feet': QQ(381) / 1250, + 'fermi': one / 1000000000000000, + 'foot': QQ(381) / 1250, + 'furlong': QQ(25146) / 125, + 'hand': QQ(127) / 1250, + 'inch': QQ(127) / 5000, + 'kilometer': 1000, + 'league': QQ(603504) / 125, + 'light_year': 9460730472580800, + 'link': QQ(12573) / 62500, + 'meter': 1, + 'micron': one / 1000000, + 'mil': QQ(127) / 5000000, + 'millimeter': one / 1000, + 'mile': QQ(201168) / 125, + 'nautical_mile': 1852, 'parsec': '3.08570000000000e16', - 'perch': '12573/2500', - 'pica': '127/30000', - 'pole': '12573/2500', - 'rod': '12573/2500', - 'rope': '762/125', - 'skein': '13716/125', - 'stadion': '118491/625', - 'stadium': '115443/625', - 'statute_mile': '201168/125', - 'survey_foot': '1200/3937', - 'survey_mile': '6336000/3937', + 'perch': QQ(12573) / 2500, + 'pica': QQ(127) / 30000, + 'pole': QQ(12573) / 2500, + 'rod': QQ(12573) / 2500, + 'rope': QQ(762) / 125, + 'skein': QQ(13716) / 125, + 'stadion': QQ(118491) / 625, + 'stadium': QQ(115443) / 625, + 'statute_mile': QQ(201168) / 125, + 'survey_foot': QQ(1200) / 3937, + 'survey_mile': QQ(6336000) / 3937, 'x_unit': '1.00210000000000e-13', - 'yard': '1143/1250'}, + 'yard': QQ(1143) / 1250}, 'luminance': {'apostilb': '1/pi', 'lambert': '10000/pi', - 'nit': '1', - 'stilb': '10000'}, + 'nit': 1, + 'stilb': 10000}, 'luminous_energy': - {'lumerg': '1', - 'talbot': '1'}, + {'lumerg': 1, + 'talbot': 1}, 'luminous_flux': - {'lumen': '1'}, + {'lumen': 1}, 'luminous_intensity': - {'candela': '1', - 'candle': '1', - 'hefnerkerze': '1019/1128'}, + {'candela': 1, + 'candle': 1, + 'hefnerkerze': QQ(1019) / 1128}, 'magnetic_field': - {'gauss': '1/10000', - 'tesla': '1'}, + {'gauss': one / 10000, + 'tesla': 1}, 'magnetic_flux': - {'maxwell': '1/100000000', - 'weber': '1'}, + {'maxwell': one / 100000000, + 'weber': 1}, 'magnetic_intensity': {'oersted': '250/pi'}, @@ -291,110 +294,110 @@ 'nuclear_magneton': '5.05078324000000e-27'}, 'magnetomotive_force': - {'ampere_turn': '1', + {'ampere_turn': 1, 'gilbert': '5/2/pi'}, 'mass': {'amu': '1.66053878200000e-27', - 'assay_ton': '7/240', + 'assay_ton': QQ(7) / 240, 'atomic_mass_unit': '1.66053878200000e-27', - 'avoirdupois_ounce': '45359237/1600000000', - 'avoirdupois_pound': '45359237/100000000', - 'bale': '45359237/200000', - 'carat': '1/5000', - 'cental': '45359237/1000000', + 'avoirdupois_ounce': QQ(45359237) / 1600000000, + 'avoirdupois_pound': QQ(45359237) / 100000000, + 'bale': QQ(45359237) / 200000, + 'carat': one / 5000, + 'cental': QQ(45359237) / 1000000, 'dalton': '1.66053878200000e-27', 'drachma': "(0.00429234000000000, {'greek':1})", - 'geepound': '14593903/1000000', - 'grain': '6479891/100000000000', - 'gram': '1/1000', - 'gross_hundredweight': '317514659/6250000', - 'hundredweight': '317514659/6250000', - 'kilogram': '1', + 'geepound': QQ(14593903) / 1000000, + 'grain': QQ(6479891) / 100000000000, + 'gram': one / 1000, + 'gross_hundredweight': QQ(317514659) / 6250000, + 'hundredweight': QQ(317514659) / 6250000, + 'kilogram': 1, 'libra': '0.325971000000000', - 'long_ton': '317514659/312500', + 'long_ton': QQ(317514659) / 312500, 'metric_ton': '1000', 'mina': "(0.429234000000000, {'greek':100})", - 'net_hundredweight': '45359237/1000000', + 'net_hundredweight': QQ(45359237) / 1000000, 'obol': "(0.000715380000000000,{'greek':1/6})", - 'ounce': '45359237/1600000000', - 'ounce_troy': '19439673/625000000', - 'pennyweight': '19439673/12500000000', + 'ounce': QQ(45359237) / 1600000000, + 'ounce_troy': QQ(19439673) / 625000000, + 'pennyweight': QQ(19439673) / 12500000000, 'pondus': '0.325969000000000', - 'pound': '45359237/100000000', - 'pound_troy': '58319019/156250000', - 'quintal': '100', + 'pound': QQ(45359237) / 100000000, + 'pound_troy': QQ(58319019) / 156250000, + 'quintal': 100, 'shekel': '0.0141000000000000', - 'short_hundredweight': '45359237/1000000', - 'short_ton': '45359237/50000', - 'slug': '14593903/1000000', + 'short_hundredweight': QQ(45359237) / 1000000, + 'short_ton': QQ(45359237) / 50000, + 'slug': QQ(14593903) / 1000000, 'solar_mass': '1.98892000000000e30', - 'stone': '317514659/50000000', + 'stone': QQ(317514659) / 50000000, 'talent': "(25.7540400000000, {'greek':6000})", - 'ton': '45359237/50000', - 'tonne': '1000', - 'wey': '2857631931/25000000'}, + 'ton': QQ(45359237) / 50000, + 'tonne': 1000, + 'wey': QQ(2857631931) / 25000000}, 'power': - {'cheval_vapeur': '588399/800', - 'horsepower': '37284993579113511/50000000000000', - 'watt': '1'}, + {'cheval_vapeur': QQ(588399) / 800, + 'horsepower': QQ(37284993579113511) / 50000000000000, + 'watt': 1}, 'pressure': - {'atmosphere': '101325', - 'bar': '100000', - 'barye': '1/10', + {'atmosphere': 101325, + 'bar': 100000, + 'barye': QQ((1, 10)), 'inch_mercury': '3386.38900000000', 'millimeter_mercury': '133.322400000000', 'mmhg': '133.322400000000', - 'pa': '1', - 'pascal': '1', - 'pounds_per_square_inch': '8896443230521/1290320000', - 'psi': '8896443230521/1290320000', - 'torr': '20265/152'}, + 'pa': 1, + 'pascal': 1, + 'pounds_per_square_inch': QQ(8896443230521) / 1290320000, + 'psi': QQ(8896443230521) / 1290320000, + 'torr': QQ(20265) / 152}, 'radiation': - {'becquerel': '1', - 'curie': '37000000000', - 'rutherford': '1000000'}, + {'becquerel': 1, + 'curie': 37000000000, + 'rutherford': 1000000}, 'radiation_absorbed': - {'gray': '1', - 'rad': '1/100'}, + {'gray': 1, + 'rad': one / 100}, 'radiation_ionizing': {'roentgen': '0.000258000000000000', 'rontgen': '0.000258000000000000'}, 'resistance': - {'abohm': '1/1000000000', - 'ohm': '1', - 'statohm': '22468879468420441/25000'}, + {'abohm': one / 1000000000, + 'ohm': 1, + 'statohm': QQ(22468879468420441) / 25000}, 'si_prefixes': - {'atto': '1/1000000000000000000', - 'centi': '1/100', - 'deca': '10', - 'deci': '1/10', - 'exa': '1000000000000000000', - 'femto': '1/1000000000000000', - 'giga': '1000000000', - 'hecto': '100', - 'kilo': '1000', - 'mega': '1000000', - 'micro': '1/1000000', - 'milli': '1/1000', - 'nano': '1/1000000000', - 'peta': '1000000000000000', - 'pico': '1/1000000000000', - 'tera': '1000000000000', - 'yocto': '1/1000000000000000000000000', - 'yotta': '1000000000000000000000000', - 'zepto': '1/1000000000000000000000', - 'zetta': '1000000000000000000000'}, + {'atto': one / 1000000000000000000, + 'centi': one / 100, + 'deca': 10, + 'deci': QQ((1, 10)), + 'exa': 1000000000000000000, + 'femto': one / 1000000000000000, + 'giga': 1000000000, + 'hecto': 100, + 'kilo': 1000, + 'mega': 1000000, + 'micro': one / 1000000, + 'milli': one / 1000, + 'nano': one / 1000000000, + 'peta': 1000000000000000, + 'pico': one / 1000000000000, + 'tera': 1000000000000, + 'yocto': one / 1000000000000000000000000, + 'yotta': 1000000000000000000000000, + 'zepto': one / 1000000000000000000000, + 'zetta': 1000000000000000000000}, 'solid_angle': - {'steradian': '1'}, + {'steradian': 1}, 'temperature': {'celsius': '(x + 273.15), (x), (x*9/5 + 32), ((x+273.15)*9/5)', @@ -404,83 +407,83 @@ 'rankine': '(5/9*x), ((x-491.67)*5/9), (x-459.67), (x)'}, 'time': - {'century': '3153600000', - 'day': '86400', - 'decade': '315360000', - 'fortnight': '1209600', - 'hour': '3600', - 'millenium': '31536000000', - 'minute': '60', - 'month': '2628000', - 'second': '1', + {'century': 3153600000, + 'day': 86400, + 'decade': 315360000, + 'fortnight': 1209600, + 'hour': 3600, + 'millenium': 31536000000, + 'minute': 60, + 'month': 2628000, + 'second': 1, 'sidereal_day': "(86164.0905308330, {'sidereal':86400})", 'sidereal_second': "(0.997269566329086, {'sidereal':1})", 'sidereal_year': '3.15581497632000e7', 'tropical_year': '3.15569251779840e7', - 'week': '604800', - 'year': '31536000'}, + 'week': 604800, + 'year': 31536000}, 'unit_multipliers': - {'bakers_dozen': '13', - 'dozen': '12', - 'gross': '144', - 'percent': '1/100'}, + {'bakers_dozen': 13, + 'dozen': 12, + 'gross': 144, + 'percent': one / 100}, 'velocity': {'knot': '463/900'}, 'viscosity_absolute': - {'poise': '1/10', - 'reyn': '8896443230521/1290320000'}, + {'poise': one / 10, + 'reyn': QQ(8896443230521) / 1290320000}, 'viscosity_kinematic': - {'stokes': '1/10000'}, + {'stokes': one / 10000}, 'viscosity_other': - {'rhes': '10'}, + {'rhes': 10}, 'volume': - {'bag': '660732565629/6250000000000', - 'barrel': '9936705933/62500000000', - 'board_foot': '18435447/7812500000', - 'bucket': '473176473/31250000000', - 'bushel': '220244188543/6250000000000', - 'butt': '29810117799/62500000000', - 'cord': '884901456/244140625', - 'cubic_meter': '1', - 'cup': '473176473/2000000000000', - 'ephah': '1982197696887/50000000000000', - 'fifth': '473176473/625000000000', - 'firkin': '4091481/100000000', - 'fluid_dram': '473176473/128000000000000', - 'fluid_ounce': '473176473/16000000000000', - 'gallon': '473176473/125000000000', - 'gill': '473176473/4000000000000', - 'hogshead': '29810117799/125000000000', - 'imperial_gallon': '454609/100000000', - 'imperial_pint': '454609/800000000', - 'jeroboam': '473176473/156250000000', - 'jigger': '1419529419/32000000000000', - 'liter': '1/1000', - 'magnum': '473176473/250000000000', - 'minim': '157725491/2560000000000000', - 'noggin': '473176473/4000000000000', - 'omer': '1982197696887/500000000000000', - 'peck': '220244188543/25000000000000', - 'pint': '473176473/1000000000000', - 'pony': '1419529419/64000000000000', - 'puncheon': '9936705933/31250000000', - 'quart': '473176473/500000000000', - 'register_ton': '55306341/19531250', - 'seam': '220244188543/781250000000', - 'shot': '473176473/16000000000000', - 'stere': '1', - 'tablespoon': '473176473/32000000000000', - 'teaspoon': '157725491/32000000000000', - 'tun': '29810117799/31250000000', - 'uk_gallon': '454609/100000000', - 'uk_pint': '454609/800000000', - 'wine_bottle': '3/4000'} + {'bag': QQ(660732565629) / 6250000000000, + 'barrel': QQ(9936705933) / 62500000000, + 'board_foot': QQ(18435447) / 7812500000, + 'bucket': QQ(473176473) / 31250000000, + 'bushel': QQ(220244188543) / 6250000000000, + 'butt': QQ(29810117799) / 62500000000, + 'cord': QQ(884901456) / 244140625, + 'cubic_meter': 1, + 'cup': QQ(473176473) / 2000000000000, + 'ephah': QQ(1982197696887) / 50000000000000, + 'fifth': QQ(473176473) / 625000000000, + 'firkin': QQ(4091481) / 100000000, + 'fluid_dram': QQ(473176473) / 128000000000000, + 'fluid_ounce': QQ(473176473) / 16000000000000, + 'gallon': QQ(473176473) / 125000000000, + 'gill': QQ(473176473) / 4000000000000, + 'hogshead': QQ(29810117799) / 125000000000, + 'imperial_gallon': QQ(454609) / 100000000, + 'imperial_pint': QQ(454609) / 800000000, + 'jeroboam': QQ(473176473) / 156250000000, + 'jigger': QQ(1419529419) / 32000000000000, + 'liter': one / 1000, + 'magnum': QQ(473176473) / 250000000000, + 'minim': QQ(157725491) / 2560000000000000, + 'noggin': QQ(473176473) / 4000000000000, + 'omer': QQ(1982197696887) / 500000000000000, + 'peck': QQ(220244188543) / 25000000000000, + 'pint': QQ(473176473) / 1000000000000, + 'pony': QQ(1419529419) / 64000000000000, + 'puncheon': QQ(9936705933) / 31250000000, + 'quart': QQ(473176473) / 500000000000, + 'register_ton': QQ(55306341) / 19531250, + 'seam': QQ(220244188543) / 781250000000, + 'shot': QQ(473176473) / 16000000000000, + 'stere': 1, + 'tablespoon': QQ(473176473) / 32000000000000, + 'teaspoon': QQ(157725491) / 32000000000000, + 'tun': QQ(29810117799) / 31250000000, + 'uk_gallon': QQ(454609) / 100000000, + 'uk_pint': QQ(454609) / 800000000, + 'wine_bottle': QQ(3) / 4000} } unit_to_type = {} @@ -500,7 +503,8 @@ def evalunitdict(): """ from sage.misc.sage_eval import sage_eval for key, value in unitdict.items(): - unitdict[key] = {a: sage_eval(repr(b)) for a, b in value.items()} + unitdict[key] = {a: (sage_eval(repr(b)) if isinstance(b, str) else b) + for a, b in value.items()} # FEATURE IDEA: create a function that would allow users to add # new entries to the table without having to know anything about @@ -1213,7 +1217,7 @@ def unitdocs(unit): sage: sage.symbolic.units.unitdocs('amu') 'Abbreviation for atomic mass unit.\nApproximately equal to 1.660538782*10^-27 kilograms.' - Units not in the list unit_docs will raise a ValueError:: + Units not in the list unit_docs will raise a :exc:`ValueError`:: sage: sage.symbolic.units.unitdocs('earth') Traceback (most recent call last): @@ -1276,7 +1280,7 @@ def convert(expr, target): sage: sage.symbolic.units.convert(units.mass.kilogram, units.mass.pound) 100000000/45359237*pound - Raises :exc:`ValueError` if expr and target are not convertible:: + This raises :exc:`ValueError` if expr and target are not convertible:: sage: sage.symbolic.units.convert(units.mass.kilogram, units.length.foot) Traceback (most recent call last): @@ -1375,7 +1379,7 @@ def base_units(unit): sage: sage.symbolic.units.base_units(units.volume.liter) 1/1000*meter^3 - Returns variable if 'unit' is not a unit:: + Returns variable if ``unit`` is not a unit:: sage: sage.symbolic.units.base_units(var('x')) x @@ -1383,18 +1387,21 @@ def base_units(unit): from sage.misc.sage_eval import sage_eval if str(unit) not in unit_to_type: return unit - elif unit_to_type[str(unit)] == 'si_prefixes' or unit_to_type[str(unit)] == 'unit_multipliers': - return sage_eval(unitdict[unit_to_type[str(unit)]][str(unit)]) + if unit_to_type[str(unit)] in ['si_prefixes', 'unit_multipliers']: + number = unitdict[unit_to_type[str(unit)]][str(unit)] + return (sage_eval(number) if isinstance(number, str) else number) + + v = SR.var(unit_to_type[str(unit)]) + if str(v) in unit_derivations: + base = unit_derivations_expr(v) + for i in base.variables(): + base = base.subs({i: SR.var(value_to_unit[str(i)][1])}) + number = unitdict[str(v)][str(unit)] + return base * (sage_eval(number) if isinstance(number, str) else number) else: - v = SR.var(unit_to_type[str(unit)]) - if str(v) in unit_derivations: - base = unit_derivations_expr(v) - for i in base.variables(): - base = base.subs({i: SR.var(value_to_unit[str(i)]['1'])}) - return base * sage_eval(unitdict[str(v)][str(unit)]) - else: - base = SR.var(value_to_unit[str(v)]['1']) * sage_eval(unitdict[str(v)][str(unit)]) - return base + base = SR.var(value_to_unit[str(v)][1]) + number = unitdict[str(v)][str(unit)] + return base * (sage_eval(number) if isinstance(number, str) else number) def convert_temperature(expr, target): @@ -1416,12 +1423,12 @@ def convert_temperature(expr, target): sage: t.convert(units.temperature.kelvin) 273.150000000000*kelvin - If target is None then it defaults to kelvin:: + If target is ``None`` then it defaults to kelvin:: sage: t.convert() 273.150000000000*kelvin - Raises :exc:`ValueError` when either input is not a unit of temperature:: + This raises :exc:`ValueError` when either input is not a unit of temperature:: sage: t.convert(units.length.foot) Traceback (most recent call last): diff --git a/src/sage/tensor/modules/alternating_contr_tensor.py b/src/sage/tensor/modules/alternating_contr_tensor.py index 515b315175c..ca03cbfa14f 100644 --- a/src/sage/tensor/modules/alternating_contr_tensor.py +++ b/src/sage/tensor/modules/alternating_contr_tensor.py @@ -39,6 +39,7 @@ class :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor`. from sage.tensor.modules.free_module_tensor import FreeModuleTensor from sage.tensor.modules.comp import Components, CompFullyAntiSym + class AlternatingContrTensor(FreeModuleTensor): r""" Alternating contravariant tensor on a free module of finite rank diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index b60916f6b6e..32ac954c7b4 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -5504,6 +5504,7 @@ def interior_product(self, other): #****************************************************************************** + class KroneckerDelta(CompFullySym): r""" Kronecker delta `\delta_{ij}`. diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index f9250e59e35..e5218372a8f 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -62,6 +62,7 @@ from sage.tensor.modules.alternating_contr_tensor import AlternatingContrTensor from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm + class ExtPowerFreeModule(FiniteRankFreeModule_abstract): r""" Exterior power of a free module of finite rank over a commutative diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 0a979fd6b6f..9a4e44f0f12 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -535,7 +535,8 @@ class :class:`~sage.modules.free_module.FreeModule_generic` # ****************************************************************************** from __future__ import annotations -from typing import Generator, Optional +from typing import Optional +from collections.abc import Generator from sage.categories.fields import Fields from sage.categories.homset import Hom @@ -2716,7 +2717,7 @@ def dual(self): """ return self.dual_exterior_power(1) - def irange(self, start: Optional[int] = None) -> Generator[int, None, None]: + def irange(self, start: int | None = None) -> Generator[int, None, None]: r""" Single index generator, labelling the elements of a basis of ``self``. diff --git a/src/sage/tensor/modules/free_module_alt_form.py b/src/sage/tensor/modules/free_module_alt_form.py index 68ebdb9fd66..b9d9f98ef63 100644 --- a/src/sage/tensor/modules/free_module_alt_form.py +++ b/src/sage/tensor/modules/free_module_alt_form.py @@ -40,6 +40,7 @@ from sage.tensor.modules.free_module_tensor import FreeModuleTensor from sage.tensor.modules.comp import Components, CompFullyAntiSym + class FreeModuleAltForm(FreeModuleTensor): r""" Alternating form on a free module of finite rank over a commutative ring. @@ -804,7 +805,7 @@ def interior_product(self, alt_tensor): """ from .format_utilities import is_atomic from .alternating_contr_tensor import AlternatingContrTensor - if not isinstance(alt_tensor, AlternatingContrTensor): + if not isinstance(alt_tensor, AlternatingContrTensor): raise TypeError("{} is not an alternating ".format(alt_tensor) + "contravariant tensor") p_res = alt_tensor._tensor_rank - self._tensor_rank # degree of result diff --git a/src/sage/tensor/modules/free_module_basis.py b/src/sage/tensor/modules/free_module_basis.py index 4e531b54e6e..f882b88e300 100644 --- a/src/sage/tensor/modules/free_module_basis.py +++ b/src/sage/tensor/modules/free_module_basis.py @@ -35,6 +35,7 @@ from sage.sets.family import AbstractFamily from sage.structure.unique_representation import UniqueRepresentation + class Basis_abstract(UniqueRepresentation, AbstractFamily): """ Abstract base class for (dual) bases of free modules. @@ -419,6 +420,7 @@ def set_name(self, symbol, latex_symbol=None, indices=None, #****************************************************************************** + class FreeModuleCoBasis(Basis_abstract): r""" Dual basis of a free module over a commutative ring. @@ -549,6 +551,7 @@ def _repr_(self): #****************************************************************************** + class FreeModuleBasis(Basis_abstract): r""" Basis of a free module over a commutative ring `R`. diff --git a/src/sage/tensor/modules/free_module_element.py b/src/sage/tensor/modules/free_module_element.py index 2f783c57eb5..4b49dcab975 100644 --- a/src/sage/tensor/modules/free_module_element.py +++ b/src/sage/tensor/modules/free_module_element.py @@ -196,8 +196,8 @@ class FiniteRankFreeModuleElement(AlternatingContrTensor): def __init__( self, fmodule: FiniteRankFreeModule, - name: Optional[str] = None, - latex_name: Optional[str] = None, + name: str | None = None, + latex_name: str | None = None, ): r""" TESTS:: diff --git a/src/sage/tensor/modules/free_module_linear_group.py b/src/sage/tensor/modules/free_module_linear_group.py index bb9ccf74e9a..9893ba32420 100644 --- a/src/sage/tensor/modules/free_module_linear_group.py +++ b/src/sage/tensor/modules/free_module_linear_group.py @@ -35,6 +35,7 @@ from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism + class FreeModuleLinearGroup(UniqueRepresentation, Parent): r""" General linear group of a free module of finite rank over a commutative diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index f540acabc77..94974e6059b 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -193,7 +193,7 @@ class being: # ***************************************************************************** from __future__ import annotations -from typing import TYPE_CHECKING, Dict, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism @@ -310,7 +310,7 @@ def __init__( self._latex_name = self._name else: self._latex_name = latex_name - self._components: Dict[FreeModuleBasis, Components] = {} # dict. of the sets of components on various + self._components: dict[FreeModuleBasis, Components] = {} # dict. of the sets of components on various # bases, with the bases as keys (initially empty) # Treatment of symmetry declarations: diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 7e44a076694..8a02751c471 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -70,6 +70,7 @@ from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym + class TensorFreeModule(ReflexiveModule_tensor, FiniteRankFreeModule_abstract): r""" Class for the free modules over a commutative ring `R` that are diff --git a/src/sage/tensor/modules/tensor_with_indices.py b/src/sage/tensor/modules/tensor_with_indices.py index f6e5104ea14..e65277935a7 100644 --- a/src/sage/tensor/modules/tensor_with_indices.py +++ b/src/sage/tensor/modules/tensor_with_indices.py @@ -27,6 +27,7 @@ # The dot is special syntax for unnamed index positions. _alph_or_dot_pattern = r"([.]|[^\d\W_])" + class TensorWithIndices(SageObject): r""" Index notation for tensors. diff --git a/src/sage/tests/books/__init__.py b/src/sage/tests/books/__init__.py index e69de29bb2d..1b10e244496 100644 --- a/src/sage/tests/books/__init__.py +++ b/src/sage/tests/books/__init__.py @@ -0,0 +1 @@ +# Here so that cython creates the correct module name diff --git a/src/sage/tests/finite_poset.py b/src/sage/tests/finite_poset.py index d04221dcc13..3d4cec06129 100644 --- a/src/sage/tests/finite_poset.py +++ b/src/sage/tests/finite_poset.py @@ -91,6 +91,7 @@ sublattice_closed = ['distributive', 'modular', 'semidistributive', 'join_semidistributive', 'meet_semidistributive'] + def test_attrcall(name, L): """ Return a function by name. @@ -122,6 +123,7 @@ def test_attrcall(name, L): return L.is_orthocomplemented(unique=True) return attrcall(name)(L) + def test_finite_lattice(L): """ Test several functions on a given finite lattice. diff --git a/src/sage/tests/functools_partial_src.py b/src/sage/tests/functools_partial_src.py index d352d160230..e8c983356aa 100644 --- a/src/sage/tests/functools_partial_src.py +++ b/src/sage/tests/functools_partial_src.py @@ -4,6 +4,7 @@ """ from functools import partial + def base(x): """ Test function to make sure diff --git a/src/sage/tests/memcheck/verify_no_leak.py b/src/sage/tests/memcheck/verify_no_leak.py index 89ca90cf89c..17a3b4975aa 100644 --- a/src/sage/tests/memcheck/verify_no_leak.py +++ b/src/sage/tests/memcheck/verify_no_leak.py @@ -1,4 +1,4 @@ -from typing import Tuple, Sequence, List, Callable, Any +from typing import Callable, Any import valgrind diff --git a/src/sage/tests/meson.build b/src/sage/tests/meson.build new file mode 100644 index 00000000000..4592d9d9d4e --- /dev/null +++ b/src/sage/tests/meson.build @@ -0,0 +1,54 @@ +py.install_sources( + '__init__.py', + 'all.py', + 'article_heuberger_krenn_kropf_fsm-in-sage.py', + 'arxiv_0812_2725.py', + 'benchmark.py', + 'book_schilling_zabrocki_kschur_primer.py', + 'book_stein_ent.py', + 'book_stein_modform.py', + 'cmdline.py', + 'combinatorial_hopf_algebras.py', + 'finite_poset.py', + 'functools_partial_src.py', + 'gosper-sum.py', + 'lazy_imports.py', + 'modular_group_cohomology.py', + 'numpy.py', + 'parigp.py', + 'startup.py', + 'symbolic-series.py', + 'sympy.py', + 'test_deprecation.py', + subdir: 'sage/tests', +) + +extension_data = {'cython' : files('cython.pyx')} + +foreach name, pyx : extension_data + py.extension_module( + name, + sources: pyx, + subdir: 'sage/tests', + install: true, + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +extension_data_cpp = {'stl_vector': files('stl_vector.pyx')} + +foreach name, pyx : extension_data_cpp + py.extension_module( + name, + sources: pyx, + subdir: 'sage/tests', + install: true, + override_options: ['cython_language=cpp'], + include_directories: [inc_cpython, inc_rings], + dependencies: [py_dep, cysignals, gmp], + ) +endforeach + +install_subdir('books', install_dir: sage_install_dir / 'tests') +install_subdir('memcheck', install_dir: sage_install_dir / 'tests') diff --git a/src/sage/topology/cell_complex.py b/src/sage/topology/cell_complex.py index 38caf541192..7b64a3d36dd 100644 --- a/src/sage/topology/cell_complex.py +++ b/src/sage/topology/cell_complex.py @@ -709,16 +709,17 @@ def betti(self, dim=None, subcomplex=None): sage: S2c.betti(2) # needs sage.modules 1 """ - dict = {} + dic = {} H = self.homology(dim, base_ring=QQ, subcomplex=subcomplex) try: for n in H.keys(): - dict[n] = H[n].dimension() + dic[n] = H[n].dimension() if n == 0: - dict[n] += 1 - return dict + dic[n] += 1 except AttributeError: return H.dimension() + else: + return dic def is_acyclic(self, base_ring=ZZ): """ diff --git a/src/sage/topology/delta_complex.py b/src/sage/topology/delta_complex.py index 6cd2ee7acc6..b9ce4d5fb62 100644 --- a/src/sage/topology/delta_complex.py +++ b/src/sage/topology/delta_complex.py @@ -1388,10 +1388,10 @@ def _epi_from_standard_simplex(self, idx=-1, dim=None): simplex_cells = simplex.cells() self_cells = self.cells() if dim > 0: - map = {dim: {tuple(simplex_cells[dim][0]): idx}} + mapping = {dim: {tuple(simplex_cells[dim][0]): idx}} else: - map = {dim: {(0,): idx}} - faces_dict = map[dim] + mapping = {dim: {(0,): idx}} + faces_dict = mapping[dim] for n in range(dim, 0, -1): n_cells = faces_dict faces_dict = {} @@ -1407,8 +1407,8 @@ def _epi_from_standard_simplex(self, idx=-1, dim=None): for j in one_cell: if j not in faces_dict: faces_dict[j] = one_cell[j] - map[n-1] = faces_dict - return map + mapping[n-1] = faces_dict + return mapping def _is_glued(self, idx=-1, dim=None): r""" diff --git a/src/sage/topology/filtered_simplicial_complex.py b/src/sage/topology/filtered_simplicial_complex.py index 1354e7a8fb6..e468b80c662 100644 --- a/src/sage/topology/filtered_simplicial_complex.py +++ b/src/sage/topology/filtered_simplicial_complex.py @@ -620,8 +620,7 @@ def _max_index(self, d): currmax = -1 for s, x_s in d: j = self._index_of_simplex[s] - if j > currmax: - currmax = j + currmax = max(j, currmax) return currmax def persistence_intervals(self, dimension, field=2, strict=True, verbose=None): diff --git a/src/sage/topology/moment_angle_complex.py b/src/sage/topology/moment_angle_complex.py index 04e14ef1ce4..3291b9ef104 100644 --- a/src/sage/topology/moment_angle_complex.py +++ b/src/sage/topology/moment_angle_complex.py @@ -200,7 +200,7 @@ def __classcall_private__(cls, simplicial_complex): immutable_complex = SimplicialComplex(is_mutable=False) return super().__classcall__(cls, immutable_complex) - def __init__(self, simplicial_complex): + def __init__(self, simplicial_complex) -> None: """ Initialize ``self``. @@ -295,7 +295,7 @@ def cubical_complex(self): .. WARNING:: - The construction can be very slow, it is not reccomended unless + The construction can be very slow, it is not recommended unless the corresponding simplicial complex has 5 or less vertices. EXAMPLES:: @@ -343,14 +343,14 @@ def simplicial_complex(self): """ return self._simplicial_complex - def components(self): + def components(self) -> dict: r""" Return the dictionary of components of ``self``, indexed by facets of the associated simplicial complex. OUTPUT: - A dictonary, whose values are lists, representing spheres + A dictionary, whose values are lists, representing spheres and disks described in the construction of the moment-angle complex. ``The 2-simplex`` represents a 2-disk, and ``Minimal triangulation of the 1-sphere`` represents a 1-sphere. @@ -507,7 +507,7 @@ def _homology_group(self, i, base_ring, cohomology, algorithm, verbose, reduced) return HomologyGroup(m, base_ring, invfac) def homology(self, dim=None, base_ring=ZZ, cohomology=False, - algorithm='pari', verbose=False, reduced=True): + algorithm='pari', verbose=False, reduced=True) -> dict: r""" The (reduced) homology of ``self``. @@ -615,15 +615,15 @@ def homology(self, dim=None, base_ring=ZZ, cohomology=False, 17: 0} sage: Z = MomentAngleComplex([[0,1,2,3], [0,1,2,4], [0,1,3,5], ....: [0,1,4,5], [0,2,3,6], [0,2,4,6]]) - sage: Z.homology(dim=range(0,5), reduced=True) + sage: Z.homology(dim=range(5), reduced=True) {0: 0, 1: 0, 2: 0, 3: Z x Z x Z x Z, 4: Z x Z} - sage: Z.homology(dim=range(0,5), reduced=False) + sage: Z.homology(dim=range(5), reduced=False) {0: Z, 1: 0, 2: 0, 3: Z x Z x Z x Z, 4: Z x Z} sage: all(Z.homology(i,reduced=True) == Z.homology(i,reduced=False) ....: for i in range(1, dim(Z))) True sage: all(Z.homology(i,reduced=True) == Z.homology(i,reduced=False) - ....: for i in range(0, dim(Z))) + ....: for i in range(dim(Z))) False """ if dim is not None: @@ -641,7 +641,7 @@ def homology(self, dim=None, base_ring=ZZ, cohomology=False, algorithm=algorithm, verbose=verbose, reduced=reduced) for i in dims} def cohomology(self, dim=None, base_ring=ZZ, algorithm='pari', - verbose=False, reduced=True): + verbose=False, reduced=True) -> dict: r""" The reduced cohomology of ``self``. @@ -672,7 +672,7 @@ def cohomology(self, dim=None, base_ring=ZZ, algorithm='pari', return self.homology(dim=dim, cohomology=True, base_ring=base_ring, algorithm=algorithm, verbose=verbose, reduced=reduced) - def betti(self, dim=None): + def betti(self, dim=None) -> dict: r""" Return the Betti number (or numbers) of ``self``. @@ -698,16 +698,17 @@ def betti(self, dim=None): sage: Z.betti(dim=6) {6: 2} """ - dict = {} + dic = {} H = self.homology(dim=dim, base_ring=QQ) try: for n in H: - dict[n] = H[n].dimension() + dic[n] = H[n].dimension() if n == 0: - dict[n] += 1 - return dict + dic[n] += 1 except AttributeError: return H.dimension() + else: + return dic def euler_characteristic(self): """ @@ -762,7 +763,7 @@ def product(self, other): simplicial_complex = self._simplicial_complex.join(other._simplicial_complex, rename_vertices=True) return MomentAngleComplex(simplicial_complex) - def has_trivial_lowest_deg_massey_product(self): + def has_trivial_lowest_deg_massey_product(self) -> bool: """ Return whether ``self`` has a non-trivial lowest degree triple Massey product. diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 241b56a0ff6..54166cf4f1f 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -3707,7 +3707,7 @@ def stellar_subdivision(self, simplex, inplace=False, is_mutable=True): if inplace and self._is_immutable: raise ValueError("this simplicial complex is not mutable") - if not Simplex(simplex) in self: + if Simplex(simplex) not in self: raise ValueError("the face to subdivide is not a face of self") if inplace: diff --git a/src/sage/topology/simplicial_complex_examples.py b/src/sage/topology/simplicial_complex_examples.py index c51954d7d9a..97958848bb0 100644 --- a/src/sage/topology/simplicial_complex_examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -614,8 +614,8 @@ def QuaternionicProjectivePlane(): (3, 4, 6, 7, 11, 12, 13, 14, 15), # L (3, 4, 6, 7, 10, 12, 13, 14, 15)] # N - return UniqueSimplicialComplex([[g(index) for index in tuple] - for tuple in start_list + return UniqueSimplicialComplex([[g(index) for index in tup] + for tup in start_list for g in PermutationGroup([P, S])]) diff --git a/src/sage/topology/simplicial_complex_morphism.py b/src/sage/topology/simplicial_complex_morphism.py index 02fbef2402e..52a2d6e82b4 100644 --- a/src/sage/topology/simplicial_complex_morphism.py +++ b/src/sage/topology/simplicial_complex_morphism.py @@ -555,7 +555,7 @@ def is_injective(self): return False return True - def is_identity(self): + def is_identity(self) -> bool: """ If ``self`` is an identity morphism, returns ``True``. Otherwise, ``False``. @@ -588,18 +588,15 @@ def is_identity(self): """ if self.domain() != self.codomain(): return False - else: - f = {} - for i in self.domain().vertices(): - f[i] = i - if self._vertex_dictionary != f: - return False - else: - return True + + f = {i: i for i in self.domain().vertices()} + return self._vertex_dictionary == f def fiber_product(self, other, rename_vertices=True): """ - Fiber product of ``self`` and ``other``. Both morphisms should have + Fiber product of ``self`` and ``other``. + + Both morphisms should have the same codomain. The method returns a morphism of simplicial complexes, which is the morphism from the space of the fiber product to the codomain. diff --git a/src/sage/topology/simplicial_set.py b/src/sage/topology/simplicial_set.py index 920df5e0437..974feac8510 100644 --- a/src/sage/topology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -2119,16 +2119,17 @@ def betti(self, dim=None, subcomplex=None): sage: BC3.betti(range(4)) # needs sage.groups sage.modules {0: 1, 1: 0, 2: 0, 3: 0} """ - dict = {} + dic = {} H = self.homology(dim, base_ring=QQ, subcomplex=subcomplex) try: for n in H.keys(): - dict[n] = H[n].dimension() + dic[n] = H[n].dimension() if n == 0: - dict[n] += 1 - return dict + dic[n] += 1 except AttributeError: return H.dimension() + else: + return dic def n_chains(self, n, base_ring=ZZ, cochains=False): r""" diff --git a/src/sage/topology/simplicial_set_examples.py b/src/sage/topology/simplicial_set_examples.py index 498284e9816..367a00413b7 100644 --- a/src/sage/topology/simplicial_set_examples.py +++ b/src/sage/topology/simplicial_set_examples.py @@ -30,7 +30,7 @@ # **************************************************************************** import re -import os +from pathlib import Path from sage.env import SAGE_ENV from sage.misc.cachefunc import cached_method, cached_function @@ -48,6 +48,8 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.categories.simplicial_sets', 'SimplicialSets') +kenzo_path = Path(SAGE_ENV['SAGE_EXTCODE']) / 'kenzo' + # ###################################################################### # The nerve of a finite monoid, used in sage.categories.finite_monoid. @@ -96,7 +98,7 @@ def __init__(self, monoid): # of monoid elements). Omit the base point. self._simplex_data = () - def __eq__(self, other): + def __eq__(self, other) -> bool: """ Return ``True`` if ``self`` and ``other`` are equal. @@ -119,7 +121,7 @@ def __eq__(self, other): and self._monoid == other._monoid and self.base_point() == other.base_point()) - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Return the negation of `__eq__`. @@ -214,13 +216,13 @@ def n_skeleton(self, n): face_dict[(g,)] = x start = 1 - for d in range(start+1, n+1): + for d in range(start + 1, n + 1): for g in monoid: if g == one: continue new_faces = {} for t in face_dict.keys(): - if len(t) != d-1: + if len(t) != d - 1: continue # chain: chain of group elements to multiply, # as a tuple. @@ -235,8 +237,8 @@ def n_skeleton(self, n): # Compute faces of x. faces = [face_dict[chain[1:]]] - for i in range(d-1): - product = chain[i] * chain[i+1] + for i in range(d - 1): + product = chain[i] * chain[i + 1] if product == one: # Degenerate. if d == 2: @@ -294,11 +296,11 @@ def Sphere(n): w_0 = AbstractSimplex(0, name='w_0') return SimplicialSet_finite({v_0: None, w_0: None}, base_point=v_0, name='S^0') - degens = range(n-2, -1, -1) + degens = range(n - 2, -1, -1) degen_v = v_0.apply_degeneracies(*degens) sigma = AbstractSimplex(n, name='sigma_{}'.format(n), latex_name='\\sigma_{}'.format(n)) - return SimplicialSet_finite({sigma: [degen_v] * (n+1)}, base_point=v_0, + return SimplicialSet_finite({sigma: [degen_v] * (n + 1)}, base_point=v_0, name='S^{}'.format(n), latex_name='S^{{{}}}'.format(n)) @@ -616,22 +618,20 @@ def ComplexProjectiveSpace(n): latex_name='CP^{2}') return K if n == 3: - file = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'CP3.txt') + file = kenzo_path / 'CP3.txt' data = simplicial_data_from_kenzo_output(file) - v = [_ for _ in data.keys() if _.dimension() == 0][0] - K = SimplicialSet_finite(data, base_point=v, name='CP^3', - latex_name='CP^{3}') - return K + v = [sigma for sigma in data if sigma.dimension() == 0][0] + return SimplicialSet_finite(data, base_point=v, name='CP^3', + latex_name='CP^{3}') if n == 4: - file = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'CP4.txt') + file = kenzo_path / 'CP4.txt' data = simplicial_data_from_kenzo_output(file) - v = [_ for _ in data.keys() if _.dimension() == 0][0] - K = SimplicialSet_finite(data, base_point=v, name='CP^4', - latex_name='CP^{4}') - return K + v = [sigma for sigma in data if sigma.dimension() == 0][0] + return SimplicialSet_finite(data, base_point=v, name='CP^4', + latex_name='CP^{4}') -def simplicial_data_from_kenzo_output(filename): +def simplicial_data_from_kenzo_output(filename) -> dict: """ Return data to construct a simplicial set, given Kenzo output. @@ -649,14 +649,15 @@ def simplicial_data_from_kenzo_output(filename): sage: from sage.topology.simplicial_set_examples import simplicial_data_from_kenzo_output sage: from sage.topology.simplicial_set import SimplicialSet - sage: sphere = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'S4.txt') + sage: from pathlib import Path + sage: sphere = Path(SAGE_ENV['SAGE_EXTCODE']) / 'kenzo' /'S4.txt' sage: S4 = SimplicialSet(simplicial_data_from_kenzo_output(sphere)) # needs pyparsing sage: S4.homology(reduced=False) # needs pyparsing {0: Z, 1: 0, 2: 0, 3: 0, 4: Z} """ from pyparsing import OneOrMore, nestedExpr - with open(filename, 'r') as f: + with open(filename) as f: data = f.read() dim = 0 start = 0 @@ -667,7 +668,7 @@ def simplicial_data_from_kenzo_output(filename): dim_idx = data.find('Dimension = {}:'.format(dim), start) while dim_idx != -1: start = dim_idx + len('Dimension = {}:'.format(dim)) - new_dim_idx = data.find('Dimension = {}:'.format(dim+1), start) + new_dim_idx = data.find('Dimension = {}:'.format(dim + 1), start) if new_dim_idx == -1: end = len(data) else: diff --git a/src/sage/topology/simplicial_set_morphism.py b/src/sage/topology/simplicial_set_morphism.py index c5285335a22..9d54e2e1b5e 100644 --- a/src/sage/topology/simplicial_set_morphism.py +++ b/src/sage/topology/simplicial_set_morphism.py @@ -817,10 +817,11 @@ def is_injective(self): if self._is_identity: return True domain = self.domain() - for n in range(domain.dimension()+1): - input = domain.n_cells(n) - output = {self(sigma) for sigma in input if self(sigma).is_nondegenerate()} - if len(input) > len(output): + for n in range(domain.dimension() + 1): + domain_cells = domain.n_cells(n) + output = {self(sigma) for sigma in domain_cells + if self(sigma).is_nondegenerate()} + if len(domain_cells) > len(output): return False return True diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 83d92af8959..cf64d92a0b9 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -44,11 +44,12 @@ sage: shell.run_cell('%display ascii_art') sage: shell.run_cell("i = var('i')") # needs sage.symbolic sage: shell.run_cell('sum(factorial(i)*x^i, i, 0, 10)') # needs sage.symbolic - 10 9 8 7 6 5 4 3 - 3628800*x + 362880*x + 40320*x + 5040*x + 720*x + 120*x + 24*x + 6*x + 10 9 8 7 6 5 4 3... + 3628800*x + 362880*x + 40320*x + 5040*x + 720*x + 120*x + 24*x + 6*x... - 2 - + 2*x + x + 1 + ...2 + ...+ 2*x + x + 1 + sage: shell.run_cell('3/(7*x)') # needs sage.symbolic 3 --- diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index e85e1143c7d..bc64866ed01 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -274,7 +274,7 @@ def _terminal_width(self): import fcntl import termios import struct - rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ, + rc = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('HHHH', sys.stdout.fileno(), 0, 0, 0)) h, w, hp, wp = struct.unpack('HHHH', rc) return w @@ -301,7 +301,7 @@ def _splitting_points(self, size, offset=0): # We implement a custom iterator instead of repeatedly using # itertools.chain to prepend elements in order to avoid quadratic time # complexity - class PrependIterator(): + class PrependIterator: """ Iterator with support for prepending of elements. """ diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index eb268f3228a..d9b13a1cb05 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -469,11 +469,9 @@ def concatenate(self, iterable, separator, empty=None, baseline=0, top = separator._h - bot for obj in iterable: bot1 = obj.get_baseline() - if bot1 > bot: - bot = bot1 + bot = max(bot1, bot) top1 = obj._h - bot1 - if top1 > top: - top = top1 + top = max(top1, top) # bot + top is the new height def padded_line(obj, i): diff --git a/src/sage/version.py b/src/sage/version.py index b9ae8e5a1c9..12c31b89416 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.5.beta2' -date = '2024-08-10' -banner = 'SageMath version 10.5.beta2, Release Date: 2024-08-10' +version = '10.5.beta9' +date = '2024-11-03' +banner = 'SageMath version 10.5.beta9, Release Date: 2024-11-03' diff --git a/src/sage_docbuild/builders.py b/src/sage_docbuild/builders.py index 871cc4705a2..ab39d93c280 100644 --- a/src/sage_docbuild/builders.py +++ b/src/sage_docbuild/builders.py @@ -1054,7 +1054,7 @@ def get_modules(self, filename): try: tag_name = line[line.index('feature_') + 8:].strip() for feature in all_features(): - if tag_name == feature.name.replace('.', '_'): + if tag_name == feature.name.replace('.', '_') and feature.is_present(): break else: skip = True diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index ddd64c2718f..18ce9f7663e 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -27,11 +27,12 @@ import sphinx.ext.intersphinx as intersphinx from sphinx import highlighting from sphinx.transforms import SphinxTransform +from sphinx.util.docutils import SphinxDirective from IPython.lib.lexers import IPythonConsoleLexer, IPyLexer from sage.misc.sagedoc import extlinks from sage.env import SAGE_DOC_SRC, SAGE_DOC, PPLPY_DOCS, MATHJAX_DIR from sage.misc.latex_macros import sage_mathjax_macros -from sage.features import PythonModule +from sage.features.sphinx import JupyterSphinx from sage.features.all import all_features import sage.version @@ -56,12 +57,15 @@ 'sphinx_inline_tabs', 'IPython.sphinxext.ipython_directive', 'matplotlib.sphinxext.plot_directive', - 'jupyter_sphinx', ] +if JupyterSphinx().is_present(): + extensions.append('jupyter_sphinx') + jupyter_execute_default_kernel = 'sagemath' if SAGE_LIVE_DOC == 'yes': + JupyterSphinx().require() SAGE_JUPYTER_SERVER = os.environ.get('SAGE_JUPYTER_SERVER', 'binder') if SAGE_JUPYTER_SERVER.startswith('binder'): # format: "binder" or @@ -183,9 +187,6 @@ def sphinx_plot(graphics, **kwds): # Add any paths that contain templates here, relative to this directory. templates_path = [os.path.join(SAGE_DOC_SRC, 'common', 'templates'), 'templates'] -# The suffix of source filenames. -source_suffix = '.rst' - # The master toctree document. master_doc = 'index' @@ -491,6 +492,7 @@ def linkcode_resolve(domain, info): 'custom-furo.css', 'custom-jupyter-sphinx.css', 'custom-codemirror-monokai.css', + 'custom-tabs.css', ] html_js_files = [ @@ -952,12 +954,13 @@ class SagecodeTransform(SphinxTransform): def apply(self): if self.app.builder.tags.has('html') or self.app.builder.tags.has('inventory'): - for node in self.document.traverse(nodes.literal_block): + for node in self.document.findall(nodes.literal_block): if node.get('language') is None and node.astext().startswith('sage:'): from docutils.nodes import container as Container, label as Label, literal_block as LiteralBlock, Text from sphinx_inline_tabs._impl import TabContainer parent = node.parent index = parent.index(node) + prev_node = node.previous_sibling() if isinstance(node.previous_sibling(), TabContainer): # Make sure not to merge inline tabs for adjacent literal blocks parent.insert(index, Text('')) @@ -972,6 +975,10 @@ def apply(self): content += node container += content parent.insert(index, container) + index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-sage-tab') + if SAGE_PREPARSED_DOC == 'yes': # Tab for preparsed version from sage.repl.preparse import preparse @@ -1002,7 +1009,10 @@ def apply(self): preparsed_node = LiteralBlock(preparsed, preparsed, language='ipycon') content += preparsed_node container += content - parent.insert(index + 1, container) + parent.insert(index, container) + index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-python-tab') if SAGE_LIVE_DOC == 'yes': # Tab for Jupyter-sphinx cell from jupyter_sphinx.ast import JupyterCellNode, CellInputNode @@ -1034,7 +1044,18 @@ def apply(self): content = Container("", is_div=True, classes=["tab-content"]) content += cell_node container += content - parent.insert(index + 1, container) + parent.insert(index, container) + index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-sage-live-tab') + + +class Ignore(SphinxDirective): + + has_content = True + + def run(self): + return [] # This replaces the setup() in sage.misc.sagedoc_conf @@ -1050,6 +1071,12 @@ def setup(app): app.add_transform(SagemathTransform) if SAGE_LIVE_DOC == 'yes' or SAGE_PREPARSED_DOC == 'yes': app.add_transform(SagecodeTransform) + if not JupyterSphinx().is_present(): + app.add_directive("jupyter-execute", Ignore) + app.add_directive("jupyter-kernel", Ignore) + app.add_directive("jupyter-input", Ignore) + app.add_directive("jupyter-output", Ignore) + app.add_directive("thebe-button", Ignore) # When building the standard docs, app.srcdir is set to SAGE_DOC_SRC + # 'LANGUAGE/DOCNAME'. @@ -1071,12 +1098,7 @@ def setup(app): # https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#tags # https://www.sphinx-doc.org/en/master/usage/configuration.html#conf-tags # https://github.com/readthedocs/readthedocs.org/issues/4603#issuecomment-1411594800 -# Workaround to allow importing this file from other confs -if 'tags' not in locals(): - class Tags(set): - has = set.__contains__ - tags = Tags() - - -for feature in all_features(): - tags.add('feature_' + feature.name.replace('.', '_')) +def feature_tags(): + for feature in all_features(): + if feature.is_present(): + yield 'feature_' + feature.name.replace('.', '_') diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index d60d5efbf44..87e4e69d7bd 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -12,7 +12,7 @@ for those who like elaborate docstrings. This module is currently based on :mod:`sphinx.ext.autodoc` from Sphinx version -7.2.6. Compare (do diff) with the upstream source file +8.0.2. Compare (do diff) with the upstream source file `sphinx/ext/autodoc/__init__.py `_. @@ -31,27 +31,30 @@ - Kwankyu Lee (2018-12-26, 2022-11-08): rebased on the latest sphinx.ext.autodoc - Kwankyu Lee (2024-02-14): rebased on Sphinx 7.2.6 + +- François Bissey (2024-08-24): rebased on Sphinx 8.0.2 + +- François Bissey (2024-09-10): Tweaks to support python 3.9 (and older sphinx) as well """ from __future__ import annotations import functools import operator -import re import sys -import warnings +import re from inspect import Parameter, Signature -from typing import TYPE_CHECKING, Any, Callable, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, NewType, TypeVar from docutils.statemachine import StringList import sphinx from sphinx.config import ENUM, Config -from sphinx.deprecation import RemovedInSphinx80Warning +from sphinx.errors import PycodeError from sphinx.ext.autodoc.importer import get_class_members, import_module, import_object from sphinx.ext.autodoc.mock import ismock, mock, undecorate from sphinx.locale import _, __ -from sphinx.pycode import ModuleAnalyzer, PycodeError +from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect, logging from sphinx.util.docstrings import prepare_docstring, separate_metadata from sphinx.util.inspect import ( @@ -61,7 +64,13 @@ safe_getattr, stringify_signature, ) -from sphinx.util.typing import OptionSpec, get_type_hints, restify, stringify_annotation +from sphinx.util.typing import ( + ExtensionMetadata, + OptionSpec, + get_type_hints, + restify, + stringify_annotation, +) # ------------------------------------------------------------------ from sage.misc.sageinspect import (sage_getdoc_original, @@ -74,9 +83,8 @@ def getdoc(obj, *args, **kwargs): return sage_getdoc_original(obj) # ------------------------------------------------------------------ - if TYPE_CHECKING: - from collections.abc import Iterator, Sequence + from collections.abc import Callable, Iterator, Sequence from types import ModuleType from sphinx.application import Sphinx @@ -208,7 +216,7 @@ def merge_members_option(options: dict) -> None: return members = options.setdefault('members', []) - for key in {'private-members', 'special-members'}: + for key in ('private-members', 'special-members'): if key in options and options[key] not in (ALL, None): for member in options[key]: if member not in members: @@ -318,13 +326,6 @@ class ObjectMember: This is used for the result of `Documenter.get_module_members()` to represent each member of the object. - - .. Note:: - - An instance of this class behaves as a tuple of (name, object) - for compatibility to old Sphinx. The behavior will be dropped - in the future. Therefore extensions should not use the tuple - interface. """ def __init__(self, name: str, obj: Any, *, docstring: str | None = None, @@ -335,12 +336,6 @@ def __init__(self, name: str, obj: Any, *, docstring: str | None = None, self.skipped = skipped self.class_ = class_ - def __getitem__(self, index: int) -> Any: - warnings.warn('The tuple interface of ObjectMember is deprecated. ' - 'Use (obj.__name__, obj.object) instead.', - RemovedInSphinx80Warning, stacklevel=2) - return (self.__name__, self.object)[index] - class Documenter: """ @@ -370,7 +365,7 @@ class Documenter: #: true if the generated content may contain titles titles_allowed = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': bool_option, 'noindex': bool_option, } @@ -447,7 +442,7 @@ def parse_name(self) -> bool: # an autogenerated one matched = py_ext_sig_re.match(self.name) if matched is None: - logger.warning(__('invalid signature for auto%s (%r)') % (self.objtype, self.name), + logger.warning(__('invalid signature for auto%s (%r)'), self.objtype, self.name, type='autodoc') return False explicit_modname, path, base, tp_list, args, retann = matched.groups() @@ -512,9 +507,7 @@ def check_module(self) -> bool: subject = inspect.unpartial(self.object) modname = self.get_attr(subject, '__module__', None) - if modname and modname != self.modname: - return False - return True + return not modname or modname == self.modname def format_args(self, **kwargs: Any) -> str: """Format the argument signature of *self.object*. @@ -737,29 +730,16 @@ def is_filtered_inherited_member(name: str, obj: Any) -> bool: # process members and determine which to skip for obj in members: - try: - membername = obj.__name__ - member = obj.object - - # --------------------------------------------------- - # Issue #17455: Immediately skip lazy imports to avoid - # deprecation messages. - from sage.misc.lazy_import import LazyImport - if isinstance(member, LazyImport): - continue - # --------------------------------------------------- - except AttributeError: - if isinstance(obj, ObjectMember): - raise - # To be removed, retained for compatibility. - # See https://github.com/sphinx-doc/sphinx/issues/11631 - membername, member = obj - warnings.warn( - 'Returning tuples of (name, object) as ' - 'the second return value from get_object_members() is deprecated. ' - 'Return ObjectMember(name, object) instances instead.', - RemovedInSphinx80Warning, stacklevel=2, - ) + membername = obj.__name__ + member = obj.object + + # --------------------------------------------------- + # Issue #17455: Immediately skip lazy imports to avoid + # deprecation messages. + from sage.misc.lazy_import import LazyImport + if isinstance(member, LazyImport): + continue + # --------------------------------------------------- # if isattr is True, the member is documented as an attribute isattr = member is INSTANCEATTR or (namespace, membername) in attr_docs @@ -953,7 +933,7 @@ def generate( logger.warning( __("don't know which module to import for autodocumenting " '%r (try placing a "module" or "currentmodule" directive ' - 'in the document, or giving an explicit module name)') % + 'in the document, or giving an explicit module name)'), self.name, type='autodoc') return @@ -1041,7 +1021,7 @@ class ModuleDocumenter(Documenter): content_indent = '' _extra_indent = ' ' - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'members': members_option, 'undoc-members': bool_option, 'no-index': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'synopsis': identity, @@ -1085,7 +1065,7 @@ def parse_name(self) -> bool: ret = super().parse_name() if self.args or self.retann: logger.warning(__('signature arguments or return annotation ' - 'given for automodule %s') % self.fullname, + 'given for automodule %s'), self.fullname, type='autodoc') return ret @@ -1098,8 +1078,8 @@ def import_object(self, raiseerror: bool = False) -> bool: except ValueError as exc: # invalid __all__ found. logger.warning(__('__all__ should be a list of strings, not %r ' - '(in module %s) -- ignoring __all__') % - (exc.args[0], self.fullname), type='autodoc') + '(in module %s) -- ignoring __all__'), + exc.args[0], self.fullname, type='autodoc') return ret @@ -1164,8 +1144,8 @@ def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]: ret.append(members[name]) else: logger.warning(__('missing attribute mentioned in :members: option: ' - 'module %s, attribute %s') % - (safe_getattr(self.object, '__name__', '???'), name), + 'module %s, attribute %s'), + safe_getattr(self.object, '__name__', '???', name), type='autodoc') return False, ret @@ -1203,10 +1183,10 @@ class ModuleLevelDocumenter(Documenter): def resolve_name(self, modname: str | None, parents: Any, path: str, base: str, ) -> tuple[str | None, list[str]]: if modname is not None: - return modname, parents + [base] + return modname, [*parents, base] if path: modname = path.rstrip('.') - return modname, parents + [base] + return modname, [*parents, base] # if documenting a toplevel object without explicit module, # it can be contained in another auto directive ... @@ -1215,7 +1195,7 @@ def resolve_name(self, modname: str | None, parents: Any, path: str, base: str, if not modname: modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid - return modname, parents + [base] + return modname, [*parents, base] class ClassLevelDocumenter(Documenter): @@ -1227,7 +1207,7 @@ class ClassLevelDocumenter(Documenter): def resolve_name(self, modname: str | None, parents: Any, path: str, base: str, ) -> tuple[str | None, list[str]]: if modname is not None: - return modname, parents + [base] + return modname, [*parents, base] if path: mod_cls = path.rstrip('.') @@ -1251,7 +1231,7 @@ def resolve_name(self, modname: str | None, parents: Any, path: str, base: str, if not modname: modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid - return modname, parents + [base] + return modname, [*parents, base] class DocstringSignatureMixin: @@ -1506,7 +1486,7 @@ def annotate_to_first_argument(self, func: Callable, typ: type) -> Callable | No if len(sig.parameters) == 0: return None - def dummy(): # NoQA: ANN202 + def dummy(): # type: ignore[no-untyped-def] # NoQA: ANN202 pass params = list(sig.parameters.values()) @@ -1562,7 +1542,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: objtype = 'class' member_order = 20 - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'members': members_option, 'undoc-members': bool_option, 'no-index': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'member-order': member_order_option, @@ -1574,7 +1554,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: # Must be higher than FunctionDocumenter, ClassDocumenter, and # AttributeDocumenter as NewType can be an attribute and is a class - # after Python 3.10. Before 3.10 it is a kind of function object + # after Python 3.10. priority = 15 _signature_class: Any = None @@ -1599,8 +1579,14 @@ def __init__(self, *args: Any) -> None: def can_document_member( cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: - return isinstance(member, type) or ( - isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) + # support both sphinx 8 and py3.9/older sphinx + try: + result_bool = isinstance(member, type) or ( + isattr and isinstance(member, NewType | TypeVar)) + except: + result_bool = isinstance(member, type) or ( + isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) + return result_bool def import_object(self, raiseerror: bool = False) -> bool: ret = super().import_object(raiseerror) @@ -1673,7 +1659,12 @@ def import_object(self, raiseerror: bool = False) -> bool: # ------------------------------------------------------------------- else: self.doc_as_attr = True - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: modname = getattr(self.object, '__module__', self.modname) if modname != self.modname and self.modname.startswith(modname): bases = self.modname[len(modname):].strip('.').split('.') @@ -1682,7 +1673,12 @@ def import_object(self, raiseerror: bool = False) -> bool: return ret def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]: - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: # Suppress signature return None, None, None @@ -1867,14 +1863,24 @@ def add_directive_header(self, sig: str) -> None: self.directivetype = 'attribute' super().add_directive_header(sig) - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: return if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(' :final:', sourcename) canonical_fullname = self.get_canonical_fullname() - if (not self.doc_as_attr and not inspect.isNewType(self.object) + # support both sphinx 8 and py3.9/older sphinx + try: + newtype_test = isinstance(self.object, NewType) + except: + newtype_test = inspect.isNewType(self.object) + if (not self.doc_as_attr and not newtype_test and canonical_fullname and self.fullname != canonical_fullname): self.add_line(' :canonical: %s' % canonical_fullname, sourcename) @@ -1914,8 +1920,8 @@ def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]: if name in members: selected.append(members[name]) else: - logger.warning(__('missing attribute %s in object %s') % - (name, self.fullname), type='autodoc') + logger.warning(__('missing attribute %s in object %s'), + name, self.fullname, type='autodoc') return False, selected elif self.options.inherited_members: return False, list(members.values()) @@ -1926,6 +1932,9 @@ def get_doc(self) -> list[list[str]] | None: if isinstance(self.object, TypeVar): if self.object.__doc__ == TypeVar.__doc__: return [] + # ------------------------------------------------------------------ + # This section is kept for compatibility with python 3.9 + # see https://github.com/sagemath/sage/pull/38549#issuecomment-2327790930 if sys.version_info[:2] < (3, 10): if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): parts = self.modname.strip('.').split('.') @@ -1944,6 +1953,7 @@ def get_doc(self) -> list[list[str]] | None: return [comment] except PycodeError: pass + # ------------------------------------------------------------------ if self.doc_as_attr: # Don't show the docstring of the class when it is an alias. if self.get_variable_comment(): @@ -2007,7 +2017,12 @@ def get_variable_comment(self) -> list[str] | None: return None def add_content(self, more_content: StringList | None) -> None: - if inspect.isNewType(self.object): + # support both sphinx 8 and py3.9/older sphinx + try: + newtype_test = isinstance(self.object, NewType) + except: + newtype_test = inspect.isNewType(self.object) + if newtype_test: if self.config.autodoc_typehints_format == "short": supertype = restify(self.object.__supertype__, "smart") else: @@ -2166,7 +2181,8 @@ def import_object(self, raiseerror: bool = False) -> bool: with mock(self.config.autodoc_mock_imports): parent = import_module(self.modname, self.config.autodoc_warningiserror) annotations = get_type_hints(parent, None, - self.config.autodoc_type_aliases) + self.config.autodoc_type_aliases, + include_extras=True) if self.objpath[-1] in annotations: self.object = UNINITIALIZED_ATTR self.parent = parent @@ -2200,7 +2216,7 @@ class DataDocumenter(GenericAliasMixin, objtype = 'data' member_order = 40 priority = -10 - option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) + option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option @@ -2255,7 +2271,8 @@ def add_directive_header(self, sig: str) -> None: if self.config.autodoc_typehints != 'none': # obtain annotation for this data annotations = get_type_hints(self.parent, None, - self.config.autodoc_type_aliases) + self.config.autodoc_type_aliases, + include_extras=True) if self.objpath[-1] in annotations: if self.config.autodoc_typehints_format == "short": objrepr = stringify_annotation(annotations.get(self.objpath[-1]), @@ -2394,7 +2411,8 @@ def add_directive_header(self, sig: str) -> None: self.add_line(' :abstractmethod:', sourcename) if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj): self.add_line(' :async:', sourcename) - if inspect.isclassmethod(obj): + if (inspect.isclassmethod(obj) or + inspect.is_singledispatch_method(obj) and inspect.isclassmethod(obj.func)): self.add_line(' :classmethod:', sourcename) if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): self.add_line(' :staticmethod:', sourcename) @@ -2434,7 +2452,7 @@ def annotate_to_first_argument(self, func: Callable, typ: type) -> Callable | No if len(sig.parameters) == 1: return None - def dummy(): # NoQA: ANN202 + def dummy(): # type: ignore[no-untyped-def] # NoQA: ANN202 pass params = list(sig.parameters.values()) @@ -2550,8 +2568,8 @@ def get_doc(self) -> list[list[str]] | None: if self.object is SLOTSATTR: try: parent___slots__ = inspect.getslots(self.parent) - if parent___slots__ and parent___slots__.get(self.objpath[-1]): - docstring = prepare_docstring(parent___slots__[self.objpath[-1]]) + if parent___slots__ and (docstring := parent___slots__.get(self.objpath[-1])): + docstring = prepare_docstring(docstring) return [docstring] else: return [] @@ -2582,9 +2600,7 @@ def is_runtime_instance_attribute(self, parent: Any) -> bool: # An instance variable defined in __init__(). if self.get_attribute_comment(parent, self.objpath[-1]): # type: ignore[attr-defined] return True - if self.is_runtime_instance_attribute_not_commented(parent): - return True - return False + return self.is_runtime_instance_attribute_not_commented(parent) def is_runtime_instance_attribute_not_commented(self, parent: Any) -> bool: """Check the subject is an attribute defined in __init__() without comment.""" @@ -2655,7 +2671,8 @@ class Foo: def is_uninitialized_instance_attribute(self, parent: Any) -> bool: """Check the subject is an annotation only attribute.""" - annotations = get_type_hints(parent, None, self.config.autodoc_type_aliases) + annotations = get_type_hints(parent, None, self.config.autodoc_type_aliases, + include_extras=True) return self.objpath[-1] in annotations def import_object(self, raiseerror: bool = False) -> bool: @@ -2703,7 +2720,7 @@ class AttributeDocumenter(GenericAliasMixin, SlotsMixin, # type: ignore[misc] objtype = 'attribute' member_order = 60 - option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) + option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option @@ -2809,7 +2826,8 @@ def add_directive_header(self, sig: str) -> None: if self.config.autodoc_typehints != 'none': # obtain type annotation for this attribute annotations = get_type_hints(self.parent, None, - self.config.autodoc_type_aliases) + self.config.autodoc_type_aliases, + include_extras=True) if self.objpath[-1] in annotations: if self.config.autodoc_typehints_format == "short": objrepr = stringify_annotation(annotations.get(self.objpath[-1]), @@ -2857,10 +2875,10 @@ def get_doc(self) -> list[list[str]] | None: # a docstring from the value which descriptor returns unexpectedly. # ref: https://github.com/sphinx-doc/sphinx/issues/7805 orig = self.config.autodoc_inherit_docstrings - self.config.autodoc_inherit_docstrings = False # type: ignore[attr-defined] + self.config.autodoc_inherit_docstrings = False return super().get_doc() finally: - self.config.autodoc_inherit_docstrings = orig # type: ignore[attr-defined] + self.config.autodoc_inherit_docstrings = orig def add_content(self, more_content: StringList | None) -> None: # Disable analyzing attribute comment on Documenter.add_content() to control it on @@ -2909,7 +2927,7 @@ def import_object(self, raiseerror: bool = False) -> bool: obj = __dict__.get(self.objpath[-1]) if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__): self.object = obj.__func__ - self.isclassmethod = True + self.isclassmethod: bool = True return True else: return False @@ -2980,7 +2998,7 @@ def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any: return safe_getattr(obj, name, *defargs) -def setup(app: Sphinx) -> dict[str, Any]: +def setup(app: Sphinx) -> ExtensionMetadata: app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 5621fe9e456..62b2d3cb112 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -307,6 +307,11 @@ def runsphinx(): saved_stdout = sys.stdout saved_stderr = sys.stderr + if not sys.warnoptions: + import warnings + original_filters = warnings.filters[:] + warnings.filterwarnings("ignore", category=DeprecationWarning, module='sphinx.util.inspect') + try: sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) @@ -323,3 +328,6 @@ def runsphinx(): sys.stderr = saved_stderr sys.stdout.flush() sys.stderr.flush() + + if not sys.warnoptions: + warnings.filters = original_filters[:] diff --git a/src/sage_setup/autogen/__init__.py b/src/sage_setup/autogen/__init__.py index 380983c06c7..2ec85acde21 100644 --- a/src/sage_setup/autogen/__init__.py +++ b/src/sage_setup/autogen/__init__.py @@ -1,6 +1,6 @@ import os -from . import interpreters +from sage_setup.autogen.interpreters.internal import rebuild def autogen_all(): @@ -11,6 +11,7 @@ def autogen_all(): of packages built/installed by setup.py. """ from sage.env import SAGE_SRC - interpreters.rebuild(os.path.join(SAGE_SRC, "sage", "ext", "interpreters")) - return ['sage.ext.interpreters'] + rebuild(os.path.join(SAGE_SRC, "sage", "ext", "interpreters")) + + return ["sage.ext.interpreters"] diff --git a/src/sage_setup/autogen/interpreters/__main__.py b/src/sage_setup/autogen/interpreters/__main__.py old mode 100644 new mode 100755 index 398a7b30039..3649565e7a0 --- a/src/sage_setup/autogen/interpreters/__main__.py +++ b/src/sage_setup/autogen/interpreters/__main__.py @@ -1,8 +1,9 @@ +#!/usr/bin/env python3 # Usage: python -m sage_setup.autogen.interpreters import argparse -from . import rebuild +from internal import rebuild parser = argparse.ArgumentParser() parser.add_argument("output_dir", help="Output directory") diff --git a/src/sage_setup/autogen/interpreters/__init__.py b/src/sage_setup/autogen/interpreters/internal/__init__.py similarity index 91% rename from src/sage_setup/autogen/interpreters/__init__.py rename to src/sage_setup/autogen/interpreters/internal/__init__.py index 75096d06b80..c2489ecd486 100644 --- a/src/sage_setup/autogen/interpreters/__init__.py +++ b/src/sage_setup/autogen/interpreters/internal/__init__.py @@ -109,20 +109,18 @@ # that will have to be changed. ##################################################################### -from __future__ import print_function, absolute_import +from __future__ import absolute_import, print_function import os - from os.path import getmtime -from .generator import InterpreterGenerator, AUTOGEN_WARN +from .generator import AUTOGEN_WARN, InterpreterGenerator from .instructions import * from .memory import * from .specs.base import * from .storage import * from .utils import * - # Tuple of (filename_root, extension, method) where filename_root is the # root of the filename to be joined with "_".ext and # method is the name of a get_ method on InterpreterGenerator that returns @@ -141,8 +139,8 @@ def build_interp(interp_spec, dir): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: testdir = tmp_dir() sage: rdf_interp = RDFInterpreter() sage: build_interp(rdf_interp, testdir) @@ -183,10 +181,10 @@ def rebuild(dirname, force=False, interpreters=None, distribution=None): Monolithic build:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: testdir = tmp_dir() sage: rebuild(testdir) - Building interpreters for fast_callable + Generating interpreters for fast_callable in ... -> First build of interpreters sage: with open(testdir + '/wrapper_el.pyx') as f: ....: f.readline() @@ -197,7 +195,7 @@ def rebuild(dirname, force=False, interpreters=None, distribution=None): sage: testdir = tmp_dir() sage: rebuild(testdir, interpreters=['Element', 'Python'], ....: distribution='sagemath-categories') - Building interpreters for fast_callable + Generating interpreters for fast_callable in ... -> First build of interpreters sage: with open(testdir + '/all__sagemath_categories.py') as f: ....: f.readline() @@ -205,16 +203,22 @@ def rebuild(dirname, force=False, interpreters=None, distribution=None): """ # This line will show up in "sage -b" (once per upgrade, not every time # you run it). - print("Building interpreters for fast_callable") + print(f"Generating interpreters for fast_callable in {dirname}") if interpreters is None: interpreters = ['CDF', 'Element', 'Python', 'RDF', 'RR', 'CC'] from importlib import import_module - _INTERPRETERS = [getattr(import_module('sage_setup.autogen.interpreters.specs.' + interpreter.lower()), - interpreter + 'Interpreter') - for interpreter in interpreters] + _INTERPRETERS = [ + getattr( + import_module( + ".specs." + interpreter.lower(), package=__name__ + ), + interpreter + "Interpreter", + ) + for interpreter in interpreters + ] if distribution is None: all_py = 'all.py' @@ -226,11 +230,6 @@ def rebuild(dirname, force=False, interpreters=None, distribution=None): except OSError: if not os.path.isdir(dirname): raise - # Remove leftover file from before move to namespace packages - try: - os.remove(os.path.join(dirname, '__init__.py')) - except FileNotFoundError: - pass # Although multiple files are generated by this function, since # they are all generated at once it suffices to make sure if just @@ -264,3 +263,6 @@ class NeedToRebuild(Exception): with open(os.path.join(dirname, all_py), 'w') as f: f.write("# " + AUTOGEN_WARN) + + with open(os.path.join(dirname, '__init__.py'), 'w') as f: + f.write("# " + AUTOGEN_WARN) diff --git a/src/sage_setup/autogen/interpreters/generator.py b/src/sage_setup/autogen/interpreters/internal/generator.py similarity index 94% rename from src/sage_setup/autogen/interpreters/generator.py rename to src/sage_setup/autogen/interpreters/internal/generator.py index 33877422a8c..ebfea5478fc 100644 --- a/src/sage_setup/autogen/interpreters/generator.py +++ b/src/sage_setup/autogen/interpreters/internal/generator.py @@ -42,8 +42,8 @@ def __init__(self, spec): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: gen = InterpreterGenerator(interp) sage: gen._spec is interp @@ -72,8 +72,8 @@ def gen_code(self, instr_desc, write): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: gen = InterpreterGenerator(interp) sage: from io import StringIO @@ -219,8 +219,8 @@ def func_header(self, cython=False): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.element import ElementInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter sage: interp = ElementInterpreter() sage: gen = InterpreterGenerator(interp) sage: print(gen.func_header()) @@ -262,8 +262,8 @@ def write_interpreter(self, write): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: gen = InterpreterGenerator(interp) sage: from io import StringIO @@ -310,8 +310,8 @@ def write_wrapper(self, write): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: gen = InterpreterGenerator(interp) sage: from io import StringIO @@ -480,8 +480,8 @@ def write_pxd(self, write): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: gen = InterpreterGenerator(interp) sage: from io import StringIO @@ -532,10 +532,10 @@ def get_interpreter(self): First we get the InterpreterSpec for several interpreters:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter - sage: from sage_setup.autogen.interpreters.specs.element import ElementInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter sage: rdf_spec = RDFInterpreter() sage: rr_spec = RRInterpreter() sage: el_spec = ElementInterpreter() @@ -657,10 +657,10 @@ def get_wrapper(self): First we get the InterpreterSpec for several interpreters:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter - sage: from sage_setup.autogen.interpreters.specs.element import ElementInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter sage: rdf_spec = RDFInterpreter() sage: rr_spec = RRInterpreter() sage: el_spec = ElementInterpreter() @@ -983,10 +983,10 @@ def get_pxd(self): First we get the InterpreterSpec for several interpreters:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter - sage: from sage_setup.autogen.interpreters.specs.element import ElementInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter sage: rdf_spec = RDFInterpreter() sage: rr_spec = RRInterpreter() sage: el_spec = ElementInterpreter() diff --git a/src/sage_setup/autogen/interpreters/instructions.py b/src/sage_setup/autogen/interpreters/internal/instructions.py similarity index 89% rename from src/sage_setup/autogen/interpreters/instructions.py rename to src/sage_setup/autogen/interpreters/internal/instructions.py index 116f598197c..aad7583196d 100644 --- a/src/sage_setup/autogen/interpreters/instructions.py +++ b/src/sage_setup/autogen/interpreters/internal/instructions.py @@ -56,7 +56,7 @@ def params_gen(**chunks): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc_stack = MemoryChunkScratch('stack', ty_double, is_stack=True) sage: mc_args = MemoryChunkArguments('args', ty_double) sage: mc_code = MemoryChunkConstants('code', ty_int) @@ -186,8 +186,8 @@ class InstrSpec(object): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;') add: SS->S = 'o0 = i0+i1;' @@ -213,8 +213,8 @@ def __init__(self, name, io, code=None, uses_error_handler=False, EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;') add: SS->S = 'o0 = i0+i1;' @@ -288,8 +288,8 @@ def __repr__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: InstrSpec('add', pg('SS','S'), code='o0 = i0+i1;') add: SS->S = 'o0 = i0+i1;' @@ -311,8 +311,8 @@ def instr_infix(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: instr_infix('mul', pg('SS', 'S'), '*') mul: SS->S = 'o0 = i0 * i1;' @@ -327,8 +327,8 @@ def instr_funcall_2args(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: instr_funcall_2args('atan2', pg('SS', 'S'), 'atan2') atan2: SS->S = 'o0 = atan2(i0, i1);' @@ -343,8 +343,8 @@ def instr_unary(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: pg = RDFInterpreter().pg sage: instr_unary('sin', pg('S','S'), 'sin(i0)') sin: S->S = 'o0 = sin(i0);' @@ -361,8 +361,8 @@ def instr_funcall_2args_mpfr(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter sage: pg = RRInterpreter().pg sage: instr_funcall_2args_mpfr('add', pg('SS','S'), 'mpfr_add') add: SS->S = 'mpfr_add(o0, i0, i1, MPFR_RNDN);' @@ -377,8 +377,8 @@ def instr_funcall_1arg_mpfr(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter sage: pg = RRInterpreter().pg sage: instr_funcall_1arg_mpfr('exp', pg('S','S'), 'mpfr_exp') exp: S->S = 'mpfr_exp(o0, i0, MPFR_RNDN);' @@ -392,8 +392,8 @@ def instr_funcall_2args_mpc(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import CCInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter sage: pg = CCInterpreter().pg sage: instr_funcall_2args_mpc('add', pg('SS','S'), 'mpc_add') add: SS->S = 'mpc_add(o0, i0, i1, MPC_RNDNN);' @@ -407,8 +407,8 @@ def instr_funcall_1arg_mpc(name, io, op): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import CCInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import CCInterpreter sage: pg = CCInterpreter().pg sage: instr_funcall_1arg_mpc('exp', pg('S','S'), 'mpc_exp') exp: S->S = 'mpc_exp(o0, i0, MPC_RNDNN);' diff --git a/src/sage_setup/autogen/interpreters/memory.py b/src/sage_setup/autogen/interpreters/internal/memory.py similarity index 90% rename from src/sage_setup/autogen/interpreters/memory.py rename to src/sage_setup/autogen/interpreters/internal/memory.py index e719f47d77a..801596e98c8 100644 --- a/src/sage_setup/autogen/interpreters/memory.py +++ b/src/sage_setup/autogen/interpreters/internal/memory.py @@ -27,7 +27,7 @@ def string_of_addr(a): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc_code = MemoryChunkConstants('code', ty_int) sage: string_of_addr(mc_code) '*code++' @@ -72,7 +72,7 @@ def __init__(self, name, storage_type): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: mc.name 'args' @@ -88,7 +88,7 @@ def __repr__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: mc {MC:args} @@ -104,7 +104,7 @@ def declare_class_members(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: mc.declare_class_members() ' cdef int _n_args\n cdef mpfr_t* _args\n' @@ -119,7 +119,7 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: print(mc.init_class_members()) count = args['args'] @@ -139,7 +139,7 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: print(mc.dealloc_class_members()) if self._args: @@ -157,7 +157,7 @@ def declare_parameter(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: mc.declare_parameter() 'mpfr_t* args' @@ -171,8 +171,8 @@ class using this memory chunk, to allocate local variables. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.declare_call_locals() ' cdef RealNumber retval = (self.domain)()\n' @@ -186,7 +186,7 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkConstants('constants', ty_mpfr) sage: mc.pass_argument() 'self._constants' @@ -201,7 +201,7 @@ def pass_call_c_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkConstants('constants', ty_mpfr) sage: mc.pass_call_c_argument() 'self._constants' @@ -221,7 +221,7 @@ def needs_cleanup_on_error(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkConstants('constants', ty_mpfr) sage: mc.needs_cleanup_on_error() False @@ -246,7 +246,7 @@ def is_stack(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('scratch', ty_mpfr) sage: mc.is_stack() False @@ -268,7 +268,7 @@ def is_python_refcounted_stack(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('args', ty_python) sage: mc.is_python_refcounted_stack() False @@ -297,7 +297,7 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_double) sage: print(mc.init_class_members()) count = args['args'] @@ -318,7 +318,7 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: print(mc.dealloc_class_members()) if self._args: @@ -336,7 +336,7 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkConstants('constants', ty_mpfr) sage: mc.pass_argument() 'self._constants' @@ -360,7 +360,7 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkConstants('constants', ty_mpfr) sage: print(mc.init_class_members()) val = args['constants'] @@ -398,7 +398,7 @@ def setup_args(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: print(mc.setup_args()) cdef mpfr_t* c_args = self._args @@ -422,7 +422,7 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkArguments('args', ty_mpfr) sage: mc.pass_argument() 'c_args' @@ -449,7 +449,7 @@ def __init__(self, name, storage_type, is_stack=False): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('stack', ty_double, is_stack=True) sage: mc.name 'stack' @@ -469,7 +469,7 @@ def is_stack(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('stack', ty_mpfr, is_stack=True) sage: mc.is_stack() True @@ -489,7 +489,7 @@ def needs_cleanup_on_error(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('registers', ty_python) sage: mc.needs_cleanup_on_error() True @@ -508,7 +508,7 @@ def handle_cleanup(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: mc = MemoryChunkScratch('registers', ty_python) sage: print(mc.handle_cleanup()) for i in range(self._n_registers): diff --git a/src/sage_setup/autogen/interpreters/internal/specs/__init__.py b/src/sage_setup/autogen/interpreters/internal/specs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage_setup/autogen/interpreters/specs/base.py b/src/sage_setup/autogen/interpreters/internal/specs/base.py similarity index 87% rename from src/sage_setup/autogen/interpreters/specs/base.py rename to src/sage_setup/autogen/interpreters/internal/specs/base.py index c311c76fb04..6abd2253870 100644 --- a/src/sage_setup/autogen/interpreters/specs/base.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/base.py @@ -48,9 +48,9 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter sage: interp = RDFInterpreter() sage: interp.c_header '#include ' @@ -85,8 +85,8 @@ def _set_opcodes(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter sage: interp = RDFInterpreter() sage: interp.instr_descs[5].opcode 5 @@ -130,10 +130,10 @@ def __init__(self, type, mc_retval=None): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import RDFInterpreter - sage: from sage_setup.autogen.interpreters.specs.rr import RRInterpreter - sage: from sage_setup.autogen.interpreters.specs.element import ElementInterpreter + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import RDFInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.rr import RRInterpreter + sage: from sage_setup.autogen.interpreters.internal.specs.element import ElementInterpreter sage: rdf = RDFInterpreter() sage: rr = RRInterpreter() sage: el = ElementInterpreter() diff --git a/src/sage_setup/autogen/interpreters/specs/cc.py b/src/sage_setup/autogen/interpreters/internal/specs/cc.py similarity index 91% rename from src/sage_setup/autogen/interpreters/specs/cc.py rename to src/sage_setup/autogen/interpreters/internal/specs/cc.py index e16252b0b79..cc42a6defab 100644 --- a/src/sage_setup/autogen/interpreters/specs/cc.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/cc.py @@ -30,8 +30,8 @@ def declare_class_members(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: mc = MemoryChunkCCRetval('retval', ty_mpc) sage: mc.declare_class_members() '' @@ -45,8 +45,8 @@ class using this memory chunk, to allocate local variables. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: mc = MemoryChunkCCRetval('retval', ty_mpc) sage: mc.declare_call_locals() ' cdef ComplexNumber retval = (self.domain_element._new())\n' @@ -63,8 +63,8 @@ def declare_parameter(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: mc = MemoryChunkCCRetval('retval', ty_mpc) sage: mc.declare_parameter() 'mpc_t retval' @@ -78,8 +78,8 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: mc = MemoryChunkCCRetval('retval', ty_mpc) sage: mc.pass_argument() '((retval.__re))' @@ -93,8 +93,8 @@ def pass_call_c_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: mc = MemoryChunkCCRetval('retval', ty_mpc) sage: mc.pass_call_c_argument() 'result' @@ -115,8 +115,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cc import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cc import * sage: interp = CCInterpreter() sage: interp.name 'cc' @@ -149,7 +149,6 @@ def __init__(self): sage: print(interp.c_header) #include - #include "wrapper_cc.h" So instructions where you need to interact with Python can @@ -171,7 +170,6 @@ def __init__(self): self.c_header = ri(0, ''' #include - #include "wrapper_cc.h" ''') self.pxd_header = ri(0, diff --git a/src/sage_setup/autogen/interpreters/specs/cdf.py b/src/sage_setup/autogen/interpreters/internal/specs/cdf.py similarity index 98% rename from src/sage_setup/autogen/interpreters/specs/cdf.py rename to src/sage_setup/autogen/interpreters/internal/specs/cdf.py index c9ea258e221..1edc1f7abe3 100644 --- a/src/sage_setup/autogen/interpreters/specs/cdf.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/cdf.py @@ -33,8 +33,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.cdf import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.cdf import * sage: interp = CDFInterpreter() sage: interp.name 'cdf' @@ -85,7 +85,6 @@ def __init__(self): self.c_header = ri(0,""" #include #include - #include "wrapper_cdf.h" /* On Solaris, we need to define _Imaginary_I when compiling with GCC, * otherwise the constant I doesn't work. The definition below is based diff --git a/src/sage_setup/autogen/interpreters/specs/element.py b/src/sage_setup/autogen/interpreters/internal/specs/element.py similarity index 90% rename from src/sage_setup/autogen/interpreters/specs/element.py rename to src/sage_setup/autogen/interpreters/internal/specs/element.py index 2f280f703f1..5dc7c6592c9 100644 --- a/src/sage_setup/autogen/interpreters/specs/element.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/element.py @@ -37,8 +37,8 @@ def setup_args(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.element import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.element import * sage: mc = MemoryChunkElementArguments('args', ty_python) sage: mc.setup_args() 'mapped_args = [self._domain(a) for a in args]\n' @@ -51,8 +51,8 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.element import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.element import * sage: mc = MemoryChunkElementArguments('args', ty_python) sage: mc.pass_argument() '(mapped_args).ob_item' @@ -81,8 +81,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.element import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.element import * sage: interp = ElementInterpreter() sage: interp.name 'el' @@ -105,8 +105,6 @@ def __init__(self): self.chunks = [self.mc_args, self.mc_constants, self.mc_stack, self.mc_domain_info, self.mc_code] self.c_header = ri(0, """ - #include "wrapper_el.h" - #define CHECK(x) do_check(&(x), domain) static inline int do_check(PyObject **x, PyObject *domain) { diff --git a/src/sage_setup/autogen/interpreters/specs/python.py b/src/sage_setup/autogen/interpreters/internal/specs/python.py similarity index 86% rename from src/sage_setup/autogen/interpreters/specs/python.py rename to src/sage_setup/autogen/interpreters/internal/specs/python.py index c185557165f..24bab75f10d 100644 --- a/src/sage_setup/autogen/interpreters/specs/python.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/python.py @@ -31,8 +31,8 @@ def declare_class_members(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPythonArguments('args', ty_python) """ return " cdef int _n_%s\n" % self.name @@ -45,8 +45,8 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPythonArguments('args', ty_python) sage: mc.init_class_members() " count = args['args']\n self._n_args = count\n" @@ -63,8 +63,8 @@ def setup_args(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPythonArguments('args', ty_python) sage: mc.setup_args() '' @@ -77,8 +77,8 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPythonArguments('args', ty_python) sage: mc.pass_argument() '(args).ob_item' @@ -100,8 +100,8 @@ def __init__(self, name): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPyConstant('domain') sage: mc.name 'domain' @@ -117,8 +117,8 @@ def declare_class_members(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPyConstant('domain') sage: mc.declare_class_members() ' cdef object _domain\n' @@ -136,8 +136,8 @@ class members. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPyConstant('domain') sage: mc.init_class_members() " self._domain = args['domain']\n" @@ -154,8 +154,8 @@ def declare_parameter(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPyConstant('domain') sage: mc.declare_parameter() 'PyObject* domain' @@ -169,8 +169,8 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: mc = MemoryChunkPyConstant('domain') sage: mc.pass_argument() 'self._domain' @@ -212,8 +212,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.python import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.python import * sage: interp = PythonInterpreter() sage: interp.name 'py' diff --git a/src/sage_setup/autogen/interpreters/specs/rdf.py b/src/sage_setup/autogen/interpreters/internal/specs/rdf.py similarity index 97% rename from src/sage_setup/autogen/interpreters/specs/rdf.py rename to src/sage_setup/autogen/interpreters/internal/specs/rdf.py index 95894f8d6d2..83a0ecde012 100644 --- a/src/sage_setup/autogen/interpreters/specs/rdf.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/rdf.py @@ -36,8 +36,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rdf import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rdf import * sage: interp = RDFInterpreter() sage: interp.name 'rdf' diff --git a/src/sage_setup/autogen/interpreters/specs/rr.py b/src/sage_setup/autogen/interpreters/internal/specs/rr.py similarity index 91% rename from src/sage_setup/autogen/interpreters/specs/rr.py rename to src/sage_setup/autogen/interpreters/internal/specs/rr.py index 06452e24047..e51262d6196 100644 --- a/src/sage_setup/autogen/interpreters/specs/rr.py +++ b/src/sage_setup/autogen/interpreters/internal/specs/rr.py @@ -30,8 +30,8 @@ def declare_class_members(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.declare_class_members() '' @@ -45,8 +45,8 @@ class using this memory chunk, to allocate local variables. EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.declare_call_locals() ' cdef RealNumber retval = (self.domain)()\n' @@ -63,8 +63,8 @@ def declare_parameter(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.declare_parameter() 'mpfr_t retval' @@ -78,8 +78,8 @@ def pass_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.pass_argument() 'retval.value' @@ -93,8 +93,8 @@ def pass_call_c_argument(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: mc = MemoryChunkRRRetval('retval', ty_mpfr) sage: mc.pass_call_c_argument() 'result' @@ -116,8 +116,8 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * - sage: from sage_setup.autogen.interpreters.specs.rr import * + sage: from sage_setup.autogen.interpreters.internal import * + sage: from sage_setup.autogen.interpreters.internal.specs.rr import * sage: interp = RRInterpreter() sage: interp.name 'rr' @@ -148,7 +148,6 @@ def __init__(self): sage: print(interp.c_header) #include - #include "wrapper_rr.h" The function ``rr_py_call_helper`` is implemented in Cython:: @@ -188,7 +187,6 @@ def __init__(self): self.c_header = ri(0, ''' #include - #include "wrapper_rr.h" ''') self.pxd_header = ri(0, diff --git a/src/sage_setup/autogen/interpreters/storage.py b/src/sage_setup/autogen/interpreters/internal/storage.py similarity index 90% rename from src/sage_setup/autogen/interpreters/storage.py rename to src/sage_setup/autogen/interpreters/internal/storage.py index 291398fd9d9..ce77abbe586 100644 --- a/src/sage_setup/autogen/interpreters/storage.py +++ b/src/sage_setup/autogen/interpreters/internal/storage.py @@ -67,7 +67,7 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.class_member_declarations '' sage: ty_double.class_member_initializations @@ -104,7 +104,7 @@ def cheap_copies(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.cheap_copies() True sage: ty_python.cheap_copies() @@ -126,7 +126,7 @@ def python_refcounted(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.python_refcounted() False sage: ty_python.python_refcounted() @@ -140,7 +140,7 @@ def cython_decl_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.cython_decl_type() 'double' sage: ty_python.cython_decl_type() @@ -157,7 +157,7 @@ def cython_array_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.cython_array_type() 'double*' sage: ty_python.cython_array_type() @@ -180,7 +180,7 @@ def needs_cython_init_clear(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.needs_cython_init_clear() False sage: ty_mpfr.needs_cython_init_clear() @@ -196,7 +196,7 @@ def c_decl_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_decl_type() 'double' sage: ty_python.c_decl_type() @@ -213,7 +213,7 @@ def c_ptr_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_ptr_type() 'double*' sage: ty_python.c_ptr_type() @@ -231,7 +231,7 @@ def c_reference_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_reference_type() 'double*' sage: ty_python.c_reference_type() @@ -248,7 +248,7 @@ def c_local_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_local_type() 'double' sage: ty_python.c_local_type() @@ -266,7 +266,7 @@ def assign_c_from_py(self, c, py): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.assign_c_from_py('foo', 'bar') 'foo = bar' sage: ty_python.assign_c_from_py('foo[i]', 'bar[j]') @@ -284,7 +284,7 @@ def declare_chunk_class_members(self, name): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.declare_chunk_class_members('args') ' cdef int _n_args\n cdef mpfr_t* _args\n' """ @@ -302,7 +302,7 @@ def alloc_chunk_data(self, name, len): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: print(ty_mpfr.alloc_chunk_data('args', 'MY_LENGTH')) self._n_args = MY_LENGTH self._args = check_allocarray(self._n_args, sizeof(mpfr_t)) @@ -328,7 +328,7 @@ def dealloc_chunk_data(self, name): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: print(ty_double.dealloc_chunk_data('args')) if self._args: sig_free(self._args) @@ -364,7 +364,7 @@ def __init__(self, ty): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.class_member_declarations '' sage: ty_double.class_member_initializations @@ -391,7 +391,7 @@ def cheap_copies(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.cheap_copies() True sage: ty_python.cheap_copies() @@ -405,7 +405,7 @@ def c_decl_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_decl_type() 'double' sage: ty_python.c_decl_type() @@ -422,7 +422,7 @@ def c_local_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_double.c_local_type() 'double' sage: ty_python.c_local_type() @@ -456,7 +456,7 @@ class StorageTypeDoubleComplex(StorageTypeSimple): """ def assign_c_from_py(self, c, py): """ - sage: from sage_setup.autogen.interpreters import ty_double_complex + sage: from sage_setup.autogen.interpreters.internal import ty_double_complex sage: ty_double_complex.assign_c_from_py('z_c', 'z_py') 'z_c = CDE_to_dz(z_py)' """ @@ -500,7 +500,7 @@ def __init__(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.class_member_declarations '' sage: ty_python.class_member_initializations @@ -521,7 +521,7 @@ def python_refcounted(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.python_refcounted() True """ @@ -533,7 +533,7 @@ def cython_decl_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.cython_decl_type() 'object' """ @@ -547,7 +547,7 @@ def declare_chunk_class_members(self, name): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.declare_chunk_class_members('args') ' cdef object _list_args\n cdef int _n_args\n cdef PyObject** _args\n' """ @@ -565,7 +565,7 @@ def alloc_chunk_data(self, name, len): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: print(ty_python.alloc_chunk_data('args', 'MY_LENGTH')) self._n_args = MY_LENGTH self._list_args = PyList_New(self._n_args) @@ -590,7 +590,7 @@ def dealloc_chunk_data(self, name): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.dealloc_chunk_data('args') '' """ @@ -605,7 +605,7 @@ def needs_cython_init_clear(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.needs_cython_init_clear() True """ @@ -619,7 +619,7 @@ def assign_c_from_py(self, c, py): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.assign_c_from_py('foo[i]', 'bar[j]') 'foo[i] = bar[j]; Py_INCREF(foo[i])' """ @@ -633,7 +633,7 @@ def cython_init(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.cython_init('foo[i]') 'foo[i] = NULL' """ @@ -646,7 +646,7 @@ def cython_clear(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_python.cython_clear('foo[i]') 'Py_CLEAR(foo[i])' """ @@ -674,7 +674,7 @@ def __init__(self, decl_ty, ref_ty): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.class_member_declarations 'cdef RealField_class domain\n' sage: ty_mpfr.class_member_initializations @@ -696,7 +696,7 @@ def c_decl_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.c_decl_type() 'mpfr_t' """ @@ -711,7 +711,7 @@ def c_local_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.c_local_type() 'mpfr_ptr' """ @@ -725,7 +725,7 @@ def c_reference_type(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.c_reference_type() 'mpfr_t' """ @@ -741,7 +741,7 @@ def needs_cython_init_clear(self): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.needs_cython_init_clear() True """ @@ -775,7 +775,7 @@ def __init__(self, id=''): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.class_member_declarations 'cdef RealField_class domain\n' sage: ty_mpfr.class_member_initializations @@ -812,7 +812,7 @@ def cython_init(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.cython_init('foo[i]') 'mpfr_init2(foo[i], self.domain.prec())' """ @@ -826,7 +826,7 @@ def cython_clear(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.cython_clear('foo[i]') 'mpfr_clear(foo[i])' """ @@ -840,7 +840,7 @@ def assign_c_from_py(self, c, py): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpfr.assign_c_from_py('foo[i]', 'bar[j]') 'rn = self.domain(bar[j])\nmpfr_set(foo[i], rn.value, MPFR_RNDN)' """ @@ -879,7 +879,7 @@ def __init__(self, id=''): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpc.class_member_declarations 'cdef object domain\ncdef ComplexNumber domain_element\n' sage: ty_mpc.class_member_initializations @@ -916,7 +916,7 @@ def cython_init(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpc.cython_init('foo[i]') 'mpc_init2(foo[i], self.domain_element._prec)' """ @@ -930,7 +930,7 @@ def cython_clear(self, loc): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpc.cython_clear('foo[i]') 'mpc_clear(foo[i])' """ @@ -944,7 +944,7 @@ def assign_c_from_py(self, c, py): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: ty_mpc.assign_c_from_py('foo[i]', 'bar[j]') 'cn = self.domain(bar[j])\nmpc_set_fr_fr(foo[i], cn.__re, cn.__im, MPC_RNDNN)' """ diff --git a/src/sage_setup/autogen/interpreters/utils.py b/src/sage_setup/autogen/interpreters/internal/utils.py similarity index 93% rename from src/sage_setup/autogen/interpreters/utils.py rename to src/sage_setup/autogen/interpreters/internal/utils.py index 10f239ed2a3..247243ef644 100644 --- a/src/sage_setup/autogen/interpreters/utils.py +++ b/src/sage_setup/autogen/interpreters/internal/utils.py @@ -50,7 +50,7 @@ def je(template, **kwargs): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import je + sage: from sage_setup.autogen.interpreters.internal import je sage: je("{{ a }} > {{ b }} * {{ c }}", a='"a suffusion of yellow"', b=3, c=7) '"a suffusion of yellow" > 3 * 7' """ @@ -76,7 +76,7 @@ def indent_lines(n, text): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import indent_lines + sage: from sage_setup.autogen.interpreters.internal import indent_lines sage: indent_lines(3, "foo") ' foo' sage: indent_lines(3, "foo\nbar") @@ -102,7 +102,7 @@ def reindent_lines(n, text): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import reindent_lines + sage: from sage_setup.autogen.interpreters.internal import reindent_lines sage: print(reindent_lines(3, " foo\n bar")) foo bar @@ -118,7 +118,7 @@ def write_if_changed(fn, value): EXAMPLES:: - sage: from sage_setup.autogen.interpreters import * + sage: from sage_setup.autogen.interpreters.internal import * sage: def last_modification(fn): return os.stat(fn).st_mtime sage: fn = tmp_filename('gen_interp') sage: write_if_changed(fn, 'Hello, world') diff --git a/src/sage_setup/find.py b/src/sage_setup/find.py index 5447f6418a4..e00336b3443 100644 --- a/src/sage_setup/find.py +++ b/src/sage_setup/find.py @@ -343,6 +343,92 @@ def find_extra_files(src_dir, modules, cythonized_dir, special_filenames=[], *, return data_files +def installed_files_by_module(site_packages, modules=('sage',)): + """ + Find all currently installed files + + INPUT: + + - ``site_packages`` -- string. The root Python path where the Sage + library is being installed. If the path doesn't exist, returns + an empty dictionary. + + - ``modules`` -- list/tuple/iterable of strings (default: + ``('sage',)``). The top-level directory name(s) in + ``site_packages``. + + OUTPUT: + + A dictionary whose keys are module names (``'sage.module.foo'``) + and values are list of corresponding file names + ``['sage/module/foo.py', 'sage/module/foo.pyc']`` relative to + ``site_packages``. + + EXAMPLES:: + + sage: site_packages = os.path.dirname(os.path.dirname(os.path.dirname(sage.cpython.__file__))) + sage: from sage_setup.find import installed_files_by_module + sage: files_by_module = installed_files_by_module(site_packages) + sage: (f,) = files_by_module['sage.structure.sage_object']; f + 'sage/structure/sage_object...' + sage: (f1, f2) = sorted(files_by_module['sage.structure']) + sage: f1 + 'sage/structure/__init__.py' + sage: f2 + 'sage/structure/....pyc' + + This takes about 30ms with warm cache:: + + sage: timeit('installed_files_by_module(site_packages)', # random output + ....: number=1, repeat=1) + 1 loops, best of 1: 29.6 ms per loop + """ + + module_files = defaultdict(set) + module_exts = get_extensions() + + def add(module, filename, dirpath): + # Find the longest extension that matches the filename + best_ext = '' + + for ext in module_exts: + if filename.endswith(ext) and len(ext) > len(best_ext): + best_ext = ext + + if not best_ext: + return + + base = filename[:-len(best_ext)] + filename = os.path.join(dirpath, filename) + + if base != '__init__': + module += '.' + base + + module_files[module].add(filename) + + cache_filename = importlib.util.cache_from_source(filename) + if os.path.exists(cache_filename): + module_files[module].add(cache_filename) + + cwd = os.getcwd() + try: + os.chdir(site_packages) + except OSError: + return module_files + try: + for module in modules: + for dirpath, dirnames, filenames in os.walk(module): + module_dir = '.'.join(dirpath.split(os.path.sep)) + + if os.path.basename(dirpath) == '__pycache__': + continue + + for filename in filenames: + add(module_dir, filename, dirpath) + finally: + os.chdir(cwd) + return module_files + def get_extensions(type=None): """ diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 76aa08f8833..969793209c8 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -33,7 +33,7 @@ dnl From Makefile.in: SAGERUNTIME SPKG_INSTALL_REQUIRES_ipython SPKG_INSTALL_REQUIRES_pexpect dnl From Makefile.in: DOC_DEPENDENCIES - SPKG_INSTALL_REQUIRES_sphinx + sphinx >=5.2, <9 SPKG_INSTALL_REQUIRES_networkx SPKG_INSTALL_REQUIRES_scipy SPKG_INSTALL_REQUIRES_sympy diff --git a/src/tox.ini b/src/tox.ini index dc244c115f6..b6548bc55a4 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -180,8 +180,8 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E271,E301,E302,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E275,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 @@ -291,7 +291,6 @@ passenv = RUFF_OUTPUT_FORMAT # 116 PLR0402 [*] Use `from matplotlib import cm` in lieu of alias # 111 PLW0603 [ ] Using the global statement to update `AA_0` is discouraged # 78 F841 [*] Local variable `B` is assigned to but never used -# 64 E713 [*] Test for membership should be `not in` # 48 PLW0602 [ ] Using global for `D` but no assignment is done # 33 PLR1711 [*] Useless `return` statement at end of function # 24 E714 [*] Test for object identity should be `is not` @@ -315,7 +314,7 @@ passenv = RUFF_OUTPUT_FORMAT # 1 F402 [ ] Import `factor` from line 259 shadowed by loop variable # 1 PLC0208 [*] Use a sequence type instead of a `set` when iterating over values # -commands = ruff check --ignore PLR2004,I001,F401,E741,F821,PLR0912,PLR0913,E402,PLR0915,PLW2901,PLR5501,PLR0911,E731,F405,PLR1714,PLR1736,F403,PLR0402,PLW0603,F841,E713,PLW0602,PLR1711,E714,PLR1701,PLR1704,PLW3301,PLW1510,E721,PLW0120,F811,PLC2401,PLC0414,E743,PLE0101,PLR0124,PLW0127,F541,PLW1508,PLC3002,E742,PLE0302,PLW0129,F402,PLC0208 {posargs:{toxinidir}/sage/} +commands = ruff check --ignore PLR2004,I001,F401,E741,F821,PLR0912,PLR0913,E402,PLR0915,PLW2901,PLR5501,PLR0911,E731,F405,PLR1714,PLR1736,F403,PLR0402,PLW0603,F841,PLW0602,PLW0642,PLR1711,E714,SIM101,PLR1704,PLW3301,PLW1510,E721,PLW0211,PLW0120,F811,PLC2401,PLC0414,E743,PLE0101,PLR0124,PLW0127,F541,PLW1508,PLC3002,E742,PLE0302,PLW0129,F402,PLC0208 {posargs:{toxinidir}/sage/} [flake8] rst-roles = @@ -380,7 +379,7 @@ exclude = [pytest] python_files = *_test.py -norecursedirs = local prefix venv build pkgs .git src/doc src/bin +norecursedirs = local prefix venv build builddir pkgs .git src/doc src/bin tools addopts = --import-mode importlib doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS # https://docs.pytest.org/en/stable/reference/reference.html#confval-consider_namespace_packages @@ -390,7 +389,9 @@ consider_namespace_packages = True source = sage concurrency = multiprocessing,thread data_file = .coverage/.coverage -disable_warnings = no-data-collected +disable_warnings = + no-data-collected + module-not-measured [coverage:report] ignore_errors = True diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000000..b0c2e4bb68b --- /dev/null +++ b/tools/README.md @@ -0,0 +1,13 @@ +# Tools Directory + +This folder contains various command-line tools that are used to facilitate different development tasks. Below is a brief description of each command available in this directory. + +## Update Meson Build Files + +This command is used to updates the Meson build files in the project. It automatically adds new source files (py, pyx) to the Meson files and removes deleted source files. This command is useful when adding or removing source files from the project. + +Within an active virtual environment where Meson is installed, run the following command: + +```bash +tools/update_meson.py +``` diff --git a/tools/update-meson.py b/tools/update-meson.py new file mode 100755 index 00000000000..827b7d9b5a3 --- /dev/null +++ b/tools/update-meson.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +import argparse +import os +from argparse import Namespace +from pathlib import Path + +from mesonbuild import mlog +from mesonbuild.ast import ( + AstPrinter, + AstVisitor, +) +from mesonbuild.ast.interpreter import MethodNode +from mesonbuild.mformat import ( + run as meson_format, +) +from mesonbuild.mparser import ( + AssignmentNode, + BaseNode, + DictNode, + SymbolNode, +) +from mesonbuild.rewriter import ( + ArgumentNode, + ArrayNode, + FunctionNode, + Rewriter, + StringNode, + Token, +) + +# Get target directory from command line arguments +parser = argparse.ArgumentParser() +parser.add_argument( + "sourcedir", help="Source directory", nargs="?", default=".", type=Path +) +options = parser.parse_args() + + +class AstPython(AstVisitor): + install_sources_calls: list[MethodNode] = [] + extension_data: list[AssignmentNode] = [] + + def visit_MethodNode(self, node: MethodNode) -> None: + if node.name.value == "install_sources": + self.install_sources_calls += [node] + return super().visit_MethodNode(node) + + def visit_AssignmentNode(self, node: AssignmentNode) -> None: + if node.var_name.value in ["extension_data", "extension_data_cpp"]: + self.extension_data += [node] + return super().visit_AssignmentNode(node) + + +# Utility function to get a list of the sources from a node +def arg_list_from_node(n): + args = [] + if isinstance(n, FunctionNode) or isinstance(n, MethodNode): + args = list(n.args.arguments) + # if 'func_name' in n and n.func_name.value in BUILD_TARGET_FUNCTIONS: + # args.pop(0) + elif isinstance(n, ArrayNode): + args = n.args.arguments + elif isinstance(n, ArgumentNode): + args = n.arguments + return args + + +def _symbol(val: str) -> SymbolNode: + return SymbolNode(Token("", "", 0, 0, 0, (0, 0), val)) + + +def update_python_sources(self: Rewriter, visitor: AstPython): + for target in visitor.install_sources_calls: + # Generate the current source list + src_list: list[str] = [] + for arg in arg_list_from_node(target): + if isinstance(arg, StringNode): + src_list += [arg.value] + + folder = Path(target.filename).parent + python_files = sorted( + list(folder.glob("*.py")) + list(folder.glob('*.pxd')) + ) # + list(folder.glob('*.pxd')) + list(folder.glob('*.h'))) + + to_append: list[StringNode] = [] + for file in python_files: + file_name = file.name + if file_name == "__init__.py": + # We don't want to add __init__.py files + continue + if file_name in src_list: + continue + token = Token("string", target.filename, 0, 0, 0, None, file_name) + to_append += [StringNode(token)] + + # Get all deleted files + to_remove = [] + for src in src_list: + if not folder.joinpath(src).exists(): + to_remove += [src] + + if not to_append and not to_remove: + continue + + # Update the source list + target.args.arguments = sorted( + [ + arg + for arg in target.args.arguments + if not (isinstance(arg, StringNode) and arg.value in to_remove) + ] + + to_append, + key=lambda x: x.value, + ) + + # Mark the node as modified + if target not in self.modified_nodes: + self.modified_nodes += [target] + + ext_data: dict[Path, list[str]] = {} + for target in visitor.extension_data: + folder = Path(target.filename).parent + # Generate the current source dict + src_list: dict[str, BaseNode] = {} + if isinstance(target.value, DictNode): + src_list.update({k.value: v for k, v in target.value.args.kwargs.items()}) + ext_data.setdefault(folder, []) + ext_data[folder] += src_list.keys() + + for target in visitor.extension_data: + if target.var_name.value != "extension_data": + continue + folder = Path(target.filename).parent + src_list = ext_data[folder] + + cython_files = sorted(list(folder.glob("*.pyx"))) + # Some cython files are compiled in a special way, so we don't want to add them + special_cython_files = { + "bliss.pyx", + "mcqd.pyx", + "tdlib.pyx", + } + cython_files = [x for x in cython_files if x.name not in special_cython_files] + # Add all cython files that are not in the source list + for file in cython_files: + file_name = file.stem + if file_name in src_list: + continue + token = Token("string", target.filename, 0, 0, 0, None, file_name) + arg = ArgumentNode(Token("", target.filename, 0, 0, 0, None, "[]")) + arg.append( + StringNode(Token("string", target.filename, 0, 0, 0, None, file.name)) + ) + func = FunctionNode(_symbol("files"), _symbol("("), arg, _symbol(")")) + target.value.args.kwargs.update({StringNode(token): func}) + if target not in self.modified_nodes: + self.modified_nodes += [target] + + +def apply_changes(self: Rewriter): + assert all( + hasattr(x, "lineno") and hasattr(x, "colno") and hasattr(x, "filename") + for x in self.modified_nodes + ) + assert all( + hasattr(x, "lineno") and hasattr(x, "colno") and hasattr(x, "filename") + for x in self.to_remove_nodes + ) + assert all( + isinstance(x, (ArrayNode, FunctionNode, MethodNode, AssignmentNode)) + for x in self.modified_nodes + ) + assert all( + isinstance(x, (ArrayNode, AssignmentNode, FunctionNode)) + for x in self.to_remove_nodes + ) + # Sort based on line and column in reversed order + work_nodes = [{"node": x, "action": "modify"} for x in self.modified_nodes] + work_nodes += [{"node": x, "action": "rm"} for x in self.to_remove_nodes] + work_nodes = sorted( + work_nodes, key=lambda x: (x["node"].lineno, x["node"].colno), reverse=True + ) + work_nodes += [{"node": x, "action": "add"} for x in self.to_add_nodes] + + # Generating the new replacement string + str_list = [] + for i in work_nodes: + new_data = "" + if i["action"] == "modify" or i["action"] == "add": + printer = AstPrinter() + i["node"].accept(printer) + printer.post_process() + new_data = printer.result.strip() + data = { + "file": i["node"].filename, + "str": new_data, + "node": i["node"], + "action": i["action"], + } + str_list += [data] + + # Load build files + files = {} + for i in str_list: + if i["file"] in files: + continue + fpath = os.path.realpath(os.path.join(self.sourcedir, i["file"])) + fdata = "" + # Create an empty file if it does not exist + if not os.path.exists(fpath): + with open(fpath, "w", encoding="utf-8"): + pass + with open(fpath, encoding="utf-8") as fp: + fdata = fp.read() + + # Generate line offsets numbers + m_lines = fdata.splitlines(True) + offset = 0 + line_offsets = [] + for j in m_lines: + line_offsets += [offset] + offset += len(j) + + files[i["file"]] = {"path": fpath, "raw": fdata, "offsets": line_offsets} + + # Replace in source code + def remove_node(i): + offsets = files[i["file"]]["offsets"] + raw = files[i["file"]]["raw"] + node = i["node"] + line = node.lineno - 1 + col = node.colno + if isinstance(node, MethodNode): + # The new data contains the source object as well + col = node.source_object.colno + elif isinstance(node, AssignmentNode): + col = node.var_name.colno + start = offsets[line] + col + end = start + if isinstance(node, (ArrayNode, FunctionNode, MethodNode)): + end = offsets[node.end_lineno - 1] + node.end_colno + elif isinstance(node, AssignmentNode): + end = offsets[node.value.end_lineno - 1] + node.value.end_colno + + # Only removal is supported for assignments + elif isinstance(node, AssignmentNode) and i["action"] == "rm": + if isinstance(node.value, (ArrayNode, FunctionNode, MethodNode)): + remove_node( + {"file": i["file"], "str": "", "node": node.value, "action": "rm"} + ) + raw = files[i["file"]]["raw"] + while raw[end] != "=": + end += 1 + end += 1 # Handle the '=' + while raw[end] in {" ", "\n", "\t"}: + end += 1 + + files[i["file"]]["raw"] = raw[:start] + i["str"] + raw[end:] + + for i in str_list: + if i["action"] in {"modify", "rm"}: + remove_node(i) + elif i["action"] == "add": + files[i["file"]]["raw"] += i["str"] + "\n" + + # Write the files back + for key, val in files.items(): + mlog.log("Rewriting", mlog.yellow(key)) + with open(val["path"], "w", encoding="utf-8") as fp: + fp.write(val["raw"]) + + +# Monkey patch the apply_changes method until https://github.com/mesonbuild/meson/pull/12899 is merged +Rewriter.apply_changes = apply_changes +# Monkey patch the update_python_sources method until this is upstreamed +Rewriter.process_update_python_sources = update_python_sources + +rewriter = Rewriter(options.sourcedir) +visitor = AstPython() +rewriter.interpreter.visitors += [visitor] +rewriter.analyze_meson() +rewriter.process_update_python_sources(visitor) +rewriter.apply_changes() +rewriter.print_info() + +# Run meson format +meson_format( + Namespace( + sources=[options.sourcedir], + inplace=True, + recursive=True, + output=None, + configuration=None, + editor_config=None, + ) +) diff --git a/tox.ini b/tox.ini index 3e3d2656553..bb3840cb3ed 100644 --- a/tox.ini +++ b/tox.ini @@ -280,7 +280,7 @@ setenv = linuxmint-21.3: BASE_IMAGE=linuxmintd/mint21.3 # # https://hub.docker.com/_/fedora - # as of 2024-01, latest=39, rawhide=40 + # as of 2024-08, latest=40, rawhide=41 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora: IGNORE_MISSING_SYSTEM_PACKAGES=yes @@ -299,6 +299,7 @@ setenv = fedora-38: BASE_TAG=38 fedora-39: BASE_TAG=39 fedora-40: BASE_TAG=40 + fedora-41: BASE_TAG=41 # # https://hub.docker.com/r/scientificlinux/sl # @@ -823,8 +824,8 @@ commands = local: config*) ;; \ local: *) make -k V=0 base-toolchain ;; \ local: esac && \ - local: make -k V=0 SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!gap,!cysignals,!linbox,!git,!ppl,!cmake,!rpy2,!sage_sws2rst" {env:TARGETS_PRE:} {posargs:build} && \ - local: ( [ -z "{env:TARGETS_OPTIONAL:}" ] || make -k V=0 SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!r,!python3,!gap,!cysignals,!linbox,!git,!ppl,!cmake,!rpy2,!sage_sws2rst" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' + local: make -k V=0 SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!python3,!cysignals,!linbox,!ppl,!cmake,!rpy2,!sage_sws2rst" {env:TARGETS_PRE:} {posargs:build} && \ + local: ( [ -z "{env:TARGETS_OPTIONAL:}" ] || make -k V=0 SAGE_CHECK=warn SAGE_CHECK_PACKAGES="!cython,!python3,!cysignals,!linbox,!ppl,!cmake,!rpy2,!sage_sws2rst" {env:TARGETS_OPTIONAL:} || echo "(error ignored)" ) ' [testenv:check_configure] ## Test that configure behaves properly @@ -855,8 +856,8 @@ setenv = ubuntu-{xenial-toolchain-gcc_9,bionic-gcc_8,focal,jammy,lunar,mantic,noble} \ debian-{bullseye,bookworm,trixie,sid} \ linuxmint-{20.1,20.2,20.3,21,21.1,21.2,21.3} \ - fedora-{30,31,32,33,34,35,36,37,38,39,40} \ - centos-stream-9-python3.9 \ + fedora-{30,31,32,33,34,35,36,37,38,39,40,41} \ + centos-stream-{9,9-python3.12} \ almalinux-{8-python3.9,9-python3.11} \ gentoo-python{3.10,3.11,3.12} \ archlinux-latest \